classification
Title: SQLite error code not exposed to python
Type: enhancement Stage: patch review
Components: Library (Lib) Versions: Python 3.7
process
Status: open Resolution:
Dependencies: Superseder:
Assigned To: ghaering Nosy List: danielsh, ezio.melotti, ghaering, levkivskyi, palaviv, sleepycal, torsten
Priority: normal Keywords: patch

Created on 2012-11-01 10:14 by torsten, last changed 2017-04-13 19:00 by Mariatta.

Files
File name Uploaded Description Edit
i16379-v1.diff danielsh, 2012-12-28 21:09 review
i16379-v2.diff danielsh, 2012-12-30 18:36 review
i16379-v3.diff danielsh, 2012-12-30 18:56 review
16379.patch palaviv, 2016-08-24 20:51 review
16379-2.patch palaviv, 2016-08-31 20:03 review
16379-3.patch palaviv, 2016-09-01 19:00 review
Pull Requests
URL Status Linked Edit
PR 1108 open palaviv, 2017-04-13 10:02
Messages (12)
msg174395 - (view) Author: Torsten Landschoff (torsten) * Date: 2012-11-01 10:14
The sqlite3 module does not expose the sqlite3 error codes to python. This makes it impossible to detect specific error conditions directly.

Case in point: If a user selects some random file as the database in our application, we can not detect that it is not a valid database file:

$ /opt/python3/bin/python3
Python 3.4.0a0 (default:2d6eec5d01f7, Nov  1 2012, 10:47:27) 
[GCC 4.6.3] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> import sqlite3
>>> conn = sqlite3.connect("/etc/passwd")
>>> from pprint import pprint
>>> try:
...     conn.execute("select * from random_table")
... except Exception as e:
...     pprint({name: getattr(e, name) for name in dir(e)})
...     raise
... 
{'__cause__': None,
 '__class__': <class 'sqlite3.DatabaseError'>,
 '__context__': None,
 '__delattr__': <method-wrapper '__delattr__' of DatabaseError object at 0x7ffc9a13b050>,
 '__dict__': {},
 '__dir__': <built-in method __dir__ of DatabaseError object at 0x7ffc9a13b050>,
 '__doc__': None,
 '__eq__': <method-wrapper '__eq__' of DatabaseError object at 0x7ffc9a13b050>,
 '__format__': <built-in method __format__ of DatabaseError object at 0x7ffc9a13b050>,
 '__ge__': <method-wrapper '__ge__' of DatabaseError object at 0x7ffc9a13b050>,
 '__getattribute__': <method-wrapper '__getattribute__' of DatabaseError object at 0x7ffc9a13b050>,
 '__gt__': <method-wrapper '__gt__' of DatabaseError object at 0x7ffc9a13b050>,
 '__hash__': <method-wrapper '__hash__' of DatabaseError object at 0x7ffc9a13b050>,
 '__init__': <method-wrapper '__init__' of DatabaseError object at 0x7ffc9a13b050>,
 '__le__': <method-wrapper '__le__' of DatabaseError object at 0x7ffc9a13b050>,
 '__lt__': <method-wrapper '__lt__' of DatabaseError object at 0x7ffc9a13b050>,
 '__module__': 'sqlite3',
 '__ne__': <method-wrapper '__ne__' of DatabaseError object at 0x7ffc9a13b050>,
 '__new__': <built-in method __new__ of type object at 0x8267e0>,
 '__reduce__': <built-in method __reduce__ of DatabaseError object at 0x7ffc9a13b050>,
 '__reduce_ex__': <built-in method __reduce_ex__ of DatabaseError object at 0x7ffc9a13b050>,
 '__repr__': <method-wrapper '__repr__' of DatabaseError object at 0x7ffc9a13b050>,
 '__setattr__': <method-wrapper '__setattr__' of DatabaseError object at 0x7ffc9a13b050>,
 '__setstate__': <built-in method __setstate__ of DatabaseError object at 0x7ffc9a13b050>,
 '__sizeof__': <built-in method __sizeof__ of DatabaseError object at 0x7ffc9a13b050>,
 '__str__': <method-wrapper '__str__' of DatabaseError object at 0x7ffc9a13b050>,
 '__subclasshook__': <built-in method __subclasshook__ of type object at 0x1238770>,
 '__suppress_context__': False,
 '__traceback__': <traceback object at 0x7ffc9a138cf8>,
 '__weakref__': None,
 'args': ('file is encrypted or is not a database',),
 'with_traceback': <built-in method with_traceback of DatabaseError object at 0x7ffc9a13b050>}
Traceback (most recent call last):
  File "<stdin>", line 2, in <module>
sqlite3.DatabaseError: file is encrypted or is not a database
>>>

Currently, one can only match the error message, with is bad programming style.

The error code for this error is SQLITE_NOTADB, as found in the function sqlite3ErrStr when searching for the message in SQLite's main.c at http://www.sqlite.org/src/artifact/02255cf1da50956c5427c469abddb15bccc4ba09

Unfortunately, the sqlite3 module does not expose the error code itself (neither the actual error code nor the defined error codes) in any way. Errors are handled in Modules/_sqlite/util.c:

http://hg.python.org/cpython/file/2d6eec5d01f7/Modules/_sqlite/util.c#l99

I would like to have the defined error codes available in some mapping inside the sqlite3 module as well as the actual error code inside every sqlite exception e as e.sqlite_errcode
msg178426 - (view) Author: Daniel Shahaf (danielsh) Date: 2012-12-28 21:09
Attached patch that:

- Adds sqlite3.SQLITE_OK constants
- Adds sqlite3.errorcode dictionary
- Adds "sqlite_errcode" attribute to sqlite.Error instances

The patch does not add support for extended result codes
(http://www.sqlite.org/c3ref/c_abort_rollback.html).
msg178429 - (view) Author: Daniel Shahaf (danielsh) Date: 2012-12-28 21:13
I didn't compile-test the Doc/ part of the patch.
msg178597 - (view) Author: Daniel Shahaf (danielsh) Date: 2012-12-30 18:36
New patch, with better docs and less error leaks, per Ezio's review.
msg178598 - (view) Author: Daniel Shahaf (danielsh) Date: 2012-12-30 18:40
A couple of random eyebrow-raisers I noticed while working on v2:

- sqlite3.Warning is a subclass of Exception, rather than sqlite3.Error
  or builtins.Warning.  (Also, the docs say "will raise a Warning",
  intending to refer to sqlite3.Warning, but the lack of markup makes
  this ambiguous --- it would be interpreted as a reference to
  builtins.Warning).

- If _PyUnicode_AsStringAndSize() returns NULL, the code sets a
  sqlite3.Warning exception without first checking what exception is
  already set.  (grep for PYSQLITE_SQL_WRONG_TYPE)
msg178601 - (view) Author: Daniel Shahaf (danielsh) Date: 2012-12-30 18:56
New patch fixing indentation of versionadded markup.
msg245819 - (view) Author: Cal Leeming (sleepycal) Date: 2015-06-25 16:22
Any update on this? Still seems to be a problem as of 3.4.0.
msg249010 - (view) Author: Gerhard Häring (ghaering) * (Python committer) Date: 2015-08-23 16:16
I propose to also set the SQLite extended error code if this is implemented.

What's the reasoning behind offering a error code to name mapping? This seem problematic to me. In case a newer SQLite version introduces a new error code, this error code cannot be found in the mapping. I propose to leave this out in order to not have this problem.

Otherwise we will have people depending on any error code being able to be found in this mapping.
msg249044 - (view) Author: Daniel Shahaf (danielsh) Date: 2015-08-24 10:49
> What's the reasoning behind offering a error code to name mapping?

Allowing code that runs into an error to print the error name rather than its
numeric value.  This saves whoever reads the error message having to look it up
himself.

> his seem problematic to me. In case a newer SQLite version introduces a new
> error code, this error code cannot be found in the mapping. I propose to
> leave this out in order to not have this problem.
> 
> Otherwise we will have people depending on any error code being able to be
> found in this mapping.

Then people shouldn't depend on the mapping being complete.  Let's keep the
mapping and document that people should only use it as
`sqlite3.errorcode.get(...)`, never as `sqlite3.errorcode[...]`.

Or if that's not a good API, we could encapsulate the incompleteness of the
mapping into a small wrapper function:

   def something(errorcode):
       return sqlite3.errorcode.get(errorcode,
                                    "<sqlite3 error {!d}>".format(errorcode))
msg257309 - (view) Author: Ezio Melotti (ezio.melotti) * (Python committer) Date: 2016-01-01 21:41
I think the error message should display both the numeric code and also the error name if available.
Instead of using a mapping, perhaps an Enum could be used instead.
msg273593 - (view) Author: Aviv Palivoda (palaviv) * Date: 2016-08-24 20:51
Attached is a patch based on Daniel last patch with the following changes:

* There is no errorcode mapping.
* The exception object has a error_name attribute.

I think this two changes should solve the API problems raised by Ezio and Gerhard about the error code mapping.

> I propose to also set the SQLite extended error code if this is implemented.

I think that this should be done in a separate patch. I will start working on the extended error code and will upload a patch to issue 24139.
msg274064 - (view) Author: Aviv Palivoda (palaviv) * Date: 2016-08-31 20:03
Attached is a new patch with the encoding problem fixed.
History
Date User Action Args
2017-04-13 19:00:53Mariattasetversions: + Python 3.7, - Python 3.6
2017-04-13 10:02:14palavivsetpull_requests: + pull_request1248
2016-09-01 19:00:52palavivsetfiles: + 16379-3.patch
2016-08-31 20:03:45palavivsetfiles: + 16379-2.patch

messages: + msg274064
2016-08-24 20:51:54palavivsetfiles: + 16379.patch
nosy: + palaviv
messages: + msg273593

2016-01-01 21:41:58ezio.melottisetstage: needs patch -> patch review
messages: + msg257309
versions: + Python 3.6, - Python 3.4
2015-08-24 10:49:22danielshsetmessages: + msg249044
2015-08-23 16:16:33ghaeringsetmessages: + msg249010
2015-08-19 09:54:12ghaeringsetassignee: ghaering
2015-06-25 18:07:44levkivskyisetnosy: + levkivskyi
2015-06-25 16:22:14sleepycalsetnosy: + sleepycal
messages: + msg245819
2012-12-30 18:56:03danielshsetfiles: + i16379-v3.diff

messages: + msg178601
2012-12-30 18:40:28danielshsetmessages: + msg178598
2012-12-30 18:36:54danielshsetfiles: + i16379-v2.diff

messages: + msg178597
2012-12-28 21:13:11danielshsetmessages: + msg178429
2012-12-28 21:09:17danielshsetfiles: + i16379-v1.diff
keywords: + patch
messages: + msg178426
2012-12-28 15:53:46danielshsetnosy: + danielsh
2012-11-02 19:02:32ezio.melottisetnosy: + ghaering, ezio.melotti

stage: needs patch
2012-11-01 10:14:38torstencreate