summaryrefslogtreecommitdiff
path: root/lib/sqlalchemy/engine/base.py
diff options
context:
space:
mode:
Diffstat (limited to 'lib/sqlalchemy/engine/base.py')
-rw-r--r--lib/sqlalchemy/engine/base.py748
1 files changed, 523 insertions, 225 deletions
diff --git a/lib/sqlalchemy/engine/base.py b/lib/sqlalchemy/engine/base.py
index 1985bcec1..10001e8a3 100644
--- a/lib/sqlalchemy/engine/base.py
+++ b/lib/sqlalchemy/engine/base.py
@@ -1,257 +1,431 @@
from sqlalchemy import exceptions, sql, schema, util, types, logging
import StringIO, sys, re
+
class ConnectionProvider(object):
- """defines an interface that returns raw Connection objects (or compatible)."""
+ """Define an interface that returns raw Connection objects (or compatible)."""
+
def get_connection(self):
- """this method should return a Connection or compatible object from a DBAPI which
- also contains a close() method.
- It is not defined what context this connection belongs to. It may be newly connected,
- returned from a pool, part of some other kind of context such as thread-local,
- or can be a fixed member of this object."""
+ """Return a Connection or compatible object from a DBAPI which also contains a close() method.
+
+ It is not defined what context this connection belongs to. It
+ may be newly connected, returned from a pool, part of some
+ other kind of context such as thread-local, or can be a fixed
+ member of this object.
+ """
+
raise NotImplementedError()
+
def dispose(self):
- """releases all resources corresponding to this ConnectionProvider, such
- as any underlying connection pools."""
+ """Release all resources corresponding to this ConnectionProvider.
+
+ This includes any underlying connection pools.
+ """
+
raise NotImplementedError()
+
class Dialect(sql.AbstractDialect):
- """Defines the behavior of a specific database/DBAPI.
+ """Define the behavior of a specific database/DBAPI.
+
+ 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 defintion, 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 DBAPIs support multiple paramstyles)
- supports_autoclose_results - usually True; if False, indicates that rows returned by fetchone()
- might not be just plain tuples, and may be "live" proxy objects which still require the cursor
- to be open in order to be read (such as pyPgSQL which has active filehandles for BLOBs). in that
- case, an auto-closing ResultProxy cannot automatically close itself after results are consumed.
+ supports_autoclose_results
+ Usually True; if False, indicates that rows returned by
+ fetchone() might not be just plain tuples, and may be
+ "live" proxy objects which still require the cursor to be open
+ in order to be read (such as pyPgSQL which has active
+ filehandles for BLOBs). In that case, an auto-closing
+ ResultProxy cannot automatically close itself after results are
+ consumed.
- 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'
"""
+
def create_connect_args(self, opts):
- """given a dictionary of key-valued connect parameters, returns a tuple
- consisting of a *args/**kwargs suitable to send directly to the dbapi's connect function.
- The connect args will have any number of the following keynames: host, hostname, database, dbanme,
- user,username, password, pw, passwd, filename."""
+ """Build DBAPI compatible connection arguments.
+
+ Given a dictionary of key-valued connect parameters, returns a
+ tuple consisting of a `*args`/`**kwargs` suitable to send directly
+ to the dbapi's connect function. The connect args will have
+ any number of the following keynames: host, hostname,
+ database, dbname, user, username, password, pw, passwd,
+ filename.
+ """
+
raise NotImplementedError()
+
def convert_compiled_params(self, parameters):
- """given a sql.ClauseParameters object, returns an array or dictionary suitable to pass
- directly to this Dialect's DBAPI's execute method."""
+ """Build DBAPI execute arguments from a ClauseParameters.
+
+ Given a sql.ClauseParameters object, returns an array or
+ dictionary suitable to pass directly to this Dialect's DBAPI's
+ execute method.
+ """
+
+ raise NotImplementedError()
+
def type_descriptor(self, typeobj):
- """provides a database-specific TypeEngine object, given the generic object
- which comes from the types module. Subclasses will usually use the adapt_type()
- method in the types module to make this job easy."""
+ """Trasform the type from generic to database-specific.
+
+ Provides a database-specific TypeEngine object, given the
+ generic object which comes from the types module. Subclasses
+ will usually use the 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 cant/wont support OID/ROWID.
-
- the 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.)
+ """Return the oid column name for this dialect, or None if the dialect cant/wont support OID/ROWID.
+
+ The 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_sane_rowcount(self):
- """Provided to indicate when MySQL is being used, which does not have standard behavior
- for the "rowcount" function on a statement handle. """
+ """Indicate whether the dialect properly implements statements rowcount.
+
+ Provided to indicate when MySQL is being used, which does not
+ have standard behavior for the "rowcount" function on a statement handle.
+ """
+
raise NotImplementedError()
+
def schemagenerator(self, engine, proxy, **params):
- """returns a schema.SchemaVisitor instance that can generate schemas, when it is
- invoked to traverse a set of schema objects.
+ """Return a ``schema.SchemaVisitor`` instance that can generate schemas.
- schemagenerator is called via the create() method on Table, Index, and others.
+ `schemagenerator()` is called via the `create()` method on Table,
+ Index, and others.
"""
+
raise NotImplementedError()
+
def schemadropper(self, engine, proxy, **params):
- """returns a schema.SchemaVisitor instance that can drop schemas, when it is
- invoked to traverse a set of schema objects.
+ """Return a ``schema.SchemaVisitor`` instance that can drop schemas.
- schemagenerator is called via the drop() method on Table, Index, and others.
+ `schemadropper()` is called via the `drop()` method on Table,
+ Index, and others.
"""
+
raise NotImplementedError()
+
def defaultrunner(self, engine, proxy, **params):
- """returns a schema.SchemaVisitor instances that can execute defaults."""
+ """Return a ``schema.SchemaVisitor`` instance that can execute defaults."""
+
raise NotImplementedError()
+
def compiler(self, statement, parameters):
- """returns a sql.ClauseVisitor which will produce a string representation of the given
- ClauseElement and parameter dictionary. This object is usually a subclass of
- ansisql.ANSICompiler.
+ """Return a ``sql.ClauseVisitor`` able to transform a ``ClauseElement`` into a string.
+
+ The returned object is usually a subclass of
+ ansisql.ANSICompiler, and will produce a string representation
+ of the given ClauseElement and `parameters` dictionary.
+
+ `compiler()` is called within the context of the compile() method.
+ """
- compiler is called within the context of the compile() method."""
raise NotImplementedError()
+
def reflecttable(self, connection, table):
- """given an Connection and a Table object, reflects its columns and properties from the database."""
+ """Load table description from the database.
+
+ Given a ``Connection`` and a ``Table`` object, reflect its
+ columns and properties from the database.
+ """
+
raise NotImplementedError()
+
def has_table(self, connection, table_name, schema=None):
+ """Check the existence of a particular table in the database.
+
+ Given a ``Connection`` object and a `table_name`, return True
+ if the given table (possibly within the specified `schema`)
+ exists in the database, False otherwise.
+ """
+
raise NotImplementedError()
+
def has_sequence(self, connection, sequence_name):
+ """Check the existence of a particular sequence in the database.
+
+ Given a ``Connection`` object and a `sequence_name`, return
+ True if the given sequence exists in the database, False
+ otherwise.
+ """
+
raise NotImplementedError()
+
def dbapi(self):
- """subclasses override this method to provide the DBAPI module used to establish
- connections."""
+ """Establish a connection to the database.
+
+ Subclasses override this method to provide the DBAPI module
+ used to establish connections.
+ """
+
raise NotImplementedError()
+
def get_default_schema_name(self, connection):
- """returns the currently selected schema given an connection"""
+ """Return the currently selected schema given a connection"""
+
raise NotImplementedError()
+
def execution_context(self):
- """returns a new ExecutionContext object."""
+ """Return a new ExecutionContext object."""
+
raise NotImplementedError()
+
def do_begin(self, connection):
- """provides an implementation of connection.begin()"""
+ """Provide an implementation of connection.begin()."""
+
raise NotImplementedError()
+
def do_rollback(self, connection):
- """provides an implementation of connection.rollback()"""
+ """Provide an implementation of connection.rollback()."""
+
raise NotImplementedError()
+
def do_commit(self, connection):
- """provides an implementation of connection.commit()"""
+ """Provide an implementation of connection.commit()"""
+
raise NotImplementedError()
+
def do_executemany(self, cursor, statement, parameters):
+ """Execute a single SQL statement looping over a sequence of parameters."""
+
raise NotImplementedError()
+
def do_execute(self, cursor, statement, parameters):
+ """Execute a single SQL statement with given parameters."""
+
raise NotImplementedError()
+
def create_cursor(self, connection):
- """return a new cursor generated from the given connection"""
+ """Return a new cursor generated from the given connection."""
+
raise NotImplementedError()
+
def create_result_proxy_args(self, connection, cursor):
- """returns a dictionary of arguments that should be passed to ResultProxy()."""
+ """Return a dictionary of arguments that should be passed to ResultProxy()."""
+
raise NotImplementedError()
+
def compile(self, clauseelement, parameters=None):
- """compile the given ClauseElement using this Dialect.
-
- a convenience method which simply flips around the compile() call
- on ClauseElement."""
+ """Compile the given ClauseElement using this Dialect.
+
+ A convenience method which simply flips around the compile()
+ call on ClauseElement.
+ """
+
return clauseelement.compile(dialect=self, parameters=parameters)
-
+
+
class ExecutionContext(object):
- """a messenger object for a Dialect that corresponds to a single execution. 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, afterwhich
- it is expected that the various methods last_inserted_ids, last_inserted_params, etc.
- will contain appropriate values, if applicable."""
+ """A messenger object for a Dialect that corresponds to a single execution.
+
+ 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, afterwhich it is
+ expected that the various methods `last_inserted_ids`,
+ `last_inserted_params`, etc. will contain appropriate values, if
+ applicable.
+ """
+
def pre_exec(self, engine, proxy, compiled, parameters):
- """called before an execution of a compiled statement. proxy is a callable that
- takes a string statement and a bind parameter list/dictionary."""
+ """Called before an execution of a compiled statement.
+
+ `proxy` is a callable that takes a string statement and a bind
+ parameter list/dictionary.
+ """
+
raise NotImplementedError()
+
def post_exec(self, engine, proxy, compiled, parameters):
- """called after the execution of a compiled statement. proxy is a callable that
- takes a string statement and a bind parameter list/dictionary."""
+ """Called after the execution of a compiled statement.
+
+ `proxy` is a callable that takes a string statement and a bind
+ parameter list/dictionary.
+ """
+
raise NotImplementedError()
+
def get_rowcount(self, cursor):
- """returns the count of rows updated/deleted for an UPDATE/DELETE statement"""
+ """Return the count of rows updated/deleted for an UPDATE/DELETE statement."""
+
raise NotImplementedError()
+
def supports_sane_rowcount(self):
- """Indicates if the "rowcount" DBAPI cursor function works properly.
-
- Currently, MySQLDB does not properly implement this function."""
+ """Indicate if the "rowcount" DBAPI cursor function works properly.
+
+ Currently, MySQLDB does not properly implement this function.
+ """
+
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, which are executed via statement.execute(). The order of items in the
- list is the same as that of the Table's 'primary_key' attribute.
-
- In some cases, this method may invoke a query back to the database to retrieve the data, based on
- the "lastrowid" value in the cursor."""
+ """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,
+ which are executed via `statement.execute()`. The order of
+ items in the list is the same as that of the Table's
+ 'primary_key' attribute.
+
+ In some cases, this method may invoke a query back to the
+ database to retrieve the data, based on the "lastrowid" value
+ in the cursor.
+ """
+
raise NotImplementedError()
+
def last_inserted_params(self):
- """return a dictionary of the full parameter dictionary for the last compiled INSERT statement.
-
- Includes any ColumnDefaults or Sequences that were pre-executed."""
+ """Return a dictionary of the full parameter dictionary for the last compiled INSERT statement.
+
+ Includes any ColumnDefaults or Sequences that were pre-executed.
+ """
+
raise NotImplementedError()
+
def last_updated_params(self):
- """return a dictionary of the full parameter dictionary for the last compiled UPDATE statement.
-
- Includes any ColumnDefaults that were pre-executed."""
+ """Return a dictionary of the full parameter dictionary for the last compiled UPDATE statement.
+
+ Includes any ColumnDefaults that were pre-executed.
+ """
+
raise NotImplementedError()
+
def lastrow_has_defaults(self):
- """return True if the last row INSERTED via a compiled insert statement contained PassiveDefaults.
-
- The presence of PassiveDefaults indicates that the database inserted data beyond that which we
- passed to the query programmatically."""
+ """Return True if the last row INSERTED via a compiled insert statement contained PassiveDefaults.
+
+ The presence of PassiveDefaults indicates that the database
+ inserted data beyond that which we passed to the query
+ programmatically.
+ """
+
raise NotImplementedError()
+
class Connectable(object):
- """interface for an object that can provide an Engine and a Connection object which correponds to that Engine."""
+ """Interface for an object that can provide an Engine and a Connection object which correponds to that Engine."""
+
def contextual_connect(self):
- """returns a Connection object which may be part of an ongoing context."""
+ """Return a Connection object which may be part of an ongoing context."""
+
raise NotImplementedError()
+
def create(self, entity, **kwargs):
- """creates a table or index given an appropriate schema object."""
+ """Create a table or index given an appropriate schema object."""
+
raise NotImplementedError()
+
def drop(self, entity, **kwargs):
+ """Drop a table or index given an appropriate schema object."""
+
raise NotImplementedError()
+
def execute(self, object, *multiparams, **params):
raise NotImplementedError()
+
def _not_impl(self):
raise NotImplementedError()
- engine = property(_not_impl, doc="returns the Engine which this Connectable is associated with.")
+
+ engine = property(_not_impl, doc="The Engine which this Connectable is associated with.")
class Connection(Connectable):
- """represents a single DBAPI connection returned from the underlying connection pool. Provides
- execution support for string-based SQL statements as well as ClauseElement, Compiled and DefaultGenerator objects.
- provides a begin method to return Transaction objects.
-
- The Connection object is **not** threadsafe."""
+ """Represent a single DBAPI connection returned from the underlying connection pool.
+
+ Provides execution support for string-based SQL statements as well
+ as ClauseElement, Compiled and DefaultGenerator objects. Provides
+ a begin method to return Transaction objects.
+
+ The Connection object is **not** threadsafe.
+ """
+
def __init__(self, engine, connection=None, close_with_result=False):
self.__engine = engine
self.__connection = connection or engine.raw_connection()
self.__transaction = None
self.__close_with_result = close_with_result
+
def _get_connection(self):
try:
return self.__connection
except AttributeError:
raise exceptions.InvalidRequestError("This Connection is closed")
+
engine = property(lambda s:s.__engine, doc="The Engine with which this Connection is associated (read only)")
connection = property(_get_connection, doc="The underlying DBAPI 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.")
+
def _create_transaction(self, parent):
return Transaction(self, parent)
+
def connect(self):
"""connect() is implemented to return self so that an incoming Engine or Connection object can be treated similarly."""
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."""
return self
+
def begin(self):
if self.__transaction is None:
self.__transaction = self._create_transaction(None)
return self.__transaction
else:
return self._create_transaction(self.__transaction)
+
def in_transaction(self):
return self.__transaction is not None
+
def _begin_impl(self):
self.__engine.logger.info("BEGIN")
self.__engine.dialect.do_begin(self.connection)
+
def _rollback_impl(self):
self.__engine.logger.info("ROLLBACK")
self.__engine.dialect.do_rollback(self.connection)
self.__connection.close_open_cursors()
self.__transaction = None
+
def _commit_impl(self):
self.__engine.logger.info("COMMIT")
self.__engine.dialect.do_commit(self.connection)
self.__transaction = None
+
def _autocommit(self, statement):
- """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
+ """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
if not self.in_transaction() and re.match(r'UPDATE|INSERT|CREATE|DELETE|DROP|ALTER', statement.lstrip().upper()):
self._commit_impl()
+
def _autorollback(self):
if not self.in_transaction():
self._rollback_impl()
+
def close(self):
try:
c = self.__connection
@@ -260,12 +434,16 @@ class Connection(Connectable):
self.__connection.close()
self.__connection = None
del self.__connection
+
def scalar(self, object, *multiparams, **params):
return self.execute(object, *multiparams, **params).scalar()
+
def execute(self, object, *multiparams, **params):
return Connection.executors[type(object).__mro__[-2]](self, object, *multiparams, **params)
+
def execute_default(self, default, **kwargs):
return default.accept_schema_visitor(self.__engine.dialect.defaultrunner(self.__engine, self.proxy, **kwargs))
+
def execute_text(self, statement, *multiparams, **params):
if len(multiparams) == 0:
parameters = params
@@ -276,6 +454,7 @@ class Connection(Connectable):
cursor = self._execute_raw(statement, parameters)
rpargs = self.__engine.dialect.create_result_proxy_args(self, cursor)
return ResultProxy(self.__engine, self, cursor, **rpargs)
+
def _params_to_listofdicts(self, *multiparams, **params):
if len(multiparams) == 0:
return [params]
@@ -288,6 +467,7 @@ class Connection(Connectable):
return [multiparams[0]]
else:
return multiparams
+
def execute_clauseelement(self, elem, *multiparams, **params):
executemany = len(multiparams) > 0
if executemany:
@@ -295,8 +475,10 @@ class Connection(Connectable):
else:
param = params
return self.execute_compiled(elem.compile(engine=self.__engine, parameters=param), *multiparams, **params)
+
def execute_compiled(self, compiled, *multiparams, **params):
- """executes a sql.Compiled object."""
+ """Execute a sql.Compiled object."""
+
if not compiled.can_execute:
raise exceptions.ArgumentError("Not an executeable clause: %s" % (str(compiled)))
cursor = self.__engine.dialect.create_cursor(self.connection)
@@ -316,7 +498,7 @@ class Connection(Connectable):
context.post_exec(self.__engine, proxy, compiled, parameters)
rpargs = self.__engine.dialect.create_result_proxy_args(self, cursor)
return ResultProxy(self.__engine, self, cursor, context, typemap=compiled.typemap, columns=compiled.columns, **rpargs)
-
+
# poor man's multimethod/generic function thingy
executors = {
sql.ClauseElement : execute_clauseelement,
@@ -324,20 +506,28 @@ class Connection(Connectable):
schema.SchemaItem:execute_default,
str.__mro__[-2] : execute_text
}
-
+
def create(self, entity, **kwargs):
- """creates a table or index given an appropriate schema object."""
+ """Create a table or index given an appropriate schema object."""
+
return self.__engine.create(entity, connection=self, **kwargs)
+
def drop(self, entity, **kwargs):
- """drops a table or index given an appropriate schema object."""
+ """Drop a table or index given an appropriate schema object."""
+
return self.__engine.drop(entity, connection=self, **kwargs)
+
def reflecttable(self, table, **kwargs):
- """reflects the columns in the given table from the database."""
+ """Reflect the columns in the given table from the database."""
+
return self.__engine.reflecttable(table, connection=self, **kwargs)
+
def default_schema_name(self):
return self.__engine.dialect.get_default_schema_name(self)
+
def run_callable(self, callable_):
return callable_(self)
+
def _execute_raw(self, statement, parameters=None, cursor=None, context=None, **kwargs):
if cursor is None:
cursor = self.__engine.dialect.create_cursor(self.connection)
@@ -367,6 +557,7 @@ class Connection(Connectable):
if self.__close_with_result:
self.close()
raise exceptions.SQLError(statement, parameters, e)
+
def _executemany(self, c, statement, parameters, context=None):
try:
self.__engine.dialect.do_executemany(c, statement, parameters, context=context)
@@ -376,27 +567,36 @@ class Connection(Connectable):
if self.__close_with_result:
self.close()
raise exceptions.SQLError(statement, parameters, e)
+
def proxy(self, statement=None, parameters=None):
- """executes the given statement string and parameter object.
- the parameter object is expected to be the result of a call to compiled.get_params().
- This callable is a generic version of a connection/cursor-specific callable that
- is produced within the execute_compiled method, and is used for objects that require
- this style of proxy when outside of an execute_compiled method, primarily the DefaultRunner."""
+ """Execute the given statement string and parameter object.
+
+ The parameter object is expected to be the result of a call to
+ ``compiled.get_params()``. This callable is a generic version
+ of a connection/cursor-specific callable that is produced
+ within the execute_compiled method, and is used for objects
+ that require this style of proxy when outside of an
+ execute_compiled method, primarily the DefaultRunner.
+ """
parameters = self.__engine.dialect.convert_compiled_params(parameters)
return self._execute_raw(statement, parameters)
class Transaction(object):
- """represents a Transaction in progress.
-
- the Transaction object is **not** threadsafe."""
+ """Represent a Transaction in progress.
+
+ The Transaction object is **not** threadsafe.
+ """
+
def __init__(self, connection, parent):
self.__connection = connection
self.__parent = parent or self
self.__is_active = True
if self.__parent is self:
self.__connection._begin_impl()
+
connection = property(lambda s:s.__connection, doc="The Connection object referenced by this Transaction")
is_active = property(lambda s:s.__is_active)
+
def rollback(self):
if not self.__parent.__is_active:
return
@@ -405,6 +605,7 @@ class Transaction(object):
self.__is_active = False
else:
self.__parent.rollback()
+
def commit(self):
if not self.__parent.__is_active:
raise exceptions.InvalidRequestError("This transaction is inactive")
@@ -414,9 +615,10 @@ class Transaction(object):
class Engine(sql.Executor, Connectable):
"""
- Connects a ConnectionProvider, a Dialect and a CompilerFactory together to
+ Connects a ConnectionProvider, a Dialect and a CompilerFactory together to
provide a default implementation of SchemaEngine.
"""
+
def __init__(self, connection_provider, dialect, echo=None):
self.connection_provider = connection_provider
self.dialect=dialect
@@ -426,27 +628,35 @@ class Engine(sql.Executor, Connectable):
name = property(lambda s:sys.modules[s.dialect.__module__].descriptor()['name'])
engine = property(lambda s:s)
echo = logging.echo_property()
-
+
def dispose(self):
self.connection_provider.dispose()
+
def create(self, entity, connection=None, **kwargs):
- """creates a table or index within this engine's database connection given a schema.Table object."""
+ """Create a table or index within this engine's database connection given a schema.Table object."""
+
self._run_visitor(self.dialect.schemagenerator, entity, connection=connection, **kwargs)
+
def drop(self, entity, connection=None, **kwargs):
- """drops a table or index within this engine's database connection given a schema.Table object."""
+ """Drop a table or index within this engine's database connection given a schema.Table object."""
+
self._run_visitor(self.dialect.schemadropper, entity, connection=connection, **kwargs)
+
def execute_default(self, default, **kwargs):
connection = self.contextual_connect()
try:
return connection.execute_default(default, **kwargs)
finally:
connection.close()
-
+
def _func(self):
return sql._FunctionGenerator(self)
+
func = property(_func)
+
def text(self, text, *args, **kwargs):
- """returns a sql.text() object for performing literal queries."""
+ """Return a sql.text() object for performing literal queries."""
+
return sql.text(text, engine=self, *args, **kwargs)
def _run_visitor(self, visitorcallable, element, connection=None, **kwargs):
@@ -459,12 +669,16 @@ class Engine(sql.Executor, Connectable):
finally:
if connection is None:
conn.close()
-
+
def transaction(self, callable_, connection=None, *args, **kwargs):
- """executes the given function within a transaction boundary. this is a shortcut for
- explicitly calling begin() and commit() and optionally rollback() when execptions are raised.
- The given *args and **kwargs will be passed to the function, as well as the Connection used
- in the transaction."""
+ """Execute the given function within a transaction boundary.
+
+ This is a shortcut for explicitly calling `begin()` and `commit()`
+ and optionally `rollback()` when exceptions are raised. The
+ given `*args` and `**kwargs` will be passed to the function, as
+ well as the Connection used in the transaction.
+ """
+
if connection is None:
conn = self.contextual_connect()
else:
@@ -481,7 +695,7 @@ class Engine(sql.Executor, Connectable):
finally:
if connection is None:
conn.close()
-
+
def run_callable(self, callable_, connection=None, *args, **kwargs):
if connection is None:
conn = self.contextual_connect()
@@ -492,32 +706,37 @@ class Engine(sql.Executor, Connectable):
finally:
if connection is None:
conn.close()
-
+
def execute(self, statement, *multiparams, **params):
connection = self.contextual_connect(close_with_result=True)
return connection.execute(statement, *multiparams, **params)
def scalar(self, statement, *multiparams, **params):
return self.execute(statement, *multiparams, **params).scalar()
-
+
def execute_compiled(self, compiled, *multiparams, **params):
connection = self.contextual_connect(close_with_result=True)
return connection.execute_compiled(compiled, *multiparams, **params)
-
+
def compiler(self, statement, parameters, **kwargs):
return self.dialect.compiler(statement, parameters, engine=self, **kwargs)
def connect(self, **kwargs):
- """returns a newly allocated Connection object."""
+ """Return a newly allocated Connection object."""
+
return Connection(self, **kwargs)
-
+
def contextual_connect(self, close_with_result=False, **kwargs):
- """returns a Connection object which may be newly allocated, or may be part of some
- ongoing context. This Connection is meant to be used by the various "auto-connecting" operations."""
+ """Return a Connection object which may be newly allocated, or may be part of some ongoing context.
+
+ This Connection is meant to be used by the various "auto-connecting" operations.
+ """
+
return Connection(self, close_with_result=close_with_result, **kwargs)
-
+
def reflecttable(self, table, connection=None):
- """given a Table object, reflects its columns and properties from the database."""
+ """Given a Table object, reflects its columns and properties from the database."""
+
if connection is None:
conn = self.contextual_connect()
else:
@@ -527,34 +746,42 @@ class Engine(sql.Executor, Connectable):
finally:
if connection is None:
conn.close()
+
def has_table(self, table_name, schema=None):
return self.run_callable(lambda c: self.dialect.has_table(c, table_name, schema=schema))
-
+
def raw_connection(self):
- """returns a DBAPI connection."""
+ """Return a DBAPI connection."""
+
return self.connection_provider.get_connection()
def log(self, msg):
- """logs a message using this SQLEngine's logger stream."""
+ """Log a message using this SQLEngine's logger stream."""
+
self.logger.info(msg)
class ResultProxy(object):
- """wraps a DBAPI cursor object to provide access to row columns based on integer
- position, case-insensitive column name, or by schema.Column object. e.g.:
-
- row = fetchone()
+ """Wraps a DBAPI 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``
+ object. e.g.::
+
+ row = fetchone()
- col1 = row[0] # access via integer position
+ col1 = row[0] # access via integer position
- col2 = row['col2'] # access via name
+ col2 = row['col2'] # access via name
- col3 = row[mytable.c.mycol] # access via Column object.
-
- ResultProxy also contains a map of TypeEngine objects and will invoke the appropriate
- convert_result_value() method before returning columns, as well as the ExecutionContext
- corresponding to the statement execution. It provides several methods for which
+ col3 = row[mytable.c.mycol] # access via Column object.
+
+ ResultProxy also contains a map of TypeEngine objects and will
+ invoke the appropriate ``convert_result_value()` method before
+ returning columns, as well as the ExecutionContext corresponding
+ to the statement execution. It provides several methods for which
to obtain information from the underlying ExecutionContext.
"""
+
class AmbiguousColumn(object):
def __init__(self, key):
self.key = key
@@ -562,15 +789,16 @@ class ResultProxy(object):
return self
def convert_result_value(self, arg, engine):
raise exceptions.InvalidRequestError("Ambiguous column name '%s' in result set! try 'use_labels' option on select statement." % (self.key))
-
+
def __new__(cls, *args, **kwargs):
if cls is ResultProxy and kwargs.has_key('should_prefetch') and kwargs['should_prefetch']:
return PrefetchingResultProxy(*args, **kwargs)
else:
return object.__new__(cls, *args, **kwargs)
-
+
def __init__(self, engine, connection, cursor, executioncontext=None, typemap=None, columns=None, should_prefetch=None):
"""ResultProxy objects are constructed via the execute() method on SQLEngine."""
+
self.connection = connection
self.dialect = engine.dialect
self.cursor = cursor
@@ -603,20 +831,25 @@ class ResultProxy(object):
self.keys.append(colname)
self.props[i] = rec
i+=1
+
def _executioncontext(self):
try:
return self.__executioncontext
except AttributeError:
raise exceptions.InvalidRequestError("This ResultProxy does not have an execution context with which to complete this operation. Execution contexts are not generated for literal SQL execution.")
executioncontext = property(_executioncontext)
-
+
def close(self):
- """close this ResultProxy, and the underlying DBAPI 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.)
-
- This method is also called automatically when all result rows are exhausted."""
+ """Close this ResultProxy, and the underlying DBAPI 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.)
+
+ This method is also called automatically when all result rows
+ are exhausted.
+ """
+
if not self.closed:
self.closed = True
self.cursor.close()
@@ -624,8 +857,13 @@ class ResultProxy(object):
self.connection.close()
def _convert_key(self, key):
- """given a key, which could be a ColumnElement, string, etc., matches it to the
- appropriate key we got from the result set's metadata; then cache it locally for quick re-access."""
+ """Convert and cache a key.
+
+ Given a key, which could be a ColumnElement, string, etc.,
+ matches it to the appropriate key we got from the result set's
+ metadata; then cache it locally for quick re-access.
+ """
+
try:
return self.__key_cache[key]
except KeyError:
@@ -659,18 +897,18 @@ class ResultProxy(object):
raise exceptions.NoSuchColumnError("Could not locate column in row for column '%s'" % str(key))
self.__key_cache[key] = rec
return rec
-
+
def _has_key(self, row, key):
try:
self._convert_key(key)
return True
except KeyError:
return False
-
+
def _get_col(self, row, key):
rec = self._convert_key(key)
return rec[0].dialect_impl(self.dialect).convert_result_value(row[rec[1]], self.dialect)
-
+
def __iter__(self):
while True:
row = self.fetchone()
@@ -678,43 +916,59 @@ class ResultProxy(object):
raise StopIteration
else:
yield row
-
+
def last_inserted_ids(self):
- """return last_inserted_ids() from the underlying ExecutionContext.
-
- See ExecutionContext for details."""
+ """Return ``last_inserted_ids()`` from the underlying ExecutionContext.
+
+ See ExecutionContext for details.
+ """
+
return self.executioncontext.last_inserted_ids()
+
def last_updated_params(self):
- """return last_updated_params() from the underlying ExecutionContext.
-
- See ExecutionContext for details."""
+ """Return ``last_updated_params()`` from the underlying ExecutionContext.
+
+ See ExecutionContext for details.
+ """
+
return self.executioncontext.last_updated_params()
+
def last_inserted_params(self):
- """return last_inserted_params() from the underlying ExecutionContext.
-
- See ExecutionContext for details."""
+ """Return ``last_inserted_params()`` from the underlying ExecutionContext.
+
+ See ExecutionContext for details.
+ """
+
return self.executioncontext.last_inserted_params()
+
def lastrow_has_defaults(self):
- """return lastrow_has_defaults() from the underlying ExecutionContext.
-
- See ExecutionContext for details."""
+ """Return ``lastrow_has_defaults()`` from the underlying ExecutionContext.
+
+ See ExecutionContext for details.
+ """
+
return self.executioncontext.lastrow_has_defaults()
+
def supports_sane_rowcount(self):
- """return supports_sane_rowcount() from the underlying ExecutionContext.
-
- See ExecutionContext for details."""
+ """Return ``supports_sane_rowcount()`` from the underlying ExecutionContext.
+
+ See ExecutionContext for details.
+ """
+
return self.executioncontext.supports_sane_rowcount()
-
+
def fetchall(self):
- """fetch all rows, just like DBAPI cursor.fetchall()."""
+ """Fetch all rows, just like DBAPI ``cursor.fetchall()``."""
+
l = []
for row in self.cursor.fetchall():
l.append(RowProxy(self, row))
self.close()
return l
-
+
def fetchmany(self, size=None):
- """fetch many rows, juts like DBAPI cursor.fetchmany(size=cursor.arraysize)"""
+ """Fetch many rows, just like DBAPI ``cursor.fetchmany(size=cursor.arraysize)``."""
+
if size is None:
rows = self.cursor.fetchmany()
else:
@@ -725,9 +979,10 @@ class ResultProxy(object):
if len(l) == 0:
self.close()
return l
-
+
def fetchone(self):
- """fetch one row, just like DBAPI cursor.fetchone()."""
+ """Fetch one row, just like DBAPI ``cursor.fetchone()``."""
+
row = self.cursor.fetchone()
if row is not None:
return RowProxy(self, row)
@@ -736,7 +991,8 @@ class ResultProxy(object):
return None
def scalar(self):
- """fetch the first column of the first row, and close the result set."""
+ """Fetch the first column of the first row, and close the result set."""
+
row = self.cursor.fetchone()
try:
if row is not None:
@@ -745,16 +1001,17 @@ class ResultProxy(object):
return None
finally:
self.close()
-
+
class PrefetchingResultProxy(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.
"""
+
def _get_col(self, row, key):
rec = self._convert_key(key)
return row[rec[1]]
-
+
def fetchall(self):
l = []
while True:
@@ -764,7 +1021,7 @@ class PrefetchingResultProxy(ResultProxy):
else:
break
return l
-
+
def fetchmany(self, size=None):
if size is None:
return self.fetchall()
@@ -776,7 +1033,7 @@ class PrefetchingResultProxy(ResultProxy):
else:
break
return l
-
+
def fetchone(self):
sup = super(PrefetchingResultProxy, self)
row = self.cursor.fetchone()
@@ -786,81 +1043,114 @@ class PrefetchingResultProxy(ResultProxy):
else:
self.close()
return None
-
+
class RowProxy(object):
- """proxies a single cursor row for a parent ResultProxy. Mostly follows
- "ordered dictionary" behavior, mapping result values to the string-based column name,
- the integer position of the result in the row, as well as Column instances which
- can be mapped to the original Columns that produced this result set (for results
- that correspond to constructed SQL expressions)."""
+ """Proxie a single cursor row for a parent ResultProxy.
+
+ Mostly follows "ordered dictionary" behavior, mapping result
+ values to the string-based column name, the integer position of
+ the result in the row, as well as Column instances which can be
+ mapped to the original Columns that produced this result set (for
+ results that correspond to constructed SQL expressions).
+ """
+
def __init__(self, parent, row):
"""RowProxy objects are constructed by ResultProxy objects."""
+
self.__parent = parent
self.__row = row
if self.__parent._ResultProxy__echo:
self.__parent.engine.logger.debug("Row " + repr(row))
+
def close(self):
- """close the parent ResultProxy."""
+ """Close the parent ResultProxy."""
+
self.__parent.close()
+
def __iter__(self):
for i in range(0, len(self.__row)):
yield self.__parent._get_col(self.__row, i)
+
def __eq__(self, other):
return (other is self) or (other == tuple([self.__parent._get_col(self.__row, key) for key in range(0, len(self.__row))]))
+
def __repr__(self):
return repr(tuple([self.__parent._get_col(self.__row, key) for key in range(0, len(self.__row))]))
+
def has_key(self, key):
- """return True if this RowProxy contains the given key."""
+ """Return True if this RowProxy contains the given key."""
+
return self.__parent._has_key(self.__row, key)
+
def __getitem__(self, key):
return self.__parent._get_col(self.__row, key)
+
def __getattr__(self, name):
try:
return self.__parent._get_col(self.__row, name)
except KeyError, e:
raise AttributeError(e.args[0])
+
def items(self):
- """return a list of tuples, each tuple containing a key/value pair."""
+ """Return a list of tuples, each tuple containing a key/value pair."""
+
return [(key, getattr(self, key)) for key in self.keys()]
+
def keys(self):
- """return the list of keys as strings represented by this RowProxy."""
+ """Return the list of keys as strings represented by this RowProxy."""
+
return self.__parent.keys
+
def values(self):
- """return the values represented by this RowProxy as a list."""
+ """Return the values represented by this RowProxy as a list."""
+
return list(self)
- def __len__(self):
+
+ def __len__(self):
return len(self.__row)
class SchemaIterator(schema.SchemaVisitor):
- """a visitor that can gather text into a buffer and execute the contents of the buffer."""
+ """A visitor that can gather text into a buffer and execute the contents of the buffer."""
+
def __init__(self, engine, proxy, **params):
- """construct a new SchemaIterator.
-
- engine - the Engine used by this SchemaIterator
-
- proxy - a callable which takes a statement and bind parameters and executes it, returning
- the cursor (the actual DBAPI cursor). The callable should use the same cursor repeatedly."""
+ """Construct a new SchemaIterator.
+
+ engine
+ the Engine used by this SchemaIterator
+
+ proxy
+ a callable which takes a statement and bind parameters and
+ executes it, returning the cursor (the actual DBAPI cursor).
+ The callable should use the same cursor repeatedly.
+ """
+
self.proxy = proxy
self.engine = engine
self.buffer = StringIO.StringIO()
def append(self, s):
- """append content to the SchemaIterator's query buffer."""
+ """Append content to the SchemaIterator's query buffer."""
+
self.buffer.write(s)
def execute(self):
- """execute the contents of the SchemaIterator's buffer."""
+ """Execute the contents of the SchemaIterator's buffer."""
+
try:
return self.proxy(self.buffer.getvalue(), None)
finally:
self.buffer.truncate(0)
class DefaultRunner(schema.SchemaVisitor):
- """a visitor which accepts ColumnDefault objects, produces the dialect-specific SQL corresponding
- to their execution, and executes the SQL, returning the result value.
-
- DefaultRunners are used internally by Engines and Dialects. Specific database modules should provide
- their own subclasses of DefaultRunner to allow database-specific behavior."""
+ """A visitor which accepts ColumnDefault objects, produces the
+ dialect-specific SQL corresponding to their execution, and
+ executes the SQL, returning the result value.
+
+ DefaultRunners are used internally by Engines and Dialects.
+ Specific database modules should provide their own subclasses of
+ DefaultRunner to allow database-specific behavior.
+ """
+
def __init__(self, engine, proxy):
self.proxy = proxy
self.engine = engine
@@ -878,12 +1168,20 @@ class DefaultRunner(schema.SchemaVisitor):
return None
def visit_passive_default(self, default):
- """passive defaults by definition return None on the app side,
- and are post-fetched to get the DB-side value"""
+ """Do nothing.
+
+ Passive defaults by definition return None on the app side,
+ and are post-fetched to get the DB-side value.
+ """
+
return None
def visit_sequence(self, seq):
- """sequences are not supported by default"""
+ """Do nothing.
+
+ Sequences are not supported by default.
+ """
+
return None
def exec_default_sql(self, default):