1# sqlalchemy/exc.py 
    2# Copyright (C) 2005-2021 the SQLAlchemy authors and contributors 
    3# <see AUTHORS file> 
    4# 
    5# This module is part of SQLAlchemy and is released under 
    6# the MIT License: http://www.opensource.org/licenses/mit-license.php 
    7 
    8"""Exceptions used with SQLAlchemy. 
    9 
    10The base exception class is :exc:`.SQLAlchemyError`.  Exceptions which are 
    11raised as a result of DBAPI exceptions are all subclasses of 
    12:exc:`.DBAPIError`. 
    13 
    14""" 
    15 
    16from .util import _preloaded 
    17from .util import compat 
    18 
    19_version_token = None 
    20 
    21 
    22class SQLAlchemyError(Exception): 
    23    """Generic error class.""" 
    24 
    25    code = None 
    26 
    27    def __init__(self, *arg, **kw): 
    28        code = kw.pop("code", None) 
    29        if code is not None: 
    30            self.code = code 
    31        super(SQLAlchemyError, self).__init__(*arg, **kw) 
    32 
    33    def _code_str(self): 
    34        if not self.code: 
    35            return "" 
    36        else: 
    37            return ( 
    38                "(Background on this error at: " 
    39                "http://sqlalche.me/e/%s/%s)" 
    40                % ( 
    41                    _version_token, 
    42                    self.code, 
    43                ) 
    44            ) 
    45 
    46    def _message(self, as_unicode=compat.py3k): 
    47        # rules: 
    48        # 
    49        # 1. under py2k, for __str__ return single string arg as it was 
    50        # given without converting to unicode.  for __unicode__ 
    51        # do a conversion but check that it's not unicode already just in 
    52        # case 
    53        # 
    54        # 2. under py3k, single arg string will usually be a unicode 
    55        # object, but since __str__() must return unicode, check for 
    56        # bytestring just in case 
    57        # 
    58        # 3. for multiple self.args, this is not a case in current 
    59        # SQLAlchemy though this is happening in at least one known external 
    60        # library, call str() which does a repr(). 
    61        # 
    62        if len(self.args) == 1: 
    63            text = self.args[0] 
    64 
    65            if as_unicode and isinstance(text, compat.binary_types): 
    66                text = compat.decode_backslashreplace(text, "utf-8") 
    67            # This is for when the argument is not a string of any sort. 
    68            # Otherwise, converting this exception to string would fail for 
    69            # non-string arguments. 
    70            elif compat.py3k or not as_unicode: 
    71                text = str(text) 
    72            else: 
    73                text = compat.text_type(text) 
    74 
    75            return text 
    76        else: 
    77            # this is not a normal case within SQLAlchemy but is here for 
    78            # compatibility with Exception.args - the str() comes out as 
    79            # a repr() of the tuple 
    80            return str(self.args) 
    81 
    82    def _sql_message(self, as_unicode): 
    83        message = self._message(as_unicode) 
    84 
    85        if self.code: 
    86            message = "%s %s" % (message, self._code_str()) 
    87 
    88        return message 
    89 
    90    def __str__(self): 
    91        return self._sql_message(compat.py3k) 
    92 
    93    def __unicode__(self): 
    94        return self._sql_message(as_unicode=True) 
    95 
    96 
    97class ArgumentError(SQLAlchemyError): 
    98    """Raised when an invalid or conflicting function argument is supplied. 
    99 
    100    This error generally corresponds to construction time state errors. 
    101 
    102    """ 
    103 
    104 
    105class ObjectNotExecutableError(ArgumentError): 
    106    """Raised when an object is passed to .execute() that can't be 
    107    executed as SQL. 
    108 
    109    .. versionadded:: 1.1 
    110 
    111    """ 
    112 
    113    def __init__(self, target): 
    114        super(ObjectNotExecutableError, self).__init__( 
    115            "Not an executable object: %r" % target 
    116        ) 
    117 
    118 
    119class NoSuchModuleError(ArgumentError): 
    120    """Raised when a dynamically-loaded module (usually a database dialect) 
    121    of a particular name cannot be located.""" 
    122 
    123 
    124class NoForeignKeysError(ArgumentError): 
    125    """Raised when no foreign keys can be located between two selectables 
    126    during a join.""" 
    127 
    128 
    129class AmbiguousForeignKeysError(ArgumentError): 
    130    """Raised when more than one foreign key matching can be located 
    131    between two selectables during a join.""" 
    132 
    133 
    134class CircularDependencyError(SQLAlchemyError): 
    135    """Raised by topological sorts when a circular dependency is detected. 
    136 
    137    There are two scenarios where this error occurs: 
    138 
    139    * In a Session flush operation, if two objects are mutually dependent 
    140      on each other, they can not be inserted or deleted via INSERT or 
    141      DELETE statements alone; an UPDATE will be needed to post-associate 
    142      or pre-deassociate one of the foreign key constrained values. 
    143      The ``post_update`` flag described at :ref:`post_update` can resolve 
    144      this cycle. 
    145    * In a :attr:`_schema.MetaData.sorted_tables` operation, two 
    146      :class:`_schema.ForeignKey` 
    147      or :class:`_schema.ForeignKeyConstraint` objects mutually refer to each 
    148      other.  Apply the ``use_alter=True`` flag to one or both, 
    149      see :ref:`use_alter`. 
    150 
    151    """ 
    152 
    153    def __init__(self, message, cycles, edges, msg=None, code=None): 
    154        if msg is None: 
    155            message += " (%s)" % ", ".join(repr(s) for s in cycles) 
    156        else: 
    157            message = msg 
    158        SQLAlchemyError.__init__(self, message, code=code) 
    159        self.cycles = cycles 
    160        self.edges = edges 
    161 
    162    def __reduce__(self): 
    163        return self.__class__, (None, self.cycles, self.edges, self.args[0]) 
    164 
    165 
    166class CompileError(SQLAlchemyError): 
    167    """Raised when an error occurs during SQL compilation""" 
    168 
    169 
    170class UnsupportedCompilationError(CompileError): 
    171    """Raised when an operation is not supported by the given compiler. 
    172 
    173    .. seealso:: 
    174 
    175        :ref:`faq_sql_expression_string` 
    176 
    177        :ref:`error_l7de` 
    178    """ 
    179 
    180    code = "l7de" 
    181 
    182    def __init__(self, compiler, element_type, message=None): 
    183        super(UnsupportedCompilationError, self).__init__( 
    184            "Compiler %r can't render element of type %s%s" 
    185            % (compiler, element_type, ": %s" % message if message else "") 
    186        ) 
    187 
    188 
    189class IdentifierError(SQLAlchemyError): 
    190    """Raised when a schema name is beyond the max character limit""" 
    191 
    192 
    193class DisconnectionError(SQLAlchemyError): 
    194    """A disconnect is detected on a raw DB-API connection. 
    195 
    196    This error is raised and consumed internally by a connection pool.  It can 
    197    be raised by the :meth:`_events.PoolEvents.checkout` 
    198    event so that the host pool 
    199    forces a retry; the exception will be caught three times in a row before 
    200    the pool gives up and raises :class:`~sqlalchemy.exc.InvalidRequestError` 
    201    regarding the connection attempt. 
    202 
    203    """ 
    204 
    205    invalidate_pool = False 
    206 
    207 
    208class InvalidatePoolError(DisconnectionError): 
    209    """Raised when the connection pool should invalidate all stale connections. 
    210 
    211    A subclass of :class:`_exc.DisconnectionError` that indicates that the 
    212    disconnect situation encountered on the connection probably means the 
    213    entire pool should be invalidated, as the database has been restarted. 
    214 
    215    This exception will be handled otherwise the same way as 
    216    :class:`_exc.DisconnectionError`, allowing three attempts to reconnect 
    217    before giving up. 
    218 
    219    .. versionadded:: 1.2 
    220 
    221    """ 
    222 
    223    invalidate_pool = True 
    224 
    225 
    226class TimeoutError(SQLAlchemyError):  # noqa 
    227    """Raised when a connection pool times out on getting a connection.""" 
    228 
    229 
    230class InvalidRequestError(SQLAlchemyError): 
    231    """SQLAlchemy was asked to do something it can't do. 
    232 
    233    This error generally corresponds to runtime state errors. 
    234 
    235    """ 
    236 
    237 
    238class NoInspectionAvailable(InvalidRequestError): 
    239    """A subject passed to :func:`sqlalchemy.inspection.inspect` produced 
    240    no context for inspection.""" 
    241 
    242 
    243class ResourceClosedError(InvalidRequestError): 
    244    """An operation was requested from a connection, cursor, or other 
    245    object that's in a closed state.""" 
    246 
    247 
    248class NoSuchColumnError(KeyError, InvalidRequestError): 
    249    """A nonexistent column is requested from a ``RowProxy``.""" 
    250 
    251 
    252class NoReferenceError(InvalidRequestError): 
    253    """Raised by ``ForeignKey`` to indicate a reference cannot be resolved.""" 
    254 
    255 
    256class NoReferencedTableError(NoReferenceError): 
    257    """Raised by ``ForeignKey`` when the referred ``Table`` cannot be 
    258    located. 
    259 
    260    """ 
    261 
    262    def __init__(self, message, tname): 
    263        NoReferenceError.__init__(self, message) 
    264        self.table_name = tname 
    265 
    266    def __reduce__(self): 
    267        return self.__class__, (self.args[0], self.table_name) 
    268 
    269 
    270class NoReferencedColumnError(NoReferenceError): 
    271    """Raised by ``ForeignKey`` when the referred ``Column`` cannot be 
    272    located. 
    273 
    274    """ 
    275 
    276    def __init__(self, message, tname, cname): 
    277        NoReferenceError.__init__(self, message) 
    278        self.table_name = tname 
    279        self.column_name = cname 
    280 
    281    def __reduce__(self): 
    282        return ( 
    283            self.__class__, 
    284            (self.args[0], self.table_name, self.column_name), 
    285        ) 
    286 
    287 
    288class NoSuchTableError(InvalidRequestError): 
    289    """Table does not exist or is not visible to a connection.""" 
    290 
    291 
    292class UnreflectableTableError(InvalidRequestError): 
    293    """Table exists but can't be reflected for some reason. 
    294 
    295    .. versionadded:: 1.2 
    296 
    297    """ 
    298 
    299 
    300class UnboundExecutionError(InvalidRequestError): 
    301    """SQL was attempted without a database connection to execute it on.""" 
    302 
    303 
    304class DontWrapMixin(object): 
    305    """A mixin class which, when applied to a user-defined Exception class, 
    306    will not be wrapped inside of :exc:`.StatementError` if the error is 
    307    emitted within the process of executing a statement. 
    308 
    309    E.g.:: 
    310 
    311        from sqlalchemy.exc import DontWrapMixin 
    312 
    313        class MyCustomException(Exception, DontWrapMixin): 
    314            pass 
    315 
    316        class MySpecialType(TypeDecorator): 
    317            impl = String 
    318 
    319            def process_bind_param(self, value, dialect): 
    320                if value == 'invalid': 
    321                    raise MyCustomException("invalid!") 
    322 
    323    """ 
    324 
    325 
    326# Moved to orm.exc; compatibility definition installed by orm import until 0.6 
    327UnmappedColumnError = None 
    328 
    329 
    330class StatementError(SQLAlchemyError): 
    331    """An error occurred during execution of a SQL statement. 
    332 
    333    :class:`StatementError` wraps the exception raised 
    334    during execution, and features :attr:`.statement` 
    335    and :attr:`.params` attributes which supply context regarding 
    336    the specifics of the statement which had an issue. 
    337 
    338    The wrapped exception object is available in 
    339    the :attr:`.orig` attribute. 
    340 
    341    """ 
    342 
    343    statement = None 
    344    """The string SQL statement being invoked when this exception occurred.""" 
    345 
    346    params = None 
    347    """The parameter list being used when this exception occurred.""" 
    348 
    349    orig = None 
    350    """The DBAPI exception object.""" 
    351 
    352    ismulti = None 
    353 
    354    def __init__( 
    355        self, 
    356        message, 
    357        statement, 
    358        params, 
    359        orig, 
    360        hide_parameters=False, 
    361        code=None, 
    362        ismulti=None, 
    363    ): 
    364        SQLAlchemyError.__init__(self, message, code=code) 
    365        self.statement = statement 
    366        self.params = params 
    367        self.orig = orig 
    368        self.ismulti = ismulti 
    369        self.hide_parameters = hide_parameters 
    370        self.detail = [] 
    371 
    372    def add_detail(self, msg): 
    373        self.detail.append(msg) 
    374 
    375    def __reduce__(self): 
    376        return ( 
    377            self.__class__, 
    378            ( 
    379                self.args[0], 
    380                self.statement, 
    381                self.params, 
    382                self.orig, 
    383                self.hide_parameters, 
    384                self.ismulti, 
    385            ), 
    386        ) 
    387 
    388    @_preloaded.dependencies("sqlalchemy.sql.util") 
    389    def _sql_message(self, util, as_unicode): 
    390 
    391        details = [self._message(as_unicode=as_unicode)] 
    392        if self.statement: 
    393            if not as_unicode and not compat.py3k: 
    394                stmt_detail = "[SQL: %s]" % compat.safe_bytestring( 
    395                    self.statement 
    396                ) 
    397            else: 
    398                stmt_detail = "[SQL: %s]" % self.statement 
    399            details.append(stmt_detail) 
    400            if self.params: 
    401                if self.hide_parameters: 
    402                    details.append( 
    403                        "[SQL parameters hidden due to hide_parameters=True]" 
    404                    ) 
    405                else: 
    406                    params_repr = util._repr_params( 
    407                        self.params, 10, ismulti=self.ismulti 
    408                    ) 
    409                    details.append("[parameters: %r]" % params_repr) 
    410        code_str = self._code_str() 
    411        if code_str: 
    412            details.append(code_str) 
    413        return "\n".join(["(%s)" % det for det in self.detail] + details) 
    414 
    415 
    416class DBAPIError(StatementError): 
    417    """Raised when the execution of a database operation fails. 
    418 
    419    Wraps exceptions raised by the DB-API underlying the 
    420    database operation.  Driver-specific implementations of the standard 
    421    DB-API exception types are wrapped by matching sub-types of SQLAlchemy's 
    422    :class:`DBAPIError` when possible.  DB-API's ``Error`` type maps to 
    423    :class:`DBAPIError` in SQLAlchemy, otherwise the names are identical.  Note 
    424    that there is no guarantee that different DB-API implementations will 
    425    raise the same exception type for any given error condition. 
    426 
    427    :class:`DBAPIError` features :attr:`~.StatementError.statement` 
    428    and :attr:`~.StatementError.params` attributes which supply context 
    429    regarding the specifics of the statement which had an issue, for the 
    430    typical case when the error was raised within the context of 
    431    emitting a SQL statement. 
    432 
    433    The wrapped exception object is available in the 
    434    :attr:`~.StatementError.orig` attribute. Its type and properties are 
    435    DB-API implementation specific. 
    436 
    437    """ 
    438 
    439    code = "dbapi" 
    440 
    441    @classmethod 
    442    def instance( 
    443        cls, 
    444        statement, 
    445        params, 
    446        orig, 
    447        dbapi_base_err, 
    448        hide_parameters=False, 
    449        connection_invalidated=False, 
    450        dialect=None, 
    451        ismulti=None, 
    452    ): 
    453        # Don't ever wrap these, just return them directly as if 
    454        # DBAPIError didn't exist. 
    455        if ( 
    456            isinstance(orig, BaseException) and not isinstance(orig, Exception) 
    457        ) or isinstance(orig, DontWrapMixin): 
    458            return orig 
    459 
    460        if orig is not None: 
    461            # not a DBAPI error, statement is present. 
    462            # raise a StatementError 
    463            if isinstance(orig, SQLAlchemyError) and statement: 
    464                return StatementError( 
    465                    "(%s.%s) %s" 
    466                    % ( 
    467                        orig.__class__.__module__, 
    468                        orig.__class__.__name__, 
    469                        orig.args[0], 
    470                    ), 
    471                    statement, 
    472                    params, 
    473                    orig, 
    474                    hide_parameters=hide_parameters, 
    475                    code=orig.code, 
    476                    ismulti=ismulti, 
    477                ) 
    478            elif not isinstance(orig, dbapi_base_err) and statement: 
    479                return StatementError( 
    480                    "(%s.%s) %s" 
    481                    % ( 
    482                        orig.__class__.__module__, 
    483                        orig.__class__.__name__, 
    484                        orig, 
    485                    ), 
    486                    statement, 
    487                    params, 
    488                    orig, 
    489                    hide_parameters=hide_parameters, 
    490                    ismulti=ismulti, 
    491                ) 
    492 
    493            glob = globals() 
    494            for super_ in orig.__class__.__mro__: 
    495                name = super_.__name__ 
    496                if dialect: 
    497                    name = dialect.dbapi_exception_translation_map.get( 
    498                        name, name 
    499                    ) 
    500                if name in glob and issubclass(glob[name], DBAPIError): 
    501                    cls = glob[name] 
    502                    break 
    503 
    504        return cls( 
    505            statement, 
    506            params, 
    507            orig, 
    508            connection_invalidated=connection_invalidated, 
    509            hide_parameters=hide_parameters, 
    510            code=cls.code, 
    511            ismulti=ismulti, 
    512        ) 
    513 
    514    def __reduce__(self): 
    515        return ( 
    516            self.__class__, 
    517            ( 
    518                self.statement, 
    519                self.params, 
    520                self.orig, 
    521                self.hide_parameters, 
    522                self.connection_invalidated, 
    523                self.ismulti, 
    524            ), 
    525        ) 
    526 
    527    def __init__( 
    528        self, 
    529        statement, 
    530        params, 
    531        orig, 
    532        hide_parameters=False, 
    533        connection_invalidated=False, 
    534        code=None, 
    535        ismulti=None, 
    536    ): 
    537        try: 
    538            text = str(orig) 
    539        except Exception as e: 
    540            text = "Error in str() of DB-API-generated exception: " + str(e) 
    541        StatementError.__init__( 
    542            self, 
    543            "(%s.%s) %s" 
    544            % (orig.__class__.__module__, orig.__class__.__name__, text), 
    545            statement, 
    546            params, 
    547            orig, 
    548            hide_parameters, 
    549            code=code, 
    550            ismulti=ismulti, 
    551        ) 
    552        self.connection_invalidated = connection_invalidated 
    553 
    554 
    555class InterfaceError(DBAPIError): 
    556    """Wraps a DB-API InterfaceError.""" 
    557 
    558    code = "rvf5" 
    559 
    560 
    561class DatabaseError(DBAPIError): 
    562    """Wraps a DB-API DatabaseError.""" 
    563 
    564    code = "4xp6" 
    565 
    566 
    567class DataError(DatabaseError): 
    568    """Wraps a DB-API DataError.""" 
    569 
    570    code = "9h9h" 
    571 
    572 
    573class OperationalError(DatabaseError): 
    574    """Wraps a DB-API OperationalError.""" 
    575 
    576    code = "e3q8" 
    577 
    578 
    579class IntegrityError(DatabaseError): 
    580    """Wraps a DB-API IntegrityError.""" 
    581 
    582    code = "gkpj" 
    583 
    584 
    585class InternalError(DatabaseError): 
    586    """Wraps a DB-API InternalError.""" 
    587 
    588    code = "2j85" 
    589 
    590 
    591class ProgrammingError(DatabaseError): 
    592    """Wraps a DB-API ProgrammingError.""" 
    593 
    594    code = "f405" 
    595 
    596 
    597class NotSupportedError(DatabaseError): 
    598    """Wraps a DB-API NotSupportedError.""" 
    599 
    600    code = "tw8g" 
    601 
    602 
    603# Warnings 
    604 
    605 
    606class SADeprecationWarning(DeprecationWarning): 
    607    """Issued once per usage of a deprecated API.""" 
    608 
    609 
    610class SAPendingDeprecationWarning(PendingDeprecationWarning): 
    611    """Issued once per usage of a deprecated API.""" 
    612 
    613 
    614class SAWarning(RuntimeWarning): 
    615    """Issued at runtime."""