Hide keyboard shortcuts

Hot-keys on this page

r m x p   toggle line displays

j k   next/prev highlighted chunk

0   (zero) top of page

1   (one) first highlighted chunk

1# sqlalchemy/exc.py 

2# Copyright (C) 2005-2020 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 compat 

17 

18_version_token = None 

19 

20 

21class SQLAlchemyError(Exception): 

22 """Generic error class.""" 

23 

24 code = None 

25 

26 def __init__(self, *arg, **kw): 

27 code = kw.pop("code", None) 

28 if code is not None: 

29 self.code = code 

30 super(SQLAlchemyError, self).__init__(*arg, **kw) 

31 

32 def _code_str(self): 

33 if not self.code: 

34 return "" 

35 else: 

36 return ( 

37 "(Background on this error at: " 

38 "http://sqlalche.me/e/%s/%s)" % (_version_token, self.code,) 

39 ) 

40 

41 def _message(self, as_unicode=compat.py3k): 

42 # rules: 

43 # 

44 # 1. under py2k, for __str__ return single string arg as it was 

45 # given without converting to unicode. for __unicode__ 

46 # do a conversion but check that it's not unicode already just in 

47 # case 

48 # 

49 # 2. under py3k, single arg string will usually be a unicode 

50 # object, but since __str__() must return unicode, check for 

51 # bytestring just in case 

52 # 

53 # 3. for multiple self.args, this is not a case in current 

54 # SQLAlchemy though this is happening in at least one known external 

55 # library, call str() which does a repr(). 

56 # 

57 if len(self.args) == 1: 

58 text = self.args[0] 

59 if as_unicode and isinstance(text, compat.binary_types): 

60 return compat.decode_backslashreplace(text, "utf-8") 

61 else: 

62 return self.args[0] 

63 else: 

64 # this is not a normal case within SQLAlchemy but is here for 

65 # compatibility with Exception.args - the str() comes out as 

66 # a repr() of the tuple 

67 return str(self.args) 

68 

69 def _sql_message(self, as_unicode): 

70 message = self._message(as_unicode) 

71 

72 if self.code: 

73 message = "%s %s" % (message, self._code_str()) 

74 

75 return message 

76 

77 def __str__(self): 

78 return self._sql_message(compat.py3k) 

79 

80 def __unicode__(self): 

81 return self._sql_message(as_unicode=True) 

82 

83 

84class ArgumentError(SQLAlchemyError): 

85 """Raised when an invalid or conflicting function argument is supplied. 

86 

87 This error generally corresponds to construction time state errors. 

88 

89 """ 

90 

91 

92class ObjectNotExecutableError(ArgumentError): 

93 """Raised when an object is passed to .execute() that can't be 

94 executed as SQL. 

95 

96 .. versionadded:: 1.1 

97 

98 """ 

99 

100 def __init__(self, target): 

101 super(ObjectNotExecutableError, self).__init__( 

102 "Not an executable object: %r" % target 

103 ) 

104 

105 

106class NoSuchModuleError(ArgumentError): 

107 """Raised when a dynamically-loaded module (usually a database dialect) 

108 of a particular name cannot be located.""" 

109 

110 

111class NoForeignKeysError(ArgumentError): 

112 """Raised when no foreign keys can be located between two selectables 

113 during a join.""" 

114 

115 

116class AmbiguousForeignKeysError(ArgumentError): 

117 """Raised when more than one foreign key matching can be located 

118 between two selectables during a join.""" 

119 

120 

121class CircularDependencyError(SQLAlchemyError): 

122 """Raised by topological sorts when a circular dependency is detected. 

123 

124 There are two scenarios where this error occurs: 

125 

126 * In a Session flush operation, if two objects are mutually dependent 

127 on each other, they can not be inserted or deleted via INSERT or 

128 DELETE statements alone; an UPDATE will be needed to post-associate 

129 or pre-deassociate one of the foreign key constrained values. 

130 The ``post_update`` flag described at :ref:`post_update` can resolve 

131 this cycle. 

132 * In a :attr:`_schema.MetaData.sorted_tables` operation, two 

133 :class:`_schema.ForeignKey` 

134 or :class:`_schema.ForeignKeyConstraint` objects mutually refer to each 

135 other. Apply the ``use_alter=True`` flag to one or both, 

136 see :ref:`use_alter`. 

137 

138 """ 

139 

140 def __init__(self, message, cycles, edges, msg=None, code=None): 

141 if msg is None: 

142 message += " (%s)" % ", ".join(repr(s) for s in cycles) 

143 else: 

144 message = msg 

145 SQLAlchemyError.__init__(self, message, code=code) 

146 self.cycles = cycles 

147 self.edges = edges 

148 

149 def __reduce__(self): 

150 return self.__class__, (None, self.cycles, self.edges, self.args[0]) 

151 

152 

153class CompileError(SQLAlchemyError): 

154 """Raised when an error occurs during SQL compilation""" 

155 

156 

157class UnsupportedCompilationError(CompileError): 

158 """Raised when an operation is not supported by the given compiler. 

159 

160 .. seealso:: 

161 

162 :ref:`faq_sql_expression_string` 

163 

164 :ref:`error_l7de` 

165 """ 

166 

167 code = "l7de" 

168 

169 def __init__(self, compiler, element_type): 

170 super(UnsupportedCompilationError, self).__init__( 

171 "Compiler %r can't render element of type %s" 

172 % (compiler, element_type) 

173 ) 

174 

175 

176class IdentifierError(SQLAlchemyError): 

177 """Raised when a schema name is beyond the max character limit""" 

178 

179 

180class DisconnectionError(SQLAlchemyError): 

181 """A disconnect is detected on a raw DB-API connection. 

182 

183 This error is raised and consumed internally by a connection pool. It can 

184 be raised by the :meth:`_events.PoolEvents.checkout` 

185 event so that the host pool 

186 forces a retry; the exception will be caught three times in a row before 

187 the pool gives up and raises :class:`~sqlalchemy.exc.InvalidRequestError` 

188 regarding the connection attempt. 

189 

190 """ 

191 

192 invalidate_pool = False 

193 

194 

195class InvalidatePoolError(DisconnectionError): 

196 """Raised when the connection pool should invalidate all stale connections. 

197 

198 A subclass of :class:`_exc.DisconnectionError` that indicates that the 

199 disconnect situation encountered on the connection probably means the 

200 entire pool should be invalidated, as the database has been restarted. 

201 

202 This exception will be handled otherwise the same way as 

203 :class:`_exc.DisconnectionError`, allowing three attempts to reconnect 

204 before giving up. 

205 

206 .. versionadded:: 1.2 

207 

208 """ 

209 

210 invalidate_pool = True 

211 

212 

213class TimeoutError(SQLAlchemyError): # noqa 

214 """Raised when a connection pool times out on getting a connection.""" 

215 

216 

217class InvalidRequestError(SQLAlchemyError): 

218 """SQLAlchemy was asked to do something it can't do. 

219 

220 This error generally corresponds to runtime state errors. 

221 

222 """ 

223 

224 

225class NoInspectionAvailable(InvalidRequestError): 

226 """A subject passed to :func:`sqlalchemy.inspection.inspect` produced 

227 no context for inspection.""" 

228 

229 

230class ResourceClosedError(InvalidRequestError): 

231 """An operation was requested from a connection, cursor, or other 

232 object that's in a closed state.""" 

233 

234 

235class NoSuchColumnError(KeyError, InvalidRequestError): 

236 """A nonexistent column is requested from a ``RowProxy``.""" 

237 

238 

239class NoReferenceError(InvalidRequestError): 

240 """Raised by ``ForeignKey`` to indicate a reference cannot be resolved.""" 

241 

242 

243class NoReferencedTableError(NoReferenceError): 

244 """Raised by ``ForeignKey`` when the referred ``Table`` cannot be 

245 located. 

246 

247 """ 

248 

249 def __init__(self, message, tname): 

250 NoReferenceError.__init__(self, message) 

251 self.table_name = tname 

252 

253 def __reduce__(self): 

254 return self.__class__, (self.args[0], self.table_name) 

255 

256 

257class NoReferencedColumnError(NoReferenceError): 

258 """Raised by ``ForeignKey`` when the referred ``Column`` cannot be 

259 located. 

260 

261 """ 

262 

263 def __init__(self, message, tname, cname): 

264 NoReferenceError.__init__(self, message) 

265 self.table_name = tname 

266 self.column_name = cname 

267 

268 def __reduce__(self): 

269 return ( 

270 self.__class__, 

271 (self.args[0], self.table_name, self.column_name), 

272 ) 

273 

274 

275class NoSuchTableError(InvalidRequestError): 

276 """Table does not exist or is not visible to a connection.""" 

277 

278 

279class UnreflectableTableError(InvalidRequestError): 

280 """Table exists but can't be reflected for some reason. 

281 

282 .. versionadded:: 1.2 

283 

284 """ 

285 

286 

287class UnboundExecutionError(InvalidRequestError): 

288 """SQL was attempted without a database connection to execute it on.""" 

289 

290 

291class DontWrapMixin(object): 

292 """A mixin class which, when applied to a user-defined Exception class, 

293 will not be wrapped inside of :exc:`.StatementError` if the error is 

294 emitted within the process of executing a statement. 

295 

296 E.g.:: 

297 

298 from sqlalchemy.exc import DontWrapMixin 

299 

300 class MyCustomException(Exception, DontWrapMixin): 

301 pass 

302 

303 class MySpecialType(TypeDecorator): 

304 impl = String 

305 

306 def process_bind_param(self, value, dialect): 

307 if value == 'invalid': 

308 raise MyCustomException("invalid!") 

309 

310 """ 

311 

312 

313# Moved to orm.exc; compatibility definition installed by orm import until 0.6 

314UnmappedColumnError = None 

315 

316 

317class StatementError(SQLAlchemyError): 

318 """An error occurred during execution of a SQL statement. 

319 

320 :class:`StatementError` wraps the exception raised 

321 during execution, and features :attr:`.statement` 

322 and :attr:`.params` attributes which supply context regarding 

323 the specifics of the statement which had an issue. 

324 

325 The wrapped exception object is available in 

326 the :attr:`.orig` attribute. 

327 

328 """ 

329 

330 statement = None 

331 """The string SQL statement being invoked when this exception occurred.""" 

332 

333 params = None 

334 """The parameter list being used when this exception occurred.""" 

335 

336 orig = None 

337 """The DBAPI exception object.""" 

338 

339 ismulti = None 

340 

341 def __init__( 

342 self, 

343 message, 

344 statement, 

345 params, 

346 orig, 

347 hide_parameters=False, 

348 code=None, 

349 ismulti=None, 

350 ): 

351 SQLAlchemyError.__init__(self, message, code=code) 

352 self.statement = statement 

353 self.params = params 

354 self.orig = orig 

355 self.ismulti = ismulti 

356 self.hide_parameters = hide_parameters 

357 self.detail = [] 

358 

359 def add_detail(self, msg): 

360 self.detail.append(msg) 

361 

362 def __reduce__(self): 

363 return ( 

364 self.__class__, 

365 ( 

366 self.args[0], 

367 self.statement, 

368 self.params, 

369 self.orig, 

370 self.hide_parameters, 

371 self.ismulti, 

372 ), 

373 ) 

374 

375 def _sql_message(self, as_unicode): 

376 from sqlalchemy.sql import util 

377 

378 details = [self._message(as_unicode=as_unicode)] 

379 if self.statement: 

380 if not as_unicode and not compat.py3k: 

381 stmt_detail = "[SQL: %s]" % compat.safe_bytestring( 

382 self.statement 

383 ) 

384 else: 

385 stmt_detail = "[SQL: %s]" % self.statement 

386 details.append(stmt_detail) 

387 if self.params: 

388 if self.hide_parameters: 

389 details.append( 

390 "[SQL parameters hidden due to hide_parameters=True]" 

391 ) 

392 else: 

393 params_repr = util._repr_params( 

394 self.params, 10, ismulti=self.ismulti 

395 ) 

396 details.append("[parameters: %r]" % params_repr) 

397 code_str = self._code_str() 

398 if code_str: 

399 details.append(code_str) 

400 return "\n".join(["(%s)" % det for det in self.detail] + details) 

401 

402 

403class DBAPIError(StatementError): 

404 """Raised when the execution of a database operation fails. 

405 

406 Wraps exceptions raised by the DB-API underlying the 

407 database operation. Driver-specific implementations of the standard 

408 DB-API exception types are wrapped by matching sub-types of SQLAlchemy's 

409 :class:`DBAPIError` when possible. DB-API's ``Error`` type maps to 

410 :class:`DBAPIError` in SQLAlchemy, otherwise the names are identical. Note 

411 that there is no guarantee that different DB-API implementations will 

412 raise the same exception type for any given error condition. 

413 

414 :class:`DBAPIError` features :attr:`~.StatementError.statement` 

415 and :attr:`~.StatementError.params` attributes which supply context 

416 regarding the specifics of the statement which had an issue, for the 

417 typical case when the error was raised within the context of 

418 emitting a SQL statement. 

419 

420 The wrapped exception object is available in the 

421 :attr:`~.StatementError.orig` attribute. Its type and properties are 

422 DB-API implementation specific. 

423 

424 """ 

425 

426 code = "dbapi" 

427 

428 @classmethod 

429 def instance( 

430 cls, 

431 statement, 

432 params, 

433 orig, 

434 dbapi_base_err, 

435 hide_parameters=False, 

436 connection_invalidated=False, 

437 dialect=None, 

438 ismulti=None, 

439 ): 

440 # Don't ever wrap these, just return them directly as if 

441 # DBAPIError didn't exist. 

442 if ( 

443 isinstance(orig, BaseException) and not isinstance(orig, Exception) 

444 ) or isinstance(orig, DontWrapMixin): 

445 return orig 

446 

447 if orig is not None: 

448 # not a DBAPI error, statement is present. 

449 # raise a StatementError 

450 if isinstance(orig, SQLAlchemyError) and statement: 

451 return StatementError( 

452 "(%s.%s) %s" 

453 % ( 

454 orig.__class__.__module__, 

455 orig.__class__.__name__, 

456 orig.args[0], 

457 ), 

458 statement, 

459 params, 

460 orig, 

461 hide_parameters=hide_parameters, 

462 code=orig.code, 

463 ismulti=ismulti, 

464 ) 

465 elif not isinstance(orig, dbapi_base_err) and statement: 

466 return StatementError( 

467 "(%s.%s) %s" 

468 % ( 

469 orig.__class__.__module__, 

470 orig.__class__.__name__, 

471 orig, 

472 ), 

473 statement, 

474 params, 

475 orig, 

476 hide_parameters=hide_parameters, 

477 ismulti=ismulti, 

478 ) 

479 

480 glob = globals() 

481 for super_ in orig.__class__.__mro__: 

482 name = super_.__name__ 

483 if dialect: 

484 name = dialect.dbapi_exception_translation_map.get( 

485 name, name 

486 ) 

487 if name in glob and issubclass(glob[name], DBAPIError): 

488 cls = glob[name] 

489 break 

490 

491 return cls( 

492 statement, 

493 params, 

494 orig, 

495 connection_invalidated=connection_invalidated, 

496 hide_parameters=hide_parameters, 

497 code=cls.code, 

498 ismulti=ismulti, 

499 ) 

500 

501 def __reduce__(self): 

502 return ( 

503 self.__class__, 

504 ( 

505 self.statement, 

506 self.params, 

507 self.orig, 

508 self.hide_parameters, 

509 self.connection_invalidated, 

510 self.ismulti, 

511 ), 

512 ) 

513 

514 def __init__( 

515 self, 

516 statement, 

517 params, 

518 orig, 

519 hide_parameters=False, 

520 connection_invalidated=False, 

521 code=None, 

522 ismulti=None, 

523 ): 

524 try: 

525 text = str(orig) 

526 except Exception as e: 

527 text = "Error in str() of DB-API-generated exception: " + str(e) 

528 StatementError.__init__( 

529 self, 

530 "(%s.%s) %s" 

531 % (orig.__class__.__module__, orig.__class__.__name__, text), 

532 statement, 

533 params, 

534 orig, 

535 hide_parameters, 

536 code=code, 

537 ismulti=ismulti, 

538 ) 

539 self.connection_invalidated = connection_invalidated 

540 

541 

542class InterfaceError(DBAPIError): 

543 """Wraps a DB-API InterfaceError.""" 

544 

545 code = "rvf5" 

546 

547 

548class DatabaseError(DBAPIError): 

549 """Wraps a DB-API DatabaseError.""" 

550 

551 code = "4xp6" 

552 

553 

554class DataError(DatabaseError): 

555 """Wraps a DB-API DataError.""" 

556 

557 code = "9h9h" 

558 

559 

560class OperationalError(DatabaseError): 

561 """Wraps a DB-API OperationalError.""" 

562 

563 code = "e3q8" 

564 

565 

566class IntegrityError(DatabaseError): 

567 """Wraps a DB-API IntegrityError.""" 

568 

569 code = "gkpj" 

570 

571 

572class InternalError(DatabaseError): 

573 """Wraps a DB-API InternalError.""" 

574 

575 code = "2j85" 

576 

577 

578class ProgrammingError(DatabaseError): 

579 """Wraps a DB-API ProgrammingError.""" 

580 

581 code = "f405" 

582 

583 

584class NotSupportedError(DatabaseError): 

585 """Wraps a DB-API NotSupportedError.""" 

586 

587 code = "tw8g" 

588 

589 

590# Warnings 

591 

592 

593class SADeprecationWarning(DeprecationWarning): 

594 """Issued once per usage of a deprecated API.""" 

595 

596 

597class SAPendingDeprecationWarning(PendingDeprecationWarning): 

598 """Issued once per usage of a deprecated API.""" 

599 

600 

601class SAWarning(RuntimeWarning): 

602 """Issued at runtime."""