mirror of
https://github.com/sqlalchemy/sqlalchemy.git
synced 2026-06-03 14:29:41 -04:00
- Connection.begin() no longer accepts nested=True, a possible source of confusion as two forms of nesting are supported. SAVEPOINT-style nesting logic is now contained soley in begin_nested().
- Docstring love for the engine package. More is needed.
This commit is contained in:
@@ -4,45 +4,50 @@
|
||||
# This module is part of SQLAlchemy and is released under
|
||||
# the MIT License: http://www.opensource.org/licenses/mit-license.php
|
||||
|
||||
"""defines the basic components used to interface DBAPI modules with
|
||||
higher-level statement-construction, connection-management,
|
||||
execution and result contexts. The primary "entry point" class into
|
||||
this package is the Engine.
|
||||
"""SQL connections, SQL execution and high-level DB-API interface.
|
||||
|
||||
The package is represented among several individual modules, including:
|
||||
The engine package defines the basic components used to interface
|
||||
DB-API modules with higher-level statement construction,
|
||||
connection-management, execution and result contexts. The primary
|
||||
"entry point" class into this package is the Engine and it's public
|
||||
constructor ``create_engine()``.
|
||||
|
||||
base.py
|
||||
Defines interface classes and some implementation classes
|
||||
which comprise the basic components used to interface between
|
||||
a DBAPI, constructed and plain-text statements,
|
||||
connections, transactions, and results.
|
||||
|
||||
default.py
|
||||
Contains default implementations of some of the components
|
||||
defined in base.py. All current database dialects use the
|
||||
classes in default.py as base classes for their own database-specific
|
||||
implementations.
|
||||
|
||||
strategies.py
|
||||
the mechanics of constructing ``Engine`` objects are represented here.
|
||||
Defines the ``EngineStrategy`` class which represents how to go from
|
||||
arguments specified to the ``create_engine()`` function, to a fully
|
||||
constructed ``Engine``, including initialization of connection pooling,
|
||||
dialects, and specific subclasses of ``Engine``.
|
||||
|
||||
threadlocal.py
|
||||
the ``TLEngine`` class is defined here, which is a subclass of the generic
|
||||
``Engine`` and tracks ``Connection`` and ``Transaction`` objects against
|
||||
the identity of the current thread. This allows certain programming patterns
|
||||
based around the concept of a "thread-local connection" to be possible. The
|
||||
``TLEngine`` is created by using the "threadlocal" engine strategy in
|
||||
conjunction with the ``create_engine()`` function.
|
||||
|
||||
url.py
|
||||
Defines the ``URL`` class which represents the individual components of a
|
||||
string URL passed to ``create_engine()``. Also defines a basic module-loading
|
||||
strategy for the dialect specifier within a URL.
|
||||
|
||||
This package includes:
|
||||
|
||||
base.py
|
||||
Defines interface classes and some implementation classes which
|
||||
comprise the basic components used to interface between a DB-API,
|
||||
constructed and plain-text statements, connections, transactions,
|
||||
and results.
|
||||
|
||||
default.py
|
||||
Contains default implementations of some of the components defined
|
||||
in base.py. All current database dialects use the classes in
|
||||
default.py as base classes for their own database-specific
|
||||
implementations.
|
||||
|
||||
strategies.py
|
||||
The mechanics of constructing ``Engine`` objects are represented
|
||||
here. Defines the ``EngineStrategy`` class which represents how
|
||||
to go from arguments specified to the ``create_engine()``
|
||||
function, to a fully constructed ``Engine``, including
|
||||
initialization of connection pooling, dialects, and specific
|
||||
subclasses of ``Engine``.
|
||||
|
||||
threadlocal.py
|
||||
The ``TLEngine`` class is defined here, which is a subclass of
|
||||
the generic ``Engine`` and tracks ``Connection`` and
|
||||
``Transaction`` objects against the identity of the current
|
||||
thread. This allows certain programming patterns based around
|
||||
the concept of a "thread-local connection" to be possible.
|
||||
The ``TLEngine`` is created by using the "threadlocal" engine
|
||||
strategy in conjunction with the ``create_engine()`` function.
|
||||
|
||||
url.py
|
||||
Defines the ``URL`` class which represents the individual
|
||||
components of a string URL passed to ``create_engine()``. Also
|
||||
defines a basic module-loading strategy for the dialect specifier
|
||||
within a URL.
|
||||
"""
|
||||
|
||||
import sqlalchemy.databases
|
||||
@@ -64,7 +69,7 @@ def engine_descriptors():
|
||||
|
||||
arguments
|
||||
a dictionary describing the name and description of each
|
||||
parameter used to connect to this engine's underlying DBAPI.
|
||||
parameter used to connect to this engine's underlying DB-API.
|
||||
|
||||
This function is meant for usage in automated configuration tools
|
||||
that wish to query the user for database and connection
|
||||
@@ -73,7 +78,8 @@ def engine_descriptors():
|
||||
|
||||
result = []
|
||||
for module in sqlalchemy.databases.__all__:
|
||||
module = getattr(__import__('sqlalchemy.databases.%s' % module).databases, module)
|
||||
module = getattr(
|
||||
__import__('sqlalchemy.databases.%s' % module).databases, module)
|
||||
result.append(module.descriptor())
|
||||
return result
|
||||
|
||||
@@ -86,10 +92,11 @@ def create_engine(*args, **kwargs):
|
||||
dialect and connection arguments, with additional keyword
|
||||
arguments sent as options to the dialect and resulting Engine.
|
||||
|
||||
The URL is a string in the form
|
||||
``dialect://user:password@host/dbname[?key=value..]``, where
|
||||
The URL is a string in the form
|
||||
``dialect://user:password@host/dbname[?key=value..]``, where
|
||||
``dialect`` is a name such as ``mysql``, ``oracle``, ``postgres``,
|
||||
etc. Alternatively, the URL can be an instance of ``sqlalchemy.engine.url.URL``.
|
||||
etc. Alternatively, the URL can be an instance of
|
||||
``sqlalchemy.engine.url.URL``.
|
||||
|
||||
`**kwargs` represents options to be sent to the Engine itself as
|
||||
well as the components of the Engine, including the Dialect, the
|
||||
@@ -97,17 +104,17 @@ def create_engine(*args, **kwargs):
|
||||
follows:
|
||||
|
||||
poolclass
|
||||
a subclass of ``sqlalchemy.pool.Pool`` which will be used to
|
||||
a subclass of ``sqlalchemy.pool.Pool`` which will be used to
|
||||
instantiate a connection pool.
|
||||
|
||||
|
||||
pool
|
||||
an instance of ``sqlalchemy.pool.DBProxy`` or
|
||||
``sqlalchemy.pool.Pool`` to be used as the underlying source for
|
||||
connections (DBProxy/Pool is described in the previous
|
||||
section). This argument supercedes "poolclass".
|
||||
connections (DBProxy/Pool is described in the previous section).
|
||||
This argument supercedes "poolclass".
|
||||
|
||||
echo
|
||||
Defaults to False: if True, the Engine will log all statements
|
||||
defaults to False: if True, the Engine will log all statements
|
||||
as well as a repr() of their parameter lists to the engines
|
||||
logger, which defaults to ``sys.stdout``. A Engine instances'
|
||||
`echo` data member can be modified at any time to turn logging
|
||||
@@ -115,38 +122,36 @@ def create_engine(*args, **kwargs):
|
||||
printed to the standard output as well.
|
||||
|
||||
logger
|
||||
Defaults to None: a file-like object where logging output can be
|
||||
defaults to None: a file-like object where logging output can be
|
||||
sent, if `echo` is set to True. This defaults to
|
||||
``sys.stdout``.
|
||||
|
||||
encoding
|
||||
Defaults to 'utf-8': the encoding to be used when
|
||||
defaults to 'utf-8': the encoding to be used when
|
||||
encoding/decoding Unicode strings.
|
||||
|
||||
convert_unicode
|
||||
Defaults to False: true if unicode conversion should be applied
|
||||
defaults to False: true if unicode conversion should be applied
|
||||
to all str types.
|
||||
|
||||
module
|
||||
Defaults to None: this is a
|
||||
reference to a DBAPI2 module to be used instead of the engine's
|
||||
default module. For Postgres, the default is psycopg2, or
|
||||
psycopg1 if 2 cannot be found. For Oracle, its cx_Oracle. For
|
||||
mysql, MySQLdb.
|
||||
defaults to None: this is a reference to a DB-API 2.0 module to
|
||||
be used instead of the dialect's default module.
|
||||
|
||||
strategy
|
||||
allows alternate Engine implementations to take effect.
|
||||
Current implementations include ``plain`` and ``threadlocal``.
|
||||
The default used by this function is ``plain``.
|
||||
allows alternate Engine implementations to take effect. Current
|
||||
implementations include ``plain`` and ``threadlocal``. The
|
||||
default used by this function is ``plain``.
|
||||
|
||||
``plain`` provides support for a Connection object which can be used
|
||||
to execute SQL queries with a specific underlying DBAPI connection.
|
||||
``plain`` provides support for a Connection object which can be
|
||||
used to execute SQL queries with a specific underlying DB-API
|
||||
connection.
|
||||
|
||||
``threadlocal`` is similar to ``plain`` except that it adds support
|
||||
for a thread-local connection and transaction context, which
|
||||
allows a group of engine operations to participate using the same
|
||||
underlying connection and transaction without the need for explicitly
|
||||
passing a single Connection.
|
||||
``threadlocal`` is similar to ``plain`` except that it adds
|
||||
support for a thread-local connection and transaction context,
|
||||
which allows a group of engine operations to participate using
|
||||
the same underlying connection and transaction without the need
|
||||
for explicitly passing a single Connection.
|
||||
"""
|
||||
|
||||
strategy = kwargs.pop('strategy', default_strategy)
|
||||
@@ -155,17 +160,19 @@ def create_engine(*args, **kwargs):
|
||||
|
||||
def engine_from_config(configuration, prefix='sqlalchemy.', **kwargs):
|
||||
"""Create a new Engine instance using a configuration dictionary.
|
||||
|
||||
the dictionary is typically produced from a config file where keys are prefixed,
|
||||
such as sqlalchemy.url, sqlalchemy.echo, etc. The 'prefix' argument indicates
|
||||
the prefix to be searched for.
|
||||
|
||||
A select set of keyword arguments will be "coerced" to their expected type based on
|
||||
string values. in a future release, this functionality will be expanded to include
|
||||
dialect-specific arguments.
|
||||
|
||||
The dictionary is typically produced from a config file where keys
|
||||
are prefixed, such as sqlalchemy.url, sqlalchemy.echo, etc. The
|
||||
'prefix' argument indicates the prefix to be searched for.
|
||||
|
||||
A select set of keyword arguments will be "coerced" to their
|
||||
expected type based on string values. in a future release, this
|
||||
functionality will be expanded to include dialect-specific
|
||||
arguments.
|
||||
"""
|
||||
|
||||
opts = dict([(key[len(prefix):], configuration[key]) for key in configuration if key.startswith(prefix)])
|
||||
opts = dict([(key[len(prefix):], configuration[key])
|
||||
for key in configuration if key.startswith(prefix)])
|
||||
for opt, type_ in (
|
||||
('convert_unicode', bool),
|
||||
('pool_timeout', int),
|
||||
|
||||
+332
-230
@@ -4,9 +4,13 @@
|
||||
# This module is part of SQLAlchemy and is released under
|
||||
# the MIT License: http://www.opensource.org/licenses/mit-license.php
|
||||
|
||||
"""defines the basic components used to interface DBAPI modules with
|
||||
higher-level statement-construction, connection-management,
|
||||
execution and result contexts."""
|
||||
|
||||
"""Basic components for SQL execution and interfacing with DB-API..
|
||||
|
||||
Defines the basic components used to interface DB-API modules with
|
||||
higher-level statement-construction, connection-management, execution
|
||||
and result contexts.
|
||||
"""
|
||||
|
||||
from sqlalchemy import exceptions, schema, util, types, logging
|
||||
from sqlalchemy.sql import expression, visitors
|
||||
@@ -14,105 +18,124 @@ import StringIO, sys
|
||||
|
||||
|
||||
class Dialect(object):
|
||||
"""Define the behavior of a specific database/DBAPI.
|
||||
"""Define the behavior of a specific database and DB-API combination.
|
||||
|
||||
Any aspect of metadata definition, SQL query generation, execution,
|
||||
result-set handling, or anything else which varies between
|
||||
databases is defined under the general category of the Dialect.
|
||||
The Dialect acts as a factory for other database-specific object
|
||||
implementations including ExecutionContext, Compiled,
|
||||
DefaultGenerator, and TypeEngine.
|
||||
Any aspect of metadata definition, SQL query generation,
|
||||
execution, result-set handling, or anything else which varies
|
||||
between databases is defined under the general category of the
|
||||
Dialect. The Dialect acts as a factory for other
|
||||
database-specific object implementations including
|
||||
ExecutionContext, Compiled, DefaultGenerator, and TypeEngine.
|
||||
|
||||
All Dialects implement the following attributes:
|
||||
|
||||
positional
|
||||
True if the paramstyle for this Dialect is positional
|
||||
positional
|
||||
True if the paramstyle for this Dialect is positional.
|
||||
|
||||
paramstyle
|
||||
The paramstyle to be used (some DBAPIs support multiple paramstyles)
|
||||
paramstyle
|
||||
the paramstyle to be used (some DB-APIs support multiple
|
||||
paramstyles).
|
||||
|
||||
convert_unicode
|
||||
True if unicode conversion should be applied to all str types
|
||||
convert_unicode
|
||||
True if Unicode conversion should be applied to all ``str``
|
||||
types.
|
||||
|
||||
encoding
|
||||
type of encoding to use for unicode, usually defaults to 'utf-8'
|
||||
encoding
|
||||
type of encoding to use for unicode, usually defaults to
|
||||
'utf-8'.
|
||||
|
||||
schemagenerator
|
||||
a [sqlalchemy.schema#SchemaVisitor] class which generates schemas.
|
||||
schemagenerator
|
||||
a [sqlalchemy.schema#SchemaVisitor] class which generates
|
||||
schemas.
|
||||
|
||||
schemadropper
|
||||
a [sqlalchemy.schema#SchemaVisitor] class which drops schemas.
|
||||
schemadropper
|
||||
a [sqlalchemy.schema#SchemaVisitor] class which drops schemas.
|
||||
|
||||
defaultrunner
|
||||
a [sqlalchemy.schema#SchemaVisitor] class which executes defaults.
|
||||
defaultrunner
|
||||
a [sqlalchemy.schema#SchemaVisitor] class which executes
|
||||
defaults.
|
||||
|
||||
statement_compiler
|
||||
a [sqlalchemy.engine.base#Compiled] class used to compile SQL statements
|
||||
statement_compiler
|
||||
a [sqlalchemy.engine.base#Compiled] class used to compile SQL
|
||||
statements
|
||||
|
||||
preparer
|
||||
a [sqlalchemy.sql.compiler#IdentifierPreparer] class used to quote
|
||||
identifiers.
|
||||
preparer
|
||||
a [sqlalchemy.sql.compiler#IdentifierPreparer] class used to
|
||||
quote identifiers.
|
||||
"""
|
||||
|
||||
def create_connect_args(self, url):
|
||||
"""Build DBAPI compatible connection arguments.
|
||||
"""Build DB-API compatible connection arguments.
|
||||
|
||||
Given a [sqlalchemy.engine.url#URL] object, returns a
|
||||
tuple consisting of a `*args`/`**kwargs` suitable to send directly
|
||||
to the dbapi's connect function.
|
||||
Given a [sqlalchemy.engine.url#URL] object, returns a tuple
|
||||
consisting of a `*args`/`**kwargs` suitable to send directly
|
||||
to the dbapi's connect function.
|
||||
"""
|
||||
|
||||
raise NotImplementedError()
|
||||
|
||||
def dbapi_type_map(self):
|
||||
"""return a mapping of DBAPI type objects present in this Dialect's DBAPI
|
||||
mapped to TypeEngine implementations used by the dialect.
|
||||
|
||||
This is used to apply types to result sets based on the DBAPI types
|
||||
present in cursor.description; it only takes effect for result sets against
|
||||
textual statements where no explicit typemap was present. Constructed SQL statements
|
||||
always have type information explicitly embedded.
|
||||
"""Returns a DB-API to sqlalchemy.types mapping.
|
||||
|
||||
A mapping of DB-API type objects present in this Dialect's
|
||||
DB-API implmentation mapped to TypeEngine implementations used
|
||||
by the dialect.
|
||||
|
||||
This is used to apply types to result sets based on the DB-API
|
||||
types present in cursor.description; it only takes effect for
|
||||
result sets against textual statements where no explicit
|
||||
typemap was present. Constructed SQL statements always have
|
||||
type information explicitly embedded.
|
||||
"""
|
||||
|
||||
raise NotImplementedError()
|
||||
|
||||
def type_descriptor(self, typeobj):
|
||||
"""Transform the given [sqlalchemy.types#TypeEngine] instance from generic to database-specific.
|
||||
"""Transform a generic type to a database-specific type.
|
||||
|
||||
Subclasses will usually use the [sqlalchemy.types#adapt_type()] method in the types module
|
||||
to make this job easy.
|
||||
Transforms the given [sqlalchemy.types#TypeEngine] instance
|
||||
from generic to database-specific.
|
||||
|
||||
Subclasses will usually use the
|
||||
[sqlalchemy.types#adapt_type()] method in the types module to
|
||||
make this job easy.
|
||||
"""
|
||||
|
||||
raise NotImplementedError()
|
||||
|
||||
def oid_column_name(self, column):
|
||||
"""Return the oid column name for this dialect, or ``None`` if the dialect can't/won't support OID/ROWID.
|
||||
"""Return the oid column name for this Dialect
|
||||
|
||||
The [sqlalchemy.schema#Column] instance which represents OID for the query being
|
||||
compiled is passed, so that the dialect can inspect the column
|
||||
and its parent selectable to determine if OID/ROWID is not
|
||||
selected for a particular selectable (i.e. oracle doesnt
|
||||
support ROWID for UNION, GROUP BY, DISTINCT, etc.)
|
||||
May return ``None`` if the dialect can't o won't support
|
||||
OID/ROWID features.
|
||||
|
||||
The [sqlalchemy.schema#Column] instance which represents OID
|
||||
for the query being compiled is passed, so that the dialect
|
||||
can inspect the column and its parent selectable to determine
|
||||
if OID/ROWID is not selected for a particular selectable
|
||||
(i.e. Oracle doesnt support ROWID for UNION, GROUP BY,
|
||||
DISTINCT, etc.)
|
||||
"""
|
||||
|
||||
raise NotImplementedError()
|
||||
|
||||
def supports_alter(self):
|
||||
"""return ``True`` if the database supports ``ALTER TABLE``."""
|
||||
"""Return ``True`` if the database supports ``ALTER TABLE``."""
|
||||
raise NotImplementedError()
|
||||
|
||||
def max_identifier_length(self):
|
||||
"""Return the maximum length of identifier names.
|
||||
|
||||
Return ``None`` if no limit."""
|
||||
|
||||
|
||||
Returns ``None`` if no limit.
|
||||
"""
|
||||
|
||||
return None
|
||||
|
||||
def supports_unicode_statements(self):
|
||||
"""indicate whether the DBAPI can receive SQL statements as Python unicode strings"""
|
||||
|
||||
"""Indicate whether the DB-API can receive SQL statements as Python unicode strings"""
|
||||
|
||||
raise NotImplementedError()
|
||||
|
||||
|
||||
def supports_sane_rowcount(self):
|
||||
"""Indicate whether the dialect properly implements rowcount for ``UPDATE`` and ``DELETE`` statements.
|
||||
|
||||
@@ -131,9 +154,11 @@ class Dialect(object):
|
||||
def reflecttable(self, connection, table, include_columns=None):
|
||||
"""Load table description from the database.
|
||||
|
||||
Given a [sqlalchemy.engine#Connection] and a [sqlalchemy.schema#Table] object, reflect its
|
||||
columns and properties from the database. If include_columns (a list or set) is specified, limit the autoload
|
||||
to the given column names.
|
||||
Given a [sqlalchemy.engine#Connection] and a
|
||||
[sqlalchemy.schema#Table] object, reflect its columns and
|
||||
properties from the database. If include_columns (a list or
|
||||
set) is specified, limit the autoload to the given column
|
||||
names.
|
||||
"""
|
||||
|
||||
raise NotImplementedError()
|
||||
@@ -141,9 +166,10 @@ class Dialect(object):
|
||||
def has_table(self, connection, table_name, schema=None):
|
||||
"""Check the existence of a particular table in the database.
|
||||
|
||||
Given a [sqlalchemy.engine#Connection] object and a string `table_name`, return True
|
||||
if the given table (possibly within the specified `schema`)
|
||||
exists in the database, False otherwise.
|
||||
Given a [sqlalchemy.engine#Connection] object and a string
|
||||
`table_name`, return True if the given table (possibly within
|
||||
the specified `schema`) exists in the database, False
|
||||
otherwise.
|
||||
"""
|
||||
|
||||
raise NotImplementedError()
|
||||
@@ -151,9 +177,9 @@ class Dialect(object):
|
||||
def has_sequence(self, connection, sequence_name):
|
||||
"""Check the existence of a particular sequence in the database.
|
||||
|
||||
Given a [sqlalchemy.engine#Connection] object and a string `sequence_name`, return
|
||||
True if the given sequence exists in the database, False
|
||||
otherwise.
|
||||
Given a [sqlalchemy.engine#Connection] object and a string
|
||||
`sequence_name`, return True if the given sequence exists in
|
||||
the database, False otherwise.
|
||||
"""
|
||||
|
||||
raise NotImplementedError()
|
||||
@@ -165,29 +191,31 @@ class Dialect(object):
|
||||
|
||||
def create_execution_context(self, connection, compiled=None, compiled_parameters=None, statement=None, parameters=None):
|
||||
"""Return a new [sqlalchemy.engine#ExecutionContext] object."""
|
||||
|
||||
|
||||
raise NotImplementedError()
|
||||
|
||||
def do_begin(self, connection):
|
||||
"""Provide an implementation of *connection.begin()*, given a DBAPI connection."""
|
||||
"""Provide an implementation of *connection.begin()*, given a DB-API connection."""
|
||||
|
||||
raise NotImplementedError()
|
||||
|
||||
def do_rollback(self, connection):
|
||||
"""Provide an implementation of *connection.rollback()*, given a DBAPI connection."""
|
||||
"""Provide an implementation of *connection.rollback()*, given a DB-API connection."""
|
||||
|
||||
raise NotImplementedError()
|
||||
|
||||
def create_xid(self):
|
||||
"""create a two-phase transaction ID.
|
||||
"""Create a two-phase transaction ID.
|
||||
|
||||
this id will be passed to do_begin_twophase(), do_rollback_twophase(),
|
||||
do_commit_twophase(). its format is unspecified."""
|
||||
This id will be passed to do_begin_twophase(),
|
||||
do_rollback_twophase(), do_commit_twophase(). Its format is
|
||||
unspecified.
|
||||
"""
|
||||
|
||||
raise NotImplementedError()
|
||||
|
||||
def do_commit(self, connection):
|
||||
"""Provide an implementation of *connection.commit()*, given a DBAPI connection."""
|
||||
"""Provide an implementation of *connection.commit()*, given a DB-API connection."""
|
||||
|
||||
raise NotImplementedError()
|
||||
|
||||
@@ -242,8 +270,8 @@ class Dialect(object):
|
||||
raise NotImplementedError()
|
||||
|
||||
def is_disconnect(self, e):
|
||||
"""Return True if the given DBAPI error indicates an invalid connection"""
|
||||
|
||||
"""Return True if the given DB-API error indicates an invalid connection"""
|
||||
|
||||
raise NotImplementedError()
|
||||
|
||||
|
||||
@@ -251,107 +279,106 @@ class ExecutionContext(object):
|
||||
"""A messenger object for a Dialect that corresponds to a single execution.
|
||||
|
||||
ExecutionContext should have these datamembers:
|
||||
|
||||
connection
|
||||
Connection object which can be freely used by default value generators
|
||||
to execute SQL. This Connection should reference the same underlying
|
||||
connection/transactional resources of root_connection.
|
||||
|
||||
root_connection
|
||||
Connection object which is the source of this ExecutionContext. This
|
||||
Connection may have close_with_result=True set, in which case it can
|
||||
only be used once.
|
||||
|
||||
dialect
|
||||
dialect which created this ExecutionContext.
|
||||
|
||||
cursor
|
||||
DBAPI cursor procured from the connection
|
||||
|
||||
compiled
|
||||
if passed to constructor, sqlalchemy.engine.base.Compiled object being executed
|
||||
|
||||
statement
|
||||
string version of the statement to be executed. Is either
|
||||
passed to the constructor, or must be created from the
|
||||
sql.Compiled object by the time pre_exec() has completed.
|
||||
|
||||
parameters
|
||||
bind parameters passed to the execute() method. for
|
||||
compiled statements, this is a dictionary or list
|
||||
of dictionaries. for textual statements, it should
|
||||
be in a format suitable for the dialect's paramstyle
|
||||
(i.e. dict or list of dicts for non positional,
|
||||
list or list of lists/tuples for positional).
|
||||
|
||||
isinsert
|
||||
True if the statement is an INSERT
|
||||
|
||||
isupdate
|
||||
True if the statement is an UPDATE
|
||||
|
||||
connection
|
||||
Connection object which can be freely used by default value
|
||||
generators to execute SQL. This Connection should reference the
|
||||
same underlying connection/transactional resources of
|
||||
root_connection.
|
||||
|
||||
root_connection
|
||||
Connection object which is the source of this ExecutionContext. This
|
||||
Connection may have close_with_result=True set, in which case it can
|
||||
only be used once.
|
||||
|
||||
dialect
|
||||
dialect which created this ExecutionContext.
|
||||
|
||||
cursor
|
||||
DB-API cursor procured from the connection,
|
||||
|
||||
compiled
|
||||
if passed to constructor, sqlalchemy.engine.base.Compiled object
|
||||
being executed,
|
||||
|
||||
statement
|
||||
string version of the statement to be executed. Is either
|
||||
passed to the constructor, or must be created from the
|
||||
sql.Compiled object by the time pre_exec() has completed.
|
||||
|
||||
parameters
|
||||
bind parameters passed to the execute() method. For compiled
|
||||
statements, this is a dictionary or list of dictionaries. For
|
||||
textual statements, it should be in a format suitable for the
|
||||
dialect's paramstyle (i.e. dict or list of dicts for non
|
||||
positional, list or list of lists/tuples for positional).
|
||||
|
||||
isinsert
|
||||
True if the statement is an INSERT.
|
||||
|
||||
isupdate
|
||||
True if the statement is an UPDATE.
|
||||
|
||||
The Dialect should provide an ExecutionContext via the
|
||||
create_execution_context() method. The `pre_exec` and `post_exec`
|
||||
methods will be called for compiled statements.
|
||||
|
||||
"""
|
||||
|
||||
def create_cursor(self):
|
||||
"""Return a new cursor generated from this ExecutionContext's connection.
|
||||
|
||||
Some dialects may wish to change the behavior of connection.cursor(),
|
||||
such as postgres which may return a PG "server side" cursor.
|
||||
|
||||
Some dialects may wish to change the behavior of
|
||||
connection.cursor(), such as postgres which may return a PG
|
||||
"server side" cursor.
|
||||
"""
|
||||
|
||||
raise NotImplementedError()
|
||||
|
||||
def pre_execution(self):
|
||||
"""Called before an execution of a compiled statement.
|
||||
|
||||
If a compiled statement was passed to this
|
||||
ExecutionContext, the `statement` and `parameters` datamembers
|
||||
must be initialized after this statement is complete.
|
||||
|
||||
If a compiled statement was passed to this ExecutionContext,
|
||||
the `statement` and `parameters` datamembers must be
|
||||
initialized after this statement is complete.
|
||||
"""
|
||||
|
||||
raise NotImplementedError()
|
||||
|
||||
def post_execution(self):
|
||||
"""Called after the execution of a compiled statement.
|
||||
|
||||
|
||||
If a compiled statement was passed to this ExecutionContext,
|
||||
the `last_insert_ids`, `last_inserted_params`, etc.
|
||||
datamembers should be available after this method
|
||||
completes.
|
||||
the `last_insert_ids`, `last_inserted_params`, etc.
|
||||
datamembers should be available after this method completes.
|
||||
"""
|
||||
|
||||
raise NotImplementedError()
|
||||
|
||||
|
||||
def result(self):
|
||||
"""return a result object corresponding to this ExecutionContext.
|
||||
|
||||
Returns a ResultProxy."""
|
||||
|
||||
"""Return a result object corresponding to this ExecutionContext.
|
||||
|
||||
Returns a ResultProxy.
|
||||
"""
|
||||
|
||||
raise NotImplementedError()
|
||||
|
||||
|
||||
def get_rowcount(self):
|
||||
"""Return the count of rows updated/deleted for an UPDATE/DELETE statement."""
|
||||
|
||||
raise NotImplementedError()
|
||||
|
||||
def should_autocommit(self):
|
||||
"""return True if this context's statement should be 'committed' automatically in a non-transactional context"""
|
||||
"""Return True if this context's statement should be 'committed' automatically in a non-transactional context"""
|
||||
|
||||
raise NotImplementedError()
|
||||
|
||||
|
||||
def last_inserted_ids(self):
|
||||
"""Return the list of the primary key values for the last insert statement executed.
|
||||
|
||||
This does not apply to straight textual clauses; only to
|
||||
``sql.Insert`` objects compiled against a ``schema.Table`` object.
|
||||
The order of
|
||||
items in the list is the same as that of the Table's
|
||||
'primary_key' attribute.
|
||||
|
||||
``sql.Insert`` objects compiled against a ``schema.Table``
|
||||
object. The order of items in the list is the same as that of
|
||||
the Table's 'primary_key' attribute.
|
||||
"""
|
||||
|
||||
raise NotImplementedError()
|
||||
@@ -498,7 +525,7 @@ class Connectable(object):
|
||||
dialect = util.NotImplProperty("Dialect which this Connectable is associated with.")
|
||||
|
||||
class Connection(Connectable):
|
||||
"""Represent a single DBAPI connection returned from the underlying connection pool.
|
||||
"""Provides high-level functionality for a wrapped DB-API connection.
|
||||
|
||||
Provides execution support for string-based SQL statements as well
|
||||
as ClauseElement, Compiled and DefaultGenerator objects. Provides
|
||||
@@ -507,7 +534,15 @@ class Connection(Connectable):
|
||||
The Connection object is **not** threadsafe.
|
||||
"""
|
||||
|
||||
def __init__(self, engine, connection=None, close_with_result=False, _branch=False):
|
||||
def __init__(self, engine, connection=None, close_with_result=False,
|
||||
_branch=False):
|
||||
"""Construct a new Connection.
|
||||
|
||||
Connection objects are typically constructed by an
|
||||
[sqlalchemy.engine#Engine], see the ``connect()`` and
|
||||
``contextual_connect()`` methods of Engine.
|
||||
"""
|
||||
|
||||
self.__engine = engine
|
||||
self.__connection = connection or engine.raw_connection()
|
||||
self.__transaction = None
|
||||
@@ -525,82 +560,130 @@ class Connection(Connectable):
|
||||
"""return a new Connection which references this Connection's
|
||||
engine and connection; but does not have close_with_result enabled,
|
||||
and also whose close() method does nothing.
|
||||
|
||||
|
||||
This is used to execute "sub" statements within a single execution,
|
||||
usually an INSERT statement.
|
||||
"""
|
||||
|
||||
|
||||
return Connection(self.__engine, self.__connection, _branch=True)
|
||||
|
||||
|
||||
engine = property(lambda s:s.__engine, doc="The Engine with which this Connection is associated.")
|
||||
dialect = property(lambda s:s.__engine.dialect, doc="Dialect used by this Connection.")
|
||||
connection = property(_get_connection, doc="The underlying DBAPI connection managed by this Connection.")
|
||||
connection = property(_get_connection, doc="The underlying DB-API connection managed by this Connection.")
|
||||
should_close_with_result = property(lambda s:s.__close_with_result, doc="Indicates if this Connection should be closed when a corresponding ResultProxy is closed; this is essentially an auto-release mode.")
|
||||
properties = property(lambda s: s._get_connection().properties,
|
||||
doc="A set of per-DBAPI connection properties.")
|
||||
doc="A collection of per-DB-API connection instance properties.")
|
||||
|
||||
def connect(self):
|
||||
"""connect() is implemented to return self so that an incoming Engine or Connection object can be treated similarly."""
|
||||
"""Returns self.
|
||||
|
||||
This ``Connectable`` interface method returns self, allowing
|
||||
Connections to be used interchangably with Engines in most
|
||||
situations that require a bind.
|
||||
"""
|
||||
|
||||
return self
|
||||
|
||||
def contextual_connect(self, **kwargs):
|
||||
"""contextual_connect() is implemented to return self so that an incoming Engine or Connection object can be treated similarly."""
|
||||
"""Returns self.
|
||||
|
||||
This ``Connectable`` interface method returns self, allowing
|
||||
Connections to be used interchangably with Engines in most
|
||||
situations that require a bind.
|
||||
"""
|
||||
|
||||
return self
|
||||
|
||||
def invalidate(self):
|
||||
"""invalidate the underying DBAPI connection and immediately close this Connection.
|
||||
|
||||
The underlying DBAPI connection is literally closed (if possible), and is discarded.
|
||||
Its source connection pool will typically create a new connection to replace it, once
|
||||
requested.
|
||||
"""Invalidate and close the Connection.
|
||||
|
||||
The underlying DB-API connection is literally closed (if
|
||||
possible), and is discarded. Its source connection pool will
|
||||
typically lazilly create a new connection to replace it.
|
||||
"""
|
||||
|
||||
|
||||
self.__connection.invalidate()
|
||||
self.__connection = None
|
||||
|
||||
def detach(self):
|
||||
"""detach the underlying DBAPI connection from its connection pool.
|
||||
|
||||
This Connection instance will remain useable. When closed, the
|
||||
DBAPI connection will be literally closed and not returned to its pool.
|
||||
The pool will typically create a new connection to replace it, once requested.
|
||||
|
||||
This method can be used to insulate the rest of an application from a modified
|
||||
state on a connection (such as a transaction isolation level or similar).
|
||||
"""Detach the underlying DB-API connection from its connection pool.
|
||||
|
||||
This Connection instance will remain useable. When closed,
|
||||
the DB-API connection will be literally closed and not
|
||||
returned to its pool. The pool will typically lazily create a
|
||||
new connection to replace the detached connection.
|
||||
|
||||
This method can be used to insulate the rest of an application
|
||||
from a modified state on a connection (such as a transaction
|
||||
isolation level or similar). Also see
|
||||
[sqlalchemy.interfaces#PoolListener] for a mechanism to modify
|
||||
connection state when connections leave and return to their
|
||||
connection pool.
|
||||
"""
|
||||
|
||||
|
||||
self.__connection.detach()
|
||||
|
||||
def begin(self, nested=False):
|
||||
|
||||
def begin(self):
|
||||
"""Begin a transaction and return a Transaction handle.
|
||||
|
||||
Repeated calls to ``begin`` on the same Connection will create
|
||||
a lightweight, emulated nested transaction. Only the
|
||||
outermost transaction may ``commit``. Calls to ``commit`` on
|
||||
inner transactions are ignored. Any transaction in the
|
||||
hierarchy may ``rollback``, however.
|
||||
"""
|
||||
|
||||
if self.__transaction is None:
|
||||
self.__transaction = RootTransaction(self)
|
||||
elif nested:
|
||||
self.__transaction = NestedTransaction(self, self.__transaction)
|
||||
else:
|
||||
return Transaction(self, self.__transaction)
|
||||
return self.__transaction
|
||||
|
||||
def begin_nested(self):
|
||||
return self.begin(nested=True)
|
||||
|
||||
"""Begin a nested transaction and return a Transaction handle.
|
||||
|
||||
Nested transactions require SAVEPOINT support in the
|
||||
underlying database. Any transaction in the hierarchy may
|
||||
``commit`` and ``rollback``, however the outermost transaction
|
||||
still controls the overall ``commit`` or ``rollback`` of the
|
||||
transaction of a whole.
|
||||
"""
|
||||
|
||||
if self.__transaction is None:
|
||||
self.__transaction = RootTransaction(self)
|
||||
else:
|
||||
self.__transaction = NestedTransaction(self, self.__transaction)
|
||||
return self.__transaction
|
||||
|
||||
def begin_twophase(self, xid=None):
|
||||
"""Begin a two-phase or XA transaction and return a Transaction handle.
|
||||
|
||||
xid
|
||||
the two phase transaction id. If not supplied, a random id
|
||||
will be generated.
|
||||
"""
|
||||
|
||||
if self.__transaction is not None:
|
||||
raise exceptions.InvalidRequestError("Cannot start a two phase transaction when a transaction is already started.")
|
||||
raise exceptions.InvalidRequestError(
|
||||
"Cannot start a two phase transaction when a transaction "
|
||||
"is already in progress.")
|
||||
if xid is None:
|
||||
xid = self.__engine.dialect.create_xid();
|
||||
self.__transaction = TwoPhaseTransaction(self, xid)
|
||||
return self.__transaction
|
||||
|
||||
|
||||
def recover_twophase(self):
|
||||
return self.__engine.dialect.do_recover_twophase(self)
|
||||
|
||||
|
||||
def rollback_prepared(self, xid, recover=False):
|
||||
self.__engine.dialect.do_rollback_twophase(self, xid, recover=recover)
|
||||
|
||||
|
||||
def commit_prepared(self, xid, recover=False):
|
||||
self.__engine.dialect.do_commit_twophase(self, xid, recover=recover)
|
||||
|
||||
def in_transaction(self):
|
||||
"""Return True if a transaction is in progress."""
|
||||
|
||||
return self.__transaction is not None
|
||||
|
||||
def _begin_impl(self):
|
||||
@@ -636,26 +719,26 @@ class Connection(Connectable):
|
||||
if self.__connection.is_valid:
|
||||
self.__engine.dialect.do_savepoint(self, name)
|
||||
return name
|
||||
|
||||
|
||||
def _rollback_to_savepoint_impl(self, name, context):
|
||||
if self.__connection.is_valid:
|
||||
self.__engine.dialect.do_rollback_to_savepoint(self, name)
|
||||
self.__transaction = context
|
||||
|
||||
|
||||
def _release_savepoint_impl(self, name, context):
|
||||
if self.__connection.is_valid:
|
||||
self.__engine.dialect.do_release_savepoint(self, name)
|
||||
self.__transaction = context
|
||||
|
||||
|
||||
def _begin_twophase_impl(self, xid):
|
||||
if self.__connection.is_valid:
|
||||
self.__engine.dialect.do_begin_twophase(self, xid)
|
||||
|
||||
|
||||
def _prepare_twophase_impl(self, xid):
|
||||
if self.__connection.is_valid:
|
||||
assert isinstance(self.__transaction, TwoPhaseTransaction)
|
||||
self.__engine.dialect.do_prepare_twophase(self, xid)
|
||||
|
||||
|
||||
def _rollback_twophase_impl(self, xid, is_prepared):
|
||||
if self.__connection.is_valid:
|
||||
assert isinstance(self.__transaction, TwoPhaseTransaction)
|
||||
@@ -669,17 +752,26 @@ class Connection(Connectable):
|
||||
self.__transaction = None
|
||||
|
||||
def _autocommit(self, context):
|
||||
"""When no Transaction is present, this is called after executions to provide "autocommit" behavior."""
|
||||
# TODO: have the dialect determine if autocommit can be set on the connection directly without this
|
||||
# extra step
|
||||
"""Possibly issue a commit.
|
||||
|
||||
When no Transaction is present, this is called after statement
|
||||
execution to provide "autocommit" behavior. Dialects may
|
||||
inspect the statement to determine if a commit is actually
|
||||
required.
|
||||
"""
|
||||
|
||||
# TODO: have the dialect determine if autocommit can be set on
|
||||
# the connection directly without this extra step
|
||||
if not self.in_transaction() and context.should_autocommit():
|
||||
self._commit_impl()
|
||||
|
||||
def _autorollback(self):
|
||||
if not self.in_transaction():
|
||||
self._rollback_impl()
|
||||
|
||||
|
||||
def close(self):
|
||||
"""Close this Connection."""
|
||||
|
||||
try:
|
||||
c = self.__connection
|
||||
except AttributeError:
|
||||
@@ -690,12 +782,16 @@ class Connection(Connectable):
|
||||
del self.__connection
|
||||
|
||||
def scalar(self, object, *multiparams, **params):
|
||||
"""Executes and returns the first column of the first row."""
|
||||
|
||||
return self.execute(object, *multiparams, **params).scalar()
|
||||
|
||||
def statement_compiler(self, statement, parameters, **kwargs):
|
||||
return self.dialect.statement_compiler(self.dialect, statement, parameters, bind=self, **kwargs)
|
||||
|
||||
def execute(self, object, *multiparams, **params):
|
||||
"""Executes and returns a ResultProxy."""
|
||||
|
||||
for c in type(object).__mro__:
|
||||
if c in Connection.executors:
|
||||
return Connection.executors[c](self, object, multiparams, params)
|
||||
@@ -722,7 +818,7 @@ class Connection(Connectable):
|
||||
|
||||
def _execute_function(self, func, multiparams, params):
|
||||
return self._execute_clauseelement(func.select(), multiparams, params)
|
||||
|
||||
|
||||
def _execute_clauseelement(self, elem, multiparams=None, params=None):
|
||||
executemany = multiparams is not None and len(multiparams) > 0
|
||||
if executemany:
|
||||
@@ -738,15 +834,15 @@ class Connection(Connectable):
|
||||
|
||||
params = self.__distill_params(multiparams, params)
|
||||
context = self.__create_execution_context(compiled=compiled, parameters=params)
|
||||
|
||||
|
||||
context.pre_execution()
|
||||
self.__execute_raw(context)
|
||||
context.post_execution()
|
||||
return context.result()
|
||||
|
||||
|
||||
def __create_execution_context(self, **kwargs):
|
||||
return self.__engine.dialect.create_execution_context(connection=self, **kwargs)
|
||||
|
||||
|
||||
def __execute_raw(self, context):
|
||||
if logging.is_info_enabled(self.__engine.logger):
|
||||
self.__engine.logger.info(context.statement)
|
||||
@@ -756,7 +852,7 @@ class Connection(Connectable):
|
||||
else:
|
||||
self.__execute(context)
|
||||
self._autocommit(context)
|
||||
|
||||
|
||||
def __execute(self, context):
|
||||
if context.parameters is None:
|
||||
if context.dialect.positional:
|
||||
@@ -834,10 +930,11 @@ class Transaction(object):
|
||||
|
||||
def close(self):
|
||||
"""close this transaction.
|
||||
|
||||
If this transaction is the base transaction in a begin/commit nesting,
|
||||
the transaction will rollback(). Otherwise, the method returns.
|
||||
|
||||
|
||||
If this transaction is the base transaction in a begin/commit
|
||||
nesting, the transaction will rollback(). Otherwise, the
|
||||
method returns.
|
||||
|
||||
This is used to cancel a Transaction without affecting the scope of
|
||||
an enclosign transaction.
|
||||
"""
|
||||
@@ -845,13 +942,13 @@ class Transaction(object):
|
||||
return
|
||||
if self._parent is self:
|
||||
self.rollback()
|
||||
|
||||
|
||||
def rollback(self):
|
||||
if not self._parent._is_active:
|
||||
return
|
||||
self._is_active = False
|
||||
self._do_rollback()
|
||||
|
||||
|
||||
def _do_rollback(self):
|
||||
self._parent.rollback()
|
||||
|
||||
@@ -860,7 +957,7 @@ class Transaction(object):
|
||||
raise exceptions.InvalidRequestError("This transaction is inactive")
|
||||
self._is_active = False
|
||||
self._do_commit()
|
||||
|
||||
|
||||
def _do_commit(self):
|
||||
pass
|
||||
|
||||
@@ -1084,7 +1181,7 @@ class Engine(Connectable):
|
||||
return self.run_callable(lambda c: self.dialect.has_table(c, table_name, schema=schema))
|
||||
|
||||
def raw_connection(self):
|
||||
"""Return a DBAPI connection."""
|
||||
"""Return a DB-API connection."""
|
||||
|
||||
return self.pool.connect()
|
||||
|
||||
@@ -1094,7 +1191,7 @@ class Engine(Connectable):
|
||||
self.logger.info(msg)
|
||||
|
||||
class ResultProxy(object):
|
||||
"""Wraps a DBAPI cursor object to provide easier access to row columns.
|
||||
"""Wraps a DB-API cursor object to provide easier access to row columns.
|
||||
|
||||
Individual columns may be accessed by their integer position,
|
||||
case-insensitive column name, or by ``schema.Column``
|
||||
@@ -1119,7 +1216,7 @@ class ResultProxy(object):
|
||||
def process(value):
|
||||
raise exceptions.InvalidRequestError("Ambiguous column name '%s' in result set! try 'use_labels' option on select statement." % colname)
|
||||
return process
|
||||
|
||||
|
||||
def __init__(self, context):
|
||||
"""ResultProxy objects are constructed via the execute() method on SQLEngine."""
|
||||
self.context = context
|
||||
@@ -1134,9 +1231,9 @@ class ResultProxy(object):
|
||||
else:
|
||||
self._rowcount = context.get_rowcount()
|
||||
self.close()
|
||||
|
||||
|
||||
connection = property(lambda self:self.context.root_connection)
|
||||
|
||||
|
||||
def _get_rowcount(self):
|
||||
if self._rowcount is not None:
|
||||
return self._rowcount
|
||||
@@ -1145,7 +1242,7 @@ class ResultProxy(object):
|
||||
rowcount = property(_get_rowcount)
|
||||
lastrowid = property(lambda s:s.cursor.lastrowid)
|
||||
out_parameters = property(lambda s:s.context.out_parameters)
|
||||
|
||||
|
||||
def _init_metadata(self):
|
||||
if hasattr(self, '_ResultProxy__props'):
|
||||
return
|
||||
@@ -1201,13 +1298,13 @@ class ResultProxy(object):
|
||||
|
||||
return rec
|
||||
return util.PopulateDict(lookup_key)
|
||||
|
||||
|
||||
def close(self):
|
||||
"""Close this ResultProxy, and the underlying DBAPI cursor corresponding to the execution.
|
||||
"""Close this ResultProxy, and the underlying DB-API cursor corresponding to the execution.
|
||||
|
||||
If this ResultProxy was generated from an implicit execution,
|
||||
the underlying Connection will also be closed (returns the
|
||||
underlying DBAPI connection to the connection pool.)
|
||||
underlying DB-API connection to the connection pool.)
|
||||
|
||||
This method is also called automatically when all result rows
|
||||
are exhausted.
|
||||
@@ -1217,9 +1314,9 @@ class ResultProxy(object):
|
||||
self.cursor.close()
|
||||
if self.connection.should_close_with_result:
|
||||
self.connection.close()
|
||||
|
||||
|
||||
keys = property(lambda s:s.__keys)
|
||||
|
||||
|
||||
def _has_key(self, row, key):
|
||||
try:
|
||||
self._key_cache[key]
|
||||
@@ -1267,7 +1364,6 @@ class ResultProxy(object):
|
||||
|
||||
return self.context.lastrow_has_defaults()
|
||||
|
||||
|
||||
def supports_sane_rowcount(self):
|
||||
"""Return ``supports_sane_rowcount()`` from the underlying ExecutionContext.
|
||||
|
||||
@@ -1282,7 +1378,7 @@ class ResultProxy(object):
|
||||
return rec[1](row[rec[2]])
|
||||
else:
|
||||
return row[rec[2]]
|
||||
|
||||
|
||||
def _fetchone_impl(self):
|
||||
return self.cursor.fetchone()
|
||||
def _fetchmany_impl(self, size=None):
|
||||
@@ -1292,16 +1388,16 @@ class ResultProxy(object):
|
||||
|
||||
def _row_processor(self):
|
||||
return RowProxy
|
||||
|
||||
|
||||
def fetchall(self):
|
||||
"""Fetch all rows, just like DBAPI ``cursor.fetchall()``."""
|
||||
"""Fetch all rows, just like DB-API ``cursor.fetchall()``."""
|
||||
|
||||
l = [self._process_row(self, row) for row in self._fetchall_impl()]
|
||||
self.close()
|
||||
return l
|
||||
|
||||
def fetchmany(self, size=None):
|
||||
"""Fetch many rows, just like DBAPI ``cursor.fetchmany(size=cursor.arraysize)``."""
|
||||
"""Fetch many rows, just like DB-API ``cursor.fetchmany(size=cursor.arraysize)``."""
|
||||
|
||||
l = [self._process_row(self, row) for row in self._fetchmany_impl(size)]
|
||||
if len(l) == 0:
|
||||
@@ -1309,7 +1405,7 @@ class ResultProxy(object):
|
||||
return l
|
||||
|
||||
def fetchone(self):
|
||||
"""Fetch one row, just like DBAPI ``cursor.fetchone()``."""
|
||||
"""Fetch one row, just like DB-API ``cursor.fetchone()``."""
|
||||
row = self._fetchone_impl()
|
||||
if row is not None:
|
||||
return self._process_row(self, row)
|
||||
@@ -1329,20 +1425,23 @@ class ResultProxy(object):
|
||||
self.close()
|
||||
|
||||
class BufferedRowResultProxy(ResultProxy):
|
||||
"""``ResultProxy`` that buffers the contents of a selection of rows before
|
||||
``fetchone()`` is called. This is to allow the results of
|
||||
``cursor.description`` to be available immediately, when interfacing
|
||||
with a DBAPI that requires rows to be consumed before this information is
|
||||
available (currently psycopg2, when used with server-side cursors).
|
||||
|
||||
The pre-fetching behavior fetches only one row initially, and then grows
|
||||
its buffer size by a fixed amount with each successive need for additional
|
||||
rows up to a size of 100.
|
||||
"""A ResultProxy with row buffering behavior.
|
||||
|
||||
``ResultProxy`` that buffers the contents of a selection of rows
|
||||
before ``fetchone()`` is called. This is to allow the results of
|
||||
``cursor.description`` to be available immediately, when
|
||||
interfacing with a DB-API that requires rows to be consumed before
|
||||
this information is available (currently psycopg2, when used with
|
||||
server-side cursors).
|
||||
|
||||
The pre-fetching behavior fetches only one row initially, and then
|
||||
grows its buffer size by a fixed amount with each successive need
|
||||
for additional rows up to a size of 100.
|
||||
"""
|
||||
def _init_metadata(self):
|
||||
self.__buffer_rows()
|
||||
super(BufferedRowResultProxy, self)._init_metadata()
|
||||
|
||||
|
||||
# this is a "growth chart" for the buffering of rows.
|
||||
# each successive __buffer_rows call will use the next
|
||||
# value in the list for the buffer size until the max
|
||||
@@ -1354,13 +1453,13 @@ class BufferedRowResultProxy(ResultProxy):
|
||||
20 : 50,
|
||||
50 : 100
|
||||
}
|
||||
|
||||
|
||||
def __buffer_rows(self):
|
||||
size = getattr(self, '_bufsize', 1)
|
||||
self.__rowbuffer = self.cursor.fetchmany(size)
|
||||
#self.context.engine.logger.debug("Buffered %d rows" % size)
|
||||
self._bufsize = self.size_growth.get(size, size)
|
||||
|
||||
|
||||
def _fetchone_impl(self):
|
||||
if self.closed:
|
||||
return None
|
||||
@@ -1378,19 +1477,22 @@ class BufferedRowResultProxy(ResultProxy):
|
||||
break
|
||||
result.append(row)
|
||||
return result
|
||||
|
||||
|
||||
def _fetchall_impl(self):
|
||||
return self.__rowbuffer + list(self.cursor.fetchall())
|
||||
|
||||
class BufferedColumnResultProxy(ResultProxy):
|
||||
"""``ResultProxy`` that loads all columns into memory each time fetchone() is
|
||||
called. If fetchmany() or fetchall() are called, the full grid of results
|
||||
is fetched. This is to operate with databases where result rows contain "live"
|
||||
results that fall out of scope unless explicitly fetched. Currently this includes
|
||||
just cx_Oracle LOB objects, but this behavior is known to exist in other DBAPIs as
|
||||
well (Pygresql, currently unsupported).
|
||||
"""A ResultProxy with column buffering behavior.
|
||||
|
||||
``ResultProxy`` that loads all columns into memory each time
|
||||
fetchone() is called. If fetchmany() or fetchall() are called,
|
||||
the full grid of results is fetched. This is to operate with
|
||||
databases where result rows contain "live" results that fall out
|
||||
of scope unless explicitly fetched. Currently this includes just
|
||||
cx_Oracle LOB objects, but this behavior is known to exist in
|
||||
other DB-APIs as well (Pygresql, currently unsupported).
|
||||
"""
|
||||
|
||||
def _get_col(self, row, key):
|
||||
rec = self._key_cache[key]
|
||||
return row[rec[2]]
|
||||
@@ -1443,7 +1545,7 @@ class RowProxy(object):
|
||||
|
||||
def __contains__(self, key):
|
||||
return self.__parent._has_key(self.__row, key)
|
||||
|
||||
|
||||
def __iter__(self):
|
||||
for i in range(0, len(self.__row)):
|
||||
yield self.__parent._get_col(self.__row, i)
|
||||
@@ -1531,7 +1633,7 @@ class DefaultRunner(schema.SchemaVisitor):
|
||||
self.context = context
|
||||
self.connection = self.context._connection._branch()
|
||||
dialect = property(lambda self:self.context.dialect)
|
||||
|
||||
|
||||
def get_column_default(self, column):
|
||||
if column.default is not None:
|
||||
return self.traverse_single(column.default)
|
||||
|
||||
@@ -4,17 +4,20 @@
|
||||
# This module is part of SQLAlchemy and is released under
|
||||
# the MIT License: http://www.opensource.org/licenses/mit-license.php
|
||||
|
||||
"""Provide default implementations of per-dialect sqlalchemy.engine classes"""
|
||||
"""Default implementations of per-dialect sqlalchemy.engine classes."""
|
||||
|
||||
|
||||
from sqlalchemy import schema, exceptions, util
|
||||
import re, random
|
||||
from sqlalchemy.engine import base
|
||||
from sqlalchemy.sql import compiler, expression
|
||||
|
||||
|
||||
AUTOCOMMIT_REGEXP = re.compile(r'\s*(?:UPDATE|INSERT|CREATE|DELETE|DROP|ALTER)',
|
||||
re.I | re.UNICODE)
|
||||
SELECT_REGEXP = re.compile(r'\s*SELECT', re.I | re.UNICODE)
|
||||
|
||||
|
||||
class DefaultDialect(base.Dialect):
|
||||
"""Default implementation of Dialect"""
|
||||
|
||||
@@ -34,7 +37,7 @@ class DefaultDialect(base.Dialect):
|
||||
self.identifier_preparer = self.preparer(self)
|
||||
|
||||
def dbapi_type_map(self):
|
||||
# most DBAPIs have problems with this (such as, psycocpg2 types
|
||||
# most DB-APIs have problems with this (such as, psycocpg2 types
|
||||
# are unhashable). So far Oracle can return it.
|
||||
|
||||
return {}
|
||||
@@ -53,14 +56,12 @@ class DefaultDialect(base.Dialect):
|
||||
typeobj = typeobj()
|
||||
return typeobj
|
||||
|
||||
|
||||
def supports_unicode_statements(self):
|
||||
"""indicate whether the DBAPI can receive SQL statements as Python unicode strings"""
|
||||
"""True if DB-API can receive SQL statements as Python Unicode."""
|
||||
return False
|
||||
|
||||
def max_identifier_length(self):
|
||||
# TODO: probably raise this and fill out
|
||||
# db modules better
|
||||
# TODO: probably raise this and fill out db modules better
|
||||
return 9999
|
||||
|
||||
def supports_alter(self):
|
||||
@@ -84,7 +85,6 @@ class DefaultDialect(base.Dialect):
|
||||
autocommit on/off, etc.
|
||||
"""
|
||||
|
||||
#print "ENGINE ROLLBACK ON ", connection.connection
|
||||
connection.rollback()
|
||||
|
||||
def do_commit(self, connection):
|
||||
@@ -92,14 +92,13 @@ class DefaultDialect(base.Dialect):
|
||||
autocommit on/off, etc.
|
||||
"""
|
||||
|
||||
#print "ENGINE COMMIT ON ", connection.connection
|
||||
connection.commit()
|
||||
|
||||
def create_xid(self):
|
||||
"""create a two-phase transaction ID.
|
||||
"""Create a random two-phase transaction ID.
|
||||
|
||||
this id will be passed to do_begin_twophase(), do_rollback_twophase(),
|
||||
do_commit_twophase(). its format is unspecified."""
|
||||
This id will be passed to do_begin_twophase(), do_rollback_twophase(),
|
||||
do_commit_twophase(). Its format is unspecified."""
|
||||
|
||||
return "_sa_%032x" % random.randint(0,2**128)
|
||||
|
||||
@@ -118,7 +117,6 @@ class DefaultDialect(base.Dialect):
|
||||
def do_execute(self, cursor, statement, parameters, **kwargs):
|
||||
cursor.execute(statement, parameters)
|
||||
|
||||
|
||||
def is_disconnect(self, e):
|
||||
return False
|
||||
|
||||
@@ -128,7 +126,6 @@ class DefaultDialect(base.Dialect):
|
||||
|
||||
paramstyle = property(lambda s:s._paramstyle, _set_paramstyle)
|
||||
|
||||
|
||||
def _figure_paramstyle(self, paramstyle=None, default='named'):
|
||||
if paramstyle is not None:
|
||||
self._paramstyle = paramstyle
|
||||
@@ -156,6 +153,7 @@ class DefaultDialect(base.Dialect):
|
||||
return self._ischema
|
||||
ischema = property(_get_ischema, doc="""returns an ISchema object for this engine, which allows access to information_schema tables (if supported)""")
|
||||
|
||||
|
||||
class DefaultExecutionContext(base.ExecutionContext):
|
||||
def __init__(self, dialect, connection, compiled=None, statement=None, parameters=None):
|
||||
self.dialect = dialect
|
||||
@@ -218,9 +216,9 @@ class DefaultExecutionContext(base.ExecutionContext):
|
||||
|
||||
def __convert_compiled_params(self, parameters):
|
||||
encode = not self.dialect.supports_unicode_statements()
|
||||
# the bind params are a CompiledParams object. but all the DBAPI's hate
|
||||
# that object (or similar). so convert it to a clean
|
||||
# dictionary/list/tuple of dictionary/tuple of list
|
||||
# the bind params are a CompiledParams object. but all the
|
||||
# DB-API's hate that object (or similar). so convert it to a
|
||||
# clean dictionary/list/tuple of dictionary/tuple of list
|
||||
if parameters is not None:
|
||||
if self.executemany:
|
||||
processors = parameters[0].get_processors()
|
||||
@@ -295,7 +293,7 @@ class DefaultExecutionContext(base.ExecutionContext):
|
||||
|
||||
def set_input_sizes(self):
|
||||
"""Given a cursor and ClauseParameters, call the appropriate
|
||||
style of ``setinputsizes()`` on the cursor, using DBAPI types
|
||||
style of ``setinputsizes()`` on the cursor, using DB-API types
|
||||
from the bind parameter's ``TypeEngine`` objects.
|
||||
"""
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
"""Define different strategies for creating new instances of sql.Engine.
|
||||
"""Strategies for creating new instances of Engine types.
|
||||
|
||||
By default there are two, one which is the "thread-local" strategy,
|
||||
one which is the "plain" strategy.
|
||||
@@ -15,9 +15,10 @@ from sqlalchemy import pool as poollib
|
||||
strategies = {}
|
||||
|
||||
class EngineStrategy(object):
|
||||
"""Define a function that receives input arguments and produces an
|
||||
instance of sql.Engine, typically an instance
|
||||
sqlalchemy.engine.base.Engine or a subclass.
|
||||
"""An adaptor that processes input arguements and produces an Engine.
|
||||
|
||||
Provides a ``create`` method that receives input arguments and
|
||||
produces an instance of base.Engine or a subclass.
|
||||
"""
|
||||
|
||||
def __init__(self, name):
|
||||
@@ -30,11 +31,13 @@ class EngineStrategy(object):
|
||||
strategies[self.name] = self
|
||||
|
||||
def create(self, *args, **kwargs):
|
||||
"""Given arguments, returns a new sql.Engine instance."""
|
||||
"""Given arguments, returns a new Engine instance."""
|
||||
|
||||
raise NotImplementedError()
|
||||
|
||||
class DefaultEngineStrategy(EngineStrategy):
|
||||
"""Base class for built-in stratgies."""
|
||||
|
||||
def create(self, name_or_url, **kwargs):
|
||||
# create url.URL object
|
||||
u = url.make_url(name_or_url)
|
||||
@@ -54,9 +57,9 @@ class DefaultEngineStrategy(EngineStrategy):
|
||||
if k in kwargs:
|
||||
dbapi_args[k] = kwargs.pop(k)
|
||||
dbapi = dialect_cls.dbapi(**dbapi_args)
|
||||
|
||||
|
||||
dialect_args['dbapi'] = dbapi
|
||||
|
||||
|
||||
# create dialect
|
||||
dialect = dialect_cls(**dialect_args)
|
||||
|
||||
@@ -78,9 +81,13 @@ class DefaultEngineStrategy(EngineStrategy):
|
||||
getattr(dialect_cls, 'poolclass', poollib.QueuePool))
|
||||
pool_args = {}
|
||||
|
||||
# consume pool arguments from kwargs, translating a few of the arguments
|
||||
# consume pool arguments from kwargs, translating a few of
|
||||
# the arguments
|
||||
translate = {'echo': 'echo_pool',
|
||||
'timeout': 'pool_timeout',
|
||||
'recycle': 'pool_recycle'}
|
||||
for k in util.get_cls_kwargs(poolclass):
|
||||
tk = {'echo':'echo_pool', 'timeout':'pool_timeout', 'recycle':'pool_recycle'}.get(k, k)
|
||||
tk = translate.get(k, k)
|
||||
if tk in kwargs:
|
||||
pool_args[k] = kwargs.pop(tk)
|
||||
pool_args['use_threadlocal'] = self.pool_threadlocal()
|
||||
@@ -100,8 +107,14 @@ class DefaultEngineStrategy(EngineStrategy):
|
||||
|
||||
# all kwargs should be consumed
|
||||
if kwargs:
|
||||
raise TypeError("Invalid argument(s) %s sent to create_engine(), using configuration %s/%s/%s. Please check that the keyword arguments are appropriate for this combination of components." % (','.join(["'%s'" % k for k in kwargs]), dialect.__class__.__name__, pool.__class__.__name__, engineclass.__name__))
|
||||
|
||||
raise TypeError(
|
||||
"Invalid argument(s) %s sent to create_engine(), "
|
||||
"using configuration %s/%s/%s. Please check that the "
|
||||
"keyword arguments are appropriate for this combination "
|
||||
"of components." % (','.join(["'%s'" % k for k in kwargs]),
|
||||
dialect.__class__.__name__,
|
||||
pool.__class__.__name__,
|
||||
engineclass.__name__))
|
||||
return engineclass(pool, dialect, u, **engine_args)
|
||||
|
||||
def pool_threadlocal(self):
|
||||
@@ -111,6 +124,8 @@ class DefaultEngineStrategy(EngineStrategy):
|
||||
raise NotImplementedError()
|
||||
|
||||
class PlainEngineStrategy(DefaultEngineStrategy):
|
||||
"""Strategy for configuring a regular Engine."""
|
||||
|
||||
def __init__(self):
|
||||
DefaultEngineStrategy.__init__(self, 'plain')
|
||||
|
||||
@@ -123,6 +138,8 @@ class PlainEngineStrategy(DefaultEngineStrategy):
|
||||
PlainEngineStrategy()
|
||||
|
||||
class ThreadLocalEngineStrategy(DefaultEngineStrategy):
|
||||
"""Strategy for configuring an Engine with thredlocal behavior."""
|
||||
|
||||
def __init__(self):
|
||||
DefaultEngineStrategy.__init__(self, 'threadlocal')
|
||||
|
||||
@@ -136,11 +153,15 @@ ThreadLocalEngineStrategy()
|
||||
|
||||
|
||||
class MockEngineStrategy(EngineStrategy):
|
||||
"""Produces a single Connection object which dispatches statement executions
|
||||
to a passed-in function"""
|
||||
"""Strategy for configuring an Engine-like object with mocked execution.
|
||||
|
||||
Produces a single mock Connectable object which dispatches
|
||||
statement execution to a passed-in function.
|
||||
"""
|
||||
|
||||
def __init__(self):
|
||||
EngineStrategy.__init__(self, 'mock')
|
||||
|
||||
|
||||
def create(self, name_or_url, executor, **kwargs):
|
||||
# create url.URL object
|
||||
u = url.make_url(name_or_url)
|
||||
@@ -165,12 +186,13 @@ class MockEngineStrategy(EngineStrategy):
|
||||
|
||||
engine = property(lambda s: s)
|
||||
dialect = property(lambda s:s._dialect)
|
||||
|
||||
|
||||
def contextual_connect(self, **kwargs):
|
||||
return self
|
||||
|
||||
def compiler(self, statement, parameters, **kwargs):
|
||||
return self._dialect.compiler(statement, parameters, engine=self, **kwargs)
|
||||
return self._dialect.compiler(
|
||||
statement, parameters, engine=self, **kwargs)
|
||||
|
||||
def create(self, entity, **kwargs):
|
||||
kwargs['checkfirst'] = False
|
||||
|
||||
@@ -1,11 +1,10 @@
|
||||
from sqlalchemy import util
|
||||
from sqlalchemy.engine import base
|
||||
|
||||
"""Provide a thread-local transactional wrapper around the root Engine class.
|
||||
"""Provides a thread-local transactional wrapper around the root Engine class.
|
||||
|
||||
Multiple calls to engine.connect() will return the same connection for
|
||||
the same thread. also provides begin/commit methods on the engine
|
||||
itself which correspond to a thread-local transaction.
|
||||
Provides begin/commit methods on the engine itself which correspond to
|
||||
a thread-local transaction.
|
||||
"""
|
||||
|
||||
class TLSession(object):
|
||||
@@ -32,10 +31,10 @@ class TLSession(object):
|
||||
if self.__tcount == 1:
|
||||
self.__trans._trans.rollback()
|
||||
self.reset()
|
||||
|
||||
|
||||
def in_transaction(self):
|
||||
return self.__tcount > 0
|
||||
|
||||
|
||||
def prepare(self):
|
||||
if self.__tcount == 1:
|
||||
try:
|
||||
@@ -44,9 +43,11 @@ class TLSession(object):
|
||||
self.reset()
|
||||
|
||||
def begin_twophase(self, xid=None):
|
||||
raise NotImplementedError("Two phase transactions not yet implemented for 'threadlocal' strategy")
|
||||
|
||||
def _dont_begin_twophase(self, xid=None):
|
||||
raise NotImplementedError(
|
||||
"Two phase transactions not yet implemented for 'threadlocal' "
|
||||
"strategy")
|
||||
|
||||
def _dont_begin_twophase(self, xid=None):
|
||||
if self.__tcount == 0:
|
||||
self.__transaction = self.get_connection()
|
||||
self.__trans = self.__transaction._begin_twophase(xid=xid)
|
||||
@@ -79,9 +80,11 @@ class TLSession(object):
|
||||
def is_begun(self):
|
||||
return self.__tcount > 0
|
||||
|
||||
|
||||
class TLConnection(base.Connection):
|
||||
def __init__(self, session, close_with_result):
|
||||
base.Connection.__init__(self, session.engine, close_with_result=close_with_result)
|
||||
base.Connection.__init__(self, session.engine,
|
||||
close_with_result=close_with_result)
|
||||
self.__session = session
|
||||
self.__opencount = 1
|
||||
|
||||
@@ -92,11 +95,13 @@ class TLConnection(base.Connection):
|
||||
return self
|
||||
|
||||
def _begin(self, **kwargs):
|
||||
return TLTransaction(super(TLConnection, self).begin(**kwargs), self.__session)
|
||||
|
||||
return TLTransaction(
|
||||
super(TLConnection, self).begin(**kwargs), self.__session)
|
||||
|
||||
def _begin_twophase(self, xid=None):
|
||||
return TLTransaction(super(TLConnection, self).begin_twophase(xid=xid), self.__session)
|
||||
|
||||
return TLTransaction(
|
||||
super(TLConnection, self).begin_twophase(xid=xid), self.__session)
|
||||
|
||||
def in_transaction(self):
|
||||
return self.session.in_transaction()
|
||||
|
||||
@@ -116,6 +121,7 @@ class TLConnection(base.Connection):
|
||||
self.__opencount = 0
|
||||
base.Connection.close(self)
|
||||
|
||||
|
||||
class TLTransaction(base.Transaction):
|
||||
def __init__(self, trans, session):
|
||||
self._trans = trans
|
||||
@@ -129,7 +135,7 @@ class TLTransaction(base.Transaction):
|
||||
|
||||
def prepare(self):
|
||||
self._session.prepare()
|
||||
|
||||
|
||||
def commit(self):
|
||||
self._session.commit()
|
||||
|
||||
@@ -143,22 +149,19 @@ class TLTransaction(base.Transaction):
|
||||
class TLEngine(base.Engine):
|
||||
"""An Engine that includes support for thread-local managed transactions.
|
||||
|
||||
This engine is better suited to be used with threadlocal Pool
|
||||
object.
|
||||
The TLEngine relies upon its Pool having "threadlocal" behavior,
|
||||
so that once a connection is checked out for the current thread,
|
||||
you get that same connection repeatedly.
|
||||
"""
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
"""The TLEngine relies upon the Pool having
|
||||
"threadlocal" behavior, so that once a connection is checked out
|
||||
for the current thread, you get that same connection
|
||||
repeatedly.
|
||||
"""
|
||||
"""Construct a new TLEngine."""
|
||||
|
||||
super(TLEngine, self).__init__(*args, **kwargs)
|
||||
self.context = util.ThreadLocal()
|
||||
|
||||
def raw_connection(self):
|
||||
"""Return a DBAPI connection."""
|
||||
"""Return a DB-API connection."""
|
||||
|
||||
return self.pool.connect()
|
||||
|
||||
@@ -166,7 +169,7 @@ class TLEngine(base.Engine):
|
||||
"""Return a Connection that is not thread-locally scoped.
|
||||
|
||||
This is the equivalent to calling ``connect()`` on a
|
||||
ComposedSQLEngine.
|
||||
base.Engine.
|
||||
"""
|
||||
|
||||
return base.Connection(self, self.pool.unique_connection())
|
||||
@@ -176,7 +179,7 @@ class TLEngine(base.Engine):
|
||||
self.context.session = TLSession(self)
|
||||
return self.context.session
|
||||
|
||||
session = property(_session, doc="returns the current thread's TLSession")
|
||||
session = property(_session, doc="Returns the current thread's TLSession")
|
||||
|
||||
def contextual_connect(self, **kwargs):
|
||||
"""Return a TLConnection which is thread-locally scoped."""
|
||||
@@ -192,3 +195,5 @@ class TLEngine(base.Engine):
|
||||
def rollback(self):
|
||||
self.session.rollback()
|
||||
|
||||
def __repr__(self):
|
||||
return 'TLEngine(%s)' % str(self.url)
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
"""Provide the URL object as well as the make_url parsing function."""
|
||||
"""Provides URL facilities for specifying database connections."""
|
||||
|
||||
import re, cgi, sys, urllib
|
||||
from sqlalchemy import exceptions
|
||||
@@ -7,15 +7,16 @@ from sqlalchemy import exceptions
|
||||
class URL(object):
|
||||
"""Represent the components of a URL used to connect to a database.
|
||||
|
||||
This object is suitable to be passed directly to a ``create_engine()``
|
||||
call. The fields of the URL are parsed from a string by the
|
||||
``module-level make_url()`` function. the string format of the URL is
|
||||
an RFC-1738-style string.
|
||||
This object is suitable to be passed directly to a
|
||||
``create_engine()`` call. The fields of the URL are parsed from a
|
||||
string by the ``module-level make_url()`` function. the string
|
||||
format of the URL is an RFC-1738-style string.
|
||||
|
||||
Attributes on URL include:
|
||||
|
||||
drivername
|
||||
The name of the database backend. this name will correspond to a module in sqlalchemy/databases
|
||||
the name of the database backend. This name will correspond to
|
||||
a module in sqlalchemy/databases or a third party plug-in.
|
||||
|
||||
username
|
||||
The user name for the connection.
|
||||
@@ -33,7 +34,8 @@ class URL(object):
|
||||
The database.
|
||||
|
||||
query
|
||||
A dictionary containing key/value pairs representing the URL's query string.
|
||||
A dictionary containing key/value pairs representing the URL's
|
||||
query string.
|
||||
"""
|
||||
|
||||
def __init__(self, drivername, username=None, password=None, host=None, port=None, database=None, query=None):
|
||||
@@ -100,7 +102,7 @@ class URL(object):
|
||||
raise ImportError('unknown database %r' % self.drivername)
|
||||
|
||||
def translate_connect_args(self, names):
|
||||
"""Translate this URL's attributes into a dictionary of connection arguments.
|
||||
"""Translate attributes into a dictionary of connection arguments.
|
||||
|
||||
Given a list of argument names corresponding to the URL
|
||||
attributes (`host`, `database`, `username`, `password`,
|
||||
@@ -121,7 +123,7 @@ class URL(object):
|
||||
def make_url(name_or_url):
|
||||
"""Given a string or unicode instance, produce a new URL instance.
|
||||
|
||||
The given string is parsed according to the rfc1738 spec. If an
|
||||
The given string is parsed according to the RFC 1738 spec. If an
|
||||
existing URL object is passed, just returns the object.
|
||||
"""
|
||||
|
||||
|
||||
Reference in New Issue
Block a user