pysqlite.py 27 KB

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