Packages update

This commit is contained in:
Ruud
2012-02-11 16:28:06 +01:00
parent 3bbf1126c3
commit 02e01fb2d6
217 changed files with 26395 additions and 21194 deletions
+93 -35
View File
@@ -1,5 +1,5 @@
# engine/__init__.py
# Copyright (C) 2005-2011 the SQLAlchemy authors and contributors <see AUTHORS file>
# Copyright (C) 2005-2012 the SQLAlchemy authors and contributors <see AUTHORS file>
#
# This module is part of SQLAlchemy and is released under
# the MIT License: http://www.opensource.org/licenses/mit-license.php
@@ -99,52 +99,69 @@ __all__ = (
default_strategy = 'plain'
def create_engine(*args, **kwargs):
"""Create a new Engine instance.
"""Create a new :class:`.Engine` instance.
The standard method of specifying the engine is via URL as the
first positional argument, to indicate the appropriate database
dialect and connection arguments, with additional keyword
arguments sent as options to the dialect and resulting Engine.
The standard calling form is to send the URL as the
first positional argument, usually a string
that indicates database dialect and connection arguments.
Additional keyword arguments may then follow it which
establish various options on the resulting :class:`.Engine`
and its underlying :class:`.Dialect` and :class:`.Pool`
constructs.
The URL is a string in the form
The string form of the URL is
``dialect+driver://user:password@host/dbname[?key=value..]``, where
``dialect`` is a database name such as ``mysql``, ``oracle``,
``postgresql``, etc., and ``driver`` the name of a DBAPI, such as
``psycopg2``, ``pyodbc``, ``cx_oracle``, etc. Alternatively,
the URL can be an instance of :class:`~sqlalchemy.engine.url.URL`.
`**kwargs` takes a wide variety of options which are routed
``**kwargs`` takes a wide variety of options which are routed
towards their appropriate components. Arguments may be
specific to the Engine, the underlying Dialect, as well as the
Pool. Specific dialects also accept keyword arguments that
specific to the :class:`.Engine`, the underlying :class:`.Dialect`, as well as the
:class:`.Pool`. Specific dialects also accept keyword arguments that
are unique to that dialect. Here, we describe the parameters
that are common to most ``create_engine()`` usage.
that are common to most :func:`.create_engine()` usage.
:param assert_unicode: Deprecated. A warning is raised in all cases when a non-Unicode
object is passed when SQLAlchemy would coerce into an encoding
(note: but **not** when the DBAPI handles unicode objects natively).
To suppress or raise this warning to an
error, use the Python warnings filter documented at:
http://docs.python.org/library/warnings.html
Once established, the newly resulting :class:`.Engine` will
request a connection from the underlying :class:`.Pool` once
:meth:`.Engine.connect` is called, or a method which depends on it
such as :meth:`.Engine.execute` is invoked. The :class:`.Pool` in turn
will establish the first actual DBAPI connection when this request
is received. The :func:`.create_engine` call itself does **not**
establish any actual DBAPI connections directly.
See also:
:ref:`engines_toplevel`
:ref:`connections_toplevel`
:param assert_unicode: Deprecated. This flag
sets an engine-wide default value for
the ``assert_unicode`` flag on the
:class:`.String` type - see that
type for further details.
:param connect_args: a dictionary of options which will be
passed directly to the DBAPI's ``connect()`` method as
additional keyword arguments.
additional keyword arguments. See the example
at :ref:`custom_dbapi_args`.
:param convert_unicode=False: if set to True, all
String/character based types will convert Python Unicode values to raw
byte values sent to the DBAPI as bind parameters, and all raw byte values to
Python Unicode coming out in result sets. This is an
engine-wide method to provide Unicode conversion across the
board for those DBAPIs that do not accept Python Unicode objects
as input. For Unicode conversion on a column-by-column level, use
the ``Unicode`` column type instead, described in :ref:`types_toplevel`. Note that
many DBAPIs have the ability to return Python Unicode objects in
result sets directly - SQLAlchemy will use these modes of operation
if possible and will also attempt to detect "Unicode returns"
behavior by the DBAPI upon first connect by the
:class:`.Engine`. When this is detected, string values in
result sets are passed through without further processing.
:param convert_unicode=False: if set to True, sets
the default behavior of ``convert_unicode`` on the
:class:`.String` type to ``True``, regardless
of a setting of ``False`` on an individual
:class:`.String` type, thus causing all :class:`.String`
-based columns
to accommodate Python ``unicode`` objects. This flag
is useful as an engine-wide setting when using a
DBAPI that does not natively support Python
``unicode`` objects and raises an error when
one is received (such as pyodbc with FreeTDS).
See :class:`.String` for further details on
what this flag indicates.
:param creator: a callable which returns a DBAPI connection.
This creation function will be passed to the underlying
@@ -167,9 +184,50 @@ def create_engine(*args, **kwargs):
:ref:`dbengine_logging` for information on how to configure logging
directly.
:param encoding='utf-8': the encoding to use for all Unicode
translations, both by engine-wide unicode conversion as well as
the ``Unicode`` type object.
:param encoding: Defaults to ``utf-8``. This is the string
encoding used by SQLAlchemy for string encode/decode
operations which occur within SQLAlchemy, **outside of
the DBAPI.** Most modern DBAPIs feature some degree of
direct support for Python ``unicode`` objects,
what you see in Python 2 as a string of the form
``u'some string'``. For those scenarios where the
DBAPI is detected as not supporting a Python ``unicode``
object, this encoding is used to determine the
source/destination encoding. It is **not used**
for those cases where the DBAPI handles unicode
directly.
To properly configure a system to accommodate Python
``unicode`` objects, the DBAPI should be
configured to handle unicode to the greatest
degree as is appropriate - see
the notes on unicode pertaining to the specific
target database in use at :ref:`dialect_toplevel`.
Areas where string encoding may need to be accommodated
outside of the DBAPI include zero or more of:
* the values passed to bound parameters, corresponding to
the :class:`.Unicode` type or the :class:`.String` type
when ``convert_unicode`` is ``True``;
* the values returned in result set columns corresponding
to the :class:`.Unicode` type or the :class:`.String`
type when ``convert_unicode`` is ``True``;
* the string SQL statement passed to the DBAPI's
``cursor.execute()`` method;
* the string names of the keys in the bound parameter
dictionary passed to the DBAPI's ``cursor.execute()``
as well as ``cursor.setinputsizes()`` methods;
* the string column names retrieved from the DBAPI's
``cursor.description`` attribute.
When using Python 3, the DBAPI is required to support
*all* of the above values as Python ``unicode`` objects,
which in Python 3 are just known as ``str``. In Python 2,
the DBAPI does not specify unicode behavior at all,
so SQLAlchemy must make decisions for each of the above
values on a per-DBAPI basis - implementations are
completely inconsistent in their behavior.
:param execution_options: Dictionary execution options which will
be applied to all connections. See
File diff suppressed because it is too large Load Diff
+86 -38
View File
@@ -21,35 +21,63 @@ class SchemaGenerator(DDLBase):
self.tables = tables and set(tables) or None
self.preparer = dialect.identifier_preparer
self.dialect = dialect
self.memo = {}
def _can_create(self, table):
def _can_create_table(self, table):
self.dialect.validate_identifier(table.name)
if table.schema:
self.dialect.validate_identifier(table.schema)
return not self.checkfirst or not self.dialect.has_table(self.connection, table.name, schema=table.schema)
return not self.checkfirst or \
not self.dialect.has_table(self.connection,
table.name, schema=table.schema)
def _can_create_sequence(self, sequence):
return self.dialect.supports_sequences and \
(
(not self.dialect.sequences_optional or
not sequence.optional) and
(
not self.checkfirst or
not self.dialect.has_sequence(
self.connection,
sequence.name,
schema=sequence.schema)
)
)
def visit_metadata(self, metadata):
if self.tables:
tables = self.tables
else:
tables = metadata.tables.values()
collection = [t for t in sql_util.sort_tables(tables) if self._can_create(t)]
collection = [t for t in sql_util.sort_tables(tables)
if self._can_create_table(t)]
seq_coll = [s for s in metadata._sequences.values()
if s.column is None and self._can_create_sequence(s)]
for listener in metadata.ddl_listeners['before-create']:
listener('before-create', metadata, self.connection, tables=collection)
metadata.dispatch.before_create(metadata, self.connection,
tables=collection,
checkfirst=self.checkfirst,
_ddl_runner=self)
for seq in seq_coll:
self.traverse_single(seq, create_ok=True)
for table in collection:
self.traverse_single(table, create_ok=True)
for listener in metadata.ddl_listeners['after-create']:
listener('after-create', metadata, self.connection, tables=collection)
metadata.dispatch.after_create(metadata, self.connection,
tables=collection,
checkfirst=self.checkfirst,
_ddl_runner=self)
def visit_table(self, table, create_ok=False):
if not create_ok and not self._can_create(table):
if not create_ok and not self._can_create_table(table):
return
for listener in table.ddl_listeners['before-create']:
listener('before-create', table, self.connection)
table.dispatch.before_create(table, self.connection,
checkfirst=self.checkfirst,
_ddl_runner=self)
for column in table.columns:
if column.default is not None:
@@ -61,16 +89,14 @@ class SchemaGenerator(DDLBase):
for index in table.indexes:
self.traverse_single(index)
for listener in table.ddl_listeners['after-create']:
listener('after-create', table, self.connection)
table.dispatch.after_create(table, self.connection,
checkfirst=self.checkfirst,
_ddl_runner=self)
def visit_sequence(self, sequence):
if self.dialect.supports_sequences:
if ((not self.dialect.sequences_optional or
not sequence.optional) and
(not self.checkfirst or
not self.dialect.has_sequence(self.connection, sequence.name, schema=sequence.schema))):
self.connection.execute(schema.CreateSequence(sequence))
def visit_sequence(self, sequence, create_ok=False):
if not create_ok and not self._can_create_sequence(sequence):
return
self.connection.execute(schema.CreateSequence(sequence))
def visit_index(self, index):
self.connection.execute(schema.CreateIndex(index))
@@ -83,38 +109,62 @@ class SchemaDropper(DDLBase):
self.tables = tables
self.preparer = dialect.identifier_preparer
self.dialect = dialect
self.memo = {}
def visit_metadata(self, metadata):
if self.tables:
tables = self.tables
else:
tables = metadata.tables.values()
collection = [t for t in reversed(sql_util.sort_tables(tables)) if self._can_drop(t)]
collection = [t for t in reversed(sql_util.sort_tables(tables))
if self._can_drop_table(t)]
seq_coll = [s for s in metadata._sequences.values()
if s.column is None and self._can_drop_sequence(s)]
for listener in metadata.ddl_listeners['before-drop']:
listener('before-drop', metadata, self.connection, tables=collection)
metadata.dispatch.before_drop(metadata, self.connection,
tables=collection,
checkfirst=self.checkfirst,
_ddl_runner=self)
for table in collection:
self.traverse_single(table, drop_ok=True)
for listener in metadata.ddl_listeners['after-drop']:
listener('after-drop', metadata, self.connection, tables=collection)
for seq in seq_coll:
self.traverse_single(seq, drop_ok=True)
def _can_drop(self, table):
metadata.dispatch.after_drop(metadata, self.connection,
tables=collection,
checkfirst=self.checkfirst,
_ddl_runner=self)
def _can_drop_table(self, table):
self.dialect.validate_identifier(table.name)
if table.schema:
self.dialect.validate_identifier(table.schema)
return not self.checkfirst or self.dialect.has_table(self.connection, table.name, schema=table.schema)
return not self.checkfirst or self.dialect.has_table(self.connection,
table.name, schema=table.schema)
def _can_drop_sequence(self, sequence):
return self.dialect.supports_sequences and \
((not self.dialect.sequences_optional or
not sequence.optional) and
(not self.checkfirst or
self.dialect.has_sequence(
self.connection,
sequence.name,
schema=sequence.schema))
)
def visit_index(self, index):
self.connection.execute(schema.DropIndex(index))
def visit_table(self, table, drop_ok=False):
if not drop_ok and not self._can_drop(table):
if not drop_ok and not self._can_drop_table(table):
return
for listener in table.ddl_listeners['before-drop']:
listener('before-drop', table, self.connection)
table.dispatch.before_drop(table, self.connection,
checkfirst=self.checkfirst,
_ddl_runner=self)
for column in table.columns:
if column.default is not None:
@@ -122,13 +172,11 @@ class SchemaDropper(DDLBase):
self.connection.execute(schema.DropTable(table))
for listener in table.ddl_listeners['after-drop']:
listener('after-drop', table, self.connection)
table.dispatch.after_drop(table, self.connection,
checkfirst=self.checkfirst,
_ddl_runner=self)
def visit_sequence(self, sequence):
if self.dialect.supports_sequences:
if ((not self.dialect.sequences_optional or
not sequence.optional) and
(not self.checkfirst or
self.dialect.has_sequence(self.connection, sequence.name, schema=sequence.schema))):
self.connection.execute(schema.DropSequence(sequence))
def visit_sequence(self, sequence, drop_ok=False):
if not drop_ok and not self._can_drop_sequence(sequence):
return
self.connection.execute(schema.DropSequence(sequence))
+287 -219
View File
@@ -1,5 +1,5 @@
# engine/default.py
# Copyright (C) 2005-2011 the SQLAlchemy authors and contributors <see AUTHORS file>
# Copyright (C) 2005-2012 the SQLAlchemy authors and contributors <see AUTHORS file>
#
# This module is part of SQLAlchemy and is released under
# the MIT License: http://www.opensource.org/licenses/mit-license.php
@@ -15,7 +15,9 @@ as the base class for their own corresponding classes.
import re, random
from sqlalchemy.engine import base, reflection
from sqlalchemy.sql import compiler, expression
from sqlalchemy import exc, types as sqltypes, util
from sqlalchemy import exc, types as sqltypes, util, pool, processors
import codecs
import weakref
AUTOCOMMIT_REGEXP = re.compile(
r'\s*(?:UPDATE|INSERT|CREATE|DELETE|DROP|ALTER)',
@@ -35,6 +37,7 @@ class DefaultDialect(base.Dialect):
# not cx_oracle.
execute_sequence_format = tuple
supports_views = True
supports_sequences = False
sequences_optional = False
preexecute_autoincrement_sequences = False
@@ -52,12 +55,16 @@ class DefaultDialect(base.Dialect):
# Py3K
#supports_unicode_statements = True
#supports_unicode_binds = True
#returns_unicode_strings = True
#description_encoding = None
# Py2K
supports_unicode_statements = False
supports_unicode_binds = False
returns_unicode_strings = False
description_encoding = 'use_encoding'
# end Py2K
name = 'default'
# length at which to truncate
@@ -97,7 +104,7 @@ class DefaultDialect(base.Dialect):
if not getattr(self, 'ported_sqla_06', True):
util.warn(
"The %s dialect is not yet ported to SQLAlchemy 0.6" %
"The %s dialect is not yet ported to SQLAlchemy 0.6/0.7" %
self.name)
self.convert_unicode = convert_unicode
@@ -134,16 +141,29 @@ class DefaultDialect(base.Dialect):
(label_length, self.max_identifier_length))
self.label_length = label_length
if not hasattr(self, 'description_encoding'):
self.description_encoding = getattr(
self,
'description_encoding',
encoding)
if self.description_encoding == 'use_encoding':
self._description_decoder = processors.to_unicode_processor_factory(
encoding
)
elif self.description_encoding is not None:
self._description_decoder = processors.to_unicode_processor_factory(
self.description_encoding
)
self._encoder = codecs.getencoder(self.encoding)
self._decoder = processors.to_unicode_processor_factory(self.encoding)
@util.memoized_property
def _type_memos(self):
return weakref.WeakKeyDictionary()
@property
def dialect_description(self):
return self.name + "+" + self.driver
@classmethod
def get_pool_class(cls, url):
return getattr(cls, 'poolclass', pool.QueuePool)
def initialize(self, connection):
try:
self.server_version_info = \
@@ -156,6 +176,12 @@ class DefaultDialect(base.Dialect):
except NotImplementedError:
self.default_schema_name = None
try:
self.default_isolation_level = \
self.get_isolation_level(connection.connection)
except NotImplementedError:
self.default_isolation_level = None
self.returns_unicode_strings = self._check_unicode_returns(connection)
self.do_rollback(connection.connection)
@@ -183,29 +209,34 @@ class DefaultDialect(base.Dialect):
# end Py2K
# Py3K
#cast_to = str
def check_unicode(type_):
def check_unicode(formatstr, type_):
cursor = connection.connection.cursor()
try:
cursor.execute(
cast_to(
expression.select(
[expression.cast(
expression.literal_column(
"'test unicode returns'"), type_)
]).compile(dialect=self)
try:
cursor.execute(
cast_to(
expression.select(
[expression.cast(
expression.literal_column(
"'test %s returns'" % formatstr), type_)
]).compile(dialect=self)
)
)
)
row = cursor.fetchone()
row = cursor.fetchone()
return isinstance(row[0], unicode)
return isinstance(row[0], unicode)
except self.dbapi.Error, de:
util.warn("Exception attempting to "
"detect unicode returns: %r" % de)
return False
finally:
cursor.close()
# detect plain VARCHAR
unicode_for_varchar = check_unicode(sqltypes.VARCHAR(60))
unicode_for_varchar = check_unicode("plain", sqltypes.VARCHAR(60))
# detect if there's an NVARCHAR type with different behavior available
unicode_for_unicode = check_unicode(sqltypes.Unicode(60))
unicode_for_unicode = check_unicode("unicode", sqltypes.Unicode(60))
if unicode_for_unicode and not unicode_for_varchar:
return "conditional"
@@ -223,9 +254,9 @@ class DefaultDialect(base.Dialect):
"""
return sqltypes.adapt_type(typeobj, self.colspecs)
def reflecttable(self, connection, table, include_columns):
def reflecttable(self, connection, table, include_columns, exclude_columns=None):
insp = reflection.Inspector.from_engine(connection)
return insp.reflecttable(table, include_columns)
return insp.reflecttable(table, include_columns, exclude_columns)
def get_pk_constraint(self, conn, table_name, schema=None, **kw):
"""Compatiblity method, adapts the result of get_primary_keys()
@@ -298,12 +329,16 @@ class DefaultDialect(base.Dialect):
def do_execute(self, cursor, statement, parameters, context=None):
cursor.execute(statement, parameters)
def is_disconnect(self, e):
def is_disconnect(self, e, connection, cursor):
return False
def reset_isolation_level(self, dbapi_conn):
# default_isolation_level is read from the first conenction
# after the initial set of 'isolation_level', if any, so is
# the configured default of this dialect.
self.set_isolation_level(dbapi_conn, self.default_isolation_level)
class DefaultExecutionContext(base.ExecutionContext):
execution_options = util.frozendict()
isinsert = False
isupdate = False
isdelete = False
@@ -312,107 +347,190 @@ class DefaultExecutionContext(base.ExecutionContext):
result_map = None
compiled = None
statement = None
_is_implicit_returning = False
_is_explicit_returning = False
def __init__(self,
dialect,
connection,
compiled_sql=None,
compiled_ddl=None,
statement=None,
parameters=None):
@classmethod
def _init_ddl(cls, dialect, connection, dbapi_connection, compiled_ddl):
"""Initialize execution context for a DDLElement construct."""
self = cls.__new__(cls)
self.dialect = dialect
self._connection = self.root_connection = connection
self.root_connection = connection
self._dbapi_connection = dbapi_connection
self.engine = connection.engine
if compiled_ddl is not None:
self.compiled = compiled = compiled_ddl
self.isddl = True
self.compiled = compiled = compiled_ddl
self.isddl = True
if compiled.statement._execution_options:
self.execution_options = compiled.statement._execution_options
if connection._execution_options:
self.execution_options = self.execution_options.union(
connection._execution_options
)
self.execution_options = compiled.statement._execution_options
if connection._execution_options:
self.execution_options = dict(self.execution_options)
self.execution_options.update(connection._execution_options)
if not dialect.supports_unicode_statements:
self.unicode_statement = unicode(compiled)
self.statement = self.unicode_statement.encode(self.dialect.encoding)
else:
self.statement = self.unicode_statement = unicode(compiled)
if not dialect.supports_unicode_statements:
self.unicode_statement = unicode(compiled)
self.statement = dialect._encoder(self.unicode_statement)[0]
else:
self.statement = self.unicode_statement = unicode(compiled)
self.cursor = self.create_cursor()
self.compiled_parameters = []
self.parameters = [self._default_params]
self.cursor = self.create_cursor()
self.compiled_parameters = []
elif compiled_sql is not None:
self.compiled = compiled = compiled_sql
if dialect.positional:
self.parameters = [dialect.execute_sequence_format()]
else:
self.parameters = [{}]
if not compiled.can_execute:
raise exc.ArgumentError("Not an executable clause: %s" % compiled)
return self
if compiled.statement._execution_options:
self.execution_options = compiled.statement._execution_options
if connection._execution_options:
self.execution_options = self.execution_options.union(
connection._execution_options
)
@classmethod
def _init_compiled(cls, dialect, connection, dbapi_connection, compiled, parameters):
"""Initialize execution context for a Compiled construct."""
# compiled clauseelement. process bind params, process table defaults,
# track collections used by ResultProxy to target and process results
self = cls.__new__(cls)
self.dialect = dialect
self.root_connection = connection
self._dbapi_connection = dbapi_connection
self.engine = connection.engine
self.processors = dict(
(key, value) for key, value in
( (compiled.bind_names[bindparam],
bindparam.bind_processor(self.dialect))
for bindparam in compiled.bind_names )
if value is not None)
self.compiled = compiled
self.result_map = compiled.result_map
if not compiled.can_execute:
raise exc.ArgumentError("Not an executable clause")
if not dialect.supports_unicode_statements:
self.unicode_statement = unicode(compiled)
self.statement = self.unicode_statement.encode(self.dialect.encoding)
else:
self.statement = self.unicode_statement = unicode(compiled)
self.execution_options = compiled.statement._execution_options
if connection._execution_options:
self.execution_options = dict(self.execution_options)
self.execution_options.update(connection._execution_options)
self.isinsert = compiled.isinsert
self.isupdate = compiled.isupdate
self.isdelete = compiled.isdelete
# compiled clauseelement. process bind params, process table defaults,
# track collections used by ResultProxy to target and process results
if not parameters:
self.compiled_parameters = [compiled.construct_params()]
else:
self.compiled_parameters = [compiled.construct_params(m, _group_number=grp) for
grp,m in enumerate(parameters)]
self.result_map = compiled.result_map
self.executemany = len(parameters) > 1
self.unicode_statement = unicode(compiled)
if not dialect.supports_unicode_statements:
self.statement = self.unicode_statement.encode(self.dialect.encoding)
else:
self.statement = self.unicode_statement
self.cursor = self.create_cursor()
if self.isinsert or self.isupdate:
self.__process_defaults()
self.parameters = self.__convert_compiled_params(self.compiled_parameters)
self.isinsert = compiled.isinsert
self.isupdate = compiled.isupdate
self.isdelete = compiled.isdelete
if self.isinsert or self.isupdate or self.isdelete:
self._is_explicit_returning = compiled.statement._returning
self._is_implicit_returning = compiled.returning and \
not compiled.statement._returning
if not parameters:
self.compiled_parameters = [compiled.construct_params()]
else:
self.compiled_parameters = \
[compiled.construct_params(m, _group_number=grp) for
grp,m in enumerate(parameters)]
elif statement is not None:
# plain text statement
if connection._execution_options:
self.execution_options = self.execution_options.union(connection._execution_options)
self.parameters = self.__encode_param_keys(parameters)
self.executemany = len(parameters) > 1
if isinstance(statement, unicode) and not dialect.supports_unicode_statements:
self.unicode_statement = statement
self.statement = statement.encode(self.dialect.encoding)
else:
self.statement = self.unicode_statement = statement
self.cursor = self.create_cursor()
if self.isinsert or self.isupdate:
self.postfetch_cols = self.compiled.postfetch
self.prefetch_cols = self.compiled.prefetch
self.__process_defaults()
self.cursor = self.create_cursor()
processors = compiled._bind_processors
# Convert the dictionary of bind parameter values
# into a dict or list to be sent to the DBAPI's
# execute() or executemany() method.
parameters = []
if dialect.positional:
for compiled_params in self.compiled_parameters:
param = []
for key in self.compiled.positiontup:
if key in processors:
param.append(processors[key](compiled_params[key]))
else:
param.append(compiled_params[key])
parameters.append(dialect.execute_sequence_format(param))
else:
# no statement. used for standalone ColumnDefault execution.
if connection._execution_options:
self.execution_options = self.execution_options.union(connection._execution_options)
self.cursor = self.create_cursor()
encode = not dialect.supports_unicode_statements
for compiled_params in self.compiled_parameters:
param = {}
if encode:
for key in compiled_params:
if key in processors:
param[dialect._encoder(key)[0]] = \
processors[key](compiled_params[key])
else:
param[dialect._encoder(key)[0]] = compiled_params[key]
else:
for key in compiled_params:
if key in processors:
param[key] = processors[key](compiled_params[key])
else:
param[key] = compiled_params[key]
parameters.append(param)
self.parameters = dialect.execute_sequence_format(parameters)
return self
@classmethod
def _init_statement(cls, dialect, connection, dbapi_connection, statement, parameters):
"""Initialize execution context for a string SQL statement."""
self = cls.__new__(cls)
self.dialect = dialect
self.root_connection = connection
self._dbapi_connection = dbapi_connection
self.engine = connection.engine
# plain text statement
self.execution_options = connection._execution_options
if not parameters:
if self.dialect.positional:
self.parameters = [dialect.execute_sequence_format()]
else:
self.parameters = [{}]
elif isinstance(parameters[0], dialect.execute_sequence_format):
self.parameters = parameters
elif isinstance(parameters[0], dict):
if dialect.supports_unicode_statements:
self.parameters = parameters
else:
self.parameters= [
dict((dialect._encoder(k)[0], d[k]) for k in d)
for d in parameters
] or [{}]
else:
self.parameters = [dialect.execute_sequence_format(p)
for p in parameters]
self.executemany = len(parameters) > 1
if not dialect.supports_unicode_statements and isinstance(statement, unicode):
self.unicode_statement = statement
self.statement = dialect._encoder(statement)[0]
else:
self.statement = self.unicode_statement = statement
self.cursor = self.create_cursor()
return self
@classmethod
def _init_default(cls, dialect, connection, dbapi_connection):
"""Initialize execution context for a ColumnDefault construct."""
self = cls.__new__(cls)
self.dialect = dialect
self.root_connection = connection
self._dbapi_connection = dbapi_connection
self.engine = connection.engine
self.execution_options = connection._execution_options
self.cursor = self.create_cursor()
return self
@util.memoized_property
def is_crud(self):
@@ -431,25 +549,7 @@ class DefaultExecutionContext(base.ExecutionContext):
else:
return autocommit
@util.memoized_property
def _is_explicit_returning(self):
return self.compiled and \
getattr(self.compiled.statement, '_returning', False)
@util.memoized_property
def _is_implicit_returning(self):
return self.compiled and \
bool(self.compiled.returning) and \
not self.compiled.statement._returning
@util.memoized_property
def _default_params(self):
if self.dialect.positional:
return self.dialect.execute_sequence_format()
else:
return {}
def _execute_scalar(self, stmt):
def _execute_scalar(self, stmt, type_):
"""Execute a string statement on the current cursor, returning a
scalar result.
@@ -459,79 +559,37 @@ class DefaultExecutionContext(base.ExecutionContext):
"""
conn = self._connection
if isinstance(stmt, unicode) and not self.dialect.supports_unicode_statements:
stmt = stmt.encode(self.dialect.encoding)
conn._cursor_execute(self.cursor, stmt, self._default_params)
return self.cursor.fetchone()[0]
conn = self.root_connection
if isinstance(stmt, unicode) and \
not self.dialect.supports_unicode_statements:
stmt = self.dialect._encoder(stmt)[0]
if self.dialect.positional:
default_params = self.dialect.execute_sequence_format()
else:
default_params = {}
conn._cursor_execute(self.cursor, stmt, default_params)
r = self.cursor.fetchone()[0]
if type_ is not None:
# apply type post processors to the result
proc = type_._cached_result_processor(
self.dialect,
self.cursor.description[0][1]
)
if proc:
return proc(r)
return r
@property
def connection(self):
return self._connection._branch()
def __encode_param_keys(self, params):
"""Apply string encoding to the keys of dictionary-based bind parameters.
This is only used executing textual, non-compiled SQL expressions.
"""
if not params:
return [self._default_params]
elif isinstance(params[0], self.dialect.execute_sequence_format):
return params
elif isinstance(params[0], dict):
if self.dialect.supports_unicode_statements:
return params
else:
def proc(d):
return dict((k.encode(self.dialect.encoding), d[k]) for k in d)
return [proc(d) for d in params] or [{}]
else:
return [self.dialect.execute_sequence_format(p) for p in params]
def __convert_compiled_params(self, compiled_parameters):
"""Convert the dictionary of bind parameter values into a dict or list
to be sent to the DBAPI's execute() or executemany() method.
"""
processors = self.processors
parameters = []
if self.dialect.positional:
for compiled_params in compiled_parameters:
param = []
for key in self.compiled.positiontup:
if key in processors:
param.append(processors[key](compiled_params[key]))
else:
param.append(compiled_params[key])
parameters.append(self.dialect.execute_sequence_format(param))
else:
encode = not self.dialect.supports_unicode_statements
for compiled_params in compiled_parameters:
param = {}
if encode:
encoding = self.dialect.encoding
for key in compiled_params:
if key in processors:
param[key.encode(encoding)] = processors[key](compiled_params[key])
else:
param[key.encode(encoding)] = compiled_params[key]
else:
for key in compiled_params:
if key in processors:
param[key] = processors[key](compiled_params[key])
else:
param[key] = compiled_params[key]
parameters.append(param)
return self.dialect.execute_sequence_format(parameters)
return self.root_connection._branch()
def should_autocommit_text(self, statement):
return AUTOCOMMIT_REGEXP.match(statement)
def create_cursor(self):
return self._connection.connection.cursor()
return self._dbapi_connection.cursor()
def pre_exec(self):
pass
@@ -584,14 +642,26 @@ class DefaultExecutionContext(base.ExecutionContext):
return self.dialect.supports_sane_multi_rowcount
def post_insert(self):
if self.dialect.postfetch_lastrowid and \
(not len(self._inserted_primary_key) or \
None in self._inserted_primary_key):
if not self._is_implicit_returning and \
self.dialect.postfetch_lastrowid and \
(not self.inserted_primary_key or \
None in self.inserted_primary_key):
table = self.compiled.statement.table
lastrowid = self.get_lastrowid()
self._inserted_primary_key = [c is table._autoincrement_column and lastrowid or v
for c, v in zip(table.primary_key, self._inserted_primary_key)
autoinc_col = table._autoincrement_column
if autoinc_col is not None:
# apply type post processors to the lastrowid
proc = autoinc_col.type._cached_result_processor(self.dialect, None)
if proc is not None:
lastrowid = proc(lastrowid)
self.inserted_primary_key = [
c is autoinc_col and lastrowid or v
for c, v in zip(
table.primary_key,
self.inserted_primary_key)
]
def _fetch_implicit_returning(self, resultproxy):
@@ -599,27 +669,26 @@ class DefaultExecutionContext(base.ExecutionContext):
row = resultproxy.fetchone()
ipk = []
for c, v in zip(table.primary_key, self._inserted_primary_key):
for c, v in zip(table.primary_key, self.inserted_primary_key):
if v is not None:
ipk.append(v)
else:
ipk.append(row[c])
self._inserted_primary_key = ipk
def last_inserted_params(self):
return self._last_inserted_params
def last_updated_params(self):
return self._last_updated_params
self.inserted_primary_key = ipk
def lastrow_has_defaults(self):
return hasattr(self, 'postfetch_cols') and len(self.postfetch_cols)
return (self.isinsert or self.isupdate) and \
bool(self.postfetch_cols)
def set_input_sizes(self, translate=None, exclude_types=None):
"""Given a cursor and ClauseParameters, call the appropriate
style of ``setinputsizes()`` on the cursor, using DB-API types
from the bind parameter's ``TypeEngine`` objects.
This method only called by those dialects which require it,
currently cx_oracle.
"""
if not hasattr(self.compiled, 'bind_names'):
@@ -639,7 +708,7 @@ class DefaultExecutionContext(base.ExecutionContext):
try:
self.cursor.setinputsizes(*inputsizes)
except Exception, e:
self._connection._handle_dbapi_exception(e, None, None, None, self)
self.root_connection._handle_dbapi_exception(e, None, None, None, self)
raise
else:
inputsizes = {}
@@ -649,16 +718,16 @@ class DefaultExecutionContext(base.ExecutionContext):
if dbtype is not None and (not exclude_types or dbtype not in exclude_types):
if translate:
key = translate.get(key, key)
inputsizes[key.encode(self.dialect.encoding)] = dbtype
inputsizes[self.dialect._encoder(key)[0]] = dbtype
try:
self.cursor.setinputsizes(**inputsizes)
except Exception, e:
self._connection._handle_dbapi_exception(e, None, None, None, self)
self.root_connection._handle_dbapi_exception(e, None, None, None, self)
raise
def _exec_default(self, default):
def _exec_default(self, default, type_):
if default.is_sequence:
return self.fire_sequence(default)
return self.fire_sequence(default, type_)
elif default.is_callable:
return default.arg(self)
elif default.is_clause_element:
@@ -674,13 +743,13 @@ class DefaultExecutionContext(base.ExecutionContext):
if column.default is None:
return None
else:
return self._exec_default(column.default)
return self._exec_default(column.default, column.type)
def get_update_default(self, column):
if column.onupdate is None:
return None
else:
return self._exec_default(column.onupdate)
return self._exec_default(column.onupdate, column.type)
def __process_defaults(self):
"""Generate default values for compiled insert/update statements,
@@ -692,8 +761,9 @@ class DefaultExecutionContext(base.ExecutionContext):
scalar_defaults = {}
# pre-determine scalar Python-side defaults
# to avoid many calls of get_insert_default()/get_update_default()
for c in self.compiled.prefetch:
# to avoid many calls of get_insert_default()/
# get_update_default()
for c in self.prefetch_cols:
if self.isinsert and c.default and c.default.is_scalar:
scalar_defaults[c] = c.default.arg
elif self.isupdate and c.onupdate and c.onupdate.is_scalar:
@@ -701,7 +771,7 @@ class DefaultExecutionContext(base.ExecutionContext):
for param in self.compiled_parameters:
self.current_parameters = param
for c in self.compiled.prefetch:
for c in self.prefetch_cols:
if c in scalar_defaults:
val = scalar_defaults[c]
elif self.isinsert:
@@ -711,9 +781,9 @@ class DefaultExecutionContext(base.ExecutionContext):
if val is not None:
param[c.key] = val
del self.current_parameters
else:
self.current_parameters = compiled_parameters = self.compiled_parameters[0]
self.current_parameters = compiled_parameters = \
self.compiled_parameters[0]
for c in self.compiled.prefetch:
if self.isinsert:
@@ -726,13 +796,11 @@ class DefaultExecutionContext(base.ExecutionContext):
del self.current_parameters
if self.isinsert:
self._inserted_primary_key = [compiled_parameters.get(c.key, None)
for c in self.compiled.statement.table.primary_key]
self._last_inserted_params = compiled_parameters
else:
self._last_updated_params = compiled_parameters
self.inserted_primary_key = [
self.compiled_parameters[0].get(c.key, None)
for c in self.compiled.\
statement.table.primary_key
]
self.postfetch_cols = self.compiled.postfetch
self.prefetch_cols = self.compiled.prefetch
DefaultDialect.execution_ctx_cls = DefaultExecutionContext
+29 -34
View File
@@ -1,5 +1,5 @@
# engine/reflection.py
# Copyright (C) 2005-2011 the SQLAlchemy authors and contributors <see AUTHORS file>
# Copyright (C) 2005-2012 the SQLAlchemy authors and contributors <see AUTHORS file>
#
# This module is part of SQLAlchemy and is released under
# the MIT License: http://www.opensource.org/licenses/mit-license.php
@@ -27,6 +27,7 @@ methods such as get_table_names, get_columns, etc.
import sqlalchemy
from sqlalchemy import exc, sql
from sqlalchemy import util
from sqlalchemy.util import topological
from sqlalchemy.types import TypeEngine
from sqlalchemy import schema as sa_schema
@@ -80,10 +81,6 @@ class Inspector(object):
:meth:`Inspector.from_engine`
"""
# ensure initialized
bind.connect()
# this might not be a connection, it could be an engine.
self.bind = bind
@@ -92,6 +89,11 @@ class Inspector(object):
self.engine = bind.engine
else:
self.engine = bind
if self.engine is bind:
# if engine, ensure initialized
bind.connect().close()
self.dialect = self.engine.dialect
self.info_cache = {}
@@ -149,27 +151,19 @@ class Inspector(object):
if hasattr(self.dialect, 'get_table_names'):
tnames = self.dialect.get_table_names(self.bind,
schema,
info_cache=self.info_cache)
schema, info_cache=self.info_cache)
else:
tnames = self.engine.table_names(schema)
if order_by == 'foreign_key':
ordered_tnames = tnames[:]
# Order based on foreign key dependencies.
import random
random.shuffle(tnames)
tuples = []
for tname in tnames:
table_pos = tnames.index(tname)
fkeys = self.get_foreign_keys(tname, schema)
for fkey in fkeys:
rtable = fkey['referred_table']
if rtable in ordered_tnames:
ref_pos = ordered_tnames.index(rtable)
# Make sure it's lower in the list than anything it
# references.
if table_pos > ref_pos:
ordered_tnames.pop(table_pos) # rtable moves up 1
# insert just below rtable
ordered_tnames.index(ref_pos, tname)
tnames = ordered_tnames
for fkey in self.get_foreign_keys(tname, schema):
if tname != fkey['referred_table']:
tuples.append((tname, fkey['referred_table']))
tnames = list(topological.sort(tuples, tnames))
return tnames
def get_table_options(self, table_name, schema=None, **kw):
@@ -323,7 +317,7 @@ class Inspector(object):
info_cache=self.info_cache, **kw)
return indexes
def reflecttable(self, table, include_columns):
def reflecttable(self, table, include_columns, exclude_columns=None):
"""Given a Table object, load its internal constructs based on introspection.
This is the underlying method used by most dialects to produce
@@ -345,12 +339,6 @@ class Inspector(object):
"""
dialect = self.bind.dialect
# MySQL dialect does this. Applicable with other dialects?
if hasattr(dialect, '_connection_charset') \
and hasattr(dialect, '_adjust_casing'):
charset = dialect._connection_charset
dialect._adjust_casing(table)
# table attributes we might need.
reflection_options = dict(
(k, table.kwargs.get(k)) for k in dialect.reflection_options if k in table.kwargs)
@@ -381,24 +369,31 @@ class Inspector(object):
found_table = False
for col_d in self.get_columns(table_name, schema, **tblkw):
found_table = True
table.dispatch.column_reflect(table, col_d)
name = col_d['name']
if include_columns and name not in include_columns:
continue
if exclude_columns and name in exclude_columns:
continue
coltype = col_d['type']
col_kw = {
'nullable':col_d['nullable'],
}
if 'autoincrement' in col_d:
col_kw['autoincrement'] = col_d['autoincrement']
if 'quote' in col_d:
col_kw['quote'] = col_d['quote']
for k in ('autoincrement', 'quote', 'info', 'key'):
if k in col_d:
col_kw[k] = col_d[k]
colargs = []
if col_d.get('default') is not None:
# the "default" value is assumed to be a literal SQL expression,
# so is wrapped in text() so that no quoting occurs on re-issuance.
colargs.append(sa_schema.DefaultClause(sql.text(col_d['default'])))
colargs.append(
sa_schema.DefaultClause(
sql.text(col_d['default']), _reflected=True
)
)
if 'sequence' in col_d:
# TODO: mssql, maxdb and sybase are using this.
+39 -18
View File
@@ -1,5 +1,5 @@
# engine/strategies.py
# Copyright (C) 2005-2011 the SQLAlchemy authors and contributors <see AUTHORS file>
# Copyright (C) 2005-2012 the SQLAlchemy authors and contributors <see AUTHORS file>
#
# This module is part of SQLAlchemy and is released under
# the MIT License: http://www.opensource.org/licenses/mit-license.php
@@ -17,7 +17,7 @@ New strategies can be added via new ``EngineStrategy`` classes.
from operator import attrgetter
from sqlalchemy.engine import base, threadlocal, url
from sqlalchemy import util, exc
from sqlalchemy import util, exc, event
from sqlalchemy import pool as poollib
strategies = {}
@@ -43,8 +43,6 @@ class EngineStrategy(object):
class DefaultEngineStrategy(EngineStrategy):
"""Base class for built-in stratgies."""
pool_threadlocal = False
def create(self, name_or_url, **kwargs):
# create url.URL object
u = url.make_url(name_or_url)
@@ -82,16 +80,25 @@ class DefaultEngineStrategy(EngineStrategy):
return dialect.connect(*cargs, **cparams)
except Exception, e:
# Py3K
#raise exc.DBAPIError.instance(None, None, e) from e
#raise exc.DBAPIError.instance(None, None,
# e, dialect.dbapi.Error,
# connection_invalidated=
# dialect.is_disconnect(e, None, None)
# ) from e
# Py2K
import sys
raise exc.DBAPIError.instance(None, None, e), None, sys.exc_info()[2]
raise exc.DBAPIError.instance(
None, None, e, dialect.dbapi.Error,
connection_invalidated=
dialect.is_disconnect(e, None, None)), \
None, sys.exc_info()[2]
# end Py2K
creator = kwargs.pop('creator', connect)
poolclass = (kwargs.pop('poolclass', None) or
getattr(dialect_cls, 'poolclass', poollib.QueuePool))
poolclass = kwargs.pop('poolclass', None)
if poolclass is None:
poolclass = dialect_cls.get_pool_class(u)
pool_args = {}
# consume pool arguments from kwargs, translating a few of
@@ -100,12 +107,12 @@ class DefaultEngineStrategy(EngineStrategy):
'echo': 'echo_pool',
'timeout': 'pool_timeout',
'recycle': 'pool_recycle',
'events':'pool_events',
'use_threadlocal':'pool_threadlocal'}
for k in util.get_cls_kwargs(poolclass):
tk = translate.get(k, k)
if tk in kwargs:
pool_args[k] = kwargs.pop(tk)
pool_args.setdefault('use_threadlocal', self.pool_threadlocal)
pool = poolclass(creator, **pool_args)
else:
if isinstance(pool, poollib._DBProxy):
@@ -138,18 +145,26 @@ class DefaultEngineStrategy(EngineStrategy):
if _initialize:
do_on_connect = dialect.on_connect()
if do_on_connect:
def on_connect(conn, rec):
conn = getattr(conn, '_sqla_unwrap', conn)
def on_connect(dbapi_connection, connection_record):
conn = getattr(dbapi_connection, '_sqla_unwrap', dbapi_connection)
if conn is None:
return
do_on_connect(conn)
pool.add_listener({'first_connect': on_connect, 'connect':on_connect})
event.listen(pool, 'first_connect', on_connect)
event.listen(pool, 'connect', on_connect)
def first_connect(dbapi_connection, connection_record):
c = base.Connection(engine, connection=dbapi_connection)
# TODO: removing this allows the on connect activities
# to generate events. tests currently assume these aren't
# sent. do we want users to get all the initial connect
# activities as events ?
c._has_events = False
def first_connect(conn, rec):
c = base.Connection(engine, connection=conn)
dialect.initialize(c)
pool.add_listener({'first_connect':first_connect})
event.listen(pool, 'first_connect', first_connect)
return engine
@@ -167,7 +182,6 @@ class ThreadLocalEngineStrategy(DefaultEngineStrategy):
"""Strategy for configuring an Engine with thredlocal behavior."""
name = 'threadlocal'
pool_threadlocal = True
engine_cls = threadlocal.TLEngine
ThreadLocalEngineStrategy()
@@ -220,12 +234,19 @@ class MockEngineStrategy(EngineStrategy):
kwargs['checkfirst'] = False
from sqlalchemy.engine import ddl
ddl.SchemaGenerator(self.dialect, self, **kwargs).traverse(entity)
ddl.SchemaGenerator(self.dialect, self, **kwargs).traverse_single(entity)
def drop(self, entity, **kwargs):
kwargs['checkfirst'] = False
from sqlalchemy.engine import ddl
ddl.SchemaDropper(self.dialect, self, **kwargs).traverse(entity)
ddl.SchemaDropper(self.dialect, self, **kwargs).traverse_single(entity)
def _run_visitor(self, visitorcallable, element,
connection=None,
**kwargs):
kwargs['checkfirst'] = False
visitorcallable(self.dialect, self,
**kwargs).traverse_single(element)
def execute(self, object, *multiparams, **params):
raise NotImplementedError()
+5 -9
View File
@@ -1,5 +1,5 @@
# engine/threadlocal.py
# Copyright (C) 2005-2011 the SQLAlchemy authors and contributors <see AUTHORS file>
# Copyright (C) 2005-2012 the SQLAlchemy authors and contributors <see AUTHORS file>
#
# This module is part of SQLAlchemy and is released under
# the MIT License: http://www.opensource.org/licenses/mit-license.php
@@ -11,7 +11,7 @@ with :func:`~sqlalchemy.engine.create_engine`. This module is semi-private and
invoked automatically when the threadlocal engine strategy is used.
"""
from sqlalchemy import util
from sqlalchemy import util, event
from sqlalchemy.engine import base
import weakref
@@ -36,16 +36,12 @@ class TLConnection(base.Connection):
class TLEngine(base.Engine):
"""An Engine that includes support for thread-local managed transactions."""
_tl_connection_cls = TLConnection
def __init__(self, *args, **kwargs):
super(TLEngine, self).__init__(*args, **kwargs)
self._connections = util.threading.local()
proxy = kwargs.get('proxy')
if proxy:
self.TLConnection = base._proxy_connection_cls(
TLConnection, proxy)
else:
self.TLConnection = TLConnection
def contextual_connect(self, **kw):
if not hasattr(self._connections, 'conn'):
@@ -56,7 +52,7 @@ class TLEngine(base.Engine):
if connection is None or connection.closed:
# guards against pool-level reapers, if desired.
# or not connection.connection.is_valid:
connection = self.TLConnection(self, self.pool.connect(), **kw)
connection = self._tl_connection_cls(self, self.pool.connect(), **kw)
self._connections.conn = conn = weakref.ref(connection)
return connection._increment_connect()
+16 -8
View File
@@ -1,5 +1,5 @@
# engine/url.py
# Copyright (C) 2005-2011 the SQLAlchemy authors and contributors <see AUTHORS file>
# Copyright (C) 2005-2012 the SQLAlchemy authors and contributors <see AUTHORS file>
#
# This module is part of SQLAlchemy and is released under
# the MIT License: http://www.opensource.org/licenses/mit-license.php
@@ -12,8 +12,8 @@ with a string argument; alternatively, the URL is a public-facing construct whic
be used directly and is also accepted directly by ``create_engine()``.
"""
import re, cgi, sys, urllib
from sqlalchemy import exc
import re, urllib
from sqlalchemy import exc, util
class URL(object):
@@ -104,7 +104,14 @@ class URL(object):
module = __import__('sqlalchemy.dialects.%s' % (dialect, )).dialects
module = getattr(module, dialect)
module = getattr(module, driver)
if hasattr(module, driver):
module = getattr(module, driver)
else:
module = self._load_entry_point()
if module is None:
raise exc.ArgumentError(
"Could not determine dialect for '%s'." %
self.drivername)
return module.dialect
except ImportError:
@@ -112,7 +119,8 @@ class URL(object):
if module is not None:
return module
else:
raise
raise exc.ArgumentError(
"Could not determine dialect for '%s'." % self.drivername)
def _load_entry_point(self):
"""attempt to load this url's dialect from entry points, or return None
@@ -127,7 +135,7 @@ class URL(object):
return None
for res in pkg_resources.iter_entry_points('sqlalchemy.dialects'):
if res.name == self.drivername:
if res.name == self.drivername.replace("+", "."):
return res.load()
else:
return None
@@ -192,7 +200,7 @@ def _parse_rfc1738_args(name):
if components['database'] is not None:
tokens = components['database'].split('?', 2)
components['database'] = tokens[0]
query = (len(tokens) > 1 and dict(cgi.parse_qsl(tokens[1]))) or None
query = (len(tokens) > 1 and dict(util.parse_qsl(tokens[1]))) or None
# Py2K
if query is not None:
query = dict((k.encode('ascii'), query[k]) for k in query)
@@ -214,7 +222,7 @@ def _parse_keyvalue_args(name):
m = re.match( r'(\w+)://(.*)', name)
if m is not None:
(name, args) = m.group(1, 2)
opts = dict( cgi.parse_qsl( args ) )
opts = dict( util.parse_qsl( args ) )
return URL(name, *opts)
else:
return None