1# -*- coding: utf-8 -*-
2# pylint: disable=C0301,W0105,W0401,W0614
3'''
4Python DB API compatible exceptions
5http://www.python.org/dev/peps/pep-0249/
6
7The PEP-249 says that database related exceptions must be inherited as follows:
8
9    StandardError
10    |__Warning
11    |__Error
12       |__InterfaceError
13       |__DatabaseError
14          |__DataError
15          |__OperationalError
16          |__IntegrityError
17          |__InternalError
18          |__ProgrammingError
19          |__NotSupportedError
20'''
21
22import os
23import socket
24import sys
25import warnings
26
27
28try:
29    class Warning(StandardError):
30        '''Exception raised for important warnings
31        like data truncations while inserting, etc. '''
32except NameError:
33    class Warning(Exception):
34        '''Exception raised for important warnings
35        like data truncations while inserting, etc. '''
36
37try:
38    class Error(StandardError):
39        '''Base class for error exceptions'''
40except NameError:
41    class Error(Exception):
42        '''Base class for error exceptions'''
43
44
45class InterfaceError(Error):
46    '''
47    Exception raised for errors that are related to the database interface
48    rather than the database itself.
49    '''
50
51
52class DatabaseError(Error):
53    '''Exception raised for errors that are related to the database.'''
54
55
56class DataError(DatabaseError):
57    '''
58    Exception raised for errors that are due to problems with the processed
59    data like division by zero, numeric value out of range, etc.
60    '''
61
62
63class OperationalError(DatabaseError):
64    '''
65    Exception raised for errors that are related to the database's operation
66    and not necessarily under the control of the programmer, e.g. an
67    unexpected disconnect occurs, the data source name is not found,
68    a transaction could not be processed, a memory allocation error occurred
69    during processing, etc.
70    '''
71
72
73class IntegrityError(DatabaseError):
74    '''
75    Exception raised when the relational integrity of the database is affected,
76    e.g. a foreign key check fails.
77    '''
78
79
80class InternalError(DatabaseError):
81    '''
82    Exception raised when the database encounters an internal error, e.g. the
83    cursor is not valid anymore, the transaction is out of sync, etc.
84    '''
85
86
87class ProgrammingError(DatabaseError):
88    '''
89    Exception raised when the database encounters an internal error, e.g. the
90    cursor is not valid anymore, the transaction is out of sync, etc.
91    '''
92
93
94class NotSupportedError(DatabaseError):
95    '''
96    Exception raised in case a method or database API was used which is not
97    supported by the database, e.g. requesting a .rollback() on a connection
98    that does not support transaction or has transactions turned off.
99    '''
100
101
102class ConfigurationError(Error):
103    '''
104    Error of initialization with a user-provided configuration.
105    '''
106
107
108__all__ = ("Warning", "Error", "InterfaceError", "DatabaseError", "DataError",
109           "OperationalError", "IntegrityError", "InternalError",
110           "ProgrammingError", "NotSupportedError")
111
112# Monkey patch os.strerror for win32
113if sys.platform == "win32":
114    # Windows Sockets Error Codes (not all, but related on network errors)
115    # http://msdn.microsoft.com/en-us/library/windows/desktop/ms740668(v=vs.85).aspx
116    _code2str = {
117        10004: "Interrupted system call",
118        10009: "Bad file descriptor",
119        10013: "Permission denied",
120        10014: "Bad address",
121        10022: "Invalid argument",
122        10024: "Too many open files",
123        10035: "Resource temporarily unavailable",
124        10036: "Operation now in progress",
125        10037: "Operation already in progress",
126        10038: "Socket operation on nonsocket",
127        10039: "Destination address required",
128        10040: "Message too long",
129        10041: "Protocol wrong type for socket",
130        10042: "Bad protocol option",
131        10043: "Protocol not supported",
132        10044: "Socket type not supported",
133        10045: "Operation not supported",
134        10046: "Protocol family not supported",
135        10047: "Address family not supported by protocol family",
136        10048: "Address already in use",
137        10049: "Cannot assign requested address",
138        10050: "Network is down",
139        10051: "Network is unreachable",
140        10052: "Network dropped connection on reset",
141        10053: "Software caused connection abort",
142        10054: "Connection reset by peer",
143        10055: "No buffer space available",
144        10056: "Socket is already connected",
145        10057: "Socket is not connected",
146        10058: "Cannot send after transport endpoint shutdown",
147        10060: "Connection timed out",
148        10061: "Connection refused",
149        10062: "Cannot translate name",
150        10063: "File name too long",
151        10064: "Host is down",
152        10065: "No route to host",
153        11001: "Host not found",
154        11004: "Name or service not known"
155    }
156
157    os_strerror_orig = os.strerror
158
159    def os_strerror_patched(code):
160        '''
161        Return cross-platform message about socket-related errors
162
163        This function exists because under Windows os.strerror returns
164        'Unknown error' on all socket-related errors.
165        And socket-related exception contain broken non-ascii encoded messages.
166        '''
167        message = os_strerror_orig(code)
168        if not message.startswith("Unknown"):
169            return message
170        else:
171            return _code2str.get(code, "Unknown error %s" % code)
172
173    os.strerror = os_strerror_patched
174    del os_strerror_patched
175
176
177class SchemaError(DatabaseError):
178    def __init__(self, value):
179        super(SchemaError, self).__init__(0, value)
180        self.value = value
181
182    def __str__(self):
183        return str(self.value)
184
185
186class SchemaReloadException(DatabaseError):
187    def __init__(self, message, schema_version):
188        super(SchemaReloadException, self).__init__(109, message)
189        self.code = 109
190        self.message = message
191        self.schema_version = schema_version
192
193    def __str__(self):
194        return str(self.message)
195
196
197class NetworkError(DatabaseError):
198    '''Error related to network'''
199
200    def __init__(self, orig_exception=None, *args):
201        self.errno = 0
202        if hasattr(orig_exception, 'errno'):
203            self.errno = orig_exception.errno
204        if orig_exception:
205            if isinstance(orig_exception, socket.timeout):
206                self.message = "Socket timeout"
207                super(NetworkError, self).__init__(0, self.message)
208            elif isinstance(orig_exception, socket.error):
209                self.message = os.strerror(orig_exception.errno)
210                super(NetworkError, self).__init__(
211                    orig_exception.errno, self.message)
212            else:
213                super(NetworkError, self).__init__(orig_exception, *args)
214
215
216class NetworkWarning(UserWarning):
217    '''Warning related to network'''
218    pass
219
220
221class ClusterDiscoveryWarning(UserWarning):
222    '''Warning related to cluster discovery'''
223    pass
224
225
226# always print this warnings
227warnings.filterwarnings("always", category=NetworkWarning)
228
229
230def warn(message, warning_class):
231    '''
232    Emit warinig message.
233    Just like standard warnings.warn() but don't output full filename.
234    '''
235    frame = sys._getframe(2)  # pylint: disable=W0212
236    module_name = frame.f_globals.get("__name__")
237    line_no = frame.f_lineno
238    warnings.warn_explicit(message, warning_class, module_name, line_no)
239
240
241_strerror = {
242    0: ("ER_UNKNOWN", "Unknown error"),
243    1: ("ER_ILLEGAL_PARAMS", "Illegal parameters, %s"),
244    2: ("ER_MEMORY_ISSUE", "Failed to allocate %u bytes in %s for %s"),
245    3: ("ER_TUPLE_FOUND",
246        "Duplicate key exists in unique index '%s' in space '%s'"),
247    4: ("ER_TUPLE_NOT_FOUND",
248        "Tuple doesn't exist in index '%s' in space '%s'"),
249    5: ("ER_UNSUPPORTED", "%s does not support %s"),
250    6: ("ER_NONMASTER",
251        "Can't modify data on a replication slave. My master is: %s"),
252    7: ("ER_READONLY",
253        "Can't modify data because this server is in read-only mode."),
254    8: ("ER_INJECTION", "Error injection '%s'"),
255    9: ("ER_CREATE_SPACE", "Failed to create space '%s': %s"),
256    10: ("ER_SPACE_EXISTS", "Space '%s' already exists"),
257    11: ("ER_DROP_SPACE", "Can't drop space '%s': %s"),
258    12: ("ER_ALTER_SPACE", "Can't modify space '%s': %s"),
259    13: ("ER_INDEX_TYPE",
260         "Unsupported index type supplied for index '%s' in space '%s'"),
261    14: ("ER_MODIFY_INDEX",
262         "Can't create or modify index '%s' in space '%s': %s"),
263    15: ("ER_LAST_DROP",
264         "Can't drop the primary key in a system space, space '%s'"),
265    16: ("ER_TUPLE_FORMAT_LIMIT", "Tuple format limit reached: %u"),
266    17: ("ER_DROP_PRIMARY_KEY",
267         "Can't drop primary key in space '%s' while secondary keys exist"),
268    18: ("ER_KEY_PART_TYPE", (
269         "Supplied key type of part %u does not match index part type:"
270         " expected %s")),
271    19: ("ER_EXACT_MATCH",
272         "Invalid key part count in an exact match (expected %u, got %u)"),
273    20: ("ER_INVALID_MSGPACK", "Invalid MsgPack - %s"),
274    21: ("ER_PROC_RET", "msgpack.encode: can not encode Lua type '%s'"),
275    22: ("ER_TUPLE_NOT_ARRAY", "Tuple/Key must be MsgPack array"),
276    23: ("ER_FIELD_TYPE", (
277         "Tuple field %u type does not match one required by operation:"
278         " expected %s")),
279    24: ("ER_FIELD_TYPE_MISMATCH", (
280         "Ambiguous field type in index '%s', key part %u. Requested type"
281         " is %s but the field has previously been defined as %s")),
282    25: ("ER_SPLICE", "SPLICE error on field %u: %s"),
283    26: ("ER_ARG_TYPE", (
284         "Argument type in operation '%c' on field %u does not match"
285         " field type: expected a %s")),
286    27: ("ER_TUPLE_IS_TOO_LONG", "Tuple is too long %u"),
287    28: ("ER_UNKNOWN_UPDATE_OP", "Unknown UPDATE operation"),
288    29: ("ER_UPDATE_FIELD", "Field %u UPDATE error: %s"),
289    30: ("ER_FIBER_STACK",
290         "Can not create a new fiber: recursion limit reached"),
291    31: ("ER_KEY_PART_COUNT",
292         "Invalid key part count (expected [0..%u], got %u)"),
293    32: ("ER_PROC_LUA", "%s"),
294    33: ("ER_NO_SUCH_PROC", "Procedure '%.*s' is not defined"),
295    34: ("ER_NO_SUCH_TRIGGER", "Trigger is not found"),
296    35: ("ER_NO_SUCH_INDEX", "No index #%u is defined in space '%s'"),
297    36: ("ER_NO_SUCH_SPACE", "Space '%s' does not exist"),
298    37: ("ER_NO_SUCH_FIELD", "Field %d was not found in the tuple"),
299    38: ("ER_SPACE_FIELD_COUNT",
300         "Tuple field count %u does not match space '%s' field count %u"),
301    39: ("ER_INDEX_FIELD_COUNT", (
302         "Tuple field count %u is less than required by a defined index"
303         " (expected %u)")),
304    40: ("ER_WAL_IO", "Failed to write to disk"),
305    41: ("ER_MORE_THAN_ONE_TUPLE", "More than one tuple found by get()"),
306    42: ("ER_ACCESS_DENIED", "%s access on %s is denied for user '%s'"),
307    43: ("ER_CREATE_USER", "Failed to create user '%s': %s"),
308    44: ("ER_DROP_USER", "Failed to drop user or role '%s': %s"),
309    45: ("ER_NO_SUCH_USER", "User '%s' is not found"),
310    46: ("ER_USER_EXISTS", "User '%s' already exists"),
311    47: ("ER_PASSWORD_MISMATCH", "Incorrect password supplied for user '%s'"),
312    48: ("ER_UNKNOWN_REQUEST_TYPE", "Unknown request type %u"),
313    49: ("ER_UNKNOWN_SCHEMA_OBJECT", "Unknown object type '%s'"),
314    50: ("ER_CREATE_FUNCTION", "Failed to create function '%s': %s"),
315    51: ("ER_NO_SUCH_FUNCTION", "Function '%s' does not exist"),
316    52: ("ER_FUNCTION_EXISTS", "Function '%s' already exists"),
317    53: ("ER_FUNCTION_ACCESS_DENIED",
318         "%s access is denied for user '%s' to function '%s'"),
319    54: ("ER_FUNCTION_MAX",
320         "A limit on the total number of functions has been reached: %u"),
321    55: ("ER_SPACE_ACCESS_DENIED",
322         "%s access is denied for user '%s' to space '%s'"),
323    56: ("ER_USER_MAX",
324         "A limit on the total number of users has been reached: %u"),
325    57: ("ER_NO_SUCH_ENGINE", "Space engine '%s' does not exist"),
326    58: ("ER_RELOAD_CFG", "Can't set option '%s' dynamically"),
327    59: ("ER_CFG", "Incorrect value for option '%s': %s"),
328    60: ("ER_SOPHIA", "%s"),
329    61: ("ER_LOCAL_SERVER_IS_NOT_ACTIVE", "Local server is not active"),
330    62: ("ER_UNKNOWN_SERVER", "Server %s is not registered with the cluster"),
331    63: ("ER_CLUSTER_ID_MISMATCH", (
332         "Cluster id of the replica %s doesn't match cluster id"
333         " of the master %s")),
334    64: ("ER_INVALID_UUID", "Invalid UUID: %s"),
335    65: ("ER_CLUSTER_ID_IS_RO",
336         "Can't reset cluster id: it is already assigned"),
337    66: ("ER_RESERVED66", "Reserved66"),
338    67: ("ER_SERVER_ID_IS_RESERVED",
339         "Can't initialize server id with a reserved value %u"),
340    68: ("ER_INVALID_ORDER", (
341         "Invalid LSN order for server %u: previous LSN = %llu,"
342         " new lsn = %llu")),
343    69: ("ER_MISSING_REQUEST_FIELD",
344         "Missing mandatory field '%s' in request"),
345    70: ("ER_IDENTIFIER", (
346         "Invalid identifier '%s' (expected letters, digits"
347         " or an underscore)")),
348    71: ("ER_DROP_FUNCTION", "Can't drop function %u: %s"),
349    72: ("ER_ITERATOR_TYPE", "Unknown iterator type '%s'"),
350    73: ("ER_REPLICA_MAX", "Replica count limit reached: %u"),
351    74: ("ER_INVALID_XLOG", "Failed to read xlog: %lld"),
352    75: ("ER_INVALID_XLOG_NAME", "Invalid xlog name: expected %lld got %lld"),
353    76: ("ER_INVALID_XLOG_ORDER", "Invalid xlog order: %lld and %lld"),
354    77: ("ER_NO_CONNECTION", "Connection is not established"),
355    78: ("ER_TIMEOUT", "Timeout exceeded"),
356    79: ("ER_ACTIVE_TRANSACTION",
357         "Operation is not permitted when there is an active transaction "),
358    80: ("ER_NO_ACTIVE_TRANSACTION",
359         "Operation is not permitted when there is no active transaction "),
360    81: ("ER_CROSS_ENGINE_TRANSACTION",
361         "A multi-statement transaction can not use multiple storage engines"),
362    82: ("ER_NO_SUCH_ROLE", "Role '%s' is not found"),
363    83: ("ER_ROLE_EXISTS", "Role '%s' already exists"),
364    84: ("ER_CREATE_ROLE", "Failed to create role '%s': %s"),
365    85: ("ER_INDEX_EXISTS", "Index '%s' already exists"),
366    86: ("ER_TUPLE_REF_OVERFLOW", "Tuple reference counter overflow"),
367    87: ("ER_ROLE_LOOP",
368         "Granting role '%s' to role '%s' would create a loop"),
369    88: ("ER_GRANT", "Incorrect grant arguments: %s"),
370    89: ("ER_PRIV_GRANTED", "User '%s' already has %s access on %s '%s'"),
371    90: ("ER_ROLE_GRANTED", "User '%s' already has role '%s'"),
372    91: ("ER_PRIV_NOT_GRANTED",
373         "User '%s' does not have %s access on %s '%s'"),
374    92: ("ER_ROLE_NOT_GRANTED", "User '%s' does not have role '%s'"),
375    93: ("ER_MISSING_SNAPSHOT", "Can't find snapshot"),
376    94: ("ER_CANT_UPDATE_PRIMARY_KEY", (
377         "Attempt to modify a tuple field which is part of index '%s'"
378         " in space '%s'")),
379    95: ("ER_UPDATE_INTEGER_OVERFLOW",
380         "Integer overflow when performing '%c' operation on field %u"),
381    96: ("ER_GUEST_USER_PASSWORD",
382         "Setting password for guest user has no effect"),
383    97: ("ER_TRANSACTION_CONFLICT",
384         "Transaction has been aborted by conflict"),
385    98: ("ER_UNSUPPORTED_ROLE_PRIV", "Unsupported role privilege '%s'"),
386    99: ("ER_LOAD_FUNCTION", "Failed to dynamically load function '%s': %s"),
387    100: ("ER_FUNCTION_LANGUAGE",
388          "Unsupported language '%s' specified for function '%s'"),
389    101: ("ER_RTREE_RECT", (
390          "RTree: %s must be an array with %u (point)"
391          " or %u (rectangle/box) numeric coordinates")),
392    102: ("ER_PROC_C", "%s"),
393    103: ("ER_UNKNOWN_RTREE_INDEX_DISTANCE_TYPE",
394          "Unknown RTREE index distance type %s"),
395    104: ("ER_PROTOCOL", "%s"),
396    105: ("ER_UPSERT_UNIQUE_SECONDARY_KEY",
397          "Space %s has a unique secondary index and does not support UPSERT"),
398    106: ("ER_WRONG_INDEX_RECORD",
399          "Wrong record in _index space: got {%s}, expected {%s}"),
400    107: ("ER_WRONG_INDEX_PARTS", (
401          "Wrong index parts (field %u): %s; expected field1 id (number),"
402          " field1 type (string), ...")),
403    108: ("ER_WRONG_INDEX_OPTIONS", "Wrong index options (field %u): %s"),
404    109: ("ER_WRONG_SCHEMA_VERSION",
405          "Wrong schema version, current: %d, in request: %u"),
406    110: ("ER_SLAB_ALLOC_MAX", (
407          "Failed to allocate %u bytes for tuple in the slab allocator:"
408          " tuple is too large. Check 'slab_alloc_maximal'"
409          " configuration option.")),
410    111: ("ER_WRONG_SPACE_OPTIONS", "Wrong space options (field %u): %s"),
411    112: ("ER_UNSUPPORTED_INDEX_FEATURE",
412          "Index '%s' (%s) of space '%s' (%s) does not support %s"),
413    113: ("ER_VIEW_IS_RO", "View '%s' is read-only"),
414}
415
416
417def tnt_strerror(num):
418    if num in _strerror:
419        return _strerror[num]
420    return "UNDEFINED"
421