1# sqlite/pysqlite.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
8r"""
9.. dialect:: sqlite+pysqlite
10 :name: pysqlite
11 :dbapi: sqlite3
12 :connectstring: sqlite+pysqlite:///file_path
13 :url: http://docs.python.org/library/sqlite3.html
14
15 Note that ``pysqlite`` is the same driver as the ``sqlite3``
16 module included with the Python distribution.
17
18Driver
19------
20
21The ``sqlite3`` Python DBAPI is standard on all modern Python versions;
22for cPython and Pypy, no additional installation is necessary.
23
24
25Connect Strings
26---------------
27
28The file specification for the SQLite database is taken as the "database"
29portion of the URL. Note that the format of a SQLAlchemy url is::
30
31 driver://user:pass@host/database
32
33This means that the actual filename to be used starts with the characters to
34the **right** of the third slash. So connecting to a relative filepath
35looks like::
36
37 # relative path
38 e = create_engine('sqlite:///path/to/database.db')
39
40An absolute path, which is denoted by starting with a slash, means you
41need **four** slashes::
42
43 # absolute path
44 e = create_engine('sqlite:////path/to/database.db')
45
46To use a Windows path, regular drive specifications and backslashes can be
47used. Double backslashes are probably needed::
48
49 # absolute path on Windows
50 e = create_engine('sqlite:///C:\\path\\to\\database.db')
51
52The sqlite ``:memory:`` identifier is the default if no filepath is
53present. Specify ``sqlite://`` and nothing else::
54
55 # in-memory database
56 e = create_engine('sqlite://')
57
58.. _pysqlite_uri_connections:
59
60URI Connections
61^^^^^^^^^^^^^^^
62
63Modern versions of SQLite support an alternative system of connecting using a
64`driver level URI <https://www.sqlite.org/uri.html>`_, which has the advantage
65that additional driver-level arguments can be passed including options such as
66"read only". The Python sqlite3 driver supports this mode under modern Python
673 versions. The SQLAlchemy pysqlite driver supports this mode of use by
68specifing "uri=true" in the URL query string. The SQLite-level "URI" is kept
69as the "database" portion of the SQLAlchemy url (that is, following a slash)::
70
71 e = create_engine("sqlite:///file:path/to/database?mode=ro&uri=true")
72
73.. note:: The "uri=true" parameter must appear in the **query string**
74 of the URL. It will not currently work as expected if it is only
75 present in the :paramref:`_sa.create_engine.connect_args`
76 parameter dictionary.
77
78The logic reconciles the simultaneous presence of SQLAlchemy's query string and
79SQLite's query string by separating out the parameters that belong to the
80Python sqlite3 driver vs. those that belong to the SQLite URI. This is
81achieved through the use of a fixed list of parameters known to be accepted by
82the Python side of the driver. For example, to include a URL that indicates
83the Python sqlite3 "timeout" and "check_same_thread" parameters, along with the
84SQLite "mode" and "nolock" parameters, they can all be passed together on the
85query string::
86
87 e = create_engine(
88 "sqlite:///file:path/to/database?"
89 "check_same_thread=true&timeout=10&mode=ro&nolock=1&uri=true"
90 )
91
92Above, the pysqlite / sqlite3 DBAPI would be passed arguments as::
93
94 sqlite3.connect(
95 "file:path/to/database?mode=ro&nolock=1",
96 check_same_thread=True, timeout=10, uri=True
97 )
98
99Regarding future parameters added to either the Python or native drivers. new
100parameter names added to the SQLite URI scheme should be automatically
101accommodated by this scheme. New parameter names added to the Python driver
102side can be accommodated by specifying them in the
103:paramref:`_sa.create_engine.connect_args` dictionary,
104until dialect support is
105added by SQLAlchemy. For the less likely case that the native SQLite driver
106adds a new parameter name that overlaps with one of the existing, known Python
107driver parameters (such as "timeout" perhaps), SQLAlchemy's dialect would
108require adjustment for the URL scheme to continue to support this.
109
110As is always the case for all SQLAlchemy dialects, the entire "URL" process
111can be bypassed in :func:`_sa.create_engine` through the use of the
112:paramref:`_sa.create_engine.creator`
113parameter which allows for a custom callable
114that creates a Python sqlite3 driver level connection directly.
115
116.. versionadded:: 1.3.9
117
118.. seealso::
119
120 `Uniform Resource Identifiers <https://www.sqlite.org/uri.html>`_ - in
121 the SQLite documentation
122
123Compatibility with sqlite3 "native" date and datetime types
124-----------------------------------------------------------
125
126The pysqlite driver includes the sqlite3.PARSE_DECLTYPES and
127sqlite3.PARSE_COLNAMES options, which have the effect of any column
128or expression explicitly cast as "date" or "timestamp" will be converted
129to a Python date or datetime object. The date and datetime types provided
130with the pysqlite dialect are not currently compatible with these options,
131since they render the ISO date/datetime including microseconds, which
132pysqlite's driver does not. Additionally, SQLAlchemy does not at
133this time automatically render the "cast" syntax required for the
134freestanding functions "current_timestamp" and "current_date" to return
135datetime/date types natively. Unfortunately, pysqlite
136does not provide the standard DBAPI types in ``cursor.description``,
137leaving SQLAlchemy with no way to detect these types on the fly
138without expensive per-row type checks.
139
140Keeping in mind that pysqlite's parsing option is not recommended,
141nor should be necessary, for use with SQLAlchemy, usage of PARSE_DECLTYPES
142can be forced if one configures "native_datetime=True" on create_engine()::
143
144 engine = create_engine('sqlite://',
145 connect_args={'detect_types':
146 sqlite3.PARSE_DECLTYPES|sqlite3.PARSE_COLNAMES},
147 native_datetime=True
148 )
149
150With this flag enabled, the DATE and TIMESTAMP types (but note - not the
151DATETIME or TIME types...confused yet ?) will not perform any bind parameter
152or result processing. Execution of "func.current_date()" will return a string.
153"func.current_timestamp()" is registered as returning a DATETIME type in
154SQLAlchemy, so this function still receives SQLAlchemy-level result
155processing.
156
157.. _pysqlite_threading_pooling:
158
159Threading/Pooling Behavior
160---------------------------
161
162Pysqlite's default behavior is to prohibit the usage of a single connection
163in more than one thread. This is originally intended to work with older
164versions of SQLite that did not support multithreaded operation under
165various circumstances. In particular, older SQLite versions
166did not allow a ``:memory:`` database to be used in multiple threads
167under any circumstances.
168
169Pysqlite does include a now-undocumented flag known as
170``check_same_thread`` which will disable this check, however note that
171pysqlite connections are still not safe to use in concurrently in multiple
172threads. In particular, any statement execution calls would need to be
173externally mutexed, as Pysqlite does not provide for thread-safe propagation
174of error messages among other things. So while even ``:memory:`` databases
175can be shared among threads in modern SQLite, Pysqlite doesn't provide enough
176thread-safety to make this usage worth it.
177
178SQLAlchemy sets up pooling to work with Pysqlite's default behavior:
179
180* When a ``:memory:`` SQLite database is specified, the dialect by default
181 will use :class:`.SingletonThreadPool`. This pool maintains a single
182 connection per thread, so that all access to the engine within the current
183 thread use the same ``:memory:`` database - other threads would access a
184 different ``:memory:`` database.
185* When a file-based database is specified, the dialect will use
186 :class:`.NullPool` as the source of connections. This pool closes and
187 discards connections which are returned to the pool immediately. SQLite
188 file-based connections have extremely low overhead, so pooling is not
189 necessary. The scheme also prevents a connection from being used again in
190 a different thread and works best with SQLite's coarse-grained file locking.
191
192Using a Memory Database in Multiple Threads
193^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
194
195To use a ``:memory:`` database in a multithreaded scenario, the same
196connection object must be shared among threads, since the database exists
197only within the scope of that connection. The
198:class:`.StaticPool` implementation will maintain a single connection
199globally, and the ``check_same_thread`` flag can be passed to Pysqlite
200as ``False``::
201
202 from sqlalchemy.pool import StaticPool
203 engine = create_engine('sqlite://',
204 connect_args={'check_same_thread':False},
205 poolclass=StaticPool)
206
207Note that using a ``:memory:`` database in multiple threads requires a recent
208version of SQLite.
209
210Using Temporary Tables with SQLite
211^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
212
213Due to the way SQLite deals with temporary tables, if you wish to use a
214temporary table in a file-based SQLite database across multiple checkouts
215from the connection pool, such as when using an ORM :class:`.Session` where
216the temporary table should continue to remain after :meth:`.Session.commit` or
217:meth:`.Session.rollback` is called, a pool which maintains a single
218connection must be used. Use :class:`.SingletonThreadPool` if the scope is
219only needed within the current thread, or :class:`.StaticPool` is scope is
220needed within multiple threads for this case::
221
222 # maintain the same connection per thread
223 from sqlalchemy.pool import SingletonThreadPool
224 engine = create_engine('sqlite:///mydb.db',
225 poolclass=SingletonThreadPool)
226
227
228 # maintain the same connection across all threads
229 from sqlalchemy.pool import StaticPool
230 engine = create_engine('sqlite:///mydb.db',
231 poolclass=StaticPool)
232
233Note that :class:`.SingletonThreadPool` should be configured for the number
234of threads that are to be used; beyond that number, connections will be
235closed out in a non deterministic way.
236
237Unicode
238-------
239
240The pysqlite driver only returns Python ``unicode`` objects in result sets,
241never plain strings, and accommodates ``unicode`` objects within bound
242parameter values in all cases. Regardless of the SQLAlchemy string type in
243use, string-based result values will by Python ``unicode`` in Python 2.
244The :class:`.Unicode` type should still be used to indicate those columns that
245require unicode, however, so that non-``unicode`` values passed inadvertently
246will emit a warning. Pysqlite will emit an error if a non-``unicode`` string
247is passed containing non-ASCII characters.
248
249Dealing with Mixed String / Binary Columns in Python 3
250------------------------------------------------------
251
252The SQLite database is weakly typed, and as such it is possible when using
253binary values, which in Python 3 are represented as ``b'some string'``, that a
254particular SQLite database can have data values within different rows where
255some of them will be returned as a ``b''`` value by the Pysqlite driver, and
256others will be returned as Python strings, e.g. ``''`` values. This situation
257is not known to occur if the SQLAlchemy :class:`.LargeBinary` datatype is used
258consistently, however if a particular SQLite database has data that was
259inserted using the Pysqlite driver directly, or when using the SQLAlchemy
260:class:`.String` type which was later changed to :class:`.LargeBinary`, the
261table will not be consistently readable because SQLAlchemy's
262:class:`.LargeBinary` datatype does not handle strings so it has no way of
263"encoding" a value that is in string format.
264
265To deal with a SQLite table that has mixed string / binary data in the
266same column, use a custom type that will check each row individually::
267
268 # note this is Python 3 only
269
270 from sqlalchemy import String
271 from sqlalchemy import TypeDecorator
272
273 class MixedBinary(TypeDecorator):
274 impl = String
275
276 def process_result_value(self, value, dialect):
277 if isinstance(value, str):
278 value = bytes(value, 'utf-8')
279 elif value is not None:
280 value = bytes(value)
281
282 return value
283
284Then use the above ``MixedBinary`` datatype in the place where
285:class:`.LargeBinary` would normally be used.
286
287.. _pysqlite_serializable:
288
289Serializable isolation / Savepoints / Transactional DDL
290-------------------------------------------------------
291
292In the section :ref:`sqlite_concurrency`, we refer to the pysqlite
293driver's assortment of issues that prevent several features of SQLite
294from working correctly. The pysqlite DBAPI driver has several
295long-standing bugs which impact the correctness of its transactional
296behavior. In its default mode of operation, SQLite features such as
297SERIALIZABLE isolation, transactional DDL, and SAVEPOINT support are
298non-functional, and in order to use these features, workarounds must
299be taken.
300
301The issue is essentially that the driver attempts to second-guess the user's
302intent, failing to start transactions and sometimes ending them prematurely, in
303an effort to minimize the SQLite databases's file locking behavior, even
304though SQLite itself uses "shared" locks for read-only activities.
305
306SQLAlchemy chooses to not alter this behavior by default, as it is the
307long-expected behavior of the pysqlite driver; if and when the pysqlite
308driver attempts to repair these issues, that will be more of a driver towards
309defaults for SQLAlchemy.
310
311The good news is that with a few events, we can implement transactional
312support fully, by disabling pysqlite's feature entirely and emitting BEGIN
313ourselves. This is achieved using two event listeners::
314
315 from sqlalchemy import create_engine, event
316
317 engine = create_engine("sqlite:///myfile.db")
318
319 @event.listens_for(engine, "connect")
320 def do_connect(dbapi_connection, connection_record):
321 # disable pysqlite's emitting of the BEGIN statement entirely.
322 # also stops it from emitting COMMIT before any DDL.
323 dbapi_connection.isolation_level = None
324
325 @event.listens_for(engine, "begin")
326 def do_begin(conn):
327 # emit our own BEGIN
328 conn.execute("BEGIN")
329
330.. warning:: When using the above recipe, it is advised to not use the
331 :paramref:`.Connection.execution_options.isolation_level` setting on
332 :class:`_engine.Connection` and :func:`_sa.create_engine`
333 with the SQLite driver,
334 as this function necessarily will also alter the ".isolation_level" setting.
335
336
337Above, we intercept a new pysqlite connection and disable any transactional
338integration. Then, at the point at which SQLAlchemy knows that transaction
339scope is to begin, we emit ``"BEGIN"`` ourselves.
340
341When we take control of ``"BEGIN"``, we can also control directly SQLite's
342locking modes, introduced at
343`BEGIN TRANSACTION <http://sqlite.org/lang_transaction.html>`_,
344by adding the desired locking mode to our ``"BEGIN"``::
345
346 @event.listens_for(engine, "begin")
347 def do_begin(conn):
348 conn.execute("BEGIN EXCLUSIVE")
349
350.. seealso::
351
352 `BEGIN TRANSACTION <http://sqlite.org/lang_transaction.html>`_ -
353 on the SQLite site
354
355 `sqlite3 SELECT does not BEGIN a transaction <http://bugs.python.org/issue9924>`_ -
356 on the Python bug tracker
357
358 `sqlite3 module breaks transactions and potentially corrupts data <http://bugs.python.org/issue10740>`_ -
359 on the Python bug tracker
360
361
362""" # noqa
363
364import os
365
366from .base import DATE
367from .base import DATETIME
368from .base import SQLiteDialect
369from ... import exc
370from ... import pool
371from ... import types as sqltypes
372from ... import util
373
374
375class _SQLite_pysqliteTimeStamp(DATETIME):
376 def bind_processor(self, dialect):
377 if dialect.native_datetime:
378 return None
379 else:
380 return DATETIME.bind_processor(self, dialect)
381
382 def result_processor(self, dialect, coltype):
383 if dialect.native_datetime:
384 return None
385 else:
386 return DATETIME.result_processor(self, dialect, coltype)
387
388
389class _SQLite_pysqliteDate(DATE):
390 def bind_processor(self, dialect):
391 if dialect.native_datetime:
392 return None
393 else:
394 return DATE.bind_processor(self, dialect)
395
396 def result_processor(self, dialect, coltype):
397 if dialect.native_datetime:
398 return None
399 else:
400 return DATE.result_processor(self, dialect, coltype)
401
402
403class SQLiteDialect_pysqlite(SQLiteDialect):
404 default_paramstyle = "qmark"
405
406 colspecs = util.update_copy(
407 SQLiteDialect.colspecs,
408 {
409 sqltypes.Date: _SQLite_pysqliteDate,
410 sqltypes.TIMESTAMP: _SQLite_pysqliteTimeStamp,
411 },
412 )
413
414 if not util.py2k:
415 description_encoding = None
416
417 driver = "pysqlite"
418
419 @classmethod
420 def dbapi(cls):
421 if util.py2k:
422 try:
423 from pysqlite2 import dbapi2 as sqlite
424 except ImportError:
425 try:
426 from sqlite3 import dbapi2 as sqlite
427 except ImportError as e:
428 raise e
429 else:
430 from sqlite3 import dbapi2 as sqlite
431 return sqlite
432
433 @classmethod
434 def _is_url_file_db(cls, url):
435 if url.database and url.database != ":memory:":
436 return True
437 else:
438 return False
439
440 @classmethod
441 def get_pool_class(cls, url):
442 if cls._is_url_file_db(url):
443 return pool.NullPool
444 else:
445 return pool.SingletonThreadPool
446
447 def _get_server_version_info(self, connection):
448 return self.dbapi.sqlite_version_info
449
450 def set_isolation_level(self, connection, level):
451 if hasattr(connection, "connection"):
452 dbapi_connection = connection.connection
453 else:
454 dbapi_connection = connection
455
456 if level == "AUTOCOMMIT":
457 dbapi_connection.isolation_level = None
458 else:
459 dbapi_connection.isolation_level = ""
460 return super(SQLiteDialect_pysqlite, self).set_isolation_level(
461 connection, level
462 )
463
464 def create_connect_args(self, url):
465 if url.username or url.password or url.host or url.port:
466 raise exc.ArgumentError(
467 "Invalid SQLite URL: %s\n"
468 "Valid SQLite URL forms are:\n"
469 " sqlite:///:memory: (or, sqlite://)\n"
470 " sqlite:///relative/path/to/file.db\n"
471 " sqlite:////absolute/path/to/file.db" % (url,)
472 )
473
474 # theoretically, this list can be augmented, at least as far as
475 # parameter names accepted by sqlite3/pysqlite, using
476 # inspect.getfullargspec(). for the moment this seems like overkill
477 # as these parameters don't change very often, and as always,
478 # parameters passed to connect_args will always go to the
479 # sqlite3/pysqlite driver.
480 pysqlite_args = [
481 ("uri", bool),
482 ("timeout", float),
483 ("isolation_level", str),
484 ("detect_types", int),
485 ("check_same_thread", bool),
486 ("cached_statements", int),
487 ]
488 opts = url.query
489 pysqlite_opts = {}
490 for key, type_ in pysqlite_args:
491 util.coerce_kw_type(opts, key, type_, dest=pysqlite_opts)
492
493 if pysqlite_opts.get("uri", False):
494 uri_opts = opts.copy()
495 # here, we are actually separating the parameters that go to
496 # sqlite3/pysqlite vs. those that go the SQLite URI. What if
497 # two names conflict? again, this seems to be not the case right
498 # now, and in the case that new names are added to
499 # either side which overlap, again the sqlite3/pysqlite parameters
500 # can be passed through connect_args instead of in the URL.
501 # If SQLite native URIs add a parameter like "timeout" that
502 # we already have listed here for the python driver, then we need
503 # to adjust for that here.
504 for key, type_ in pysqlite_args:
505 uri_opts.pop(key, None)
506 filename = url.database
507 if uri_opts:
508 # sorting of keys is for unit test support
509 filename += "?" + (
510 "&".join(
511 "%s=%s" % (key, uri_opts[key])
512 for key in sorted(uri_opts)
513 )
514 )
515 else:
516 filename = url.database or ":memory:"
517 if filename != ":memory:":
518 filename = os.path.abspath(filename)
519
520 return ([filename], pysqlite_opts)
521
522 def is_disconnect(self, e, connection, cursor):
523 return isinstance(
524 e, self.dbapi.ProgrammingError
525 ) and "Cannot operate on a closed database." in str(e)
526
527
528dialect = SQLiteDialect_pysqlite