Remove Elixir library
Update SQLAlchemy
This commit is contained in:
@@ -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))
|
||||
|
||||
Reference in New Issue
Block a user