Packages update
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
# sqlite/pysqlite.py
|
||||
# Copyright (C) 2005-2011 the SQLAlchemy authors and contributors <see AUTHORS file>
|
||||
# Copyright (C) 2005-2012 the SQLAlchemy authors and contributors <see AUTHORS file>
|
||||
#
|
||||
# This module is part of SQLAlchemy and is released under
|
||||
# the MIT License: http://www.opensource.org/licenses/mit-license.php
|
||||
@@ -93,61 +93,130 @@ processing. Execution of "func.current_date()" will return a string.
|
||||
"func.current_timestamp()" is registered as returning a DATETIME type in
|
||||
SQLAlchemy, so this function still receives SQLAlchemy-level result processing.
|
||||
|
||||
Threading Behavior
|
||||
------------------
|
||||
Threading/Pooling Behavior
|
||||
---------------------------
|
||||
|
||||
Pysqlite connections do not support being moved between threads, unless
|
||||
the ``check_same_thread`` Pysqlite flag is set to ``False``. In addition,
|
||||
when using an in-memory SQLite database, the full database exists only within
|
||||
the scope of a single connection. It is reported that an in-memory
|
||||
database does not support being shared between threads regardless of the
|
||||
``check_same_thread`` flag - which means that a multithreaded
|
||||
application **cannot** share data from a ``:memory:`` database across threads
|
||||
unless access to the connection is limited to a single worker thread which communicates
|
||||
through a queueing mechanism to concurrent threads.
|
||||
Pysqlite's default behavior is to prohibit the usage of a single connection
|
||||
in more than one thread. This is controlled by the ``check_same_thread``
|
||||
Pysqlite flag. This default is intended to work with older versions
|
||||
of SQLite that did not support multithreaded operation under
|
||||
various circumstances. In particular, older SQLite versions
|
||||
did not allow a ``:memory:`` database to be used in multiple threads
|
||||
under any circumstances.
|
||||
|
||||
To provide a default which accomodates SQLite's default threading capabilities
|
||||
somewhat reasonably, the SQLite dialect will specify that the :class:`~sqlalchemy.pool.SingletonThreadPool`
|
||||
be used by default. This pool maintains a single SQLite connection per thread
|
||||
that is held open up to a count of five concurrent threads. When more than five threads
|
||||
are used, a cleanup mechanism will dispose of excess unused connections.
|
||||
SQLAlchemy sets up pooling to work with Pysqlite's default behavior:
|
||||
|
||||
Two optional pool implementations that may be appropriate for particular SQLite usage scenarios:
|
||||
* When a ``:memory:`` SQLite database is specified, the dialect by default will use
|
||||
:class:`.SingletonThreadPool`. This pool maintains a single connection per
|
||||
thread, so that all access to the engine within the current thread use the
|
||||
same ``:memory:`` database - other threads would access a different
|
||||
``:memory:`` database.
|
||||
* When a file-based database is specified, the dialect will use :class:`.NullPool`
|
||||
as the source of connections. This pool closes and discards connections
|
||||
which are returned to the pool immediately. SQLite file-based connections
|
||||
have extremely low overhead, so pooling is not necessary. The scheme also
|
||||
prevents a connection from being used again in a different thread and works
|
||||
best with SQLite's coarse-grained file locking.
|
||||
|
||||
* the :class:`sqlalchemy.pool.StaticPool` might be appropriate for a multithreaded
|
||||
application using an in-memory database, assuming the threading issues inherent in
|
||||
pysqlite are somehow accomodated for. This pool holds persistently onto a single connection
|
||||
which is never closed, and is returned for all requests.
|
||||
.. note::
|
||||
|
||||
The default selection of :class:`.NullPool` for SQLite file-based databases
|
||||
is new in SQLAlchemy 0.7. Previous versions
|
||||
select :class:`.SingletonThreadPool` by
|
||||
default for all SQLite databases.
|
||||
|
||||
* the :class:`sqlalchemy.pool.NullPool` might be appropriate for an application that
|
||||
makes use of a file-based sqlite database. This pool disables any actual "pooling"
|
||||
behavior, and simply opens and closes real connections corresonding to the :func:`connect()`
|
||||
and :func:`close()` methods. SQLite can "connect" to a particular file with very high
|
||||
efficiency, so this option may actually perform better without the extra overhead
|
||||
of :class:`SingletonThreadPool`. NullPool will of course render a ``:memory:`` connection
|
||||
useless since the database would be lost as soon as the connection is "returned" to the pool.
|
||||
Modern versions of SQLite no longer have the threading restrictions, and assuming
|
||||
the sqlite3/pysqlite library was built with SQLite's default threading mode
|
||||
of "Serialized", even ``:memory:`` databases can be shared among threads.
|
||||
|
||||
Using a Memory Database in Multiple Threads
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
To use a ``:memory:`` database in a multithreaded scenario, the same connection
|
||||
object must be shared among threads, since the database exists
|
||||
only within the scope of that connection. The :class:`.StaticPool` implementation
|
||||
will maintain a single connection globally, and the ``check_same_thread`` flag
|
||||
can be passed to Pysqlite as ``False``::
|
||||
|
||||
from sqlalchemy.pool import StaticPool
|
||||
engine = create_engine('sqlite://',
|
||||
connect_args={'check_same_thread':False},
|
||||
poolclass=StaticPool)
|
||||
|
||||
Note that using a ``:memory:`` database in multiple threads requires a recent
|
||||
version of SQLite.
|
||||
|
||||
Using Temporary Tables with SQLite
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
Due to the way SQLite deals with temporary tables, if you wish to use a temporary table
|
||||
in a file-based SQLite database across multiple checkouts from the connection pool, such
|
||||
as when using an ORM :class:`.Session` where the temporary table should continue to remain
|
||||
after :meth:`.commit` or :meth:`.rollback` is called,
|
||||
a pool which maintains a single connection must be used. Use :class:`.SingletonThreadPool`
|
||||
if the scope is only needed within the current thread, or :class:`.StaticPool` is scope is
|
||||
needed within multiple threads for this case::
|
||||
|
||||
# maintain the same connection per thread
|
||||
from sqlalchemy.pool import SingletonThreadPool
|
||||
engine = create_engine('sqlite:///mydb.db',
|
||||
poolclass=SingletonThreadPool)
|
||||
|
||||
|
||||
# maintain the same connection across all threads
|
||||
from sqlalchemy.pool import StaticPool
|
||||
engine = create_engine('sqlite:///mydb.db',
|
||||
poolclass=StaticPool)
|
||||
|
||||
Note that :class:`.SingletonThreadPool` should be configured for the number of threads
|
||||
that are to be used; beyond that number, connections will be closed out in a non deterministic
|
||||
way.
|
||||
|
||||
Unicode
|
||||
-------
|
||||
|
||||
In contrast to SQLAlchemy's active handling of date and time types for pysqlite, pysqlite's
|
||||
default behavior regarding Unicode is that all strings are returned as Python unicode objects
|
||||
in all cases. So even if the :class:`~sqlalchemy.types.Unicode` type is
|
||||
*not* used, you will still always receive unicode data back from a result set. It is
|
||||
**strongly** recommended that you do use the :class:`~sqlalchemy.types.Unicode` type
|
||||
to represent strings, since it will raise a warning if a non-unicode Python string is
|
||||
passed from the user application. Mixing the usage of non-unicode objects with returned unicode objects can
|
||||
quickly create confusion, particularly when using the ORM as internal data is not
|
||||
always represented by an actual database result string.
|
||||
The pysqlite driver only returns Python ``unicode`` objects in result sets, never
|
||||
plain strings, and accommodates ``unicode`` objects within bound parameter
|
||||
values in all cases. Regardless of the SQLAlchemy string type in use,
|
||||
string-based result values will by Python ``unicode`` in Python 2.
|
||||
The :class:`.Unicode` type should still be used to indicate those columns that
|
||||
require unicode, however, so that non-``unicode`` values passed inadvertently
|
||||
will emit a warning. Pysqlite will emit an error if a non-``unicode`` string
|
||||
is passed containing non-ASCII characters.
|
||||
|
||||
.. _pysqlite_serializable:
|
||||
|
||||
Serializable Transaction Isolation
|
||||
----------------------------------
|
||||
|
||||
The pysqlite DBAPI driver has a long-standing bug in which transactional
|
||||
state is not begun until the first DML statement, that is INSERT, UPDATE
|
||||
or DELETE, is emitted. A SELECT statement will not cause transactional
|
||||
state to begin. While this mode of usage is fine for typical situations
|
||||
and has the advantage that the SQLite database file is not prematurely
|
||||
locked, it breaks serializable transaction isolation, which requires
|
||||
that the database file be locked upon any SQL being emitted.
|
||||
|
||||
To work around this issue, the ``BEGIN`` keyword can be emitted
|
||||
at the start of each transaction. The following recipe establishes
|
||||
a :meth:`.ConnectionEvents.begin` handler to achieve this::
|
||||
|
||||
from sqlalchemy import create_engine, event
|
||||
|
||||
engine = create_engine("sqlite:///myfile.db", isolation_level='SERIALIZABLE')
|
||||
|
||||
@event.listens_for(engine, "begin")
|
||||
def do_begin(conn):
|
||||
conn.execute("BEGIN")
|
||||
|
||||
"""
|
||||
|
||||
from sqlalchemy.dialects.sqlite.base import SQLiteDialect, DATETIME, DATE
|
||||
from sqlalchemy import schema, exc, pool
|
||||
from sqlalchemy.engine import default
|
||||
from sqlalchemy import exc, pool
|
||||
from sqlalchemy import types as sqltypes
|
||||
from sqlalchemy import util
|
||||
|
||||
import os
|
||||
|
||||
class _SQLite_pysqliteTimeStamp(DATETIME):
|
||||
def bind_processor(self, dialect):
|
||||
@@ -177,7 +246,6 @@ class _SQLite_pysqliteDate(DATE):
|
||||
|
||||
class SQLiteDialect_pysqlite(SQLiteDialect):
|
||||
default_paramstyle = 'qmark'
|
||||
poolclass = pool.SingletonThreadPool
|
||||
|
||||
colspecs = util.update_copy(
|
||||
SQLiteDialect.colspecs,
|
||||
@@ -215,6 +283,13 @@ class SQLiteDialect_pysqlite(SQLiteDialect):
|
||||
raise e
|
||||
return sqlite
|
||||
|
||||
@classmethod
|
||||
def get_pool_class(cls, url):
|
||||
if url.database and url.database != ':memory:':
|
||||
return pool.NullPool
|
||||
else:
|
||||
return pool.SingletonThreadPool
|
||||
|
||||
def _get_server_version_info(self, connection):
|
||||
return self.dbapi.sqlite_version_info
|
||||
|
||||
@@ -227,6 +302,8 @@ class SQLiteDialect_pysqlite(SQLiteDialect):
|
||||
" sqlite:///relative/path/to/file.db\n"
|
||||
" sqlite:////absolute/path/to/file.db" % (url,))
|
||||
filename = url.database or ':memory:'
|
||||
if filename != ':memory:':
|
||||
filename = os.path.abspath(filename)
|
||||
|
||||
opts = url.query.copy()
|
||||
util.coerce_kw_type(opts, 'timeout', float)
|
||||
@@ -237,7 +314,8 @@ class SQLiteDialect_pysqlite(SQLiteDialect):
|
||||
|
||||
return ([filename], opts)
|
||||
|
||||
def is_disconnect(self, e):
|
||||
return isinstance(e, self.dbapi.ProgrammingError) and "Cannot operate on a closed database." in str(e)
|
||||
def is_disconnect(self, e, connection, cursor):
|
||||
return isinstance(e, self.dbapi.ProgrammingError) and \
|
||||
"Cannot operate on a closed database." in str(e)
|
||||
|
||||
dialect = SQLiteDialect_pysqlite
|
||||
|
||||
Reference in New Issue
Block a user