Remove Elixir library
Update SQLAlchemy
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
# engine/__init__.py
|
||||
# Copyright (C) 2005-2013 the SQLAlchemy authors and contributors <see AUTHORS file>
|
||||
# Copyright (C) 2005-2014 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
|
||||
@@ -50,54 +50,47 @@ url.py
|
||||
within a URL.
|
||||
"""
|
||||
|
||||
# not sure what this was used for
|
||||
#import sqlalchemy.databases
|
||||
from .interfaces import (
|
||||
Connectable,
|
||||
Dialect,
|
||||
ExecutionContext,
|
||||
|
||||
from sqlalchemy.engine.base import (
|
||||
# backwards compat
|
||||
Compiled,
|
||||
TypeCompiler
|
||||
)
|
||||
|
||||
from .base import (
|
||||
Connection,
|
||||
Engine,
|
||||
NestedTransaction,
|
||||
RootTransaction,
|
||||
Transaction,
|
||||
TwoPhaseTransaction,
|
||||
)
|
||||
|
||||
from .result import (
|
||||
BufferedColumnResultProxy,
|
||||
BufferedColumnRow,
|
||||
BufferedRowResultProxy,
|
||||
Compiled,
|
||||
Connectable,
|
||||
Connection,
|
||||
Dialect,
|
||||
Engine,
|
||||
ExecutionContext,
|
||||
NestedTransaction,
|
||||
FullyBufferedResultProxy,
|
||||
ResultProxy,
|
||||
RootTransaction,
|
||||
RowProxy,
|
||||
Transaction,
|
||||
TwoPhaseTransaction,
|
||||
TypeCompiler
|
||||
)
|
||||
from sqlalchemy.engine import strategies
|
||||
from sqlalchemy import util
|
||||
|
||||
|
||||
__all__ = (
|
||||
'BufferedColumnResultProxy',
|
||||
'BufferedColumnRow',
|
||||
'BufferedRowResultProxy',
|
||||
'Compiled',
|
||||
'Connectable',
|
||||
'Connection',
|
||||
'Dialect',
|
||||
'Engine',
|
||||
'ExecutionContext',
|
||||
'NestedTransaction',
|
||||
'ResultProxy',
|
||||
'RootTransaction',
|
||||
'RowProxy',
|
||||
'Transaction',
|
||||
'TwoPhaseTransaction',
|
||||
'TypeCompiler',
|
||||
'create_engine',
|
||||
'engine_from_config',
|
||||
)
|
||||
|
||||
from .util import (
|
||||
connection_memoize
|
||||
)
|
||||
|
||||
|
||||
from . import util, strategies
|
||||
|
||||
# backwards compat
|
||||
from ..sql import ddl
|
||||
|
||||
default_strategy = 'plain'
|
||||
|
||||
|
||||
def create_engine(*args, **kwargs):
|
||||
"""Create a new :class:`.Engine` instance.
|
||||
|
||||
@@ -117,11 +110,11 @@ def create_engine(*args, **kwargs):
|
||||
the URL can be an instance of :class:`~sqlalchemy.engine.url.URL`.
|
||||
|
||||
``**kwargs`` takes a wide variety of options which are routed
|
||||
towards their appropriate components. Arguments may be
|
||||
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 :func:`.create_engine()` usage.
|
||||
towards their appropriate components. Arguments may be 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 :func:`.create_engine()` usage.
|
||||
|
||||
Once established, the newly resulting :class:`.Engine` will
|
||||
request a connection from the underlying :class:`.Pool` once
|
||||
@@ -133,15 +126,17 @@ def create_engine(*args, **kwargs):
|
||||
|
||||
See also:
|
||||
|
||||
:ref:`engines_toplevel`
|
||||
:doc:`/core/engines`
|
||||
|
||||
: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 case_sensitive=True: if False, result column names
|
||||
will match in a case-insensitive fashion, that is,
|
||||
``row['SomeColumn']``.
|
||||
|
||||
.. versionchanged:: 0.8
|
||||
By default, result row names match case-sensitively.
|
||||
In version 0.7 and prior, all matches were case-insensitive.
|
||||
|
||||
:param connect_args: a dictionary of options which will be
|
||||
passed directly to the DBAPI's ``connect()`` method as
|
||||
@@ -231,7 +226,7 @@ def create_engine(*args, **kwargs):
|
||||
|
||||
:param execution_options: Dictionary execution options which will
|
||||
be applied to all connections. See
|
||||
:meth:`~sqlalchemy.engine.base.Connection.execution_options`
|
||||
:meth:`~sqlalchemy.engine.Connection.execution_options`
|
||||
|
||||
:param implicit_returning=True: When ``True``, a RETURNING-
|
||||
compatible construct, if available, will be used to
|
||||
@@ -262,13 +257,13 @@ def create_engine(*args, **kwargs):
|
||||
opened above and beyond the pool_size setting, which defaults
|
||||
to five. this is only used with :class:`~sqlalchemy.pool.QueuePool`.
|
||||
|
||||
:param module=None: reference to a Python module object (the module itself, not
|
||||
its string name). Specifies an alternate DBAPI module to be used
|
||||
by the engine's dialect. Each sub-dialect references a specific DBAPI which
|
||||
will be imported before first connect. This parameter causes the
|
||||
import to be bypassed, and the given module to be used instead.
|
||||
Can be used for testing of DBAPIs as well as to inject "mock"
|
||||
DBAPI implementations into the :class:`.Engine`.
|
||||
:param module=None: reference to a Python module object (the module
|
||||
itself, not its string name). Specifies an alternate DBAPI module to
|
||||
be used by the engine's dialect. Each sub-dialect references a
|
||||
specific DBAPI which will be imported before first connect. This
|
||||
parameter causes the import to be bypassed, and the given module to
|
||||
be used instead. Can be used for testing of DBAPIs as well as to
|
||||
inject "mock" DBAPI implementations into the :class:`.Engine`.
|
||||
|
||||
:param pool=None: an already-constructed instance of
|
||||
:class:`~sqlalchemy.pool.Pool`, such as a
|
||||
@@ -291,7 +286,8 @@ def create_engine(*args, **kwargs):
|
||||
id.
|
||||
|
||||
:param pool_size=5: the number of connections to keep open
|
||||
inside the connection pool. This used with :class:`~sqlalchemy.pool.QueuePool` as
|
||||
inside the connection pool. This used with
|
||||
:class:`~sqlalchemy.pool.QueuePool` as
|
||||
well as :class:`~sqlalchemy.pool.SingletonThreadPool`. With
|
||||
:class:`~sqlalchemy.pool.QueuePool`, a ``pool_size`` setting
|
||||
of 0 indicates no limit; to disable pooling, set ``poolclass`` to
|
||||
@@ -325,7 +321,8 @@ def create_engine(*args, **kwargs):
|
||||
:ref:`threadlocal_strategy`;
|
||||
* the ``mock`` strategy, which dispatches all statement
|
||||
execution to a function passed as the argument ``executor``.
|
||||
See `example in the FAQ <http://www.sqlalchemy.org/trac/wiki/FAQ#HowcanIgettheCREATETABLEDROPTABLEoutputasastring>`_.
|
||||
See `example in the FAQ
|
||||
<http://www.sqlalchemy.org/trac/wiki/FAQ#HowcanIgettheCREATETABLEDROPTABLEoutputasastring>`_.
|
||||
|
||||
:param executor=None: a function taking arguments
|
||||
``(sql, *multiparams, **params)``, to which the ``mock`` strategy will
|
||||
@@ -337,6 +334,7 @@ def create_engine(*args, **kwargs):
|
||||
strategy = strategies.strategies[strategy]
|
||||
return strategy.create(*args, **kwargs)
|
||||
|
||||
|
||||
def engine_from_config(configuration, prefix='sqlalchemy.', **kwargs):
|
||||
"""Create a new Engine instance using a configuration dictionary.
|
||||
|
||||
@@ -350,27 +348,16 @@ def engine_from_config(configuration, prefix='sqlalchemy.', **kwargs):
|
||||
arguments.
|
||||
"""
|
||||
|
||||
opts = _coerce_config(configuration, prefix)
|
||||
opts.update(kwargs)
|
||||
url = opts.pop('url')
|
||||
return create_engine(url, **opts)
|
||||
|
||||
def _coerce_config(configuration, prefix):
|
||||
"""Convert configuration values to expected types."""
|
||||
|
||||
options = dict((key[len(prefix):], configuration[key])
|
||||
for key in configuration
|
||||
if key.startswith(prefix))
|
||||
for option, type_ in (
|
||||
('convert_unicode', util.bool_or_str('force')),
|
||||
('pool_timeout', int),
|
||||
('echo', util.bool_or_str('debug')),
|
||||
('echo_pool', util.bool_or_str('debug')),
|
||||
('pool_recycle', int),
|
||||
('pool_size', int),
|
||||
('max_overflow', int),
|
||||
('pool_threadlocal', bool),
|
||||
('use_native_unicode', bool),
|
||||
):
|
||||
util.coerce_kw_type(options, option, type_)
|
||||
return options
|
||||
options['_coerce_config'] = True
|
||||
options.update(kwargs)
|
||||
url = options.pop('url')
|
||||
return create_engine(url, **options)
|
||||
|
||||
|
||||
__all__ = (
|
||||
'create_engine',
|
||||
'engine_from_config',
|
||||
)
|
||||
|
||||
+353
-2102
File diff suppressed because it is too large
Load Diff
@@ -1,182 +0,0 @@
|
||||
# engine/ddl.py
|
||||
# Copyright (C) 2009-2011 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
|
||||
|
||||
"""Routines to handle CREATE/DROP workflow."""
|
||||
|
||||
from sqlalchemy import engine, schema
|
||||
from sqlalchemy.sql import util as sql_util
|
||||
|
||||
|
||||
class DDLBase(schema.SchemaVisitor):
|
||||
def __init__(self, connection):
|
||||
self.connection = connection
|
||||
|
||||
class SchemaGenerator(DDLBase):
|
||||
def __init__(self, dialect, connection, checkfirst=False, tables=None, **kwargs):
|
||||
super(SchemaGenerator, self).__init__(connection, **kwargs)
|
||||
self.checkfirst = checkfirst
|
||||
self.tables = tables and set(tables) or None
|
||||
self.preparer = dialect.identifier_preparer
|
||||
self.dialect = dialect
|
||||
self.memo = {}
|
||||
|
||||
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)
|
||||
|
||||
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_table(t)]
|
||||
seq_coll = [s for s in metadata._sequences.values()
|
||||
if s.column is None and self._can_create_sequence(s)]
|
||||
|
||||
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)
|
||||
|
||||
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(table):
|
||||
return
|
||||
|
||||
table.dispatch.before_create(table, self.connection,
|
||||
checkfirst=self.checkfirst,
|
||||
_ddl_runner=self)
|
||||
|
||||
for column in table.columns:
|
||||
if column.default is not None:
|
||||
self.traverse_single(column.default)
|
||||
|
||||
self.connection.execute(schema.CreateTable(table))
|
||||
|
||||
if hasattr(table, 'indexes'):
|
||||
for index in table.indexes:
|
||||
self.traverse_single(index)
|
||||
|
||||
table.dispatch.after_create(table, self.connection,
|
||||
checkfirst=self.checkfirst,
|
||||
_ddl_runner=self)
|
||||
|
||||
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))
|
||||
|
||||
|
||||
class SchemaDropper(DDLBase):
|
||||
def __init__(self, dialect, connection, checkfirst=False, tables=None, **kwargs):
|
||||
super(SchemaDropper, self).__init__(connection, **kwargs)
|
||||
self.checkfirst = checkfirst
|
||||
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_table(t)]
|
||||
seq_coll = [s for s in metadata._sequences.values()
|
||||
if s.column is None and self._can_drop_sequence(s)]
|
||||
|
||||
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 seq in seq_coll:
|
||||
self.traverse_single(seq, drop_ok=True)
|
||||
|
||||
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)
|
||||
|
||||
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(table):
|
||||
return
|
||||
|
||||
table.dispatch.before_drop(table, self.connection,
|
||||
checkfirst=self.checkfirst,
|
||||
_ddl_runner=self)
|
||||
|
||||
for column in table.columns:
|
||||
if column.default is not None:
|
||||
self.traverse_single(column.default)
|
||||
|
||||
self.connection.execute(schema.DropTable(table))
|
||||
|
||||
table.dispatch.after_drop(table, self.connection,
|
||||
checkfirst=self.checkfirst,
|
||||
_ddl_runner=self)
|
||||
|
||||
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))
|
||||
@@ -1,5 +1,5 @@
|
||||
# engine/default.py
|
||||
# Copyright (C) 2005-2013 the SQLAlchemy authors and contributors <see AUTHORS file>
|
||||
# Copyright (C) 2005-2014 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,19 +12,23 @@ 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, pool, processors
|
||||
import re
|
||||
import random
|
||||
from . import reflection, interfaces, result
|
||||
from ..sql import compiler, expression
|
||||
from .. import types as sqltypes
|
||||
from .. import exc, util, pool, processors
|
||||
import codecs
|
||||
import weakref
|
||||
from .. import event
|
||||
|
||||
AUTOCOMMIT_REGEXP = re.compile(
|
||||
r'\s*(?:UPDATE|INSERT|CREATE|DELETE|DROP|ALTER)',
|
||||
re.I | re.UNICODE)
|
||||
|
||||
|
||||
class DefaultDialect(base.Dialect):
|
||||
|
||||
class DefaultDialect(interfaces.Dialect):
|
||||
"""Default implementation of Dialect"""
|
||||
|
||||
statement_compiler = compiler.SQLCompiler
|
||||
@@ -33,6 +37,10 @@ class DefaultDialect(base.Dialect):
|
||||
preparer = compiler.IdentifierPreparer
|
||||
supports_alter = True
|
||||
|
||||
# the first value we'd get for an autoincrement
|
||||
# column.
|
||||
default_sequence_base = 1
|
||||
|
||||
# most DBAPIs happy with this for execute().
|
||||
# not cx_oracle.
|
||||
execute_sequence_format = tuple
|
||||
@@ -44,27 +52,40 @@ class DefaultDialect(base.Dialect):
|
||||
postfetch_lastrowid = True
|
||||
implicit_returning = False
|
||||
|
||||
supports_right_nested_joins = True
|
||||
|
||||
supports_native_enum = False
|
||||
supports_native_boolean = False
|
||||
|
||||
supports_simple_order_by_label = True
|
||||
|
||||
engine_config_types = util.immutabledict([
|
||||
('convert_unicode', util.bool_or_str('force')),
|
||||
('pool_timeout', int),
|
||||
('echo', util.bool_or_str('debug')),
|
||||
('echo_pool', util.bool_or_str('debug')),
|
||||
('pool_recycle', int),
|
||||
('pool_size', int),
|
||||
('max_overflow', int),
|
||||
('pool_threadlocal', bool),
|
||||
('use_native_unicode', bool),
|
||||
])
|
||||
|
||||
# if the NUMERIC type
|
||||
# returns decimal.Decimal.
|
||||
# *not* the FLOAT type however.
|
||||
supports_native_decimal = False
|
||||
|
||||
# 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
|
||||
|
||||
if util.py3k:
|
||||
supports_unicode_statements = True
|
||||
supports_unicode_binds = True
|
||||
returns_unicode_strings = True
|
||||
description_encoding = None
|
||||
else:
|
||||
supports_unicode_statements = False
|
||||
supports_unicode_binds = False
|
||||
returns_unicode_strings = False
|
||||
description_encoding = 'use_encoding'
|
||||
|
||||
name = 'default'
|
||||
|
||||
@@ -86,6 +107,7 @@ class DefaultDialect(base.Dialect):
|
||||
default_paramstyle = 'named'
|
||||
supports_default_values = False
|
||||
supports_empty_insert = True
|
||||
supports_multivalues_insert = False
|
||||
|
||||
server_version_info = None
|
||||
|
||||
@@ -98,27 +120,20 @@ class DefaultDialect(base.Dialect):
|
||||
|
||||
reflection_options = ()
|
||||
|
||||
def __init__(self, convert_unicode=False, assert_unicode=False,
|
||||
def __init__(self, convert_unicode=False,
|
||||
encoding='utf-8', paramstyle=None, dbapi=None,
|
||||
implicit_returning=None,
|
||||
supports_right_nested_joins=None,
|
||||
case_sensitive=True,
|
||||
supports_native_boolean=None,
|
||||
label_length=None, **kwargs):
|
||||
|
||||
if not getattr(self, 'ported_sqla_06', True):
|
||||
util.warn(
|
||||
"The %s dialect is not yet ported to SQLAlchemy 0.6/0.7" %
|
||||
"The %s dialect is not yet ported to the 0.6 format" %
|
||||
self.name)
|
||||
|
||||
self.convert_unicode = convert_unicode
|
||||
if assert_unicode:
|
||||
util.warn_deprecated(
|
||||
"assert_unicode is deprecated. "
|
||||
"SQLAlchemy emits a warning in all cases where it "
|
||||
"would otherwise like to encode a Python unicode object "
|
||||
"into a specific encoding but a plain bytestring is "
|
||||
"received. "
|
||||
"This does *not* apply to DBAPIs that coerce Unicode "
|
||||
"natively.")
|
||||
|
||||
self.encoding = encoding
|
||||
self.positional = False
|
||||
self._ischema = None
|
||||
@@ -134,6 +149,11 @@ class DefaultDialect(base.Dialect):
|
||||
self.positional = self.paramstyle in ('qmark', 'format', 'numeric')
|
||||
self.identifier_preparer = self.preparer(self)
|
||||
self.type_compiler = self.type_compiler(self)
|
||||
if supports_right_nested_joins is not None:
|
||||
self.supports_right_nested_joins = supports_right_nested_joins
|
||||
if supports_native_boolean is not None:
|
||||
self.supports_native_boolean = supports_native_boolean
|
||||
self.case_sensitive = case_sensitive
|
||||
|
||||
if label_length and label_length > self.max_identifier_length:
|
||||
raise exc.ArgumentError(
|
||||
@@ -143,16 +163,19 @@ class DefaultDialect(base.Dialect):
|
||||
self.label_length = label_length
|
||||
|
||||
if self.description_encoding == 'use_encoding':
|
||||
self._description_decoder = processors.to_unicode_processor_factory(
|
||||
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_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()
|
||||
@@ -185,6 +208,10 @@ class DefaultDialect(base.Dialect):
|
||||
|
||||
self.returns_unicode_strings = self._check_unicode_returns(connection)
|
||||
|
||||
if self.description_encoding is not None and \
|
||||
self._check_unicode_description(connection):
|
||||
self._description_decoder = self.description_encoding = None
|
||||
|
||||
self.do_rollback(connection.connection)
|
||||
|
||||
def on_connect(self):
|
||||
@@ -202,14 +229,11 @@ class DefaultDialect(base.Dialect):
|
||||
return None
|
||||
|
||||
def _check_unicode_returns(self, connection):
|
||||
# Py2K
|
||||
if self.supports_unicode_statements:
|
||||
cast_to = unicode
|
||||
if util.py2k and not self.supports_unicode_statements:
|
||||
cast_to = util.binary_type
|
||||
else:
|
||||
cast_to = str
|
||||
# end Py2K
|
||||
# Py3K
|
||||
#cast_to = str
|
||||
cast_to = util.text_type
|
||||
|
||||
def check_unicode(formatstr, type_):
|
||||
cursor = connection.connection.cursor()
|
||||
try:
|
||||
@@ -217,16 +241,17 @@ class DefaultDialect(base.Dialect):
|
||||
cursor.execute(
|
||||
cast_to(
|
||||
expression.select(
|
||||
[expression.cast(
|
||||
expression.literal_column(
|
||||
"'test %s returns'" % formatstr), type_)
|
||||
[expression.cast(
|
||||
expression.literal_column(
|
||||
"'test %s returns'" % formatstr),
|
||||
type_)
|
||||
]).compile(dialect=self)
|
||||
)
|
||||
)
|
||||
row = cursor.fetchone()
|
||||
|
||||
return isinstance(row[0], unicode)
|
||||
except self.dbapi.Error, de:
|
||||
return isinstance(row[0], util.text_type)
|
||||
except self.dbapi.Error as de:
|
||||
util.warn("Exception attempting to "
|
||||
"detect unicode returns: %r" % de)
|
||||
return False
|
||||
@@ -244,18 +269,41 @@ class DefaultDialect(base.Dialect):
|
||||
else:
|
||||
return unicode_for_varchar
|
||||
|
||||
def _check_unicode_description(self, connection):
|
||||
# all DBAPIs on Py2K return cursor.description as encoded,
|
||||
# until pypy2.1beta2 with sqlite, so let's just check it -
|
||||
# it's likely others will start doing this too in Py2k.
|
||||
|
||||
if util.py2k and not self.supports_unicode_statements:
|
||||
cast_to = util.binary_type
|
||||
else:
|
||||
cast_to = util.text_type
|
||||
|
||||
cursor = connection.connection.cursor()
|
||||
try:
|
||||
cursor.execute(
|
||||
cast_to(
|
||||
expression.select([
|
||||
expression.literal_column("'x'").label("some_label")
|
||||
]).compile(dialect=self)
|
||||
)
|
||||
)
|
||||
return isinstance(cursor.description[0][0], util.text_type)
|
||||
finally:
|
||||
cursor.close()
|
||||
|
||||
def type_descriptor(self, typeobj):
|
||||
"""Provide a database-specific ``TypeEngine`` object, given
|
||||
"""Provide a database-specific :class:`.TypeEngine` object, given
|
||||
the generic object which comes from the types module.
|
||||
|
||||
This method looks for a dictionary called
|
||||
``colspecs`` as a class or instance-level variable,
|
||||
and passes on to ``types.adapt_type()``.
|
||||
and passes on to :func:`.types.adapt_type`.
|
||||
|
||||
"""
|
||||
return sqltypes.adapt_type(typeobj, self.colspecs)
|
||||
|
||||
def reflecttable(self, connection, table, include_columns, exclude_columns=None):
|
||||
def reflecttable(self, connection, table, include_columns, exclude_columns):
|
||||
insp = reflection.Inspector.from_engine(connection)
|
||||
return insp.reflecttable(table, include_columns, exclude_columns)
|
||||
|
||||
@@ -285,26 +333,35 @@ class DefaultDialect(base.Dialect):
|
||||
opts.update(url.query)
|
||||
return [[], opts]
|
||||
|
||||
def do_begin(self, connection):
|
||||
"""Implementations might want to put logic here for turning
|
||||
autocommit on/off, etc.
|
||||
"""
|
||||
def set_engine_execution_options(self, engine, opts):
|
||||
if 'isolation_level' in opts:
|
||||
isolation_level = opts['isolation_level']
|
||||
@event.listens_for(engine, "engine_connect")
|
||||
def set_isolation(connection, branch):
|
||||
if not branch:
|
||||
self._set_connection_isolation(connection, isolation_level)
|
||||
|
||||
def set_connection_execution_options(self, connection, opts):
|
||||
if 'isolation_level' in opts:
|
||||
self._set_connection_isolation(connection, opts['isolation_level'])
|
||||
|
||||
def _set_connection_isolation(self, connection, level):
|
||||
self.set_isolation_level(connection.connection, level)
|
||||
connection.connection._connection_record.\
|
||||
finalize_callback.append(self.reset_isolation_level)
|
||||
|
||||
|
||||
def do_begin(self, dbapi_connection):
|
||||
pass
|
||||
|
||||
def do_rollback(self, connection):
|
||||
"""Implementations might want to put logic here for turning
|
||||
autocommit on/off, etc.
|
||||
"""
|
||||
def do_rollback(self, dbapi_connection):
|
||||
dbapi_connection.rollback()
|
||||
|
||||
connection.rollback()
|
||||
def do_commit(self, dbapi_connection):
|
||||
dbapi_connection.commit()
|
||||
|
||||
def do_commit(self, connection):
|
||||
"""Implementations might want to put logic here for turning
|
||||
autocommit on/off, etc.
|
||||
"""
|
||||
|
||||
connection.commit()
|
||||
def do_close(self, dbapi_connection):
|
||||
dbapi_connection.close()
|
||||
|
||||
def create_xid(self):
|
||||
"""Create a random two-phase transaction ID.
|
||||
@@ -342,7 +399,8 @@ class DefaultDialect(base.Dialect):
|
||||
# the configured default of this dialect.
|
||||
self.set_isolation_level(dbapi_conn, self.default_isolation_level)
|
||||
|
||||
class DefaultExecutionContext(base.ExecutionContext):
|
||||
|
||||
class DefaultExecutionContext(interfaces.ExecutionContext):
|
||||
isinsert = False
|
||||
isupdate = False
|
||||
isdelete = False
|
||||
@@ -353,6 +411,7 @@ class DefaultExecutionContext(base.ExecutionContext):
|
||||
statement = None
|
||||
postfetch_cols = None
|
||||
prefetch_cols = None
|
||||
returning_cols = None
|
||||
_is_implicit_returning = False
|
||||
_is_explicit_returning = False
|
||||
|
||||
@@ -379,10 +438,10 @@ class DefaultExecutionContext(base.ExecutionContext):
|
||||
self.execution_options.update(connection._execution_options)
|
||||
|
||||
if not dialect.supports_unicode_statements:
|
||||
self.unicode_statement = unicode(compiled)
|
||||
self.unicode_statement = util.text_type(compiled)
|
||||
self.statement = dialect._encoder(self.unicode_statement)[0]
|
||||
else:
|
||||
self.statement = self.unicode_statement = unicode(compiled)
|
||||
self.statement = self.unicode_statement = util.text_type(compiled)
|
||||
|
||||
self.cursor = self.create_cursor()
|
||||
self.compiled_parameters = []
|
||||
@@ -395,7 +454,8 @@ class DefaultExecutionContext(base.ExecutionContext):
|
||||
return self
|
||||
|
||||
@classmethod
|
||||
def _init_compiled(cls, dialect, connection, dbapi_connection, compiled, parameters):
|
||||
def _init_compiled(cls, dialect, connection, dbapi_connection,
|
||||
compiled, parameters):
|
||||
"""Initialize execution context for a Compiled construct."""
|
||||
|
||||
self = cls.__new__(cls)
|
||||
@@ -419,9 +479,10 @@ class DefaultExecutionContext(base.ExecutionContext):
|
||||
|
||||
self.result_map = compiled.result_map
|
||||
|
||||
self.unicode_statement = unicode(compiled)
|
||||
self.unicode_statement = util.text_type(compiled)
|
||||
if not dialect.supports_unicode_statements:
|
||||
self.statement = self.unicode_statement.encode(self.dialect.encoding)
|
||||
self.statement = self.unicode_statement.encode(
|
||||
self.dialect.encoding)
|
||||
else:
|
||||
self.statement = self.unicode_statement
|
||||
|
||||
@@ -430,16 +491,16 @@ class DefaultExecutionContext(base.ExecutionContext):
|
||||
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
|
||||
self._is_explicit_returning = bool(compiled.statement._returning)
|
||||
self._is_implicit_returning = bool(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)]
|
||||
grp, m in enumerate(parameters)]
|
||||
|
||||
self.executemany = len(parameters) > 1
|
||||
|
||||
@@ -447,6 +508,7 @@ class DefaultExecutionContext(base.ExecutionContext):
|
||||
if self.isinsert or self.isupdate:
|
||||
self.postfetch_cols = self.compiled.postfetch
|
||||
self.prefetch_cols = self.compiled.prefetch
|
||||
self.returning_cols = self.compiled.returning
|
||||
self.__process_defaults()
|
||||
|
||||
processors = compiled._bind_processors
|
||||
@@ -474,7 +536,8 @@ class DefaultExecutionContext(base.ExecutionContext):
|
||||
param[dialect._encoder(key)[0]] = \
|
||||
processors[key](compiled_params[key])
|
||||
else:
|
||||
param[dialect._encoder(key)[0]] = compiled_params[key]
|
||||
param[dialect._encoder(key)[0]] = \
|
||||
compiled_params[key]
|
||||
else:
|
||||
for key in compiled_params:
|
||||
if key in processors:
|
||||
@@ -487,7 +550,8 @@ class DefaultExecutionContext(base.ExecutionContext):
|
||||
return self
|
||||
|
||||
@classmethod
|
||||
def _init_statement(cls, dialect, connection, dbapi_connection, statement, parameters):
|
||||
def _init_statement(cls, dialect, connection, dbapi_connection,
|
||||
statement, parameters):
|
||||
"""Initialize execution context for a string SQL statement."""
|
||||
|
||||
self = cls.__new__(cls)
|
||||
@@ -510,7 +574,7 @@ class DefaultExecutionContext(base.ExecutionContext):
|
||||
if dialect.supports_unicode_statements:
|
||||
self.parameters = parameters
|
||||
else:
|
||||
self.parameters= [
|
||||
self.parameters = [
|
||||
dict((dialect._encoder(k)[0], d[k]) for k in d)
|
||||
for d in parameters
|
||||
] or [{}]
|
||||
@@ -520,7 +584,8 @@ class DefaultExecutionContext(base.ExecutionContext):
|
||||
|
||||
self.executemany = len(parameters) > 1
|
||||
|
||||
if not dialect.supports_unicode_statements and isinstance(statement, unicode):
|
||||
if not dialect.supports_unicode_statements and \
|
||||
isinstance(statement, util.text_type):
|
||||
self.unicode_statement = statement
|
||||
self.statement = dialect._encoder(statement)[0]
|
||||
else:
|
||||
@@ -574,8 +639,8 @@ class DefaultExecutionContext(base.ExecutionContext):
|
||||
"""
|
||||
|
||||
conn = self.root_connection
|
||||
if isinstance(stmt, unicode) and \
|
||||
not self.dialect.supports_unicode_statements:
|
||||
if isinstance(stmt, util.text_type) and \
|
||||
not self.dialect.supports_unicode_statements:
|
||||
stmt = self.dialect._encoder(stmt)[0]
|
||||
|
||||
if self.dialect.positional:
|
||||
@@ -583,7 +648,7 @@ class DefaultExecutionContext(base.ExecutionContext):
|
||||
else:
|
||||
default_params = {}
|
||||
|
||||
conn._cursor_execute(self.cursor, stmt, default_params)
|
||||
conn._cursor_execute(self.cursor, stmt, default_params, context=self)
|
||||
r = self.cursor.fetchone()[0]
|
||||
if type_ is not None:
|
||||
# apply type post processors to the result
|
||||
@@ -611,6 +676,16 @@ class DefaultExecutionContext(base.ExecutionContext):
|
||||
def post_exec(self):
|
||||
pass
|
||||
|
||||
def get_result_processor(self, type_, colname, coltype):
|
||||
"""Return a 'result processor' for a given type as present in
|
||||
cursor.description.
|
||||
|
||||
This has a default implementation that dialects can override
|
||||
for context-sensitive result type handling.
|
||||
|
||||
"""
|
||||
return type_._cached_result_processor(self.dialect, coltype)
|
||||
|
||||
def get_lastrowid(self):
|
||||
"""return self.cursor.lastrowid, or equivalent, after an INSERT.
|
||||
|
||||
@@ -643,7 +718,7 @@ class DefaultExecutionContext(base.ExecutionContext):
|
||||
pass
|
||||
|
||||
def get_result_proxy(self):
|
||||
return base.ResultProxy(self)
|
||||
return result.ResultProxy(self)
|
||||
|
||||
@property
|
||||
def rowcount(self):
|
||||
@@ -657,22 +732,24 @@ class DefaultExecutionContext(base.ExecutionContext):
|
||||
|
||||
def post_insert(self):
|
||||
if not self._is_implicit_returning and \
|
||||
not self._is_explicit_returning and \
|
||||
not self.compiled.inline 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()
|
||||
|
||||
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)
|
||||
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
|
||||
lastrowid if c is autoinc_col else v
|
||||
for c, v in zip(
|
||||
table.primary_key,
|
||||
self.inserted_primary_key)
|
||||
@@ -690,6 +767,11 @@ class DefaultExecutionContext(base.ExecutionContext):
|
||||
ipk.append(row[c])
|
||||
|
||||
self.inserted_primary_key = ipk
|
||||
self.returned_defaults = row
|
||||
|
||||
def _fetch_implicit_update_returning(self, resultproxy):
|
||||
row = resultproxy.fetchone()
|
||||
self.returned_defaults = row
|
||||
|
||||
def lastrow_has_defaults(self):
|
||||
return (self.isinsert or self.isupdate) and \
|
||||
@@ -716,28 +798,34 @@ class DefaultExecutionContext(base.ExecutionContext):
|
||||
inputsizes = []
|
||||
for key in self.compiled.positiontup:
|
||||
typeengine = types[key]
|
||||
dbtype = typeengine.dialect_impl(self.dialect).get_dbapi_type(self.dialect.dbapi)
|
||||
if dbtype is not None and (not exclude_types or dbtype not in exclude_types):
|
||||
dbtype = typeengine.dialect_impl(self.dialect).\
|
||||
get_dbapi_type(self.dialect.dbapi)
|
||||
if dbtype is not None and \
|
||||
(not exclude_types or dbtype not in exclude_types):
|
||||
inputsizes.append(dbtype)
|
||||
try:
|
||||
self.cursor.setinputsizes(*inputsizes)
|
||||
except Exception, e:
|
||||
self.root_connection._handle_dbapi_exception(e, None, None, None, self)
|
||||
raise
|
||||
except Exception as e:
|
||||
self.root_connection._handle_dbapi_exception(
|
||||
e, None, None, None, self)
|
||||
else:
|
||||
inputsizes = {}
|
||||
for key in self.compiled.bind_names.values():
|
||||
typeengine = types[key]
|
||||
dbtype = typeengine.dialect_impl(self.dialect).get_dbapi_type(self.dialect.dbapi)
|
||||
if dbtype is not None and (not exclude_types or dbtype not in exclude_types):
|
||||
dbtype = typeengine.dialect_impl(self.dialect).\
|
||||
get_dbapi_type(self.dialect.dbapi)
|
||||
if dbtype is not None and \
|
||||
(not exclude_types or dbtype not in exclude_types):
|
||||
if translate:
|
||||
key = translate.get(key, key)
|
||||
inputsizes[self.dialect._encoder(key)[0]] = dbtype
|
||||
if not self.dialect.supports_unicode_binds:
|
||||
key = self.dialect._encoder(key)[0]
|
||||
inputsizes[key] = dbtype
|
||||
try:
|
||||
self.cursor.setinputsizes(**inputsizes)
|
||||
except Exception, e:
|
||||
self.root_connection._handle_dbapi_exception(e, None, None, None, self)
|
||||
raise
|
||||
except Exception as e:
|
||||
self.root_connection._handle_dbapi_exception(
|
||||
e, None, None, None, self)
|
||||
|
||||
def _exec_default(self, default, type_):
|
||||
if default.is_sequence:
|
||||
|
||||
@@ -0,0 +1,846 @@
|
||||
# engine/interfaces.py
|
||||
# Copyright (C) 2005-2014 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
|
||||
|
||||
"""Define core interfaces used by the engine system."""
|
||||
|
||||
from .. import util, event
|
||||
|
||||
# backwards compat
|
||||
from ..sql.compiler import Compiled, TypeCompiler
|
||||
|
||||
class Dialect(object):
|
||||
"""Define the behavior of a specific database and DB-API combination.
|
||||
|
||||
Any aspect of metadata definition, SQL query generation,
|
||||
execution, result-set handling, or anything else which varies
|
||||
between databases is defined under the general category of the
|
||||
Dialect. The Dialect acts as a factory for other
|
||||
database-specific object implementations including
|
||||
ExecutionContext, Compiled, DefaultGenerator, and TypeEngine.
|
||||
|
||||
All Dialects implement the following attributes:
|
||||
|
||||
name
|
||||
identifying name for the dialect from a DBAPI-neutral point of view
|
||||
(i.e. 'sqlite')
|
||||
|
||||
driver
|
||||
identifying name for the dialect's DBAPI
|
||||
|
||||
positional
|
||||
True if the paramstyle for this Dialect is positional.
|
||||
|
||||
paramstyle
|
||||
the paramstyle to be used (some DB-APIs support multiple
|
||||
paramstyles).
|
||||
|
||||
convert_unicode
|
||||
True if Unicode conversion should be applied to all ``str``
|
||||
types.
|
||||
|
||||
encoding
|
||||
type of encoding to use for unicode, usually defaults to
|
||||
'utf-8'.
|
||||
|
||||
statement_compiler
|
||||
a :class:`.Compiled` class used to compile SQL statements
|
||||
|
||||
ddl_compiler
|
||||
a :class:`.Compiled` class used to compile DDL statements
|
||||
|
||||
server_version_info
|
||||
a tuple containing a version number for the DB backend in use.
|
||||
This value is only available for supporting dialects, and is
|
||||
typically populated during the initial connection to the database.
|
||||
|
||||
default_schema_name
|
||||
the name of the default schema. This value is only available for
|
||||
supporting dialects, and is typically populated during the
|
||||
initial connection to the database.
|
||||
|
||||
execution_ctx_cls
|
||||
a :class:`.ExecutionContext` class used to handle statement execution
|
||||
|
||||
execute_sequence_format
|
||||
either the 'tuple' or 'list' type, depending on what cursor.execute()
|
||||
accepts for the second argument (they vary).
|
||||
|
||||
preparer
|
||||
a :class:`~sqlalchemy.sql.compiler.IdentifierPreparer` class used to
|
||||
quote identifiers.
|
||||
|
||||
supports_alter
|
||||
``True`` if the database supports ``ALTER TABLE``.
|
||||
|
||||
max_identifier_length
|
||||
The maximum length of identifier names.
|
||||
|
||||
supports_unicode_statements
|
||||
Indicate whether the DB-API can receive SQL statements as Python
|
||||
unicode strings
|
||||
|
||||
supports_unicode_binds
|
||||
Indicate whether the DB-API can receive string bind parameters
|
||||
as Python unicode strings
|
||||
|
||||
supports_sane_rowcount
|
||||
Indicate whether the dialect properly implements rowcount for
|
||||
``UPDATE`` and ``DELETE`` statements.
|
||||
|
||||
supports_sane_multi_rowcount
|
||||
Indicate whether the dialect properly implements rowcount for
|
||||
``UPDATE`` and ``DELETE`` statements when executed via
|
||||
executemany.
|
||||
|
||||
preexecute_autoincrement_sequences
|
||||
True if 'implicit' primary key functions must be executed separately
|
||||
in order to get their value. This is currently oriented towards
|
||||
Postgresql.
|
||||
|
||||
implicit_returning
|
||||
use RETURNING or equivalent during INSERT execution in order to load
|
||||
newly generated primary keys and other column defaults in one execution,
|
||||
which are then available via inserted_primary_key.
|
||||
If an insert statement has returning() specified explicitly,
|
||||
the "implicit" functionality is not used and inserted_primary_key
|
||||
will not be available.
|
||||
|
||||
dbapi_type_map
|
||||
A mapping of DB-API type objects present in this Dialect's
|
||||
DB-API implementation mapped to TypeEngine implementations used
|
||||
by the dialect.
|
||||
|
||||
This is used to apply types to result sets based on the DB-API
|
||||
types present in cursor.description; it only takes effect for
|
||||
result sets against textual statements where no explicit
|
||||
typemap was present.
|
||||
|
||||
colspecs
|
||||
A dictionary of TypeEngine classes from sqlalchemy.types mapped
|
||||
to subclasses that are specific to the dialect class. This
|
||||
dictionary is class-level only and is not accessed from the
|
||||
dialect instance itself.
|
||||
|
||||
supports_default_values
|
||||
Indicates if the construct ``INSERT INTO tablename DEFAULT
|
||||
VALUES`` is supported
|
||||
|
||||
supports_sequences
|
||||
Indicates if the dialect supports CREATE SEQUENCE or similar.
|
||||
|
||||
sequences_optional
|
||||
If True, indicates if the "optional" flag on the Sequence() construct
|
||||
should signal to not generate a CREATE SEQUENCE. Applies only to
|
||||
dialects that support sequences. Currently used only to allow Postgresql
|
||||
SERIAL to be used on a column that specifies Sequence() for usage on
|
||||
other backends.
|
||||
|
||||
supports_native_enum
|
||||
Indicates if the dialect supports a native ENUM construct.
|
||||
This will prevent types.Enum from generating a CHECK
|
||||
constraint when that type is used.
|
||||
|
||||
supports_native_boolean
|
||||
Indicates if the dialect supports a native boolean construct.
|
||||
This will prevent types.Boolean from generating a CHECK
|
||||
constraint when that type is used.
|
||||
|
||||
"""
|
||||
|
||||
def create_connect_args(self, url):
|
||||
"""Build DB-API compatible connection arguments.
|
||||
|
||||
Given a :class:`~sqlalchemy.engine.url.URL` object, returns a tuple
|
||||
consisting of a `*args`/`**kwargs` suitable to send directly
|
||||
to the dbapi's connect function.
|
||||
|
||||
"""
|
||||
|
||||
raise NotImplementedError()
|
||||
|
||||
@classmethod
|
||||
def type_descriptor(cls, typeobj):
|
||||
"""Transform a generic type to a dialect-specific type.
|
||||
|
||||
Dialect classes will usually use the
|
||||
:func:`.types.adapt_type` function in the types module to
|
||||
accomplish this.
|
||||
|
||||
The returned result is cached *per dialect class* so can
|
||||
contain no dialect-instance state.
|
||||
|
||||
"""
|
||||
|
||||
raise NotImplementedError()
|
||||
|
||||
def initialize(self, connection):
|
||||
"""Called during strategized creation of the dialect with a
|
||||
connection.
|
||||
|
||||
Allows dialects to configure options based on server version info or
|
||||
other properties.
|
||||
|
||||
The connection passed here is a SQLAlchemy Connection object,
|
||||
with full capabilities.
|
||||
|
||||
The initalize() method of the base dialect should be called via
|
||||
super().
|
||||
|
||||
"""
|
||||
|
||||
pass
|
||||
|
||||
def reflecttable(self, connection, table, include_columns, exclude_columns):
|
||||
"""Load table description from the database.
|
||||
|
||||
Given a :class:`.Connection` and a
|
||||
:class:`~sqlalchemy.schema.Table` object, reflect its columns and
|
||||
properties from the database.
|
||||
|
||||
The implementation of this method is provided by
|
||||
:meth:`.DefaultDialect.reflecttable`, which makes use of
|
||||
:class:`.Inspector` to retrieve column information.
|
||||
|
||||
Dialects should **not** seek to implement this method, and should
|
||||
instead implement individual schema inspection operations such as
|
||||
:meth:`.Dialect.get_columns`, :meth:`.Dialect.get_pk_constraint`,
|
||||
etc.
|
||||
|
||||
"""
|
||||
|
||||
raise NotImplementedError()
|
||||
|
||||
def get_columns(self, connection, table_name, schema=None, **kw):
|
||||
"""Return information about columns in `table_name`.
|
||||
|
||||
Given a :class:`.Connection`, a string
|
||||
`table_name`, and an optional string `schema`, return column
|
||||
information as a list of dictionaries with these keys:
|
||||
|
||||
name
|
||||
the column's name
|
||||
|
||||
type
|
||||
[sqlalchemy.types#TypeEngine]
|
||||
|
||||
nullable
|
||||
boolean
|
||||
|
||||
default
|
||||
the column's default value
|
||||
|
||||
autoincrement
|
||||
boolean
|
||||
|
||||
sequence
|
||||
a dictionary of the form
|
||||
{'name' : str, 'start' :int, 'increment': int}
|
||||
|
||||
Additional column attributes may be present.
|
||||
"""
|
||||
|
||||
raise NotImplementedError()
|
||||
|
||||
def get_primary_keys(self, connection, table_name, schema=None, **kw):
|
||||
"""Return information about primary keys in `table_name`.
|
||||
|
||||
|
||||
Deprecated. This method is only called by the default
|
||||
implementation of :meth:`.Dialect.get_pk_constraint`. Dialects should
|
||||
instead implement the :meth:`.Dialect.get_pk_constraint` method directly.
|
||||
|
||||
"""
|
||||
|
||||
raise NotImplementedError()
|
||||
|
||||
def get_pk_constraint(self, connection, table_name, schema=None, **kw):
|
||||
"""Return information about the primary key constraint on
|
||||
table_name`.
|
||||
|
||||
Given a :class:`.Connection`, a string
|
||||
`table_name`, and an optional string `schema`, return primary
|
||||
key information as a dictionary with these keys:
|
||||
|
||||
constrained_columns
|
||||
a list of column names that make up the primary key
|
||||
|
||||
name
|
||||
optional name of the primary key constraint.
|
||||
|
||||
"""
|
||||
raise NotImplementedError()
|
||||
|
||||
def get_foreign_keys(self, connection, table_name, schema=None, **kw):
|
||||
"""Return information about foreign_keys in `table_name`.
|
||||
|
||||
Given a :class:`.Connection`, a string
|
||||
`table_name`, and an optional string `schema`, return foreign
|
||||
key information as a list of dicts with these keys:
|
||||
|
||||
name
|
||||
the constraint's name
|
||||
|
||||
constrained_columns
|
||||
a list of column names that make up the foreign key
|
||||
|
||||
referred_schema
|
||||
the name of the referred schema
|
||||
|
||||
referred_table
|
||||
the name of the referred table
|
||||
|
||||
referred_columns
|
||||
a list of column names in the referred table that correspond to
|
||||
constrained_columns
|
||||
"""
|
||||
|
||||
raise NotImplementedError()
|
||||
|
||||
def get_table_names(self, connection, schema=None, **kw):
|
||||
"""Return a list of table names for `schema`."""
|
||||
|
||||
raise NotImplementedError
|
||||
|
||||
def get_view_names(self, connection, schema=None, **kw):
|
||||
"""Return a list of all view names available in the database.
|
||||
|
||||
schema:
|
||||
Optional, retrieve names from a non-default schema.
|
||||
"""
|
||||
|
||||
raise NotImplementedError()
|
||||
|
||||
def get_view_definition(self, connection, view_name, schema=None, **kw):
|
||||
"""Return view definition.
|
||||
|
||||
Given a :class:`.Connection`, a string
|
||||
`view_name`, and an optional string `schema`, return the view
|
||||
definition.
|
||||
"""
|
||||
|
||||
raise NotImplementedError()
|
||||
|
||||
def get_indexes(self, connection, table_name, schema=None, **kw):
|
||||
"""Return information about indexes in `table_name`.
|
||||
|
||||
Given a :class:`.Connection`, a string
|
||||
`table_name` and an optional string `schema`, return index
|
||||
information as a list of dictionaries with these keys:
|
||||
|
||||
name
|
||||
the index's name
|
||||
|
||||
column_names
|
||||
list of column names in order
|
||||
|
||||
unique
|
||||
boolean
|
||||
"""
|
||||
|
||||
raise NotImplementedError()
|
||||
|
||||
def get_unique_constraints(self, table_name, schema=None, **kw):
|
||||
"""Return information about unique constraints in `table_name`.
|
||||
|
||||
Given a string `table_name` and an optional string `schema`, return
|
||||
unique constraint information as a list of dicts with these keys:
|
||||
|
||||
name
|
||||
the unique constraint's name
|
||||
|
||||
column_names
|
||||
list of column names in order
|
||||
|
||||
\**kw
|
||||
other options passed to the dialect's get_unique_constraints() method.
|
||||
|
||||
.. versionadded:: 0.9.0
|
||||
|
||||
"""
|
||||
|
||||
raise NotImplementedError()
|
||||
|
||||
def normalize_name(self, name):
|
||||
"""convert the given name to lowercase if it is detected as
|
||||
case insensitive.
|
||||
|
||||
this method is only used if the dialect defines
|
||||
requires_name_normalize=True.
|
||||
|
||||
"""
|
||||
raise NotImplementedError()
|
||||
|
||||
def denormalize_name(self, name):
|
||||
"""convert the given name to a case insensitive identifier
|
||||
for the backend if it is an all-lowercase name.
|
||||
|
||||
this method is only used if the dialect defines
|
||||
requires_name_normalize=True.
|
||||
|
||||
"""
|
||||
raise NotImplementedError()
|
||||
|
||||
def has_table(self, connection, table_name, schema=None):
|
||||
"""Check the existence of a particular table in the database.
|
||||
|
||||
Given a :class:`.Connection` object and a string
|
||||
`table_name`, return True if the given table (possibly within
|
||||
the specified `schema`) exists in the database, False
|
||||
otherwise.
|
||||
"""
|
||||
|
||||
raise NotImplementedError()
|
||||
|
||||
def has_sequence(self, connection, sequence_name, schema=None):
|
||||
"""Check the existence of a particular sequence in the database.
|
||||
|
||||
Given a :class:`.Connection` object and a string
|
||||
`sequence_name`, return True if the given sequence exists in
|
||||
the database, False otherwise.
|
||||
"""
|
||||
|
||||
raise NotImplementedError()
|
||||
|
||||
def _get_server_version_info(self, connection):
|
||||
"""Retrieve the server version info from the given connection.
|
||||
|
||||
This is used by the default implementation to populate the
|
||||
"server_version_info" attribute and is called exactly
|
||||
once upon first connect.
|
||||
|
||||
"""
|
||||
|
||||
raise NotImplementedError()
|
||||
|
||||
def _get_default_schema_name(self, connection):
|
||||
"""Return the string name of the currently selected schema from
|
||||
the given connection.
|
||||
|
||||
This is used by the default implementation to populate the
|
||||
"default_schema_name" attribute and is called exactly
|
||||
once upon first connect.
|
||||
|
||||
"""
|
||||
|
||||
raise NotImplementedError()
|
||||
|
||||
def do_begin(self, dbapi_connection):
|
||||
"""Provide an implementation of ``connection.begin()``, given a
|
||||
DB-API connection.
|
||||
|
||||
The DBAPI has no dedicated "begin" method and it is expected
|
||||
that transactions are implicit. This hook is provided for those
|
||||
DBAPIs that might need additional help in this area.
|
||||
|
||||
Note that :meth:`.Dialect.do_begin` is not called unless a
|
||||
:class:`.Transaction` object is in use. The
|
||||
:meth:`.Dialect.do_autocommit`
|
||||
hook is provided for DBAPIs that need some extra commands emitted
|
||||
after a commit in order to enter the next transaction, when the
|
||||
SQLAlchemy :class:`.Connection` is used in it's default "autocommit"
|
||||
mode.
|
||||
|
||||
:param dbapi_connection: a DBAPI connection, typically
|
||||
proxied within a :class:`.ConnectionFairy`.
|
||||
|
||||
"""
|
||||
|
||||
raise NotImplementedError()
|
||||
|
||||
def do_rollback(self, dbapi_connection):
|
||||
"""Provide an implementation of ``connection.rollback()``, given
|
||||
a DB-API connection.
|
||||
|
||||
:param dbapi_connection: a DBAPI connection, typically
|
||||
proxied within a :class:`.ConnectionFairy`.
|
||||
|
||||
"""
|
||||
|
||||
raise NotImplementedError()
|
||||
|
||||
|
||||
def do_commit(self, dbapi_connection):
|
||||
"""Provide an implementation of ``connection.commit()``, given a
|
||||
DB-API connection.
|
||||
|
||||
:param dbapi_connection: a DBAPI connection, typically
|
||||
proxied within a :class:`.ConnectionFairy`.
|
||||
|
||||
"""
|
||||
|
||||
raise NotImplementedError()
|
||||
|
||||
def do_close(self, dbapi_connection):
|
||||
"""Provide an implementation of ``connection.close()``, given a DBAPI
|
||||
connection.
|
||||
|
||||
This hook is called by the :class:`.Pool` when a connection has been
|
||||
detached from the pool, or is being returned beyond the normal
|
||||
capacity of the pool.
|
||||
|
||||
.. versionadded:: 0.8
|
||||
|
||||
"""
|
||||
|
||||
raise NotImplementedError()
|
||||
|
||||
def create_xid(self):
|
||||
"""Create a two-phase transaction ID.
|
||||
|
||||
This id will be passed to do_begin_twophase(),
|
||||
do_rollback_twophase(), do_commit_twophase(). Its format is
|
||||
unspecified.
|
||||
"""
|
||||
|
||||
raise NotImplementedError()
|
||||
|
||||
def do_savepoint(self, connection, name):
|
||||
"""Create a savepoint with the given name.
|
||||
|
||||
:param connection: a :class:`.Connection`.
|
||||
:param name: savepoint name.
|
||||
|
||||
"""
|
||||
|
||||
raise NotImplementedError()
|
||||
|
||||
def do_rollback_to_savepoint(self, connection, name):
|
||||
"""Rollback a connection to the named savepoint.
|
||||
|
||||
:param connection: a :class:`.Connection`.
|
||||
:param name: savepoint name.
|
||||
|
||||
"""
|
||||
|
||||
raise NotImplementedError()
|
||||
|
||||
def do_release_savepoint(self, connection, name):
|
||||
"""Release the named savepoint on a connection.
|
||||
|
||||
:param connection: a :class:`.Connection`.
|
||||
:param name: savepoint name.
|
||||
"""
|
||||
|
||||
raise NotImplementedError()
|
||||
|
||||
def do_begin_twophase(self, connection, xid):
|
||||
"""Begin a two phase transaction on the given connection.
|
||||
|
||||
:param connection: a :class:`.Connection`.
|
||||
:param xid: xid
|
||||
|
||||
"""
|
||||
|
||||
raise NotImplementedError()
|
||||
|
||||
def do_prepare_twophase(self, connection, xid):
|
||||
"""Prepare a two phase transaction on the given connection.
|
||||
|
||||
:param connection: a :class:`.Connection`.
|
||||
:param xid: xid
|
||||
|
||||
"""
|
||||
|
||||
raise NotImplementedError()
|
||||
|
||||
def do_rollback_twophase(self, connection, xid, is_prepared=True,
|
||||
recover=False):
|
||||
"""Rollback a two phase transaction on the given connection.
|
||||
|
||||
:param connection: a :class:`.Connection`.
|
||||
:param xid: xid
|
||||
:param is_prepared: whether or not
|
||||
:meth:`.TwoPhaseTransaction.prepare` was called.
|
||||
:param recover: if the recover flag was passed.
|
||||
|
||||
"""
|
||||
|
||||
raise NotImplementedError()
|
||||
|
||||
def do_commit_twophase(self, connection, xid, is_prepared=True,
|
||||
recover=False):
|
||||
"""Commit a two phase transaction on the given connection.
|
||||
|
||||
|
||||
:param connection: a :class:`.Connection`.
|
||||
:param xid: xid
|
||||
:param is_prepared: whether or not
|
||||
:meth:`.TwoPhaseTransaction.prepare` was called.
|
||||
:param recover: if the recover flag was passed.
|
||||
|
||||
"""
|
||||
|
||||
raise NotImplementedError()
|
||||
|
||||
def do_recover_twophase(self, connection):
|
||||
"""Recover list of uncommited prepared two phase transaction
|
||||
identifiers on the given connection.
|
||||
|
||||
:param connection: a :class:`.Connection`.
|
||||
|
||||
"""
|
||||
|
||||
raise NotImplementedError()
|
||||
|
||||
def do_executemany(self, cursor, statement, parameters, context=None):
|
||||
"""Provide an implementation of ``cursor.executemany(statement,
|
||||
parameters)``."""
|
||||
|
||||
raise NotImplementedError()
|
||||
|
||||
def do_execute(self, cursor, statement, parameters, context=None):
|
||||
"""Provide an implementation of ``cursor.execute(statement,
|
||||
parameters)``."""
|
||||
|
||||
raise NotImplementedError()
|
||||
|
||||
def do_execute_no_params(self, cursor, statement, parameters,
|
||||
context=None):
|
||||
"""Provide an implementation of ``cursor.execute(statement)``.
|
||||
|
||||
The parameter collection should not be sent.
|
||||
|
||||
"""
|
||||
|
||||
raise NotImplementedError()
|
||||
|
||||
def is_disconnect(self, e, connection, cursor):
|
||||
"""Return True if the given DB-API error indicates an invalid
|
||||
connection"""
|
||||
|
||||
raise NotImplementedError()
|
||||
|
||||
def connect(self):
|
||||
"""return a callable which sets up a newly created DBAPI connection.
|
||||
|
||||
The callable accepts a single argument "conn" which is the
|
||||
DBAPI connection itself. It has no return value.
|
||||
|
||||
This is used to set dialect-wide per-connection options such as
|
||||
isolation modes, unicode modes, etc.
|
||||
|
||||
If a callable is returned, it will be assembled into a pool listener
|
||||
that receives the direct DBAPI connection, with all wrappers removed.
|
||||
|
||||
If None is returned, no listener will be generated.
|
||||
|
||||
"""
|
||||
return None
|
||||
|
||||
def reset_isolation_level(self, dbapi_conn):
|
||||
"""Given a DBAPI connection, revert its isolation to the default."""
|
||||
|
||||
raise NotImplementedError()
|
||||
|
||||
def set_isolation_level(self, dbapi_conn, level):
|
||||
"""Given a DBAPI connection, set its isolation level."""
|
||||
|
||||
raise NotImplementedError()
|
||||
|
||||
def get_isolation_level(self, dbapi_conn):
|
||||
"""Given a DBAPI connection, return its isolation level."""
|
||||
|
||||
raise NotImplementedError()
|
||||
|
||||
|
||||
class ExecutionContext(object):
|
||||
"""A messenger object for a Dialect that corresponds to a single
|
||||
execution.
|
||||
|
||||
ExecutionContext should have these data members:
|
||||
|
||||
connection
|
||||
Connection object which can be freely used by default value
|
||||
generators to execute SQL. This Connection should reference the
|
||||
same underlying connection/transactional resources of
|
||||
root_connection.
|
||||
|
||||
root_connection
|
||||
Connection object which is the source of this ExecutionContext. This
|
||||
Connection may have close_with_result=True set, in which case it can
|
||||
only be used once.
|
||||
|
||||
dialect
|
||||
dialect which created this ExecutionContext.
|
||||
|
||||
cursor
|
||||
DB-API cursor procured from the connection,
|
||||
|
||||
compiled
|
||||
if passed to constructor, sqlalchemy.engine.base.Compiled object
|
||||
being executed,
|
||||
|
||||
statement
|
||||
string version of the statement to be executed. Is either
|
||||
passed to the constructor, or must be created from the
|
||||
sql.Compiled object by the time pre_exec() has completed.
|
||||
|
||||
parameters
|
||||
bind parameters passed to the execute() method. For compiled
|
||||
statements, this is a dictionary or list of dictionaries. For
|
||||
textual statements, it should be in a format suitable for the
|
||||
dialect's paramstyle (i.e. dict or list of dicts for non
|
||||
positional, list or list of lists/tuples for positional).
|
||||
|
||||
isinsert
|
||||
True if the statement is an INSERT.
|
||||
|
||||
isupdate
|
||||
True if the statement is an UPDATE.
|
||||
|
||||
should_autocommit
|
||||
True if the statement is a "committable" statement.
|
||||
|
||||
prefetch_cols
|
||||
a list of Column objects for which a client-side default
|
||||
was fired off. Applies to inserts and updates.
|
||||
|
||||
postfetch_cols
|
||||
a list of Column objects for which a server-side default or
|
||||
inline SQL expression value was fired off. Applies to inserts
|
||||
and updates.
|
||||
"""
|
||||
|
||||
def create_cursor(self):
|
||||
"""Return a new cursor generated from this ExecutionContext's
|
||||
connection.
|
||||
|
||||
Some dialects may wish to change the behavior of
|
||||
connection.cursor(), such as postgresql which may return a PG
|
||||
"server side" cursor.
|
||||
"""
|
||||
|
||||
raise NotImplementedError()
|
||||
|
||||
def pre_exec(self):
|
||||
"""Called before an execution of a compiled statement.
|
||||
|
||||
If a compiled statement was passed to this ExecutionContext,
|
||||
the `statement` and `parameters` datamembers must be
|
||||
initialized after this statement is complete.
|
||||
"""
|
||||
|
||||
raise NotImplementedError()
|
||||
|
||||
def post_exec(self):
|
||||
"""Called after the execution of a compiled statement.
|
||||
|
||||
If a compiled statement was passed to this ExecutionContext,
|
||||
the `last_insert_ids`, `last_inserted_params`, etc.
|
||||
datamembers should be available after this method completes.
|
||||
"""
|
||||
|
||||
raise NotImplementedError()
|
||||
|
||||
def result(self):
|
||||
"""Return a result object corresponding to this ExecutionContext.
|
||||
|
||||
Returns a ResultProxy.
|
||||
"""
|
||||
|
||||
raise NotImplementedError()
|
||||
|
||||
def handle_dbapi_exception(self, e):
|
||||
"""Receive a DBAPI exception which occurred upon execute, result
|
||||
fetch, etc."""
|
||||
|
||||
raise NotImplementedError()
|
||||
|
||||
def should_autocommit_text(self, statement):
|
||||
"""Parse the given textual statement and return True if it refers to
|
||||
a "committable" statement"""
|
||||
|
||||
raise NotImplementedError()
|
||||
|
||||
def lastrow_has_defaults(self):
|
||||
"""Return True if the last INSERT or UPDATE row contained
|
||||
inlined or database-side defaults.
|
||||
"""
|
||||
|
||||
raise NotImplementedError()
|
||||
|
||||
def get_rowcount(self):
|
||||
"""Return the DBAPI ``cursor.rowcount`` value, or in some
|
||||
cases an interpreted value.
|
||||
|
||||
See :attr:`.ResultProxy.rowcount` for details on this.
|
||||
|
||||
"""
|
||||
|
||||
raise NotImplementedError()
|
||||
|
||||
|
||||
class Connectable(object):
|
||||
"""Interface for an object which supports execution of SQL constructs.
|
||||
|
||||
The two implementations of :class:`.Connectable` are
|
||||
:class:`.Connection` and :class:`.Engine`.
|
||||
|
||||
Connectable must also implement the 'dialect' member which references a
|
||||
:class:`.Dialect` instance.
|
||||
|
||||
"""
|
||||
|
||||
def connect(self, **kwargs):
|
||||
"""Return a :class:`.Connection` object.
|
||||
|
||||
Depending on context, this may be ``self`` if this object
|
||||
is already an instance of :class:`.Connection`, or a newly
|
||||
procured :class:`.Connection` if this object is an instance
|
||||
of :class:`.Engine`.
|
||||
|
||||
"""
|
||||
|
||||
def contextual_connect(self):
|
||||
"""Return a :class:`.Connection` object which may be part of an ongoing
|
||||
context.
|
||||
|
||||
Depending on context, this may be ``self`` if this object
|
||||
is already an instance of :class:`.Connection`, or a newly
|
||||
procured :class:`.Connection` if this object is an instance
|
||||
of :class:`.Engine`.
|
||||
|
||||
"""
|
||||
|
||||
raise NotImplementedError()
|
||||
|
||||
@util.deprecated("0.7",
|
||||
"Use the create() method on the given schema "
|
||||
"object directly, i.e. :meth:`.Table.create`, "
|
||||
":meth:`.Index.create`, :meth:`.MetaData.create_all`")
|
||||
def create(self, entity, **kwargs):
|
||||
"""Emit CREATE statements for the given schema entity.
|
||||
"""
|
||||
|
||||
raise NotImplementedError()
|
||||
|
||||
@util.deprecated("0.7",
|
||||
"Use the drop() method on the given schema "
|
||||
"object directly, i.e. :meth:`.Table.drop`, "
|
||||
":meth:`.Index.drop`, :meth:`.MetaData.drop_all`")
|
||||
def drop(self, entity, **kwargs):
|
||||
"""Emit DROP statements for the given schema entity.
|
||||
"""
|
||||
|
||||
raise NotImplementedError()
|
||||
|
||||
def execute(self, object, *multiparams, **params):
|
||||
"""Executes the given construct and returns a :class:`.ResultProxy`."""
|
||||
raise NotImplementedError()
|
||||
|
||||
def scalar(self, object, *multiparams, **params):
|
||||
"""Executes and returns the first column of the first row.
|
||||
|
||||
The underlying cursor is closed after execution.
|
||||
"""
|
||||
raise NotImplementedError()
|
||||
|
||||
def _run_visitor(self, visitorcallable, element,
|
||||
**kwargs):
|
||||
raise NotImplementedError()
|
||||
|
||||
def _execute_clauseelement(self, elem, multiparams=None, params=None):
|
||||
raise NotImplementedError()
|
||||
@@ -1,5 +1,5 @@
|
||||
# engine/reflection.py
|
||||
# Copyright (C) 2005-2013 the SQLAlchemy authors and contributors <see AUTHORS file>
|
||||
# Copyright (C) 2005-2014 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
|
||||
@@ -24,12 +24,14 @@ methods such as get_table_names, get_columns, etc.
|
||||
'name' attribute..
|
||||
"""
|
||||
|
||||
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
|
||||
from .. import exc, sql
|
||||
from ..sql import schema as sa_schema
|
||||
from .. import util
|
||||
from ..sql.type_api import TypeEngine
|
||||
from ..util import deprecated
|
||||
from ..util import topological
|
||||
from .. import inspection
|
||||
from .base import Connectable
|
||||
|
||||
|
||||
@util.decorator
|
||||
@@ -39,8 +41,12 @@ def cache(fn, self, con, *args, **kw):
|
||||
return fn(self, con, *args, **kw)
|
||||
key = (
|
||||
fn.__name__,
|
||||
tuple(a for a in args if isinstance(a, basestring)),
|
||||
tuple((k, v) for k, v in kw.iteritems() if isinstance(v, (basestring, int, float)))
|
||||
tuple(a for a in args if isinstance(a, util.string_types)),
|
||||
tuple((k, v) for k, v in kw.items() if
|
||||
isinstance(v,
|
||||
util.string_types + util.int_types + (float, )
|
||||
)
|
||||
)
|
||||
)
|
||||
ret = info_cache.get(key)
|
||||
if ret is None:
|
||||
@@ -53,17 +59,24 @@ class Inspector(object):
|
||||
"""Performs database schema inspection.
|
||||
|
||||
The Inspector acts as a proxy to the reflection methods of the
|
||||
:class:`~sqlalchemy.engine.base.Dialect`, providing a
|
||||
:class:`~sqlalchemy.engine.interfaces.Dialect`, providing a
|
||||
consistent interface as well as caching support for previously
|
||||
fetched metadata.
|
||||
|
||||
The preferred method to construct an :class:`.Inspector` is via the
|
||||
:meth:`Inspector.from_engine` method. I.e.::
|
||||
A :class:`.Inspector` object is usually created via the
|
||||
:func:`.inspect` function::
|
||||
|
||||
from sqlalchemy import inspect, create_engine
|
||||
engine = create_engine('...')
|
||||
insp = inspect(engine)
|
||||
|
||||
The inspection method above is equivalent to using the
|
||||
:meth:`.Inspector.from_engine` method, i.e.::
|
||||
|
||||
engine = create_engine('...')
|
||||
insp = Inspector.from_engine(engine)
|
||||
|
||||
Where above, the :class:`~sqlalchemy.engine.base.Dialect` may opt
|
||||
Where above, the :class:`~sqlalchemy.engine.interfaces.Dialect` may opt
|
||||
to return an :class:`.Inspector` subclass that provides additional
|
||||
methods specific to the dialect's target database.
|
||||
|
||||
@@ -72,13 +85,13 @@ class Inspector(object):
|
||||
def __init__(self, bind):
|
||||
"""Initialize a new :class:`.Inspector`.
|
||||
|
||||
:param bind: a :class:`~sqlalchemy.engine.base.Connectable`,
|
||||
:param bind: a :class:`~sqlalchemy.engine.Connectable`,
|
||||
which is typically an instance of
|
||||
:class:`~sqlalchemy.engine.base.Engine` or
|
||||
:class:`~sqlalchemy.engine.base.Connection`.
|
||||
:class:`~sqlalchemy.engine.Engine` or
|
||||
:class:`~sqlalchemy.engine.Connection`.
|
||||
|
||||
For a dialect-specific instance of :class:`.Inspector`, see
|
||||
:meth:`Inspector.from_engine`
|
||||
:meth:`.Inspector.from_engine`
|
||||
|
||||
"""
|
||||
# this might not be a connection, it could be an engine.
|
||||
@@ -99,17 +112,19 @@ class Inspector(object):
|
||||
|
||||
@classmethod
|
||||
def from_engine(cls, bind):
|
||||
"""Construct a new dialect-specific Inspector object from the given engine or connection.
|
||||
"""Construct a new dialect-specific Inspector object from the given
|
||||
engine or connection.
|
||||
|
||||
:param bind: a :class:`~sqlalchemy.engine.base.Connectable`,
|
||||
:param bind: a :class:`~sqlalchemy.engine.Connectable`,
|
||||
which is typically an instance of
|
||||
:class:`~sqlalchemy.engine.base.Engine` or
|
||||
:class:`~sqlalchemy.engine.base.Connection`.
|
||||
:class:`~sqlalchemy.engine.Engine` or
|
||||
:class:`~sqlalchemy.engine.Connection`.
|
||||
|
||||
This method differs from direct a direct constructor call of :class:`.Inspector`
|
||||
in that the :class:`~sqlalchemy.engine.base.Dialect` is given a chance to provide
|
||||
a dialect-specific :class:`.Inspector` instance, which may provide additional
|
||||
methods.
|
||||
This method differs from direct a direct constructor call of
|
||||
:class:`.Inspector` in that the
|
||||
:class:`~sqlalchemy.engine.interfaces.Dialect` is given a chance to
|
||||
provide a dialect-specific :class:`.Inspector` instance, which may
|
||||
provide additional methods.
|
||||
|
||||
See the example at :class:`.Inspector`.
|
||||
|
||||
@@ -118,6 +133,10 @@ class Inspector(object):
|
||||
return bind.dialect.inspector(bind)
|
||||
return Inspector(bind)
|
||||
|
||||
@inspection._inspects(Connectable)
|
||||
def _insp(bind):
|
||||
return Inspector.from_engine(bind)
|
||||
|
||||
@property
|
||||
def default_schema_name(self):
|
||||
"""Return the default schema name presented by the dialect
|
||||
@@ -139,14 +158,32 @@ class Inspector(object):
|
||||
return []
|
||||
|
||||
def get_table_names(self, schema=None, order_by=None):
|
||||
"""Return all table names in `schema`.
|
||||
"""Return all table names in referred to within a particular schema.
|
||||
|
||||
The names are expected to be real tables only, not views.
|
||||
Views are instead returned using the :meth:`.Inspector.get_view_names`
|
||||
method.
|
||||
|
||||
|
||||
:param schema: Schema name. If ``schema`` is left at ``None``, the
|
||||
database's default schema is
|
||||
used, else the named schema is searched. If the database does not
|
||||
support named schemas, behavior is undefined if ``schema`` is not
|
||||
passed as ``None``. For special quoting, use :class:`.quoted_name`.
|
||||
|
||||
:param schema: Optional, retrieve names from a non-default schema.
|
||||
:param order_by: Optional, may be the string "foreign_key" to sort
|
||||
the result on foreign key dependencies.
|
||||
the result on foreign key dependencies.
|
||||
|
||||
.. versionchanged:: 0.8 the "foreign_key" sorting sorts tables
|
||||
in order of dependee to dependent; that is, in creation
|
||||
order, rather than in drop order. This is to maintain
|
||||
consistency with similar features such as
|
||||
:attr:`.MetaData.sorted_tables` and :func:`.util.sort_tables`.
|
||||
|
||||
.. seealso::
|
||||
|
||||
:attr:`.MetaData.sorted_tables`
|
||||
|
||||
This should probably not return view names or maybe it should return
|
||||
them with an indicator t or v.
|
||||
"""
|
||||
|
||||
if hasattr(self.dialect, 'get_table_names'):
|
||||
@@ -155,33 +192,40 @@ class Inspector(object):
|
||||
else:
|
||||
tnames = self.engine.table_names(schema)
|
||||
if order_by == 'foreign_key':
|
||||
import random
|
||||
random.shuffle(tnames)
|
||||
|
||||
tuples = []
|
||||
for tname in tnames:
|
||||
for fkey in self.get_foreign_keys(tname, schema):
|
||||
if tname != fkey['referred_table']:
|
||||
tuples.append((tname, fkey['referred_table']))
|
||||
tuples.append((fkey['referred_table'], tname))
|
||||
tnames = list(topological.sort(tuples, tnames))
|
||||
return tnames
|
||||
|
||||
def get_table_options(self, table_name, schema=None, **kw):
|
||||
"""Return a dictionary of options specified when the table of the given name was created.
|
||||
"""Return a dictionary of options specified when the table of the
|
||||
given name was created.
|
||||
|
||||
This currently includes some options that apply to MySQL tables.
|
||||
|
||||
:param table_name: string name of the table. For special quoting,
|
||||
use :class:`.quoted_name`.
|
||||
|
||||
:param schema: string schema name; if omitted, uses the default schema
|
||||
of the database connection. For special quoting,
|
||||
use :class:`.quoted_name`.
|
||||
|
||||
"""
|
||||
if hasattr(self.dialect, 'get_table_options'):
|
||||
return self.dialect.get_table_options(self.bind, table_name, schema,
|
||||
info_cache=self.info_cache,
|
||||
**kw)
|
||||
return self.dialect.get_table_options(
|
||||
self.bind, table_name, schema,
|
||||
info_cache=self.info_cache, **kw)
|
||||
return {}
|
||||
|
||||
def get_view_names(self, schema=None):
|
||||
"""Return all view names in `schema`.
|
||||
|
||||
:param schema: Optional, retrieve names from a non-default schema.
|
||||
For special quoting, use :class:`.quoted_name`.
|
||||
|
||||
"""
|
||||
|
||||
return self.dialect.get_view_names(self.bind, schema,
|
||||
@@ -191,6 +235,8 @@ class Inspector(object):
|
||||
"""Return definition for `view_name`.
|
||||
|
||||
:param schema: Optional, retrieve names from a non-default schema.
|
||||
For special quoting, use :class:`.quoted_name`.
|
||||
|
||||
"""
|
||||
|
||||
return self.dialect.get_view_definition(
|
||||
@@ -216,6 +262,14 @@ class Inspector(object):
|
||||
|
||||
attrs
|
||||
dict containing optional column attributes
|
||||
|
||||
:param table_name: string name of the table. For special quoting,
|
||||
use :class:`.quoted_name`.
|
||||
|
||||
:param schema: string schema name; if omitted, uses the default schema
|
||||
of the database connection. For special quoting,
|
||||
use :class:`.quoted_name`.
|
||||
|
||||
"""
|
||||
|
||||
col_defs = self.dialect.get_columns(self.bind, table_name, schema,
|
||||
@@ -228,6 +282,8 @@ class Inspector(object):
|
||||
col_def['type'] = coltype()
|
||||
return col_defs
|
||||
|
||||
@deprecated('0.7', 'Call to deprecated method get_primary_keys.'
|
||||
' Use get_pk_constraint instead.')
|
||||
def get_primary_keys(self, table_name, schema=None, **kw):
|
||||
"""Return information about primary keys in `table_name`.
|
||||
|
||||
@@ -235,11 +291,9 @@ class Inspector(object):
|
||||
primary key information as a list of column names.
|
||||
"""
|
||||
|
||||
pkeys = self.dialect.get_primary_keys(self.bind, table_name, schema,
|
||||
info_cache=self.info_cache,
|
||||
**kw)
|
||||
|
||||
return pkeys
|
||||
return self.dialect.get_pk_constraint(self.bind, table_name, schema,
|
||||
info_cache=self.info_cache,
|
||||
**kw)['constrained_columns']
|
||||
|
||||
def get_pk_constraint(self, table_name, schema=None, **kw):
|
||||
"""Return information about primary key constraint on `table_name`.
|
||||
@@ -253,14 +307,18 @@ class Inspector(object):
|
||||
name
|
||||
optional name of the primary key constraint.
|
||||
|
||||
:param table_name: string name of the table. For special quoting,
|
||||
use :class:`.quoted_name`.
|
||||
|
||||
:param schema: string schema name; if omitted, uses the default schema
|
||||
of the database connection. For special quoting,
|
||||
use :class:`.quoted_name`.
|
||||
|
||||
"""
|
||||
pkeys = self.dialect.get_pk_constraint(self.bind, table_name, schema,
|
||||
return self.dialect.get_pk_constraint(self.bind, table_name, schema,
|
||||
info_cache=self.info_cache,
|
||||
**kw)
|
||||
|
||||
return pkeys
|
||||
|
||||
|
||||
def get_foreign_keys(self, table_name, schema=None, **kw):
|
||||
"""Return information about foreign_keys in `table_name`.
|
||||
|
||||
@@ -283,15 +341,18 @@ class Inspector(object):
|
||||
name
|
||||
optional name of the foreign key constraint.
|
||||
|
||||
\**kw
|
||||
other options passed to the dialect's get_foreign_keys() method.
|
||||
:param table_name: string name of the table. For special quoting,
|
||||
use :class:`.quoted_name`.
|
||||
|
||||
:param schema: string schema name; if omitted, uses the default schema
|
||||
of the database connection. For special quoting,
|
||||
use :class:`.quoted_name`.
|
||||
|
||||
"""
|
||||
|
||||
fk_defs = self.dialect.get_foreign_keys(self.bind, table_name, schema,
|
||||
return self.dialect.get_foreign_keys(self.bind, table_name, schema,
|
||||
info_cache=self.info_cache,
|
||||
**kw)
|
||||
return fk_defs
|
||||
|
||||
def get_indexes(self, table_name, schema=None, **kw):
|
||||
"""Return information about indexes in `table_name`.
|
||||
@@ -308,17 +369,48 @@ class Inspector(object):
|
||||
unique
|
||||
boolean
|
||||
|
||||
\**kw
|
||||
other options passed to the dialect's get_indexes() method.
|
||||
:param table_name: string name of the table. For special quoting,
|
||||
use :class:`.quoted_name`.
|
||||
|
||||
:param schema: string schema name; if omitted, uses the default schema
|
||||
of the database connection. For special quoting,
|
||||
use :class:`.quoted_name`.
|
||||
|
||||
"""
|
||||
|
||||
indexes = self.dialect.get_indexes(self.bind, table_name,
|
||||
return self.dialect.get_indexes(self.bind, table_name,
|
||||
schema,
|
||||
info_cache=self.info_cache, **kw)
|
||||
return indexes
|
||||
|
||||
def get_unique_constraints(self, table_name, schema=None, **kw):
|
||||
"""Return information about unique constraints in `table_name`.
|
||||
|
||||
Given a string `table_name` and an optional string `schema`, return
|
||||
unique constraint information as a list of dicts with these keys:
|
||||
|
||||
name
|
||||
the unique constraint's name
|
||||
|
||||
column_names
|
||||
list of column names in order
|
||||
|
||||
:param table_name: string name of the table. For special quoting,
|
||||
use :class:`.quoted_name`.
|
||||
|
||||
:param schema: string schema name; if omitted, uses the default schema
|
||||
of the database connection. For special quoting,
|
||||
use :class:`.quoted_name`.
|
||||
|
||||
.. versionadded:: 0.8.4
|
||||
|
||||
"""
|
||||
|
||||
return self.dialect.get_unique_constraints(
|
||||
self.bind, table_name, schema, info_cache=self.info_cache, **kw)
|
||||
|
||||
def reflecttable(self, table, include_columns, exclude_columns=()):
|
||||
"""Given a Table object, load its internal constructs based on introspection.
|
||||
"""Given a Table object, load its internal constructs based on
|
||||
introspection.
|
||||
|
||||
This is the underlying method used by most dialects to produce
|
||||
table reflection. Direct usage is like::
|
||||
@@ -341,7 +433,8 @@ class Inspector(object):
|
||||
|
||||
# table attributes we might need.
|
||||
reflection_options = dict(
|
||||
(k, table.kwargs.get(k)) for k in dialect.reflection_options if k in table.kwargs)
|
||||
(k, table.kwargs.get(k))
|
||||
for k in dialect.reflection_options if k in table.kwargs)
|
||||
|
||||
schema = table.schema
|
||||
table_name = table.name
|
||||
@@ -354,22 +447,25 @@ class Inspector(object):
|
||||
# table.kwargs will need to be passed to each reflection method. Make
|
||||
# sure keywords are strings.
|
||||
tblkw = table.kwargs.copy()
|
||||
for (k, v) in tblkw.items():
|
||||
for (k, v) in list(tblkw.items()):
|
||||
del tblkw[k]
|
||||
tblkw[str(k)] = v
|
||||
|
||||
# Py2K
|
||||
if isinstance(schema, str):
|
||||
schema = schema.decode(dialect.encoding)
|
||||
if isinstance(table_name, str):
|
||||
table_name = table_name.decode(dialect.encoding)
|
||||
# end Py2K
|
||||
if util.py2k:
|
||||
if isinstance(schema, str):
|
||||
schema = schema.decode(dialect.encoding)
|
||||
if isinstance(table_name, str):
|
||||
table_name = table_name.decode(dialect.encoding)
|
||||
|
||||
# columns
|
||||
found_table = False
|
||||
cols_by_orig_name = {}
|
||||
|
||||
for col_d in self.get_columns(table_name, schema, **tblkw):
|
||||
found_table = True
|
||||
table.dispatch.column_reflect(table, col_d)
|
||||
orig_name = col_d['name']
|
||||
|
||||
table.dispatch.column_reflect(self, table, col_d)
|
||||
|
||||
name = col_d['name']
|
||||
if include_columns and name not in include_columns:
|
||||
@@ -387,8 +483,9 @@ class Inspector(object):
|
||||
|
||||
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.
|
||||
# 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']), _reflected=True
|
||||
@@ -396,7 +493,7 @@ class Inspector(object):
|
||||
)
|
||||
|
||||
if 'sequence' in col_d:
|
||||
# TODO: mssql, maxdb and sybase are using this.
|
||||
# TODO: mssql and sybase are using this.
|
||||
seq = col_d['sequence']
|
||||
sequence = sa_schema.Sequence(seq['name'], 1, 1)
|
||||
if 'start' in seq:
|
||||
@@ -405,7 +502,9 @@ class Inspector(object):
|
||||
sequence.increment = seq['increment']
|
||||
colargs.append(sequence)
|
||||
|
||||
col = sa_schema.Column(name, coltype, *colargs, **col_kw)
|
||||
cols_by_orig_name[orig_name] = col = \
|
||||
sa_schema.Column(name, coltype, *colargs, **col_kw)
|
||||
|
||||
table.append_column(col)
|
||||
|
||||
if not found_table:
|
||||
@@ -414,11 +513,18 @@ class Inspector(object):
|
||||
# Primary keys
|
||||
pk_cons = self.get_pk_constraint(table_name, schema, **tblkw)
|
||||
if pk_cons:
|
||||
pk_cols = [table.c[pk]
|
||||
for pk in pk_cons['constrained_columns']
|
||||
if pk in table.c and pk not in exclude_columns
|
||||
] + [pk for pk in table.primary_key if pk.key in exclude_columns]
|
||||
primary_key_constraint = sa_schema.PrimaryKeyConstraint(name=pk_cons.get('name'),
|
||||
pk_cols = [
|
||||
cols_by_orig_name[pk]
|
||||
for pk in pk_cons['constrained_columns']
|
||||
if pk in cols_by_orig_name and pk not in exclude_columns
|
||||
]
|
||||
pk_cols += [
|
||||
pk
|
||||
for pk in table.primary_key
|
||||
if pk.key in exclude_columns
|
||||
]
|
||||
primary_key_constraint = sa_schema.PrimaryKeyConstraint(
|
||||
name=pk_cons.get('name'),
|
||||
*pk_cols
|
||||
)
|
||||
|
||||
@@ -428,7 +534,16 @@ class Inspector(object):
|
||||
fkeys = self.get_foreign_keys(table_name, schema, **tblkw)
|
||||
for fkey_d in fkeys:
|
||||
conname = fkey_d['name']
|
||||
constrained_columns = fkey_d['constrained_columns']
|
||||
# look for columns by orig name in cols_by_orig_name,
|
||||
# but support columns that are in-Python only as fallback
|
||||
constrained_columns = [
|
||||
cols_by_orig_name[c].key
|
||||
if c in cols_by_orig_name else c
|
||||
for c in fkey_d['constrained_columns']
|
||||
]
|
||||
if exclude_columns and set(constrained_columns).intersection(
|
||||
exclude_columns):
|
||||
continue
|
||||
referred_schema = fkey_d['referred_schema']
|
||||
referred_table = fkey_d['referred_table']
|
||||
referred_columns = fkey_d['referred_columns']
|
||||
@@ -449,9 +564,14 @@ class Inspector(object):
|
||||
)
|
||||
for column in referred_columns:
|
||||
refspec.append(".".join([referred_table, column]))
|
||||
if 'options' in fkey_d:
|
||||
options = fkey_d['options']
|
||||
else:
|
||||
options = {}
|
||||
table.append_constraint(
|
||||
sa_schema.ForeignKeyConstraint(constrained_columns, refspec,
|
||||
conname, link_to_name=True))
|
||||
conname, link_to_name=True,
|
||||
**options))
|
||||
# Indexes
|
||||
indexes = self.get_indexes(table_name, schema)
|
||||
for index_d in indexes:
|
||||
@@ -465,5 +585,11 @@ class Inspector(object):
|
||||
"Omitting %s KEY for (%s), key covers omitted columns." %
|
||||
(flavor, ', '.join(columns)))
|
||||
continue
|
||||
sa_schema.Index(name, *[table.columns[c] for c in columns],
|
||||
# look for columns by orig name in cols_by_orig_name,
|
||||
# but support columns that are in-Python only as fallback
|
||||
sa_schema.Index(name, *[
|
||||
cols_by_orig_name[c] if c in cols_by_orig_name
|
||||
else table.c[c]
|
||||
for c in columns
|
||||
],
|
||||
**dict(unique=unique))
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,5 +1,5 @@
|
||||
# engine/strategies.py
|
||||
# Copyright (C) 2005-2013 the SQLAlchemy authors and contributors <see AUTHORS file>
|
||||
# Copyright (C) 2005-2014 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
|
||||
@@ -49,18 +49,27 @@ class DefaultEngineStrategy(EngineStrategy):
|
||||
|
||||
dialect_cls = u.get_dialect()
|
||||
|
||||
if kwargs.pop('_coerce_config', False):
|
||||
def pop_kwarg(key, default=None):
|
||||
value = kwargs.pop(key, default)
|
||||
if key in dialect_cls.engine_config_types:
|
||||
value = dialect_cls.engine_config_types[key](value)
|
||||
return value
|
||||
else:
|
||||
pop_kwarg = kwargs.pop
|
||||
|
||||
dialect_args = {}
|
||||
# consume dialect arguments from kwargs
|
||||
for k in util.get_cls_kwargs(dialect_cls):
|
||||
if k in kwargs:
|
||||
dialect_args[k] = kwargs.pop(k)
|
||||
dialect_args[k] = pop_kwarg(k)
|
||||
|
||||
dbapi = kwargs.pop('module', None)
|
||||
if dbapi is None:
|
||||
dbapi_args = {}
|
||||
for k in util.get_func_kwargs(dialect_cls.dbapi):
|
||||
if k in kwargs:
|
||||
dbapi_args[k] = kwargs.pop(k)
|
||||
dbapi_args[k] = pop_kwarg(k)
|
||||
dbapi = dialect_cls.dbapi(**dbapi_args)
|
||||
|
||||
dialect_args['dbapi'] = dbapi
|
||||
@@ -70,33 +79,26 @@ class DefaultEngineStrategy(EngineStrategy):
|
||||
|
||||
# assemble connection arguments
|
||||
(cargs, cparams) = dialect.create_connect_args(u)
|
||||
cparams.update(kwargs.pop('connect_args', {}))
|
||||
cparams.update(pop_kwarg('connect_args', {}))
|
||||
|
||||
# look for existing pool or create
|
||||
pool = kwargs.pop('pool', None)
|
||||
pool = pop_kwarg('pool', None)
|
||||
if pool is None:
|
||||
def connect():
|
||||
try:
|
||||
return dialect.connect(*cargs, **cparams)
|
||||
except Exception, e:
|
||||
# Py3K
|
||||
#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, dialect.dbapi.Error,
|
||||
connection_invalidated=
|
||||
dialect.is_disconnect(e, None, None)), \
|
||||
None, sys.exc_info()[2]
|
||||
# end Py2K
|
||||
except dialect.dbapi.Error as e:
|
||||
invalidated = dialect.is_disconnect(e, None, None)
|
||||
util.raise_from_cause(
|
||||
exc.DBAPIError.instance(None, None,
|
||||
e, dialect.dbapi.Error,
|
||||
connection_invalidated=invalidated
|
||||
)
|
||||
)
|
||||
|
||||
creator = kwargs.pop('creator', connect)
|
||||
creator = pop_kwarg('creator', connect)
|
||||
|
||||
poolclass = kwargs.pop('poolclass', None)
|
||||
poolclass = pop_kwarg('poolclass', None)
|
||||
if poolclass is None:
|
||||
poolclass = dialect_cls.get_pool_class(u)
|
||||
pool_args = {}
|
||||
@@ -107,13 +109,13 @@ class DefaultEngineStrategy(EngineStrategy):
|
||||
'echo': 'echo_pool',
|
||||
'timeout': 'pool_timeout',
|
||||
'recycle': 'pool_recycle',
|
||||
'events':'pool_events',
|
||||
'use_threadlocal':'pool_threadlocal',
|
||||
'reset_on_return':'pool_reset_on_return'}
|
||||
'events': 'pool_events',
|
||||
'use_threadlocal': 'pool_threadlocal',
|
||||
'reset_on_return': 'pool_reset_on_return'}
|
||||
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[k] = pop_kwarg(tk)
|
||||
pool = poolclass(creator, **pool_args)
|
||||
else:
|
||||
if isinstance(pool, poollib._DBProxy):
|
||||
@@ -126,7 +128,7 @@ class DefaultEngineStrategy(EngineStrategy):
|
||||
engine_args = {}
|
||||
for k in util.get_cls_kwargs(engineclass):
|
||||
if k in kwargs:
|
||||
engine_args[k] = kwargs.pop(k)
|
||||
engine_args[k] = pop_kwarg(k)
|
||||
|
||||
_initialize = kwargs.pop('_initialize', True)
|
||||
|
||||
@@ -147,7 +149,8 @@ class DefaultEngineStrategy(EngineStrategy):
|
||||
do_on_connect = dialect.on_connect()
|
||||
if do_on_connect:
|
||||
def on_connect(dbapi_connection, connection_record):
|
||||
conn = getattr(dbapi_connection, '_sqla_unwrap', dbapi_connection)
|
||||
conn = getattr(
|
||||
dbapi_connection, '_sqla_unwrap', dbapi_connection)
|
||||
if conn is None:
|
||||
return
|
||||
do_on_connect(conn)
|
||||
@@ -155,14 +158,10 @@ class DefaultEngineStrategy(EngineStrategy):
|
||||
event.listen(pool, 'first_connect', on_connect)
|
||||
event.listen(pool, 'connect', on_connect)
|
||||
|
||||
@util.only_once
|
||||
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
|
||||
c = base.Connection(engine, connection=dbapi_connection,
|
||||
_has_events=False)
|
||||
|
||||
dialect.initialize(c)
|
||||
event.listen(pool, 'first_connect', first_connect)
|
||||
@@ -238,12 +237,14 @@ class MockEngineStrategy(EngineStrategy):
|
||||
kwargs['checkfirst'] = False
|
||||
from sqlalchemy.engine import ddl
|
||||
|
||||
ddl.SchemaGenerator(self.dialect, self, **kwargs).traverse_single(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_single(entity)
|
||||
ddl.SchemaDropper(
|
||||
self.dialect, self, **kwargs).traverse_single(entity)
|
||||
|
||||
def _run_visitor(self, visitorcallable, element,
|
||||
connection=None,
|
||||
|
||||
@@ -1,21 +1,24 @@
|
||||
# engine/threadlocal.py
|
||||
# Copyright (C) 2005-2013 the SQLAlchemy authors and contributors <see AUTHORS file>
|
||||
# Copyright (C) 2005-2014 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
|
||||
|
||||
"""Provides a thread-local transactional wrapper around the root Engine class.
|
||||
|
||||
The ``threadlocal`` module is invoked when using the ``strategy="threadlocal"`` flag
|
||||
with :func:`~sqlalchemy.engine.create_engine`. This module is semi-private and is
|
||||
invoked automatically when the threadlocal engine strategy is used.
|
||||
The ``threadlocal`` module is invoked when using the
|
||||
``strategy="threadlocal"`` flag with :func:`~sqlalchemy.engine.create_engine`.
|
||||
This module is semi-private and is invoked automatically when the threadlocal
|
||||
engine strategy is used.
|
||||
"""
|
||||
|
||||
from sqlalchemy import util, event
|
||||
from sqlalchemy.engine import base
|
||||
from .. import util
|
||||
from . import base
|
||||
import weakref
|
||||
|
||||
|
||||
class TLConnection(base.Connection):
|
||||
|
||||
def __init__(self, *arg, **kw):
|
||||
super(TLConnection, self).__init__(*arg, **kw)
|
||||
self.__opencount = 0
|
||||
@@ -33,16 +36,18 @@ class TLConnection(base.Connection):
|
||||
self.__opencount = 0
|
||||
base.Connection.close(self)
|
||||
|
||||
class TLEngine(base.Engine):
|
||||
"""An Engine that includes support for thread-local managed transactions."""
|
||||
|
||||
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()
|
||||
|
||||
|
||||
def contextual_connect(self, **kw):
|
||||
if not hasattr(self._connections, 'conn'):
|
||||
connection = None
|
||||
@@ -52,21 +57,24 @@ 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._tl_connection_cls(self, self.pool.connect(), **kw)
|
||||
self._connections.conn = conn = weakref.ref(connection)
|
||||
connection = self._tl_connection_cls(
|
||||
self, self.pool.connect(), **kw)
|
||||
self._connections.conn = weakref.ref(connection)
|
||||
|
||||
return connection._increment_connect()
|
||||
|
||||
def begin_twophase(self, xid=None):
|
||||
if not hasattr(self._connections, 'trans'):
|
||||
self._connections.trans = []
|
||||
self._connections.trans.append(self.contextual_connect().begin_twophase(xid=xid))
|
||||
self._connections.trans.append(
|
||||
self.contextual_connect().begin_twophase(xid=xid))
|
||||
return self
|
||||
|
||||
def begin_nested(self):
|
||||
if not hasattr(self._connections, 'trans'):
|
||||
self._connections.trans = []
|
||||
self._connections.trans.append(self.contextual_connect().begin_nested())
|
||||
self._connections.trans.append(
|
||||
self.contextual_connect().begin_nested())
|
||||
return self
|
||||
|
||||
def begin(self):
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
# engine/url.py
|
||||
# Copyright (C) 2005-2013 the SQLAlchemy authors and contributors <see AUTHORS file>
|
||||
# Copyright (C) 2005-2014 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
|
||||
@@ -7,13 +7,16 @@
|
||||
"""Provides the :class:`~sqlalchemy.engine.url.URL` class which encapsulates
|
||||
information about a database connection specification.
|
||||
|
||||
The URL object is created automatically when :func:`~sqlalchemy.engine.create_engine` is called
|
||||
with a string argument; alternatively, the URL is a public-facing construct which can
|
||||
The URL object is created automatically when
|
||||
:func:`~sqlalchemy.engine.create_engine` is called with a string
|
||||
argument; alternatively, the URL is a public-facing construct which can
|
||||
be used directly and is also accepted directly by ``create_engine()``.
|
||||
"""
|
||||
|
||||
import re, urllib
|
||||
from sqlalchemy import exc, util
|
||||
import re
|
||||
from .. import exc, util
|
||||
from . import Dialect
|
||||
from ..dialects import registry
|
||||
|
||||
|
||||
class URL(object):
|
||||
@@ -21,8 +24,8 @@ class URL(object):
|
||||
Represent the components of a URL used to connect to a database.
|
||||
|
||||
This object is suitable to be passed directly to a
|
||||
``create_engine()`` call. The fields of the URL are parsed from a
|
||||
string by the ``module-level make_url()`` function. the string
|
||||
:func:`~sqlalchemy.create_engine` call. The fields of the URL are parsed from a
|
||||
string by the :func:`.make_url` function. the string
|
||||
format of the URL is an RFC-1738-style string.
|
||||
|
||||
All initialization parameters are available as public attributes.
|
||||
@@ -59,25 +62,35 @@ class URL(object):
|
||||
self.database = database
|
||||
self.query = query or {}
|
||||
|
||||
def __str__(self):
|
||||
def __to_string__(self, hide_password=True):
|
||||
s = self.drivername + "://"
|
||||
if self.username is not None:
|
||||
s += self.username
|
||||
s += _rfc_1738_quote(self.username)
|
||||
if self.password is not None:
|
||||
s += ':' + urllib.quote_plus(self.password)
|
||||
s += ':' + ('***' if hide_password
|
||||
else _rfc_1738_quote(self.password))
|
||||
s += "@"
|
||||
if self.host is not None:
|
||||
s += self.host
|
||||
if ':' in self.host:
|
||||
s += "[%s]" % self.host
|
||||
else:
|
||||
s += self.host
|
||||
if self.port is not None:
|
||||
s += ':' + str(self.port)
|
||||
if self.database is not None:
|
||||
s += '/' + self.database
|
||||
if self.query:
|
||||
keys = self.query.keys()
|
||||
keys = list(self.query)
|
||||
keys.sort()
|
||||
s += '?' + "&".join("%s=%s" % (k, self.query[k]) for k in keys)
|
||||
return s
|
||||
|
||||
def __str__(self):
|
||||
return self.__to_string__(hide_password=False)
|
||||
|
||||
def __repr__(self):
|
||||
return self.__to_string__()
|
||||
|
||||
def __hash__(self):
|
||||
return hash(str(self))
|
||||
|
||||
@@ -96,49 +109,20 @@ class URL(object):
|
||||
to this URL's driver name.
|
||||
"""
|
||||
|
||||
try:
|
||||
if '+' in self.drivername:
|
||||
dialect, driver = self.drivername.split('+')
|
||||
else:
|
||||
dialect, driver = self.drivername, 'base'
|
||||
|
||||
module = __import__('sqlalchemy.dialects.%s' % (dialect, )).dialects
|
||||
module = getattr(module, dialect)
|
||||
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:
|
||||
module = self._load_entry_point()
|
||||
if module is not None:
|
||||
return module
|
||||
else:
|
||||
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
|
||||
if pkg_resources is not installed or there is no matching entry point.
|
||||
|
||||
Raise ImportError if the actual load fails.
|
||||
|
||||
"""
|
||||
try:
|
||||
import pkg_resources
|
||||
except ImportError:
|
||||
return None
|
||||
|
||||
for res in pkg_resources.iter_entry_points('sqlalchemy.dialects'):
|
||||
if res.name == self.drivername.replace("+", "."):
|
||||
return res.load()
|
||||
if '+' not in self.drivername:
|
||||
name = self.drivername
|
||||
else:
|
||||
return None
|
||||
name = self.drivername.replace('+', '.')
|
||||
cls = registry.load(name)
|
||||
# check for legacy dialects that
|
||||
# would return a module with 'dialect' as the
|
||||
# actual class
|
||||
if hasattr(cls, 'dialect') and \
|
||||
isinstance(cls.dialect, type) and \
|
||||
issubclass(cls.dialect, Dialect):
|
||||
return cls.dialect
|
||||
else:
|
||||
return cls
|
||||
|
||||
def translate_connect_args(self, names=[], **kw):
|
||||
"""Translate url attributes into a dictionary of connection arguments.
|
||||
@@ -150,8 +134,8 @@ class URL(object):
|
||||
|
||||
:param \**kw: Optional, alternate key names for url attributes.
|
||||
|
||||
:param names: Deprecated. Same purpose as the keyword-based alternate names,
|
||||
but correlates the name to the original positionally.
|
||||
:param names: Deprecated. Same purpose as the keyword-based alternate
|
||||
names, but correlates the name to the original positionally.
|
||||
"""
|
||||
|
||||
translated = {}
|
||||
@@ -167,6 +151,7 @@ class URL(object):
|
||||
translated[name] = getattr(self, sname)
|
||||
return translated
|
||||
|
||||
|
||||
def make_url(name_or_url):
|
||||
"""Given a string or unicode instance, produce a new URL instance.
|
||||
|
||||
@@ -174,25 +159,28 @@ def make_url(name_or_url):
|
||||
existing URL object is passed, just returns the object.
|
||||
"""
|
||||
|
||||
if isinstance(name_or_url, basestring):
|
||||
if isinstance(name_or_url, util.string_types):
|
||||
return _parse_rfc1738_args(name_or_url)
|
||||
else:
|
||||
return name_or_url
|
||||
|
||||
|
||||
def _parse_rfc1738_args(name):
|
||||
pattern = re.compile(r'''
|
||||
(?P<name>[\w\+]+)://
|
||||
(?:
|
||||
(?P<username>[^:/]*)
|
||||
(?::(?P<password>[^/]*))?
|
||||
(?::(?P<password>.*))?
|
||||
@)?
|
||||
(?:
|
||||
(?P<host>[^/:]*)
|
||||
(?:
|
||||
\[(?P<ipv6host>[^/]+)\] |
|
||||
(?P<ipv4host>[^/:]+)
|
||||
)?
|
||||
(?::(?P<port>[^/]*))?
|
||||
)?
|
||||
(?:/(?P<database>.*))?
|
||||
'''
|
||||
, re.X)
|
||||
''', re.X)
|
||||
|
||||
m = pattern.match(name)
|
||||
if m is not None:
|
||||
@@ -201,28 +189,39 @@ def _parse_rfc1738_args(name):
|
||||
tokens = components['database'].split('?', 2)
|
||||
components['database'] = tokens[0]
|
||||
query = (len(tokens) > 1 and dict(util.parse_qsl(tokens[1]))) or None
|
||||
# Py2K
|
||||
if query is not None:
|
||||
if util.py2k and query is not None:
|
||||
query = dict((k.encode('ascii'), query[k]) for k in query)
|
||||
# end Py2K
|
||||
else:
|
||||
query = None
|
||||
components['query'] = query
|
||||
|
||||
if components['password'] is not None:
|
||||
components['password'] = urllib.unquote_plus(components['password'])
|
||||
if components['username'] is not None:
|
||||
components['username'] = _rfc_1738_unquote(components['username'])
|
||||
|
||||
if components['password'] is not None:
|
||||
components['password'] = _rfc_1738_unquote(components['password'])
|
||||
|
||||
ipv4host = components.pop('ipv4host')
|
||||
ipv6host = components.pop('ipv6host')
|
||||
components['host'] = ipv4host or ipv6host
|
||||
name = components.pop('name')
|
||||
return URL(name, **components)
|
||||
else:
|
||||
raise exc.ArgumentError(
|
||||
"Could not parse rfc1738 URL from string '%s'" % name)
|
||||
|
||||
|
||||
def _rfc_1738_quote(text):
|
||||
return re.sub(r'[:@/]', lambda m: "%%%X" % ord(m.group(0)), text)
|
||||
|
||||
def _rfc_1738_unquote(text):
|
||||
return util.unquote(text)
|
||||
|
||||
def _parse_keyvalue_args(name):
|
||||
m = re.match( r'(\w+)://(.*)', name)
|
||||
m = re.match(r'(\w+)://(.*)', name)
|
||||
if m is not None:
|
||||
(name, args) = m.group(1, 2)
|
||||
opts = dict( util.parse_qsl( args ) )
|
||||
opts = dict(util.parse_qsl(args))
|
||||
return URL(name, *opts)
|
||||
else:
|
||||
return None
|
||||
|
||||
@@ -0,0 +1,72 @@
|
||||
# engine/util.py
|
||||
# Copyright (C) 2005-2014 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
|
||||
|
||||
from .. import util
|
||||
|
||||
def connection_memoize(key):
|
||||
"""Decorator, memoize a function in a connection.info stash.
|
||||
|
||||
Only applicable to functions which take no arguments other than a
|
||||
connection. The memo will be stored in ``connection.info[key]``.
|
||||
"""
|
||||
|
||||
@util.decorator
|
||||
def decorated(fn, self, connection):
|
||||
connection = connection.connect()
|
||||
try:
|
||||
return connection.info[key]
|
||||
except KeyError:
|
||||
connection.info[key] = val = fn(self, connection)
|
||||
return val
|
||||
|
||||
return decorated
|
||||
|
||||
|
||||
def py_fallback():
|
||||
def _distill_params(multiparams, params):
|
||||
"""Given arguments from the calling form *multiparams, **params,
|
||||
return a list of bind parameter structures, usually a list of
|
||||
dictionaries.
|
||||
|
||||
In the case of 'raw' execution which accepts positional parameters,
|
||||
it may be a list of tuples or lists.
|
||||
|
||||
"""
|
||||
|
||||
if not multiparams:
|
||||
if params:
|
||||
return [params]
|
||||
else:
|
||||
return []
|
||||
elif len(multiparams) == 1:
|
||||
zero = multiparams[0]
|
||||
if isinstance(zero, (list, tuple)):
|
||||
if not zero or hasattr(zero[0], '__iter__') and \
|
||||
not hasattr(zero[0], 'strip'):
|
||||
# execute(stmt, [{}, {}, {}, ...])
|
||||
# execute(stmt, [(), (), (), ...])
|
||||
return zero
|
||||
else:
|
||||
# execute(stmt, ("value", "value"))
|
||||
return [zero]
|
||||
elif hasattr(zero, 'keys'):
|
||||
# execute(stmt, {"key":"value"})
|
||||
return [zero]
|
||||
else:
|
||||
# execute(stmt, "value")
|
||||
return [[zero]]
|
||||
else:
|
||||
if hasattr(multiparams[0], '__iter__') and \
|
||||
not hasattr(multiparams[0], 'strip'):
|
||||
return multiparams
|
||||
else:
|
||||
return [multiparams]
|
||||
|
||||
return locals()
|
||||
try:
|
||||
from sqlalchemy.cutils import _distill_params
|
||||
except ImportError:
|
||||
globals().update(py_fallback())
|
||||
Reference in New Issue
Block a user