Packages update
This commit is contained in:
@@ -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
|
||||
|
||||
+927
-411
File diff suppressed because it is too large
Load Diff
@@ -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
@@ -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
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user