tarantool_module/
error.rs

1//! Error handling utils. See ["failure" crate documentation](https://docs.rs/failure/) for details
2//!
3//! The Tarantool error handling works most like libc's errno. All API calls
4//! return -1 or `NULL` in the event of error. An internal pointer to
5//! `box_error_t` type is set by API functions to indicate what went wrong.
6//! This value is only significant if API call failed (returned -1 or `NULL`).
7//!
8//! Successful function can also touch the last error in some
9//! cases. You don't have to clear the last error before calling
10//! API functions. The returned object is valid only until next
11//! call to **any** API function.
12//!
13//! You must set the last error using `set_error()` in your stored C
14//! procedures if you want to return a custom error message.
15//! You can re-throw the last API error to IPROTO client by keeping
16//! the current value and returning -1 to Tarantool from your
17//! stored procedure.
18
19use std::ffi::{CStr, CString};
20use std::os::raw::c_int;
21use std::{fmt, io};
22
23use failure::_core::fmt::{Display, Formatter};
24use num_traits::{FromPrimitive, ToPrimitive};
25use rmp::decode::ValueReadError;
26use rmp_serde::decode::Error as DecodeError;
27use rmp_serde::encode::Error as EncodeError;
28
29/// Represents all error cases for all routines of crate (including Tarantool errors)
30#[derive(Debug, Fail)]
31pub enum Error {
32    #[fail(display = "Tarantool error: {}", _0)]
33    Tarantool(TarantoolError),
34
35    #[fail(display = "IO error: {}", _0)]
36    IO(io::Error),
37
38    #[fail(display = "Failed to encode tuple: {}", _0)]
39    Encode(EncodeError),
40
41    #[fail(display = "Failed to decode tuple: {}", _0)]
42    Decode(DecodeError),
43
44    #[fail(display = "Value read error: {}", _0)]
45    ValueRead(ValueReadError),
46
47    #[fail(display = "Transaction issue: {}", _0)]
48    Transaction(TransactionError),
49}
50
51impl From<io::Error> for Error {
52    fn from(error: io::Error) -> Self {
53        Error::IO(error)
54    }
55}
56
57impl From<EncodeError> for Error {
58    fn from(error: EncodeError) -> Self {
59        Error::Encode(error)
60    }
61}
62
63impl From<DecodeError> for Error {
64    fn from(error: DecodeError) -> Self {
65        Error::Decode(error)
66    }
67}
68
69impl From<ValueReadError> for Error {
70    fn from(error: ValueReadError) -> Self {
71        Error::ValueRead(error)
72    }
73}
74
75/// Transaction-related error cases
76#[derive(Debug, Fail)]
77pub enum TransactionError {
78    #[fail(display = "Transaction has already been started")]
79    AlreadyStarted,
80
81    #[fail(display = "Failed to commit")]
82    FailedToCommit,
83
84    #[fail(display = "Failed to rollback")]
85    FailedToRollback,
86}
87
88impl From<TransactionError> for Error {
89    fn from(error: TransactionError) -> Self {
90        Error::Transaction(error)
91    }
92}
93
94/// Settable by Tarantool error type
95#[derive(Derivative)]
96#[derivative(Debug)]
97pub struct TarantoolError {
98    code: TarantoolErrorCode,
99    message: String,
100    #[derivative(Debug = "ignore")]
101    error_ptr: Box<ffi::BoxError>,
102}
103
104impl TarantoolError {
105    /// Tries to get the information about the last API call error. If error was not set
106    /// returns `Ok(())`
107    pub fn maybe_last() -> Result<(), Self> {
108        let error_ptr = unsafe { ffi::box_error_last() };
109        if error_ptr.is_null() {
110            return Ok(());
111        }
112
113        let code = unsafe { ffi::box_error_code(error_ptr) };
114        let code = match TarantoolErrorCode::from_u32(code) {
115            Some(code) => code,
116            None => TarantoolErrorCode::Unknown,
117        };
118
119        let message = unsafe { CStr::from_ptr(ffi::box_error_message(error_ptr)) };
120        let message = message.to_string_lossy().into_owned();
121
122        Err(TarantoolError {
123            code,
124            message,
125            error_ptr: unsafe { Box::from_raw(error_ptr) },
126        })
127    }
128
129    /// Get the information about the last API call error.
130    pub fn last() -> Self {
131        TarantoolError::maybe_last().err().unwrap()
132    }
133
134    /// Return IPROTO error code
135    pub fn error_code(&self) -> TarantoolErrorCode {
136        self.code.clone()
137    }
138
139    /// Return the error type, e.g. "ClientError", "SocketError", etc.
140    pub fn error_type(&self) -> String {
141        let result = unsafe { ffi::box_error_type(&*self.error_ptr) };
142        unsafe { CStr::from_ptr(result) }
143            .to_string_lossy()
144            .to_string()
145    }
146}
147
148impl Display for TarantoolError {
149    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
150        write!(f, "{:?}: {}", self.code, self.message)
151    }
152}
153
154impl From<TarantoolError> for Error {
155    fn from(error: TarantoolError) -> Self {
156        Error::Tarantool(error)
157    }
158}
159
160/// Codes of Tarantool errors
161#[repr(u32)]
162#[derive(Debug, Clone, PartialEq, ToPrimitive, FromPrimitive)]
163pub enum TarantoolErrorCode {
164    Unknown = 0,
165    IllegalParams = 1,
166    MemoryIssue = 2,
167    TupleFound = 3,
168    TupleNotFound = 4,
169    Unsupported = 5,
170    NonMaster = 6,
171    Readonly = 7,
172    Injection = 8,
173    CreateSpace = 9,
174    SpaceExists = 10,
175    DropSpace = 11,
176    AlterSpace = 12,
177    IndexType = 13,
178    ModifyIndex = 14,
179    LastDrop = 15,
180    TupleFormatLimit = 16,
181    DropPrimaryKey = 17,
182    KeyPartType = 18,
183    ExactMatch = 19,
184    InvalidMsgpack = 20,
185    ProcRet = 21,
186    TupleNotArray = 22,
187    FieldType = 23,
188    IndexPartTypeMismatch = 24,
189    Splice = 25,
190    UpdateArgType = 26,
191    FormatMismatchIndexPart = 27,
192    UnknownUpdateOp = 28,
193    UpdateField = 29,
194    FunctionTxActive = 30,
195    KeyPartCount = 31,
196    ProcLua = 32,
197    NoSuchProc = 33,
198    NoSuchTrigger = 34,
199    NoSuchIndexID = 35,
200    NoSuchSpace = 36,
201    NoSuchFieldNo = 37,
202    ExactFieldCount = 38,
203    FieldMissing = 39,
204    WalIo = 40,
205    MoreThanOneTuple = 41,
206    AccessDenied = 42,
207    CreateUser = 43,
208    DropUser = 44,
209    NoSuchUser = 45,
210    UserExists = 46,
211    PasswordMismatch = 47,
212    UnknownRequestType = 48,
213    UnknownSchemaObject = 49,
214    CreateFunction = 50,
215    NoSuchFunction = 51,
216    FunctionExists = 52,
217    BeforeReplaceRet = 53,
218    MultistatementTransaction = 54,
219    TriggerExists = 55,
220    UserMax = 56,
221    NoSuchEngine = 57,
222    ReloadCfg = 58,
223    Cfg = 59,
224    SavepointEmptyTx = 60,
225    NoSuchSavepoint = 61,
226    UnknownReplica = 62,
227    ReplicasetUuidMismatch = 63,
228    InvalidUuid = 64,
229    ReplicasetUuidIsRo = 65,
230    InstanceUuidMismatch = 66,
231    ReplicaIDIsReserved = 67,
232    InvalidOrder = 68,
233    MissingRequestField = 69,
234    Identifier = 70,
235    DropFunction = 71,
236    IteratorType = 72,
237    ReplicaMax = 73,
238    InvalidXlog = 74,
239    InvalidXlogName = 75,
240    InvalidXlogOrder = 76,
241    NoConnection = 77,
242    Timeout = 78,
243    ActiveTransaction = 79,
244    CursorNoTransaction = 80,
245    CrossEngineTransaction = 81,
246    NoSuchRole = 82,
247    RoleExists = 83,
248    CreateRole = 84,
249    IndexExists = 85,
250    SessionClosed = 86,
251    RoleLoop = 87,
252    Grant = 88,
253    PrivGranted = 89,
254    RoleGranted = 90,
255    PrivNotGranted = 91,
256    RoleNotGranted = 92,
257    MissingSnapshot = 93,
258    CantUpdatePrimaryKey = 94,
259    UpdateIntegerOverflow = 95,
260    GuestUserPassword = 96,
261    TransactionConflict = 97,
262    UnsupportedPriv = 98,
263    LoadFunction = 99,
264    FunctionLanguage = 100,
265    RtreeRect = 101,
266    ProcC = 102,
267    UnknownRtreeIndexDistanceType = 103,
268    Protocol = 104,
269    UpsertUniqueSecondaryKey = 105,
270    WrongIndexRecord = 106,
271    WrongIndexParts = 107,
272    WrongIndexOptions = 108,
273    WrongSchemaVersion = 109,
274    MemtxMaxTupleSize = 110,
275    WrongSpaceOptions = 111,
276    UnsupportedIndexFeature = 112,
277    ViewIsRo = 113,
278    NoTransaction = 114,
279    System = 115,
280    Loading = 116,
281    ConnectionToSelf = 117,
282    KeyPartIsTooLong = 118,
283    Compression = 119,
284    CheckpointInProgress = 120,
285    SubStmtMax = 121,
286    CommitInSubStmt = 122,
287    RollbackInSubStmt = 123,
288    Decompression = 124,
289    InvalidXlogType = 125,
290    AlreadyRunning = 126,
291    IndexFieldCountLimit = 127,
292    LocalInstanceIDIsReadOnly = 128,
293    BackupInProgress = 129,
294    ReadViewAborted = 130,
295    InvalidIndexFile = 131,
296    InvalidRunFile = 132,
297    InvalidVylogFile = 133,
298    CheckpointRollback = 134,
299    VyQuotaTimeout = 135,
300    PartialKey = 136,
301    TruncateSystemSpace = 137,
302    LoadModule = 138,
303    VinylMaxTupleSize = 139,
304    WrongDdVersion = 140,
305    WrongSpaceFormat = 141,
306    CreateSequence = 142,
307    AlterSequence = 143,
308    DropSequence = 144,
309    NoSuchSequence = 145,
310    SequenceExists = 146,
311    SequenceOverflow = 147,
312    NoSuchIndexName = 148,
313    SpaceFieldIsDuplicate = 149,
314    CantCreateCollation = 150,
315    WrongCollationOptions = 151,
316    NullablePrimary = 152,
317    NoSuchFieldName = 153,
318    TransactionYield = 154,
319    NoSuchGroup = 155,
320    SqlBindValue = 156,
321    SqlBindType = 157,
322    SqlBindParameterMax = 158,
323    SqlExecute = 159,
324    Unused = 160,
325    SqlBindNotFound = 161,
326    ActionMismatch = 162,
327    ViewMissingSql = 163,
328    ForeignKeyConstraint = 164,
329    NoSuchModule = 165,
330    NoSuchCollation = 166,
331    CreateFkConstraint = 167,
332    DropFkConstraint = 168,
333    NoSuchConstraint = 169,
334    ConstraintExists = 170,
335    SqlTypeMismatch = 171,
336    RowidOverflow = 172,
337    DropCollation = 173,
338    IllegalCollationMix = 174,
339    SqlNoSuchPragma = 175,
340    SqlCantResolveField = 176,
341    IndexExistsInSpace = 177,
342    InconsistentTypes = 178,
343    SqlSyntax = 179,
344    SqlStackOverflow = 180,
345    SqlSelectWildcard = 181,
346    SqlStatementEmpty = 182,
347    SqlKeywordIsReserved = 183,
348    SqlUnrecognizedSyntax = 184,
349    SqlUnknownToken = 185,
350    SqlParserGeneric = 186,
351    SqlAnalyzeArgument = 187,
352    SqlColumnCountMax = 188,
353    HexLiteralMax = 189,
354    IntLiteralMax = 190,
355    SqlParserLimit = 191,
356    IndexDefUnsupported = 192,
357    CkDefUnsupported = 193,
358    MultikeyIndexMismatch = 194,
359    CreateCkConstraint = 195,
360    CkConstraintFailed = 196,
361    SqlColumnCount = 197,
362    FuncIndexFunc = 198,
363    FuncIndexFormat = 199,
364    FuncIndexParts = 200,
365    BootstrapReadonly = 201,
366}
367
368/// Clear the last error.
369pub fn clear_error() {
370    unsafe { ffi::box_error_clear() }
371}
372
373/// Set the last error.
374pub fn set_error(file: &str, line: u32, code: &TarantoolErrorCode, msg: &str) -> c_int {
375    unsafe {
376        ffi::box_error_set(
377            CString::new(file).unwrap().as_ptr(),
378            line,
379            code.to_u32().unwrap(),
380            CString::new(msg).unwrap().as_ptr(),
381        )
382    }
383}
384
385mod ffi {
386    use std::os::raw::{c_char, c_int, c_uint};
387
388    #[repr(C)]
389    pub struct BoxError {
390        _unused: [u8; 0],
391    }
392
393    extern "C" {
394        pub fn box_error_code(error: *const BoxError) -> u32;
395        pub fn box_error_message(error: *const BoxError) -> *const c_char;
396        pub fn box_error_last() -> *mut BoxError;
397        pub fn box_error_type(error: *const BoxError) -> *const c_char;
398        pub fn box_error_clear();
399        pub fn box_error_set(
400            file: *const c_char,
401            line: c_uint,
402            code: u32,
403            format: *const c_char,
404            ...
405        ) -> c_int;
406    }
407}