summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--doc/build/changelog/changelog_09.rst24
-rw-r--r--lib/sqlalchemy/__init__.py7
-rw-r--r--lib/sqlalchemy/dialects/drizzle/base.py2
-rw-r--r--lib/sqlalchemy/dialects/mysql/base.py4
-rw-r--r--lib/sqlalchemy/engine/__init__.py13
-rw-r--r--lib/sqlalchemy/engine/base.py6
-rw-r--r--lib/sqlalchemy/engine/ddl.py193
-rw-r--r--lib/sqlalchemy/engine/default.py3
-rw-r--r--lib/sqlalchemy/engine/interfaces.py106
-rw-r--r--lib/sqlalchemy/engine/reflection.py4
-rw-r--r--lib/sqlalchemy/engine/result.py8
-rw-r--r--lib/sqlalchemy/events.py8
-rw-r--r--lib/sqlalchemy/inspection.py5
-rw-r--r--lib/sqlalchemy/log.py8
-rw-r--r--lib/sqlalchemy/orm/__init__.py3
-rw-r--r--lib/sqlalchemy/orm/attributes.py3
-rw-r--r--lib/sqlalchemy/orm/dynamic.py5
-rw-r--r--lib/sqlalchemy/orm/mapper.py12
-rw-r--r--lib/sqlalchemy/orm/properties.py6
-rw-r--r--lib/sqlalchemy/orm/query.py7
-rw-r--r--lib/sqlalchemy/orm/strategies.py21
-rw-r--r--lib/sqlalchemy/schema.py3692
-rw-r--r--lib/sqlalchemy/sql/__init__.py12
-rw-r--r--lib/sqlalchemy/sql/annotation.py182
-rw-r--r--lib/sqlalchemy/sql/base.py329
-rw-r--r--lib/sqlalchemy/sql/compiler.py216
-rw-r--r--lib/sqlalchemy/sql/ddl.py826
-rw-r--r--lib/sqlalchemy/sql/default_comparator.py281
-rw-r--r--lib/sqlalchemy/sql/dml.py668
-rw-r--r--lib/sqlalchemy/sql/elements.py2369
-rw-r--r--lib/sqlalchemy/sql/expression.py6685
-rw-r--r--lib/sqlalchemy/sql/functions.py292
-rw-r--r--lib/sqlalchemy/sql/schema.py3025
-rw-r--r--lib/sqlalchemy/sql/selectable.py2775
-rw-r--r--lib/sqlalchemy/sql/sqltypes.py1559
-rw-r--r--lib/sqlalchemy/sql/type_api.py968
-rw-r--r--lib/sqlalchemy/sql/util.py393
-rw-r--r--lib/sqlalchemy/testing/plugin/noseplugin.py2
-rw-r--r--lib/sqlalchemy/types.py2539
-rw-r--r--lib/sqlalchemy/util/__init__.py2
-rw-r--r--lib/sqlalchemy/util/langhelpers.py102
-rw-r--r--test/engine/test_bind.py42
-rw-r--r--test/orm/test_relationships.py8
-rw-r--r--test/sql/test_ddlemit.py (renamed from test/engine/test_ddlemit.py)2
44 files changed, 13866 insertions, 13551 deletions
diff --git a/doc/build/changelog/changelog_09.rst b/doc/build/changelog/changelog_09.rst
index d9788aed2..ea04ee024 100644
--- a/doc/build/changelog/changelog_09.rst
+++ b/doc/build/changelog/changelog_09.rst
@@ -14,6 +14,30 @@
when used against an empty collection. Also in 0.8.3.
.. change::
+ :tags: general, sql
+
+ A large refactoring of the ``sqlalchemy.sql`` package has reorganized
+ the import structure of many core modules.
+ ``sqlalchemy.schema`` and ``sqlalchemy.types``
+ remain in the top-level package, but are now just lists of names
+ that pull from within ``sqlalchemy.sql``. Their implementations
+ are now broken out among ``sqlalchemy.sql.type_api``, ``sqlalchemy.sql.sqltypes``,
+ ``sqlalchemy.sql.schema`` and ``sqlalchemy.sql.ddl``, the last of which was
+ moved from ``sqlalchemy.engine``. ``sqlalchemy.sql.expression`` is also
+ a namespace now which pulls implementations mostly from ``sqlalchemy.sql.elements``,
+ ``sqlalchemy.sql.selectable``, and ``sqlalchemy.sql.dml``.
+ Most of the "factory" functions
+ used to create SQL expression objects have been moved to classmethods
+ or constructors, which are exposed in ``sqlalchemy.sql.expression``
+ using a programmatic system. Care has been taken such that all the
+ original import namespaces remain intact and there should be no impact
+ on any existing applications. The rationale here was to break out these
+ very large modules into smaller ones, provide more manageable lists
+ of function names, to greatly reduce "import cycles" and clarify the
+ up-front importing of names, and to remove the need for redundant
+ functions and documentation throughout the expression package.
+
+ .. change::
:tags: orm, feature, orm
Added a new attribute :attr:`.Session.info` to :class:`.Session`;
diff --git a/lib/sqlalchemy/__init__.py b/lib/sqlalchemy/__init__.py
index 06c597877..d21d0fbb9 100644
--- a/lib/sqlalchemy/__init__.py
+++ b/lib/sqlalchemy/__init__.py
@@ -97,7 +97,6 @@ from .schema import (
Column,
ColumnDefault,
Constraint,
- DDL,
DefaultClause,
FetchedValue,
ForeignKey,
@@ -110,7 +109,9 @@ from .schema import (
Table,
ThreadLocalMetaData,
UniqueConstraint,
- )
+ DDL,
+)
+
from .inspection import inspect
@@ -125,4 +126,4 @@ __version__ = '0.9.0'
del _inspect, sys
from . import util as _sa_util
-_sa_util.importlater.resolve_all() \ No newline at end of file
+_sa_util.importlater.resolve_all("sqlalchemy") \ No newline at end of file
diff --git a/lib/sqlalchemy/dialects/drizzle/base.py b/lib/sqlalchemy/dialects/drizzle/base.py
index efad13549..2f5ddd79e 100644
--- a/lib/sqlalchemy/dialects/drizzle/base.py
+++ b/lib/sqlalchemy/dialects/drizzle/base.py
@@ -417,6 +417,7 @@ class DrizzleIdentifierPreparer(mysql_dialect.MySQLIdentifierPreparer):
pass
+@log.class_logger
class DrizzleDialect(mysql_dialect.MySQLDialect):
"""Details of the Drizzle dialect.
@@ -495,4 +496,3 @@ class DrizzleDialect(mysql_dialect.MySQLDialect):
self._backslash_escapes = False
-log.class_logger(DrizzleDialect)
diff --git a/lib/sqlalchemy/dialects/mysql/base.py b/lib/sqlalchemy/dialects/mysql/base.py
index 1da0497f5..095cc6f19 100644
--- a/lib/sqlalchemy/dialects/mysql/base.py
+++ b/lib/sqlalchemy/dialects/mysql/base.py
@@ -1881,6 +1881,7 @@ class MySQLIdentifierPreparer(compiler.IdentifierPreparer):
return tuple([self.quote_identifier(i) for i in ids if i is not None])
+@log.class_logger
class MySQLDialect(default.DefaultDialect):
"""Details of the MySQL dialect. Not used directly in application code."""
@@ -2404,6 +2405,7 @@ class ReflectedState(object):
self.constraints = []
+@log.class_logger
class MySQLTableDefinitionParser(object):
"""Parses the results of a SHOW CREATE TABLE statement."""
@@ -2806,8 +2808,6 @@ class MySQLTableDefinitionParser(object):
_options_of_type_string = ('COMMENT', 'DATA DIRECTORY', 'INDEX DIRECTORY',
'PASSWORD', 'CONNECTION')
-log.class_logger(MySQLTableDefinitionParser)
-log.class_logger(MySQLDialect)
class _DecodingRowProxy(object):
diff --git a/lib/sqlalchemy/engine/__init__.py b/lib/sqlalchemy/engine/__init__.py
index a00976d4c..16d214140 100644
--- a/lib/sqlalchemy/engine/__init__.py
+++ b/lib/sqlalchemy/engine/__init__.py
@@ -50,14 +50,13 @@ url.py
within a URL.
"""
-# not sure what this was used for
-#import sqlalchemy.databases
-
from .interfaces import (
- Compiled,
Connectable,
Dialect,
ExecutionContext,
+
+ # backwards compat
+ Compiled,
TypeCompiler
)
@@ -83,7 +82,11 @@ from .util import (
connection_memoize
)
-from . import util, strategies, ddl
+
+from . import util, strategies
+
+# backwards compat
+from ..sql import ddl
default_strategy = 'plain'
diff --git a/lib/sqlalchemy/engine/base.py b/lib/sqlalchemy/engine/base.py
index f69bd3d4b..83fa34f2c 100644
--- a/lib/sqlalchemy/engine/base.py
+++ b/lib/sqlalchemy/engine/base.py
@@ -11,8 +11,8 @@ from __future__ import with_statement
import sys
-from .. import exc, schema, util, log, interfaces
-from ..sql import expression, util as sql_util
+from .. import exc, util, log, interfaces
+from ..sql import expression, util as sql_util, schema, ddl
from .interfaces import Connectable, Compiled
from .util import _distill_params
import contextlib
@@ -1039,7 +1039,7 @@ class Connection(Connectable):
expression.ClauseElement: _execute_clauseelement,
Compiled: _execute_compiled,
schema.SchemaItem: _execute_default,
- schema.DDLElement: _execute_ddl,
+ ddl.DDLElement: _execute_ddl,
util.string_types[0]: _execute_text
}
diff --git a/lib/sqlalchemy/engine/ddl.py b/lib/sqlalchemy/engine/ddl.py
deleted file mode 100644
index 6daa9be6b..000000000
--- a/lib/sqlalchemy/engine/ddl.py
+++ /dev/null
@@ -1,193 +0,0 @@
-# engine/ddl.py
-# Copyright (C) 2009-2013 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 .. import schema
-from ..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
- 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 is not None:
- tables = self.tables
- else:
- tables = list(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 is not None:
- tables = self.tables
- else:
- tables = list(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))
diff --git a/lib/sqlalchemy/engine/default.py b/lib/sqlalchemy/engine/default.py
index 7b5a22251..017dfa902 100644
--- a/lib/sqlalchemy/engine/default.py
+++ b/lib/sqlalchemy/engine/default.py
@@ -16,7 +16,8 @@ import re
import random
from . import reflection, interfaces, result
from ..sql import compiler, expression
-from .. import exc, types as sqltypes, util, pool, processors
+from .. import types as sqltypes
+from .. import exc, util, pool, processors
import codecs
import weakref
from .. import event
diff --git a/lib/sqlalchemy/engine/interfaces.py b/lib/sqlalchemy/engine/interfaces.py
index 750aa2fcd..8f4af6db5 100644
--- a/lib/sqlalchemy/engine/interfaces.py
+++ b/lib/sqlalchemy/engine/interfaces.py
@@ -8,6 +8,8 @@
from .. import util, event, events
+# backwards compat
+from ..sql.compiler import Compiled, TypeCompiler
class Dialect(object):
"""Define the behavior of a specific database and DB-API combination.
@@ -769,110 +771,6 @@ class ExecutionContext(object):
raise NotImplementedError()
-class Compiled(object):
- """Represent a compiled SQL or DDL expression.
-
- The ``__str__`` method of the ``Compiled`` object should produce
- the actual text of the statement. ``Compiled`` objects are
- specific to their underlying database dialect, and also may
- or may not be specific to the columns referenced within a
- particular set of bind parameters. In no case should the
- ``Compiled`` object be dependent on the actual values of those
- bind parameters, even though it may reference those values as
- defaults.
- """
-
- def __init__(self, dialect, statement, bind=None,
- compile_kwargs=util.immutabledict()):
- """Construct a new ``Compiled`` object.
-
- :param dialect: ``Dialect`` to compile against.
-
- :param statement: ``ClauseElement`` to be compiled.
-
- :param bind: Optional Engine or Connection to compile this
- statement against.
-
- :param compile_kwargs: additional kwargs that will be
- passed to the initial call to :meth:`.Compiled.process`.
-
- .. versionadded:: 0.8
-
- """
-
- self.dialect = dialect
- self.bind = bind
- if statement is not None:
- self.statement = statement
- self.can_execute = statement.supports_execution
- self.string = self.process(self.statement, **compile_kwargs)
-
- @util.deprecated("0.7", ":class:`.Compiled` objects now compile "
- "within the constructor.")
- def compile(self):
- """Produce the internal string representation of this element."""
- pass
-
- @property
- def sql_compiler(self):
- """Return a Compiled that is capable of processing SQL expressions.
-
- If this compiler is one, it would likely just return 'self'.
-
- """
-
- raise NotImplementedError()
-
- def process(self, obj, **kwargs):
- return obj._compiler_dispatch(self, **kwargs)
-
- def __str__(self):
- """Return the string text of the generated SQL or DDL."""
-
- return self.string or ''
-
- def construct_params(self, params=None):
- """Return the bind params for this compiled object.
-
- :param params: a dict of string/object pairs whose values will
- override bind values compiled in to the
- statement.
- """
-
- raise NotImplementedError()
-
- @property
- def params(self):
- """Return the bind params for this compiled object."""
- return self.construct_params()
-
- def execute(self, *multiparams, **params):
- """Execute this compiled object."""
-
- e = self.bind
- if e is None:
- raise exc.UnboundExecutionError(
- "This Compiled object is not bound to any Engine "
- "or Connection.")
- return e._execute_compiled(self, multiparams, params)
-
- def scalar(self, *multiparams, **params):
- """Execute this compiled object and return the result's
- scalar value."""
-
- return self.execute(*multiparams, **params).scalar()
-
-
-class TypeCompiler(object):
- """Produces DDL specification for TypeEngine objects."""
-
- def __init__(self, dialect):
- self.dialect = dialect
-
- def process(self, type_):
- return type_._compiler_dispatch(self)
-
-
class Connectable(object):
"""Interface for an object which supports execution of SQL constructs.
diff --git a/lib/sqlalchemy/engine/reflection.py b/lib/sqlalchemy/engine/reflection.py
index 50b3f774c..29ede9579 100644
--- a/lib/sqlalchemy/engine/reflection.py
+++ b/lib/sqlalchemy/engine/reflection.py
@@ -25,9 +25,9 @@ methods such as get_table_names, get_columns, etc.
"""
from .. import exc, sql
-from .. import schema as sa_schema
+from ..sql import schema as sa_schema
from .. import util
-from ..types import TypeEngine
+from ..sql.type_api import TypeEngine
from ..util import deprecated
from ..util import topological
from .. import inspection
diff --git a/lib/sqlalchemy/engine/result.py b/lib/sqlalchemy/engine/result.py
index 65ce3b742..91f3c2275 100644
--- a/lib/sqlalchemy/engine/result.py
+++ b/lib/sqlalchemy/engine/result.py
@@ -9,8 +9,8 @@ and :class:`.RowProxy."""
-from .. import exc, types, util
-from ..sql import expression
+from .. import exc, util
+from ..sql import expression, sqltypes
import collections
# This reconstructor is necessary so that pickles with the C extension or
@@ -205,10 +205,10 @@ class ResultMetaData(object):
else colname.lower()]
except KeyError:
name, obj, type_ = \
- colname, None, typemap.get(coltype, types.NULLTYPE)
+ colname, None, typemap.get(coltype, sqltypes.NULLTYPE)
else:
name, obj, type_ = \
- colname, None, typemap.get(coltype, types.NULLTYPE)
+ colname, None, typemap.get(coltype, sqltypes.NULLTYPE)
processor = context.get_result_processor(type_, colname, coltype)
diff --git a/lib/sqlalchemy/events.py b/lib/sqlalchemy/events.py
index 555d3b4a1..a5dc6e326 100644
--- a/lib/sqlalchemy/events.py
+++ b/lib/sqlalchemy/events.py
@@ -7,8 +7,6 @@
"""Core event interfaces."""
from . import event, exc, util
-engine = util.importlater('sqlalchemy', 'engine')
-pool = util.importlater('sqlalchemy', 'pool')
class DDLEvents(event.Events):
@@ -271,7 +269,11 @@ class PoolEvents(event.Events):
_target_class_doc = "SomeEngineOrPool"
@classmethod
- def _accept_with(cls, target):
+ @util.dependencies(
+ "sqlalchemy.engine",
+ "sqlalchemy.pool"
+ )
+ def _accept_with(cls, engine, pool, target):
if isinstance(target, type):
if issubclass(target, engine.Engine):
return pool.Pool
diff --git a/lib/sqlalchemy/inspection.py b/lib/sqlalchemy/inspection.py
index 74218fa91..42e08b30d 100644
--- a/lib/sqlalchemy/inspection.py
+++ b/lib/sqlalchemy/inspection.py
@@ -87,5 +87,6 @@ def _inspects(*types):
return decorate
-def _self_inspects(*types):
- _inspects(*types)(True)
+def _self_inspects(cls):
+ _inspects(cls)(True)
+ return cls
diff --git a/lib/sqlalchemy/log.py b/lib/sqlalchemy/log.py
index 1bb5581de..723a21d2f 100644
--- a/lib/sqlalchemy/log.py
+++ b/lib/sqlalchemy/log.py
@@ -38,17 +38,13 @@ def _add_default_handler(logger):
_logged_classes = set()
-def class_logger(cls, enable=False):
+def class_logger(cls):
logger = logging.getLogger(cls.__module__ + "." + cls.__name__)
- if enable == 'debug':
- logger.setLevel(logging.DEBUG)
- elif enable == 'info':
- logger.setLevel(logging.INFO)
cls._should_log_debug = lambda self: logger.isEnabledFor(logging.DEBUG)
cls._should_log_info = lambda self: logger.isEnabledFor(logging.INFO)
cls.logger = logger
_logged_classes.add(cls)
-
+ return cls
class Identified(object):
logging_name = None
diff --git a/lib/sqlalchemy/orm/__init__.py b/lib/sqlalchemy/orm/__init__.py
index 0bfaf72c9..112035ac5 100644
--- a/lib/sqlalchemy/orm/__init__.py
+++ b/lib/sqlalchemy/orm/__init__.py
@@ -1782,6 +1782,5 @@ def undefer_group(name):
"""
return strategies.UndeferGroupOption(name)
-
from .. import util as _sa_util
-_sa_util.importlater.resolve_all() \ No newline at end of file
+_sa_util.importlater.resolve_all("sqlalchemy.orm") \ No newline at end of file
diff --git a/lib/sqlalchemy/orm/attributes.py b/lib/sqlalchemy/orm/attributes.py
index f4f0cc782..c25792354 100644
--- a/lib/sqlalchemy/orm/attributes.py
+++ b/lib/sqlalchemy/orm/attributes.py
@@ -117,6 +117,7 @@ PASSIVE_ONLY_PERSISTENT = util.symbol("PASSIVE_ONLY_PERSISTENT",
)
+@inspection._self_inspects
class QueryableAttribute(interfaces._MappedAttribute,
interfaces._InspectionAttr,
interfaces.PropComparator):
@@ -286,8 +287,6 @@ class QueryableAttribute(interfaces._MappedAttribute,
"""
return self.comparator.property
-inspection._self_inspects(QueryableAttribute)
-
class InstrumentedAttribute(QueryableAttribute):
"""Class bound instrumented attribute which adds basic
diff --git a/lib/sqlalchemy/orm/dynamic.py b/lib/sqlalchemy/orm/dynamic.py
index fb46713d0..4ad204357 100644
--- a/lib/sqlalchemy/orm/dynamic.py
+++ b/lib/sqlalchemy/orm/dynamic.py
@@ -19,7 +19,7 @@ from . import (
)
from .query import Query
-
+@log.class_logger
class DynaLoader(strategies.AbstractRelationshipLoader):
def init_class_attribute(self, mapper):
self.is_class_level = True
@@ -39,9 +39,6 @@ class DynaLoader(strategies.AbstractRelationshipLoader):
backref=self.parent_property.back_populates,
)
-log.class_logger(DynaLoader)
-
-
class DynamicAttributeImpl(attributes.AttributeImpl):
uses_objects = True
accepts_scalar_loader = False
diff --git a/lib/sqlalchemy/orm/mapper.py b/lib/sqlalchemy/orm/mapper.py
index 9dfc1e267..a169d6d63 100644
--- a/lib/sqlalchemy/orm/mapper.py
+++ b/lib/sqlalchemy/orm/mapper.py
@@ -56,6 +56,8 @@ NO_ATTRIBUTE = util.symbol('NO_ATTRIBUTE')
_CONFIGURE_MUTEX = util.threading.RLock()
+@inspection._self_inspects
+@log.class_logger
class Mapper(_InspectionAttr):
"""Define the correlation of class attributes to database table
columns.
@@ -950,7 +952,7 @@ class Mapper(_InspectionAttr):
prop = self.polymorphic_on
self.polymorphic_on = prop.columns[0]
polymorphic_key = prop.key
- elif not expression.is_column(self.polymorphic_on):
+ elif not expression._is_column(self.polymorphic_on):
# polymorphic_on is not a Column and not a ColumnProperty;
# not supported right now.
raise sa_exc.ArgumentError(
@@ -1199,7 +1201,7 @@ class Mapper(_InspectionAttr):
# generate a properties.ColumnProperty
columns = util.to_list(prop)
column = columns[0]
- if not expression.is_column(column):
+ if not expression._is_column(column):
raise sa_exc.ArgumentError(
"%s=%r is not an instance of MapperProperty or Column"
% (key, prop))
@@ -2064,9 +2066,9 @@ class Mapper(_InspectionAttr):
dep is not None and \
dep is not parent and \
dep.inherit_condition is not None:
- cols = set(sql_util.find_columns(dep.inherit_condition))
+ cols = set(sql_util._find_columns(dep.inherit_condition))
if parent.inherit_condition is not None:
- cols = cols.union(sql_util.find_columns(
+ cols = cols.union(sql_util._find_columns(
parent.inherit_condition))
return fk.parent not in cols and fk.column not in cols
else:
@@ -2106,8 +2108,6 @@ class Mapper(_InspectionAttr):
return result
-inspection._self_inspects(Mapper)
-log.class_logger(Mapper)
def configure_mappers():
diff --git a/lib/sqlalchemy/orm/properties.py b/lib/sqlalchemy/orm/properties.py
index 1bc2cff05..551461818 100644
--- a/lib/sqlalchemy/orm/properties.py
+++ b/lib/sqlalchemy/orm/properties.py
@@ -34,7 +34,7 @@ from .descriptor_props import CompositeProperty, SynonymProperty, \
__all__ = ['ColumnProperty', 'CompositeProperty', 'SynonymProperty',
'ComparableProperty', 'RelationshipProperty', 'RelationProperty']
-
+@log.class_logger
class ColumnProperty(StrategizedProperty):
"""Describes an object attribute that corresponds to a table column.
@@ -222,9 +222,8 @@ class ColumnProperty(StrategizedProperty):
def __str__(self):
return str(self.parent.class_.__name__) + "." + self.key
-log.class_logger(ColumnProperty)
-
+@log.class_logger
class RelationshipProperty(StrategizedProperty):
"""Describes an object property that holds a single item or list
of items that correspond to a related database table.
@@ -1312,4 +1311,3 @@ class RelationshipProperty(StrategizedProperty):
PropertyLoader = RelationProperty = RelationshipProperty
-log.class_logger(RelationshipProperty)
diff --git a/lib/sqlalchemy/orm/query.py b/lib/sqlalchemy/orm/query.py
index 5ed63916a..ad8183b0d 100644
--- a/lib/sqlalchemy/orm/query.py
+++ b/lib/sqlalchemy/orm/query.py
@@ -55,7 +55,8 @@ def _generative(*assertions):
_path_registry = PathRegistry.root
-
+@inspection._self_inspects
+@log.class_logger
class Query(object):
"""ORM-level SQL construction object.
@@ -2838,8 +2839,6 @@ class Query(object):
def __str__(self):
return str(self._compile_context().statement)
-inspection._self_inspects(Query)
-
class _QueryEntity(object):
"""represent an entity column returned within a Query result."""
@@ -3188,8 +3187,6 @@ class _ColumnEntity(_QueryEntity):
return str(self.column)
-log.class_logger(Query)
-
class QueryContext(object):
multi_row_eager_loaders = False
diff --git a/lib/sqlalchemy/orm/strategies.py b/lib/sqlalchemy/orm/strategies.py
index fac8a3c6f..39ddaa7b8 100644
--- a/lib/sqlalchemy/orm/strategies.py
+++ b/lib/sqlalchemy/orm/strategies.py
@@ -111,6 +111,7 @@ class UninstrumentedColumnLoader(LoaderStrategy):
return None, None, None
+@log.class_logger
class ColumnLoader(LoaderStrategy):
"""Provide loading behavior for a :class:`.ColumnProperty`."""
@@ -156,9 +157,8 @@ class ColumnLoader(LoaderStrategy):
return expire_for_non_present_col, None, None
-log.class_logger(ColumnLoader)
-
+@log.class_logger
class DeferredColumnLoader(LoaderStrategy):
"""Provide loading behavior for a deferred :class:`.ColumnProperty`."""
@@ -251,8 +251,6 @@ class DeferredColumnLoader(LoaderStrategy):
return attributes.ATTR_WAS_SET
-log.class_logger(DeferredColumnLoader)
-
class LoadDeferredColumns(object):
"""serializable loader object used by DeferredColumnLoader"""
@@ -304,6 +302,7 @@ class AbstractRelationshipLoader(LoaderStrategy):
+@log.class_logger
class NoLoader(AbstractRelationshipLoader):
"""Provide loading behavior for a :class:`.RelationshipProperty`
with "lazy=None".
@@ -325,9 +324,8 @@ class NoLoader(AbstractRelationshipLoader):
return invoke_no_load, None, None
-log.class_logger(NoLoader)
-
+@log.class_logger
class LazyLoader(AbstractRelationshipLoader):
"""Provide loading behavior for a :class:`.RelationshipProperty`
with "lazy=True", that is loads when first accessed.
@@ -629,8 +627,6 @@ class LazyLoader(AbstractRelationshipLoader):
return reset_for_lazy_callable, None, None
-log.class_logger(LazyLoader)
-
class LoadLazyAttribute(object):
"""serializable loader object used by LazyLoader"""
@@ -666,6 +662,7 @@ class ImmediateLoader(AbstractRelationshipLoader):
return None, None, load_immediate
+@log.class_logger
class SubqueryLoader(AbstractRelationshipLoader):
def __init__(self, parent):
super(SubqueryLoader, self).__init__(parent)
@@ -983,9 +980,8 @@ class SubqueryLoader(AbstractRelationshipLoader):
return load_scalar_from_subq, None, None
-log.class_logger(SubqueryLoader)
-
+@log.class_logger
class JoinedLoader(AbstractRelationshipLoader):
"""Provide loading behavior for a :class:`.RelationshipProperty`
using joined eager loading.
@@ -1201,7 +1197,7 @@ class JoinedLoader(AbstractRelationshipLoader):
# by the Query propagates those columns outward.
# This has the effect
# of "undefering" those columns.
- for col in sql_util.find_columns(
+ for col in sql_util._find_columns(
self.parent_property.primaryjoin):
if localparent.mapped_table.c.contains_column(col):
if adapter:
@@ -1335,9 +1331,6 @@ class JoinedLoader(AbstractRelationshipLoader):
None, load_scalar_from_joined_exec
-log.class_logger(JoinedLoader)
-
-
class EagerLazyOption(StrategizedOption):
def __init__(self, key, lazy=True, chained=False,
propagate_to_loaders=True
diff --git a/lib/sqlalchemy/schema.py b/lib/sqlalchemy/schema.py
index 9550f0264..568efb3d6 100644
--- a/lib/sqlalchemy/schema.py
+++ b/lib/sqlalchemy/schema.py
@@ -1,3656 +1,54 @@
-# sqlalchemy/schema.py
+# schema.py
# Copyright (C) 2005-2013 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
-"""The schema module provides the building blocks for database metadata.
-
-Each element within this module describes a database entity which can be
-created and dropped, or is otherwise part of such an entity. Examples include
-tables, columns, sequences, and indexes.
-
-All entities are subclasses of :class:`~sqlalchemy.schema.SchemaItem`, and as
-defined in this module they are intended to be agnostic of any vendor-specific
-constructs.
-
-A collection of entities are grouped into a unit called
-:class:`~sqlalchemy.schema.MetaData`. MetaData serves as a logical grouping of
-schema elements, and can also be associated with an actual database connection
-such that operations involving the contained elements can contact the database
-as needed.
-
-Two of the elements here also build upon their "syntactic" counterparts, which
-are defined in :class:`~sqlalchemy.sql.expression.`, specifically
-:class:`~sqlalchemy.schema.Table` and :class:`~sqlalchemy.schema.Column`.
-Since these objects are part of the SQL expression language, they are usable
-as components in SQL expressions.
+"""Compatiblity namespace for sqlalchemy.sql.schema and related.
"""
-import re
-import inspect
-from . import exc, util, dialects, event, events, inspection
-from .sql import expression, visitors
-import collections
-import sqlalchemy
-
-ddl = util.importlater("sqlalchemy.engine", "ddl")
-sqlutil = util.importlater("sqlalchemy.sql", "util")
-url = util.importlater("sqlalchemy.engine", "url")
-sqltypes = util.importlater("sqlalchemy", "types")
-
-__all__ = ['SchemaItem', 'Table', 'Column', 'ForeignKey', 'Sequence', 'Index',
- 'ForeignKeyConstraint', 'PrimaryKeyConstraint', 'CheckConstraint',
- 'UniqueConstraint', 'DefaultGenerator', 'Constraint', 'MetaData',
- 'ThreadLocalMetaData', 'SchemaVisitor', 'PassiveDefault',
- 'DefaultClause', 'FetchedValue', 'ColumnDefault', 'DDL',
- 'CreateTable', 'DropTable', 'CreateSequence', 'DropSequence',
- 'AddConstraint', 'DropConstraint',
- ]
-__all__.sort()
-
-RETAIN_SCHEMA = util.symbol('retain_schema')
-
-
-class SchemaItem(events.SchemaEventTarget, visitors.Visitable):
- """Base class for items that define a database schema."""
-
- __visit_name__ = 'schema_item'
- quote = None
-
- def _init_items(self, *args):
- """Initialize the list of child items for this SchemaItem."""
-
- for item in args:
- if item is not None:
- item._set_parent_with_dispatch(self)
-
- def get_children(self, **kwargs):
- """used to allow SchemaVisitor access"""
- return []
-
- def __repr__(self):
- return util.generic_repr(self)
-
- @util.memoized_property
- def info(self):
- """Info dictionary associated with the object, allowing user-defined
- data to be associated with this :class:`.SchemaItem`.
-
- The dictionary is automatically generated when first accessed.
- It can also be specified in the constructor of some objects,
- such as :class:`.Table` and :class:`.Column`.
-
- """
- return {}
-
-
-def _get_table_key(name, schema):
- if schema is None:
- return name
- else:
- return schema + "." + name
-
-
-def _validate_dialect_kwargs(kwargs, name):
- # validate remaining kwargs that they all specify DB prefixes
-
- for k in kwargs:
- m = re.match('^(.+?)_.*', k)
- if m is None:
- raise TypeError("Additional arguments should be "
- "named <dialectname>_<argument>, got '%s'" % k)
-
-
-inspection._self_inspects(SchemaItem)
-
-
-class Table(SchemaItem, expression.TableClause):
- """Represent a table in a database.
-
- e.g.::
-
- mytable = Table("mytable", metadata,
- Column('mytable_id', Integer, primary_key=True),
- Column('value', String(50))
- )
-
- The :class:`.Table` object constructs a unique instance of itself based
- on its name and optional schema name within the given
- :class:`.MetaData` object. Calling the :class:`.Table`
- constructor with the same name and same :class:`.MetaData` argument
- a second time will return the *same* :class:`.Table` object - in this way
- the :class:`.Table` constructor acts as a registry function.
-
- See also:
-
- :ref:`metadata_describing` - Introduction to database metadata
-
- Constructor arguments are as follows:
-
- :param name: The name of this table as represented in the database.
-
- This property, along with the *schema*, indicates the *singleton
- identity* of this table in relation to its parent :class:`.MetaData`.
- Additional calls to :class:`.Table` with the same name, metadata,
- and schema name will return the same :class:`.Table` object.
-
- Names which contain no upper case characters
- will be treated as case insensitive names, and will not be quoted
- unless they are a reserved word. Names with any number of upper
- case characters will be quoted and sent exactly. Note that this
- behavior applies even for databases which standardize upper
- case names as case insensitive such as Oracle.
-
- :param metadata: a :class:`.MetaData` object which will contain this
- table. The metadata is used as a point of association of this table
- with other tables which are referenced via foreign key. It also
- may be used to associate this table with a particular
- :class:`.Connectable`.
-
- :param \*args: Additional positional arguments are used primarily
- to add the list of :class:`.Column` objects contained within this
- table. Similar to the style of a CREATE TABLE statement, other
- :class:`.SchemaItem` constructs may be added here, including
- :class:`.PrimaryKeyConstraint`, and :class:`.ForeignKeyConstraint`.
-
- :param autoload: Defaults to False: the Columns for this table should
- be reflected from the database. Usually there will be no Column
- objects in the constructor if this property is set.
-
- :param autoload_replace: If ``True``, when using ``autoload=True``
- and ``extend_existing=True``,
- replace ``Column`` objects already present in the ``Table`` that's
- in the ``MetaData`` registry with
- what's reflected. Otherwise, all existing columns will be
- excluded from the reflection process. Note that this does
- not impact ``Column`` objects specified in the same call to ``Table``
- which includes ``autoload``, those always take precedence.
- Defaults to ``True``.
-
- .. versionadded:: 0.7.5
-
- :param autoload_with: If autoload==True, this is an optional Engine
- or Connection instance to be used for the table reflection. If
- ``None``, the underlying MetaData's bound connectable will be used.
-
- :param extend_existing: When ``True``, indicates that if this
- :class:`.Table` is already present in the given :class:`.MetaData`,
- apply further arguments within the constructor to the existing
- :class:`.Table`.
-
- If ``extend_existing`` or ``keep_existing`` are not set, an error is
- raised if additional table modifiers are specified when
- the given :class:`.Table` is already present in the :class:`.MetaData`.
-
- .. versionchanged:: 0.7.4
- ``extend_existing`` will work in conjunction
- with ``autoload=True`` to run a new reflection operation against
- the database; new :class:`.Column` objects will be produced
- from database metadata to replace those existing with the same
- name, and additional :class:`.Column` objects not present
- in the :class:`.Table` will be added.
-
- As is always the case with ``autoload=True``, :class:`.Column`
- objects can be specified in the same :class:`.Table` constructor,
- which will take precedence. I.e.::
-
- Table("mytable", metadata,
- Column('y', Integer),
- extend_existing=True,
- autoload=True,
- autoload_with=engine
- )
-
- The above will overwrite all columns within ``mytable`` which
- are present in the database, except for ``y`` which will be used as is
- from the above definition. If the ``autoload_replace`` flag
- is set to False, no existing columns will be replaced.
-
- :param implicit_returning: True by default - indicates that
- RETURNING can be used by default to fetch newly inserted primary key
- values, for backends which support this. Note that
- create_engine() also provides an implicit_returning flag.
-
- :param include_columns: A list of strings indicating a subset of
- columns to be loaded via the ``autoload`` operation; table columns who
- aren't present in this list will not be represented on the resulting
- ``Table`` object. Defaults to ``None`` which indicates all columns
- should be reflected.
-
- :param info: Optional data dictionary which will be populated into the
- :attr:`.SchemaItem.info` attribute of this object.
-
- :param keep_existing: When ``True``, indicates that if this Table
- is already present in the given :class:`.MetaData`, ignore
- further arguments within the constructor to the existing
- :class:`.Table`, and return the :class:`.Table` object as
- originally created. This is to allow a function that wishes
- to define a new :class:`.Table` on first call, but on
- subsequent calls will return the same :class:`.Table`,
- without any of the declarations (particularly constraints)
- being applied a second time. Also see extend_existing.
-
- If extend_existing or keep_existing are not set, an error is
- raised if additional table modifiers are specified when
- the given :class:`.Table` is already present in the :class:`.MetaData`.
-
- :param listeners: A list of tuples of the form ``(<eventname>, <fn>)``
- which will be passed to :func:`.event.listen` upon construction.
- This alternate hook to :func:`.event.listen` allows the establishment
- of a listener function specific to this :class:`.Table` before
- the "autoload" process begins. Particularly useful for
- the :meth:`.DDLEvents.column_reflect` event::
-
- def listen_for_reflect(table, column_info):
- "handle the column reflection event"
- # ...
-
- t = Table(
- 'sometable',
- autoload=True,
- listeners=[
- ('column_reflect', listen_for_reflect)
- ])
-
- :param mustexist: When ``True``, indicates that this Table must already
- be present in the given :class:`.MetaData` collection, else
- an exception is raised.
-
- :param prefixes:
- A list of strings to insert after CREATE in the CREATE TABLE
- statement. They will be separated by spaces.
-
- :param quote: Force quoting of this table's name on or off, corresponding
- to ``True`` or ``False``. When left at its default of ``None``,
- the column identifier will be quoted according to whether the name is
- case sensitive (identifiers with at least one upper case character are
- treated as case sensitive), or if it's a reserved word. This flag
- is only needed to force quoting of a reserved word which is not known
- by the SQLAlchemy dialect.
-
- :param quote_schema: same as 'quote' but applies to the schema identifier.
-
- :param schema: The *schema name* for this table, which is required if
- the table resides in a schema other than the default selected schema
- for the engine's database connection. Defaults to ``None``.
-
- :param useexisting: Deprecated. Use extend_existing.
-
- """
-
- __visit_name__ = 'table'
-
- def __new__(cls, *args, **kw):
- if not args:
- # python3k pickle seems to call this
- return object.__new__(cls)
-
- try:
- name, metadata, args = args[0], args[1], args[2:]
- except IndexError:
- raise TypeError("Table() takes at least two arguments")
-
- schema = kw.get('schema', None)
- if schema is None:
- schema = metadata.schema
- keep_existing = kw.pop('keep_existing', False)
- extend_existing = kw.pop('extend_existing', False)
- if 'useexisting' in kw:
- msg = "useexisting is deprecated. Use extend_existing."
- util.warn_deprecated(msg)
- if extend_existing:
- msg = "useexisting is synonymous with extend_existing."
- raise exc.ArgumentError(msg)
- extend_existing = kw.pop('useexisting', False)
-
- if keep_existing and extend_existing:
- msg = "keep_existing and extend_existing are mutually exclusive."
- raise exc.ArgumentError(msg)
-
- mustexist = kw.pop('mustexist', False)
- key = _get_table_key(name, schema)
- if key in metadata.tables:
- if not keep_existing and not extend_existing and bool(args):
- raise exc.InvalidRequestError(
- "Table '%s' is already defined for this MetaData "
- "instance. Specify 'extend_existing=True' "
- "to redefine "
- "options and columns on an "
- "existing Table object." % key)
- table = metadata.tables[key]
- if extend_existing:
- table._init_existing(*args, **kw)
- return table
- else:
- if mustexist:
- raise exc.InvalidRequestError(
- "Table '%s' not defined" % (key))
- table = object.__new__(cls)
- table.dispatch.before_parent_attach(table, metadata)
- metadata._add_table(name, schema, table)
- try:
- table._init(name, metadata, *args, **kw)
- table.dispatch.after_parent_attach(table, metadata)
- return table
- except:
- metadata._remove_table(name, schema)
- raise
-
- def __init__(self, *args, **kw):
- """Constructor for :class:`~.schema.Table`.
-
- This method is a no-op. See the top-level
- documentation for :class:`~.schema.Table`
- for constructor arguments.
-
- """
- # __init__ is overridden to prevent __new__ from
- # calling the superclass constructor.
-
- def _init(self, name, metadata, *args, **kwargs):
- super(Table, self).__init__(name)
- self.metadata = metadata
- self.schema = kwargs.pop('schema', None)
- if self.schema is None:
- self.schema = metadata.schema
- self.quote_schema = kwargs.pop(
- 'quote_schema', metadata.quote_schema)
- else:
- self.quote_schema = kwargs.pop('quote_schema', None)
-
- self.indexes = set()
- self.constraints = set()
- self._columns = expression.ColumnCollection()
- PrimaryKeyConstraint()._set_parent_with_dispatch(self)
- self.foreign_keys = set()
- self._extra_dependencies = set()
- self.kwargs = {}
- if self.schema is not None:
- self.fullname = "%s.%s" % (self.schema, self.name)
- else:
- self.fullname = self.name
-
- autoload = kwargs.pop('autoload', False)
- autoload_with = kwargs.pop('autoload_with', None)
- # this argument is only used with _init_existing()
- kwargs.pop('autoload_replace', True)
- include_columns = kwargs.pop('include_columns', None)
-
- self.implicit_returning = kwargs.pop('implicit_returning', True)
- self.quote = kwargs.pop('quote', None)
- if 'info' in kwargs:
- self.info = kwargs.pop('info')
- if 'listeners' in kwargs:
- listeners = kwargs.pop('listeners')
- for evt, fn in listeners:
- event.listen(self, evt, fn)
-
- self._prefixes = kwargs.pop('prefixes', [])
-
- self._extra_kwargs(**kwargs)
-
- # load column definitions from the database if 'autoload' is defined
- # we do it after the table is in the singleton dictionary to support
- # circular foreign keys
- if autoload:
- self._autoload(metadata, autoload_with, include_columns)
-
- # initialize all the column, etc. objects. done after reflection to
- # allow user-overrides
- self._init_items(*args)
-
- def _autoload(self, metadata, autoload_with, include_columns,
- exclude_columns=()):
- if self.primary_key.columns:
- PrimaryKeyConstraint(*[
- c for c in self.primary_key.columns
- if c.key in exclude_columns
- ])._set_parent_with_dispatch(self)
-
- if autoload_with:
- autoload_with.run_callable(
- autoload_with.dialect.reflecttable,
- self, include_columns, exclude_columns
- )
- else:
- bind = _bind_or_error(metadata,
- msg="No engine is bound to this Table's MetaData. "
- "Pass an engine to the Table via "
- "autoload_with=<someengine>, "
- "or associate the MetaData with an engine via "
- "metadata.bind=<someengine>")
- bind.run_callable(
- bind.dialect.reflecttable,
- self, include_columns, exclude_columns
- )
-
- @property
- def _sorted_constraints(self):
- """Return the set of constraints as a list, sorted by creation
- order.
-
- """
- return sorted(self.constraints, key=lambda c: c._creation_order)
-
- def _init_existing(self, *args, **kwargs):
- autoload = kwargs.pop('autoload', False)
- autoload_with = kwargs.pop('autoload_with', None)
- autoload_replace = kwargs.pop('autoload_replace', True)
- schema = kwargs.pop('schema', None)
- if schema and schema != self.schema:
- raise exc.ArgumentError(
- "Can't change schema of existing table from '%s' to '%s'",
- (self.schema, schema))
-
- include_columns = kwargs.pop('include_columns', None)
-
- if include_columns is not None:
- for c in self.c:
- if c.name not in include_columns:
- self._columns.remove(c)
-
- for key in ('quote', 'quote_schema'):
- if key in kwargs:
- setattr(self, key, kwargs.pop(key))
-
- if 'info' in kwargs:
- self.info = kwargs.pop('info')
-
- if autoload:
- if not autoload_replace:
- exclude_columns = [c.name for c in self.c]
- else:
- exclude_columns = ()
- self._autoload(
- self.metadata, autoload_with, include_columns, exclude_columns)
-
- self._extra_kwargs(**kwargs)
- self._init_items(*args)
-
- def _extra_kwargs(self, **kwargs):
- # validate remaining kwargs that they all specify DB prefixes
- _validate_dialect_kwargs(kwargs, "Table")
- self.kwargs.update(kwargs)
-
- def _init_collections(self):
- pass
-
- @util.memoized_property
- def _autoincrement_column(self):
- for col in self.primary_key:
- if col.autoincrement and \
- col.type._type_affinity is not None and \
- issubclass(col.type._type_affinity, sqltypes.Integer) and \
- (not col.foreign_keys or col.autoincrement == 'ignore_fk') and \
- isinstance(col.default, (type(None), Sequence)) and \
- (col.server_default is None or col.server_default.reflected):
- return col
-
- @property
- def key(self):
- return _get_table_key(self.name, self.schema)
-
- def __repr__(self):
- return "Table(%s)" % ', '.join(
- [repr(self.name)] + [repr(self.metadata)] +
- [repr(x) for x in self.columns] +
- ["%s=%s" % (k, repr(getattr(self, k))) for k in ['schema']])
-
- def __str__(self):
- return _get_table_key(self.description, self.schema)
-
- @property
- def bind(self):
- """Return the connectable associated with this Table."""
-
- return self.metadata and self.metadata.bind or None
-
- def add_is_dependent_on(self, table):
- """Add a 'dependency' for this Table.
-
- This is another Table object which must be created
- first before this one can, or dropped after this one.
-
- Usually, dependencies between tables are determined via
- ForeignKey objects. However, for other situations that
- create dependencies outside of foreign keys (rules, inheriting),
- this method can manually establish such a link.
-
- """
- self._extra_dependencies.add(table)
-
- def append_column(self, column):
- """Append a :class:`~.schema.Column` to this :class:`~.schema.Table`.
-
- The "key" of the newly added :class:`~.schema.Column`, i.e. the
- value of its ``.key`` attribute, will then be available
- in the ``.c`` collection of this :class:`~.schema.Table`, and the
- column definition will be included in any CREATE TABLE, SELECT,
- UPDATE, etc. statements generated from this :class:`~.schema.Table`
- construct.
-
- Note that this does **not** change the definition of the table
- as it exists within any underlying database, assuming that
- table has already been created in the database. Relational
- databases support the addition of columns to existing tables
- using the SQL ALTER command, which would need to be
- emitted for an already-existing table that doesn't contain
- the newly added column.
-
- """
-
- column._set_parent_with_dispatch(self)
-
- def append_constraint(self, constraint):
- """Append a :class:`~.schema.Constraint` to this
- :class:`~.schema.Table`.
-
- This has the effect of the constraint being included in any
- future CREATE TABLE statement, assuming specific DDL creation
- events have not been associated with the given
- :class:`~.schema.Constraint` object.
-
- Note that this does **not** produce the constraint within the
- relational database automatically, for a table that already exists
- in the database. To add a constraint to an
- existing relational database table, the SQL ALTER command must
- be used. SQLAlchemy also provides the
- :class:`.AddConstraint` construct which can produce this SQL when
- invoked as an executable clause.
-
- """
-
- constraint._set_parent_with_dispatch(self)
-
- def append_ddl_listener(self, event_name, listener):
- """Append a DDL event listener to this ``Table``.
-
- .. deprecated:: 0.7
- See :class:`.DDLEvents`.
-
- """
-
- def adapt_listener(target, connection, **kw):
- listener(event_name, target, connection)
-
- event.listen(self, "" + event_name.replace('-', '_'), adapt_listener)
-
- def _set_parent(self, metadata):
- metadata._add_table(self.name, self.schema, self)
- self.metadata = metadata
-
- def get_children(self, column_collections=True,
- schema_visitor=False, **kw):
- if not schema_visitor:
- return expression.TableClause.get_children(
- self, column_collections=column_collections, **kw)
- else:
- if column_collections:
- return list(self.columns)
- else:
- return []
-
- def exists(self, bind=None):
- """Return True if this table exists."""
-
- if bind is None:
- bind = _bind_or_error(self)
-
- return bind.run_callable(bind.dialect.has_table,
- self.name, schema=self.schema)
-
- def create(self, bind=None, checkfirst=False):
- """Issue a ``CREATE`` statement for this
- :class:`.Table`, using the given :class:`.Connectable`
- for connectivity.
-
- See also :meth:`.MetaData.create_all`.
-
- """
-
- if bind is None:
- bind = _bind_or_error(self)
- bind._run_visitor(ddl.SchemaGenerator,
- self,
- checkfirst=checkfirst)
-
- def drop(self, bind=None, checkfirst=False):
- """Issue a ``DROP`` statement for this
- :class:`.Table`, using the given :class:`.Connectable`
- for connectivity.
-
- See also :meth:`.MetaData.drop_all`.
-
- """
- if bind is None:
- bind = _bind_or_error(self)
- bind._run_visitor(ddl.SchemaDropper,
- self,
- checkfirst=checkfirst)
-
- def tometadata(self, metadata, schema=RETAIN_SCHEMA):
- """Return a copy of this :class:`.Table` associated with a different
- :class:`.MetaData`.
-
- E.g.::
-
- some_engine = create_engine("sqlite:///some.db")
-
- # create two metadata
- meta1 = MetaData()
- meta2 = MetaData()
-
- # load 'users' from the sqlite engine
- users_table = Table('users', meta1, autoload=True,
- autoload_with=some_engine)
-
- # create the same Table object for the plain metadata
- users_table_2 = users_table.tometadata(meta2)
-
- :param metadata: Target :class:`.MetaData` object.
- :param schema: Optional string name of a target schema, or
- ``None`` for no schema. The :class:`.Table` object will be
- given this schema name upon copy. Defaults to the special
- symbol :attr:`.RETAIN_SCHEMA` which indicates no change should be
- made to the schema name of the resulting :class:`.Table`.
-
- """
-
- if schema is RETAIN_SCHEMA:
- schema = self.schema
- elif schema is None:
- schema = metadata.schema
- key = _get_table_key(self.name, schema)
- if key in metadata.tables:
- util.warn("Table '%s' already exists within the given "
- "MetaData - not copying." % self.description)
- return metadata.tables[key]
-
- args = []
- for c in self.columns:
- args.append(c.copy(schema=schema))
- table = Table(
- self.name, metadata, schema=schema,
- *args, **self.kwargs
- )
- for c in self.constraints:
- table.append_constraint(c.copy(schema=schema, target_table=table))
-
- for index in self.indexes:
- # skip indexes that would be generated
- # by the 'index' flag on Column
- if len(index.columns) == 1 and \
- list(index.columns)[0].index:
- continue
- Index(index.name,
- unique=index.unique,
- *[table.c[col] for col in index.columns.keys()],
- **index.kwargs)
- table.dispatch._update(self.dispatch)
- return table
-
-
-class Column(SchemaItem, expression.ColumnClause):
- """Represents a column in a database table."""
-
- __visit_name__ = 'column'
-
- def __init__(self, *args, **kwargs):
- """
- Construct a new ``Column`` object.
-
- :param name: The name of this column as represented in the database.
- This argument may be the first positional argument, or specified
- via keyword.
-
- Names which contain no upper case characters
- will be treated as case insensitive names, and will not be quoted
- unless they are a reserved word. Names with any number of upper
- case characters will be quoted and sent exactly. Note that this
- behavior applies even for databases which standardize upper
- case names as case insensitive such as Oracle.
-
- The name field may be omitted at construction time and applied
- later, at any time before the Column is associated with a
- :class:`.Table`. This is to support convenient
- usage within the :mod:`~sqlalchemy.ext.declarative` extension.
-
- :param type\_: The column's type, indicated using an instance which
- subclasses :class:`~sqlalchemy.types.TypeEngine`. If no arguments
- are required for the type, the class of the type can be sent
- as well, e.g.::
-
- # use a type with arguments
- Column('data', String(50))
-
- # use no arguments
- Column('level', Integer)
-
- The ``type`` argument may be the second positional argument
- or specified by keyword.
-
- If the ``type`` is ``None`` or is omitted, it will first default to the special
- type :class:`.NullType`. If and when this :class:`.Column` is
- made to refer to another column using :class:`.ForeignKey`
- and/or :class:`.ForeignKeyConstraint`, the type of the remote-referenced
- column will be copied to this column as well, at the moment that
- the foreign key is resolved against that remote :class:`.Column`
- object.
-
- .. versionchanged:: 0.9.0
- Support for propagation of type to a :class:`.Column` from its
- :class:`.ForeignKey` object has been improved and should be
- more reliable and timely.
-
- :param \*args: Additional positional arguments include various
- :class:`.SchemaItem` derived constructs which will be applied
- as options to the column. These include instances of
- :class:`.Constraint`, :class:`.ForeignKey`, :class:`.ColumnDefault`,
- and :class:`.Sequence`. In some cases an equivalent keyword
- argument is available such as ``server_default``, ``default``
- and ``unique``.
-
- :param autoincrement: This flag may be set to ``False`` to
- indicate an integer primary key column that should not be
- considered to be the "autoincrement" column, that is
- the integer primary key column which generates values
- implicitly upon INSERT and whose value is usually returned
- via the DBAPI cursor.lastrowid attribute. It defaults
- to ``True`` to satisfy the common use case of a table
- with a single integer primary key column. If the table
- has a composite primary key consisting of more than one
- integer column, set this flag to True only on the
- column that should be considered "autoincrement".
-
- The setting *only* has an effect for columns which are:
-
- * Integer derived (i.e. INT, SMALLINT, BIGINT).
-
- * Part of the primary key
-
- * Are not referenced by any foreign keys, unless
- the value is specified as ``'ignore_fk'``
-
- .. versionadded:: 0.7.4
-
- * have no server side or client side defaults (with the exception
- of Postgresql SERIAL).
-
- The setting has these two effects on columns that meet the
- above criteria:
-
- * DDL issued for the column will include database-specific
- keywords intended to signify this column as an
- "autoincrement" column, such as AUTO INCREMENT on MySQL,
- SERIAL on Postgresql, and IDENTITY on MS-SQL. It does
- *not* issue AUTOINCREMENT for SQLite since this is a
- special SQLite flag that is not required for autoincrementing
- behavior. See the SQLite dialect documentation for
- information on SQLite's AUTOINCREMENT.
-
- * The column will be considered to be available as
- cursor.lastrowid or equivalent, for those dialects which
- "post fetch" newly inserted identifiers after a row has
- been inserted (SQLite, MySQL, MS-SQL). It does not have
- any effect in this regard for databases that use sequences
- to generate primary key identifiers (i.e. Firebird, Postgresql,
- Oracle).
-
- .. versionchanged:: 0.7.4
- ``autoincrement`` accepts a special value ``'ignore_fk'``
- to indicate that autoincrementing status regardless of foreign
- key references. This applies to certain composite foreign key
- setups, such as the one demonstrated in the ORM documentation
- at :ref:`post_update`.
-
- :param default: A scalar, Python callable, or
- :class:`.ColumnElement` expression representing the
- *default value* for this column, which will be invoked upon insert
- if this column is otherwise not specified in the VALUES clause of
- the insert. This is a shortcut to using :class:`.ColumnDefault` as
- a positional argument; see that class for full detail on the
- structure of the argument.
-
- Contrast this argument to ``server_default`` which creates a
- default generator on the database side.
-
- :param doc: optional String that can be used by the ORM or similar
- to document attributes. This attribute does not render SQL
- comments (a future attribute 'comment' will achieve that).
-
- :param key: An optional string identifier which will identify this
- ``Column`` object on the :class:`.Table`. When a key is provided,
- this is the only identifier referencing the ``Column`` within the
- application, including ORM attribute mapping; the ``name`` field
- is used only when rendering SQL.
-
- :param index: When ``True``, indicates that the column is indexed.
- This is a shortcut for using a :class:`.Index` construct on the
- table. To specify indexes with explicit names or indexes that
- contain multiple columns, use the :class:`.Index` construct
- instead.
-
- :param info: Optional data dictionary which will be populated into the
- :attr:`.SchemaItem.info` attribute of this object.
-
- :param nullable: If set to the default of ``True``, indicates the
- column will be rendered as allowing NULL, else it's rendered as
- NOT NULL. This parameter is only used when issuing CREATE TABLE
- statements.
-
- :param onupdate: A scalar, Python callable, or
- :class:`~sqlalchemy.sql.expression.ClauseElement` representing a
- default value to be applied to the column within UPDATE
- statements, which wil be invoked upon update if this column is not
- present in the SET clause of the update. This is a shortcut to
- using :class:`.ColumnDefault` as a positional argument with
- ``for_update=True``.
-
- :param primary_key: If ``True``, marks this column as a primary key
- column. Multiple columns can have this flag set to specify
- composite primary keys. As an alternative, the primary key of a
- :class:`.Table` can be specified via an explicit
- :class:`.PrimaryKeyConstraint` object.
-
- :param server_default: A :class:`.FetchedValue` instance, str, Unicode
- or :func:`~sqlalchemy.sql.expression.text` construct representing
- the DDL DEFAULT value for the column.
-
- String types will be emitted as-is, surrounded by single quotes::
-
- Column('x', Text, server_default="val")
-
- x TEXT DEFAULT 'val'
-
- A :func:`~sqlalchemy.sql.expression.text` expression will be
- rendered as-is, without quotes::
-
- Column('y', DateTime, server_default=text('NOW()'))0
-
- y DATETIME DEFAULT NOW()
-
- Strings and text() will be converted into a :class:`.DefaultClause`
- object upon initialization.
-
- Use :class:`.FetchedValue` to indicate that an already-existing
- column will generate a default value on the database side which
- will be available to SQLAlchemy for post-fetch after inserts. This
- construct does not specify any DDL and the implementation is left
- to the database, such as via a trigger.
-
- :param server_onupdate: A :class:`.FetchedValue` instance
- representing a database-side default generation function. This
- indicates to SQLAlchemy that a newly generated value will be
- available after updates. This construct does not specify any DDL
- and the implementation is left to the database, such as via a
- trigger.
-
- :param quote: Force quoting of this column's name on or off,
- corresponding to ``True`` or ``False``. When left at its default
- of ``None``, the column identifier will be quoted according to
- whether the name is case sensitive (identifiers with at least one
- upper case character are treated as case sensitive), or if it's a
- reserved word. This flag is only needed to force quoting of a
- reserved word which is not known by the SQLAlchemy dialect.
-
- :param unique: When ``True``, indicates that this column contains a
- unique constraint, or if ``index`` is ``True`` as well, indicates
- that the :class:`.Index` should be created with the unique flag.
- To specify multiple columns in the constraint/index or to specify
- an explicit name, use the :class:`.UniqueConstraint` or
- :class:`.Index` constructs explicitly.
-
- """
-
- name = kwargs.pop('name', None)
- type_ = kwargs.pop('type_', None)
- args = list(args)
- if args:
- if isinstance(args[0], util.string_types):
- if name is not None:
- raise exc.ArgumentError(
- "May not pass name positionally and as a keyword.")
- name = args.pop(0)
- if args:
- coltype = args[0]
-
- if (isinstance(coltype, sqltypes.TypeEngine) or
- (isinstance(coltype, type) and
- issubclass(coltype, sqltypes.TypeEngine))):
- if type_ is not None:
- raise exc.ArgumentError(
- "May not pass type_ positionally and as a keyword.")
- type_ = args.pop(0)
-
- super(Column, self).__init__(name, None, type_)
- self.key = kwargs.pop('key', name)
- self.primary_key = kwargs.pop('primary_key', False)
- self.nullable = kwargs.pop('nullable', not self.primary_key)
- self.default = kwargs.pop('default', None)
- self.server_default = kwargs.pop('server_default', None)
- self.server_onupdate = kwargs.pop('server_onupdate', None)
- self.index = kwargs.pop('index', None)
- self.unique = kwargs.pop('unique', None)
- self.quote = kwargs.pop('quote', None)
- self.doc = kwargs.pop('doc', None)
- self.onupdate = kwargs.pop('onupdate', None)
- self.autoincrement = kwargs.pop('autoincrement', True)
- self.constraints = set()
- self.foreign_keys = set()
-
- # check if this Column is proxying another column
- if '_proxies' in kwargs:
- self._proxies = kwargs.pop('_proxies')
- # otherwise, add DDL-related events
- elif isinstance(self.type, sqltypes.SchemaType):
- self.type._set_parent_with_dispatch(self)
-
- if self.default is not None:
- if isinstance(self.default, (ColumnDefault, Sequence)):
- args.append(self.default)
- else:
- if getattr(self.type, '_warn_on_bytestring', False):
- if isinstance(self.default, util.binary_type):
- util.warn("Unicode column received non-unicode "
- "default value.")
- args.append(ColumnDefault(self.default))
-
- if self.server_default is not None:
- if isinstance(self.server_default, FetchedValue):
- args.append(self.server_default._as_for_update(False))
- else:
- args.append(DefaultClause(self.server_default))
-
- if self.onupdate is not None:
- if isinstance(self.onupdate, (ColumnDefault, Sequence)):
- args.append(self.onupdate)
- else:
- args.append(ColumnDefault(self.onupdate, for_update=True))
-
- if self.server_onupdate is not None:
- if isinstance(self.server_onupdate, FetchedValue):
- args.append(self.server_onupdate._as_for_update(True))
- else:
- args.append(DefaultClause(self.server_onupdate,
- for_update=True))
- self._init_items(*args)
-
- util.set_creation_order(self)
-
- if 'info' in kwargs:
- self.info = kwargs.pop('info')
-
- if kwargs:
- raise exc.ArgumentError(
- "Unknown arguments passed to Column: " + repr(list(kwargs)))
-
- def __str__(self):
- if self.name is None:
- return "(no name)"
- elif self.table is not None:
- if self.table.named_with_column:
- return (self.table.description + "." + self.description)
- else:
- return self.description
- else:
- return self.description
-
- def references(self, column):
- """Return True if this Column references the given column via foreign
- key."""
-
- for fk in self.foreign_keys:
- if fk.column.proxy_set.intersection(column.proxy_set):
- return True
- else:
- return False
-
- def append_foreign_key(self, fk):
- fk._set_parent_with_dispatch(self)
-
- def __repr__(self):
- kwarg = []
- if self.key != self.name:
- kwarg.append('key')
- if self.primary_key:
- kwarg.append('primary_key')
- if not self.nullable:
- kwarg.append('nullable')
- if self.onupdate:
- kwarg.append('onupdate')
- if self.default:
- kwarg.append('default')
- if self.server_default:
- kwarg.append('server_default')
- return "Column(%s)" % ', '.join(
- [repr(self.name)] + [repr(self.type)] +
- [repr(x) for x in self.foreign_keys if x is not None] +
- [repr(x) for x in self.constraints] +
- [(self.table is not None and "table=<%s>" %
- self.table.description or "table=None")] +
- ["%s=%s" % (k, repr(getattr(self, k))) for k in kwarg])
-
- def _set_parent(self, table):
- if not self.name:
- raise exc.ArgumentError(
- "Column must be constructed with a non-blank name or "
- "assign a non-blank .name before adding to a Table.")
- if self.key is None:
- self.key = self.name
-
- existing = getattr(self, 'table', None)
- if existing is not None and existing is not table:
- raise exc.ArgumentError(
- "Column object already assigned to Table '%s'" %
- existing.description)
-
- if self.key in table._columns:
- col = table._columns.get(self.key)
- if col is not self:
- for fk in col.foreign_keys:
- table.foreign_keys.remove(fk)
- if fk.constraint in table.constraints:
- # this might have been removed
- # already, if it's a composite constraint
- # and more than one col being replaced
- table.constraints.remove(fk.constraint)
-
- table._columns.replace(self)
-
- if self.primary_key:
- table.primary_key._replace(self)
- Table._autoincrement_column._reset(table)
- elif self.key in table.primary_key:
- raise exc.ArgumentError(
- "Trying to redefine primary-key column '%s' as a "
- "non-primary-key column on table '%s'" % (
- self.key, table.fullname))
- self.table = table
-
- if self.index:
- if isinstance(self.index, util.string_types):
- raise exc.ArgumentError(
- "The 'index' keyword argument on Column is boolean only. "
- "To create indexes with a specific name, create an "
- "explicit Index object external to the Table.")
- Index(expression._truncated_label('ix_%s' % self._label),
- self, unique=self.unique)
- elif self.unique:
- if isinstance(self.unique, util.string_types):
- raise exc.ArgumentError(
- "The 'unique' keyword argument on Column is boolean "
- "only. To create unique constraints or indexes with a "
- "specific name, append an explicit UniqueConstraint to "
- "the Table's list of elements, or create an explicit "
- "Index object external to the Table.")
- table.append_constraint(UniqueConstraint(self.key))
-
- fk_key = (table.key, self.key)
- if fk_key in self.table.metadata._fk_memos:
- for fk in self.table.metadata._fk_memos[fk_key]:
- fk._set_remote_table(table)
-
- def _on_table_attach(self, fn):
- if self.table is not None:
- fn(self, self.table)
- event.listen(self, 'after_parent_attach', fn)
-
- def copy(self, **kw):
- """Create a copy of this ``Column``, unitialized.
-
- This is used in ``Table.tometadata``.
-
- """
-
- # Constraint objects plus non-constraint-bound ForeignKey objects
- args = \
- [c.copy(**kw) for c in self.constraints] + \
- [c.copy(**kw) for c in self.foreign_keys if not c.constraint]
-
- type_ = self.type
- if isinstance(type_, sqltypes.SchemaType):
- type_ = type_.copy(**kw)
-
- c = self._constructor(
- name=self.name,
- type_=type_,
- key=self.key,
- primary_key=self.primary_key,
- nullable=self.nullable,
- unique=self.unique,
- quote=self.quote,
- index=self.index,
- autoincrement=self.autoincrement,
- default=self.default,
- server_default=self.server_default,
- onupdate=self.onupdate,
- server_onupdate=self.server_onupdate,
- info=self.info,
- doc=self.doc,
- *args
- )
- c.dispatch._update(self.dispatch)
- return c
-
- def _make_proxy(self, selectable, name=None, key=None,
- name_is_truncatable=False, **kw):
- """Create a *proxy* for this column.
-
- This is a copy of this ``Column`` referenced by a different parent
- (such as an alias or select statement). The column should
- be used only in select scenarios, as its full DDL/default
- information is not transferred.
-
- """
- fk = [ForeignKey(f.column, _constraint=f.constraint)
- for f in self.foreign_keys]
- if name is None and self.name is None:
- raise exc.InvalidRequestError("Cannot initialize a sub-selectable"
- " with this Column object until it's 'name' has "
- "been assigned.")
- try:
- c = self._constructor(
- expression._as_truncated(name or self.name) if \
- name_is_truncatable else (name or self.name),
- self.type,
- key=key if key else name if name else self.key,
- primary_key=self.primary_key,
- nullable=self.nullable,
- quote=self.quote,
- _proxies=[self], *fk)
- except TypeError:
- util.raise_from_cause(
- TypeError(
- "Could not create a copy of this %r object. "
- "Ensure the class includes a _constructor() "
- "attribute or method which accepts the "
- "standard Column constructor arguments, or "
- "references the Column class itself." % self.__class__)
- )
-
- c.table = selectable
- selectable._columns.add(c)
- if selectable._is_clone_of is not None:
- c._is_clone_of = selectable._is_clone_of.columns[c.key]
- if self.primary_key:
- selectable.primary_key.add(c)
- c.dispatch.after_parent_attach(c, selectable)
- return c
-
- def get_children(self, schema_visitor=False, **kwargs):
- if schema_visitor:
- return [x for x in (self.default, self.onupdate)
- if x is not None] + \
- list(self.foreign_keys) + list(self.constraints)
- else:
- return expression.ColumnClause.get_children(self, **kwargs)
-
-
-class ForeignKey(SchemaItem):
- """Defines a dependency between two columns.
-
- ``ForeignKey`` is specified as an argument to a :class:`.Column` object,
- e.g.::
-
- t = Table("remote_table", metadata,
- Column("remote_id", ForeignKey("main_table.id"))
- )
-
- Note that ``ForeignKey`` is only a marker object that defines
- a dependency between two columns. The actual constraint
- is in all cases represented by the :class:`.ForeignKeyConstraint`
- object. This object will be generated automatically when
- a ``ForeignKey`` is associated with a :class:`.Column` which
- in turn is associated with a :class:`.Table`. Conversely,
- when :class:`.ForeignKeyConstraint` is applied to a :class:`.Table`,
- ``ForeignKey`` markers are automatically generated to be
- present on each associated :class:`.Column`, which are also
- associated with the constraint object.
-
- Note that you cannot define a "composite" foreign key constraint,
- that is a constraint between a grouping of multiple parent/child
- columns, using ``ForeignKey`` objects. To define this grouping,
- the :class:`.ForeignKeyConstraint` object must be used, and applied
- to the :class:`.Table`. The associated ``ForeignKey`` objects
- are created automatically.
-
- The ``ForeignKey`` objects associated with an individual
- :class:`.Column` object are available in the `foreign_keys` collection
- of that column.
-
- Further examples of foreign key configuration are in
- :ref:`metadata_foreignkeys`.
-
- """
-
- __visit_name__ = 'foreign_key'
-
- def __init__(self, column, _constraint=None, use_alter=False, name=None,
- onupdate=None, ondelete=None, deferrable=None,
- schema=None,
- initially=None, link_to_name=False, match=None):
- """
- Construct a column-level FOREIGN KEY.
-
- The :class:`.ForeignKey` object when constructed generates a
- :class:`.ForeignKeyConstraint` which is associated with the parent
- :class:`.Table` object's collection of constraints.
-
- :param column: A single target column for the key relationship. A
- :class:`.Column` object or a column name as a string:
- ``tablename.columnkey`` or ``schema.tablename.columnkey``.
- ``columnkey`` is the ``key`` which has been assigned to the column
- (defaults to the column name itself), unless ``link_to_name`` is
- ``True`` in which case the rendered name of the column is used.
-
- .. versionadded:: 0.7.4
- Note that if the schema name is not included, and the
- underlying :class:`.MetaData` has a "schema", that value will
- be used.
-
- :param name: Optional string. An in-database name for the key if
- `constraint` is not provided.
-
- :param onupdate: Optional string. If set, emit ON UPDATE <value> when
- issuing DDL for this constraint. Typical values include CASCADE,
- DELETE and RESTRICT.
-
- :param ondelete: Optional string. If set, emit ON DELETE <value> when
- issuing DDL for this constraint. Typical values include CASCADE,
- DELETE and RESTRICT.
-
- :param deferrable: Optional bool. If set, emit DEFERRABLE or NOT
- DEFERRABLE when issuing DDL for this constraint.
-
- :param initially: Optional string. If set, emit INITIALLY <value> when
- issuing DDL for this constraint.
-
- :param link_to_name: if True, the string name given in ``column`` is
- the rendered name of the referenced column, not its locally
- assigned ``key``.
-
- :param use_alter: passed to the underlying
- :class:`.ForeignKeyConstraint` to indicate the constraint should be
- generated/dropped externally from the CREATE TABLE/ DROP TABLE
- statement. See that classes' constructor for details.
-
- :param match: Optional string. If set, emit MATCH <value> when issuing
- DDL for this constraint. Typical values include SIMPLE, PARTIAL
- and FULL.
-
- """
-
- self._colspec = column
-
- # the linked ForeignKeyConstraint.
- # ForeignKey will create this when parent Column
- # is attached to a Table, *or* ForeignKeyConstraint
- # object passes itself in when creating ForeignKey
- # markers.
- self.constraint = _constraint
- self.parent = None
- self.use_alter = use_alter
- self.name = name
- self.onupdate = onupdate
- self.ondelete = ondelete
- self.deferrable = deferrable
- self.initially = initially
- self.link_to_name = link_to_name
- self.match = match
-
- def __repr__(self):
- return "ForeignKey(%r)" % self._get_colspec()
-
- def copy(self, schema=None):
- """Produce a copy of this :class:`.ForeignKey` object.
-
- The new :class:`.ForeignKey` will not be bound
- to any :class:`.Column`.
-
- This method is usually used by the internal
- copy procedures of :class:`.Column`, :class:`.Table`,
- and :class:`.MetaData`.
-
- :param schema: The returned :class:`.ForeignKey` will
- reference the original table and column name, qualified
- by the given string schema name.
-
- """
-
- fk = ForeignKey(
- self._get_colspec(schema=schema),
- use_alter=self.use_alter,
- name=self.name,
- onupdate=self.onupdate,
- ondelete=self.ondelete,
- deferrable=self.deferrable,
- initially=self.initially,
- link_to_name=self.link_to_name,
- match=self.match
- )
- fk.dispatch._update(self.dispatch)
- return fk
-
- def _get_colspec(self, schema=None):
- """Return a string based 'column specification' for this
- :class:`.ForeignKey`.
-
- This is usually the equivalent of the string-based "tablename.colname"
- argument first passed to the object's constructor.
-
- """
- if schema:
- return schema + "." + self.column.table.name + \
- "." + self.column.key
- elif isinstance(self._colspec, util.string_types):
- return self._colspec
- elif hasattr(self._colspec, '__clause_element__'):
- _column = self._colspec.__clause_element__()
- else:
- _column = self._colspec
-
- return "%s.%s" % (_column.table.fullname, _column.key)
-
-
- target_fullname = property(_get_colspec)
-
- def references(self, table):
- """Return True if the given :class:`.Table` is referenced by this
- :class:`.ForeignKey`."""
-
- return table.corresponding_column(self.column) is not None
-
- def get_referent(self, table):
- """Return the :class:`.Column` in the given :class:`.Table`
- referenced by this :class:`.ForeignKey`.
-
- Returns None if this :class:`.ForeignKey` does not reference the given
- :class:`.Table`.
-
- """
-
- return table.corresponding_column(self.column)
-
- @util.memoized_property
- def _column_tokens(self):
- """parse a string-based _colspec into its component parts."""
-
- m = self._colspec.split('.')
- if m is None:
- raise exc.ArgumentError(
- "Invalid foreign key column specification: %s" %
- self._colspec)
- if (len(m) == 1):
- tname = m.pop()
- colname = None
- else:
- colname = m.pop()
- tname = m.pop()
-
- # A FK between column 'bar' and table 'foo' can be
- # specified as 'foo', 'foo.bar', 'dbo.foo.bar',
- # 'otherdb.dbo.foo.bar'. Once we have the column name and
- # the table name, treat everything else as the schema
- # name. Some databases (e.g. Sybase) support
- # inter-database foreign keys. See tickets#1341 and --
- # indirectly related -- Ticket #594. This assumes that '.'
- # will never appear *within* any component of the FK.
-
- if (len(m) > 0):
- schema = '.'.join(m)
- else:
- schema = None
- return schema, tname, colname
-
- def _table_key(self):
- if isinstance(self._colspec, util.string_types):
- schema, tname, colname = self._column_tokens
- return _get_table_key(tname, schema)
- elif hasattr(self._colspec, '__clause_element__'):
- _column = self._colspec.__clause_element__()
- else:
- _column = self._colspec
-
- if _column.table is None:
- return None
- else:
- return _column.table.key
-
- def _resolve_col_tokens(self):
- if self.parent is None:
- raise exc.InvalidRequestError(
- "this ForeignKey object does not yet have a "
- "parent Column associated with it.")
-
- elif self.parent.table is None:
- raise exc.InvalidRequestError(
- "this ForeignKey's parent column is not yet associated "
- "with a Table.")
-
- parenttable = self.parent.table
-
- # assertion, can be commented out.
- # basically Column._make_proxy() sends the actual
- # target Column to the ForeignKey object, so the
- # string resolution here is never called.
- for c in self.parent.base_columns:
- if isinstance(c, Column):
- assert c.table is parenttable
- break
- else:
- assert False
- ######################
-
- schema, tname, colname = self._column_tokens
-
- if schema is None and parenttable.metadata.schema is not None:
- schema = parenttable.metadata.schema
-
- tablekey = _get_table_key(tname, schema)
- return parenttable, tablekey, colname
-
-
- def _link_to_col_by_colstring(self, parenttable, table, colname):
- if not hasattr(self.constraint, '_referred_table'):
- self.constraint._referred_table = table
- else:
- assert self.constraint._referred_table is table
-
- _column = None
- if colname is None:
- # colname is None in the case that ForeignKey argument
- # was specified as table name only, in which case we
- # match the column name to the same column on the
- # parent.
- key = self.parent
- _column = table.c.get(self.parent.key, None)
- elif self.link_to_name:
- key = colname
- for c in table.c:
- if c.name == colname:
- _column = c
- else:
- key = colname
- _column = table.c.get(colname, None)
-
- if _column is None:
- raise exc.NoReferencedColumnError(
- "Could not initialize target column for ForeignKey '%s' on table '%s': "
- "table '%s' has no column named '%s'" % (
- self._colspec, parenttable.name, table.name, key),
- table.name, key)
-
- self._set_target_column(_column)
-
- def _set_target_column(self, column):
- # propagate TypeEngine to parent if it didn't have one
- if isinstance(self.parent.type, sqltypes.NullType):
- self.parent.type = column.type
-
- # super-edgy case, if other FKs point to our column,
- # they'd get the type propagated out also.
- if isinstance(self.parent.table, Table):
- fk_key = (self.parent.table.key, self.parent.key)
- if fk_key in self.parent.table.metadata._fk_memos:
- for fk in self.parent.table.metadata._fk_memos[fk_key]:
- if isinstance(fk.parent.type, sqltypes.NullType):
- fk.parent.type = column.type
-
- self.column = column
-
- @util.memoized_property
- def column(self):
- """Return the target :class:`.Column` referenced by this
- :class:`.ForeignKey`.
-
- If no target column has been established, an exception
- is raised.
-
- .. versionchanged:: 0.9.0
- Foreign key target column resolution now occurs as soon as both
- the ForeignKey object and the remote Column to which it refers
- are both associated with the same MetaData object.
-
- """
-
- if isinstance(self._colspec, util.string_types):
-
- parenttable, tablekey, colname = self._resolve_col_tokens()
-
- if tablekey not in parenttable.metadata:
- raise exc.NoReferencedTableError(
- "Foreign key associated with column '%s' could not find "
- "table '%s' with which to generate a "
- "foreign key to target column '%s'" %
- (self.parent, tablekey, colname),
- tablekey)
- elif parenttable.key not in parenttable.metadata:
- raise exc.InvalidRequestError(
- "Table %s is no longer associated with its "
- "parent MetaData" % parenttable)
- else:
- raise exc.NoReferencedColumnError(
- "Could not initialize target column for "
- "ForeignKey '%s' on table '%s': "
- "table '%s' has no column named '%s'" % (
- self._colspec, parenttable.name, tablekey, colname),
- tablekey, colname)
- elif hasattr(self._colspec, '__clause_element__'):
- _column = self._colspec.__clause_element__()
- return _column
- else:
- _column = self._colspec
- return _column
-
- def _set_parent(self, column):
- if self.parent is not None and self.parent is not column:
- raise exc.InvalidRequestError(
- "This ForeignKey already has a parent !")
- self.parent = column
- self.parent.foreign_keys.add(self)
- self.parent._on_table_attach(self._set_table)
-
- def _set_remote_table(self, table):
- parenttable, tablekey, colname = self._resolve_col_tokens()
- self._link_to_col_by_colstring(parenttable, table, colname)
- self.constraint._validate_dest_table(table)
-
- def _remove_from_metadata(self, metadata):
- parenttable, table_key, colname = self._resolve_col_tokens()
- fk_key = (table_key, colname)
-
- if self in metadata._fk_memos[fk_key]:
- # TODO: no test coverage for self not in memos
- metadata._fk_memos[fk_key].remove(self)
-
- def _set_table(self, column, table):
- # standalone ForeignKey - create ForeignKeyConstraint
- # on the hosting Table when attached to the Table.
- if self.constraint is None and isinstance(table, Table):
- self.constraint = ForeignKeyConstraint(
- [], [], use_alter=self.use_alter, name=self.name,
- onupdate=self.onupdate, ondelete=self.ondelete,
- deferrable=self.deferrable, initially=self.initially,
- match=self.match,
- )
- self.constraint._elements[self.parent] = self
- self.constraint._set_parent_with_dispatch(table)
- table.foreign_keys.add(self)
-
- # set up remote ".column" attribute, or a note to pick it
- # up when the other Table/Column shows up
- if isinstance(self._colspec, util.string_types):
- parenttable, table_key, colname = self._resolve_col_tokens()
- fk_key = (table_key, colname)
- if table_key in parenttable.metadata.tables:
- table = parenttable.metadata.tables[table_key]
- try:
- self._link_to_col_by_colstring(parenttable, table, colname)
- except exc.NoReferencedColumnError:
- # this is OK, we'll try later
- pass
- parenttable.metadata._fk_memos[fk_key].append(self)
- elif hasattr(self._colspec, '__clause_element__'):
- _column = self._colspec.__clause_element__()
- self._set_target_column(_column)
- else:
- _column = self._colspec
- self._set_target_column(_column)
-
-
-
-class _NotAColumnExpr(object):
- def _not_a_column_expr(self):
- raise exc.InvalidRequestError(
- "This %s cannot be used directly "
- "as a column expression." % self.__class__.__name__)
-
- __clause_element__ = self_group = lambda self: self._not_a_column_expr()
- _from_objects = property(lambda self: self._not_a_column_expr())
-
-
-class DefaultGenerator(_NotAColumnExpr, SchemaItem):
- """Base class for column *default* values."""
-
- __visit_name__ = 'default_generator'
-
- is_sequence = False
- is_server_default = False
- column = None
-
- def __init__(self, for_update=False):
- self.for_update = for_update
-
- def _set_parent(self, column):
- self.column = column
- if self.for_update:
- self.column.onupdate = self
- else:
- self.column.default = self
-
- def execute(self, bind=None, **kwargs):
- if bind is None:
- bind = _bind_or_error(self)
- return bind._execute_default(self, **kwargs)
-
- @property
- def bind(self):
- """Return the connectable associated with this default."""
- if getattr(self, 'column', None) is not None:
- return self.column.table.bind
- else:
- return None
-
-
-class ColumnDefault(DefaultGenerator):
- """A plain default value on a column.
-
- This could correspond to a constant, a callable function,
- or a SQL clause.
-
- :class:`.ColumnDefault` is generated automatically
- whenever the ``default``, ``onupdate`` arguments of
- :class:`.Column` are used. A :class:`.ColumnDefault`
- can be passed positionally as well.
-
- For example, the following::
-
- Column('foo', Integer, default=50)
-
- Is equivalent to::
-
- Column('foo', Integer, ColumnDefault(50))
-
-
- """
-
- def __init__(self, arg, **kwargs):
- """"Construct a new :class:`.ColumnDefault`.
-
-
- :param arg: argument representing the default value.
- May be one of the following:
-
- * a plain non-callable Python value, such as a
- string, integer, boolean, or other simple type.
- The default value will be used as is each time.
- * a SQL expression, that is one which derives from
- :class:`.ColumnElement`. The SQL expression will
- be rendered into the INSERT or UPDATE statement,
- or in the case of a primary key column when
- RETURNING is not used may be
- pre-executed before an INSERT within a SELECT.
- * A Python callable. The function will be invoked for each
- new row subject to an INSERT or UPDATE.
- The callable must accept exactly
- zero or one positional arguments. The one-argument form
- will receive an instance of the :class:`.ExecutionContext`,
- which provides contextual information as to the current
- :class:`.Connection` in use as well as the current
- statement and parameters.
-
- """
- super(ColumnDefault, self).__init__(**kwargs)
- if isinstance(arg, FetchedValue):
- raise exc.ArgumentError(
- "ColumnDefault may not be a server-side default type.")
- if util.callable(arg):
- arg = self._maybe_wrap_callable(arg)
- self.arg = arg
-
- @util.memoized_property
- def is_callable(self):
- return util.callable(self.arg)
-
- @util.memoized_property
- def is_clause_element(self):
- return isinstance(self.arg, expression.ClauseElement)
-
- @util.memoized_property
- def is_scalar(self):
- return not self.is_callable and \
- not self.is_clause_element and \
- not self.is_sequence
-
- def _maybe_wrap_callable(self, fn):
- """Wrap callables that don't accept a context.
-
- The alternative here is to require that
- a simple callable passed to "default" would need
- to be of the form "default=lambda ctx: datetime.now".
- That is the more "correct" way to go, but the case
- of using a zero-arg callable for "default" is so
- much more prominent than the context-specific one
- I'm having trouble justifying putting that inconvenience
- on everyone.
-
- """
- if inspect.isfunction(fn):
- inspectable = fn
- elif inspect.isclass(fn):
- inspectable = fn.__init__
- elif hasattr(fn, '__call__'):
- inspectable = fn.__call__
- else:
- # probably not inspectable, try anyways.
- inspectable = fn
- try:
- argspec = inspect.getargspec(inspectable)
- except TypeError:
- return lambda ctx: fn()
-
- defaulted = argspec[3] is not None and len(argspec[3]) or 0
- positionals = len(argspec[0]) - defaulted
-
- # Py3K compat - no unbound methods
- if inspect.ismethod(inspectable) or inspect.isclass(fn):
- positionals -= 1
-
- if positionals == 0:
- return lambda ctx: fn()
- elif positionals == 1:
- return fn
- else:
- raise exc.ArgumentError(
- "ColumnDefault Python function takes zero or one "
- "positional arguments")
-
- def _visit_name(self):
- if self.for_update:
- return "column_onupdate"
- else:
- return "column_default"
- __visit_name__ = property(_visit_name)
-
- def __repr__(self):
- return "ColumnDefault(%r)" % self.arg
-
-
-class Sequence(DefaultGenerator):
- """Represents a named database sequence.
-
- The :class:`.Sequence` object represents the name and configurational
- parameters of a database sequence. It also represents
- a construct that can be "executed" by a SQLAlchemy :class:`.Engine`
- or :class:`.Connection`, rendering the appropriate "next value" function
- for the target database and returning a result.
-
- The :class:`.Sequence` is typically associated with a primary key column::
-
- some_table = Table('some_table', metadata,
- Column('id', Integer, Sequence('some_table_seq'), primary_key=True)
- )
-
- When CREATE TABLE is emitted for the above :class:`.Table`, if the
- target platform supports sequences, a CREATE SEQUENCE statement will
- be emitted as well. For platforms that don't support sequences,
- the :class:`.Sequence` construct is ignored.
-
- See also: :class:`.CreateSequence` :class:`.DropSequence`
-
- """
-
- __visit_name__ = 'sequence'
-
- is_sequence = True
-
- def __init__(self, name, start=None, increment=None, schema=None,
- optional=False, quote=None, metadata=None,
- quote_schema=None,
- for_update=False):
- """Construct a :class:`.Sequence` object.
-
- :param name: The name of the sequence.
- :param start: the starting index of the sequence. This value is
- used when the CREATE SEQUENCE command is emitted to the database
- as the value of the "START WITH" clause. If ``None``, the
- clause is omitted, which on most platforms indicates a starting
- value of 1.
- :param increment: the increment value of the sequence. This
- value is used when the CREATE SEQUENCE command is emitted to
- the database as the value of the "INCREMENT BY" clause. If ``None``,
- the clause is omitted, which on most platforms indicates an
- increment of 1.
- :param schema: Optional schema name for the sequence, if located
- in a schema other than the default.
- :param optional: boolean value, when ``True``, indicates that this
- :class:`.Sequence` object only needs to be explicitly generated
- on backends that don't provide another way to generate primary
- key identifiers. Currently, it essentially means, "don't create
- this sequence on the Postgresql backend, where the SERIAL keyword
- creates a sequence for us automatically".
- :param quote: boolean value, when ``True`` or ``False``, explicitly
- forces quoting of the schema name on or off. When left at its
- default of ``None``, normal quoting rules based on casing and reserved
- words take place.
- :param metadata: optional :class:`.MetaData` object which will be
- associated with this :class:`.Sequence`. A :class:`.Sequence`
- that is associated with a :class:`.MetaData` gains access to the
- ``bind`` of that :class:`.MetaData`, meaning the
- :meth:`.Sequence.create` and :meth:`.Sequence.drop` methods will
- make usage of that engine automatically.
-
- .. versionchanged:: 0.7
- Additionally, the appropriate CREATE SEQUENCE/
- DROP SEQUENCE DDL commands will be emitted corresponding to this
- :class:`.Sequence` when :meth:`.MetaData.create_all` and
- :meth:`.MetaData.drop_all` are invoked.
-
- Note that when a :class:`.Sequence` is applied to a :class:`.Column`,
- the :class:`.Sequence` is automatically associated with the
- :class:`.MetaData` object of that column's parent :class:`.Table`,
- when that association is made. The :class:`.Sequence` will then
- be subject to automatic CREATE SEQUENCE/DROP SEQUENCE corresponding
- to when the :class:`.Table` object itself is created or dropped,
- rather than that of the :class:`.MetaData` object overall.
- :param for_update: Indicates this :class:`.Sequence`, when associated
- with a :class:`.Column`, should be invoked for UPDATE statements
- on that column's table, rather than for INSERT statements, when
- no value is otherwise present for that column in the statement.
-
- """
- super(Sequence, self).__init__(for_update=for_update)
- self.name = name
- self.start = start
- self.increment = increment
- self.optional = optional
- self.quote = quote
- if metadata is not None and schema is None and metadata.schema:
- self.schema = schema = metadata.schema
- self.quote_schema = metadata.quote_schema
- else:
- self.schema = schema
- self.quote_schema = quote_schema
- self.metadata = metadata
- self._key = _get_table_key(name, schema)
- if metadata:
- self._set_metadata(metadata)
-
- @util.memoized_property
- def is_callable(self):
- return False
-
- @util.memoized_property
- def is_clause_element(self):
- return False
-
- def next_value(self):
- """Return a :class:`.next_value` function element
- which will render the appropriate increment function
- for this :class:`.Sequence` within any SQL expression.
-
- """
- return expression.func.next_value(self, bind=self.bind)
-
- def _set_parent(self, column):
- super(Sequence, self)._set_parent(column)
- column._on_table_attach(self._set_table)
-
- def _set_table(self, column, table):
- self._set_metadata(table.metadata)
-
- def _set_metadata(self, metadata):
- self.metadata = metadata
- self.metadata._sequences[self._key] = self
-
- @property
- def bind(self):
- if self.metadata:
- return self.metadata.bind
- else:
- return None
-
- def create(self, bind=None, checkfirst=True):
- """Creates this sequence in the database."""
-
- if bind is None:
- bind = _bind_or_error(self)
- bind._run_visitor(ddl.SchemaGenerator,
- self,
- checkfirst=checkfirst)
-
- def drop(self, bind=None, checkfirst=True):
- """Drops this sequence from the database."""
-
- if bind is None:
- bind = _bind_or_error(self)
- bind._run_visitor(ddl.SchemaDropper,
- self,
- checkfirst=checkfirst)
-
- def _not_a_column_expr(self):
- raise exc.InvalidRequestError(
- "This %s cannot be used directly "
- "as a column expression. Use func.next_value(sequence) "
- "to produce a 'next value' function that's usable "
- "as a column element."
- % self.__class__.__name__)
-
-
-class FetchedValue(_NotAColumnExpr, events.SchemaEventTarget):
- """A marker for a transparent database-side default.
-
- Use :class:`.FetchedValue` when the database is configured
- to provide some automatic default for a column.
-
- E.g.::
-
- Column('foo', Integer, FetchedValue())
-
- Would indicate that some trigger or default generator
- will create a new value for the ``foo`` column during an
- INSERT.
-
- .. seealso::
-
- :ref:`triggered_columns`
-
- """
- is_server_default = True
- reflected = False
- has_argument = False
-
- def __init__(self, for_update=False):
- self.for_update = for_update
-
- def _as_for_update(self, for_update):
- if for_update == self.for_update:
- return self
- else:
- return self._clone(for_update)
-
- def _clone(self, for_update):
- n = self.__class__.__new__(self.__class__)
- n.__dict__.update(self.__dict__)
- n.__dict__.pop('column', None)
- n.for_update = for_update
- return n
-
- def _set_parent(self, column):
- self.column = column
- if self.for_update:
- self.column.server_onupdate = self
- else:
- self.column.server_default = self
-
- def __repr__(self):
- return util.generic_repr(self)
-
-inspection._self_inspects(FetchedValue)
-
-
-class DefaultClause(FetchedValue):
- """A DDL-specified DEFAULT column value.
-
- :class:`.DefaultClause` is a :class:`.FetchedValue`
- that also generates a "DEFAULT" clause when
- "CREATE TABLE" is emitted.
-
- :class:`.DefaultClause` is generated automatically
- whenever the ``server_default``, ``server_onupdate`` arguments of
- :class:`.Column` are used. A :class:`.DefaultClause`
- can be passed positionally as well.
-
- For example, the following::
-
- Column('foo', Integer, server_default="50")
-
- Is equivalent to::
-
- Column('foo', Integer, DefaultClause("50"))
-
- """
-
- has_argument = True
-
- def __init__(self, arg, for_update=False, _reflected=False):
- util.assert_arg_type(arg, (util.string_types[0],
- expression.ClauseElement,
- expression.TextClause), 'arg')
- super(DefaultClause, self).__init__(for_update)
- self.arg = arg
- self.reflected = _reflected
-
- def __repr__(self):
- return "DefaultClause(%r, for_update=%r)" % \
- (self.arg, self.for_update)
-
-
-class PassiveDefault(DefaultClause):
- """A DDL-specified DEFAULT column value.
-
- .. deprecated:: 0.6
- :class:`.PassiveDefault` is deprecated.
- Use :class:`.DefaultClause`.
- """
- @util.deprecated("0.6",
- ":class:`.PassiveDefault` is deprecated. "
- "Use :class:`.DefaultClause`.",
- False)
- def __init__(self, *arg, **kw):
- DefaultClause.__init__(self, *arg, **kw)
-
-
-class Constraint(SchemaItem):
- """A table-level SQL constraint."""
-
- __visit_name__ = 'constraint'
-
- def __init__(self, name=None, deferrable=None, initially=None,
- _create_rule=None,
- **kw):
- """Create a SQL constraint.
-
- :param name:
- Optional, the in-database name of this ``Constraint``.
-
- :param deferrable:
- Optional bool. If set, emit DEFERRABLE or NOT DEFERRABLE when
- issuing DDL for this constraint.
-
- :param initially:
- Optional string. If set, emit INITIALLY <value> when issuing DDL
- for this constraint.
-
- :param _create_rule:
- a callable which is passed the DDLCompiler object during
- compilation. Returns True or False to signal inline generation of
- this Constraint.
-
- The AddConstraint and DropConstraint DDL constructs provide
- DDLElement's more comprehensive "conditional DDL" approach that is
- passed a database connection when DDL is being issued. _create_rule
- is instead called during any CREATE TABLE compilation, where there
- may not be any transaction/connection in progress. However, it
- allows conditional compilation of the constraint even for backends
- which do not support addition of constraints through ALTER TABLE,
- which currently includes SQLite.
-
- _create_rule is used by some types to create constraints.
- Currently, its call signature is subject to change at any time.
-
- :param \**kwargs:
- Dialect-specific keyword parameters, see the documentation
- for various dialects and constraints regarding options here.
-
- """
-
- self.name = name
- self.deferrable = deferrable
- self.initially = initially
- self._create_rule = _create_rule
- util.set_creation_order(self)
- _validate_dialect_kwargs(kw, self.__class__.__name__)
- self.kwargs = kw
-
- @property
- def table(self):
- try:
- if isinstance(self.parent, Table):
- return self.parent
- except AttributeError:
- pass
- raise exc.InvalidRequestError(
- "This constraint is not bound to a table. Did you "
- "mean to call table.append_constraint(constraint) ?")
-
- def _set_parent(self, parent):
- self.parent = parent
- parent.constraints.add(self)
-
- def copy(self, **kw):
- raise NotImplementedError()
-
-
-class ColumnCollectionMixin(object):
- def __init__(self, *columns):
- self.columns = expression.ColumnCollection()
- self._pending_colargs = [_to_schema_column_or_string(c)
- for c in columns]
- if self._pending_colargs and \
- isinstance(self._pending_colargs[0], Column) and \
- isinstance(self._pending_colargs[0].table, Table):
- self._set_parent_with_dispatch(self._pending_colargs[0].table)
-
- def _set_parent(self, table):
- for col in self._pending_colargs:
- if isinstance(col, util.string_types):
- col = table.c[col]
- self.columns.add(col)
-
-
-class ColumnCollectionConstraint(ColumnCollectionMixin, Constraint):
- """A constraint that proxies a ColumnCollection."""
-
- def __init__(self, *columns, **kw):
- """
- :param \*columns:
- A sequence of column names or Column objects.
-
- :param name:
- Optional, the in-database name of this constraint.
-
- :param deferrable:
- Optional bool. If set, emit DEFERRABLE or NOT DEFERRABLE when
- issuing DDL for this constraint.
-
- :param initially:
- Optional string. If set, emit INITIALLY <value> when issuing DDL
- for this constraint.
-
- """
- ColumnCollectionMixin.__init__(self, *columns)
- Constraint.__init__(self, **kw)
-
- def _set_parent(self, table):
- ColumnCollectionMixin._set_parent(self, table)
- Constraint._set_parent(self, table)
-
- def __contains__(self, x):
- return x in self.columns
-
- def copy(self, **kw):
- c = self.__class__(name=self.name, deferrable=self.deferrable,
- initially=self.initially, *self.columns.keys())
- c.dispatch._update(self.dispatch)
- return c
-
- def contains_column(self, col):
- return self.columns.contains_column(col)
-
- def __iter__(self):
- # inlining of
- # return iter(self.columns)
- # ColumnCollection->OrderedProperties->OrderedDict
- ordered_dict = self.columns._data
- return (ordered_dict[key] for key in ordered_dict._list)
-
- def __len__(self):
- return len(self.columns._data)
-
-
-class CheckConstraint(Constraint):
- """A table- or column-level CHECK constraint.
-
- Can be included in the definition of a Table or Column.
- """
-
- def __init__(self, sqltext, name=None, deferrable=None,
- initially=None, table=None, _create_rule=None,
- _autoattach=True):
- """Construct a CHECK constraint.
-
- :param sqltext:
- A string containing the constraint definition, which will be used
- verbatim, or a SQL expression construct.
-
- :param name:
- Optional, the in-database name of the constraint.
-
- :param deferrable:
- Optional bool. If set, emit DEFERRABLE or NOT DEFERRABLE when
- issuing DDL for this constraint.
-
- :param initially:
- Optional string. If set, emit INITIALLY <value> when issuing DDL
- for this constraint.
-
- """
-
- super(CheckConstraint, self).\
- __init__(name, deferrable, initially, _create_rule)
- self.sqltext = expression._literal_as_text(sqltext)
- if table is not None:
- self._set_parent_with_dispatch(table)
- elif _autoattach:
- cols = sqlutil.find_columns(self.sqltext)
- tables = set([c.table for c in cols
- if isinstance(c.table, Table)])
- if len(tables) == 1:
- self._set_parent_with_dispatch(
- tables.pop())
-
- def __visit_name__(self):
- if isinstance(self.parent, Table):
- return "check_constraint"
- else:
- return "column_check_constraint"
- __visit_name__ = property(__visit_name__)
-
- def copy(self, target_table=None, **kw):
- if target_table is not None:
- def replace(col):
- if self.table.c.contains_column(col):
- return target_table.c[col.key]
- else:
- return None
- sqltext = visitors.replacement_traverse(self.sqltext, {}, replace)
- else:
- sqltext = self.sqltext
- c = CheckConstraint(sqltext,
- name=self.name,
- initially=self.initially,
- deferrable=self.deferrable,
- _create_rule=self._create_rule,
- table=target_table,
- _autoattach=False)
- c.dispatch._update(self.dispatch)
- return c
-
-
-class ForeignKeyConstraint(Constraint):
- """A table-level FOREIGN KEY constraint.
-
- Defines a single column or composite FOREIGN KEY ... REFERENCES
- constraint. For a no-frills, single column foreign key, adding a
- :class:`.ForeignKey` to the definition of a :class:`.Column` is a shorthand
- equivalent for an unnamed, single column :class:`.ForeignKeyConstraint`.
-
- Examples of foreign key configuration are in :ref:`metadata_foreignkeys`.
-
- """
- __visit_name__ = 'foreign_key_constraint'
-
- def __init__(self, columns, refcolumns, name=None, onupdate=None,
- ondelete=None, deferrable=None, initially=None, use_alter=False,
- link_to_name=False, match=None, table=None):
- """Construct a composite-capable FOREIGN KEY.
-
- :param columns: A sequence of local column names. The named columns
- must be defined and present in the parent Table. The names should
- match the ``key`` given to each column (defaults to the name) unless
- ``link_to_name`` is True.
-
- :param refcolumns: A sequence of foreign column names or Column
- objects. The columns must all be located within the same Table.
-
- :param name: Optional, the in-database name of the key.
-
- :param onupdate: Optional string. If set, emit ON UPDATE <value> when
- issuing DDL for this constraint. Typical values include CASCADE,
- DELETE and RESTRICT.
-
- :param ondelete: Optional string. If set, emit ON DELETE <value> when
- issuing DDL for this constraint. Typical values include CASCADE,
- DELETE and RESTRICT.
-
- :param deferrable: Optional bool. If set, emit DEFERRABLE or NOT
- DEFERRABLE when issuing DDL for this constraint.
-
- :param initially: Optional string. If set, emit INITIALLY <value> when
- issuing DDL for this constraint.
-
- :param link_to_name: if True, the string name given in ``column`` is
- the rendered name of the referenced column, not its locally assigned
- ``key``.
-
- :param use_alter: If True, do not emit the DDL for this constraint as
- part of the CREATE TABLE definition. Instead, generate it via an
- ALTER TABLE statement issued after the full collection of tables
- have been created, and drop it via an ALTER TABLE statement before
- the full collection of tables are dropped. This is shorthand for the
- usage of :class:`.AddConstraint` and :class:`.DropConstraint` applied
- as "after-create" and "before-drop" events on the MetaData object.
- This is normally used to generate/drop constraints on objects that
- are mutually dependent on each other.
-
- :param match: Optional string. If set, emit MATCH <value> when issuing
- DDL for this constraint. Typical values include SIMPLE, PARTIAL
- and FULL.
-
- """
- super(ForeignKeyConstraint, self).\
- __init__(name, deferrable, initially)
-
- self.onupdate = onupdate
- self.ondelete = ondelete
- self.link_to_name = link_to_name
- if self.name is None and use_alter:
- raise exc.ArgumentError("Alterable Constraint requires a name")
- self.use_alter = use_alter
- self.match = match
-
- self._elements = util.OrderedDict()
-
- # standalone ForeignKeyConstraint - create
- # associated ForeignKey objects which will be applied to hosted
- # Column objects (in col.foreign_keys), either now or when attached
- # to the Table for string-specified names
- for col, refcol in zip(columns, refcolumns):
- self._elements[col] = ForeignKey(
- refcol,
- _constraint=self,
- name=self.name,
- onupdate=self.onupdate,
- ondelete=self.ondelete,
- use_alter=self.use_alter,
- link_to_name=self.link_to_name,
- match=self.match
- )
-
- if table is not None:
- self._set_parent_with_dispatch(table)
- elif columns and \
- isinstance(columns[0], Column) and \
- columns[0].table is not None:
- self._set_parent_with_dispatch(columns[0].table)
-
- def _validate_dest_table(self, table):
- table_keys = set([elem._table_key() for elem in self._elements.values()])
- if None not in table_keys and len(table_keys) > 1:
- elem0, elem1 = sorted(table_keys)[0:2]
- raise exc.ArgumentError(
- 'ForeignKeyConstraint on %s(%s) refers to '
- 'multiple remote tables: %s and %s' % (
- table.fullname,
- self._col_description,
- elem0,
- elem1
- ))
-
- @property
- def _col_description(self):
- return ", ".join(self._elements)
-
- @property
- def columns(self):
- return list(self._elements)
-
- @property
- def elements(self):
- return list(self._elements.values())
-
- def _set_parent(self, table):
- super(ForeignKeyConstraint, self)._set_parent(table)
-
- self._validate_dest_table(table)
-
- for col, fk in self._elements.items():
- # string-specified column names now get
- # resolved to Column objects
- if isinstance(col, util.string_types):
- try:
- col = table.c[col]
- except KeyError:
- raise exc.ArgumentError(
- "Can't create ForeignKeyConstraint "
- "on table '%s': no column "
- "named '%s' is present." % (table.description, col))
-
- if not hasattr(fk, 'parent') or \
- fk.parent is not col:
- fk._set_parent_with_dispatch(col)
-
- if self.use_alter:
- def supports_alter(ddl, event, schema_item, bind, **kw):
- return table in set(kw['tables']) and \
- bind.dialect.supports_alter
-
- event.listen(table.metadata, "after_create",
- AddConstraint(self, on=supports_alter))
- event.listen(table.metadata, "before_drop",
- DropConstraint(self, on=supports_alter))
-
- def copy(self, schema=None, **kw):
- fkc = ForeignKeyConstraint(
- [x.parent.key for x in self._elements.values()],
- [x._get_colspec(schema=schema) for x in self._elements.values()],
- name=self.name,
- onupdate=self.onupdate,
- ondelete=self.ondelete,
- use_alter=self.use_alter,
- deferrable=self.deferrable,
- initially=self.initially,
- link_to_name=self.link_to_name,
- match=self.match
- )
- fkc.dispatch._update(self.dispatch)
- return fkc
-
-
-class PrimaryKeyConstraint(ColumnCollectionConstraint):
- """A table-level PRIMARY KEY constraint.
-
- Defines a single column or composite PRIMARY KEY constraint. For a
- no-frills primary key, adding ``primary_key=True`` to one or more
- ``Column`` definitions is a shorthand equivalent for an unnamed single- or
- multiple-column PrimaryKeyConstraint.
- """
-
- __visit_name__ = 'primary_key_constraint'
-
- def _set_parent(self, table):
- super(PrimaryKeyConstraint, self)._set_parent(table)
-
- if table.primary_key in table.constraints:
- table.constraints.remove(table.primary_key)
- table.primary_key = self
- table.constraints.add(self)
-
- for c in self.columns:
- c.primary_key = True
-
- def _replace(self, col):
- self.columns.replace(col)
-
-
-class UniqueConstraint(ColumnCollectionConstraint):
- """A table-level UNIQUE constraint.
-
- Defines a single column or composite UNIQUE constraint. For a no-frills,
- single column constraint, adding ``unique=True`` to the ``Column``
- definition is a shorthand equivalent for an unnamed, single column
- UniqueConstraint.
- """
-
- __visit_name__ = 'unique_constraint'
-
-
-class Index(ColumnCollectionMixin, SchemaItem):
- """A table-level INDEX.
-
- Defines a composite (one or more column) INDEX.
-
- E.g.::
-
- sometable = Table("sometable", metadata,
- Column("name", String(50)),
- Column("address", String(100))
- )
-
- Index("some_index", sometable.c.name)
-
- For a no-frills, single column index, adding
- :class:`.Column` also supports ``index=True``::
-
- sometable = Table("sometable", metadata,
- Column("name", String(50), index=True)
- )
-
- For a composite index, multiple columns can be specified::
-
- Index("some_index", sometable.c.name, sometable.c.address)
-
- Functional indexes are supported as well, keeping in mind that at least
- one :class:`.Column` must be present::
-
- Index("some_index", func.lower(sometable.c.name))
-
- .. versionadded:: 0.8 support for functional and expression-based indexes.
-
- .. seealso::
-
- :ref:`schema_indexes` - General information on :class:`.Index`.
-
- :ref:`postgresql_indexes` - PostgreSQL-specific options available for the
- :class:`.Index` construct.
-
- :ref:`mysql_indexes` - MySQL-specific options available for the
- :class:`.Index` construct.
-
- :ref:`mssql_indexes` - MSSQL-specific options available for the
- :class:`.Index` construct.
-
- """
-
- __visit_name__ = 'index'
-
- def __init__(self, name, *expressions, **kw):
- """Construct an index object.
-
- :param name:
- The name of the index
-
- :param \*expressions:
- Column or SQL expressions.
-
- :param unique:
- Defaults to False: create a unique index.
-
- :param \**kw:
- Other keyword arguments may be interpreted by specific dialects.
-
- """
- self.table = None
-
- columns = []
- for expr in expressions:
- if not isinstance(expr, expression.ClauseElement):
- columns.append(expr)
- else:
- cols = []
- visitors.traverse(expr, {}, {'column': cols.append})
- if cols:
- columns.append(cols[0])
- else:
- columns.append(expr)
-
- self.expressions = expressions
-
- # will call _set_parent() if table-bound column
- # objects are present
- ColumnCollectionMixin.__init__(self, *columns)
-
- self.name = name
- self.unique = kw.pop('unique', False)
- self.kwargs = kw
-
- def _set_parent(self, table):
- ColumnCollectionMixin._set_parent(self, table)
-
- if self.table is not None and table is not self.table:
- raise exc.ArgumentError(
- "Index '%s' is against table '%s', and "
- "cannot be associated with table '%s'." % (
- self.name,
- self.table.description,
- table.description
- )
- )
- self.table = table
- for c in self.columns:
- if c.table != self.table:
- raise exc.ArgumentError(
- "Column '%s' is not part of table '%s'." %
- (c, self.table.description)
- )
- table.indexes.add(self)
-
- self.expressions = [
- expr if isinstance(expr, expression.ClauseElement)
- else colexpr
- for expr, colexpr in zip(self.expressions, self.columns)
- ]
-
- @property
- def bind(self):
- """Return the connectable associated with this Index."""
-
- return self.table.bind
-
- def create(self, bind=None):
- """Issue a ``CREATE`` statement for this
- :class:`.Index`, using the given :class:`.Connectable`
- for connectivity.
-
- See also :meth:`.MetaData.create_all`.
-
- """
- if bind is None:
- bind = _bind_or_error(self)
- bind._run_visitor(ddl.SchemaGenerator, self)
- return self
-
- def drop(self, bind=None):
- """Issue a ``DROP`` statement for this
- :class:`.Index`, using the given :class:`.Connectable`
- for connectivity.
-
- See also :meth:`.MetaData.drop_all`.
-
- """
- if bind is None:
- bind = _bind_or_error(self)
- bind._run_visitor(ddl.SchemaDropper, self)
-
- def __repr__(self):
- return 'Index(%s)' % (
- ", ".join(
- [repr(self.name)] +
- [repr(c) for c in self.columns] +
- (self.unique and ["unique=True"] or [])
- ))
-
-
-class MetaData(SchemaItem):
- """A collection of :class:`.Table` objects and their associated schema
- constructs.
-
- Holds a collection of :class:`.Table` objects as well as
- an optional binding to an :class:`.Engine` or
- :class:`.Connection`. If bound, the :class:`.Table` objects
- in the collection and their columns may participate in implicit SQL
- execution.
-
- The :class:`.Table` objects themselves are stored in the
- ``metadata.tables`` dictionary.
-
- The ``bind`` property may be assigned to dynamically. A common pattern is
- to start unbound and then bind later when an engine is available::
-
- metadata = MetaData()
- # define tables
- Table('mytable', metadata, ...)
- # connect to an engine later, perhaps after loading a URL from a
- # configuration file
- metadata.bind = an_engine
-
- MetaData is a thread-safe object after tables have been explicitly defined
- or loaded via reflection.
-
- See also:
-
- :ref:`metadata_describing` - Introduction to database metadata
-
- .. index::
- single: thread safety; MetaData
-
- """
-
- __visit_name__ = 'metadata'
-
- def __init__(self, bind=None, reflect=False, schema=None,
- quote_schema=None):
- """Create a new MetaData object.
-
- :param bind:
- An Engine or Connection to bind to. May also be a string or URL
- instance, these are passed to create_engine() and this MetaData will
- be bound to the resulting engine.
-
- :param reflect:
- Optional, automatically load all tables from the bound database.
- Defaults to False. ``bind`` is required when this option is set.
-
- .. deprecated:: 0.8
- Please use the :meth:`.MetaData.reflect` method.
-
- :param schema:
- The default schema to use for the :class:`.Table`,
- :class:`.Sequence`, and other objects associated with this
- :class:`.MetaData`. Defaults to ``None``.
-
- :param quote_schema:
- Sets the ``quote_schema`` flag for those :class:`.Table`,
- :class:`.Sequence`, and other objects which make usage of the
- local ``schema`` name.
-
- .. versionadded:: 0.7.4
- ``schema`` and ``quote_schema`` parameters.
-
- """
- self.tables = util.immutabledict()
- self.schema = schema
- self.quote_schema = quote_schema
- self._schemas = set()
- self._sequences = {}
- self._fk_memos = collections.defaultdict(list)
-
- self.bind = bind
- if reflect:
- util.warn("reflect=True is deprecate; please "
- "use the reflect() method.")
- if not bind:
- raise exc.ArgumentError(
- "A bind must be supplied in conjunction "
- "with reflect=True")
- self.reflect()
-
- def __repr__(self):
- return 'MetaData(bind=%r)' % self.bind
-
- def __contains__(self, table_or_key):
- if not isinstance(table_or_key, util.string_types):
- table_or_key = table_or_key.key
- return table_or_key in self.tables
-
- def _add_table(self, name, schema, table):
- key = _get_table_key(name, schema)
- dict.__setitem__(self.tables, key, table)
- if schema:
- self._schemas.add(schema)
-
-
-
- def _remove_table(self, name, schema):
- key = _get_table_key(name, schema)
- removed = dict.pop(self.tables, key, None)
- if removed is not None:
- for fk in removed.foreign_keys:
- fk._remove_from_metadata(self)
- if self._schemas:
- self._schemas = set([t.schema
- for t in self.tables.values()
- if t.schema is not None])
-
-
- def __getstate__(self):
- return {'tables': self.tables,
- 'schema': self.schema,
- 'quote_schema': self.quote_schema,
- 'schemas': self._schemas,
- 'sequences': self._sequences,
- 'fk_memos': self._fk_memos}
-
- def __setstate__(self, state):
- self.tables = state['tables']
- self.schema = state['schema']
- self.quote_schema = state['quote_schema']
- self._bind = None
- self._sequences = state['sequences']
- self._schemas = state['schemas']
- self._fk_memos = state['fk_memos']
-
- def is_bound(self):
- """True if this MetaData is bound to an Engine or Connection."""
-
- return self._bind is not None
-
- def bind(self):
- """An :class:`.Engine` or :class:`.Connection` to which this
- :class:`.MetaData` is bound.
-
- Typically, a :class:`.Engine` is assigned to this attribute
- so that "implicit execution" may be used, or alternatively
- as a means of providing engine binding information to an
- ORM :class:`.Session` object::
-
- engine = create_engine("someurl://")
- metadata.bind = engine
-
- .. seealso::
-
- :ref:`dbengine_implicit` - background on "bound metadata"
-
- """
- return self._bind
-
- def _bind_to(self, bind):
- """Bind this MetaData to an Engine, Connection, string or URL."""
-
- if isinstance(bind, util.string_types + (url.URL, )):
- self._bind = sqlalchemy.create_engine(bind)
- else:
- self._bind = bind
- bind = property(bind, _bind_to)
-
- def clear(self):
- """Clear all Table objects from this MetaData."""
-
- dict.clear(self.tables)
- self._schemas.clear()
- self._fk_memos.clear()
-
- def remove(self, table):
- """Remove the given Table object from this MetaData."""
-
- self._remove_table(table.name, table.schema)
-
- @property
- def sorted_tables(self):
- """Returns a list of :class:`.Table` objects sorted in order of
- foreign key dependency.
-
- The sorting will place :class:`.Table` objects that have dependencies
- first, before the dependencies themselves, representing the
- order in which they can be created. To get the order in which
- the tables would be dropped, use the ``reversed()`` Python built-in.
-
- .. seealso::
-
- :meth:`.Inspector.sorted_tables`
-
- """
- return sqlutil.sort_tables(self.tables.values())
-
- def reflect(self, bind=None, schema=None, views=False, only=None):
- """Load all available table definitions from the database.
-
- Automatically creates ``Table`` entries in this ``MetaData`` for any
- table available in the database but not yet present in the
- ``MetaData``. May be called multiple times to pick up tables recently
- added to the database, however no special action is taken if a table
- in this ``MetaData`` no longer exists in the database.
-
- :param bind:
- A :class:`.Connectable` used to access the database; if None, uses
- the existing bind on this ``MetaData``, if any.
-
- :param schema:
- Optional, query and reflect tables from an alterate schema.
- If None, the schema associated with this :class:`.MetaData`
- is used, if any.
-
- :param views:
- If True, also reflect views.
-
- :param only:
- Optional. Load only a sub-set of available named tables. May be
- specified as a sequence of names or a callable.
-
- If a sequence of names is provided, only those tables will be
- reflected. An error is raised if a table is requested but not
- available. Named tables already present in this ``MetaData`` are
- ignored.
-
- If a callable is provided, it will be used as a boolean predicate to
- filter the list of potential table names. The callable is called
- with a table name and this ``MetaData`` instance as positional
- arguments and should return a true value for any table to reflect.
-
- """
- if bind is None:
- bind = _bind_or_error(self)
-
- with bind.connect() as conn:
-
- reflect_opts = {
- 'autoload': True,
- 'autoload_with': conn
- }
-
- if schema is None:
- schema = self.schema
-
- if schema is not None:
- reflect_opts['schema'] = schema
-
- available = util.OrderedSet(bind.engine.table_names(schema,
- connection=conn))
- if views:
- available.update(
- bind.dialect.get_view_names(conn, schema)
- )
-
- if schema is not None:
- available_w_schema = util.OrderedSet(["%s.%s" % (schema, name)
- for name in available])
- else:
- available_w_schema = available
-
- current = set(self.tables)
-
- if only is None:
- load = [name for name, schname in
- zip(available, available_w_schema)
- if schname not in current]
- elif util.callable(only):
- load = [name for name, schname in
- zip(available, available_w_schema)
- if schname not in current and only(name, self)]
- else:
- missing = [name for name in only if name not in available]
- if missing:
- s = schema and (" schema '%s'" % schema) or ''
- raise exc.InvalidRequestError(
- 'Could not reflect: requested table(s) not available '
- 'in %s%s: (%s)' %
- (bind.engine.url, s, ', '.join(missing)))
- load = [name for name in only if name not in current]
-
- for name in load:
- Table(name, self, **reflect_opts)
-
- def append_ddl_listener(self, event_name, listener):
- """Append a DDL event listener to this ``MetaData``.
-
- .. deprecated:: 0.7
- See :class:`.DDLEvents`.
-
- """
- def adapt_listener(target, connection, **kw):
- tables = kw['tables']
- listener(event, target, connection, tables=tables)
-
- event.listen(self, "" + event_name.replace('-', '_'), adapt_listener)
-
- def create_all(self, bind=None, tables=None, checkfirst=True):
- """Create all tables stored in this metadata.
-
- Conditional by default, will not attempt to recreate tables already
- present in the target database.
-
- :param bind:
- A :class:`.Connectable` used to access the
- database; if None, uses the existing bind on this ``MetaData``, if
- any.
-
- :param tables:
- Optional list of ``Table`` objects, which is a subset of the total
- tables in the ``MetaData`` (others are ignored).
-
- :param checkfirst:
- Defaults to True, don't issue CREATEs for tables already present
- in the target database.
-
- """
- if bind is None:
- bind = _bind_or_error(self)
- bind._run_visitor(ddl.SchemaGenerator,
- self,
- checkfirst=checkfirst,
- tables=tables)
-
- def drop_all(self, bind=None, tables=None, checkfirst=True):
- """Drop all tables stored in this metadata.
-
- Conditional by default, will not attempt to drop tables not present in
- the target database.
-
- :param bind:
- A :class:`.Connectable` used to access the
- database; if None, uses the existing bind on this ``MetaData``, if
- any.
-
- :param tables:
- Optional list of ``Table`` objects, which is a subset of the
- total tables in the ``MetaData`` (others are ignored).
-
- :param checkfirst:
- Defaults to True, only issue DROPs for tables confirmed to be
- present in the target database.
-
- """
- if bind is None:
- bind = _bind_or_error(self)
- bind._run_visitor(ddl.SchemaDropper,
- self,
- checkfirst=checkfirst,
- tables=tables)
-
-
-class ThreadLocalMetaData(MetaData):
- """A MetaData variant that presents a different ``bind`` in every thread.
-
- Makes the ``bind`` property of the MetaData a thread-local value, allowing
- this collection of tables to be bound to different ``Engine``
- implementations or connections in each thread.
-
- The ThreadLocalMetaData starts off bound to None in each thread. Binds
- must be made explicitly by assigning to the ``bind`` property or using
- ``connect()``. You can also re-bind dynamically multiple times per
- thread, just like a regular ``MetaData``.
-
- """
-
- __visit_name__ = 'metadata'
-
- def __init__(self):
- """Construct a ThreadLocalMetaData."""
-
- self.context = util.threading.local()
- self.__engines = {}
- super(ThreadLocalMetaData, self).__init__()
-
- def bind(self):
- """The bound Engine or Connection for this thread.
-
- This property may be assigned an Engine or Connection, or assigned a
- string or URL to automatically create a basic Engine for this bind
- with ``create_engine()``."""
-
- return getattr(self.context, '_engine', None)
-
- def _bind_to(self, bind):
- """Bind to a Connectable in the caller's thread."""
-
- if isinstance(bind, util.string_types + (url.URL, )):
- try:
- self.context._engine = self.__engines[bind]
- except KeyError:
- e = sqlalchemy.create_engine(bind)
- self.__engines[bind] = e
- self.context._engine = e
- else:
- # TODO: this is squirrely. we shouldnt have to hold onto engines
- # in a case like this
- if bind not in self.__engines:
- self.__engines[bind] = bind
- self.context._engine = bind
-
- bind = property(bind, _bind_to)
-
- def is_bound(self):
- """True if there is a bind for this thread."""
- return (hasattr(self.context, '_engine') and
- self.context._engine is not None)
-
- def dispose(self):
- """Dispose all bound engines, in all thread contexts."""
-
- for e in self.__engines.values():
- if hasattr(e, 'dispose'):
- e.dispose()
-
-
-class SchemaVisitor(visitors.ClauseVisitor):
- """Define the visiting for ``SchemaItem`` objects."""
-
- __traverse_options__ = {'schema_visitor': True}
-
-
-class _DDLCompiles(expression.ClauseElement):
- def _compiler(self, dialect, **kw):
- """Return a compiler appropriate for this ClauseElement, given a
- Dialect."""
-
- return dialect.ddl_compiler(dialect, self, **kw)
-
-
-class DDLElement(expression.Executable, _DDLCompiles):
- """Base class for DDL expression constructs.
-
- This class is the base for the general purpose :class:`.DDL` class,
- as well as the various create/drop clause constructs such as
- :class:`.CreateTable`, :class:`.DropTable`, :class:`.AddConstraint`,
- etc.
-
- :class:`.DDLElement` integrates closely with SQLAlchemy events,
- introduced in :ref:`event_toplevel`. An instance of one is
- itself an event receiving callable::
-
- event.listen(
- users,
- 'after_create',
- AddConstraint(constraint).execute_if(dialect='postgresql')
- )
-
- See also:
-
- :class:`.DDL`
-
- :class:`.DDLEvents`
-
- :ref:`event_toplevel`
-
- :ref:`schema_ddl_sequences`
-
- """
-
- _execution_options = expression.Executable.\
- _execution_options.union({'autocommit': True})
-
- target = None
- on = None
- dialect = None
- callable_ = None
-
- def execute(self, bind=None, target=None):
- """Execute this DDL immediately.
-
- Executes the DDL statement in isolation using the supplied
- :class:`.Connectable` or
- :class:`.Connectable` assigned to the ``.bind``
- property, if not supplied. If the DDL has a conditional ``on``
- criteria, it will be invoked with None as the event.
-
- :param bind:
- Optional, an ``Engine`` or ``Connection``. If not supplied, a valid
- :class:`.Connectable` must be present in the
- ``.bind`` property.
-
- :param target:
- Optional, defaults to None. The target SchemaItem for the
- execute call. Will be passed to the ``on`` callable if any,
- and may also provide string expansion data for the
- statement. See ``execute_at`` for more information.
-
- """
-
- if bind is None:
- bind = _bind_or_error(self)
-
- if self._should_execute(target, bind):
- return bind.execute(self.against(target))
- else:
- bind.engine.logger.info(
- "DDL execution skipped, criteria not met.")
-
- @util.deprecated("0.7", "See :class:`.DDLEvents`, as well as "
- ":meth:`.DDLElement.execute_if`.")
- def execute_at(self, event_name, target):
- """Link execution of this DDL to the DDL lifecycle of a SchemaItem.
-
- Links this ``DDLElement`` to a ``Table`` or ``MetaData`` instance,
- executing it when that schema item is created or dropped. The DDL
- statement will be executed using the same Connection and transactional
- context as the Table create/drop itself. The ``.bind`` property of
- this statement is ignored.
-
- :param event:
- One of the events defined in the schema item's ``.ddl_events``;
- e.g. 'before-create', 'after-create', 'before-drop' or 'after-drop'
-
- :param target:
- The Table or MetaData instance for which this DDLElement will
- be associated with.
-
- A DDLElement instance can be linked to any number of schema items.
-
- ``execute_at`` builds on the ``append_ddl_listener`` interface of
- :class:`.MetaData` and :class:`.Table` objects.
-
- Caveat: Creating or dropping a Table in isolation will also trigger
- any DDL set to ``execute_at`` that Table's MetaData. This may change
- in a future release.
-
- """
-
- def call_event(target, connection, **kw):
- if self._should_execute_deprecated(event_name,
- target, connection, **kw):
- return connection.execute(self.against(target))
-
- event.listen(target, "" + event_name.replace('-', '_'), call_event)
-
- @expression._generative
- def against(self, target):
- """Return a copy of this DDL against a specific schema item."""
-
- self.target = target
-
- @expression._generative
- def execute_if(self, dialect=None, callable_=None, state=None):
- """Return a callable that will execute this
- DDLElement conditionally.
-
- Used to provide a wrapper for event listening::
-
- event.listen(
- metadata,
- 'before_create',
- DDL("my_ddl").execute_if(dialect='postgresql')
- )
-
- :param dialect: May be a string, tuple or a callable
- predicate. If a string, it will be compared to the name of the
- executing database dialect::
-
- DDL('something').execute_if(dialect='postgresql')
-
- If a tuple, specifies multiple dialect names::
-
- DDL('something').execute_if(dialect=('postgresql', 'mysql'))
-
- :param callable_: A callable, which will be invoked with
- four positional arguments as well as optional keyword
- arguments:
-
- :ddl:
- This DDL element.
-
- :target:
- The :class:`.Table` or :class:`.MetaData` object which is the
- target of this event. May be None if the DDL is executed
- explicitly.
-
- :bind:
- The :class:`.Connection` being used for DDL execution
-
- :tables:
- Optional keyword argument - a list of Table objects which are to
- be created/ dropped within a MetaData.create_all() or drop_all()
- method call.
-
- :state:
- Optional keyword argument - will be the ``state`` argument
- passed to this function.
-
- :checkfirst:
- Keyword argument, will be True if the 'checkfirst' flag was
- set during the call to ``create()``, ``create_all()``,
- ``drop()``, ``drop_all()``.
-
- If the callable returns a true value, the DDL statement will be
- executed.
-
- :param state: any value which will be passed to the callable_
- as the ``state`` keyword argument.
-
- See also:
-
- :class:`.DDLEvents`
-
- :ref:`event_toplevel`
-
- """
- self.dialect = dialect
- self.callable_ = callable_
- self.state = state
-
- def _should_execute(self, target, bind, **kw):
- if self.on is not None and \
- not self._should_execute_deprecated(None, target, bind, **kw):
- return False
-
- if isinstance(self.dialect, util.string_types):
- if self.dialect != bind.engine.name:
- return False
- elif isinstance(self.dialect, (tuple, list, set)):
- if bind.engine.name not in self.dialect:
- return False
- if self.callable_ is not None and \
- not self.callable_(self, target, bind, state=self.state, **kw):
- return False
-
- return True
-
- def _should_execute_deprecated(self, event, target, bind, **kw):
- if self.on is None:
- return True
- elif isinstance(self.on, util.string_types):
- return self.on == bind.engine.name
- elif isinstance(self.on, (tuple, list, set)):
- return bind.engine.name in self.on
- else:
- return self.on(self, event, target, bind, **kw)
-
- def __call__(self, target, bind, **kw):
- """Execute the DDL as a ddl_listener."""
-
- if self._should_execute(target, bind, **kw):
- return bind.execute(self.against(target))
-
- def _check_ddl_on(self, on):
- if (on is not None and
- (not isinstance(on, util.string_types + (tuple, list, set)) and
- not util.callable(on))):
- raise exc.ArgumentError(
- "Expected the name of a database dialect, a tuple "
- "of names, or a callable for "
- "'on' criteria, got type '%s'." % type(on).__name__)
-
- def bind(self):
- if self._bind:
- return self._bind
-
- def _set_bind(self, bind):
- self._bind = bind
- bind = property(bind, _set_bind)
-
- def _generate(self):
- s = self.__class__.__new__(self.__class__)
- s.__dict__ = self.__dict__.copy()
- return s
-
-
-class DDL(DDLElement):
- """A literal DDL statement.
-
- Specifies literal SQL DDL to be executed by the database. DDL objects
- function as DDL event listeners, and can be subscribed to those events
- listed in :class:`.DDLEvents`, using either :class:`.Table` or
- :class:`.MetaData` objects as targets. Basic templating support allows
- a single DDL instance to handle repetitive tasks for multiple tables.
-
- Examples::
-
- from sqlalchemy import event, DDL
-
- tbl = Table('users', metadata, Column('uid', Integer))
- event.listen(tbl, 'before_create', DDL('DROP TRIGGER users_trigger'))
-
- spow = DDL('ALTER TABLE %(table)s SET secretpowers TRUE')
- event.listen(tbl, 'after_create', spow.execute_if(dialect='somedb'))
-
- drop_spow = DDL('ALTER TABLE users SET secretpowers FALSE')
- connection.execute(drop_spow)
-
- When operating on Table events, the following ``statement``
- string substitions are available::
-
- %(table)s - the Table name, with any required quoting applied
- %(schema)s - the schema name, with any required quoting applied
- %(fullname)s - the Table name including schema, quoted if needed
-
- The DDL's "context", if any, will be combined with the standard
- substutions noted above. Keys present in the context will override
- the standard substitutions.
-
- """
-
- __visit_name__ = "ddl"
-
- def __init__(self, statement, on=None, context=None, bind=None):
- """Create a DDL statement.
-
- :param statement:
- A string or unicode string to be executed. Statements will be
- processed with Python's string formatting operator. See the
- ``context`` argument and the ``execute_at`` method.
-
- A literal '%' in a statement must be escaped as '%%'.
-
- SQL bind parameters are not available in DDL statements.
-
- :param on:
- .. deprecated:: 0.7
- See :meth:`.DDLElement.execute_if`.
-
- Optional filtering criteria. May be a string, tuple or a callable
- predicate. If a string, it will be compared to the name of the
- executing database dialect::
-
- DDL('something', on='postgresql')
-
- If a tuple, specifies multiple dialect names::
-
- DDL('something', on=('postgresql', 'mysql'))
-
- If a callable, it will be invoked with four positional arguments
- as well as optional keyword arguments:
-
- :ddl:
- This DDL element.
-
- :event:
- The name of the event that has triggered this DDL, such as
- 'after-create' Will be None if the DDL is executed explicitly.
-
- :target:
- The ``Table`` or ``MetaData`` object which is the target of
- this event. May be None if the DDL is executed explicitly.
-
- :connection:
- The ``Connection`` being used for DDL execution
-
- :tables:
- Optional keyword argument - a list of Table objects which are to
- be created/ dropped within a MetaData.create_all() or drop_all()
- method call.
-
-
- If the callable returns a true value, the DDL statement will be
- executed.
-
- :param context:
- Optional dictionary, defaults to None. These values will be
- available for use in string substitutions on the DDL statement.
-
- :param bind:
- Optional. A :class:`.Connectable`, used by
- default when ``execute()`` is invoked without a bind argument.
-
-
- See also:
-
- :class:`.DDLEvents`
- :mod:`sqlalchemy.event`
-
- """
-
- if not isinstance(statement, util.string_types):
- raise exc.ArgumentError(
- "Expected a string or unicode SQL statement, got '%r'" %
- statement)
-
- self.statement = statement
- self.context = context or {}
-
- self._check_ddl_on(on)
- self.on = on
- self._bind = bind
-
- def __repr__(self):
- return '<%s@%s; %s>' % (
- type(self).__name__, id(self),
- ', '.join([repr(self.statement)] +
- ['%s=%r' % (key, getattr(self, key))
- for key in ('on', 'context')
- if getattr(self, key)]))
-
-
-def _to_schema_column(element):
- if hasattr(element, '__clause_element__'):
- element = element.__clause_element__()
- if not isinstance(element, Column):
- raise exc.ArgumentError("schema.Column object expected")
- return element
-
-
-def _to_schema_column_or_string(element):
- if hasattr(element, '__clause_element__'):
- element = element.__clause_element__()
- if not isinstance(element, util.string_types + (expression.ColumnElement, )):
- msg = "Element %r is not a string name or column element"
- raise exc.ArgumentError(msg % element)
- return element
-
-
-class _CreateDropBase(DDLElement):
- """Base class for DDL constucts that represent CREATE and DROP or
- equivalents.
-
- The common theme of _CreateDropBase is a single
- ``element`` attribute which refers to the element
- to be created or dropped.
-
- """
-
- def __init__(self, element, on=None, bind=None):
- self.element = element
- self._check_ddl_on(on)
- self.on = on
- self.bind = bind
-
- def _create_rule_disable(self, compiler):
- """Allow disable of _create_rule using a callable.
-
- Pass to _create_rule using
- util.portable_instancemethod(self._create_rule_disable)
- to retain serializability.
-
- """
- return False
-
-
-class CreateSchema(_CreateDropBase):
- """Represent a CREATE SCHEMA statement.
-
- .. versionadded:: 0.7.4
-
- The argument here is the string name of the schema.
-
- """
-
- __visit_name__ = "create_schema"
-
- def __init__(self, name, quote=None, **kw):
- """Create a new :class:`.CreateSchema` construct."""
-
- self.quote = quote
- super(CreateSchema, self).__init__(name, **kw)
-
-
-class DropSchema(_CreateDropBase):
- """Represent a DROP SCHEMA statement.
-
- The argument here is the string name of the schema.
-
- .. versionadded:: 0.7.4
-
- """
-
- __visit_name__ = "drop_schema"
-
- def __init__(self, name, quote=None, cascade=False, **kw):
- """Create a new :class:`.DropSchema` construct."""
-
- self.quote = quote
- self.cascade = cascade
- super(DropSchema, self).__init__(name, **kw)
-
-
-class CreateTable(_CreateDropBase):
- """Represent a CREATE TABLE statement."""
-
- __visit_name__ = "create_table"
-
- def __init__(self, element, on=None, bind=None):
- """Create a :class:`.CreateTable` construct.
-
- :param element: a :class:`.Table` that's the subject
- of the CREATE
- :param on: See the description for 'on' in :class:`.DDL`.
- :param bind: See the description for 'bind' in :class:`.DDL`.
-
- """
- super(CreateTable, self).__init__(element, on=on, bind=bind)
- self.columns = [CreateColumn(column)
- for column in element.columns
- ]
-
-
-class _DropView(_CreateDropBase):
- """Semi-public 'DROP VIEW' construct.
-
- Used by the test suite for dialect-agnostic drops of views.
- This object will eventually be part of a public "view" API.
-
- """
- __visit_name__ = "drop_view"
-
-
-class CreateColumn(_DDLCompiles):
- """Represent a :class:`.Column` as rendered in a CREATE TABLE statement,
- via the :class:`.CreateTable` construct.
-
- This is provided to support custom column DDL within the generation
- of CREATE TABLE statements, by using the
- compiler extension documented in :ref:`sqlalchemy.ext.compiler_toplevel`
- to extend :class:`.CreateColumn`.
-
- Typical integration is to examine the incoming :class:`.Column`
- object, and to redirect compilation if a particular flag or condition
- is found::
-
- from sqlalchemy import schema
- from sqlalchemy.ext.compiler import compiles
-
- @compiles(schema.CreateColumn)
- def compile(element, compiler, **kw):
- column = element.element
-
- if "special" not in column.info:
- return compiler.visit_create_column(element, **kw)
-
- text = "%s SPECIAL DIRECTIVE %s" % (
- column.name,
- compiler.type_compiler.process(column.type)
- )
- default = compiler.get_column_default_string(column)
- if default is not None:
- text += " DEFAULT " + default
-
- if not column.nullable:
- text += " NOT NULL"
-
- if column.constraints:
- text += " ".join(
- compiler.process(const)
- for const in column.constraints)
- return text
-
- The above construct can be applied to a :class:`.Table` as follows::
-
- from sqlalchemy import Table, Metadata, Column, Integer, String
- from sqlalchemy import schema
-
- metadata = MetaData()
-
- table = Table('mytable', MetaData(),
- Column('x', Integer, info={"special":True}, primary_key=True),
- Column('y', String(50)),
- Column('z', String(20), info={"special":True})
- )
-
- metadata.create_all(conn)
-
- Above, the directives we've added to the :attr:`.Column.info` collection
- will be detected by our custom compilation scheme::
-
- CREATE TABLE mytable (
- x SPECIAL DIRECTIVE INTEGER NOT NULL,
- y VARCHAR(50),
- z SPECIAL DIRECTIVE VARCHAR(20),
- PRIMARY KEY (x)
- )
-
- .. versionadded:: 0.8 The :class:`.CreateColumn` construct was added
- to support custom column creation styles.
-
- """
- __visit_name__ = 'create_column'
-
- def __init__(self, element):
- self.element = element
-
-
-class DropTable(_CreateDropBase):
- """Represent a DROP TABLE statement."""
-
- __visit_name__ = "drop_table"
-
-
-class CreateSequence(_CreateDropBase):
- """Represent a CREATE SEQUENCE statement."""
-
- __visit_name__ = "create_sequence"
-
-
-class DropSequence(_CreateDropBase):
- """Represent a DROP SEQUENCE statement."""
-
- __visit_name__ = "drop_sequence"
-
-
-class CreateIndex(_CreateDropBase):
- """Represent a CREATE INDEX statement."""
-
- __visit_name__ = "create_index"
-
-
-class DropIndex(_CreateDropBase):
- """Represent a DROP INDEX statement."""
-
- __visit_name__ = "drop_index"
-
-
-class AddConstraint(_CreateDropBase):
- """Represent an ALTER TABLE ADD CONSTRAINT statement."""
-
- __visit_name__ = "add_constraint"
-
- def __init__(self, element, *args, **kw):
- super(AddConstraint, self).__init__(element, *args, **kw)
- element._create_rule = util.portable_instancemethod(
- self._create_rule_disable)
-
-
-class DropConstraint(_CreateDropBase):
- """Represent an ALTER TABLE DROP CONSTRAINT statement."""
-
- __visit_name__ = "drop_constraint"
-
- def __init__(self, element, cascade=False, **kw):
- self.cascade = cascade
- super(DropConstraint, self).__init__(element, **kw)
- element._create_rule = util.portable_instancemethod(
- self._create_rule_disable)
-
-
-def _bind_or_error(schemaitem, msg=None):
- bind = schemaitem.bind
- if not bind:
- name = schemaitem.__class__.__name__
- label = getattr(schemaitem, 'fullname',
- getattr(schemaitem, 'name', None))
- if label:
- item = '%s %r' % (name, label)
- else:
- item = name
- if isinstance(schemaitem, (MetaData, DDL)):
- bindable = "the %s's .bind" % name
- else:
- bindable = "this %s's .metadata.bind" % name
- if msg is None:
- msg = "The %s is not bound to an Engine or Connection. "\
- "Execution can not proceed without a database to execute "\
- "against. Either execute with an explicit connection or "\
- "assign %s to enable implicit execution." % \
- (item, bindable)
- raise exc.UnboundExecutionError(msg)
- return bind
+from .sql.schema import (
+ CheckConstraint,
+ Column,
+ ColumnDefault,
+ Constraint,
+ DefaultClause,
+ DefaultGenerator,
+ FetchedValue,
+ ForeignKey,
+ ForeignKeyConstraint,
+ Index,
+ MetaData,
+ PassiveDefault,
+ PrimaryKeyConstraint,
+ SchemaItem,
+ Sequence,
+ Table,
+ ThreadLocalMetaData,
+ UniqueConstraint,
+ _get_table_key,
+ ColumnCollectionConstraint,
+ )
+
+
+from .sql.ddl import (
+ DDL,
+ CreateTable,
+ DropTable,
+ CreateSequence,
+ DropSequence,
+ CreateIndex,
+ DropIndex,
+ CreateSchema,
+ DropSchema,
+ _DropView,
+ CreateColumn,
+ AddConstraint,
+ DropConstraint,
+ DDLBase,
+ DDLElement,
+ _CreateDropBase,
+ _DDLCompiles
+)
diff --git a/lib/sqlalchemy/sql/__init__.py b/lib/sqlalchemy/sql/__init__.py
index 9700f26a0..3503f8c97 100644
--- a/lib/sqlalchemy/sql/__init__.py
+++ b/lib/sqlalchemy/sql/__init__.py
@@ -66,3 +66,15 @@ from .visitors import ClauseVisitor
__tmp = list(locals().keys())
__all__ = sorted([i for i in __tmp if not i.startswith('__')])
+
+def __go():
+ from .annotation import _prepare_annotations, Annotated
+ from .elements import AnnotatedColumnElement, ClauseList
+ from .selectable import AnnotatedFromClause
+ _prepare_annotations(ColumnElement, AnnotatedColumnElement)
+ _prepare_annotations(FromClause, AnnotatedFromClause)
+ _prepare_annotations(ClauseList, Annotated)
+
+ from .. import util as _sa_util
+ _sa_util.importlater.resolve_all("sqlalchemy.sql")
+__go() \ No newline at end of file
diff --git a/lib/sqlalchemy/sql/annotation.py b/lib/sqlalchemy/sql/annotation.py
new file mode 100644
index 000000000..4d7c0b3f4
--- /dev/null
+++ b/lib/sqlalchemy/sql/annotation.py
@@ -0,0 +1,182 @@
+# sql/annotation.py
+# Copyright (C) 2005-2013 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
+
+"""The :class:`.Annotated` class and related routines; creates hash-equivalent
+copies of SQL constructs which contain context-specific markers and associations.
+
+"""
+
+from .. import util
+from . import operators
+
+class Annotated(object):
+ """clones a ClauseElement and applies an 'annotations' dictionary.
+
+ Unlike regular clones, this clone also mimics __hash__() and
+ __cmp__() of the original element so that it takes its place
+ in hashed collections.
+
+ A reference to the original element is maintained, for the important
+ reason of keeping its hash value current. When GC'ed, the
+ hash value may be reused, causing conflicts.
+
+ """
+
+ def __new__(cls, *args):
+ if not args:
+ # clone constructor
+ return object.__new__(cls)
+ else:
+ element, values = args
+ # pull appropriate subclass from registry of annotated
+ # classes
+ try:
+ cls = annotated_classes[element.__class__]
+ except KeyError:
+ cls = _new_annotation_type(element.__class__, cls)
+ return object.__new__(cls)
+
+ def __init__(self, element, values):
+ self.__dict__ = element.__dict__.copy()
+ self.__element = element
+ self._annotations = values
+
+ def _annotate(self, values):
+ _values = self._annotations.copy()
+ _values.update(values)
+ return self._with_annotations(_values)
+
+ def _with_annotations(self, values):
+ clone = self.__class__.__new__(self.__class__)
+ clone.__dict__ = self.__dict__.copy()
+ clone._annotations = values
+ return clone
+
+ def _deannotate(self, values=None, clone=True):
+ if values is None:
+ return self.__element
+ else:
+ _values = self._annotations.copy()
+ for v in values:
+ _values.pop(v, None)
+ return self._with_annotations(_values)
+
+ def _compiler_dispatch(self, visitor, **kw):
+ return self.__element.__class__._compiler_dispatch(self, visitor, **kw)
+
+ @property
+ def _constructor(self):
+ return self.__element._constructor
+
+ def _clone(self):
+ clone = self.__element._clone()
+ if clone is self.__element:
+ # detect immutable, don't change anything
+ return self
+ else:
+ # update the clone with any changes that have occurred
+ # to this object's __dict__.
+ clone.__dict__.update(self.__dict__)
+ return self.__class__(clone, self._annotations)
+
+ def __hash__(self):
+ return hash(self.__element)
+
+ def __eq__(self, other):
+ if isinstance(self.__element, operators.ColumnOperators):
+ return self.__element.__class__.__eq__(self, other)
+ else:
+ return hash(other) == hash(self)
+
+
+
+# hard-generate Annotated subclasses. this technique
+# is used instead of on-the-fly types (i.e. type.__new__())
+# so that the resulting objects are pickleable.
+annotated_classes = {}
+
+
+
+def _deep_annotate(element, annotations, exclude=None):
+ """Deep copy the given ClauseElement, annotating each element
+ with the given annotations dictionary.
+
+ Elements within the exclude collection will be cloned but not annotated.
+
+ """
+ def clone(elem):
+ if exclude and \
+ hasattr(elem, 'proxy_set') and \
+ elem.proxy_set.intersection(exclude):
+ newelem = elem._clone()
+ elif annotations != elem._annotations:
+ newelem = elem._annotate(annotations)
+ else:
+ newelem = elem
+ newelem._copy_internals(clone=clone)
+ return newelem
+
+ if element is not None:
+ element = clone(element)
+ return element
+
+
+def _deep_deannotate(element, values=None):
+ """Deep copy the given element, removing annotations."""
+
+ cloned = util.column_dict()
+
+ def clone(elem):
+ # if a values dict is given,
+ # the elem must be cloned each time it appears,
+ # as there may be different annotations in source
+ # elements that are remaining. if totally
+ # removing all annotations, can assume the same
+ # slate...
+ if values or elem not in cloned:
+ newelem = elem._deannotate(values=values, clone=True)
+ newelem._copy_internals(clone=clone)
+ if not values:
+ cloned[elem] = newelem
+ return newelem
+ else:
+ return cloned[elem]
+
+ if element is not None:
+ element = clone(element)
+ return element
+
+
+def _shallow_annotate(element, annotations):
+ """Annotate the given ClauseElement and copy its internals so that
+ internal objects refer to the new annotated object.
+
+ Basically used to apply a "dont traverse" annotation to a
+ selectable, without digging throughout the whole
+ structure wasting time.
+ """
+ element = element._annotate(annotations)
+ element._copy_internals()
+ return element
+
+def _new_annotation_type(cls, base_cls):
+ if issubclass(cls, Annotated):
+ return cls
+ elif cls in annotated_classes:
+ return annotated_classes[cls]
+ annotated_classes[cls] = anno_cls = type(
+ "Annotated%s" % cls.__name__,
+ (base_cls, cls), {})
+ globals()["Annotated%s" % cls.__name__] = anno_cls
+ return anno_cls
+
+def _prepare_annotations(target_hierarchy, base_cls):
+ stack = [target_hierarchy]
+ while stack:
+ cls = stack.pop()
+ stack.extend(cls.__subclasses__())
+
+ _new_annotation_type(cls, base_cls)
diff --git a/lib/sqlalchemy/sql/base.py b/lib/sqlalchemy/sql/base.py
new file mode 100644
index 000000000..e7d83627d
--- /dev/null
+++ b/lib/sqlalchemy/sql/base.py
@@ -0,0 +1,329 @@
+# sql/base.py
+# Copyright (C) 2005-2013 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
+
+"""Foundational utilities common to many sql modules.
+
+"""
+
+
+from .. import util, exc
+import itertools
+from .visitors import ClauseVisitor
+
+
+PARSE_AUTOCOMMIT = util.symbol('PARSE_AUTOCOMMIT')
+NO_ARG = util.symbol('NO_ARG')
+
+class Immutable(object):
+ """mark a ClauseElement as 'immutable' when expressions are cloned."""
+
+ def unique_params(self, *optionaldict, **kwargs):
+ raise NotImplementedError("Immutable objects do not support copying")
+
+ def params(self, *optionaldict, **kwargs):
+ raise NotImplementedError("Immutable objects do not support copying")
+
+ def _clone(self):
+ return self
+
+
+def _from_objects(*elements):
+ return itertools.chain(*[element._from_objects for element in elements])
+
+@util.decorator
+def _generative(fn, *args, **kw):
+ """Mark a method as generative."""
+
+ self = args[0]._generate()
+ fn(self, *args[1:], **kw)
+ return self
+
+
+class Generative(object):
+ """Allow a ClauseElement to generate itself via the
+ @_generative decorator.
+
+ """
+
+ def _generate(self):
+ s = self.__class__.__new__(self.__class__)
+ s.__dict__ = self.__dict__.copy()
+ return s
+
+
+class Executable(Generative):
+ """Mark a ClauseElement as supporting execution.
+
+ :class:`.Executable` is a superclass for all "statement" types
+ of objects, including :func:`select`, :func:`delete`, :func:`update`,
+ :func:`insert`, :func:`text`.
+
+ """
+
+ supports_execution = True
+ _execution_options = util.immutabledict()
+ _bind = None
+
+ @_generative
+ def execution_options(self, **kw):
+ """ Set non-SQL options for the statement which take effect during
+ execution.
+
+ Execution options can be set on a per-statement or
+ per :class:`.Connection` basis. Additionally, the
+ :class:`.Engine` and ORM :class:`~.orm.query.Query` objects provide
+ access to execution options which they in turn configure upon
+ connections.
+
+ The :meth:`execution_options` method is generative. A new
+ instance of this statement is returned that contains the options::
+
+ statement = select([table.c.x, table.c.y])
+ statement = statement.execution_options(autocommit=True)
+
+ Note that only a subset of possible execution options can be applied
+ to a statement - these include "autocommit" and "stream_results",
+ but not "isolation_level" or "compiled_cache".
+ See :meth:`.Connection.execution_options` for a full list of
+ possible options.
+
+ .. seealso::
+
+ :meth:`.Connection.execution_options()`
+
+ :meth:`.Query.execution_options()`
+
+ """
+ if 'isolation_level' in kw:
+ raise exc.ArgumentError(
+ "'isolation_level' execution option may only be specified "
+ "on Connection.execution_options(), or "
+ "per-engine using the isolation_level "
+ "argument to create_engine()."
+ )
+ if 'compiled_cache' in kw:
+ raise exc.ArgumentError(
+ "'compiled_cache' execution option may only be specified "
+ "on Connection.execution_options(), not per statement."
+ )
+ self._execution_options = self._execution_options.union(kw)
+
+ def execute(self, *multiparams, **params):
+ """Compile and execute this :class:`.Executable`."""
+ e = self.bind
+ if e is None:
+ label = getattr(self, 'description', self.__class__.__name__)
+ msg = ('This %s is not directly bound to a Connection or Engine.'
+ 'Use the .execute() method of a Connection or Engine '
+ 'to execute this construct.' % label)
+ raise exc.UnboundExecutionError(msg)
+ return e._execute_clauseelement(self, multiparams, params)
+
+ def scalar(self, *multiparams, **params):
+ """Compile and execute this :class:`.Executable`, returning the
+ result's scalar representation.
+
+ """
+ return self.execute(*multiparams, **params).scalar()
+
+ @property
+ def bind(self):
+ """Returns the :class:`.Engine` or :class:`.Connection` to
+ which this :class:`.Executable` is bound, or None if none found.
+
+ This is a traversal which checks locally, then
+ checks among the "from" clauses of associated objects
+ until a bound engine or connection is found.
+
+ """
+ if self._bind is not None:
+ return self._bind
+
+ for f in _from_objects(self):
+ if f is self:
+ continue
+ engine = f.bind
+ if engine is not None:
+ return engine
+ else:
+ return None
+
+
+class SchemaVisitor(ClauseVisitor):
+ """Define the visiting for ``SchemaItem`` objects."""
+
+ __traverse_options__ = {'schema_visitor': True}
+
+class ColumnCollection(util.OrderedProperties):
+ """An ordered dictionary that stores a list of ColumnElement
+ instances.
+
+ Overrides the ``__eq__()`` method to produce SQL clauses between
+ sets of correlated columns.
+
+ """
+
+ def __init__(self, *cols):
+ super(ColumnCollection, self).__init__()
+ self._data.update((c.key, c) for c in cols)
+ self.__dict__['_all_cols'] = util.column_set(self)
+
+ def __str__(self):
+ return repr([str(c) for c in self])
+
+ def replace(self, column):
+ """add the given column to this collection, removing unaliased
+ versions of this column as well as existing columns with the
+ same key.
+
+ e.g.::
+
+ t = Table('sometable', metadata, Column('col1', Integer))
+ t.columns.replace(Column('col1', Integer, key='columnone'))
+
+ will remove the original 'col1' from the collection, and add
+ the new column under the name 'columnname'.
+
+ Used by schema.Column to override columns during table reflection.
+
+ """
+ if column.name in self and column.key != column.name:
+ other = self[column.name]
+ if other.name == other.key:
+ del self._data[other.name]
+ self._all_cols.remove(other)
+ if column.key in self._data:
+ self._all_cols.remove(self._data[column.key])
+ self._all_cols.add(column)
+ self._data[column.key] = column
+
+ def add(self, column):
+ """Add a column to this collection.
+
+ The key attribute of the column will be used as the hash key
+ for this dictionary.
+
+ """
+ self[column.key] = column
+
+ def __delitem__(self, key):
+ raise NotImplementedError()
+
+ def __setattr__(self, key, object):
+ raise NotImplementedError()
+
+ def __setitem__(self, key, value):
+ if key in self:
+
+ # this warning is primarily to catch select() statements
+ # which have conflicting column names in their exported
+ # columns collection
+
+ existing = self[key]
+ if not existing.shares_lineage(value):
+ util.warn('Column %r on table %r being replaced by '
+ '%r, which has the same key. Consider '
+ 'use_labels for select() statements.' % (key,
+ getattr(existing, 'table', None), value))
+ self._all_cols.remove(existing)
+ # pop out memoized proxy_set as this
+ # operation may very well be occurring
+ # in a _make_proxy operation
+ util.memoized_property.reset(value, "proxy_set")
+ self._all_cols.add(value)
+ self._data[key] = value
+
+ def clear(self):
+ self._data.clear()
+ self._all_cols.clear()
+
+ def remove(self, column):
+ del self._data[column.key]
+ self._all_cols.remove(column)
+
+ def update(self, value):
+ self._data.update(value)
+ self._all_cols.clear()
+ self._all_cols.update(self._data.values())
+
+ def extend(self, iter):
+ self.update((c.key, c) for c in iter)
+
+ __hash__ = None
+
+ @util.dependencies("sqlalchemy.sql.elements")
+ def __eq__(self, elements, other):
+ l = []
+ for c in other:
+ for local in self:
+ if c.shares_lineage(local):
+ l.append(c == local)
+ return elements.and_(*l)
+
+ def __contains__(self, other):
+ if not isinstance(other, util.string_types):
+ raise exc.ArgumentError("__contains__ requires a string argument")
+ return util.OrderedProperties.__contains__(self, other)
+
+ def __setstate__(self, state):
+ self.__dict__['_data'] = state['_data']
+ self.__dict__['_all_cols'] = util.column_set(self._data.values())
+
+ def contains_column(self, col):
+ # this has to be done via set() membership
+ return col in self._all_cols
+
+ def as_immutable(self):
+ return ImmutableColumnCollection(self._data, self._all_cols)
+
+
+class ImmutableColumnCollection(util.ImmutableProperties, ColumnCollection):
+ def __init__(self, data, colset):
+ util.ImmutableProperties.__init__(self, data)
+ self.__dict__['_all_cols'] = colset
+
+ extend = remove = util.ImmutableProperties._immutable
+
+
+class ColumnSet(util.ordered_column_set):
+ def contains_column(self, col):
+ return col in self
+
+ def extend(self, cols):
+ for col in cols:
+ self.add(col)
+
+ def __add__(self, other):
+ return list(self) + list(other)
+
+ @util.dependencies("sqlalchemy.sql.elements")
+ def __eq__(self, elements, other):
+ l = []
+ for c in other:
+ for local in self:
+ if c.shares_lineage(local):
+ l.append(c == local)
+ return elements.and_(*l)
+
+ def __hash__(self):
+ return hash(tuple(x for x in self))
+
+def _bind_or_error(schemaitem, msg=None):
+ bind = schemaitem.bind
+ if not bind:
+ name = schemaitem.__class__.__name__
+ label = getattr(schemaitem, 'fullname',
+ getattr(schemaitem, 'name', None))
+ if label:
+ item = '%s object %r' % (name, label)
+ else:
+ item = '%s object' % name
+ if msg is None:
+ msg = "%s is not bound to an Engine or Connection. "\
+ "Execution can not proceed without a database to execute "\
+ "against." % item
+ raise exc.UnboundExecutionError(msg)
+ return bind
diff --git a/lib/sqlalchemy/sql/compiler.py b/lib/sqlalchemy/sql/compiler.py
index daed7c50f..a6e6987c5 100644
--- a/lib/sqlalchemy/sql/compiler.py
+++ b/lib/sqlalchemy/sql/compiler.py
@@ -23,11 +23,9 @@ To generate user-defined SQL strings, see
"""
import re
-import sys
-from .. import schema, engine, util, exc, types
-from . import (
- operators, functions, util as sql_util, visitors, expression as sql
-)
+from . import schema, sqltypes, operators, functions, \
+ util as sql_util, visitors, elements, selectable
+from .. import util, exc
import decimal
import itertools
@@ -150,14 +148,118 @@ EXTRACT_MAP = {
}
COMPOUND_KEYWORDS = {
- sql.CompoundSelect.UNION: 'UNION',
- sql.CompoundSelect.UNION_ALL: 'UNION ALL',
- sql.CompoundSelect.EXCEPT: 'EXCEPT',
- sql.CompoundSelect.EXCEPT_ALL: 'EXCEPT ALL',
- sql.CompoundSelect.INTERSECT: 'INTERSECT',
- sql.CompoundSelect.INTERSECT_ALL: 'INTERSECT ALL'
+ selectable.CompoundSelect.UNION: 'UNION',
+ selectable.CompoundSelect.UNION_ALL: 'UNION ALL',
+ selectable.CompoundSelect.EXCEPT: 'EXCEPT',
+ selectable.CompoundSelect.EXCEPT_ALL: 'EXCEPT ALL',
+ selectable.CompoundSelect.INTERSECT: 'INTERSECT',
+ selectable.CompoundSelect.INTERSECT_ALL: 'INTERSECT ALL'
}
+class Compiled(object):
+ """Represent a compiled SQL or DDL expression.
+
+ The ``__str__`` method of the ``Compiled`` object should produce
+ the actual text of the statement. ``Compiled`` objects are
+ specific to their underlying database dialect, and also may
+ or may not be specific to the columns referenced within a
+ particular set of bind parameters. In no case should the
+ ``Compiled`` object be dependent on the actual values of those
+ bind parameters, even though it may reference those values as
+ defaults.
+ """
+
+ def __init__(self, dialect, statement, bind=None,
+ compile_kwargs=util.immutabledict()):
+ """Construct a new ``Compiled`` object.
+
+ :param dialect: ``Dialect`` to compile against.
+
+ :param statement: ``ClauseElement`` to be compiled.
+
+ :param bind: Optional Engine or Connection to compile this
+ statement against.
+
+ :param compile_kwargs: additional kwargs that will be
+ passed to the initial call to :meth:`.Compiled.process`.
+
+ .. versionadded:: 0.8
+
+ """
+
+ self.dialect = dialect
+ self.bind = bind
+ if statement is not None:
+ self.statement = statement
+ self.can_execute = statement.supports_execution
+ self.string = self.process(self.statement, **compile_kwargs)
+
+ @util.deprecated("0.7", ":class:`.Compiled` objects now compile "
+ "within the constructor.")
+ def compile(self):
+ """Produce the internal string representation of this element."""
+ pass
+
+ @property
+ def sql_compiler(self):
+ """Return a Compiled that is capable of processing SQL expressions.
+
+ If this compiler is one, it would likely just return 'self'.
+
+ """
+
+ raise NotImplementedError()
+
+ def process(self, obj, **kwargs):
+ return obj._compiler_dispatch(self, **kwargs)
+
+ def __str__(self):
+ """Return the string text of the generated SQL or DDL."""
+
+ return self.string or ''
+
+ def construct_params(self, params=None):
+ """Return the bind params for this compiled object.
+
+ :param params: a dict of string/object pairs whose values will
+ override bind values compiled in to the
+ statement.
+ """
+
+ raise NotImplementedError()
+
+ @property
+ def params(self):
+ """Return the bind params for this compiled object."""
+ return self.construct_params()
+
+ def execute(self, *multiparams, **params):
+ """Execute this compiled object."""
+
+ e = self.bind
+ if e is None:
+ raise exc.UnboundExecutionError(
+ "This Compiled object is not bound to any Engine "
+ "or Connection.")
+ return e._execute_compiled(self, multiparams, params)
+
+ def scalar(self, *multiparams, **params):
+ """Execute this compiled object and return the result's
+ scalar value."""
+
+ return self.execute(*multiparams, **params).scalar()
+
+
+class TypeCompiler(object):
+ """Produces DDL specification for TypeEngine objects."""
+
+ def __init__(self, dialect):
+ self.dialect = dialect
+
+ def process(self, type_):
+ return type_._compiler_dispatch(self)
+
+
class _CompileLabel(visitors.Visitable):
"""lightweight label object which acts as an expression.Label."""
@@ -183,7 +285,7 @@ class _CompileLabel(visitors.Visitable):
return self.element.quote
-class SQLCompiler(engine.Compiled):
+class SQLCompiler(Compiled):
"""Default implementation of Compiled.
Compiles ClauseElements into SQL strings. Uses a similar visit
@@ -284,7 +386,7 @@ class SQLCompiler(engine.Compiled):
# a map which tracks "truncated" names based on
# dialect.label_length or dialect.max_identifier_length
self.truncated_names = {}
- engine.Compiled.__init__(self, dialect, statement, **kwargs)
+ Compiled.__init__(self, dialect, statement, **kwargs)
if self.positional and dialect.paramstyle == 'numeric':
self._apply_numbered_params()
@@ -397,7 +499,7 @@ class SQLCompiler(engine.Compiled):
render_label_only = render_label_as_label is label
if render_label_only or render_label_with_as:
- if isinstance(label.name, sql._truncated_label):
+ if isinstance(label.name, elements._truncated_label):
labelname = self._truncated_identifier("colident", label.name)
else:
labelname = label.name
@@ -432,7 +534,7 @@ class SQLCompiler(engine.Compiled):
"its 'name' is assigned.")
is_literal = column.is_literal
- if not is_literal and isinstance(name, sql._truncated_label):
+ if not is_literal and isinstance(name, elements._truncated_label):
name = self._truncated_identifier("colident", name)
if add_to_result_map is not None:
@@ -459,7 +561,7 @@ class SQLCompiler(engine.Compiled):
else:
schema_prefix = ''
tablename = table.name
- if isinstance(tablename, sql._truncated_label):
+ if isinstance(tablename, elements._truncated_label):
tablename = self._truncated_identifier("alias", tablename)
return schema_prefix + \
@@ -687,8 +789,8 @@ class SQLCompiler(engine.Compiled):
def visit_binary(self, binary, **kw):
# don't allow "? = ?" to render
if self.ansi_bind_rules and \
- isinstance(binary.left, sql.BindParameter) and \
- isinstance(binary.right, sql.BindParameter):
+ isinstance(binary.left, elements.BindParameter) and \
+ isinstance(binary.right, elements.BindParameter):
kw['literal_binds'] = True
operator = binary.operator
@@ -728,7 +830,7 @@ class SQLCompiler(engine.Compiled):
@util.memoized_property
def _like_percent_literal(self):
- return sql.literal_column("'%'", type_=types.String())
+ return elements.literal_column("'%'", type_=sqltypes.String())
def visit_contains_op_binary(self, binary, operator, **kw):
binary = binary._clone()
@@ -888,7 +990,7 @@ class SQLCompiler(engine.Compiled):
return self.bind_names[bindparam]
bind_name = bindparam.key
- if isinstance(bind_name, sql._truncated_label):
+ if isinstance(bind_name, elements._truncated_label):
bind_name = self._truncated_identifier("bindparam", bind_name)
# add to bind_names for translation
@@ -937,7 +1039,7 @@ class SQLCompiler(engine.Compiled):
if self.positional:
kwargs['positional_names'] = self.cte_positional
- if isinstance(cte.name, sql._truncated_label):
+ if isinstance(cte.name, elements._truncated_label):
cte_name = self._truncated_identifier("alias", cte.name)
else:
cte_name = cte.name
@@ -966,7 +1068,7 @@ class SQLCompiler(engine.Compiled):
if orig_cte not in self.ctes:
self.visit_cte(orig_cte)
cte_alias_name = cte._cte_alias.name
- if isinstance(cte_alias_name, sql._truncated_label):
+ if isinstance(cte_alias_name, elements._truncated_label):
cte_alias_name = self._truncated_identifier("alias", cte_alias_name)
else:
orig_cte = cte
@@ -976,9 +1078,9 @@ class SQLCompiler(engine.Compiled):
self.ctes_recursive = True
text = self.preparer.format_alias(cte, cte_name)
if cte.recursive:
- if isinstance(cte.original, sql.Select):
+ if isinstance(cte.original, selectable.Select):
col_source = cte.original
- elif isinstance(cte.original, sql.CompoundSelect):
+ elif isinstance(cte.original, selectable.CompoundSelect):
col_source = cte.original.selects[0]
else:
assert False
@@ -1007,7 +1109,7 @@ class SQLCompiler(engine.Compiled):
iscrud=False,
fromhints=None, **kwargs):
if asfrom or ashint:
- if isinstance(alias.name, sql._truncated_label):
+ if isinstance(alias.name, elements._truncated_label):
alias_name = self._truncated_identifier("alias", alias.name)
else:
alias_name = alias.name
@@ -1065,7 +1167,7 @@ class SQLCompiler(engine.Compiled):
if not within_columns_clause:
result_expr = col_expr
- elif isinstance(column, sql.Label):
+ elif isinstance(column, elements.Label):
if col_expr is not column:
result_expr = _CompileLabel(
col_expr,
@@ -1084,23 +1186,23 @@ class SQLCompiler(engine.Compiled):
elif \
asfrom and \
- isinstance(column, sql.ColumnClause) and \
+ isinstance(column, elements.ColumnClause) and \
not column.is_literal and \
column.table is not None and \
- not isinstance(column.table, sql.Select):
+ not isinstance(column.table, selectable.Select):
result_expr = _CompileLabel(col_expr,
- sql._as_truncated(column.name),
+ elements._as_truncated(column.name),
alt_names=(column.key,))
elif not isinstance(column,
- (sql.UnaryExpression, sql.TextClause)) \
+ (elements.UnaryExpression, elements.TextClause)) \
and (not hasattr(column, 'name') or \
- isinstance(column, sql.Function)):
+ isinstance(column, functions.Function)):
result_expr = _CompileLabel(col_expr, column.anon_label)
elif col_expr is not column:
# TODO: are we sure "column" has a .name and .key here ?
- # assert isinstance(column, sql.ColumnClause)
+ # assert isinstance(column, elements.ColumnClause)
result_expr = _CompileLabel(col_expr,
- sql._as_truncated(column.name),
+ elements._as_truncated(column.name),
alt_names=(column.key,))
else:
result_expr = col_expr
@@ -1143,8 +1245,8 @@ class SQLCompiler(engine.Compiled):
# as this whole system won't work for custom Join/Select
# subclasses where compilation routines
# call down to compiler.visit_join(), compiler.visit_select()
- join_name = sql.Join.__visit_name__
- select_name = sql.Select.__visit_name__
+ join_name = selectable.Join.__visit_name__
+ select_name = selectable.Select.__visit_name__
def visit(element, **kw):
if element in column_translate[-1]:
@@ -1156,25 +1258,25 @@ class SQLCompiler(engine.Compiled):
newelem = cloned[element] = element._clone()
if newelem.__visit_name__ is join_name and \
- isinstance(newelem.right, sql.FromGrouping):
+ isinstance(newelem.right, selectable.FromGrouping):
newelem._reset_exported()
newelem.left = visit(newelem.left, **kw)
right = visit(newelem.right, **kw)
- selectable = sql.select(
+ selectable_ = selectable.Select(
[right.element],
use_labels=True).alias()
- for c in selectable.c:
+ for c in selectable_.c:
c._key_label = c.key
c._label = c.name
translate_dict = dict(
- zip(right.element.c, selectable.c)
+ zip(right.element.c, selectable_.c)
)
- translate_dict[right.element.left] = selectable
- translate_dict[right.element.right] = selectable
+ translate_dict[right.element.left] = selectable_
+ translate_dict[right.element.right] = selectable_
# propagate translations that we've gained
# from nested visit(newelem.right) outwards
@@ -1190,7 +1292,7 @@ class SQLCompiler(engine.Compiled):
column_translate[-1].update(translate_dict)
- newelem.right = selectable
+ newelem.right = selectable_
newelem.onclause = visit(newelem.onclause, **kw)
elif newelem.__visit_name__ is select_name:
column_translate.append({})
@@ -1299,7 +1401,7 @@ class SQLCompiler(engine.Compiled):
explicit_correlate_froms=correlate_froms,
implicit_correlate_froms=asfrom_froms)
- new_correlate_froms = set(sql._from_objects(*froms))
+ new_correlate_froms = set(selectable._from_objects(*froms))
all_correlate_froms = new_correlate_froms.union(correlate_froms)
new_entry = {
@@ -1461,11 +1563,11 @@ class SQLCompiler(engine.Compiled):
def limit_clause(self, select):
text = ""
if select._limit is not None:
- text += "\n LIMIT " + self.process(sql.literal(select._limit))
+ text += "\n LIMIT " + self.process(elements.literal(select._limit))
if select._offset is not None:
if select._limit is None:
text += "\n LIMIT -1"
- text += " OFFSET " + self.process(sql.literal(select._offset))
+ text += " OFFSET " + self.process(elements.literal(select._offset))
return text
def visit_table(self, table, asfrom=False, iscrud=False, ashint=False,
@@ -1692,7 +1794,7 @@ class SQLCompiler(engine.Compiled):
def _create_crud_bind_param(self, col, value, required=False, name=None):
if name is None:
name = col.key
- bindparam = sql.bindparam(name, value,
+ bindparam = elements.BindParameter(name, value,
type_=col.type, required=required,
quote=col.quote)
bindparam._is_crud = True
@@ -1732,7 +1834,7 @@ class SQLCompiler(engine.Compiled):
if self.column_keys is None:
parameters = {}
else:
- parameters = dict((sql._column_as_key(key), REQUIRED)
+ parameters = dict((elements._column_as_key(key), REQUIRED)
for key in self.column_keys
if not stmt_parameters or
key not in stmt_parameters)
@@ -1742,15 +1844,15 @@ class SQLCompiler(engine.Compiled):
if stmt_parameters is not None:
for k, v in stmt_parameters.items():
- colkey = sql._column_as_key(k)
+ colkey = elements._column_as_key(k)
if colkey is not None:
parameters.setdefault(colkey, v)
else:
# a non-Column expression on the left side;
# add it to values() in an "as-is" state,
# coercing right side to bound param
- if sql._is_literal(v):
- v = self.process(sql.bindparam(None, v, type_=k.type))
+ if elements._is_literal(v):
+ v = self.process(elements.BindParameter(None, v, type_=k.type))
else:
v = self.process(v.self_group())
@@ -1771,7 +1873,7 @@ class SQLCompiler(engine.Compiled):
# statements
if extra_tables and stmt_parameters:
normalized_params = dict(
- (sql._clause_element_as_expr(c), param)
+ (elements._clause_element_as_expr(c), param)
for c, param in stmt_parameters.items()
)
assert self.isupdate
@@ -1782,7 +1884,7 @@ class SQLCompiler(engine.Compiled):
affected_tables.add(t)
check_columns[c.key] = c
value = normalized_params[c]
- if sql._is_literal(value):
+ if elements._is_literal(value):
value = self._create_crud_bind_param(
c, value, required=value is REQUIRED)
else:
@@ -1816,7 +1918,7 @@ class SQLCompiler(engine.Compiled):
for c in stmt.table.columns:
if c.key in parameters and c.key not in check_columns:
value = parameters.pop(c.key)
- if sql._is_literal(value):
+ if elements._is_literal(value):
value = self._create_crud_bind_param(
c, value, required=value is REQUIRED,
name=c.key
@@ -1918,7 +2020,7 @@ class SQLCompiler(engine.Compiled):
if parameters and stmt_parameters:
check = set(parameters).intersection(
- sql._column_as_key(k) for k in stmt.parameters
+ elements._column_as_key(k) for k in stmt.parameters
).difference(check_columns)
if check:
raise exc.CompileError(
@@ -2013,7 +2115,7 @@ class SQLCompiler(engine.Compiled):
self.preparer.format_savepoint(savepoint_stmt)
-class DDLCompiler(engine.Compiled):
+class DDLCompiler(Compiled):
@util.memoized_property
def sql_compiler(self):
@@ -2183,7 +2285,7 @@ class DDLCompiler(engine.Compiled):
schema_name = None
ident = index.name
- if isinstance(ident, sql._truncated_label):
+ if isinstance(ident, elements._truncated_label):
max_ = self.dialect.max_index_name_length or \
self.dialect.max_identifier_length
if len(ident) > max_:
@@ -2343,7 +2445,7 @@ class DDLCompiler(engine.Compiled):
return text
-class GenericTypeCompiler(engine.TypeCompiler):
+class GenericTypeCompiler(TypeCompiler):
def visit_FLOAT(self, type_):
return "FLOAT"
diff --git a/lib/sqlalchemy/sql/ddl.py b/lib/sqlalchemy/sql/ddl.py
new file mode 100644
index 000000000..ee13c07dd
--- /dev/null
+++ b/lib/sqlalchemy/sql/ddl.py
@@ -0,0 +1,826 @@
+# schema/ddl.py
+# Copyright (C) 2009-2013 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 the hierarchy of DDL-defining schema items as well as routines
+to invoke them for a create/drop call.
+
+"""
+
+from .. import util
+from .elements import ClauseElement
+from .visitors import traverse
+from .base import Executable, _generative, SchemaVisitor, _bind_or_error
+from ..util import topological
+from .. import event
+from .. import exc
+
+class _DDLCompiles(ClauseElement):
+ def _compiler(self, dialect, **kw):
+ """Return a compiler appropriate for this ClauseElement, given a
+ Dialect."""
+
+ return dialect.ddl_compiler(dialect, self, **kw)
+
+
+class DDLElement(Executable, _DDLCompiles):
+ """Base class for DDL expression constructs.
+
+ This class is the base for the general purpose :class:`.DDL` class,
+ as well as the various create/drop clause constructs such as
+ :class:`.CreateTable`, :class:`.DropTable`, :class:`.AddConstraint`,
+ etc.
+
+ :class:`.DDLElement` integrates closely with SQLAlchemy events,
+ introduced in :ref:`event_toplevel`. An instance of one is
+ itself an event receiving callable::
+
+ event.listen(
+ users,
+ 'after_create',
+ AddConstraint(constraint).execute_if(dialect='postgresql')
+ )
+
+ See also:
+
+ :class:`.DDL`
+
+ :class:`.DDLEvents`
+
+ :ref:`event_toplevel`
+
+ :ref:`schema_ddl_sequences`
+
+ """
+
+ _execution_options = Executable.\
+ _execution_options.union({'autocommit': True})
+
+ target = None
+ on = None
+ dialect = None
+ callable_ = None
+
+ def execute(self, bind=None, target=None):
+ """Execute this DDL immediately.
+
+ Executes the DDL statement in isolation using the supplied
+ :class:`.Connectable` or
+ :class:`.Connectable` assigned to the ``.bind``
+ property, if not supplied. If the DDL has a conditional ``on``
+ criteria, it will be invoked with None as the event.
+
+ :param bind:
+ Optional, an ``Engine`` or ``Connection``. If not supplied, a valid
+ :class:`.Connectable` must be present in the
+ ``.bind`` property.
+
+ :param target:
+ Optional, defaults to None. The target SchemaItem for the
+ execute call. Will be passed to the ``on`` callable if any,
+ and may also provide string expansion data for the
+ statement. See ``execute_at`` for more information.
+
+ """
+
+ if bind is None:
+ bind = _bind_or_error(self)
+
+ if self._should_execute(target, bind):
+ return bind.execute(self.against(target))
+ else:
+ bind.engine.logger.info(
+ "DDL execution skipped, criteria not met.")
+
+ @util.deprecated("0.7", "See :class:`.DDLEvents`, as well as "
+ ":meth:`.DDLElement.execute_if`.")
+ def execute_at(self, event_name, target):
+ """Link execution of this DDL to the DDL lifecycle of a SchemaItem.
+
+ Links this ``DDLElement`` to a ``Table`` or ``MetaData`` instance,
+ executing it when that schema item is created or dropped. The DDL
+ statement will be executed using the same Connection and transactional
+ context as the Table create/drop itself. The ``.bind`` property of
+ this statement is ignored.
+
+ :param event:
+ One of the events defined in the schema item's ``.ddl_events``;
+ e.g. 'before-create', 'after-create', 'before-drop' or 'after-drop'
+
+ :param target:
+ The Table or MetaData instance for which this DDLElement will
+ be associated with.
+
+ A DDLElement instance can be linked to any number of schema items.
+
+ ``execute_at`` builds on the ``append_ddl_listener`` interface of
+ :class:`.MetaData` and :class:`.Table` objects.
+
+ Caveat: Creating or dropping a Table in isolation will also trigger
+ any DDL set to ``execute_at`` that Table's MetaData. This may change
+ in a future release.
+
+ """
+
+ def call_event(target, connection, **kw):
+ if self._should_execute_deprecated(event_name,
+ target, connection, **kw):
+ return connection.execute(self.against(target))
+
+ event.listen(target, "" + event_name.replace('-', '_'), call_event)
+
+ @_generative
+ def against(self, target):
+ """Return a copy of this DDL against a specific schema item."""
+
+ self.target = target
+
+ @_generative
+ def execute_if(self, dialect=None, callable_=None, state=None):
+ """Return a callable that will execute this
+ DDLElement conditionally.
+
+ Used to provide a wrapper for event listening::
+
+ event.listen(
+ metadata,
+ 'before_create',
+ DDL("my_ddl").execute_if(dialect='postgresql')
+ )
+
+ :param dialect: May be a string, tuple or a callable
+ predicate. If a string, it will be compared to the name of the
+ executing database dialect::
+
+ DDL('something').execute_if(dialect='postgresql')
+
+ If a tuple, specifies multiple dialect names::
+
+ DDL('something').execute_if(dialect=('postgresql', 'mysql'))
+
+ :param callable_: A callable, which will be invoked with
+ four positional arguments as well as optional keyword
+ arguments:
+
+ :ddl:
+ This DDL element.
+
+ :target:
+ The :class:`.Table` or :class:`.MetaData` object which is the
+ target of this event. May be None if the DDL is executed
+ explicitly.
+
+ :bind:
+ The :class:`.Connection` being used for DDL execution
+
+ :tables:
+ Optional keyword argument - a list of Table objects which are to
+ be created/ dropped within a MetaData.create_all() or drop_all()
+ method call.
+
+ :state:
+ Optional keyword argument - will be the ``state`` argument
+ passed to this function.
+
+ :checkfirst:
+ Keyword argument, will be True if the 'checkfirst' flag was
+ set during the call to ``create()``, ``create_all()``,
+ ``drop()``, ``drop_all()``.
+
+ If the callable returns a true value, the DDL statement will be
+ executed.
+
+ :param state: any value which will be passed to the callable_
+ as the ``state`` keyword argument.
+
+ See also:
+
+ :class:`.DDLEvents`
+
+ :ref:`event_toplevel`
+
+ """
+ self.dialect = dialect
+ self.callable_ = callable_
+ self.state = state
+
+ def _should_execute(self, target, bind, **kw):
+ if self.on is not None and \
+ not self._should_execute_deprecated(None, target, bind, **kw):
+ return False
+
+ if isinstance(self.dialect, util.string_types):
+ if self.dialect != bind.engine.name:
+ return False
+ elif isinstance(self.dialect, (tuple, list, set)):
+ if bind.engine.name not in self.dialect:
+ return False
+ if self.callable_ is not None and \
+ not self.callable_(self, target, bind, state=self.state, **kw):
+ return False
+
+ return True
+
+ def _should_execute_deprecated(self, event, target, bind, **kw):
+ if self.on is None:
+ return True
+ elif isinstance(self.on, util.string_types):
+ return self.on == bind.engine.name
+ elif isinstance(self.on, (tuple, list, set)):
+ return bind.engine.name in self.on
+ else:
+ return self.on(self, event, target, bind, **kw)
+
+ def __call__(self, target, bind, **kw):
+ """Execute the DDL as a ddl_listener."""
+
+ if self._should_execute(target, bind, **kw):
+ return bind.execute(self.against(target))
+
+ def _check_ddl_on(self, on):
+ if (on is not None and
+ (not isinstance(on, util.string_types + (tuple, list, set)) and
+ not util.callable(on))):
+ raise exc.ArgumentError(
+ "Expected the name of a database dialect, a tuple "
+ "of names, or a callable for "
+ "'on' criteria, got type '%s'." % type(on).__name__)
+
+ def bind(self):
+ if self._bind:
+ return self._bind
+
+ def _set_bind(self, bind):
+ self._bind = bind
+ bind = property(bind, _set_bind)
+
+ def _generate(self):
+ s = self.__class__.__new__(self.__class__)
+ s.__dict__ = self.__dict__.copy()
+ return s
+
+
+class DDL(DDLElement):
+ """A literal DDL statement.
+
+ Specifies literal SQL DDL to be executed by the database. DDL objects
+ function as DDL event listeners, and can be subscribed to those events
+ listed in :class:`.DDLEvents`, using either :class:`.Table` or
+ :class:`.MetaData` objects as targets. Basic templating support allows
+ a single DDL instance to handle repetitive tasks for multiple tables.
+
+ Examples::
+
+ from sqlalchemy import event, DDL
+
+ tbl = Table('users', metadata, Column('uid', Integer))
+ event.listen(tbl, 'before_create', DDL('DROP TRIGGER users_trigger'))
+
+ spow = DDL('ALTER TABLE %(table)s SET secretpowers TRUE')
+ event.listen(tbl, 'after_create', spow.execute_if(dialect='somedb'))
+
+ drop_spow = DDL('ALTER TABLE users SET secretpowers FALSE')
+ connection.execute(drop_spow)
+
+ When operating on Table events, the following ``statement``
+ string substitions are available::
+
+ %(table)s - the Table name, with any required quoting applied
+ %(schema)s - the schema name, with any required quoting applied
+ %(fullname)s - the Table name including schema, quoted if needed
+
+ The DDL's "context", if any, will be combined with the standard
+ substutions noted above. Keys present in the context will override
+ the standard substitutions.
+
+ """
+
+ __visit_name__ = "ddl"
+
+ def __init__(self, statement, on=None, context=None, bind=None):
+ """Create a DDL statement.
+
+ :param statement:
+ A string or unicode string to be executed. Statements will be
+ processed with Python's string formatting operator. See the
+ ``context`` argument and the ``execute_at`` method.
+
+ A literal '%' in a statement must be escaped as '%%'.
+
+ SQL bind parameters are not available in DDL statements.
+
+ :param on:
+ .. deprecated:: 0.7
+ See :meth:`.DDLElement.execute_if`.
+
+ Optional filtering criteria. May be a string, tuple or a callable
+ predicate. If a string, it will be compared to the name of the
+ executing database dialect::
+
+ DDL('something', on='postgresql')
+
+ If a tuple, specifies multiple dialect names::
+
+ DDL('something', on=('postgresql', 'mysql'))
+
+ If a callable, it will be invoked with four positional arguments
+ as well as optional keyword arguments:
+
+ :ddl:
+ This DDL element.
+
+ :event:
+ The name of the event that has triggered this DDL, such as
+ 'after-create' Will be None if the DDL is executed explicitly.
+
+ :target:
+ The ``Table`` or ``MetaData`` object which is the target of
+ this event. May be None if the DDL is executed explicitly.
+
+ :connection:
+ The ``Connection`` being used for DDL execution
+
+ :tables:
+ Optional keyword argument - a list of Table objects which are to
+ be created/ dropped within a MetaData.create_all() or drop_all()
+ method call.
+
+
+ If the callable returns a true value, the DDL statement will be
+ executed.
+
+ :param context:
+ Optional dictionary, defaults to None. These values will be
+ available for use in string substitutions on the DDL statement.
+
+ :param bind:
+ Optional. A :class:`.Connectable`, used by
+ default when ``execute()`` is invoked without a bind argument.
+
+
+ See also:
+
+ :class:`.DDLEvents`
+ :mod:`sqlalchemy.event`
+
+ """
+
+ if not isinstance(statement, util.string_types):
+ raise exc.ArgumentError(
+ "Expected a string or unicode SQL statement, got '%r'" %
+ statement)
+
+ self.statement = statement
+ self.context = context or {}
+
+ self._check_ddl_on(on)
+ self.on = on
+ self._bind = bind
+
+ def __repr__(self):
+ return '<%s@%s; %s>' % (
+ type(self).__name__, id(self),
+ ', '.join([repr(self.statement)] +
+ ['%s=%r' % (key, getattr(self, key))
+ for key in ('on', 'context')
+ if getattr(self, key)]))
+
+
+
+class _CreateDropBase(DDLElement):
+ """Base class for DDL constucts that represent CREATE and DROP or
+ equivalents.
+
+ The common theme of _CreateDropBase is a single
+ ``element`` attribute which refers to the element
+ to be created or dropped.
+
+ """
+
+ def __init__(self, element, on=None, bind=None):
+ self.element = element
+ self._check_ddl_on(on)
+ self.on = on
+ self.bind = bind
+
+ def _create_rule_disable(self, compiler):
+ """Allow disable of _create_rule using a callable.
+
+ Pass to _create_rule using
+ util.portable_instancemethod(self._create_rule_disable)
+ to retain serializability.
+
+ """
+ return False
+
+
+class CreateSchema(_CreateDropBase):
+ """Represent a CREATE SCHEMA statement.
+
+ .. versionadded:: 0.7.4
+
+ The argument here is the string name of the schema.
+
+ """
+
+ __visit_name__ = "create_schema"
+
+ def __init__(self, name, quote=None, **kw):
+ """Create a new :class:`.CreateSchema` construct."""
+
+ self.quote = quote
+ super(CreateSchema, self).__init__(name, **kw)
+
+
+class DropSchema(_CreateDropBase):
+ """Represent a DROP SCHEMA statement.
+
+ The argument here is the string name of the schema.
+
+ .. versionadded:: 0.7.4
+
+ """
+
+ __visit_name__ = "drop_schema"
+
+ def __init__(self, name, quote=None, cascade=False, **kw):
+ """Create a new :class:`.DropSchema` construct."""
+
+ self.quote = quote
+ self.cascade = cascade
+ super(DropSchema, self).__init__(name, **kw)
+
+
+class CreateTable(_CreateDropBase):
+ """Represent a CREATE TABLE statement."""
+
+ __visit_name__ = "create_table"
+
+ def __init__(self, element, on=None, bind=None):
+ """Create a :class:`.CreateTable` construct.
+
+ :param element: a :class:`.Table` that's the subject
+ of the CREATE
+ :param on: See the description for 'on' in :class:`.DDL`.
+ :param bind: See the description for 'bind' in :class:`.DDL`.
+
+ """
+ super(CreateTable, self).__init__(element, on=on, bind=bind)
+ self.columns = [CreateColumn(column)
+ for column in element.columns
+ ]
+
+
+class _DropView(_CreateDropBase):
+ """Semi-public 'DROP VIEW' construct.
+
+ Used by the test suite for dialect-agnostic drops of views.
+ This object will eventually be part of a public "view" API.
+
+ """
+ __visit_name__ = "drop_view"
+
+
+class CreateColumn(_DDLCompiles):
+ """Represent a :class:`.Column` as rendered in a CREATE TABLE statement,
+ via the :class:`.CreateTable` construct.
+
+ This is provided to support custom column DDL within the generation
+ of CREATE TABLE statements, by using the
+ compiler extension documented in :ref:`sqlalchemy.ext.compiler_toplevel`
+ to extend :class:`.CreateColumn`.
+
+ Typical integration is to examine the incoming :class:`.Column`
+ object, and to redirect compilation if a particular flag or condition
+ is found::
+
+ from sqlalchemy import schema
+ from sqlalchemy.ext.compiler import compiles
+
+ @compiles(schema.CreateColumn)
+ def compile(element, compiler, **kw):
+ column = element.element
+
+ if "special" not in column.info:
+ return compiler.visit_create_column(element, **kw)
+
+ text = "%s SPECIAL DIRECTIVE %s" % (
+ column.name,
+ compiler.type_compiler.process(column.type)
+ )
+ default = compiler.get_column_default_string(column)
+ if default is not None:
+ text += " DEFAULT " + default
+
+ if not column.nullable:
+ text += " NOT NULL"
+
+ if column.constraints:
+ text += " ".join(
+ compiler.process(const)
+ for const in column.constraints)
+ return text
+
+ The above construct can be applied to a :class:`.Table` as follows::
+
+ from sqlalchemy import Table, Metadata, Column, Integer, String
+ from sqlalchemy import schema
+
+ metadata = MetaData()
+
+ table = Table('mytable', MetaData(),
+ Column('x', Integer, info={"special":True}, primary_key=True),
+ Column('y', String(50)),
+ Column('z', String(20), info={"special":True})
+ )
+
+ metadata.create_all(conn)
+
+ Above, the directives we've added to the :attr:`.Column.info` collection
+ will be detected by our custom compilation scheme::
+
+ CREATE TABLE mytable (
+ x SPECIAL DIRECTIVE INTEGER NOT NULL,
+ y VARCHAR(50),
+ z SPECIAL DIRECTIVE VARCHAR(20),
+ PRIMARY KEY (x)
+ )
+
+ .. versionadded:: 0.8 The :class:`.CreateColumn` construct was added
+ to support custom column creation styles.
+
+ """
+ __visit_name__ = 'create_column'
+
+ def __init__(self, element):
+ self.element = element
+
+
+class DropTable(_CreateDropBase):
+ """Represent a DROP TABLE statement."""
+
+ __visit_name__ = "drop_table"
+
+
+class CreateSequence(_CreateDropBase):
+ """Represent a CREATE SEQUENCE statement."""
+
+ __visit_name__ = "create_sequence"
+
+
+class DropSequence(_CreateDropBase):
+ """Represent a DROP SEQUENCE statement."""
+
+ __visit_name__ = "drop_sequence"
+
+
+class CreateIndex(_CreateDropBase):
+ """Represent a CREATE INDEX statement."""
+
+ __visit_name__ = "create_index"
+
+
+class DropIndex(_CreateDropBase):
+ """Represent a DROP INDEX statement."""
+
+ __visit_name__ = "drop_index"
+
+
+class AddConstraint(_CreateDropBase):
+ """Represent an ALTER TABLE ADD CONSTRAINT statement."""
+
+ __visit_name__ = "add_constraint"
+
+ def __init__(self, element, *args, **kw):
+ super(AddConstraint, self).__init__(element, *args, **kw)
+ element._create_rule = util.portable_instancemethod(
+ self._create_rule_disable)
+
+
+class DropConstraint(_CreateDropBase):
+ """Represent an ALTER TABLE DROP CONSTRAINT statement."""
+
+ __visit_name__ = "drop_constraint"
+
+ def __init__(self, element, cascade=False, **kw):
+ self.cascade = cascade
+ super(DropConstraint, self).__init__(element, **kw)
+ element._create_rule = util.portable_instancemethod(
+ self._create_rule_disable)
+
+
+class DDLBase(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
+ 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 is not None:
+ tables = self.tables
+ else:
+ tables = list(metadata.tables.values())
+ collection = [t for t in 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(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(CreateSequence(sequence))
+
+ def visit_index(self, index):
+ self.connection.execute(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 is not None:
+ tables = self.tables
+ else:
+ tables = list(metadata.tables.values())
+
+ collection = [
+ t
+ for t in reversed(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(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(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(DropSequence(sequence))
+
+def sort_tables(tables, skip_fn=None, extra_dependencies=None):
+ """sort a collection of Table objects in order of
+ their foreign-key dependency."""
+
+ tables = list(tables)
+ tuples = []
+ if extra_dependencies is not None:
+ tuples.extend(extra_dependencies)
+
+ def visit_foreign_key(fkey):
+ if fkey.use_alter:
+ return
+ elif skip_fn and skip_fn(fkey):
+ return
+ parent_table = fkey.column.table
+ if parent_table in tables:
+ child_table = fkey.parent.table
+ if parent_table is not child_table:
+ tuples.append((parent_table, child_table))
+
+ for table in tables:
+ traverse(table,
+ {'schema_visitor': True},
+ {'foreign_key': visit_foreign_key})
+
+ tuples.extend(
+ [parent, table] for parent in table._extra_dependencies
+ )
+
+ return list(topological.sort(tuples, tables))
+
diff --git a/lib/sqlalchemy/sql/default_comparator.py b/lib/sqlalchemy/sql/default_comparator.py
new file mode 100644
index 000000000..c9125108a
--- /dev/null
+++ b/lib/sqlalchemy/sql/default_comparator.py
@@ -0,0 +1,281 @@
+# sql/default_comparator.py
+# Copyright (C) 2005-2013 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
+
+"""Default implementation of SQL comparison operations.
+"""
+
+from .. import exc, util
+from . import operators
+from . import type_api
+from .elements import BindParameter, True_, False_, BinaryExpression, \
+ Null, _const_expr, _clause_element_as_expr, \
+ ClauseList, ColumnElement, TextClause, UnaryExpression, \
+ collate, _is_literal
+from .selectable import SelectBase, Alias, Selectable, ScalarSelect
+
+class _DefaultColumnComparator(operators.ColumnOperators):
+ """Defines comparison and math operations.
+
+ See :class:`.ColumnOperators` and :class:`.Operators` for descriptions
+ of all operations.
+
+ """
+
+ @util.memoized_property
+ def type(self):
+ return self.expr.type
+
+ def operate(self, op, *other, **kwargs):
+ o = self.operators[op.__name__]
+ return o[0](self, self.expr, op, *(other + o[1:]), **kwargs)
+
+ def reverse_operate(self, op, other, **kwargs):
+ o = self.operators[op.__name__]
+ return o[0](self, self.expr, op, other, reverse=True, *o[1:], **kwargs)
+
+ def _adapt_expression(self, op, other_comparator):
+ """evaluate the return type of <self> <op> <othertype>,
+ and apply any adaptations to the given operator.
+
+ This method determines the type of a resulting binary expression
+ given two source types and an operator. For example, two
+ :class:`.Column` objects, both of the type :class:`.Integer`, will
+ produce a :class:`.BinaryExpression` that also has the type
+ :class:`.Integer` when compared via the addition (``+``) operator.
+ However, using the addition operator with an :class:`.Integer`
+ and a :class:`.Date` object will produce a :class:`.Date`, assuming
+ "days delta" behavior by the database (in reality, most databases
+ other than Postgresql don't accept this particular operation).
+
+ The method returns a tuple of the form <operator>, <type>.
+ The resulting operator and type will be those applied to the
+ resulting :class:`.BinaryExpression` as the final operator and the
+ right-hand side of the expression.
+
+ Note that only a subset of operators make usage of
+ :meth:`._adapt_expression`,
+ including math operators and user-defined operators, but not
+ boolean comparison or special SQL keywords like MATCH or BETWEEN.
+
+ """
+ return op, other_comparator.type
+
+ def _boolean_compare(self, expr, op, obj, negate=None, reverse=False,
+ _python_is_types=(util.NoneType, bool),
+ **kwargs):
+
+ if isinstance(obj, _python_is_types + (Null, True_, False_)):
+
+ # allow x ==/!= True/False to be treated as a literal.
+ # this comes out to "== / != true/false" or "1/0" if those
+ # constants aren't supported and works on all platforms
+ if op in (operators.eq, operators.ne) and \
+ isinstance(obj, (bool, True_, False_)):
+ return BinaryExpression(expr,
+ obj,
+ op,
+ type_=type_api.BOOLEANTYPE,
+ negate=negate, modifiers=kwargs)
+ else:
+ # all other None/True/False uses IS, IS NOT
+ if op in (operators.eq, operators.is_):
+ return BinaryExpression(expr, _const_expr(obj),
+ operators.is_,
+ negate=operators.isnot)
+ elif op in (operators.ne, operators.isnot):
+ return BinaryExpression(expr, _const_expr(obj),
+ operators.isnot,
+ negate=operators.is_)
+ else:
+ raise exc.ArgumentError(
+ "Only '=', '!=', 'is_()', 'isnot()' operators can "
+ "be used with None/True/False")
+ else:
+ obj = self._check_literal(expr, op, obj)
+
+ if reverse:
+ return BinaryExpression(obj,
+ expr,
+ op,
+ type_=type_api.BOOLEANTYPE,
+ negate=negate, modifiers=kwargs)
+ else:
+ return BinaryExpression(expr,
+ obj,
+ op,
+ type_=type_api.BOOLEANTYPE,
+ negate=negate, modifiers=kwargs)
+
+ def _binary_operate(self, expr, op, obj, reverse=False, result_type=None,
+ **kw):
+ obj = self._check_literal(expr, op, obj)
+
+ if reverse:
+ left, right = obj, expr
+ else:
+ left, right = expr, obj
+
+ if result_type is None:
+ op, result_type = left.comparator._adapt_expression(
+ op, right.comparator)
+
+ return BinaryExpression(left, right, op, type_=result_type)
+
+ def _scalar(self, expr, op, fn, **kw):
+ return fn(expr)
+
+ def _in_impl(self, expr, op, seq_or_selectable, negate_op, **kw):
+ seq_or_selectable = _clause_element_as_expr(seq_or_selectable)
+
+ if isinstance(seq_or_selectable, ScalarSelect):
+ return self._boolean_compare(expr, op, seq_or_selectable,
+ negate=negate_op)
+ elif isinstance(seq_or_selectable, SelectBase):
+
+ # TODO: if we ever want to support (x, y, z) IN (select x,
+ # y, z from table), we would need a multi-column version of
+ # as_scalar() to produce a multi- column selectable that
+ # does not export itself as a FROM clause
+
+ return self._boolean_compare(
+ expr, op, seq_or_selectable.as_scalar(),
+ negate=negate_op, **kw)
+ elif isinstance(seq_or_selectable, (Selectable, TextClause)):
+ return self._boolean_compare(expr, op, seq_or_selectable,
+ negate=negate_op, **kw)
+
+ # Handle non selectable arguments as sequences
+ args = []
+ for o in seq_or_selectable:
+ if not _is_literal(o):
+ if not isinstance(o, operators.ColumnOperators):
+ raise exc.InvalidRequestError('in() function accept'
+ 's either a list of non-selectable values, '
+ 'or a selectable: %r' % o)
+ elif o is None:
+ o = Null()
+ else:
+ o = expr._bind_param(op, o)
+ args.append(o)
+ if len(args) == 0:
+
+ # Special case handling for empty IN's, behave like
+ # comparison against zero row selectable. We use != to
+ # build the contradiction as it handles NULL values
+ # appropriately, i.e. "not (x IN ())" should not return NULL
+ # values for x.
+
+ util.warn('The IN-predicate on "%s" was invoked with an '
+ 'empty sequence. This results in a '
+ 'contradiction, which nonetheless can be '
+ 'expensive to evaluate. Consider alternative '
+ 'strategies for improved performance.' % expr)
+ if op is operators.in_op:
+ return expr != expr
+ else:
+ return expr == expr
+
+ return self._boolean_compare(expr, op,
+ ClauseList(*args).self_group(against=op),
+ negate=negate_op)
+
+ def _unsupported_impl(self, expr, op, *arg, **kw):
+ raise NotImplementedError("Operator '%s' is not supported on "
+ "this expression" % op.__name__)
+
+ def _neg_impl(self, expr, op, **kw):
+ """See :meth:`.ColumnOperators.__neg__`."""
+ return UnaryExpression(expr, operator=operators.neg)
+
+ def _match_impl(self, expr, op, other, **kw):
+ """See :meth:`.ColumnOperators.match`."""
+ return self._boolean_compare(expr, operators.match_op,
+ self._check_literal(expr, operators.match_op,
+ other))
+
+ def _distinct_impl(self, expr, op, **kw):
+ """See :meth:`.ColumnOperators.distinct`."""
+ return UnaryExpression(expr, operator=operators.distinct_op,
+ type_=expr.type)
+
+ def _between_impl(self, expr, op, cleft, cright, **kw):
+ """See :meth:`.ColumnOperators.between`."""
+ return BinaryExpression(
+ expr,
+ ClauseList(
+ self._check_literal(expr, operators.and_, cleft),
+ self._check_literal(expr, operators.and_, cright),
+ operator=operators.and_,
+ group=False),
+ operators.between_op)
+
+ def _collate_impl(self, expr, op, other, **kw):
+ return collate(expr, other)
+
+ # a mapping of operators with the method they use, along with
+ # their negated operator for comparison operators
+ operators = {
+ "add": (_binary_operate,),
+ "mul": (_binary_operate,),
+ "sub": (_binary_operate,),
+ "div": (_binary_operate,),
+ "mod": (_binary_operate,),
+ "truediv": (_binary_operate,),
+ "custom_op": (_binary_operate,),
+ "concat_op": (_binary_operate,),
+ "lt": (_boolean_compare, operators.ge),
+ "le": (_boolean_compare, operators.gt),
+ "ne": (_boolean_compare, operators.eq),
+ "gt": (_boolean_compare, operators.le),
+ "ge": (_boolean_compare, operators.lt),
+ "eq": (_boolean_compare, operators.ne),
+ "like_op": (_boolean_compare, operators.notlike_op),
+ "ilike_op": (_boolean_compare, operators.notilike_op),
+ "notlike_op": (_boolean_compare, operators.like_op),
+ "notilike_op": (_boolean_compare, operators.ilike_op),
+ "contains_op": (_boolean_compare, operators.notcontains_op),
+ "startswith_op": (_boolean_compare, operators.notstartswith_op),
+ "endswith_op": (_boolean_compare, operators.notendswith_op),
+ "desc_op": (_scalar, UnaryExpression._create_desc),
+ "asc_op": (_scalar, UnaryExpression._create_asc),
+ "nullsfirst_op": (_scalar, UnaryExpression._create_nullsfirst),
+ "nullslast_op": (_scalar, UnaryExpression._create_nullslast),
+ "in_op": (_in_impl, operators.notin_op),
+ "notin_op": (_in_impl, operators.in_op),
+ "is_": (_boolean_compare, operators.is_),
+ "isnot": (_boolean_compare, operators.isnot),
+ "collate": (_collate_impl,),
+ "match_op": (_match_impl,),
+ "distinct_op": (_distinct_impl,),
+ "between_op": (_between_impl, ),
+ "neg": (_neg_impl,),
+ "getitem": (_unsupported_impl,),
+ "lshift": (_unsupported_impl,),
+ "rshift": (_unsupported_impl,),
+ }
+
+ def _check_literal(self, expr, operator, other):
+ if isinstance(other, (ColumnElement, TextClause)):
+ if isinstance(other, BindParameter) and \
+ other.type._isnull:
+ # TODO: perhaps we should not mutate the incoming
+ # bindparam() here and instead make a copy of it.
+ # this might be the only place that we're mutating
+ # an incoming construct.
+ other.type = expr.type
+ return other
+ elif hasattr(other, '__clause_element__'):
+ other = other.__clause_element__()
+ elif isinstance(other, type_api.TypeEngine.Comparator):
+ other = other.expr
+
+ if isinstance(other, (SelectBase, Alias)):
+ return other.as_scalar()
+ elif not isinstance(other, (ColumnElement, TextClause)):
+ return expr._bind_param(operator, other)
+ else:
+ return other
+
diff --git a/lib/sqlalchemy/sql/dml.py b/lib/sqlalchemy/sql/dml.py
new file mode 100644
index 000000000..cd06bf21a
--- /dev/null
+++ b/lib/sqlalchemy/sql/dml.py
@@ -0,0 +1,668 @@
+# schema/dml.py
+# Copyright (C) 2009-2013 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
+"""
+Provide :class:`.Insert`, :class:`.Update` and :class:`.Delete`.
+
+"""
+
+from .base import Executable, _generative, _from_objects
+from .elements import ClauseElement, _literal_as_text, Null, and_, _clone
+from .selectable import _interpret_as_from, _interpret_as_select, HasPrefixes
+from .. import util
+from .. import exc
+
+class UpdateBase(HasPrefixes, Executable, ClauseElement):
+ """Form the base for ``INSERT``, ``UPDATE``, and ``DELETE`` statements.
+
+ """
+
+ __visit_name__ = 'update_base'
+
+ _execution_options = \
+ Executable._execution_options.union({'autocommit': True})
+ kwargs = util.immutabledict()
+ _hints = util.immutabledict()
+ _prefixes = ()
+
+ def _process_colparams(self, parameters):
+ def process_single(p):
+ if isinstance(p, (list, tuple)):
+ return dict(
+ (c.key, pval)
+ for c, pval in zip(self.table.c, p)
+ )
+ else:
+ return p
+
+ if isinstance(parameters, (list, tuple)) and \
+ isinstance(parameters[0], (list, tuple, dict)):
+
+ if not self._supports_multi_parameters:
+ raise exc.InvalidRequestError(
+ "This construct does not support "
+ "multiple parameter sets.")
+
+ return [process_single(p) for p in parameters], True
+ else:
+ return process_single(parameters), False
+
+ def params(self, *arg, **kw):
+ """Set the parameters for the statement.
+
+ This method raises ``NotImplementedError`` on the base class,
+ and is overridden by :class:`.ValuesBase` to provide the
+ SET/VALUES clause of UPDATE and INSERT.
+
+ """
+ raise NotImplementedError(
+ "params() is not supported for INSERT/UPDATE/DELETE statements."
+ " To set the values for an INSERT or UPDATE statement, use"
+ " stmt.values(**parameters).")
+
+ def bind(self):
+ """Return a 'bind' linked to this :class:`.UpdateBase`
+ or a :class:`.Table` associated with it.
+
+ """
+ return self._bind or self.table.bind
+
+ def _set_bind(self, bind):
+ self._bind = bind
+ bind = property(bind, _set_bind)
+
+ @_generative
+ def returning(self, *cols):
+ """Add a RETURNING or equivalent clause to this statement.
+
+ The given list of columns represent columns within the table that is
+ the target of the INSERT, UPDATE, or DELETE. Each element can be any
+ column expression. :class:`~sqlalchemy.schema.Table` objects will be
+ expanded into their individual columns.
+
+ Upon compilation, a RETURNING clause, or database equivalent,
+ will be rendered within the statement. For INSERT and UPDATE,
+ the values are the newly inserted/updated values. For DELETE,
+ the values are those of the rows which were deleted.
+
+ Upon execution, the values of the columns to be returned
+ are made available via the result set and can be iterated
+ using ``fetchone()`` and similar. For DBAPIs which do not
+ natively support returning values (i.e. cx_oracle),
+ SQLAlchemy will approximate this behavior at the result level
+ so that a reasonable amount of behavioral neutrality is
+ provided.
+
+ Note that not all databases/DBAPIs
+ support RETURNING. For those backends with no support,
+ an exception is raised upon compilation and/or execution.
+ For those who do support it, the functionality across backends
+ varies greatly, including restrictions on executemany()
+ and other statements which return multiple rows. Please
+ read the documentation notes for the database in use in
+ order to determine the availability of RETURNING.
+
+ """
+ self._returning = cols
+
+ @_generative
+ def with_hint(self, text, selectable=None, dialect_name="*"):
+ """Add a table hint for a single table to this
+ INSERT/UPDATE/DELETE statement.
+
+ .. note::
+
+ :meth:`.UpdateBase.with_hint` currently applies only to
+ Microsoft SQL Server. For MySQL INSERT/UPDATE/DELETE hints, use
+ :meth:`.UpdateBase.prefix_with`.
+
+ The text of the hint is rendered in the appropriate
+ location for the database backend in use, relative
+ to the :class:`.Table` that is the subject of this
+ statement, or optionally to that of the given
+ :class:`.Table` passed as the ``selectable`` argument.
+
+ The ``dialect_name`` option will limit the rendering of a particular
+ hint to a particular backend. Such as, to add a hint
+ that only takes effect for SQL Server::
+
+ mytable.insert().with_hint("WITH (PAGLOCK)", dialect_name="mssql")
+
+ .. versionadded:: 0.7.6
+
+ :param text: Text of the hint.
+ :param selectable: optional :class:`.Table` that specifies
+ an element of the FROM clause within an UPDATE or DELETE
+ to be the subject of the hint - applies only to certain backends.
+ :param dialect_name: defaults to ``*``, if specified as the name
+ of a particular dialect, will apply these hints only when
+ that dialect is in use.
+ """
+ if selectable is None:
+ selectable = self.table
+
+ self._hints = self._hints.union(
+ {(selectable, dialect_name): text})
+
+
+class ValuesBase(UpdateBase):
+ """Supplies support for :meth:`.ValuesBase.values` to
+ INSERT and UPDATE constructs."""
+
+ __visit_name__ = 'values_base'
+
+ _supports_multi_parameters = False
+ _has_multi_parameters = False
+ select = None
+
+ def __init__(self, table, values, prefixes):
+ self.table = _interpret_as_from(table)
+ self.parameters, self._has_multi_parameters = \
+ self._process_colparams(values)
+ if prefixes:
+ self._setup_prefixes(prefixes)
+
+ @_generative
+ def values(self, *args, **kwargs):
+ """specify a fixed VALUES clause for an INSERT statement, or the SET
+ clause for an UPDATE.
+
+ Note that the :class:`.Insert` and :class:`.Update` constructs support
+ per-execution time formatting of the VALUES and/or SET clauses,
+ based on the arguments passed to :meth:`.Connection.execute`. However,
+ the :meth:`.ValuesBase.values` method can be used to "fix" a particular
+ set of parameters into the statement.
+
+ Multiple calls to :meth:`.ValuesBase.values` will produce a new
+ construct, each one with the parameter list modified to include
+ the new parameters sent. In the typical case of a single
+ dictionary of parameters, the newly passed keys will replace
+ the same keys in the previous construct. In the case of a list-based
+ "multiple values" construct, each new list of values is extended
+ onto the existing list of values.
+
+ :param \**kwargs: key value pairs representing the string key
+ of a :class:`.Column` mapped to the value to be rendered into the
+ VALUES or SET clause::
+
+ users.insert().values(name="some name")
+
+ users.update().where(users.c.id==5).values(name="some name")
+
+ :param \*args: Alternatively, a dictionary, tuple or list
+ of dictionaries or tuples can be passed as a single positional
+ argument in order to form the VALUES or
+ SET clause of the statement. The single dictionary form
+ works the same as the kwargs form::
+
+ users.insert().values({"name": "some name"})
+
+ If a tuple is passed, the tuple should contain the same number
+ of columns as the target :class:`.Table`::
+
+ users.insert().values((5, "some name"))
+
+ The :class:`.Insert` construct also supports multiply-rendered VALUES
+ construct, for those backends which support this SQL syntax
+ (SQLite, Postgresql, MySQL). This mode is indicated by passing a list
+ of one or more dictionaries/tuples::
+
+ users.insert().values([
+ {"name": "some name"},
+ {"name": "some other name"},
+ {"name": "yet another name"},
+ ])
+
+ In the case of an :class:`.Update`
+ construct, only the single dictionary/tuple form is accepted,
+ else an exception is raised. It is also an exception case to
+ attempt to mix the single-/multiple- value styles together,
+ either through multiple :meth:`.ValuesBase.values` calls
+ or by sending a list + kwargs at the same time.
+
+ .. note::
+
+ Passing a multiple values list is *not* the same
+ as passing a multiple values list to the :meth:`.Connection.execute`
+ method. Passing a list of parameter sets to :meth:`.ValuesBase.values`
+ produces a construct of this form::
+
+ INSERT INTO table (col1, col2, col3) VALUES
+ (col1_0, col2_0, col3_0),
+ (col1_1, col2_1, col3_1),
+ ...
+
+ whereas a multiple list passed to :meth:`.Connection.execute`
+ has the effect of using the DBAPI
+ `executemany() <http://www.python.org/dev/peps/pep-0249/#id18>`_
+ method, which provides a high-performance system of invoking
+ a single-row INSERT statement many times against a series
+ of parameter sets. The "executemany" style is supported by
+ all database backends, as it does not depend on a special SQL
+ syntax.
+
+ .. versionadded:: 0.8
+ Support for multiple-VALUES INSERT statements.
+
+
+ .. seealso::
+
+ :ref:`inserts_and_updates` - SQL Expression
+ Language Tutorial
+
+ :func:`~.expression.insert` - produce an ``INSERT`` statement
+
+ :func:`~.expression.update` - produce an ``UPDATE`` statement
+
+ """
+ if self.select is not None:
+ raise exc.InvalidRequestError(
+ "This construct already inserts from a SELECT")
+ if self._has_multi_parameters and kwargs:
+ raise exc.InvalidRequestError(
+ "This construct already has multiple parameter sets.")
+
+ if args:
+ if len(args) > 1:
+ raise exc.ArgumentError(
+ "Only a single dictionary/tuple or list of "
+ "dictionaries/tuples is accepted positionally.")
+ v = args[0]
+ else:
+ v = {}
+
+ if self.parameters is None:
+ self.parameters, self._has_multi_parameters = \
+ self._process_colparams(v)
+ else:
+ if self._has_multi_parameters:
+ self.parameters = list(self.parameters)
+ p, self._has_multi_parameters = self._process_colparams(v)
+ if not self._has_multi_parameters:
+ raise exc.ArgumentError(
+ "Can't mix single-values and multiple values "
+ "formats in one statement")
+
+ self.parameters.extend(p)
+ else:
+ self.parameters = self.parameters.copy()
+ p, self._has_multi_parameters = self._process_colparams(v)
+ if self._has_multi_parameters:
+ raise exc.ArgumentError(
+ "Can't mix single-values and multiple values "
+ "formats in one statement")
+ self.parameters.update(p)
+
+ if kwargs:
+ if self._has_multi_parameters:
+ raise exc.ArgumentError(
+ "Can't pass kwargs and multiple parameter sets "
+ "simultaenously")
+ else:
+ self.parameters.update(kwargs)
+
+
+class Insert(ValuesBase):
+ """Represent an INSERT construct.
+
+ The :class:`.Insert` object is created using the
+ :func:`~.expression.insert()` function.
+
+ .. seealso::
+
+ :ref:`coretutorial_insert_expressions`
+
+ """
+ __visit_name__ = 'insert'
+
+ _supports_multi_parameters = True
+
+ def __init__(self,
+ table,
+ values=None,
+ inline=False,
+ bind=None,
+ prefixes=None,
+ returning=None,
+ **kwargs):
+ """Construct an :class:`.Insert` object.
+
+ Similar functionality is available via the
+ :meth:`~.TableClause.insert` method on
+ :class:`~.schema.Table`.
+
+ :param table: :class:`.TableClause` which is the subject of the insert.
+
+ :param values: collection of values to be inserted; see
+ :meth:`.Insert.values` for a description of allowed formats here.
+ Can be omitted entirely; a :class:`.Insert` construct will also
+ dynamically render the VALUES clause at execution time based on
+ the parameters passed to :meth:`.Connection.execute`.
+
+ :param inline: if True, SQL defaults will be compiled 'inline' into the
+ statement and not pre-executed.
+
+ If both `values` and compile-time bind parameters are present, the
+ compile-time bind parameters override the information specified
+ within `values` on a per-key basis.
+
+ The keys within `values` can be either :class:`~sqlalchemy.schema.Column`
+ objects or their string identifiers. Each key may reference one of:
+
+ * a literal data value (i.e. string, number, etc.);
+ * a Column object;
+ * a SELECT statement.
+
+ If a ``SELECT`` statement is specified which references this
+ ``INSERT`` statement's table, the statement will be correlated
+ against the ``INSERT`` statement.
+
+ .. seealso::
+
+ :ref:`coretutorial_insert_expressions` - SQL Expression Tutorial
+
+ :ref:`inserts_and_updates` - SQL Expression Tutorial
+
+ """
+ ValuesBase.__init__(self, table, values, prefixes)
+ self._bind = bind
+ self.select = None
+ self.inline = inline
+ self._returning = returning
+ self.kwargs = kwargs
+
+ def get_children(self, **kwargs):
+ if self.select is not None:
+ return self.select,
+ else:
+ return ()
+
+ @_generative
+ def from_select(self, names, select):
+ """Return a new :class:`.Insert` construct which represents
+ an ``INSERT...FROM SELECT`` statement.
+
+ e.g.::
+
+ sel = select([table1.c.a, table1.c.b]).where(table1.c.c > 5)
+ ins = table2.insert().from_select(['a', 'b'], sel)
+
+ :param names: a sequence of string column names or :class:`.Column`
+ objects representing the target columns.
+ :param select: a :func:`.select` construct, :class:`.FromClause`
+ or other construct which resolves into a :class:`.FromClause`,
+ such as an ORM :class:`.Query` object, etc. The order of
+ columns returned from this FROM clause should correspond to the
+ order of columns sent as the ``names`` parameter; while this
+ is not checked before passing along to the database, the database
+ would normally raise an exception if these column lists don't
+ correspond.
+
+ .. note::
+
+ Depending on backend, it may be necessary for the :class:`.Insert`
+ statement to be constructed using the ``inline=True`` flag; this
+ flag will prevent the implicit usage of ``RETURNING`` when the
+ ``INSERT`` statement is rendered, which isn't supported on a backend
+ such as Oracle in conjunction with an ``INSERT..SELECT`` combination::
+
+ sel = select([table1.c.a, table1.c.b]).where(table1.c.c > 5)
+ ins = table2.insert(inline=True).from_select(['a', 'b'], sel)
+
+ .. versionadded:: 0.8.3
+
+ """
+ if self.parameters:
+ raise exc.InvalidRequestError(
+ "This construct already inserts value expressions")
+
+ self.parameters, self._has_multi_parameters = \
+ self._process_colparams(dict((n, Null()) for n in names))
+
+ self.select = _interpret_as_select(select)
+
+ def _copy_internals(self, clone=_clone, **kw):
+ # TODO: coverage
+ self.parameters = self.parameters.copy()
+ if self.select is not None:
+ self.select = _clone(self.select)
+
+
+class Update(ValuesBase):
+ """Represent an Update construct.
+
+ The :class:`.Update` object is created using the :func:`update()` function.
+
+ """
+ __visit_name__ = 'update'
+
+ def __init__(self,
+ table,
+ whereclause=None,
+ values=None,
+ inline=False,
+ bind=None,
+ prefixes=None,
+ returning=None,
+ **kwargs):
+ """Construct an :class:`.Update` object.
+
+ E.g.::
+
+ from sqlalchemy import update
+
+ stmt = update(users).where(users.c.id==5).\\
+ values(name='user #5')
+
+ Similar functionality is available via the
+ :meth:`~.TableClause.update` method on
+ :class:`.Table`::
+
+ stmt = users.update().\\
+ where(users.c.id==5).\\
+ values(name='user #5')
+
+ :param table: A :class:`.Table` object representing the database
+ table to be updated.
+
+ :param whereclause: Optional SQL expression describing the ``WHERE``
+ condition of the ``UPDATE`` statement. Modern applications
+ may prefer to use the generative :meth:`~Update.where()`
+ method to specify the ``WHERE`` clause.
+
+ The WHERE clause can refer to multiple tables.
+ For databases which support this, an ``UPDATE FROM`` clause will
+ be generated, or on MySQL, a multi-table update. The statement
+ will fail on databases that don't have support for multi-table
+ update statements. A SQL-standard method of referring to
+ additional tables in the WHERE clause is to use a correlated
+ subquery::
+
+ users.update().values(name='ed').where(
+ users.c.name==select([addresses.c.email_address]).\\
+ where(addresses.c.user_id==users.c.id).\\
+ as_scalar()
+ )
+
+ .. versionchanged:: 0.7.4
+ The WHERE clause can refer to multiple tables.
+
+ :param values:
+ Optional dictionary which specifies the ``SET`` conditions of the
+ ``UPDATE``. If left as ``None``, the ``SET``
+ conditions are determined from those parameters passed to the
+ statement during the execution and/or compilation of the
+ statement. When compiled standalone without any parameters,
+ the ``SET`` clause generates for all columns.
+
+ Modern applications may prefer to use the generative
+ :meth:`.Update.values` method to set the values of the
+ UPDATE statement.
+
+ :param inline:
+ if True, SQL defaults present on :class:`.Column` objects via
+ the ``default`` keyword will be compiled 'inline' into the statement
+ and not pre-executed. This means that their values will not
+ be available in the dictionary returned from
+ :meth:`.ResultProxy.last_updated_params`.
+
+ If both ``values`` and compile-time bind parameters are present, the
+ compile-time bind parameters override the information specified
+ within ``values`` on a per-key basis.
+
+ The keys within ``values`` can be either :class:`.Column`
+ objects or their string identifiers (specifically the "key" of the
+ :class:`.Column`, normally but not necessarily equivalent to
+ its "name"). Normally, the
+ :class:`.Column` objects used here are expected to be
+ part of the target :class:`.Table` that is the table
+ to be updated. However when using MySQL, a multiple-table
+ UPDATE statement can refer to columns from any of
+ the tables referred to in the WHERE clause.
+
+ The values referred to in ``values`` are typically:
+
+ * a literal data value (i.e. string, number, etc.)
+ * a SQL expression, such as a related :class:`.Column`,
+ a scalar-returning :func:`.select` construct,
+ etc.
+
+ When combining :func:`.select` constructs within the values
+ clause of an :func:`.update` construct,
+ the subquery represented by the :func:`.select` should be
+ *correlated* to the parent table, that is, providing criterion
+ which links the table inside the subquery to the outer table
+ being updated::
+
+ users.update().values(
+ name=select([addresses.c.email_address]).\\
+ where(addresses.c.user_id==users.c.id).\\
+ as_scalar()
+ )
+
+ .. seealso::
+
+ :ref:`inserts_and_updates` - SQL Expression
+ Language Tutorial
+
+
+ """
+ ValuesBase.__init__(self, table, values, prefixes)
+ self._bind = bind
+ self._returning = returning
+ if whereclause is not None:
+ self._whereclause = _literal_as_text(whereclause)
+ else:
+ self._whereclause = None
+ self.inline = inline
+ self.kwargs = kwargs
+
+
+ def get_children(self, **kwargs):
+ if self._whereclause is not None:
+ return self._whereclause,
+ else:
+ return ()
+
+ def _copy_internals(self, clone=_clone, **kw):
+ # TODO: coverage
+ self._whereclause = clone(self._whereclause, **kw)
+ self.parameters = self.parameters.copy()
+
+ @_generative
+ def where(self, whereclause):
+ """return a new update() construct with the given expression added to
+ its WHERE clause, joined to the existing clause via AND, if any.
+
+ """
+ if self._whereclause is not None:
+ self._whereclause = and_(self._whereclause,
+ _literal_as_text(whereclause))
+ else:
+ self._whereclause = _literal_as_text(whereclause)
+
+ @property
+ def _extra_froms(self):
+ # TODO: this could be made memoized
+ # if the memoization is reset on each generative call.
+ froms = []
+ seen = set([self.table])
+
+ if self._whereclause is not None:
+ for item in _from_objects(self._whereclause):
+ if not seen.intersection(item._cloned_set):
+ froms.append(item)
+ seen.update(item._cloned_set)
+
+ return froms
+
+
+class Delete(UpdateBase):
+ """Represent a DELETE construct.
+
+ The :class:`.Delete` object is created using the :func:`delete()` function.
+
+ """
+
+ __visit_name__ = 'delete'
+
+ def __init__(self,
+ table,
+ whereclause=None,
+ bind=None,
+ returning=None,
+ prefixes=None,
+ **kwargs):
+ """Construct :class:`.Delete` object.
+
+ Similar functionality is available via the
+ :meth:`~.TableClause.delete` method on
+ :class:`~.schema.Table`.
+
+ :param table: The table to be updated.
+
+ :param whereclause: A :class:`.ClauseElement` describing the ``WHERE``
+ condition of the ``UPDATE`` statement. Note that the
+ :meth:`~Delete.where()` generative method may be used instead.
+
+ .. seealso::
+
+ :ref:`deletes` - SQL Expression Tutorial
+
+ """
+ self._bind = bind
+ self.table = _interpret_as_from(table)
+ self._returning = returning
+
+ if prefixes:
+ self._setup_prefixes(prefixes)
+
+ if whereclause is not None:
+ self._whereclause = _literal_as_text(whereclause)
+ else:
+ self._whereclause = None
+
+ self.kwargs = kwargs
+
+ def get_children(self, **kwargs):
+ if self._whereclause is not None:
+ return self._whereclause,
+ else:
+ return ()
+
+ @_generative
+ def where(self, whereclause):
+ """Add the given WHERE clause to a newly returned delete construct."""
+
+ if self._whereclause is not None:
+ self._whereclause = and_(self._whereclause,
+ _literal_as_text(whereclause))
+ else:
+ self._whereclause = _literal_as_text(whereclause)
+
+ def _copy_internals(self, clone=_clone, **kw):
+ # TODO: coverage
+ self._whereclause = clone(self._whereclause, **kw)
+
diff --git a/lib/sqlalchemy/sql/elements.py b/lib/sqlalchemy/sql/elements.py
new file mode 100644
index 000000000..151157e15
--- /dev/null
+++ b/lib/sqlalchemy/sql/elements.py
@@ -0,0 +1,2369 @@
+from __future__ import unicode_literals
+
+from .. import util, exc, inspection
+from . import type_api
+from . import operators
+from .visitors import Visitable, cloned_traverse, traverse
+from .annotation import Annotated
+import itertools
+from .base import Executable, PARSE_AUTOCOMMIT, Immutable, NO_ARG
+import re
+import operator
+
+def _clone(element, **kw):
+ return element._clone()
+
+def collate(expression, collation):
+ """Return the clause ``expression COLLATE collation``.
+
+ e.g.::
+
+ collate(mycolumn, 'utf8_bin')
+
+ produces::
+
+ mycolumn COLLATE utf8_bin
+
+ """
+
+ expr = _literal_as_binds(expression)
+ return BinaryExpression(
+ expr,
+ _literal_as_text(collation),
+ operators.collate, type_=expr.type)
+
+def between(ctest, cleft, cright):
+ """Return a ``BETWEEN`` predicate clause.
+
+ Equivalent of SQL ``clausetest BETWEEN clauseleft AND clauseright``.
+
+ The :func:`between()` method on all
+ :class:`.ColumnElement` subclasses provides
+ similar functionality.
+
+ """
+ ctest = _literal_as_binds(ctest)
+ return ctest.between(cleft, cright)
+
+def literal(value, type_=None):
+ """Return a literal clause, bound to a bind parameter.
+
+ Literal clauses are created automatically when non- :class:`.ClauseElement`
+ objects (such as strings, ints, dates, etc.) are used in a comparison
+ operation with a :class:`.ColumnElement`
+ subclass, such as a :class:`~sqlalchemy.schema.Column` object.
+ Use this function to force the
+ generation of a literal clause, which will be created as a
+ :class:`BindParameter` with a bound value.
+
+ :param value: the value to be bound. Can be any Python object supported by
+ the underlying DB-API, or is translatable via the given type argument.
+
+ :param type\_: an optional :class:`~sqlalchemy.types.TypeEngine` which
+ will provide bind-parameter translation for this literal.
+
+ """
+ return BindParameter(None, value, type_=type_, unique=True)
+
+
+
+def type_coerce(expr, type_):
+ """Coerce the given expression into the given type,
+ on the Python side only.
+
+ :func:`.type_coerce` is roughly similar to :func:`.cast`, except no
+ "CAST" expression is rendered - the given type is only applied towards
+ expression typing and against received result values.
+
+ e.g.::
+
+ from sqlalchemy.types import TypeDecorator
+ import uuid
+
+ class AsGuid(TypeDecorator):
+ impl = String
+
+ def process_bind_param(self, value, dialect):
+ if value is not None:
+ return str(value)
+ else:
+ return None
+
+ def process_result_value(self, value, dialect):
+ if value is not None:
+ return uuid.UUID(value)
+ else:
+ return None
+
+ conn.execute(
+ select([type_coerce(mytable.c.ident, AsGuid)]).\\
+ where(
+ type_coerce(mytable.c.ident, AsGuid) ==
+ uuid.uuid3(uuid.NAMESPACE_URL, 'bar')
+ )
+ )
+
+ """
+ type_ = type_api.to_instance(type_)
+
+ if hasattr(expr, '__clause_expr__'):
+ return type_coerce(expr.__clause_expr__())
+ elif isinstance(expr, BindParameter):
+ bp = expr._clone()
+ bp.type = type_
+ return bp
+ elif not isinstance(expr, Visitable):
+ if expr is None:
+ return Null()
+ else:
+ return literal(expr, type_=type_)
+ else:
+ return Label(None, expr, type_=type_)
+
+
+
+
+
+def outparam(key, type_=None):
+ """Create an 'OUT' parameter for usage in functions (stored procedures),
+ for databases which support them.
+
+ The ``outparam`` can be used like a regular function parameter.
+ The "output" value will be available from the
+ :class:`~sqlalchemy.engine.ResultProxy` object via its ``out_parameters``
+ attribute, which returns a dictionary containing the values.
+
+ """
+ return BindParameter(
+ key, None, type_=type_, unique=False, isoutparam=True)
+
+
+def and_(*clauses):
+ """Join a list of clauses together using the ``AND`` operator.
+
+ The ``&`` operator is also overloaded on all :class:`.ColumnElement`
+ subclasses to produce the
+ same result.
+
+ """
+ if len(clauses) == 1:
+ return clauses[0]
+ return BooleanClauseList(operator=operators.and_, *clauses)
+
+
+def or_(*clauses):
+ """Join a list of clauses together using the ``OR`` operator.
+
+ The ``|`` operator is also overloaded on all
+ :class:`.ColumnElement` subclasses to produce the
+ same result.
+
+ """
+ if len(clauses) == 1:
+ return clauses[0]
+ return BooleanClauseList(operator=operators.or_, *clauses)
+
+
+def not_(clause):
+ """Return a negation of the given clause, i.e. ``NOT(clause)``.
+
+ The ``~`` operator is also overloaded on all
+ :class:`.ColumnElement` subclasses to produce the
+ same result.
+
+ """
+ return operators.inv(_literal_as_binds(clause))
+
+
+
+@inspection._self_inspects
+class ClauseElement(Visitable):
+ """Base class for elements of a programmatically constructed SQL
+ expression.
+
+ """
+ __visit_name__ = 'clause'
+
+ _annotations = {}
+ supports_execution = False
+ _from_objects = []
+ bind = None
+ _is_clone_of = None
+ is_selectable = False
+ is_clause_element = True
+
+ _order_by_label_element = None
+
+ def _clone(self):
+ """Create a shallow copy of this ClauseElement.
+
+ This method may be used by a generative API. Its also used as
+ part of the "deep" copy afforded by a traversal that combines
+ the _copy_internals() method.
+
+ """
+ c = self.__class__.__new__(self.__class__)
+ c.__dict__ = self.__dict__.copy()
+ ClauseElement._cloned_set._reset(c)
+ ColumnElement.comparator._reset(c)
+
+ # this is a marker that helps to "equate" clauses to each other
+ # when a Select returns its list of FROM clauses. the cloning
+ # process leaves around a lot of remnants of the previous clause
+ # typically in the form of column expressions still attached to the
+ # old table.
+ c._is_clone_of = self
+
+ return c
+
+ @property
+ def _constructor(self):
+ """return the 'constructor' for this ClauseElement.
+
+ This is for the purposes for creating a new object of
+ this type. Usually, its just the element's __class__.
+ However, the "Annotated" version of the object overrides
+ to return the class of its proxied element.
+
+ """
+ return self.__class__
+
+ @util.memoized_property
+ def _cloned_set(self):
+ """Return the set consisting all cloned ancestors of this
+ ClauseElement.
+
+ Includes this ClauseElement. This accessor tends to be used for
+ FromClause objects to identify 'equivalent' FROM clauses, regardless
+ of transformative operations.
+
+ """
+ s = util.column_set()
+ f = self
+ while f is not None:
+ s.add(f)
+ f = f._is_clone_of
+ return s
+
+ def __getstate__(self):
+ d = self.__dict__.copy()
+ d.pop('_is_clone_of', None)
+ return d
+
+ def _annotate(self, values):
+ """return a copy of this ClauseElement with annotations
+ updated by the given dictionary.
+
+ """
+ return Annotated(self, values)
+
+ def _with_annotations(self, values):
+ """return a copy of this ClauseElement with annotations
+ replaced by the given dictionary.
+
+ """
+ return Annotated(self, values)
+
+ def _deannotate(self, values=None, clone=False):
+ """return a copy of this :class:`.ClauseElement` with annotations
+ removed.
+
+ :param values: optional tuple of individual values
+ to remove.
+
+ """
+ if clone:
+ # clone is used when we are also copying
+ # the expression for a deep deannotation
+ return self._clone()
+ else:
+ # if no clone, since we have no annotations we return
+ # self
+ return self
+
+ def unique_params(self, *optionaldict, **kwargs):
+ """Return a copy with :func:`bindparam()` elements replaced.
+
+ Same functionality as ``params()``, except adds `unique=True`
+ to affected bind parameters so that multiple statements can be
+ used.
+
+ """
+ return self._params(True, optionaldict, kwargs)
+
+ def params(self, *optionaldict, **kwargs):
+ """Return a copy with :func:`bindparam()` elements replaced.
+
+ Returns a copy of this ClauseElement with :func:`bindparam()`
+ elements replaced with values taken from the given dictionary::
+
+ >>> clause = column('x') + bindparam('foo')
+ >>> print clause.compile().params
+ {'foo':None}
+ >>> print clause.params({'foo':7}).compile().params
+ {'foo':7}
+
+ """
+ return self._params(False, optionaldict, kwargs)
+
+ def _params(self, unique, optionaldict, kwargs):
+ if len(optionaldict) == 1:
+ kwargs.update(optionaldict[0])
+ elif len(optionaldict) > 1:
+ raise exc.ArgumentError(
+ "params() takes zero or one positional dictionary argument")
+
+ def visit_bindparam(bind):
+ if bind.key in kwargs:
+ bind.value = kwargs[bind.key]
+ bind.required = False
+ if unique:
+ bind._convert_to_unique()
+ return cloned_traverse(self, {}, {'bindparam': visit_bindparam})
+
+ def compare(self, other, **kw):
+ """Compare this ClauseElement to the given ClauseElement.
+
+ Subclasses should override the default behavior, which is a
+ straight identity comparison.
+
+ \**kw are arguments consumed by subclass compare() methods and
+ may be used to modify the criteria for comparison.
+ (see :class:`.ColumnElement`)
+
+ """
+ return self is other
+
+ def _copy_internals(self, clone=_clone, **kw):
+ """Reassign internal elements to be clones of themselves.
+
+ Called during a copy-and-traverse operation on newly
+ shallow-copied elements to create a deep copy.
+
+ The given clone function should be used, which may be applying
+ additional transformations to the element (i.e. replacement
+ traversal, cloned traversal, annotations).
+
+ """
+ pass
+
+ def get_children(self, **kwargs):
+ """Return immediate child elements of this :class:`.ClauseElement`.
+
+ This is used for visit traversal.
+
+ \**kwargs may contain flags that change the collection that is
+ returned, for example to return a subset of items in order to
+ cut down on larger traversals, or to return child items from a
+ different context (such as schema-level collections instead of
+ clause-level).
+
+ """
+ return []
+
+ def self_group(self, against=None):
+ """Apply a 'grouping' to this :class:`.ClauseElement`.
+
+ This method is overridden by subclasses to return a
+ "grouping" construct, i.e. parenthesis. In particular
+ it's used by "binary" expressions to provide a grouping
+ around themselves when placed into a larger expression,
+ as well as by :func:`.select` constructs when placed into
+ the FROM clause of another :func:`.select`. (Note that
+ subqueries should be normally created using the
+ :func:`.Select.alias` method, as many platforms require
+ nested SELECT statements to be named).
+
+ As expressions are composed together, the application of
+ :meth:`self_group` is automatic - end-user code should never
+ need to use this method directly. Note that SQLAlchemy's
+ clause constructs take operator precedence into account -
+ so parenthesis might not be needed, for example, in
+ an expression like ``x OR (y AND z)`` - AND takes precedence
+ over OR.
+
+ The base :meth:`self_group` method of :class:`.ClauseElement`
+ just returns self.
+ """
+ return self
+
+ @util.dependencies("sqlalchemy.engine.default")
+ def compile(self, default, bind=None, dialect=None, **kw):
+ """Compile this SQL expression.
+
+ The return value is a :class:`~.Compiled` object.
+ Calling ``str()`` or ``unicode()`` on the returned value will yield a
+ string representation of the result. The
+ :class:`~.Compiled` object also can return a
+ dictionary of bind parameter names and values
+ using the ``params`` accessor.
+
+ :param bind: An ``Engine`` or ``Connection`` from which a
+ ``Compiled`` will be acquired. This argument takes precedence over
+ this :class:`.ClauseElement`'s bound engine, if any.
+
+ :param column_keys: Used for INSERT and UPDATE statements, a list of
+ column names which should be present in the VALUES clause of the
+ compiled statement. If ``None``, all columns from the target table
+ object are rendered.
+
+ :param dialect: A ``Dialect`` instance from which a ``Compiled``
+ will be acquired. This argument takes precedence over the `bind`
+ argument as well as this :class:`.ClauseElement`'s bound engine, if
+ any.
+
+ :param inline: Used for INSERT statements, for a dialect which does
+ not support inline retrieval of newly generated primary key
+ columns, will force the expression used to create the new primary
+ key value to be rendered inline within the INSERT statement's
+ VALUES clause. This typically refers to Sequence execution but may
+ also refer to any server-side default generation function
+ associated with a primary key `Column`.
+
+ """
+
+ if not dialect:
+ if bind:
+ dialect = bind.dialect
+ elif self.bind:
+ dialect = self.bind.dialect
+ bind = self.bind
+ else:
+ dialect = default.DefaultDialect()
+ return self._compiler(dialect, bind=bind, **kw)
+
+ def _compiler(self, dialect, **kw):
+ """Return a compiler appropriate for this ClauseElement, given a
+ Dialect."""
+
+ return dialect.statement_compiler(dialect, self, **kw)
+
+ def __str__(self):
+ if util.py3k:
+ return str(self.compile())
+ else:
+ return unicode(self.compile()).encode('ascii', 'backslashreplace')
+
+ def __and__(self, other):
+ return and_(self, other)
+
+ def __or__(self, other):
+ return or_(self, other)
+
+ def __invert__(self):
+ return self._negate()
+
+ def __bool__(self):
+ raise TypeError("Boolean value of this clause is not defined")
+
+ __nonzero__ = __bool__
+
+ def _negate(self):
+ if hasattr(self, 'negation_clause'):
+ return self.negation_clause
+ else:
+ return UnaryExpression(
+ self.self_group(against=operators.inv),
+ operator=operators.inv,
+ negate=None)
+
+ def __repr__(self):
+ friendly = getattr(self, 'description', None)
+ if friendly is None:
+ return object.__repr__(self)
+ else:
+ return '<%s.%s at 0x%x; %s>' % (
+ self.__module__, self.__class__.__name__, id(self), friendly)
+
+
+
+class ColumnElement(ClauseElement, operators.ColumnOperators):
+ """Represent a column-oriented SQL expression suitable for usage in the
+ "columns" clause, WHERE clause etc. of a statement.
+
+ While the most familiar kind of :class:`.ColumnElement` is the
+ :class:`.Column` object, :class:`.ColumnElement` serves as the basis
+ for any unit that may be present in a SQL expression, including
+ the expressions themselves, SQL functions, bound parameters,
+ literal expressions, keywords such as ``NULL``, etc.
+ :class:`.ColumnElement` is the ultimate base class for all such elements.
+
+ A :class:`.ColumnElement` provides the ability to generate new
+ :class:`.ColumnElement`
+ objects using Python expressions. This means that Python operators
+ such as ``==``, ``!=`` and ``<`` are overloaded to mimic SQL operations,
+ and allow the instantiation of further :class:`.ColumnElement` instances
+ which are composed from other, more fundamental :class:`.ColumnElement`
+ objects. For example, two :class:`.ColumnClause` objects can be added
+ together with the addition operator ``+`` to produce
+ a :class:`.BinaryExpression`.
+ Both :class:`.ColumnClause` and :class:`.BinaryExpression` are subclasses
+ of :class:`.ColumnElement`::
+
+ >>> from sqlalchemy.sql import column
+ >>> column('a') + column('b')
+ <sqlalchemy.sql.expression.BinaryExpression object at 0x101029dd0>
+ >>> print column('a') + column('b')
+ a + b
+
+ :class:`.ColumnElement` supports the ability to be a *proxy* element,
+ which indicates that the :class:`.ColumnElement` may be associated with
+ a :class:`.Selectable` which was derived from another :class:`.Selectable`.
+ An example of a "derived" :class:`.Selectable` is an :class:`.Alias` of a
+ :class:`~sqlalchemy.schema.Table`. For the ambitious, an in-depth
+ discussion of this concept can be found at
+ `Expression Transformations <http://techspot.zzzeek.org/2008/01/23/expression-transformations/>`_.
+
+ """
+
+ __visit_name__ = 'column'
+ primary_key = False
+ foreign_keys = []
+ quote = None
+ _label = None
+ _key_label = None
+ _alt_names = ()
+
+ @util.memoized_property
+ def type(self):
+ return type_api.NULLTYPE
+
+ @util.memoized_property
+ def comparator(self):
+ return self.type.comparator_factory(self)
+
+ def __getattr__(self, key):
+ try:
+ return getattr(self.comparator, key)
+ except AttributeError:
+ raise AttributeError(
+ 'Neither %r object nor %r object has an attribute %r' % (
+ type(self).__name__,
+ type(self.comparator).__name__,
+ key)
+ )
+
+ def operate(self, op, *other, **kwargs):
+ return op(self.comparator, *other, **kwargs)
+
+ def reverse_operate(self, op, other, **kwargs):
+ return op(other, self.comparator, **kwargs)
+
+ def _bind_param(self, operator, obj):
+ return BindParameter(None, obj,
+ _compared_to_operator=operator,
+ _compared_to_type=self.type, unique=True)
+
+ @property
+ def expression(self):
+ """Return a column expression.
+
+ Part of the inspection interface; returns self.
+
+ """
+ return self
+
+ @property
+ def _select_iterable(self):
+ return (self, )
+
+ @util.memoized_property
+ def base_columns(self):
+ return util.column_set(c for c in self.proxy_set
+ if not hasattr(c, '_proxies'))
+
+ @util.memoized_property
+ def proxy_set(self):
+ s = util.column_set([self])
+ if hasattr(self, '_proxies'):
+ for c in self._proxies:
+ s.update(c.proxy_set)
+ return s
+
+ def shares_lineage(self, othercolumn):
+ """Return True if the given :class:`.ColumnElement`
+ has a common ancestor to this :class:`.ColumnElement`."""
+
+ return bool(self.proxy_set.intersection(othercolumn.proxy_set))
+
+ def _compare_name_for_result(self, other):
+ """Return True if the given column element compares to this one
+ when targeting within a result row."""
+
+ return hasattr(other, 'name') and hasattr(self, 'name') and \
+ other.name == self.name
+
+ def _make_proxy(self, selectable, name=None, name_is_truncatable=False, **kw):
+ """Create a new :class:`.ColumnElement` representing this
+ :class:`.ColumnElement` as it appears in the select list of a
+ descending selectable.
+
+ """
+ if name is None:
+ name = self.anon_label
+ try:
+ key = str(self)
+ except exc.UnsupportedCompilationError:
+ key = self.anon_label
+ else:
+ key = name
+ co = ColumnClause(
+ _as_truncated(name) if name_is_truncatable else name,
+ type_=getattr(self, 'type', None),
+ _selectable=selectable
+ )
+ co._proxies = [self]
+ if selectable._is_clone_of is not None:
+ co._is_clone_of = \
+ selectable._is_clone_of.columns.get(key)
+ selectable._columns[key] = co
+ return co
+
+ def compare(self, other, use_proxies=False, equivalents=None, **kw):
+ """Compare this ColumnElement to another.
+
+ Special arguments understood:
+
+ :param use_proxies: when True, consider two columns that
+ share a common base column as equivalent (i.e. shares_lineage())
+
+ :param equivalents: a dictionary of columns as keys mapped to sets
+ of columns. If the given "other" column is present in this
+ dictionary, if any of the columns in the corresponding set() pass the
+ comparison test, the result is True. This is used to expand the
+ comparison to other columns that may be known to be equivalent to
+ this one via foreign key or other criterion.
+
+ """
+ to_compare = (other, )
+ if equivalents and other in equivalents:
+ to_compare = equivalents[other].union(to_compare)
+
+ for oth in to_compare:
+ if use_proxies and self.shares_lineage(oth):
+ return True
+ elif hash(oth) == hash(self):
+ return True
+ else:
+ return False
+
+ def label(self, name):
+ """Produce a column label, i.e. ``<columnname> AS <name>``.
+
+ This is a shortcut to the :func:`~.expression.label` function.
+
+ if 'name' is None, an anonymous label name will be generated.
+
+ """
+ return Label(name, self, self.type)
+
+ @util.memoized_property
+ def anon_label(self):
+ """provides a constant 'anonymous label' for this ColumnElement.
+
+ This is a label() expression which will be named at compile time.
+ The same label() is returned each time anon_label is called so
+ that expressions can reference anon_label multiple times, producing
+ the same label name at compile time.
+
+ the compiler uses this function automatically at compile time
+ for expressions that are known to be 'unnamed' like binary
+ expressions and function calls.
+
+ """
+ return _anonymous_label('%%(%d %s)s' % (id(self), getattr(self,
+ 'name', 'anon')))
+
+
+
+class BindParameter(ColumnElement):
+ """Represent a bound parameter value.
+
+ """
+
+ __visit_name__ = 'bindparam'
+ quote = None
+
+ _is_crud = False
+
+ def __init__(self, key, value=NO_ARG, type_=None,
+ unique=False, required=NO_ARG,
+ quote=None, callable_=None,
+ isoutparam=False,
+ _compared_to_operator=None,
+ _compared_to_type=None):
+ """Construct a new :class:`.BindParameter`.
+
+ :param key:
+ the key for this bind param. Will be used in the generated
+ SQL statement for dialects that use named parameters. This
+ value may be modified when part of a compilation operation,
+ if other :class:`BindParameter` objects exist with the same
+ key, or if its length is too long and truncation is
+ required.
+
+ :param value:
+ Initial value for this bind param. This value may be
+ overridden by the dictionary of parameters sent to statement
+ compilation/execution.
+
+ Defaults to ``None``, however if neither ``value`` nor
+ ``callable`` are passed explicitly, the ``required`` flag will be
+ set to ``True`` which has the effect of requiring a value be present
+ when the statement is actually executed.
+
+ .. versionchanged:: 0.8 The ``required`` flag is set to ``True``
+ automatically if ``value`` or ``callable`` is not passed.
+
+ :param callable\_:
+ A callable function that takes the place of "value". The function
+ will be called at statement execution time to determine the
+ ultimate value. Used for scenarios where the actual bind
+ value cannot be determined at the point at which the clause
+ construct is created, but embedded bind values are still desirable.
+
+ :param type\_:
+ A ``TypeEngine`` object that will be used to pre-process the
+ value corresponding to this :class:`BindParameter` at
+ execution time.
+
+ :param unique:
+ if True, the key name of this BindParamClause will be
+ modified if another :class:`BindParameter` of the same name
+ already has been located within the containing
+ :class:`.ClauseElement`.
+
+ :param required:
+ If ``True``, a value is required at execution time. If not passed,
+ is set to ``True`` or ``False`` based on whether or not
+ one of ``value`` or ``callable`` were passed..
+
+ .. versionchanged:: 0.8 If the ``required`` flag is not specified,
+ it will be set automatically to ``True`` or ``False`` depending
+ on whether or not the ``value`` or ``callable`` parameters
+ were specified.
+
+ :param quote:
+ True if this parameter name requires quoting and is not
+ currently known as a SQLAlchemy reserved word; this currently
+ only applies to the Oracle backend.
+
+ :param isoutparam:
+ if True, the parameter should be treated like a stored procedure
+ "OUT" parameter.
+
+ .. seealso::
+
+ :func:`.outparam`
+
+
+
+ """
+ if isinstance(key, ColumnClause):
+ type_ = key.type
+ key = key.name
+ if required is NO_ARG:
+ required = (value is NO_ARG and callable_ is None)
+ if value is NO_ARG:
+ value = None
+
+
+ if unique:
+ self.key = _anonymous_label('%%(%d %s)s' % (id(self), key
+ or 'param'))
+ else:
+ self.key = key or _anonymous_label('%%(%d param)s'
+ % id(self))
+
+ # identifying key that won't change across
+ # clones, used to identify the bind's logical
+ # identity
+ self._identifying_key = self.key
+
+ # key that was passed in the first place, used to
+ # generate new keys
+ self._orig_key = key or 'param'
+
+ self.unique = unique
+ self.value = value
+ self.callable = callable_
+ self.isoutparam = isoutparam
+ self.required = required
+ self.quote = quote
+ if type_ is None:
+ if _compared_to_type is not None:
+ self.type = \
+ _compared_to_type.coerce_compared_value(
+ _compared_to_operator, value)
+ else:
+ self.type = type_api._type_map.get(type(value),
+ type_api.NULLTYPE)
+ elif isinstance(type_, type):
+ self.type = type_()
+ else:
+ self.type = type_
+
+ @property
+ def effective_value(self):
+ """Return the value of this bound parameter,
+ taking into account if the ``callable`` parameter
+ was set.
+
+ The ``callable`` value will be evaluated
+ and returned if present, else ``value``.
+
+ """
+ if self.callable:
+ return self.callable()
+ else:
+ return self.value
+
+ def _clone(self):
+ c = ClauseElement._clone(self)
+ if self.unique:
+ c.key = _anonymous_label('%%(%d %s)s' % (id(c), c._orig_key
+ or 'param'))
+ return c
+
+ def _convert_to_unique(self):
+ if not self.unique:
+ self.unique = True
+ self.key = _anonymous_label('%%(%d %s)s' % (id(self),
+ self._orig_key or 'param'))
+
+ def compare(self, other, **kw):
+ """Compare this :class:`BindParameter` to the given
+ clause."""
+
+ return isinstance(other, BindParameter) \
+ and self.type._compare_type_affinity(other.type) \
+ and self.value == other.value
+
+ def __getstate__(self):
+ """execute a deferred value for serialization purposes."""
+
+ d = self.__dict__.copy()
+ v = self.value
+ if self.callable:
+ v = self.callable()
+ d['callable'] = None
+ d['value'] = v
+ return d
+
+ def __repr__(self):
+ return 'BindParameter(%r, %r, type_=%r)' % (self.key,
+ self.value, self.type)
+
+
+class TypeClause(ClauseElement):
+ """Handle a type keyword in a SQL statement.
+
+ Used by the ``Case`` statement.
+
+ """
+
+ __visit_name__ = 'typeclause'
+
+ def __init__(self, type):
+ self.type = type
+
+
+class TextClause(Executable, ClauseElement):
+ """Represent a literal SQL text fragment.
+
+ Public constructor is the :func:`text()` function.
+
+ """
+
+ __visit_name__ = 'textclause'
+
+ _bind_params_regex = re.compile(r'(?<![:\w\x5c]):(\w+)(?!:)', re.UNICODE)
+ _execution_options = \
+ Executable._execution_options.union(
+ {'autocommit': PARSE_AUTOCOMMIT})
+
+ @property
+ def _select_iterable(self):
+ return (self,)
+
+ @property
+ def selectable(self):
+ return self
+
+ _hide_froms = []
+
+ def __init__(
+ self,
+ text='',
+ bind=None,
+ bindparams=None,
+ typemap=None,
+ autocommit=None):
+ """Construct a new :class:`.TextClause` clause.
+
+ E.g.::
+
+ fom sqlalchemy import text
+
+ t = text("SELECT * FROM users")
+ result = connection.execute(t)
+
+ The advantages :func:`.text` provides over a plain string are
+ backend-neutral support for bind parameters, per-statement
+ execution options, as well as
+ bind parameter and result-column typing behavior, allowing
+ SQLAlchemy type constructs to play a role when executing
+ a statement that is specified literally.
+
+ Bind parameters are specified by name, using the format ``:name``.
+ E.g.::
+
+ t = text("SELECT * FROM users WHERE id=:user_id")
+ result = connection.execute(t, user_id=12)
+
+ To invoke SQLAlchemy typing logic for bind parameters, the
+ ``bindparams`` list allows specification of :func:`bindparam`
+ constructs which specify the type for a given name::
+
+ t = text("SELECT id FROM users WHERE updated_at>:updated",
+ bindparams=[bindparam('updated', DateTime())]
+ )
+
+ Typing during result row processing is also an important concern.
+ Result column types
+ are specified using the ``typemap`` dictionary, where the keys
+ match the names of columns. These names are taken from what
+ the DBAPI returns as ``cursor.description``::
+
+ t = text("SELECT id, name FROM users",
+ typemap={
+ 'id':Integer,
+ 'name':Unicode
+ }
+ )
+
+ The :func:`text` construct is used internally for most cases when
+ a literal string is specified for part of a larger query, such as
+ within :func:`select()`, :func:`update()`,
+ :func:`insert()` or :func:`delete()`. In those cases, the same
+ bind parameter syntax is applied::
+
+ s = select([users.c.id, users.c.name]).where("id=:user_id")
+ result = connection.execute(s, user_id=12)
+
+ Using :func:`text` explicitly usually implies the construction
+ of a full, standalone statement. As such, SQLAlchemy refers
+ to it as an :class:`.Executable` object, and it supports
+ the :meth:`Executable.execution_options` method. For example,
+ a :func:`text` construct that should be subject to "autocommit"
+ can be set explicitly so using the ``autocommit`` option::
+
+ t = text("EXEC my_procedural_thing()").\\
+ execution_options(autocommit=True)
+
+ Note that SQLAlchemy's usual "autocommit" behavior applies to
+ :func:`text` constructs - that is, statements which begin
+ with a phrase such as ``INSERT``, ``UPDATE``, ``DELETE``,
+ or a variety of other phrases specific to certain backends, will
+ be eligible for autocommit if no transaction is in progress.
+
+ :param text:
+ the text of the SQL statement to be created. use ``:<param>``
+ to specify bind parameters; they will be compiled to their
+ engine-specific format.
+
+ :param autocommit:
+ Deprecated. Use .execution_options(autocommit=<True|False>)
+ to set the autocommit option.
+
+ :param bind:
+ an optional connection or engine to be used for this text query.
+
+ :param bindparams:
+ a list of :func:`bindparam()` instances which can be used to define
+ the types and/or initial values for the bind parameters within
+ the textual statement; the keynames of the bindparams must match
+ those within the text of the statement. The types will be used
+ for pre-processing on bind values.
+
+ :param typemap:
+ a dictionary mapping the names of columns represented in the
+ columns clause of a ``SELECT`` statement to type objects,
+ which will be used to perform post-processing on columns within
+ the result set. This argument applies to any expression
+ that returns result sets.
+
+ """
+
+ self._bind = bind
+ self.bindparams = {}
+ self.typemap = typemap
+ if autocommit is not None:
+ util.warn_deprecated('autocommit on text() is deprecated. '
+ 'Use .execution_options(autocommit=Tru'
+ 'e)')
+ self._execution_options = \
+ self._execution_options.union(
+ {'autocommit': autocommit})
+ if typemap is not None:
+ for key in typemap:
+ typemap[key] = type_api.to_instance(typemap[key])
+
+ def repl(m):
+ self.bindparams[m.group(1)] = BindParameter(m.group(1))
+ return ':%s' % m.group(1)
+
+ # scan the string and search for bind parameter names, add them
+ # to the list of bindparams
+
+ self.text = self._bind_params_regex.sub(repl, text)
+ if bindparams is not None:
+ for b in bindparams:
+ self.bindparams[b.key] = b
+
+ @property
+ def type(self):
+ if self.typemap is not None and len(self.typemap) == 1:
+ return list(self.typemap)[0]
+ else:
+ return type_api.NULLTYPE
+
+ @property
+ def comparator(self):
+ return self.type.comparator_factory(self)
+
+ def self_group(self, against=None):
+ if against is operators.in_op:
+ return Grouping(self)
+ else:
+ return self
+
+ def _copy_internals(self, clone=_clone, **kw):
+ self.bindparams = dict((b.key, clone(b, **kw))
+ for b in self.bindparams.values())
+
+ def get_children(self, **kwargs):
+ return list(self.bindparams.values())
+
+
+class Null(ColumnElement):
+ """Represent the NULL keyword in a SQL statement.
+
+ """
+
+ __visit_name__ = 'null'
+
+ def __init__(self):
+ """Return a :class:`Null` object, which compiles to ``NULL``.
+
+ """
+ self.type = type_api.NULLTYPE
+
+ def compare(self, other):
+ return isinstance(other, Null)
+
+
+class False_(ColumnElement):
+ """Represent the ``false`` keyword in a SQL statement.
+
+ """
+
+ __visit_name__ = 'false'
+
+ def __init__(self):
+ """Return a :class:`False_` object.
+
+ """
+ self.type = type_api.BOOLEANTYPE
+
+ def compare(self, other):
+ return isinstance(other, False_)
+
+class True_(ColumnElement):
+ """Represent the ``true`` keyword in a SQL statement.
+
+ """
+
+ __visit_name__ = 'true'
+
+ def __init__(self):
+ """Return a :class:`True_` object.
+
+ """
+ self.type = type_api.BOOLEANTYPE
+
+ def compare(self, other):
+ return isinstance(other, True_)
+
+
+class ClauseList(ClauseElement):
+ """Describe a list of clauses, separated by an operator.
+
+ By default, is comma-separated, such as a column listing.
+
+ """
+ __visit_name__ = 'clauselist'
+
+ def __init__(self, *clauses, **kwargs):
+ self.operator = kwargs.pop('operator', operators.comma_op)
+ self.group = kwargs.pop('group', True)
+ self.group_contents = kwargs.pop('group_contents', True)
+ if self.group_contents:
+ self.clauses = [
+ _literal_as_text(clause).self_group(against=self.operator)
+ for clause in clauses if clause is not None]
+ else:
+ self.clauses = [
+ _literal_as_text(clause)
+ for clause in clauses if clause is not None]
+
+ def __iter__(self):
+ return iter(self.clauses)
+
+ def __len__(self):
+ return len(self.clauses)
+
+ @property
+ def _select_iterable(self):
+ return iter(self)
+
+ def append(self, clause):
+ # TODO: not sure if i like the 'group_contents' flag. need to
+ # define the difference between a ClauseList of ClauseLists,
+ # and a "flattened" ClauseList of ClauseLists. flatten()
+ # method ?
+ if self.group_contents:
+ self.clauses.append(_literal_as_text(clause).\
+ self_group(against=self.operator))
+ else:
+ self.clauses.append(_literal_as_text(clause))
+
+ def _copy_internals(self, clone=_clone, **kw):
+ self.clauses = [clone(clause, **kw) for clause in self.clauses]
+
+ def get_children(self, **kwargs):
+ return self.clauses
+
+ @property
+ def _from_objects(self):
+ return list(itertools.chain(*[c._from_objects for c in self.clauses]))
+
+ def self_group(self, against=None):
+ if self.group and operators.is_precedent(self.operator, against):
+ return Grouping(self)
+ else:
+ return self
+
+ def compare(self, other, **kw):
+ """Compare this :class:`.ClauseList` to the given :class:`.ClauseList`,
+ including a comparison of all the clause items.
+
+ """
+ if not isinstance(other, ClauseList) and len(self.clauses) == 1:
+ return self.clauses[0].compare(other, **kw)
+ elif isinstance(other, ClauseList) and \
+ len(self.clauses) == len(other.clauses):
+ for i in range(0, len(self.clauses)):
+ if not self.clauses[i].compare(other.clauses[i], **kw):
+ return False
+ else:
+ return self.operator == other.operator
+ else:
+ return False
+
+
+class BooleanClauseList(ClauseList, ColumnElement):
+ __visit_name__ = 'clauselist'
+
+ def __init__(self, *clauses, **kwargs):
+ super(BooleanClauseList, self).__init__(*clauses, **kwargs)
+ self.type = type_api.to_instance(kwargs.get('type_',
+ type_api.BOOLEANTYPE))
+
+ @property
+ def _select_iterable(self):
+ return (self, )
+
+ def self_group(self, against=None):
+ if not self.clauses:
+ return self
+ else:
+ return super(BooleanClauseList, self).self_group(against=against)
+
+
+class Tuple(ClauseList, ColumnElement):
+ """Represent a SQL tuple."""
+
+ def __init__(self, *clauses, **kw):
+ """Return a :class:`.Tuple`.
+
+ Main usage is to produce a composite IN construct::
+
+ from sqlalchemy import tuple_
+
+ tuple_(table.c.col1, table.c.col2).in_(
+ [(1, 2), (5, 12), (10, 19)]
+ )
+
+ .. warning::
+
+ The composite IN construct is not supported by all backends,
+ and is currently known to work on Postgresql and MySQL,
+ but not SQLite. Unsupported backends will raise
+ a subclass of :class:`~sqlalchemy.exc.DBAPIError` when such
+ an expression is invoked.
+
+ """
+
+ clauses = [_literal_as_binds(c) for c in clauses]
+ self.type = kw.pop('type_', None)
+ if self.type is None:
+ self.type = _type_from_args(clauses)
+ super(Tuple, self).__init__(*clauses, **kw)
+
+ @property
+ def _select_iterable(self):
+ return (self, )
+
+ def _bind_param(self, operator, obj):
+ return Tuple(*[
+ BindParameter(None, o, _compared_to_operator=operator,
+ _compared_to_type=self.type, unique=True)
+ for o in obj
+ ]).self_group()
+
+
+class Case(ColumnElement):
+ """Represent a SQL ``CASE`` construct.
+
+
+ """
+ __visit_name__ = 'case'
+
+ def __init__(self, whens, value=None, else_=None):
+ """Produce a :class:`.Case` object.
+
+ :param whens: A sequence of pairs, or alternatively a dict,
+ to be translated into "WHEN / THEN" clauses.
+
+ :param value: Optional for simple case statements, produces
+ a column expression as in "CASE <expr> WHEN ..."
+
+ :param else\_: Optional as well, for case defaults produces
+ the "ELSE" portion of the "CASE" statement.
+
+ The expressions used for THEN and ELSE,
+ when specified as strings, will be interpreted
+ as bound values. To specify textual SQL expressions
+ for these, use the :func:`literal_column`
+ construct.
+
+ The expressions used for the WHEN criterion
+ may only be literal strings when "value" is
+ present, i.e. CASE table.somecol WHEN "x" THEN "y".
+ Otherwise, literal strings are not accepted
+ in this position, and either the text(<string>)
+ or literal(<string>) constructs must be used to
+ interpret raw string values.
+
+ Usage examples::
+
+ case([(orderline.c.qty > 100, item.c.specialprice),
+ (orderline.c.qty > 10, item.c.bulkprice)
+ ], else_=item.c.regularprice)
+
+ case(value=emp.c.type, whens={
+ 'engineer': emp.c.salary * 1.1,
+ 'manager': emp.c.salary * 3,
+ })
+
+ Using :func:`.literal_column()`, to allow for databases that
+ do not support bind parameters in the ``then`` clause. The type
+ can be specified which determines the type of the :func:`case()` construct
+ overall::
+
+ case([(orderline.c.qty > 100,
+ literal_column("'greaterthan100'", String)),
+ (orderline.c.qty > 10, literal_column("'greaterthan10'",
+ String))
+ ], else_=literal_column("'lethan10'", String))
+
+ """
+
+ try:
+ whens = util.dictlike_iteritems(whens)
+ except TypeError:
+ pass
+
+ if value is not None:
+ whenlist = [
+ (_literal_as_binds(c).self_group(),
+ _literal_as_binds(r)) for (c, r) in whens
+ ]
+ else:
+ whenlist = [
+ (_no_literals(c).self_group(),
+ _literal_as_binds(r)) for (c, r) in whens
+ ]
+
+ if whenlist:
+ type_ = list(whenlist[-1])[-1].type
+ else:
+ type_ = None
+
+ if value is None:
+ self.value = None
+ else:
+ self.value = _literal_as_binds(value)
+
+ self.type = type_
+ self.whens = whenlist
+ if else_ is not None:
+ self.else_ = _literal_as_binds(else_)
+ else:
+ self.else_ = None
+
+ def _copy_internals(self, clone=_clone, **kw):
+ if self.value is not None:
+ self.value = clone(self.value, **kw)
+ self.whens = [(clone(x, **kw), clone(y, **kw))
+ for x, y in self.whens]
+ if self.else_ is not None:
+ self.else_ = clone(self.else_, **kw)
+
+ def get_children(self, **kwargs):
+ if self.value is not None:
+ yield self.value
+ for x, y in self.whens:
+ yield x
+ yield y
+ if self.else_ is not None:
+ yield self.else_
+
+ @property
+ def _from_objects(self):
+ return list(itertools.chain(*[x._from_objects for x in
+ self.get_children()]))
+
+
+def literal_column(text, type_=None):
+ """Return a textual column expression, as would be in the columns
+ clause of a ``SELECT`` statement.
+
+ The object returned supports further expressions in the same way as any
+ other column object, including comparison, math and string operations.
+ The type\_ parameter is important to determine proper expression behavior
+ (such as, '+' means string concatenation or numerical addition based on
+ the type).
+
+ :param text: the text of the expression; can be any SQL expression.
+ Quoting rules will not be applied. To specify a column-name expression
+ which should be subject to quoting rules, use the :func:`column`
+ function.
+
+ :param type\_: an optional :class:`~sqlalchemy.types.TypeEngine`
+ object which will
+ provide result-set translation and additional expression semantics for
+ this column. If left as None the type will be NullType.
+
+ """
+ return ColumnClause(text, type_=type_, is_literal=True)
+
+
+
+class Cast(ColumnElement):
+ """Represent the SQL ``CAST`` construct."""
+
+ __visit_name__ = 'cast'
+
+ def __init__(self, clause, totype, **kwargs):
+ """Return a :class:`.Cast` object.
+
+ Equivalent of SQL ``CAST(clause AS totype)``.
+
+ Use with a :class:`~sqlalchemy.types.TypeEngine` subclass, i.e::
+
+ cast(table.c.unit_price * table.c.qty, Numeric(10,4))
+
+ or::
+
+ cast(table.c.timestamp, DATE)
+
+ :class:`.Cast` is available using :func:`.cast` or alternatively
+ ``func.cast`` from the :data:`.func` namespace.
+
+ """
+ self.type = type_api.to_instance(totype)
+ self.clause = _literal_as_binds(clause, None)
+ self.typeclause = TypeClause(self.type)
+
+ def _copy_internals(self, clone=_clone, **kw):
+ self.clause = clone(self.clause, **kw)
+ self.typeclause = clone(self.typeclause, **kw)
+
+ def get_children(self, **kwargs):
+ return self.clause, self.typeclause
+
+ @property
+ def _from_objects(self):
+ return self.clause._from_objects
+
+
+class Extract(ColumnElement):
+ """Represent a SQL EXTRACT clause, ``extract(field FROM expr)``."""
+
+ __visit_name__ = 'extract'
+
+ def __init__(self, field, expr, **kwargs):
+ """Return a :class:`.Extract` construct.
+
+ This is typically available as :func:`.extract`
+ as well as ``func.extract`` from the
+ :data:`.func` namespace.
+
+ """
+ self.type = type_api.INTEGERTYPE
+ self.field = field
+ self.expr = _literal_as_binds(expr, None)
+
+ def _copy_internals(self, clone=_clone, **kw):
+ self.expr = clone(self.expr, **kw)
+
+ def get_children(self, **kwargs):
+ return self.expr,
+
+ @property
+ def _from_objects(self):
+ return self.expr._from_objects
+
+
+class UnaryExpression(ColumnElement):
+ """Define a 'unary' expression.
+
+ A unary expression has a single column expression
+ and an operator. The operator can be placed on the left
+ (where it is called the 'operator') or right (where it is called the
+ 'modifier') of the column expression.
+
+ """
+ __visit_name__ = 'unary'
+
+ def __init__(self, element, operator=None, modifier=None,
+ type_=None, negate=None):
+ self.operator = operator
+ self.modifier = modifier
+
+ self.element = _literal_as_text(element).\
+ self_group(against=self.operator or self.modifier)
+ self.type = type_api.to_instance(type_)
+ self.negate = negate
+
+ @classmethod
+ def _create_nullsfirst(cls, column):
+ """Return a NULLS FIRST ``ORDER BY`` clause element.
+
+ e.g.::
+
+ someselect.order_by(desc(table1.mycol).nullsfirst())
+
+ produces::
+
+ ORDER BY mycol DESC NULLS FIRST
+
+ """
+ return UnaryExpression(column, modifier=operators.nullsfirst_op)
+
+
+ @classmethod
+ def _create_nullslast(cls, column):
+ """Return a NULLS LAST ``ORDER BY`` clause element.
+
+ e.g.::
+
+ someselect.order_by(desc(table1.mycol).nullslast())
+
+ produces::
+
+ ORDER BY mycol DESC NULLS LAST
+
+ """
+ return UnaryExpression(column, modifier=operators.nullslast_op)
+
+
+ @classmethod
+ def _create_desc(cls, column):
+ """Return a descending ``ORDER BY`` clause element.
+
+ e.g.::
+
+ someselect.order_by(desc(table1.mycol))
+
+ produces::
+
+ ORDER BY mycol DESC
+
+ """
+ return UnaryExpression(column, modifier=operators.desc_op)
+
+ @classmethod
+ def _create_asc(cls, column):
+ """Return an ascending ``ORDER BY`` clause element.
+
+ e.g.::
+
+ someselect.order_by(asc(table1.mycol))
+
+ produces::
+
+ ORDER BY mycol ASC
+
+ """
+ return UnaryExpression(column, modifier=operators.asc_op)
+
+ @classmethod
+ def _create_distinct(cls, expr):
+ """Return a ``DISTINCT`` clause.
+
+ e.g.::
+
+ distinct(a)
+
+ renders::
+
+ DISTINCT a
+
+ """
+ expr = _literal_as_binds(expr)
+ return UnaryExpression(expr,
+ operator=operators.distinct_op, type_=expr.type)
+
+ @util.memoized_property
+ def _order_by_label_element(self):
+ if self.modifier in (operators.desc_op, operators.asc_op):
+ return self.element._order_by_label_element
+ else:
+ return None
+
+ @property
+ def _from_objects(self):
+ return self.element._from_objects
+
+ def _copy_internals(self, clone=_clone, **kw):
+ self.element = clone(self.element, **kw)
+
+ def get_children(self, **kwargs):
+ return self.element,
+
+ def compare(self, other, **kw):
+ """Compare this :class:`UnaryExpression` against the given
+ :class:`.ClauseElement`."""
+
+ return (
+ isinstance(other, UnaryExpression) and
+ self.operator == other.operator and
+ self.modifier == other.modifier and
+ self.element.compare(other.element, **kw)
+ )
+
+ def _negate(self):
+ if self.negate is not None:
+ return UnaryExpression(
+ self.element,
+ operator=self.negate,
+ negate=self.operator,
+ modifier=self.modifier,
+ type_=self.type)
+ else:
+ return super(UnaryExpression, self)._negate()
+
+ def self_group(self, against=None):
+ if self.operator and operators.is_precedent(self.operator,
+ against):
+ return Grouping(self)
+ else:
+ return self
+
+
+class BinaryExpression(ColumnElement):
+ """Represent an expression that is ``LEFT <operator> RIGHT``.
+
+ A :class:`.BinaryExpression` is generated automatically
+ whenever two column expressions are used in a Python binary expresion::
+
+ >>> from sqlalchemy.sql import column
+ >>> column('a') + column('b')
+ <sqlalchemy.sql.expression.BinaryExpression object at 0x101029dd0>
+ >>> print column('a') + column('b')
+ a + b
+
+ """
+
+ __visit_name__ = 'binary'
+
+ def __init__(self, left, right, operator, type_=None,
+ negate=None, modifiers=None):
+ # allow compatibility with libraries that
+ # refer to BinaryExpression directly and pass strings
+ if isinstance(operator, util.string_types):
+ operator = operators.custom_op(operator)
+ self._orig = (left, right)
+ self.left = _literal_as_text(left).self_group(against=operator)
+ self.right = _literal_as_text(right).self_group(against=operator)
+ self.operator = operator
+ self.type = type_api.to_instance(type_)
+ self.negate = negate
+
+ if modifiers is None:
+ self.modifiers = {}
+ else:
+ self.modifiers = modifiers
+
+ def __bool__(self):
+ if self.operator in (operator.eq, operator.ne):
+ return self.operator(hash(self._orig[0]), hash(self._orig[1]))
+ else:
+ raise TypeError("Boolean value of this clause is not defined")
+
+ __nonzero__ = __bool__
+
+ @property
+ def is_comparison(self):
+ return operators.is_comparison(self.operator)
+
+ @property
+ def _from_objects(self):
+ return self.left._from_objects + self.right._from_objects
+
+ def _copy_internals(self, clone=_clone, **kw):
+ self.left = clone(self.left, **kw)
+ self.right = clone(self.right, **kw)
+
+ def get_children(self, **kwargs):
+ return self.left, self.right
+
+ def compare(self, other, **kw):
+ """Compare this :class:`BinaryExpression` against the
+ given :class:`BinaryExpression`."""
+
+ return (
+ isinstance(other, BinaryExpression) and
+ self.operator == other.operator and
+ (
+ self.left.compare(other.left, **kw) and
+ self.right.compare(other.right, **kw) or
+ (
+ operators.is_commutative(self.operator) and
+ self.left.compare(other.right, **kw) and
+ self.right.compare(other.left, **kw)
+ )
+ )
+ )
+
+ def self_group(self, against=None):
+ if operators.is_precedent(self.operator, against):
+ return Grouping(self)
+ else:
+ return self
+
+ def _negate(self):
+ if self.negate is not None:
+ return BinaryExpression(
+ self.left,
+ self.right,
+ self.negate,
+ negate=self.operator,
+ type_=type_api.BOOLEANTYPE,
+ modifiers=self.modifiers)
+ else:
+ return super(BinaryExpression, self)._negate()
+
+
+
+
+class Grouping(ColumnElement):
+ """Represent a grouping within a column expression"""
+
+ __visit_name__ = 'grouping'
+
+ def __init__(self, element):
+ self.element = element
+ self.type = getattr(element, 'type', type_api.NULLTYPE)
+
+ @property
+ def _label(self):
+ return getattr(self.element, '_label', None) or self.anon_label
+
+ def _copy_internals(self, clone=_clone, **kw):
+ self.element = clone(self.element, **kw)
+
+ def get_children(self, **kwargs):
+ return self.element,
+
+ @property
+ def _from_objects(self):
+ return self.element._from_objects
+
+ def __getattr__(self, attr):
+ return getattr(self.element, attr)
+
+ def __getstate__(self):
+ return {'element': self.element, 'type': self.type}
+
+ def __setstate__(self, state):
+ self.element = state['element']
+ self.type = state['type']
+
+ def compare(self, other, **kw):
+ return isinstance(other, Grouping) and \
+ self.element.compare(other.element)
+
+
+class Over(ColumnElement):
+ """Represent an OVER clause.
+
+ This is a special operator against a so-called
+ "window" function, as well as any aggregate function,
+ which produces results relative to the result set
+ itself. It's supported only by certain database
+ backends.
+
+ """
+ __visit_name__ = 'over'
+
+ order_by = None
+ partition_by = None
+
+ def __init__(self, func, partition_by=None, order_by=None):
+ """Produce an :class:`.Over` object against a function.
+
+ Used against aggregate or so-called "window" functions,
+ for database backends that support window functions.
+
+ E.g.::
+
+ from sqlalchemy import over
+ over(func.row_number(), order_by='x')
+
+ Would produce "ROW_NUMBER() OVER(ORDER BY x)".
+
+ :param func: a :class:`.FunctionElement` construct, typically
+ generated by :data:`~.expression.func`.
+ :param partition_by: a column element or string, or a list
+ of such, that will be used as the PARTITION BY clause
+ of the OVER construct.
+ :param order_by: a column element or string, or a list
+ of such, that will be used as the ORDER BY clause
+ of the OVER construct.
+
+ This function is also available from the :data:`~.expression.func`
+ construct itself via the :meth:`.FunctionElement.over` method.
+
+ .. versionadded:: 0.7
+
+ """
+ self.func = func
+ if order_by is not None:
+ self.order_by = ClauseList(*util.to_list(order_by))
+ if partition_by is not None:
+ self.partition_by = ClauseList(*util.to_list(partition_by))
+
+ @util.memoized_property
+ def type(self):
+ return self.func.type
+
+ def get_children(self, **kwargs):
+ return [c for c in
+ (self.func, self.partition_by, self.order_by)
+ if c is not None]
+
+ def _copy_internals(self, clone=_clone, **kw):
+ self.func = clone(self.func, **kw)
+ if self.partition_by is not None:
+ self.partition_by = clone(self.partition_by, **kw)
+ if self.order_by is not None:
+ self.order_by = clone(self.order_by, **kw)
+
+ @property
+ def _from_objects(self):
+ return list(itertools.chain(
+ *[c._from_objects for c in
+ (self.func, self.partition_by, self.order_by)
+ if c is not None]
+ ))
+
+
+class Label(ColumnElement):
+ """Represents a column label (AS).
+
+ Represent a label, as typically applied to any column-level
+ element using the ``AS`` sql keyword.
+
+ """
+
+ __visit_name__ = 'label'
+
+ def __init__(self, name, element, type_=None):
+ """Return a :class:`Label` object for the
+ given :class:`.ColumnElement`.
+
+ A label changes the name of an element in the columns clause of a
+ ``SELECT`` statement, typically via the ``AS`` SQL keyword.
+
+ This functionality is more conveniently available via the
+ :meth:`.ColumnElement.label` method on :class:`.ColumnElement`.
+
+ :param name: label name
+
+ :param obj: a :class:`.ColumnElement`.
+
+ """
+ while isinstance(element, Label):
+ element = element.element
+ if name:
+ self.name = name
+ else:
+ self.name = _anonymous_label('%%(%d %s)s' % (id(self),
+ getattr(element, 'name', 'anon')))
+ self.key = self._label = self._key_label = self.name
+ self._element = element
+ self._type = type_
+ self.quote = element.quote
+ self._proxies = [element]
+
+ @util.memoized_property
+ def _order_by_label_element(self):
+ return self
+
+ @util.memoized_property
+ def type(self):
+ return type_api.to_instance(
+ self._type or getattr(self._element, 'type', None)
+ )
+
+ @util.memoized_property
+ def element(self):
+ return self._element.self_group(against=operators.as_)
+
+ def self_group(self, against=None):
+ sub_element = self._element.self_group(against=against)
+ if sub_element is not self._element:
+ return Label(self.name,
+ sub_element,
+ type_=self._type)
+ else:
+ return self
+
+ @property
+ def primary_key(self):
+ return self.element.primary_key
+
+ @property
+ def foreign_keys(self):
+ return self.element.foreign_keys
+
+ def get_children(self, **kwargs):
+ return self.element,
+
+ def _copy_internals(self, clone=_clone, **kw):
+ self.element = clone(self.element, **kw)
+
+ @property
+ def _from_objects(self):
+ return self.element._from_objects
+
+ def _make_proxy(self, selectable, name=None, **kw):
+ e = self.element._make_proxy(selectable,
+ name=name if name else self.name)
+ e._proxies.append(self)
+ if self._type is not None:
+ e.type = self._type
+ return e
+
+
+class ColumnClause(Immutable, ColumnElement):
+ """Represents a generic column expression from any textual string.
+
+ This includes columns associated with tables, aliases and select
+ statements, but also any arbitrary text. May or may not be bound
+ to an underlying :class:`.Selectable`.
+
+ :class:`.ColumnClause` is constructed by itself typically via
+ the :func:`~.expression.column` function. It may be placed directly
+ into constructs such as :func:`.select` constructs::
+
+ from sqlalchemy.sql import column, select
+
+ c1, c2 = column("c1"), column("c2")
+ s = select([c1, c2]).where(c1==5)
+
+ There is also a variant on :func:`~.expression.column` known
+ as :func:`~.expression.literal_column` - the difference is that
+ in the latter case, the string value is assumed to be an exact
+ expression, rather than a column name, so that no quoting rules
+ or similar are applied::
+
+ from sqlalchemy.sql import literal_column, select
+
+ s = select([literal_column("5 + 7")])
+
+ :class:`.ColumnClause` can also be used in a table-like
+ fashion by combining the :func:`~.expression.column` function
+ with the :func:`~.expression.table` function, to produce
+ a "lightweight" form of table metadata::
+
+ from sqlalchemy.sql import table, column
+
+ user = table("user",
+ column("id"),
+ column("name"),
+ column("description"),
+ )
+
+ The above construct can be created in an ad-hoc fashion and is
+ not associated with any :class:`.schema.MetaData`, unlike it's
+ more full fledged :class:`.schema.Table` counterpart.
+
+ """
+ __visit_name__ = 'column'
+
+ onupdate = default = server_default = server_onupdate = None
+
+ _memoized_property = util.group_expirable_memoized_property()
+
+ def __init__(self, text, type_=None, is_literal=False, _selectable=None):
+ """Construct a :class:`.ColumnClause` object.
+
+ :param text: the text of the element.
+
+ :param type: :class:`.types.TypeEngine` object which can associate
+ this :class:`.ColumnClause` with a type.
+
+ :param is_literal: if True, the :class:`.ColumnClause` is assumed to
+ be an exact expression that will be delivered to the output with no
+ quoting rules applied regardless of case sensitive settings. the
+ :func:`literal_column()` function is usually used to create such a
+ :class:`.ColumnClause`.
+
+ :param text: the name of the column. Quoting rules will be applied
+ to the clause like any other column name. For textual column constructs
+ that are not to be quoted, use the :func:`literal_column` function.
+
+ :param type\_: an optional :class:`~sqlalchemy.types.TypeEngine` object
+ which will provide result-set translation for this column.
+
+
+ """
+
+ self.key = self.name = text
+ self.table = _selectable
+ self.type = type_api.to_instance(type_)
+ self.is_literal = is_literal
+
+ def _compare_name_for_result(self, other):
+ if self.is_literal or \
+ self.table is None or \
+ not hasattr(other, 'proxy_set') or (
+ isinstance(other, ColumnClause) and other.is_literal
+ ):
+ return super(ColumnClause, self).\
+ _compare_name_for_result(other)
+ else:
+ return other.proxy_set.intersection(self.proxy_set)
+
+ def _get_table(self):
+ return self.__dict__['table']
+
+ def _set_table(self, table):
+ self._memoized_property.expire_instance(self)
+ self.__dict__['table'] = table
+ table = property(_get_table, _set_table)
+
+ @_memoized_property
+ def _from_objects(self):
+ t = self.table
+ if t is not None:
+ return [t]
+ else:
+ return []
+
+ @util.memoized_property
+ def description(self):
+ if util.py3k:
+ return self.name
+ else:
+ return self.name.encode('ascii', 'backslashreplace')
+
+ @_memoized_property
+ def _key_label(self):
+ if self.key != self.name:
+ return self._gen_label(self.key)
+ else:
+ return self._label
+
+ @_memoized_property
+ def _label(self):
+ return self._gen_label(self.name)
+
+ def _gen_label(self, name):
+ t = self.table
+ if self.is_literal:
+ return None
+
+ elif t is not None and t.named_with_column:
+ if getattr(t, 'schema', None):
+ label = t.schema.replace('.', '_') + "_" + \
+ t.name + "_" + name
+ else:
+ label = t.name + "_" + name
+
+ # ensure the label name doesn't conflict with that
+ # of an existing column
+ if label in t.c:
+ _label = label
+ counter = 1
+ while _label in t.c:
+ _label = label + "_" + str(counter)
+ counter += 1
+ label = _label
+
+ return _as_truncated(label)
+
+ else:
+ return name
+
+ def _bind_param(self, operator, obj):
+ return BindParameter(self.name, obj,
+ _compared_to_operator=operator,
+ _compared_to_type=self.type,
+ unique=True)
+
+ def _make_proxy(self, selectable, name=None, attach=True,
+ name_is_truncatable=False, **kw):
+ # propagate the "is_literal" flag only if we are keeping our name,
+ # otherwise its considered to be a label
+ is_literal = self.is_literal and (name is None or name == self.name)
+ c = self._constructor(
+ _as_truncated(name or self.name) if \
+ name_is_truncatable else \
+ (name or self.name),
+ type_=self.type,
+ _selectable=selectable,
+ is_literal=is_literal
+ )
+ if name is None:
+ c.key = self.key
+ c._proxies = [self]
+ if selectable._is_clone_of is not None:
+ c._is_clone_of = \
+ selectable._is_clone_of.columns.get(c.key)
+
+ if attach:
+ selectable._columns[c.key] = c
+ return c
+
+
+class _IdentifiedClause(Executable, ClauseElement):
+
+ __visit_name__ = 'identified'
+ _execution_options = \
+ Executable._execution_options.union({'autocommit': False})
+ quote = None
+
+ def __init__(self, ident):
+ self.ident = ident
+
+
+class SavepointClause(_IdentifiedClause):
+ __visit_name__ = 'savepoint'
+
+
+class RollbackToSavepointClause(_IdentifiedClause):
+ __visit_name__ = 'rollback_to_savepoint'
+
+
+class ReleaseSavepointClause(_IdentifiedClause):
+ __visit_name__ = 'release_savepoint'
+
+
+class _truncated_label(util.text_type):
+ """A unicode subclass used to identify symbolic "
+ "names that may require truncation."""
+
+ def apply_map(self, map_):
+ return self
+
+# for backwards compatibility in case
+# someone is re-implementing the
+# _truncated_identifier() sequence in a custom
+# compiler
+_generated_label = _truncated_label
+
+
+class _anonymous_label(_truncated_label):
+ """A unicode subclass used to identify anonymously
+ generated names."""
+
+ def __add__(self, other):
+ return _anonymous_label(
+ util.text_type(self) +
+ util.text_type(other))
+
+ def __radd__(self, other):
+ return _anonymous_label(
+ util.text_type(other) +
+ util.text_type(self))
+
+ def apply_map(self, map_):
+ return self % map_
+
+
+def _as_truncated(value):
+ """coerce the given value to :class:`._truncated_label`.
+
+ Existing :class:`._truncated_label` and
+ :class:`._anonymous_label` objects are passed
+ unchanged.
+ """
+
+ if isinstance(value, _truncated_label):
+ return value
+ else:
+ return _truncated_label(value)
+
+
+def _string_or_unprintable(element):
+ if isinstance(element, util.string_types):
+ return element
+ else:
+ try:
+ return str(element)
+ except:
+ return "unprintable element %r" % element
+
+
+def _expand_cloned(elements):
+ """expand the given set of ClauseElements to be the set of all 'cloned'
+ predecessors.
+
+ """
+ return itertools.chain(*[x._cloned_set for x in elements])
+
+
+def _select_iterables(elements):
+ """expand tables into individual columns in the
+ given list of column expressions.
+
+ """
+ return itertools.chain(*[c._select_iterable for c in elements])
+
+
+def _cloned_intersection(a, b):
+ """return the intersection of sets a and b, counting
+ any overlap between 'cloned' predecessors.
+
+ The returned set is in terms of the entities present within 'a'.
+
+ """
+ all_overlap = set(_expand_cloned(a)).intersection(_expand_cloned(b))
+ return set(elem for elem in a
+ if all_overlap.intersection(elem._cloned_set))
+
+def _cloned_difference(a, b):
+ all_overlap = set(_expand_cloned(a)).intersection(_expand_cloned(b))
+ return set(elem for elem in a
+ if not all_overlap.intersection(elem._cloned_set))
+
+
+def _labeled(element):
+ if not hasattr(element, 'name'):
+ return element.label(None)
+ else:
+ return element
+
+
+def _is_column(col):
+ """True if ``col`` is an instance of :class:`.ColumnElement`."""
+
+ return isinstance(col, ColumnElement)
+
+
+def _find_columns(clause):
+ """locate Column objects within the given expression."""
+
+ cols = util.column_set()
+ traverse(clause, {}, {'column': cols.add})
+ return cols
+
+
+# there is some inconsistency here between the usage of
+# inspect() vs. checking for Visitable and __clause_element__.
+# Ideally all functions here would derive from inspect(),
+# however the inspect() versions add significant callcount
+# overhead for critical functions like _interpret_as_column_or_from().
+# Generally, the column-based functions are more performance critical
+# and are fine just checking for __clause_element__(). it's only
+# _interpret_as_from() where we'd like to be able to receive ORM entities
+# that have no defined namespace, hence inspect() is needed there.
+
+
+def _column_as_key(element):
+ if isinstance(element, util.string_types):
+ return element
+ if hasattr(element, '__clause_element__'):
+ element = element.__clause_element__()
+ try:
+ return element.key
+ except AttributeError:
+ return None
+
+
+def _clause_element_as_expr(element):
+ if hasattr(element, '__clause_element__'):
+ return element.__clause_element__()
+ else:
+ return element
+
+
+def _literal_as_text(element):
+ if isinstance(element, Visitable):
+ return element
+ elif hasattr(element, '__clause_element__'):
+ return element.__clause_element__()
+ elif isinstance(element, util.string_types):
+ return TextClause(util.text_type(element))
+ elif isinstance(element, (util.NoneType, bool)):
+ return _const_expr(element)
+ else:
+ raise exc.ArgumentError(
+ "SQL expression object or string expected."
+ )
+
+
+def _no_literals(element):
+ if hasattr(element, '__clause_element__'):
+ return element.__clause_element__()
+ elif not isinstance(element, Visitable):
+ raise exc.ArgumentError("Ambiguous literal: %r. Use the 'text()' "
+ "function to indicate a SQL expression "
+ "literal, or 'literal()' to indicate a "
+ "bound value." % element)
+ else:
+ return element
+
+
+def _is_literal(element):
+ return not isinstance(element, Visitable) and \
+ not hasattr(element, '__clause_element__')
+
+
+def _only_column_elements_or_none(element, name):
+ if element is None:
+ return None
+ else:
+ return _only_column_elements(element, name)
+
+
+def _only_column_elements(element, name):
+ if hasattr(element, '__clause_element__'):
+ element = element.__clause_element__()
+ if not isinstance(element, ColumnElement):
+ raise exc.ArgumentError(
+ "Column-based expression object expected for argument "
+ "'%s'; got: '%s', type %s" % (name, element, type(element)))
+ return element
+
+
+def _literal_as_binds(element, name=None, type_=None):
+ if hasattr(element, '__clause_element__'):
+ return element.__clause_element__()
+ elif not isinstance(element, Visitable):
+ if element is None:
+ return Null()
+ else:
+ return BindParameter(name, element, type_=type_, unique=True)
+ else:
+ return element
+
+
+def _interpret_as_column_or_from(element):
+ if isinstance(element, Visitable):
+ return element
+ elif hasattr(element, '__clause_element__'):
+ return element.__clause_element__()
+
+ insp = inspection.inspect(element, raiseerr=False)
+ if insp is None:
+ if isinstance(element, (util.NoneType, bool)):
+ return _const_expr(element)
+ elif hasattr(insp, "selectable"):
+ return insp.selectable
+
+ return ColumnClause(str(element), is_literal=True)
+
+
+def _const_expr(element):
+ if isinstance(element, (Null, False_, True_)):
+ return element
+ elif element is None:
+ return Null()
+ elif element is False:
+ return False_()
+ elif element is True:
+ return True_()
+ else:
+ raise exc.ArgumentError(
+ "Expected None, False, or True"
+ )
+
+
+def _type_from_args(args):
+ for a in args:
+ if not a.type._isnull:
+ return a.type
+ else:
+ return type_api.NULLTYPE
+
+
+def _corresponding_column_or_error(fromclause, column,
+ require_embedded=False):
+ c = fromclause.corresponding_column(column,
+ require_embedded=require_embedded)
+ if c is None:
+ raise exc.InvalidRequestError(
+ "Given column '%s', attached to table '%s', "
+ "failed to locate a corresponding column from table '%s'"
+ %
+ (column,
+ getattr(column, 'table', None),
+ fromclause.description)
+ )
+ return c
+
+
+class AnnotatedColumnElement(Annotated):
+ def __init__(self, element, values):
+ Annotated.__init__(self, element, values)
+ ColumnElement.comparator._reset(self)
+ for attr in ('name', 'key'):
+ if self.__dict__.get(attr, False) is None:
+ self.__dict__.pop(attr)
+
+ def _with_annotations(self, values):
+ clone = super(AnnotatedColumnElement, self)._with_annotations(values)
+ ColumnElement.comparator._reset(clone)
+ return clone
+
+ @util.memoized_property
+ def name(self):
+ """pull 'name' from parent, if not present"""
+ return self._Annotated__element.name
+
+ @util.memoized_property
+ def key(self):
+ """pull 'key' from parent, if not present"""
+ return self._Annotated__element.key
+
+ @util.memoized_property
+ def info(self):
+ return self._Annotated__element.info
+
diff --git a/lib/sqlalchemy/sql/expression.py b/lib/sqlalchemy/sql/expression.py
index 121583cf8..872165859 100644
--- a/lib/sqlalchemy/sql/expression.py
+++ b/lib/sqlalchemy/sql/expression.py
@@ -4,44 +4,15 @@
# This module is part of SQLAlchemy and is released under
# the MIT License: http://www.opensource.org/licenses/mit-license.php
-"""Defines the base components of SQL expression trees.
+"""Defines the public namespace for SQL expression constructs.
-All components are derived from a common base class
-:class:`.ClauseElement`. Common behaviors are organized
-based on class hierarchies, in some cases via mixins.
-
-All object construction from this package occurs via functions which
-in some cases will construct composite :class:`.ClauseElement` structures
-together, and in other cases simply return a single :class:`.ClauseElement`
-constructed directly. The function interface affords a more "DSL-ish"
-feel to constructing SQL expressions and also allows future class
-reorganizations.
-
-Even though classes are not constructed directly from the outside,
-most classes which have additional public methods are considered to be
-public (i.e. have no leading underscore). Other classes which are
-"semi-public" are marked with a single leading underscore; these
-classes usually have few or no public methods and are less guaranteed
-to stay the same in future releases.
+Prior to version 0.9, this module contained all of "elements", "dml",
+"default_comparator" and "selectable". The module was broken up
+and most "factory" functions were moved to be grouped with their associated
+class.
"""
-from __future__ import unicode_literals
-import itertools
-import re
-from operator import attrgetter
-
-from .. import util, exc, inspection
-from . import operators
-from .operators import ColumnOperators
-from .visitors import Visitable, cloned_traverse
-import operator
-
-functions = util.importlater("sqlalchemy.sql", "functions")
-sqlutil = util.importlater("sqlalchemy.sql", "util")
-sqltypes = util.importlater("sqlalchemy", "types")
-default = util.importlater("sqlalchemy.engine", "default")
-
__all__ = [
'Alias', 'ClauseElement', 'ColumnCollection', 'ColumnElement',
'CompoundSelect', 'Delete', 'FromClause', 'Insert', 'Join', 'Select',
@@ -52,6582 +23,84 @@ __all__ = [
'literal', 'literal_column', 'not_', 'null', 'nullsfirst', 'nullslast',
'or_', 'outparam', 'outerjoin', 'over', 'select', 'subquery',
'table', 'text',
- 'tuple_', 'type_coerce', 'union', 'union_all', 'update', ]
-
-PARSE_AUTOCOMMIT = util.symbol('PARSE_AUTOCOMMIT')
-NO_ARG = util.symbol('NO_ARG')
-
-
-def nullsfirst(column):
- """Return a NULLS FIRST ``ORDER BY`` clause element.
-
- e.g.::
-
- someselect.order_by(desc(table1.mycol).nullsfirst())
-
- produces::
-
- ORDER BY mycol DESC NULLS FIRST
-
- """
- return UnaryExpression(column, modifier=operators.nullsfirst_op)
-
-
-def nullslast(column):
- """Return a NULLS LAST ``ORDER BY`` clause element.
-
- e.g.::
-
- someselect.order_by(desc(table1.mycol).nullslast())
-
- produces::
-
- ORDER BY mycol DESC NULLS LAST
-
- """
- return UnaryExpression(column, modifier=operators.nullslast_op)
-
-
-def desc(column):
- """Return a descending ``ORDER BY`` clause element.
-
- e.g.::
-
- someselect.order_by(desc(table1.mycol))
-
- produces::
-
- ORDER BY mycol DESC
-
- """
- return UnaryExpression(column, modifier=operators.desc_op)
-
-
-def asc(column):
- """Return an ascending ``ORDER BY`` clause element.
-
- e.g.::
-
- someselect.order_by(asc(table1.mycol))
-
- produces::
-
- ORDER BY mycol ASC
-
- """
- return UnaryExpression(column, modifier=operators.asc_op)
-
-
-def outerjoin(left, right, onclause=None):
- """Return an ``OUTER JOIN`` clause element.
-
- The returned object is an instance of :class:`.Join`.
-
- Similar functionality is also available via the
- :meth:`~.FromClause.outerjoin()` method on any
- :class:`.FromClause`.
-
- :param left: The left side of the join.
-
- :param right: The right side of the join.
-
- :param onclause: Optional criterion for the ``ON`` clause, is
- derived from foreign key relationships established between
- left and right otherwise.
-
- To chain joins together, use the :meth:`.FromClause.join` or
- :meth:`.FromClause.outerjoin` methods on the resulting
- :class:`.Join` object.
-
- """
- return Join(left, right, onclause, isouter=True)
-
-
-def join(left, right, onclause=None, isouter=False):
- """Return a ``JOIN`` clause element (regular inner join).
-
- The returned object is an instance of :class:`.Join`.
-
- Similar functionality is also available via the
- :meth:`~.FromClause.join()` method on any
- :class:`.FromClause`.
-
- :param left: The left side of the join.
-
- :param right: The right side of the join.
-
- :param onclause: Optional criterion for the ``ON`` clause, is
- derived from foreign key relationships established between
- left and right otherwise.
-
- To chain joins together, use the :meth:`.FromClause.join` or
- :meth:`.FromClause.outerjoin` methods on the resulting
- :class:`.Join` object.
-
-
- """
- return Join(left, right, onclause, isouter)
-
-
-def select(columns=None, whereclause=None, from_obj=[], **kwargs):
- """Returns a ``SELECT`` clause element.
-
- Similar functionality is also available via the :func:`select()`
- method on any :class:`.FromClause`.
-
- The returned object is an instance of :class:`.Select`.
-
- All arguments which accept :class:`.ClauseElement` arguments also accept
- string arguments, which will be converted as appropriate into
- either :func:`text()` or :func:`literal_column()` constructs.
-
- .. seealso::
-
- :ref:`coretutorial_selecting` - Core Tutorial description of
- :func:`.select`.
-
- :param columns:
- A list of :class:`.ClauseElement` objects, typically
- :class:`.ColumnElement` objects or subclasses, which will form the
- columns clause of the resulting statement. For all members which are
- instances of :class:`.Selectable`, the individual :class:`.ColumnElement`
- members of the :class:`.Selectable` will be added individually to the
- columns clause. For example, specifying a
- :class:`~sqlalchemy.schema.Table` instance will result in all the
- contained :class:`~sqlalchemy.schema.Column` objects within to be added
- to the columns clause.
-
- This argument is not present on the form of :func:`select()`
- available on :class:`~sqlalchemy.schema.Table`.
-
- :param whereclause:
- A :class:`.ClauseElement` expression which will be used to form the
- ``WHERE`` clause.
-
- :param from_obj:
- A list of :class:`.ClauseElement` objects which will be added to the
- ``FROM`` clause of the resulting statement. Note that "from" objects are
- automatically located within the columns and whereclause ClauseElements.
- Use this parameter to explicitly specify "from" objects which are not
- automatically locatable. This could include
- :class:`~sqlalchemy.schema.Table` objects that aren't otherwise present,
- or :class:`.Join` objects whose presence will supercede that of the
- :class:`~sqlalchemy.schema.Table` objects already located in the other
- clauses.
-
- :param autocommit:
- Deprecated. Use .execution_options(autocommit=<True|False>)
- to set the autocommit option.
-
- :param bind=None:
- an :class:`~.base.Engine` or :class:`~.base.Connection` instance
- to which the
- resulting :class:`.Select` object will be bound. The :class:`.Select`
- object will otherwise automatically bind to whatever
- :class:`~.base.Connectable` instances can be located within its contained
- :class:`.ClauseElement` members.
-
- :param correlate=True:
- indicates that this :class:`.Select` object should have its
- contained :class:`.FromClause` elements "correlated" to an enclosing
- :class:`.Select` object. This means that any :class:`.ClauseElement`
- instance within the "froms" collection of this :class:`.Select`
- which is also present in the "froms" collection of an
- enclosing select will not be rendered in the ``FROM`` clause
- of this select statement.
-
- :param distinct=False:
- when ``True``, applies a ``DISTINCT`` qualifier to the columns
- clause of the resulting statement.
-
- The boolean argument may also be a column expression or list
- of column expressions - this is a special calling form which
- is understood by the Postgresql dialect to render the
- ``DISTINCT ON (<columns>)`` syntax.
-
- ``distinct`` is also available via the :meth:`~.Select.distinct`
- generative method.
-
- :param for_update=False:
- when ``True``, applies ``FOR UPDATE`` to the end of the
- resulting statement.
-
- Certain database dialects also support
- alternate values for this parameter:
-
- * With the MySQL dialect, the value ``"read"`` translates to
- ``LOCK IN SHARE MODE``.
- * With the Oracle and Postgresql dialects, the value ``"nowait"``
- translates to ``FOR UPDATE NOWAIT``.
- * With the Postgresql dialect, the values "read" and ``"read_nowait"``
- translate to ``FOR SHARE`` and ``FOR SHARE NOWAIT``, respectively.
-
- .. versionadded:: 0.7.7
-
- :param group_by:
- a list of :class:`.ClauseElement` objects which will comprise the
- ``GROUP BY`` clause of the resulting select.
-
- :param having:
- a :class:`.ClauseElement` that will comprise the ``HAVING`` clause
- of the resulting select when ``GROUP BY`` is used.
-
- :param limit=None:
- a numerical value which usually compiles to a ``LIMIT``
- expression in the resulting select. Databases that don't
- support ``LIMIT`` will attempt to provide similar
- functionality.
-
- :param offset=None:
- a numeric value which usually compiles to an ``OFFSET``
- expression in the resulting select. Databases that don't
- support ``OFFSET`` will attempt to provide similar
- functionality.
-
- :param order_by:
- a scalar or list of :class:`.ClauseElement` objects which will
- comprise the ``ORDER BY`` clause of the resulting select.
-
- :param use_labels=False:
- when ``True``, the statement will be generated using labels
- for each column in the columns clause, which qualify each
- column with its parent table's (or aliases) name so that name
- conflicts between columns in different tables don't occur.
- The format of the label is <tablename>_<column>. The "c"
- collection of the resulting :class:`.Select` object will use these
- names as well for targeting column members.
-
- use_labels is also available via the :meth:`~.SelectBase.apply_labels`
- generative method.
-
- """
- return Select(columns, whereclause=whereclause, from_obj=from_obj,
- **kwargs)
-
-
-def subquery(alias, *args, **kwargs):
- """Return an :class:`.Alias` object derived
- from a :class:`.Select`.
-
- name
- alias name
-
- \*args, \**kwargs
-
- all other arguments are delivered to the
- :func:`select` function.
-
- """
- return Select(*args, **kwargs).alias(alias)
-
-
-def insert(table, values=None, inline=False, **kwargs):
- """Represent an ``INSERT`` statement via the :class:`.Insert` SQL
- construct.
-
- Similar functionality is available via the
- :meth:`~.TableClause.insert` method on
- :class:`~.schema.Table`.
-
-
- :param table: :class:`.TableClause` which is the subject of the insert.
-
- :param values: collection of values to be inserted; see
- :meth:`.Insert.values` for a description of allowed formats here.
- Can be omitted entirely; a :class:`.Insert` construct will also
- dynamically render the VALUES clause at execution time based on
- the parameters passed to :meth:`.Connection.execute`.
-
- :param inline: if True, SQL defaults will be compiled 'inline' into the
- statement and not pre-executed.
-
- If both `values` and compile-time bind parameters are present, the
- compile-time bind parameters override the information specified
- within `values` on a per-key basis.
-
- The keys within `values` can be either :class:`~sqlalchemy.schema.Column`
- objects or their string identifiers. Each key may reference one of:
-
- * a literal data value (i.e. string, number, etc.);
- * a Column object;
- * a SELECT statement.
-
- If a ``SELECT`` statement is specified which references this
- ``INSERT`` statement's table, the statement will be correlated
- against the ``INSERT`` statement.
-
- .. seealso::
-
- :ref:`coretutorial_insert_expressions` - SQL Expression Tutorial
-
- :ref:`inserts_and_updates` - SQL Expression Tutorial
-
- """
- return Insert(table, values, inline=inline, **kwargs)
-
-
-def update(table, whereclause=None, values=None, inline=False, **kwargs):
- """Represent an ``UPDATE`` statement via the :class:`.Update` SQL
- construct.
-
- E.g.::
-
- from sqlalchemy import update
-
- stmt = update(users).where(users.c.id==5).\\
- values(name='user #5')
-
- Similar functionality is available via the
- :meth:`~.TableClause.update` method on
- :class:`.Table`::
-
-
- stmt = users.update().\\
- where(users.c.id==5).\\
- values(name='user #5')
-
- :param table: A :class:`.Table` object representing the database
- table to be updated.
-
- :param whereclause: Optional SQL expression describing the ``WHERE``
- condition of the ``UPDATE`` statement. Modern applications
- may prefer to use the generative :meth:`~Update.where()`
- method to specify the ``WHERE`` clause.
-
- The WHERE clause can refer to multiple tables.
- For databases which support this, an ``UPDATE FROM`` clause will
- be generated, or on MySQL, a multi-table update. The statement
- will fail on databases that don't have support for multi-table
- update statements. A SQL-standard method of referring to
- additional tables in the WHERE clause is to use a correlated
- subquery::
-
- users.update().values(name='ed').where(
- users.c.name==select([addresses.c.email_address]).\\
- where(addresses.c.user_id==users.c.id).\\
- as_scalar()
- )
-
- .. versionchanged:: 0.7.4
- The WHERE clause can refer to multiple tables.
-
- :param values:
- Optional dictionary which specifies the ``SET`` conditions of the
- ``UPDATE``. If left as ``None``, the ``SET``
- conditions are determined from those parameters passed to the
- statement during the execution and/or compilation of the
- statement. When compiled standalone without any parameters,
- the ``SET`` clause generates for all columns.
-
- Modern applications may prefer to use the generative
- :meth:`.Update.values` method to set the values of the
- UPDATE statement.
-
- :param inline:
- if True, SQL defaults present on :class:`.Column` objects via
- the ``default`` keyword will be compiled 'inline' into the statement
- and not pre-executed. This means that their values will not
- be available in the dictionary returned from
- :meth:`.ResultProxy.last_updated_params`.
-
- If both ``values`` and compile-time bind parameters are present, the
- compile-time bind parameters override the information specified
- within ``values`` on a per-key basis.
-
- The keys within ``values`` can be either :class:`.Column`
- objects or their string identifiers (specifically the "key" of the
- :class:`.Column`, normally but not necessarily equivalent to
- its "name"). Normally, the
- :class:`.Column` objects used here are expected to be
- part of the target :class:`.Table` that is the table
- to be updated. However when using MySQL, a multiple-table
- UPDATE statement can refer to columns from any of
- the tables referred to in the WHERE clause.
-
- The values referred to in ``values`` are typically:
-
- * a literal data value (i.e. string, number, etc.)
- * a SQL expression, such as a related :class:`.Column`,
- a scalar-returning :func:`.select` construct,
- etc.
-
- When combining :func:`.select` constructs within the values
- clause of an :func:`.update` construct,
- the subquery represented by the :func:`.select` should be
- *correlated* to the parent table, that is, providing criterion
- which links the table inside the subquery to the outer table
- being updated::
-
- users.update().values(
- name=select([addresses.c.email_address]).\\
- where(addresses.c.user_id==users.c.id).\\
- as_scalar()
- )
-
- .. seealso::
-
- :ref:`inserts_and_updates` - SQL Expression
- Language Tutorial
-
-
- """
- return Update(
- table,
- whereclause=whereclause,
- values=values,
- inline=inline,
- **kwargs)
-
-
-def delete(table, whereclause=None, **kwargs):
- """Represent a ``DELETE`` statement via the :class:`.Delete` SQL
- construct.
-
- Similar functionality is available via the
- :meth:`~.TableClause.delete` method on
- :class:`~.schema.Table`.
-
- :param table: The table to be updated.
-
- :param whereclause: A :class:`.ClauseElement` describing the ``WHERE``
- condition of the ``UPDATE`` statement. Note that the
- :meth:`~Delete.where()` generative method may be used instead.
-
- .. seealso::
-
- :ref:`deletes` - SQL Expression Tutorial
-
- """
- return Delete(table, whereclause, **kwargs)
-
-
-def and_(*clauses):
- """Join a list of clauses together using the ``AND`` operator.
-
- The ``&`` operator is also overloaded on all :class:`.ColumnElement`
- subclasses to produce the
- same result.
-
- """
- if len(clauses) == 1:
- return clauses[0]
- return BooleanClauseList(operator=operators.and_, *clauses)
-
-
-def or_(*clauses):
- """Join a list of clauses together using the ``OR`` operator.
-
- The ``|`` operator is also overloaded on all
- :class:`.ColumnElement` subclasses to produce the
- same result.
-
- """
- if len(clauses) == 1:
- return clauses[0]
- return BooleanClauseList(operator=operators.or_, *clauses)
-
-
-def not_(clause):
- """Return a negation of the given clause, i.e. ``NOT(clause)``.
-
- The ``~`` operator is also overloaded on all
- :class:`.ColumnElement` subclasses to produce the
- same result.
-
- """
- return operators.inv(_literal_as_binds(clause))
-
-
-def distinct(expr):
- """Return a ``DISTINCT`` clause.
-
- e.g.::
-
- distinct(a)
-
- renders::
-
- DISTINCT a
-
- """
- expr = _literal_as_binds(expr)
- return UnaryExpression(expr,
- operator=operators.distinct_op, type_=expr.type)
-
-
-def between(ctest, cleft, cright):
- """Return a ``BETWEEN`` predicate clause.
-
- Equivalent of SQL ``clausetest BETWEEN clauseleft AND clauseright``.
-
- The :func:`between()` method on all
- :class:`.ColumnElement` subclasses provides
- similar functionality.
-
- """
- ctest = _literal_as_binds(ctest)
- return ctest.between(cleft, cright)
-
-
-def case(whens, value=None, else_=None):
- """Produce a ``CASE`` statement.
-
- whens
- A sequence of pairs, or alternatively a dict,
- to be translated into "WHEN / THEN" clauses.
-
- value
- Optional for simple case statements, produces
- a column expression as in "CASE <expr> WHEN ..."
-
- else\_
- Optional as well, for case defaults produces
- the "ELSE" portion of the "CASE" statement.
-
- The expressions used for THEN and ELSE,
- when specified as strings, will be interpreted
- as bound values. To specify textual SQL expressions
- for these, use the :func:`literal_column`
- construct.
-
- The expressions used for the WHEN criterion
- may only be literal strings when "value" is
- present, i.e. CASE table.somecol WHEN "x" THEN "y".
- Otherwise, literal strings are not accepted
- in this position, and either the text(<string>)
- or literal(<string>) constructs must be used to
- interpret raw string values.
-
- Usage examples::
-
- case([(orderline.c.qty > 100, item.c.specialprice),
- (orderline.c.qty > 10, item.c.bulkprice)
- ], else_=item.c.regularprice)
- case(value=emp.c.type, whens={
- 'engineer': emp.c.salary * 1.1,
- 'manager': emp.c.salary * 3,
- })
-
- Using :func:`literal_column()`, to allow for databases that
- do not support bind parameters in the ``then`` clause. The type
- can be specified which determines the type of the :func:`case()` construct
- overall::
-
- case([(orderline.c.qty > 100,
- literal_column("'greaterthan100'", String)),
- (orderline.c.qty > 10, literal_column("'greaterthan10'",
- String))
- ], else_=literal_column("'lethan10'", String))
-
- """
-
- return Case(whens, value=value, else_=else_)
-
-
-def cast(clause, totype, **kwargs):
- """Return a ``CAST`` function.
-
- Equivalent of SQL ``CAST(clause AS totype)``.
-
- Use with a :class:`~sqlalchemy.types.TypeEngine` subclass, i.e::
-
- cast(table.c.unit_price * table.c.qty, Numeric(10,4))
-
- or::
-
- cast(table.c.timestamp, DATE)
-
- """
- return Cast(clause, totype, **kwargs)
-
-
-def extract(field, expr):
- """Return the clause ``extract(field FROM expr)``."""
-
- return Extract(field, expr)
-
-
-def collate(expression, collation):
- """Return the clause ``expression COLLATE collation``.
-
- e.g.::
-
- collate(mycolumn, 'utf8_bin')
-
- produces::
-
- mycolumn COLLATE utf8_bin
-
- """
-
- expr = _literal_as_binds(expression)
- return BinaryExpression(
- expr,
- _literal_as_text(collation),
- operators.collate, type_=expr.type)
-
-
-def exists(*args, **kwargs):
- """Return an ``EXISTS`` clause as applied to a :class:`.Select` object.
-
- Calling styles are of the following forms::
-
- # use on an existing select()
- s = select([table.c.col1]).where(table.c.col2==5)
- s = exists(s)
-
- # construct a select() at once
- exists(['*'], **select_arguments).where(criterion)
-
- # columns argument is optional, generates "EXISTS (SELECT *)"
- # by default.
- exists().where(table.c.col2==5)
-
- """
- return Exists(*args, **kwargs)
-
-
-def union(*selects, **kwargs):
- """Return a ``UNION`` of multiple selectables.
-
- The returned object is an instance of
- :class:`.CompoundSelect`.
-
- A similar :func:`union()` method is available on all
- :class:`.FromClause` subclasses.
-
- \*selects
- a list of :class:`.Select` instances.
-
- \**kwargs
- available keyword arguments are the same as those of
- :func:`select`.
-
- """
- return CompoundSelect(CompoundSelect.UNION, *selects, **kwargs)
-
-
-def union_all(*selects, **kwargs):
- """Return a ``UNION ALL`` of multiple selectables.
-
- The returned object is an instance of
- :class:`.CompoundSelect`.
-
- A similar :func:`union_all()` method is available on all
- :class:`.FromClause` subclasses.
-
- \*selects
- a list of :class:`.Select` instances.
-
- \**kwargs
- available keyword arguments are the same as those of
- :func:`select`.
-
- """
- return CompoundSelect(CompoundSelect.UNION_ALL, *selects, **kwargs)
-
-
-def except_(*selects, **kwargs):
- """Return an ``EXCEPT`` of multiple selectables.
-
- The returned object is an instance of
- :class:`.CompoundSelect`.
-
- \*selects
- a list of :class:`.Select` instances.
-
- \**kwargs
- available keyword arguments are the same as those of
- :func:`select`.
-
- """
- return CompoundSelect(CompoundSelect.EXCEPT, *selects, **kwargs)
-
-
-def except_all(*selects, **kwargs):
- """Return an ``EXCEPT ALL`` of multiple selectables.
-
- The returned object is an instance of
- :class:`.CompoundSelect`.
-
- \*selects
- a list of :class:`.Select` instances.
-
- \**kwargs
- available keyword arguments are the same as those of
- :func:`select`.
-
- """
- return CompoundSelect(CompoundSelect.EXCEPT_ALL, *selects, **kwargs)
-
-
-def intersect(*selects, **kwargs):
- """Return an ``INTERSECT`` of multiple selectables.
-
- The returned object is an instance of
- :class:`.CompoundSelect`.
-
- \*selects
- a list of :class:`.Select` instances.
-
- \**kwargs
- available keyword arguments are the same as those of
- :func:`select`.
-
- """
- return CompoundSelect(CompoundSelect.INTERSECT, *selects, **kwargs)
-
-
-def intersect_all(*selects, **kwargs):
- """Return an ``INTERSECT ALL`` of multiple selectables.
-
- The returned object is an instance of
- :class:`.CompoundSelect`.
-
- \*selects
- a list of :class:`.Select` instances.
-
- \**kwargs
- available keyword arguments are the same as those of
- :func:`select`.
-
- """
- return CompoundSelect(CompoundSelect.INTERSECT_ALL, *selects, **kwargs)
-
-
-def alias(selectable, name=None, flat=False):
- """Return an :class:`.Alias` object.
-
- An :class:`.Alias` represents any :class:`.FromClause`
- with an alternate name assigned within SQL, typically using the ``AS``
- clause when generated, e.g. ``SELECT * FROM table AS aliasname``.
-
- Similar functionality is available via the
- :meth:`~.FromClause.alias` method
- available on all :class:`.FromClause` subclasses.
-
- When an :class:`.Alias` is created from a :class:`.Table` object,
- this has the effect of the table being rendered
- as ``tablename AS aliasname`` in a SELECT statement.
-
- For :func:`.select` objects, the effect is that of creating a named
- subquery, i.e. ``(select ...) AS aliasname``.
-
- The ``name`` parameter is optional, and provides the name
- to use in the rendered SQL. If blank, an "anonymous" name
- will be deterministically generated at compile time.
- Deterministic means the name is guaranteed to be unique against
- other constructs used in the same statement, and will also be the
- same name for each successive compilation of the same statement
- object.
-
- :param selectable: any :class:`.FromClause` subclass,
- such as a table, select statement, etc.
-
- :param name: string name to be assigned as the alias.
- If ``None``, a name will be deterministically generated
- at compile time.
-
- :param flat: Will be passed through to if the given selectable
- is an instance of :class:`.Join` - see :meth:`.Join.alias`
- for details.
-
- .. versionadded:: 0.9.0
-
- """
- return selectable.alias(name=name, flat=flat)
-
-
-def literal(value, type_=None):
- """Return a literal clause, bound to a bind parameter.
-
- Literal clauses are created automatically when non- :class:`.ClauseElement`
- objects (such as strings, ints, dates, etc.) are used in a comparison
- operation with a :class:`.ColumnElement`
- subclass, such as a :class:`~sqlalchemy.schema.Column` object.
- Use this function to force the
- generation of a literal clause, which will be created as a
- :class:`BindParameter` with a bound value.
-
- :param value: the value to be bound. Can be any Python object supported by
- the underlying DB-API, or is translatable via the given type argument.
-
- :param type\_: an optional :class:`~sqlalchemy.types.TypeEngine` which
- will provide bind-parameter translation for this literal.
-
- """
- return BindParameter(None, value, type_=type_, unique=True)
-
-
-def tuple_(*expr):
- """Return a SQL tuple.
-
- Main usage is to produce a composite IN construct::
-
- tuple_(table.c.col1, table.c.col2).in_(
- [(1, 2), (5, 12), (10, 19)]
- )
-
- .. warning::
-
- The composite IN construct is not supported by all backends,
- and is currently known to work on Postgresql and MySQL,
- but not SQLite. Unsupported backends will raise
- a subclass of :class:`~sqlalchemy.exc.DBAPIError` when such
- an expression is invoked.
-
- """
- return Tuple(*expr)
-
-
-def type_coerce(expr, type_):
- """Coerce the given expression into the given type,
- on the Python side only.
-
- :func:`.type_coerce` is roughly similar to :func:`.cast`, except no
- "CAST" expression is rendered - the given type is only applied towards
- expression typing and against received result values.
-
- e.g.::
-
- from sqlalchemy.types import TypeDecorator
- import uuid
-
- class AsGuid(TypeDecorator):
- impl = String
-
- def process_bind_param(self, value, dialect):
- if value is not None:
- return str(value)
- else:
- return None
-
- def process_result_value(self, value, dialect):
- if value is not None:
- return uuid.UUID(value)
- else:
- return None
-
- conn.execute(
- select([type_coerce(mytable.c.ident, AsGuid)]).\\
- where(
- type_coerce(mytable.c.ident, AsGuid) ==
- uuid.uuid3(uuid.NAMESPACE_URL, 'bar')
- )
- )
-
- """
- type_ = sqltypes.to_instance(type_)
-
- if hasattr(expr, '__clause_expr__'):
- return type_coerce(expr.__clause_expr__())
- elif isinstance(expr, BindParameter):
- bp = expr._clone()
- bp.type = type_
- return bp
- elif not isinstance(expr, Visitable):
- if expr is None:
- return null()
- else:
- return literal(expr, type_=type_)
- else:
- return Label(None, expr, type_=type_)
-
-
-def label(name, obj):
- """Return a :class:`Label` object for the
- given :class:`.ColumnElement`.
-
- A label changes the name of an element in the columns clause of a
- ``SELECT`` statement, typically via the ``AS`` SQL keyword.
-
- This functionality is more conveniently available via the
- :func:`label()` method on :class:`.ColumnElement`.
-
- name
- label name
-
- obj
- a :class:`.ColumnElement`.
-
- """
- return Label(name, obj)
-
-
-def column(text, type_=None):
- """Return a textual column clause, as would be in the columns clause of a
- ``SELECT`` statement.
-
- The object returned is an instance of :class:`.ColumnClause`, which
- represents the "syntactical" portion of the schema-level
- :class:`~sqlalchemy.schema.Column` object. It is often used directly
- within :func:`~.expression.select` constructs or with lightweight
- :func:`~.expression.table` constructs.
-
- Note that the :func:`~.expression.column` function is not part of
- the ``sqlalchemy`` namespace. It must be imported from the
- ``sql`` package::
-
- from sqlalchemy.sql import table, column
-
- :param text: the name of the column. Quoting rules will be applied
- to the clause like any other column name. For textual column constructs
- that are not to be quoted, use the :func:`literal_column` function.
-
- :param type\_: an optional :class:`~sqlalchemy.types.TypeEngine` object
- which will provide result-set translation for this column.
-
- See :class:`.ColumnClause` for further examples.
-
- """
- return ColumnClause(text, type_=type_)
-
-
-def literal_column(text, type_=None):
- """Return a textual column expression, as would be in the columns
- clause of a ``SELECT`` statement.
-
- The object returned supports further expressions in the same way as any
- other column object, including comparison, math and string operations.
- The type\_ parameter is important to determine proper expression behavior
- (such as, '+' means string concatenation or numerical addition based on
- the type).
-
- :param text: the text of the expression; can be any SQL expression.
- Quoting rules will not be applied. To specify a column-name expression
- which should be subject to quoting rules, use the :func:`column`
- function.
-
- :param type\_: an optional :class:`~sqlalchemy.types.TypeEngine`
- object which will
- provide result-set translation and additional expression semantics for
- this column. If left as None the type will be NullType.
-
- """
- return ColumnClause(text, type_=type_, is_literal=True)
-
-
-def table(name, *columns):
- """Represent a textual table clause.
-
- The object returned is an instance of :class:`.TableClause`, which
- represents the "syntactical" portion of the schema-level
- :class:`~.schema.Table` object.
- It may be used to construct lightweight table constructs.
-
- Note that the :func:`~.expression.table` function is not part of
- the ``sqlalchemy`` namespace. It must be imported from the
- ``sql`` package::
-
- from sqlalchemy.sql import table, column
-
- :param name: Name of the table.
-
- :param columns: A collection of :func:`~.expression.column` constructs.
-
- See :class:`.TableClause` for further examples.
-
- """
- return TableClause(name, *columns)
-
-
-def bindparam(key, value=NO_ARG, type_=None, unique=False, required=NO_ARG,
- quote=None, callable_=None):
- """Create a bind parameter clause with the given key.
-
- :param key:
- the key for this bind param. Will be used in the generated
- SQL statement for dialects that use named parameters. This
- value may be modified when part of a compilation operation,
- if other :class:`BindParameter` objects exist with the same
- key, or if its length is too long and truncation is
- required.
-
- :param value:
- Initial value for this bind param. This value may be
- overridden by the dictionary of parameters sent to statement
- compilation/execution.
-
- Defaults to ``None``, however if neither ``value`` nor
- ``callable`` are passed explicitly, the ``required`` flag will be
- set to ``True`` which has the effect of requiring a value be present
- when the statement is actually executed.
-
- .. versionchanged:: 0.8 The ``required`` flag is set to ``True``
- automatically if ``value`` or ``callable`` is not passed.
-
- :param callable\_:
- A callable function that takes the place of "value". The function
- will be called at statement execution time to determine the
- ultimate value. Used for scenarios where the actual bind
- value cannot be determined at the point at which the clause
- construct is created, but embedded bind values are still desirable.
-
- :param type\_:
- A ``TypeEngine`` object that will be used to pre-process the
- value corresponding to this :class:`BindParameter` at
- execution time.
-
- :param unique:
- if True, the key name of this BindParamClause will be
- modified if another :class:`BindParameter` of the same name
- already has been located within the containing
- :class:`.ClauseElement`.
-
- :param required:
- If ``True``, a value is required at execution time. If not passed,
- is set to ``True`` or ``False`` based on whether or not
- one of ``value`` or ``callable`` were passed..
-
- .. versionchanged:: 0.8 If the ``required`` flag is not specified,
- it will be set automatically to ``True`` or ``False`` depending
- on whether or not the ``value`` or ``callable`` parameters
- were specified.
-
- :param quote:
- True if this parameter name requires quoting and is not
- currently known as a SQLAlchemy reserved word; this currently
- only applies to the Oracle backend.
-
- """
- if isinstance(key, ColumnClause):
- type_ = key.type
- key = key.name
- if required is NO_ARG:
- required = (value is NO_ARG and callable_ is None)
- if value is NO_ARG:
- value = None
- return BindParameter(key, value, type_=type_,
- callable_=callable_,
- unique=unique, required=required,
- quote=quote)
-
-
-def outparam(key, type_=None):
- """Create an 'OUT' parameter for usage in functions (stored procedures),
- for databases which support them.
-
- The ``outparam`` can be used like a regular function parameter.
- The "output" value will be available from the
- :class:`~sqlalchemy.engine.ResultProxy` object via its ``out_parameters``
- attribute, which returns a dictionary containing the values.
-
- """
- return BindParameter(
- key, None, type_=type_, unique=False, isoutparam=True)
-
-
-def text(text, bind=None, *args, **kwargs):
- """Create a SQL construct that is represented by a literal string.
-
- E.g.::
-
- t = text("SELECT * FROM users")
- result = connection.execute(t)
-
- The advantages :func:`text` provides over a plain string are
- backend-neutral support for bind parameters, per-statement
- execution options, as well as
- bind parameter and result-column typing behavior, allowing
- SQLAlchemy type constructs to play a role when executing
- a statement that is specified literally.
-
- Bind parameters are specified by name, using the format ``:name``.
- E.g.::
-
- t = text("SELECT * FROM users WHERE id=:user_id")
- result = connection.execute(t, user_id=12)
-
- To invoke SQLAlchemy typing logic for bind parameters, the
- ``bindparams`` list allows specification of :func:`bindparam`
- constructs which specify the type for a given name::
-
- t = text("SELECT id FROM users WHERE updated_at>:updated",
- bindparams=[bindparam('updated', DateTime())]
- )
-
- Typing during result row processing is also an important concern.
- Result column types
- are specified using the ``typemap`` dictionary, where the keys
- match the names of columns. These names are taken from what
- the DBAPI returns as ``cursor.description``::
-
- t = text("SELECT id, name FROM users",
- typemap={
- 'id':Integer,
- 'name':Unicode
- }
- )
-
- The :func:`text` construct is used internally for most cases when
- a literal string is specified for part of a larger query, such as
- within :func:`select()`, :func:`update()`,
- :func:`insert()` or :func:`delete()`. In those cases, the same
- bind parameter syntax is applied::
-
- s = select([users.c.id, users.c.name]).where("id=:user_id")
- result = connection.execute(s, user_id=12)
-
- Using :func:`text` explicitly usually implies the construction
- of a full, standalone statement. As such, SQLAlchemy refers
- to it as an :class:`.Executable` object, and it supports
- the :meth:`Executable.execution_options` method. For example,
- a :func:`text` construct that should be subject to "autocommit"
- can be set explicitly so using the ``autocommit`` option::
-
- t = text("EXEC my_procedural_thing()").\\
- execution_options(autocommit=True)
-
- Note that SQLAlchemy's usual "autocommit" behavior applies to
- :func:`text` constructs - that is, statements which begin
- with a phrase such as ``INSERT``, ``UPDATE``, ``DELETE``,
- or a variety of other phrases specific to certain backends, will
- be eligible for autocommit if no transaction is in progress.
-
- :param text:
- the text of the SQL statement to be created. use ``:<param>``
- to specify bind parameters; they will be compiled to their
- engine-specific format.
-
- :param autocommit:
- Deprecated. Use .execution_options(autocommit=<True|False>)
- to set the autocommit option.
-
- :param bind:
- an optional connection or engine to be used for this text query.
-
- :param bindparams:
- a list of :func:`bindparam()` instances which can be used to define
- the types and/or initial values for the bind parameters within
- the textual statement; the keynames of the bindparams must match
- those within the text of the statement. The types will be used
- for pre-processing on bind values.
-
- :param typemap:
- a dictionary mapping the names of columns represented in the
- columns clause of a ``SELECT`` statement to type objects,
- which will be used to perform post-processing on columns within
- the result set. This argument applies to any expression
- that returns result sets.
-
- """
- return TextClause(text, bind=bind, *args, **kwargs)
-
-
-def over(func, partition_by=None, order_by=None):
- """Produce an OVER clause against a function.
-
- Used against aggregate or so-called "window" functions,
- for database backends that support window functions.
-
- E.g.::
-
- from sqlalchemy import over
- over(func.row_number(), order_by='x')
-
- Would produce "ROW_NUMBER() OVER(ORDER BY x)".
-
- :param func: a :class:`.FunctionElement` construct, typically
- generated by :data:`~.expression.func`.
- :param partition_by: a column element or string, or a list
- of such, that will be used as the PARTITION BY clause
- of the OVER construct.
- :param order_by: a column element or string, or a list
- of such, that will be used as the ORDER BY clause
- of the OVER construct.
-
- This function is also available from the :data:`~.expression.func`
- construct itself via the :meth:`.FunctionElement.over` method.
-
- .. versionadded:: 0.7
-
- """
- return Over(func, partition_by=partition_by, order_by=order_by)
-
-
-def null():
- """Return a :class:`Null` object, which compiles to ``NULL``.
-
- """
- return Null()
-
-
-def true():
- """Return a :class:`True_` object, which compiles to ``true``, or the
- boolean equivalent for the target dialect.
-
- """
- return True_()
-
-
-def false():
- """Return a :class:`False_` object, which compiles to ``false``, or the
- boolean equivalent for the target dialect.
-
- """
- return False_()
-
-
-class _FunctionGenerator(object):
- """Generate :class:`.Function` objects based on getattr calls."""
-
- def __init__(self, **opts):
- self.__names = []
- self.opts = opts
-
- def __getattr__(self, name):
- # passthru __ attributes; fixes pydoc
- if name.startswith('__'):
- try:
- return self.__dict__[name]
- except KeyError:
- raise AttributeError(name)
-
- elif name.endswith('_'):
- name = name[0:-1]
- f = _FunctionGenerator(**self.opts)
- f.__names = list(self.__names) + [name]
- return f
-
- def __call__(self, *c, **kwargs):
- o = self.opts.copy()
- o.update(kwargs)
-
- tokens = len(self.__names)
-
- if tokens == 2:
- package, fname = self.__names
- elif tokens == 1:
- package, fname = "_default", self.__names[0]
- else:
- package = None
-
- if package is not None and \
- package in functions._registry and \
- fname in functions._registry[package]:
- func = functions._registry[package][fname]
- return func(*c, **o)
-
- return Function(self.__names[-1],
- packagenames=self.__names[0:-1], *c, **o)
-
-# "func" global - i.e. func.count()
-func = _FunctionGenerator()
-"""Generate SQL function expressions.
-
- :data:`.func` is a special object instance which generates SQL
- functions based on name-based attributes, e.g.::
-
- >>> print func.count(1)
- count(:param_1)
-
- The element is a column-oriented SQL element like any other, and is
- used in that way::
-
- >>> print select([func.count(table.c.id)])
- SELECT count(sometable.id) FROM sometable
-
- Any name can be given to :data:`.func`. If the function name is unknown to
- SQLAlchemy, it will be rendered exactly as is. For common SQL functions
- which SQLAlchemy is aware of, the name may be interpreted as a *generic
- function* which will be compiled appropriately to the target database::
-
- >>> print func.current_timestamp()
- CURRENT_TIMESTAMP
-
- To call functions which are present in dot-separated packages,
- specify them in the same manner::
-
- >>> print func.stats.yield_curve(5, 10)
- stats.yield_curve(:yield_curve_1, :yield_curve_2)
-
- SQLAlchemy can be made aware of the return type of functions to enable
- type-specific lexical and result-based behavior. For example, to ensure
- that a string-based function returns a Unicode value and is similarly
- treated as a string in expressions, specify
- :class:`~sqlalchemy.types.Unicode` as the type:
-
- >>> print func.my_string(u'hi', type_=Unicode) + ' ' + \
- ... func.my_string(u'there', type_=Unicode)
- my_string(:my_string_1) || :my_string_2 || my_string(:my_string_3)
-
- The object returned by a :data:`.func` call is usually an instance of
- :class:`.Function`.
- This object meets the "column" interface, including comparison and labeling
- functions. The object can also be passed the :meth:`~.Connectable.execute`
- method of a :class:`.Connection` or :class:`.Engine`, where it will be
- wrapped inside of a SELECT statement first::
-
- print connection.execute(func.current_timestamp()).scalar()
-
- In a few exception cases, the :data:`.func` accessor
- will redirect a name to a built-in expression such as :func:`.cast`
- or :func:`.extract`, as these names have well-known meaning
- but are not exactly the same as "functions" from a SQLAlchemy
- perspective.
-
- .. versionadded:: 0.8 :data:`.func` can return non-function expression
- constructs for common quasi-functional names like :func:`.cast`
- and :func:`.extract`.
-
- Functions which are interpreted as "generic" functions know how to
- calculate their return type automatically. For a listing of known generic
- functions, see :ref:`generic_functions`.
-
-"""
-
-# "modifier" global - i.e. modifier.distinct
-# TODO: use UnaryExpression for this instead ?
-modifier = _FunctionGenerator(group=False)
-
-
-class _truncated_label(util.text_type):
- """A unicode subclass used to identify symbolic "
- "names that may require truncation."""
-
- def apply_map(self, map_):
- return self
-
-# for backwards compatibility in case
-# someone is re-implementing the
-# _truncated_identifier() sequence in a custom
-# compiler
-_generated_label = _truncated_label
-
-
-class _anonymous_label(_truncated_label):
- """A unicode subclass used to identify anonymously
- generated names."""
-
- def __add__(self, other):
- return _anonymous_label(
- util.text_type(self) +
- util.text_type(other))
-
- def __radd__(self, other):
- return _anonymous_label(
- util.text_type(other) +
- util.text_type(self))
-
- def apply_map(self, map_):
- return self % map_
-
-
-def _as_truncated(value):
- """coerce the given value to :class:`._truncated_label`.
-
- Existing :class:`._truncated_label` and
- :class:`._anonymous_label` objects are passed
- unchanged.
- """
-
- if isinstance(value, _truncated_label):
- return value
- else:
- return _truncated_label(value)
-
-
-def _string_or_unprintable(element):
- if isinstance(element, util.string_types):
- return element
- else:
- try:
- return str(element)
- except:
- return "unprintable element %r" % element
-
-
-def _clone(element, **kw):
- return element._clone()
-
-
-def _expand_cloned(elements):
- """expand the given set of ClauseElements to be the set of all 'cloned'
- predecessors.
-
- """
- return itertools.chain(*[x._cloned_set for x in elements])
-
-
-def _select_iterables(elements):
- """expand tables into individual columns in the
- given list of column expressions.
-
- """
- return itertools.chain(*[c._select_iterable for c in elements])
-
-
-def _cloned_intersection(a, b):
- """return the intersection of sets a and b, counting
- any overlap between 'cloned' predecessors.
-
- The returned set is in terms of the entities present within 'a'.
-
- """
- all_overlap = set(_expand_cloned(a)).intersection(_expand_cloned(b))
- return set(elem for elem in a
- if all_overlap.intersection(elem._cloned_set))
-
-def _cloned_difference(a, b):
- all_overlap = set(_expand_cloned(a)).intersection(_expand_cloned(b))
- return set(elem for elem in a
- if not all_overlap.intersection(elem._cloned_set))
-
-def _from_objects(*elements):
- return itertools.chain(*[element._from_objects for element in elements])
-
-
-def _labeled(element):
- if not hasattr(element, 'name'):
- return element.label(None)
- else:
- return element
-
-
-# there is some inconsistency here between the usage of
-# inspect() vs. checking for Visitable and __clause_element__.
-# Ideally all functions here would derive from inspect(),
-# however the inspect() versions add significant callcount
-# overhead for critical functions like _interpret_as_column_or_from().
-# Generally, the column-based functions are more performance critical
-# and are fine just checking for __clause_element__(). it's only
-# _interpret_as_from() where we'd like to be able to receive ORM entities
-# that have no defined namespace, hence inspect() is needed there.
-
-
-def _column_as_key(element):
- if isinstance(element, util.string_types):
- return element
- if hasattr(element, '__clause_element__'):
- element = element.__clause_element__()
- try:
- return element.key
- except AttributeError:
- return None
-
-
-def _clause_element_as_expr(element):
- if hasattr(element, '__clause_element__'):
- return element.__clause_element__()
- else:
- return element
-
-
-def _literal_as_text(element):
- if isinstance(element, Visitable):
- return element
- elif hasattr(element, '__clause_element__'):
- return element.__clause_element__()
- elif isinstance(element, util.string_types):
- return TextClause(util.text_type(element))
- elif isinstance(element, (util.NoneType, bool)):
- return _const_expr(element)
- else:
- raise exc.ArgumentError(
- "SQL expression object or string expected."
- )
-
-
-def _no_literals(element):
- if hasattr(element, '__clause_element__'):
- return element.__clause_element__()
- elif not isinstance(element, Visitable):
- raise exc.ArgumentError("Ambiguous literal: %r. Use the 'text()' "
- "function to indicate a SQL expression "
- "literal, or 'literal()' to indicate a "
- "bound value." % element)
- else:
- return element
-
-
-def _is_literal(element):
- return not isinstance(element, Visitable) and \
- not hasattr(element, '__clause_element__')
-
-
-def _only_column_elements_or_none(element, name):
- if element is None:
- return None
- else:
- return _only_column_elements(element, name)
-
-
-def _only_column_elements(element, name):
- if hasattr(element, '__clause_element__'):
- element = element.__clause_element__()
- if not isinstance(element, ColumnElement):
- raise exc.ArgumentError(
- "Column-based expression object expected for argument "
- "'%s'; got: '%s', type %s" % (name, element, type(element)))
- return element
-
-
-def _literal_as_binds(element, name=None, type_=None):
- if hasattr(element, '__clause_element__'):
- return element.__clause_element__()
- elif not isinstance(element, Visitable):
- if element is None:
- return null()
- else:
- return _BindParamClause(name, element, type_=type_, unique=True)
- else:
- return element
-
-
-def _interpret_as_column_or_from(element):
- if isinstance(element, Visitable):
- return element
- elif hasattr(element, '__clause_element__'):
- return element.__clause_element__()
-
- insp = inspection.inspect(element, raiseerr=False)
- if insp is None:
- if isinstance(element, (util.NoneType, bool)):
- return _const_expr(element)
- elif hasattr(insp, "selectable"):
- return insp.selectable
-
- return literal_column(str(element))
-
-
-def _interpret_as_from(element):
- insp = inspection.inspect(element, raiseerr=False)
- if insp is None:
- if isinstance(element, util.string_types):
- return TextClause(util.text_type(element))
- elif hasattr(insp, "selectable"):
- return insp.selectable
- raise exc.ArgumentError("FROM expression expected")
-
-def _interpret_as_select(element):
- element = _interpret_as_from(element)
- if isinstance(element, Alias):
- element = element.original
- if not isinstance(element, Select):
- element = element.select()
- return element
-
-
-def _const_expr(element):
- if isinstance(element, (Null, False_, True_)):
- return element
- elif element is None:
- return null()
- elif element is False:
- return false()
- elif element is True:
- return true()
- else:
- raise exc.ArgumentError(
- "Expected None, False, or True"
- )
-
-
-def _type_from_args(args):
- for a in args:
- if not isinstance(a.type, sqltypes.NullType):
- return a.type
- else:
- return sqltypes.NullType
-
-
-def _corresponding_column_or_error(fromclause, column,
- require_embedded=False):
- c = fromclause.corresponding_column(column,
- require_embedded=require_embedded)
- if c is None:
- raise exc.InvalidRequestError(
- "Given column '%s', attached to table '%s', "
- "failed to locate a corresponding column from table '%s'"
- %
- (column,
- getattr(column, 'table', None),
- fromclause.description)
- )
- return c
-
-
-@util.decorator
-def _generative(fn, *args, **kw):
- """Mark a method as generative."""
-
- self = args[0]._generate()
- fn(self, *args[1:], **kw)
- return self
-
-
-def is_column(col):
- """True if ``col`` is an instance of :class:`.ColumnElement`."""
-
- return isinstance(col, ColumnElement)
-
-
-class ClauseElement(Visitable):
- """Base class for elements of a programmatically constructed SQL
- expression.
-
- """
- __visit_name__ = 'clause'
-
- _annotations = {}
- supports_execution = False
- _from_objects = []
- bind = None
- _is_clone_of = None
- is_selectable = False
- is_clause_element = True
-
- _order_by_label_element = None
-
- def _clone(self):
- """Create a shallow copy of this ClauseElement.
-
- This method may be used by a generative API. Its also used as
- part of the "deep" copy afforded by a traversal that combines
- the _copy_internals() method.
-
- """
- c = self.__class__.__new__(self.__class__)
- c.__dict__ = self.__dict__.copy()
- ClauseElement._cloned_set._reset(c)
- ColumnElement.comparator._reset(c)
-
- # this is a marker that helps to "equate" clauses to each other
- # when a Select returns its list of FROM clauses. the cloning
- # process leaves around a lot of remnants of the previous clause
- # typically in the form of column expressions still attached to the
- # old table.
- c._is_clone_of = self
-
- return c
-
- @property
- def _constructor(self):
- """return the 'constructor' for this ClauseElement.
-
- This is for the purposes for creating a new object of
- this type. Usually, its just the element's __class__.
- However, the "Annotated" version of the object overrides
- to return the class of its proxied element.
-
- """
- return self.__class__
-
- @util.memoized_property
- def _cloned_set(self):
- """Return the set consisting all cloned ancestors of this
- ClauseElement.
-
- Includes this ClauseElement. This accessor tends to be used for
- FromClause objects to identify 'equivalent' FROM clauses, regardless
- of transformative operations.
-
- """
- s = util.column_set()
- f = self
- while f is not None:
- s.add(f)
- f = f._is_clone_of
- return s
-
- def __getstate__(self):
- d = self.__dict__.copy()
- d.pop('_is_clone_of', None)
- return d
-
- def _annotate(self, values):
- """return a copy of this ClauseElement with annotations
- updated by the given dictionary.
-
- """
- return sqlutil.Annotated(self, values)
-
- def _with_annotations(self, values):
- """return a copy of this ClauseElement with annotations
- replaced by the given dictionary.
-
- """
- return sqlutil.Annotated(self, values)
-
- def _deannotate(self, values=None, clone=False):
- """return a copy of this :class:`.ClauseElement` with annotations
- removed.
-
- :param values: optional tuple of individual values
- to remove.
-
- """
- if clone:
- # clone is used when we are also copying
- # the expression for a deep deannotation
- return self._clone()
- else:
- # if no clone, since we have no annotations we return
- # self
- return self
-
- def unique_params(self, *optionaldict, **kwargs):
- """Return a copy with :func:`bindparam()` elements replaced.
-
- Same functionality as ``params()``, except adds `unique=True`
- to affected bind parameters so that multiple statements can be
- used.
-
- """
- return self._params(True, optionaldict, kwargs)
-
- def params(self, *optionaldict, **kwargs):
- """Return a copy with :func:`bindparam()` elements replaced.
-
- Returns a copy of this ClauseElement with :func:`bindparam()`
- elements replaced with values taken from the given dictionary::
-
- >>> clause = column('x') + bindparam('foo')
- >>> print clause.compile().params
- {'foo':None}
- >>> print clause.params({'foo':7}).compile().params
- {'foo':7}
-
- """
- return self._params(False, optionaldict, kwargs)
-
- def _params(self, unique, optionaldict, kwargs):
- if len(optionaldict) == 1:
- kwargs.update(optionaldict[0])
- elif len(optionaldict) > 1:
- raise exc.ArgumentError(
- "params() takes zero or one positional dictionary argument")
-
- def visit_bindparam(bind):
- if bind.key in kwargs:
- bind.value = kwargs[bind.key]
- bind.required = False
- if unique:
- bind._convert_to_unique()
- return cloned_traverse(self, {}, {'bindparam': visit_bindparam})
-
- def compare(self, other, **kw):
- """Compare this ClauseElement to the given ClauseElement.
-
- Subclasses should override the default behavior, which is a
- straight identity comparison.
-
- \**kw are arguments consumed by subclass compare() methods and
- may be used to modify the criteria for comparison.
- (see :class:`.ColumnElement`)
-
- """
- return self is other
-
- def _copy_internals(self, clone=_clone, **kw):
- """Reassign internal elements to be clones of themselves.
-
- Called during a copy-and-traverse operation on newly
- shallow-copied elements to create a deep copy.
-
- The given clone function should be used, which may be applying
- additional transformations to the element (i.e. replacement
- traversal, cloned traversal, annotations).
-
- """
- pass
-
- def get_children(self, **kwargs):
- """Return immediate child elements of this :class:`.ClauseElement`.
-
- This is used for visit traversal.
-
- \**kwargs may contain flags that change the collection that is
- returned, for example to return a subset of items in order to
- cut down on larger traversals, or to return child items from a
- different context (such as schema-level collections instead of
- clause-level).
-
- """
- return []
-
- def self_group(self, against=None):
- """Apply a 'grouping' to this :class:`.ClauseElement`.
-
- This method is overridden by subclasses to return a
- "grouping" construct, i.e. parenthesis. In particular
- it's used by "binary" expressions to provide a grouping
- around themselves when placed into a larger expression,
- as well as by :func:`.select` constructs when placed into
- the FROM clause of another :func:`.select`. (Note that
- subqueries should be normally created using the
- :func:`.Select.alias` method, as many platforms require
- nested SELECT statements to be named).
-
- As expressions are composed together, the application of
- :meth:`self_group` is automatic - end-user code should never
- need to use this method directly. Note that SQLAlchemy's
- clause constructs take operator precedence into account -
- so parenthesis might not be needed, for example, in
- an expression like ``x OR (y AND z)`` - AND takes precedence
- over OR.
-
- The base :meth:`self_group` method of :class:`.ClauseElement`
- just returns self.
- """
- return self
-
- def compile(self, bind=None, dialect=None, **kw):
- """Compile this SQL expression.
-
- The return value is a :class:`~.Compiled` object.
- Calling ``str()`` or ``unicode()`` on the returned value will yield a
- string representation of the result. The
- :class:`~.Compiled` object also can return a
- dictionary of bind parameter names and values
- using the ``params`` accessor.
-
- :param bind: An ``Engine`` or ``Connection`` from which a
- ``Compiled`` will be acquired. This argument takes precedence over
- this :class:`.ClauseElement`'s bound engine, if any.
-
- :param column_keys: Used for INSERT and UPDATE statements, a list of
- column names which should be present in the VALUES clause of the
- compiled statement. If ``None``, all columns from the target table
- object are rendered.
-
- :param dialect: A ``Dialect`` instance from which a ``Compiled``
- will be acquired. This argument takes precedence over the `bind`
- argument as well as this :class:`.ClauseElement`'s bound engine, if
- any.
-
- :param inline: Used for INSERT statements, for a dialect which does
- not support inline retrieval of newly generated primary key
- columns, will force the expression used to create the new primary
- key value to be rendered inline within the INSERT statement's
- VALUES clause. This typically refers to Sequence execution but may
- also refer to any server-side default generation function
- associated with a primary key `Column`.
-
- """
-
- if not dialect:
- if bind:
- dialect = bind.dialect
- elif self.bind:
- dialect = self.bind.dialect
- bind = self.bind
- else:
- dialect = default.DefaultDialect()
- return self._compiler(dialect, bind=bind, **kw)
-
- def _compiler(self, dialect, **kw):
- """Return a compiler appropriate for this ClauseElement, given a
- Dialect."""
-
- return dialect.statement_compiler(dialect, self, **kw)
-
- def __str__(self):
- if util.py3k:
- return str(self.compile())
- else:
- return unicode(self.compile()).encode('ascii', 'backslashreplace')
-
- def __and__(self, other):
- return and_(self, other)
-
- def __or__(self, other):
- return or_(self, other)
-
- def __invert__(self):
- return self._negate()
-
- def __bool__(self):
- raise TypeError("Boolean value of this clause is not defined")
-
- __nonzero__ = __bool__
-
- def _negate(self):
- if hasattr(self, 'negation_clause'):
- return self.negation_clause
- else:
- return UnaryExpression(
- self.self_group(against=operators.inv),
- operator=operators.inv,
- negate=None)
-
- def __repr__(self):
- friendly = getattr(self, 'description', None)
- if friendly is None:
- return object.__repr__(self)
- else:
- return '<%s.%s at 0x%x; %s>' % (
- self.__module__, self.__class__.__name__, id(self), friendly)
-
-inspection._self_inspects(ClauseElement)
-
-
-class Immutable(object):
- """mark a ClauseElement as 'immutable' when expressions are cloned."""
-
- def unique_params(self, *optionaldict, **kwargs):
- raise NotImplementedError("Immutable objects do not support copying")
-
- def params(self, *optionaldict, **kwargs):
- raise NotImplementedError("Immutable objects do not support copying")
-
- def _clone(self):
- return self
-
-
-class _DefaultColumnComparator(operators.ColumnOperators):
- """Defines comparison and math operations.
-
- See :class:`.ColumnOperators` and :class:`.Operators` for descriptions
- of all operations.
-
- """
-
- @util.memoized_property
- def type(self):
- return self.expr.type
-
- def operate(self, op, *other, **kwargs):
- o = self.operators[op.__name__]
- return o[0](self, self.expr, op, *(other + o[1:]), **kwargs)
-
- def reverse_operate(self, op, other, **kwargs):
- o = self.operators[op.__name__]
- return o[0](self, self.expr, op, other, reverse=True, *o[1:], **kwargs)
-
- def _adapt_expression(self, op, other_comparator):
- """evaluate the return type of <self> <op> <othertype>,
- and apply any adaptations to the given operator.
-
- This method determines the type of a resulting binary expression
- given two source types and an operator. For example, two
- :class:`.Column` objects, both of the type :class:`.Integer`, will
- produce a :class:`.BinaryExpression` that also has the type
- :class:`.Integer` when compared via the addition (``+``) operator.
- However, using the addition operator with an :class:`.Integer`
- and a :class:`.Date` object will produce a :class:`.Date`, assuming
- "days delta" behavior by the database (in reality, most databases
- other than Postgresql don't accept this particular operation).
-
- The method returns a tuple of the form <operator>, <type>.
- The resulting operator and type will be those applied to the
- resulting :class:`.BinaryExpression` as the final operator and the
- right-hand side of the expression.
-
- Note that only a subset of operators make usage of
- :meth:`._adapt_expression`,
- including math operators and user-defined operators, but not
- boolean comparison or special SQL keywords like MATCH or BETWEEN.
-
- """
- return op, other_comparator.type
-
- def _boolean_compare(self, expr, op, obj, negate=None, reverse=False,
- _python_is_types=(util.NoneType, bool),
- **kwargs):
-
- if isinstance(obj, _python_is_types + (Null, True_, False_)):
-
- # allow x ==/!= True/False to be treated as a literal.
- # this comes out to "== / != true/false" or "1/0" if those
- # constants aren't supported and works on all platforms
- if op in (operators.eq, operators.ne) and \
- isinstance(obj, (bool, True_, False_)):
- return BinaryExpression(expr,
- obj,
- op,
- type_=sqltypes.BOOLEANTYPE,
- negate=negate, modifiers=kwargs)
- else:
- # all other None/True/False uses IS, IS NOT
- if op in (operators.eq, operators.is_):
- return BinaryExpression(expr, _const_expr(obj),
- operators.is_,
- negate=operators.isnot)
- elif op in (operators.ne, operators.isnot):
- return BinaryExpression(expr, _const_expr(obj),
- operators.isnot,
- negate=operators.is_)
- else:
- raise exc.ArgumentError(
- "Only '=', '!=', 'is_()', 'isnot()' operators can "
- "be used with None/True/False")
- else:
- obj = self._check_literal(expr, op, obj)
-
- if reverse:
- return BinaryExpression(obj,
- expr,
- op,
- type_=sqltypes.BOOLEANTYPE,
- negate=negate, modifiers=kwargs)
- else:
- return BinaryExpression(expr,
- obj,
- op,
- type_=sqltypes.BOOLEANTYPE,
- negate=negate, modifiers=kwargs)
-
- def _binary_operate(self, expr, op, obj, reverse=False, result_type=None,
- **kw):
- obj = self._check_literal(expr, op, obj)
-
- if reverse:
- left, right = obj, expr
- else:
- left, right = expr, obj
-
- if result_type is None:
- op, result_type = left.comparator._adapt_expression(
- op, right.comparator)
-
- return BinaryExpression(left, right, op, type_=result_type)
-
- def _scalar(self, expr, op, fn, **kw):
- return fn(expr)
-
- def _in_impl(self, expr, op, seq_or_selectable, negate_op, **kw):
- seq_or_selectable = _clause_element_as_expr(seq_or_selectable)
-
- if isinstance(seq_or_selectable, ScalarSelect):
- return self._boolean_compare(expr, op, seq_or_selectable,
- negate=negate_op)
- elif isinstance(seq_or_selectable, SelectBase):
-
- # TODO: if we ever want to support (x, y, z) IN (select x,
- # y, z from table), we would need a multi-column version of
- # as_scalar() to produce a multi- column selectable that
- # does not export itself as a FROM clause
-
- return self._boolean_compare(
- expr, op, seq_or_selectable.as_scalar(),
- negate=negate_op, **kw)
- elif isinstance(seq_or_selectable, (Selectable, TextClause)):
- return self._boolean_compare(expr, op, seq_or_selectable,
- negate=negate_op, **kw)
-
- # Handle non selectable arguments as sequences
- args = []
- for o in seq_or_selectable:
- if not _is_literal(o):
- if not isinstance(o, ColumnOperators):
- raise exc.InvalidRequestError('in() function accept'
- 's either a list of non-selectable values, '
- 'or a selectable: %r' % o)
- elif o is None:
- o = null()
- else:
- o = expr._bind_param(op, o)
- args.append(o)
- if len(args) == 0:
-
- # Special case handling for empty IN's, behave like
- # comparison against zero row selectable. We use != to
- # build the contradiction as it handles NULL values
- # appropriately, i.e. "not (x IN ())" should not return NULL
- # values for x.
-
- util.warn('The IN-predicate on "%s" was invoked with an '
- 'empty sequence. This results in a '
- 'contradiction, which nonetheless can be '
- 'expensive to evaluate. Consider alternative '
- 'strategies for improved performance.' % expr)
- if op is operators.in_op:
- return expr != expr
- else:
- return expr == expr
-
- return self._boolean_compare(expr, op,
- ClauseList(*args).self_group(against=op),
- negate=negate_op)
-
- def _unsupported_impl(self, expr, op, *arg, **kw):
- raise NotImplementedError("Operator '%s' is not supported on "
- "this expression" % op.__name__)
-
- def _neg_impl(self, expr, op, **kw):
- """See :meth:`.ColumnOperators.__neg__`."""
- return UnaryExpression(expr, operator=operators.neg)
-
- def _match_impl(self, expr, op, other, **kw):
- """See :meth:`.ColumnOperators.match`."""
- return self._boolean_compare(expr, operators.match_op,
- self._check_literal(expr, operators.match_op,
- other))
-
- def _distinct_impl(self, expr, op, **kw):
- """See :meth:`.ColumnOperators.distinct`."""
- return UnaryExpression(expr, operator=operators.distinct_op,
- type_=expr.type)
-
- def _between_impl(self, expr, op, cleft, cright, **kw):
- """See :meth:`.ColumnOperators.between`."""
- return BinaryExpression(
- expr,
- ClauseList(
- self._check_literal(expr, operators.and_, cleft),
- self._check_literal(expr, operators.and_, cright),
- operator=operators.and_,
- group=False),
- operators.between_op)
-
- def _collate_impl(self, expr, op, other, **kw):
- return collate(expr, other)
-
- # a mapping of operators with the method they use, along with
- # their negated operator for comparison operators
- operators = {
- "add": (_binary_operate,),
- "mul": (_binary_operate,),
- "sub": (_binary_operate,),
- "div": (_binary_operate,),
- "mod": (_binary_operate,),
- "truediv": (_binary_operate,),
- "custom_op": (_binary_operate,),
- "concat_op": (_binary_operate,),
- "lt": (_boolean_compare, operators.ge),
- "le": (_boolean_compare, operators.gt),
- "ne": (_boolean_compare, operators.eq),
- "gt": (_boolean_compare, operators.le),
- "ge": (_boolean_compare, operators.lt),
- "eq": (_boolean_compare, operators.ne),
- "like_op": (_boolean_compare, operators.notlike_op),
- "ilike_op": (_boolean_compare, operators.notilike_op),
- "notlike_op": (_boolean_compare, operators.like_op),
- "notilike_op": (_boolean_compare, operators.ilike_op),
- "contains_op": (_boolean_compare, operators.notcontains_op),
- "startswith_op": (_boolean_compare, operators.notstartswith_op),
- "endswith_op": (_boolean_compare, operators.notendswith_op),
- "desc_op": (_scalar, desc),
- "asc_op": (_scalar, asc),
- "nullsfirst_op": (_scalar, nullsfirst),
- "nullslast_op": (_scalar, nullslast),
- "in_op": (_in_impl, operators.notin_op),
- "notin_op": (_in_impl, operators.in_op),
- "is_": (_boolean_compare, operators.is_),
- "isnot": (_boolean_compare, operators.isnot),
- "collate": (_collate_impl,),
- "match_op": (_match_impl,),
- "distinct_op": (_distinct_impl,),
- "between_op": (_between_impl, ),
- "neg": (_neg_impl,),
- "getitem": (_unsupported_impl,),
- "lshift": (_unsupported_impl,),
- "rshift": (_unsupported_impl,),
- }
-
- def _check_literal(self, expr, operator, other):
- if isinstance(other, (ColumnElement, TextClause)):
- if isinstance(other, BindParameter) and \
- isinstance(other.type, sqltypes.NullType):
- # TODO: perhaps we should not mutate the incoming
- # bindparam() here and instead make a copy of it.
- # this might be the only place that we're mutating
- # an incoming construct.
- other.type = expr.type
- return other
- elif hasattr(other, '__clause_element__'):
- other = other.__clause_element__()
- elif isinstance(other, sqltypes.TypeEngine.Comparator):
- other = other.expr
-
- if isinstance(other, (SelectBase, Alias)):
- return other.as_scalar()
- elif not isinstance(other, (ColumnElement, TextClause)):
- return expr._bind_param(operator, other)
- else:
- return other
-
-
-class ColumnElement(ClauseElement, ColumnOperators):
- """Represent a column-oriented SQL expression suitable for usage in the
- "columns" clause, WHERE clause etc. of a statement.
-
- While the most familiar kind of :class:`.ColumnElement` is the
- :class:`.Column` object, :class:`.ColumnElement` serves as the basis
- for any unit that may be present in a SQL expression, including
- the expressions themselves, SQL functions, bound parameters,
- literal expressions, keywords such as ``NULL``, etc.
- :class:`.ColumnElement` is the ultimate base class for all such elements.
-
- A :class:`.ColumnElement` provides the ability to generate new
- :class:`.ColumnElement`
- objects using Python expressions. This means that Python operators
- such as ``==``, ``!=`` and ``<`` are overloaded to mimic SQL operations,
- and allow the instantiation of further :class:`.ColumnElement` instances
- which are composed from other, more fundamental :class:`.ColumnElement`
- objects. For example, two :class:`.ColumnClause` objects can be added
- together with the addition operator ``+`` to produce
- a :class:`.BinaryExpression`.
- Both :class:`.ColumnClause` and :class:`.BinaryExpression` are subclasses
- of :class:`.ColumnElement`::
-
- >>> from sqlalchemy.sql import column
- >>> column('a') + column('b')
- <sqlalchemy.sql.expression.BinaryExpression object at 0x101029dd0>
- >>> print column('a') + column('b')
- a + b
-
- :class:`.ColumnElement` supports the ability to be a *proxy* element,
- which indicates that the :class:`.ColumnElement` may be associated with
- a :class:`.Selectable` which was derived from another :class:`.Selectable`.
- An example of a "derived" :class:`.Selectable` is an :class:`.Alias` of a
- :class:`~sqlalchemy.schema.Table`. For the ambitious, an in-depth
- discussion of this concept can be found at
- `Expression Transformations <http://techspot.zzzeek.org/2008/01/23/expression-transformations/>`_.
-
- """
-
- __visit_name__ = 'column'
- primary_key = False
- foreign_keys = []
- quote = None
- _label = None
- _key_label = None
- _alt_names = ()
-
- @util.memoized_property
- def type(self):
- return sqltypes.NULLTYPE
-
- @util.memoized_property
- def comparator(self):
- return self.type.comparator_factory(self)
-
- def __getattr__(self, key):
- try:
- return getattr(self.comparator, key)
- except AttributeError:
- raise AttributeError(
- 'Neither %r object nor %r object has an attribute %r' % (
- type(self).__name__,
- type(self.comparator).__name__,
- key)
- )
-
- def operate(self, op, *other, **kwargs):
- return op(self.comparator, *other, **kwargs)
-
- def reverse_operate(self, op, other, **kwargs):
- return op(other, self.comparator, **kwargs)
-
- def _bind_param(self, operator, obj):
- return BindParameter(None, obj,
- _compared_to_operator=operator,
- _compared_to_type=self.type, unique=True)
-
- @property
- def expression(self):
- """Return a column expression.
-
- Part of the inspection interface; returns self.
-
- """
- return self
-
- @property
- def _select_iterable(self):
- return (self, )
-
- @util.memoized_property
- def base_columns(self):
- return util.column_set(c for c in self.proxy_set
- if not hasattr(c, '_proxies'))
-
- @util.memoized_property
- def proxy_set(self):
- s = util.column_set([self])
- if hasattr(self, '_proxies'):
- for c in self._proxies:
- s.update(c.proxy_set)
- return s
-
- def shares_lineage(self, othercolumn):
- """Return True if the given :class:`.ColumnElement`
- has a common ancestor to this :class:`.ColumnElement`."""
-
- return bool(self.proxy_set.intersection(othercolumn.proxy_set))
-
- def _compare_name_for_result(self, other):
- """Return True if the given column element compares to this one
- when targeting within a result row."""
-
- return hasattr(other, 'name') and hasattr(self, 'name') and \
- other.name == self.name
-
- def _make_proxy(self, selectable, name=None, name_is_truncatable=False, **kw):
- """Create a new :class:`.ColumnElement` representing this
- :class:`.ColumnElement` as it appears in the select list of a
- descending selectable.
-
- """
- if name is None:
- name = self.anon_label
- try:
- key = str(self)
- except exc.UnsupportedCompilationError:
- key = self.anon_label
- else:
- key = name
- co = ColumnClause(_as_truncated(name) if name_is_truncatable else name,
- selectable,
- type_=getattr(self,
- 'type', None))
- co._proxies = [self]
- if selectable._is_clone_of is not None:
- co._is_clone_of = \
- selectable._is_clone_of.columns.get(key)
- selectable._columns[key] = co
- return co
-
- def compare(self, other, use_proxies=False, equivalents=None, **kw):
- """Compare this ColumnElement to another.
-
- Special arguments understood:
-
- :param use_proxies: when True, consider two columns that
- share a common base column as equivalent (i.e. shares_lineage())
-
- :param equivalents: a dictionary of columns as keys mapped to sets
- of columns. If the given "other" column is present in this
- dictionary, if any of the columns in the corresponding set() pass the
- comparison test, the result is True. This is used to expand the
- comparison to other columns that may be known to be equivalent to
- this one via foreign key or other criterion.
-
- """
- to_compare = (other, )
- if equivalents and other in equivalents:
- to_compare = equivalents[other].union(to_compare)
-
- for oth in to_compare:
- if use_proxies and self.shares_lineage(oth):
- return True
- elif hash(oth) == hash(self):
- return True
- else:
- return False
-
- def label(self, name):
- """Produce a column label, i.e. ``<columnname> AS <name>``.
-
- This is a shortcut to the :func:`~.expression.label` function.
-
- if 'name' is None, an anonymous label name will be generated.
-
- """
- return Label(name, self, self.type)
-
- @util.memoized_property
- def anon_label(self):
- """provides a constant 'anonymous label' for this ColumnElement.
-
- This is a label() expression which will be named at compile time.
- The same label() is returned each time anon_label is called so
- that expressions can reference anon_label multiple times, producing
- the same label name at compile time.
-
- the compiler uses this function automatically at compile time
- for expressions that are known to be 'unnamed' like binary
- expressions and function calls.
-
- """
- return _anonymous_label('%%(%d %s)s' % (id(self), getattr(self,
- 'name', 'anon')))
-
-
-class ColumnCollection(util.OrderedProperties):
- """An ordered dictionary that stores a list of ColumnElement
- instances.
-
- Overrides the ``__eq__()`` method to produce SQL clauses between
- sets of correlated columns.
-
- """
-
- def __init__(self, *cols):
- super(ColumnCollection, self).__init__()
- self._data.update((c.key, c) for c in cols)
- self.__dict__['_all_cols'] = util.column_set(self)
-
- def __str__(self):
- return repr([str(c) for c in self])
-
- def replace(self, column):
- """add the given column to this collection, removing unaliased
- versions of this column as well as existing columns with the
- same key.
-
- e.g.::
-
- t = Table('sometable', metadata, Column('col1', Integer))
- t.columns.replace(Column('col1', Integer, key='columnone'))
-
- will remove the original 'col1' from the collection, and add
- the new column under the name 'columnname'.
-
- Used by schema.Column to override columns during table reflection.
-
- """
- if column.name in self and column.key != column.name:
- other = self[column.name]
- if other.name == other.key:
- del self._data[other.name]
- self._all_cols.remove(other)
- if column.key in self._data:
- self._all_cols.remove(self._data[column.key])
- self._all_cols.add(column)
- self._data[column.key] = column
-
- def add(self, column):
- """Add a column to this collection.
-
- The key attribute of the column will be used as the hash key
- for this dictionary.
-
- """
- self[column.key] = column
-
- def __delitem__(self, key):
- raise NotImplementedError()
-
- def __setattr__(self, key, object):
- raise NotImplementedError()
-
- def __setitem__(self, key, value):
- if key in self:
-
- # this warning is primarily to catch select() statements
- # which have conflicting column names in their exported
- # columns collection
-
- existing = self[key]
- if not existing.shares_lineage(value):
- util.warn('Column %r on table %r being replaced by '
- '%r, which has the same key. Consider '
- 'use_labels for select() statements.' % (key,
- getattr(existing, 'table', None), value))
- self._all_cols.remove(existing)
- # pop out memoized proxy_set as this
- # operation may very well be occurring
- # in a _make_proxy operation
- ColumnElement.proxy_set._reset(value)
- self._all_cols.add(value)
- self._data[key] = value
-
- def clear(self):
- self._data.clear()
- self._all_cols.clear()
-
- def remove(self, column):
- del self._data[column.key]
- self._all_cols.remove(column)
-
- def update(self, value):
- self._data.update(value)
- self._all_cols.clear()
- self._all_cols.update(self._data.values())
-
- def extend(self, iter):
- self.update((c.key, c) for c in iter)
-
- __hash__ = None
-
- def __eq__(self, other):
- l = []
- for c in other:
- for local in self:
- if c.shares_lineage(local):
- l.append(c == local)
- return and_(*l)
-
- def __contains__(self, other):
- if not isinstance(other, util.string_types):
- raise exc.ArgumentError("__contains__ requires a string argument")
- return util.OrderedProperties.__contains__(self, other)
-
- def __setstate__(self, state):
- self.__dict__['_data'] = state['_data']
- self.__dict__['_all_cols'] = util.column_set(self._data.values())
-
- def contains_column(self, col):
- # this has to be done via set() membership
- return col in self._all_cols
-
- def as_immutable(self):
- return ImmutableColumnCollection(self._data, self._all_cols)
-
-
-class ImmutableColumnCollection(util.ImmutableProperties, ColumnCollection):
- def __init__(self, data, colset):
- util.ImmutableProperties.__init__(self, data)
- self.__dict__['_all_cols'] = colset
-
- extend = remove = util.ImmutableProperties._immutable
-
-
-class ColumnSet(util.ordered_column_set):
- def contains_column(self, col):
- return col in self
-
- def extend(self, cols):
- for col in cols:
- self.add(col)
-
- def __add__(self, other):
- return list(self) + list(other)
-
- def __eq__(self, other):
- l = []
- for c in other:
- for local in self:
- if c.shares_lineage(local):
- l.append(c == local)
- return and_(*l)
-
- def __hash__(self):
- return hash(tuple(x for x in self))
-
-
-class Selectable(ClauseElement):
- """mark a class as being selectable"""
- __visit_name__ = 'selectable'
-
- is_selectable = True
-
- @property
- def selectable(self):
- return self
-
-
-class FromClause(Selectable):
- """Represent an element that can be used within the ``FROM``
- clause of a ``SELECT`` statement.
-
- The most common forms of :class:`.FromClause` are the
- :class:`.Table` and the :func:`.select` constructs. Key
- features common to all :class:`.FromClause` objects include:
-
- * a :attr:`.c` collection, which provides per-name access to a collection
- of :class:`.ColumnElement` objects.
- * a :attr:`.primary_key` attribute, which is a collection of all those
- :class:`.ColumnElement` objects that indicate the ``primary_key`` flag.
- * Methods to generate various derivations of a "from" clause, including
- :meth:`.FromClause.alias`, :meth:`.FromClause.join`,
- :meth:`.FromClause.select`.
-
-
- """
- __visit_name__ = 'fromclause'
- named_with_column = False
- _hide_froms = []
- quote = None
- schema = None
- _memoized_property = util.group_expirable_memoized_property(["_columns"])
-
- def count(self, whereclause=None, **params):
- """return a SELECT COUNT generated against this
- :class:`.FromClause`."""
-
- if self.primary_key:
- col = list(self.primary_key)[0]
- else:
- col = list(self.columns)[0]
- return select(
- [func.count(col).label('tbl_row_count')],
- whereclause,
- from_obj=[self],
- **params)
-
- def select(self, whereclause=None, **params):
- """return a SELECT of this :class:`.FromClause`.
-
- .. seealso::
-
- :func:`~.sql.expression.select` - general purpose
- method which allows for arbitrary column lists.
-
- """
-
- return select([self], whereclause, **params)
-
- def join(self, right, onclause=None, isouter=False):
- """return a join of this :class:`.FromClause` against another
- :class:`.FromClause`."""
-
- return Join(self, right, onclause, isouter)
-
- def outerjoin(self, right, onclause=None):
- """return an outer join of this :class:`.FromClause` against another
- :class:`.FromClause`."""
-
- return Join(self, right, onclause, True)
-
- def alias(self, name=None, flat=False):
- """return an alias of this :class:`.FromClause`.
-
- This is shorthand for calling::
-
- from sqlalchemy import alias
- a = alias(self, name=name)
-
- See :func:`~.expression.alias` for details.
-
- """
-
- return Alias(self, name)
-
- def is_derived_from(self, fromclause):
- """Return True if this FromClause is 'derived' from the given
- FromClause.
-
- An example would be an Alias of a Table is derived from that Table.
-
- """
- # this is essentially an "identity" check in the base class.
- # Other constructs override this to traverse through
- # contained elements.
- return fromclause in self._cloned_set
-
- def _is_lexical_equivalent(self, other):
- """Return True if this FromClause and the other represent
- the same lexical identity.
-
- This tests if either one is a copy of the other, or
- if they are the same via annotation identity.
-
- """
- return self._cloned_set.intersection(other._cloned_set)
-
- def replace_selectable(self, old, alias):
- """replace all occurrences of FromClause 'old' with the given Alias
- object, returning a copy of this :class:`.FromClause`.
-
- """
-
- return sqlutil.ClauseAdapter(alias).traverse(self)
-
- def correspond_on_equivalents(self, column, equivalents):
- """Return corresponding_column for the given column, or if None
- search for a match in the given dictionary.
-
- """
- col = self.corresponding_column(column, require_embedded=True)
- if col is None and col in equivalents:
- for equiv in equivalents[col]:
- nc = self.corresponding_column(equiv, require_embedded=True)
- if nc:
- return nc
- return col
-
- def corresponding_column(self, column, require_embedded=False):
- """Given a :class:`.ColumnElement`, return the exported
- :class:`.ColumnElement` object from this :class:`.Selectable`
- which corresponds to that original
- :class:`~sqlalchemy.schema.Column` via a common ancestor
- column.
-
- :param column: the target :class:`.ColumnElement` to be matched
-
- :param require_embedded: only return corresponding columns for
- the given :class:`.ColumnElement`, if the given
- :class:`.ColumnElement` is actually present within a sub-element
- of this :class:`.FromClause`. Normally the column will match if
- it merely shares a common ancestor with one of the exported
- columns of this :class:`.FromClause`.
-
- """
-
- def embedded(expanded_proxy_set, target_set):
- for t in target_set.difference(expanded_proxy_set):
- if not set(_expand_cloned([t])
- ).intersection(expanded_proxy_set):
- return False
- return True
-
- # don't dig around if the column is locally present
- if self.c.contains_column(column):
- return column
- col, intersect = None, None
- target_set = column.proxy_set
- cols = self.c
- for c in cols:
- expanded_proxy_set = set(_expand_cloned(c.proxy_set))
- i = target_set.intersection(expanded_proxy_set)
- if i and (not require_embedded
- or embedded(expanded_proxy_set, target_set)):
- if col is None:
-
- # no corresponding column yet, pick this one.
-
- col, intersect = c, i
- elif len(i) > len(intersect):
-
- # 'c' has a larger field of correspondence than
- # 'col'. i.e. selectable.c.a1_x->a1.c.x->table.c.x
- # matches a1.c.x->table.c.x better than
- # selectable.c.x->table.c.x does.
-
- col, intersect = c, i
- elif i == intersect:
-
- # they have the same field of correspondence. see
- # which proxy_set has fewer columns in it, which
- # indicates a closer relationship with the root
- # column. Also take into account the "weight"
- # attribute which CompoundSelect() uses to give
- # higher precedence to columns based on vertical
- # position in the compound statement, and discard
- # columns that have no reference to the target
- # column (also occurs with CompoundSelect)
-
- col_distance = util.reduce(operator.add,
- [sc._annotations.get('weight', 1) for sc in
- col.proxy_set if sc.shares_lineage(column)])
- c_distance = util.reduce(operator.add,
- [sc._annotations.get('weight', 1) for sc in
- c.proxy_set if sc.shares_lineage(column)])
- if c_distance < col_distance:
- col, intersect = c, i
- return col
-
- @property
- def description(self):
- """a brief description of this FromClause.
-
- Used primarily for error message formatting.
-
- """
- return getattr(self, 'name', self.__class__.__name__ + " object")
-
- def _reset_exported(self):
- """delete memoized collections when a FromClause is cloned."""
-
- self._memoized_property.expire_instance(self)
-
- @_memoized_property
- def columns(self):
- """A named-based collection of :class:`.ColumnElement` objects
- maintained by this :class:`.FromClause`.
-
- The :attr:`.columns`, or :attr:`.c` collection, is the gateway
- to the construction of SQL expressions using table-bound or
- other selectable-bound columns::
-
- select([mytable]).where(mytable.c.somecolumn == 5)
-
- """
-
- if '_columns' not in self.__dict__:
- self._init_collections()
- self._populate_column_collection()
- return self._columns.as_immutable()
-
- @_memoized_property
- def primary_key(self):
- """Return the collection of Column objects which comprise the
- primary key of this FromClause."""
-
- self._init_collections()
- self._populate_column_collection()
- return self.primary_key
-
- @_memoized_property
- def foreign_keys(self):
- """Return the collection of ForeignKey objects which this
- FromClause references."""
-
- self._init_collections()
- self._populate_column_collection()
- return self.foreign_keys
-
- c = property(attrgetter('columns'),
- doc="An alias for the :attr:`.columns` attribute.")
- _select_iterable = property(attrgetter('columns'))
-
- def _init_collections(self):
- assert '_columns' not in self.__dict__
- assert 'primary_key' not in self.__dict__
- assert 'foreign_keys' not in self.__dict__
-
- self._columns = ColumnCollection()
- self.primary_key = ColumnSet()
- self.foreign_keys = set()
-
- @property
- def _cols_populated(self):
- return '_columns' in self.__dict__
-
- def _populate_column_collection(self):
- """Called on subclasses to establish the .c collection.
-
- Each implementation has a different way of establishing
- this collection.
-
- """
-
- def _refresh_for_new_column(self, column):
- """Given a column added to the .c collection of an underlying
- selectable, produce the local version of that column, assuming this
- selectable ultimately should proxy this column.
-
- this is used to "ping" a derived selectable to add a new column
- to its .c. collection when a Column has been added to one of the
- Table objects it ultimtely derives from.
-
- If the given selectable hasn't populated it's .c. collection yet,
- it should at least pass on the message to the contained selectables,
- but it will return None.
-
- This method is currently used by Declarative to allow Table
- columns to be added to a partially constructed inheritance
- mapping that may have already produced joins. The method
- isn't public right now, as the full span of implications
- and/or caveats aren't yet clear.
-
- It's also possible that this functionality could be invoked by
- default via an event, which would require that
- selectables maintain a weak referencing collection of all
- derivations.
-
- """
- if not self._cols_populated:
- return None
- elif column.key in self.columns and self.columns[column.key] is column:
- return column
- else:
- return None
-
-
-class BindParameter(ColumnElement):
- """Represent a bind parameter.
-
- Public constructor is the :func:`bindparam()` function.
-
- """
-
- __visit_name__ = 'bindparam'
- quote = None
-
- _is_crud = False
-
- def __init__(self, key, value, type_=None, unique=False,
- callable_=None,
- isoutparam=False, required=False,
- quote=None,
- _compared_to_operator=None,
- _compared_to_type=None):
- """Construct a BindParameter.
-
- :param key:
- the key for this bind param. Will be used in the generated
- SQL statement for dialects that use named parameters. This
- value may be modified when part of a compilation operation,
- if other :class:`BindParameter` objects exist with the same
- key, or if its length is too long and truncation is
- required.
-
- :param value:
- Initial value for this bind param. This value may be
- overridden by the dictionary of parameters sent to statement
- compilation/execution.
-
- :param callable\_:
- A callable function that takes the place of "value". The function
- will be called at statement execution time to determine the
- ultimate value. Used for scenarios where the actual bind
- value cannot be determined at the point at which the clause
- construct is created, but embedded bind values are still desirable.
-
- :param type\_:
- A ``TypeEngine`` object that will be used to pre-process the
- value corresponding to this :class:`BindParameter` at
- execution time.
-
- :param unique:
- if True, the key name of this BindParamClause will be
- modified if another :class:`BindParameter` of the same name
- already has been located within the containing
- :class:`.ClauseElement`.
-
- :param quote:
- True if this parameter name requires quoting and is not
- currently known as a SQLAlchemy reserved word; this currently
- only applies to the Oracle backend.
-
- :param required:
- a value is required at execution time.
-
- :param isoutparam:
- if True, the parameter should be treated like a stored procedure
- "OUT" parameter.
-
- """
- if unique:
- self.key = _anonymous_label('%%(%d %s)s' % (id(self), key
- or 'param'))
- else:
- self.key = key or _anonymous_label('%%(%d param)s'
- % id(self))
-
- # identifying key that won't change across
- # clones, used to identify the bind's logical
- # identity
- self._identifying_key = self.key
-
- # key that was passed in the first place, used to
- # generate new keys
- self._orig_key = key or 'param'
-
- self.unique = unique
- self.value = value
- self.callable = callable_
- self.isoutparam = isoutparam
- self.required = required
- self.quote = quote
- if type_ is None:
- if _compared_to_type is not None:
- self.type = \
- _compared_to_type.coerce_compared_value(
- _compared_to_operator, value)
- else:
- self.type = sqltypes._type_map.get(type(value),
- sqltypes.NULLTYPE)
- elif isinstance(type_, type):
- self.type = type_()
- else:
- self.type = type_
-
- @property
- def effective_value(self):
- """Return the value of this bound parameter,
- taking into account if the ``callable`` parameter
- was set.
-
- The ``callable`` value will be evaluated
- and returned if present, else ``value``.
-
- """
- if self.callable:
- return self.callable()
- else:
- return self.value
-
- def _clone(self):
- c = ClauseElement._clone(self)
- if self.unique:
- c.key = _anonymous_label('%%(%d %s)s' % (id(c), c._orig_key
- or 'param'))
- return c
-
- def _convert_to_unique(self):
- if not self.unique:
- self.unique = True
- self.key = _anonymous_label('%%(%d %s)s' % (id(self),
- self._orig_key or 'param'))
-
- def compare(self, other, **kw):
- """Compare this :class:`BindParameter` to the given
- clause."""
-
- return isinstance(other, BindParameter) \
- and self.type._compare_type_affinity(other.type) \
- and self.value == other.value
-
- def __getstate__(self):
- """execute a deferred value for serialization purposes."""
-
- d = self.__dict__.copy()
- v = self.value
- if self.callable:
- v = self.callable()
- d['callable'] = None
- d['value'] = v
- return d
-
- def __repr__(self):
- return 'BindParameter(%r, %r, type_=%r)' % (self.key,
- self.value, self.type)
-
-
-class TypeClause(ClauseElement):
- """Handle a type keyword in a SQL statement.
-
- Used by the ``Case`` statement.
-
- """
-
- __visit_name__ = 'typeclause'
-
- def __init__(self, type):
- self.type = type
-
-
-class Generative(object):
- """Allow a ClauseElement to generate itself via the
- @_generative decorator.
-
- """
-
- def _generate(self):
- s = self.__class__.__new__(self.__class__)
- s.__dict__ = self.__dict__.copy()
- return s
-
-
-class Executable(Generative):
- """Mark a ClauseElement as supporting execution.
-
- :class:`.Executable` is a superclass for all "statement" types
- of objects, including :func:`select`, :func:`delete`, :func:`update`,
- :func:`insert`, :func:`text`.
-
- """
-
- supports_execution = True
- _execution_options = util.immutabledict()
- _bind = None
-
- @_generative
- def execution_options(self, **kw):
- """ Set non-SQL options for the statement which take effect during
- execution.
-
- Execution options can be set on a per-statement or
- per :class:`.Connection` basis. Additionally, the
- :class:`.Engine` and ORM :class:`~.orm.query.Query` objects provide
- access to execution options which they in turn configure upon
- connections.
-
- The :meth:`execution_options` method is generative. A new
- instance of this statement is returned that contains the options::
-
- statement = select([table.c.x, table.c.y])
- statement = statement.execution_options(autocommit=True)
-
- Note that only a subset of possible execution options can be applied
- to a statement - these include "autocommit" and "stream_results",
- but not "isolation_level" or "compiled_cache".
- See :meth:`.Connection.execution_options` for a full list of
- possible options.
-
- .. seealso::
-
- :meth:`.Connection.execution_options()`
-
- :meth:`.Query.execution_options()`
-
- """
- if 'isolation_level' in kw:
- raise exc.ArgumentError(
- "'isolation_level' execution option may only be specified "
- "on Connection.execution_options(), or "
- "per-engine using the isolation_level "
- "argument to create_engine()."
- )
- if 'compiled_cache' in kw:
- raise exc.ArgumentError(
- "'compiled_cache' execution option may only be specified "
- "on Connection.execution_options(), not per statement."
- )
- self._execution_options = self._execution_options.union(kw)
-
- def execute(self, *multiparams, **params):
- """Compile and execute this :class:`.Executable`."""
- e = self.bind
- if e is None:
- label = getattr(self, 'description', self.__class__.__name__)
- msg = ('This %s is not directly bound to a Connection or Engine.'
- 'Use the .execute() method of a Connection or Engine '
- 'to execute this construct.' % label)
- raise exc.UnboundExecutionError(msg)
- return e._execute_clauseelement(self, multiparams, params)
-
- def scalar(self, *multiparams, **params):
- """Compile and execute this :class:`.Executable`, returning the
- result's scalar representation.
-
- """
- return self.execute(*multiparams, **params).scalar()
-
- @property
- def bind(self):
- """Returns the :class:`.Engine` or :class:`.Connection` to
- which this :class:`.Executable` is bound, or None if none found.
-
- This is a traversal which checks locally, then
- checks among the "from" clauses of associated objects
- until a bound engine or connection is found.
-
- """
- if self._bind is not None:
- return self._bind
-
- for f in _from_objects(self):
- if f is self:
- continue
- engine = f.bind
- if engine is not None:
- return engine
- else:
- return None
-
-
-# legacy, some outside users may be calling this
-_Executable = Executable
-
-
-class TextClause(Executable, ClauseElement):
- """Represent a literal SQL text fragment.
-
- Public constructor is the :func:`text()` function.
-
- """
+ 'tuple_', 'type_coerce', 'union', 'union_all', 'update']
+
+
+from .visitors import Visitable
+from .functions import func, modifier, FunctionElement
+from ..util.langhelpers import public_factory
+from .elements import ClauseElement, ColumnElement,\
+ BindParameter, UnaryExpression, BooleanClauseList, \
+ Label, Cast, Case, ColumnClause, TextClause, Over, Null, \
+ True_, False_, BinaryExpression, Tuple, TypeClause, Extract, \
+ Grouping, and_, or_, not_, \
+ collate, literal_column, between,\
+ literal, outparam, type_coerce, ClauseList
+
+from .base import ColumnCollection, Generative, Executable, \
+ PARSE_AUTOCOMMIT
+
+from .selectable import Alias, Join, Select, Selectable, TableClause, \
+ CompoundSelect, FromClause, FromGrouping, SelectBase, \
+ alias, \
+ subquery, HasPrefixes, Exists, ScalarSelect
+
+
+from .dml import Insert, Update, Delete
+
+# factory functions - these pull class-bound constructors and classmethods
+# from SQL elements and selectables into public functions. This allows
+# the functions to be available in the sqlalchemy.sql.* namespace and
+# to be auto-cross-documenting from the function to the class itself.
+
+bindparam = public_factory(BindParameter)
+select = public_factory(Select)
+text = public_factory(TextClause)
+table = public_factory(TableClause)
+column = public_factory(ColumnClause)
+over = public_factory(Over)
+label = public_factory(Label)
+case = public_factory(Case)
+cast = public_factory(Cast)
+extract = public_factory(Extract)
+tuple_ = public_factory(Tuple)
+except_ = public_factory(CompoundSelect._create_except)
+except_all = public_factory(CompoundSelect._create_except_all)
+intersect = public_factory(CompoundSelect._create_intersect)
+intersect_all = public_factory(CompoundSelect._create_intersect_all)
+union = public_factory(CompoundSelect._create_union)
+union_all = public_factory(CompoundSelect._create_union_all)
+exists = public_factory(Exists)
+nullsfirst = public_factory(UnaryExpression._create_nullsfirst)
+nullslast = public_factory(UnaryExpression._create_nullslast)
+asc = public_factory(UnaryExpression._create_asc)
+desc = public_factory(UnaryExpression._create_desc)
+distinct = public_factory(UnaryExpression._create_distinct)
+true = public_factory(True_)
+false = public_factory(False_)
+null = public_factory(Null)
+join = public_factory(Join._create_join)
+outerjoin = public_factory(Join._create_outerjoin)
+insert = public_factory(Insert)
+update = public_factory(Update)
+delete = public_factory(Delete)
+
+
+
+# internal functions still being called from tests and the ORM,
+# these might be better off in some other namespace
+from .base import _from_objects
+from .elements import _literal_as_text, _clause_element_as_expr,\
+ _is_column, _labeled, _only_column_elements, _string_or_unprintable, \
+ _truncated_label, _clone, _cloned_difference, _cloned_intersection,\
+ _column_as_key, _literal_as_binds, _select_iterables, \
+ _corresponding_column_or_error
+from .selectable import _interpret_as_from
- __visit_name__ = 'textclause'
- _bind_params_regex = re.compile(r'(?<![:\w\x5c]):(\w+)(?!:)', re.UNICODE)
- _execution_options = \
- Executable._execution_options.union(
- {'autocommit': PARSE_AUTOCOMMIT})
-
- @property
- def _select_iterable(self):
- return (self,)
-
- @property
- def selectable(self):
- return self
-
- _hide_froms = []
-
- def __init__(
- self,
- text='',
- bind=None,
- bindparams=None,
- typemap=None,
- autocommit=None):
-
- self._bind = bind
- self.bindparams = {}
- self.typemap = typemap
- if autocommit is not None:
- util.warn_deprecated('autocommit on text() is deprecated. '
- 'Use .execution_options(autocommit=Tru'
- 'e)')
- self._execution_options = \
- self._execution_options.union(
- {'autocommit': autocommit})
- if typemap is not None:
- for key in typemap:
- typemap[key] = sqltypes.to_instance(typemap[key])
-
- def repl(m):
- self.bindparams[m.group(1)] = bindparam(m.group(1))
- return ':%s' % m.group(1)
-
- # scan the string and search for bind parameter names, add them
- # to the list of bindparams
-
- self.text = self._bind_params_regex.sub(repl, text)
- if bindparams is not None:
- for b in bindparams:
- self.bindparams[b.key] = b
-
- @property
- def type(self):
- if self.typemap is not None and len(self.typemap) == 1:
- return list(self.typemap)[0]
- else:
- return sqltypes.NULLTYPE
-
- @property
- def comparator(self):
- return self.type.comparator_factory(self)
-
- def self_group(self, against=None):
- if against is operators.in_op:
- return Grouping(self)
- else:
- return self
-
- def _copy_internals(self, clone=_clone, **kw):
- self.bindparams = dict((b.key, clone(b, **kw))
- for b in self.bindparams.values())
-
- def get_children(self, **kwargs):
- return list(self.bindparams.values())
-
-
-class Null(ColumnElement):
- """Represent the NULL keyword in a SQL statement.
-
- Public constructor is the :func:`null()` function.
-
- """
-
- __visit_name__ = 'null'
-
- def __init__(self):
- self.type = sqltypes.NULLTYPE
-
- def compare(self, other):
- return isinstance(other, Null)
-
-
-class False_(ColumnElement):
- """Represent the ``false`` keyword in a SQL statement.
-
- Public constructor is the :func:`false()` function.
-
- """
-
- __visit_name__ = 'false'
-
- def __init__(self):
- self.type = sqltypes.BOOLEANTYPE
-
- def compare(self, other):
- return isinstance(other, False_)
-
-class True_(ColumnElement):
- """Represent the ``true`` keyword in a SQL statement.
-
- Public constructor is the :func:`true()` function.
-
- """
-
- __visit_name__ = 'true'
-
- def __init__(self):
- self.type = sqltypes.BOOLEANTYPE
-
- def compare(self, other):
- return isinstance(other, True_)
-
-
-class ClauseList(ClauseElement):
- """Describe a list of clauses, separated by an operator.
-
- By default, is comma-separated, such as a column listing.
-
- """
- __visit_name__ = 'clauselist'
-
- def __init__(self, *clauses, **kwargs):
- self.operator = kwargs.pop('operator', operators.comma_op)
- self.group = kwargs.pop('group', True)
- self.group_contents = kwargs.pop('group_contents', True)
- if self.group_contents:
- self.clauses = [
- _literal_as_text(clause).self_group(against=self.operator)
- for clause in clauses if clause is not None]
- else:
- self.clauses = [
- _literal_as_text(clause)
- for clause in clauses if clause is not None]
-
- def __iter__(self):
- return iter(self.clauses)
-
- def __len__(self):
- return len(self.clauses)
-
- @property
- def _select_iterable(self):
- return iter(self)
-
- def append(self, clause):
- # TODO: not sure if i like the 'group_contents' flag. need to
- # define the difference between a ClauseList of ClauseLists,
- # and a "flattened" ClauseList of ClauseLists. flatten()
- # method ?
- if self.group_contents:
- self.clauses.append(_literal_as_text(clause).\
- self_group(against=self.operator))
- else:
- self.clauses.append(_literal_as_text(clause))
-
- def _copy_internals(self, clone=_clone, **kw):
- self.clauses = [clone(clause, **kw) for clause in self.clauses]
-
- def get_children(self, **kwargs):
- return self.clauses
-
- @property
- def _from_objects(self):
- return list(itertools.chain(*[c._from_objects for c in self.clauses]))
-
- def self_group(self, against=None):
- if self.group and operators.is_precedent(self.operator, against):
- return Grouping(self)
- else:
- return self
-
- def compare(self, other, **kw):
- """Compare this :class:`.ClauseList` to the given :class:`.ClauseList`,
- including a comparison of all the clause items.
-
- """
- if not isinstance(other, ClauseList) and len(self.clauses) == 1:
- return self.clauses[0].compare(other, **kw)
- elif isinstance(other, ClauseList) and \
- len(self.clauses) == len(other.clauses):
- for i in range(0, len(self.clauses)):
- if not self.clauses[i].compare(other.clauses[i], **kw):
- return False
- else:
- return self.operator == other.operator
- else:
- return False
-
-
-class BooleanClauseList(ClauseList, ColumnElement):
- __visit_name__ = 'clauselist'
-
- def __init__(self, *clauses, **kwargs):
- super(BooleanClauseList, self).__init__(*clauses, **kwargs)
- self.type = sqltypes.to_instance(kwargs.get('type_',
- sqltypes.Boolean))
-
- @property
- def _select_iterable(self):
- return (self, )
-
- def self_group(self, against=None):
- if not self.clauses:
- return self
- else:
- return super(BooleanClauseList, self).self_group(against=against)
-
-
-class Tuple(ClauseList, ColumnElement):
-
- def __init__(self, *clauses, **kw):
- clauses = [_literal_as_binds(c) for c in clauses]
- self.type = kw.pop('type_', None)
- if self.type is None:
- self.type = _type_from_args(clauses)
- super(Tuple, self).__init__(*clauses, **kw)
-
- @property
- def _select_iterable(self):
- return (self, )
-
- def _bind_param(self, operator, obj):
- return Tuple(*[
- BindParameter(None, o, _compared_to_operator=operator,
- _compared_to_type=self.type, unique=True)
- for o in obj
- ]).self_group()
-
-
-class Case(ColumnElement):
- __visit_name__ = 'case'
-
- def __init__(self, whens, value=None, else_=None):
- try:
- whens = util.dictlike_iteritems(whens)
- except TypeError:
- pass
-
- if value is not None:
- whenlist = [
- (_literal_as_binds(c).self_group(),
- _literal_as_binds(r)) for (c, r) in whens
- ]
- else:
- whenlist = [
- (_no_literals(c).self_group(),
- _literal_as_binds(r)) for (c, r) in whens
- ]
-
- if whenlist:
- type_ = list(whenlist[-1])[-1].type
- else:
- type_ = None
-
- if value is None:
- self.value = None
- else:
- self.value = _literal_as_binds(value)
-
- self.type = type_
- self.whens = whenlist
- if else_ is not None:
- self.else_ = _literal_as_binds(else_)
- else:
- self.else_ = None
-
- def _copy_internals(self, clone=_clone, **kw):
- if self.value is not None:
- self.value = clone(self.value, **kw)
- self.whens = [(clone(x, **kw), clone(y, **kw))
- for x, y in self.whens]
- if self.else_ is not None:
- self.else_ = clone(self.else_, **kw)
-
- def get_children(self, **kwargs):
- if self.value is not None:
- yield self.value
- for x, y in self.whens:
- yield x
- yield y
- if self.else_ is not None:
- yield self.else_
-
- @property
- def _from_objects(self):
- return list(itertools.chain(*[x._from_objects for x in
- self.get_children()]))
-
-
-class FunctionElement(Executable, ColumnElement, FromClause):
- """Base for SQL function-oriented constructs.
-
- .. seealso::
-
- :class:`.Function` - named SQL function.
-
- :data:`.func` - namespace which produces registered or ad-hoc
- :class:`.Function` instances.
-
- :class:`.GenericFunction` - allows creation of registered function
- types.
-
- """
-
- packagenames = ()
-
- def __init__(self, *clauses, **kwargs):
- """Construct a :class:`.FunctionElement`.
- """
- args = [_literal_as_binds(c, self.name) for c in clauses]
- self.clause_expr = ClauseList(
- operator=operators.comma_op,
- group_contents=True, *args).\
- self_group()
-
- @property
- def columns(self):
- """Fulfill the 'columns' contract of :class:`.ColumnElement`.
-
- Returns a single-element list consisting of this object.
-
- """
- return [self]
-
- @util.memoized_property
- def clauses(self):
- """Return the underlying :class:`.ClauseList` which contains
- the arguments for this :class:`.FunctionElement`.
-
- """
- return self.clause_expr.element
-
- def over(self, partition_by=None, order_by=None):
- """Produce an OVER clause against this function.
-
- Used against aggregate or so-called "window" functions,
- for database backends that support window functions.
-
- The expression::
-
- func.row_number().over(order_by='x')
-
- is shorthand for::
-
- from sqlalchemy import over
- over(func.row_number(), order_by='x')
-
- See :func:`~.expression.over` for a full description.
-
- .. versionadded:: 0.7
-
- """
- return over(self, partition_by=partition_by, order_by=order_by)
-
- @property
- def _from_objects(self):
- return self.clauses._from_objects
-
- def get_children(self, **kwargs):
- return self.clause_expr,
-
- def _copy_internals(self, clone=_clone, **kw):
- self.clause_expr = clone(self.clause_expr, **kw)
- self._reset_exported()
- FunctionElement.clauses._reset(self)
-
- def select(self):
- """Produce a :func:`~.expression.select` construct
- against this :class:`.FunctionElement`.
-
- This is shorthand for::
-
- s = select([function_element])
-
- """
- s = select([self])
- if self._execution_options:
- s = s.execution_options(**self._execution_options)
- return s
-
- def scalar(self):
- """Execute this :class:`.FunctionElement` against an embedded
- 'bind' and return a scalar value.
-
- This first calls :meth:`~.FunctionElement.select` to
- produce a SELECT construct.
-
- Note that :class:`.FunctionElement` can be passed to
- the :meth:`.Connectable.scalar` method of :class:`.Connection`
- or :class:`.Engine`.
-
- """
- return self.select().execute().scalar()
-
- def execute(self):
- """Execute this :class:`.FunctionElement` against an embedded
- 'bind'.
-
- This first calls :meth:`~.FunctionElement.select` to
- produce a SELECT construct.
-
- Note that :class:`.FunctionElement` can be passed to
- the :meth:`.Connectable.execute` method of :class:`.Connection`
- or :class:`.Engine`.
-
- """
- return self.select().execute()
-
- def _bind_param(self, operator, obj):
- return BindParameter(None, obj, _compared_to_operator=operator,
- _compared_to_type=self.type, unique=True)
-
-
-class Function(FunctionElement):
- """Describe a named SQL function.
-
- See the superclass :class:`.FunctionElement` for a description
- of public methods.
-
- .. seealso::
-
- :data:`.func` - namespace which produces registered or ad-hoc
- :class:`.Function` instances.
-
- :class:`.GenericFunction` - allows creation of registered function
- types.
-
- """
-
- __visit_name__ = 'function'
-
- def __init__(self, name, *clauses, **kw):
- """Construct a :class:`.Function`.
-
- The :data:`.func` construct is normally used to construct
- new :class:`.Function` instances.
-
- """
- self.packagenames = kw.pop('packagenames', None) or []
- self.name = name
- self._bind = kw.get('bind', None)
- self.type = sqltypes.to_instance(kw.get('type_', None))
-
- FunctionElement.__init__(self, *clauses, **kw)
-
- def _bind_param(self, operator, obj):
- return BindParameter(self.name, obj,
- _compared_to_operator=operator,
- _compared_to_type=self.type,
- unique=True)
-
-
-class Cast(ColumnElement):
-
- __visit_name__ = 'cast'
-
- def __init__(self, clause, totype, **kwargs):
- self.type = sqltypes.to_instance(totype)
- self.clause = _literal_as_binds(clause, None)
- self.typeclause = TypeClause(self.type)
-
- def _copy_internals(self, clone=_clone, **kw):
- self.clause = clone(self.clause, **kw)
- self.typeclause = clone(self.typeclause, **kw)
-
- def get_children(self, **kwargs):
- return self.clause, self.typeclause
-
- @property
- def _from_objects(self):
- return self.clause._from_objects
-
-
-class Extract(ColumnElement):
-
- __visit_name__ = 'extract'
-
- def __init__(self, field, expr, **kwargs):
- self.type = sqltypes.Integer()
- self.field = field
- self.expr = _literal_as_binds(expr, None)
-
- def _copy_internals(self, clone=_clone, **kw):
- self.expr = clone(self.expr, **kw)
-
- def get_children(self, **kwargs):
- return self.expr,
-
- @property
- def _from_objects(self):
- return self.expr._from_objects
-
-
-class UnaryExpression(ColumnElement):
- """Define a 'unary' expression.
-
- A unary expression has a single column expression
- and an operator. The operator can be placed on the left
- (where it is called the 'operator') or right (where it is called the
- 'modifier') of the column expression.
-
- """
- __visit_name__ = 'unary'
-
- def __init__(self, element, operator=None, modifier=None,
- type_=None, negate=None):
- self.operator = operator
- self.modifier = modifier
-
- self.element = _literal_as_text(element).\
- self_group(against=self.operator or self.modifier)
- self.type = sqltypes.to_instance(type_)
- self.negate = negate
-
- @util.memoized_property
- def _order_by_label_element(self):
- if self.modifier in (operators.desc_op, operators.asc_op):
- return self.element._order_by_label_element
- else:
- return None
-
- @property
- def _from_objects(self):
- return self.element._from_objects
-
- def _copy_internals(self, clone=_clone, **kw):
- self.element = clone(self.element, **kw)
-
- def get_children(self, **kwargs):
- return self.element,
-
- def compare(self, other, **kw):
- """Compare this :class:`UnaryExpression` against the given
- :class:`.ClauseElement`."""
-
- return (
- isinstance(other, UnaryExpression) and
- self.operator == other.operator and
- self.modifier == other.modifier and
- self.element.compare(other.element, **kw)
- )
-
- def _negate(self):
- if self.negate is not None:
- return UnaryExpression(
- self.element,
- operator=self.negate,
- negate=self.operator,
- modifier=self.modifier,
- type_=self.type)
- else:
- return super(UnaryExpression, self)._negate()
-
- def self_group(self, against=None):
- if self.operator and operators.is_precedent(self.operator,
- against):
- return Grouping(self)
- else:
- return self
-
-
-class BinaryExpression(ColumnElement):
- """Represent an expression that is ``LEFT <operator> RIGHT``.
-
- A :class:`.BinaryExpression` is generated automatically
- whenever two column expressions are used in a Python binary expresion::
-
- >>> from sqlalchemy.sql import column
- >>> column('a') + column('b')
- <sqlalchemy.sql.expression.BinaryExpression object at 0x101029dd0>
- >>> print column('a') + column('b')
- a + b
-
- """
-
- __visit_name__ = 'binary'
-
- def __init__(self, left, right, operator, type_=None,
- negate=None, modifiers=None):
- # allow compatibility with libraries that
- # refer to BinaryExpression directly and pass strings
- if isinstance(operator, util.string_types):
- operator = operators.custom_op(operator)
- self._orig = (left, right)
- self.left = _literal_as_text(left).self_group(against=operator)
- self.right = _literal_as_text(right).self_group(against=operator)
- self.operator = operator
- self.type = sqltypes.to_instance(type_)
- self.negate = negate
-
- if modifiers is None:
- self.modifiers = {}
- else:
- self.modifiers = modifiers
-
- def __bool__(self):
- if self.operator in (operator.eq, operator.ne):
- return self.operator(hash(self._orig[0]), hash(self._orig[1]))
- else:
- raise TypeError("Boolean value of this clause is not defined")
-
- __nonzero__ = __bool__
-
- @property
- def is_comparison(self):
- return operators.is_comparison(self.operator)
-
- @property
- def _from_objects(self):
- return self.left._from_objects + self.right._from_objects
-
- def _copy_internals(self, clone=_clone, **kw):
- self.left = clone(self.left, **kw)
- self.right = clone(self.right, **kw)
-
- def get_children(self, **kwargs):
- return self.left, self.right
-
- def compare(self, other, **kw):
- """Compare this :class:`BinaryExpression` against the
- given :class:`BinaryExpression`."""
-
- return (
- isinstance(other, BinaryExpression) and
- self.operator == other.operator and
- (
- self.left.compare(other.left, **kw) and
- self.right.compare(other.right, **kw) or
- (
- operators.is_commutative(self.operator) and
- self.left.compare(other.right, **kw) and
- self.right.compare(other.left, **kw)
- )
- )
- )
-
- def self_group(self, against=None):
- if operators.is_precedent(self.operator, against):
- return Grouping(self)
- else:
- return self
-
- def _negate(self):
- if self.negate is not None:
- return BinaryExpression(
- self.left,
- self.right,
- self.negate,
- negate=self.operator,
- type_=sqltypes.BOOLEANTYPE,
- modifiers=self.modifiers)
- else:
- return super(BinaryExpression, self)._negate()
-
-
-class Exists(UnaryExpression):
- __visit_name__ = UnaryExpression.__visit_name__
- _from_objects = []
-
- def __init__(self, *args, **kwargs):
- if args and isinstance(args[0], (SelectBase, ScalarSelect)):
- s = args[0]
- else:
- if not args:
- args = ([literal_column('*')],)
- s = select(*args, **kwargs).as_scalar().self_group()
-
- UnaryExpression.__init__(self, s, operator=operators.exists,
- type_=sqltypes.Boolean)
-
- def select(self, whereclause=None, **params):
- return select([self], whereclause, **params)
-
- def correlate(self, *fromclause):
- e = self._clone()
- e.element = self.element.correlate(*fromclause).self_group()
- return e
-
- def correlate_except(self, *fromclause):
- e = self._clone()
- e.element = self.element.correlate_except(*fromclause).self_group()
- return e
-
- def select_from(self, clause):
- """return a new :class:`.Exists` construct, applying the given
- expression to the :meth:`.Select.select_from` method of the select
- statement contained.
-
- """
- e = self._clone()
- e.element = self.element.select_from(clause).self_group()
- return e
-
- def where(self, clause):
- """return a new exists() construct with the given expression added to
- its WHERE clause, joined to the existing clause via AND, if any.
-
- """
- e = self._clone()
- e.element = self.element.where(clause).self_group()
- return e
-
-
-class Join(FromClause):
- """represent a ``JOIN`` construct between two :class:`.FromClause`
- elements.
-
- The public constructor function for :class:`.Join` is the module-level
- :func:`join()` function, as well as the :func:`join()` method available
- off all :class:`.FromClause` subclasses.
-
- """
- __visit_name__ = 'join'
-
- def __init__(self, left, right, onclause=None, isouter=False):
- """Construct a new :class:`.Join`.
-
- The usual entrypoint here is the :func:`~.expression.join`
- function or the :meth:`.FromClause.join` method of any
- :class:`.FromClause` object.
-
- """
- self.left = _interpret_as_from(left)
- self.right = _interpret_as_from(right).self_group()
-
- if onclause is None:
- self.onclause = self._match_primaries(self.left, self.right)
- else:
- self.onclause = onclause
-
- self.isouter = isouter
-
- @property
- def description(self):
- return "Join object on %s(%d) and %s(%d)" % (
- self.left.description,
- id(self.left),
- self.right.description,
- id(self.right))
-
- def is_derived_from(self, fromclause):
- return fromclause is self or \
- self.left.is_derived_from(fromclause) or \
- self.right.is_derived_from(fromclause)
-
- def self_group(self, against=None):
- return FromGrouping(self)
-
- def _populate_column_collection(self):
- columns = [c for c in self.left.columns] + \
- [c for c in self.right.columns]
-
- self.primary_key.extend(sqlutil.reduce_columns(
- (c for c in columns if c.primary_key), self.onclause))
- self._columns.update((col._label, col) for col in columns)
- self.foreign_keys.update(itertools.chain(
- *[col.foreign_keys for col in columns]))
-
- def _refresh_for_new_column(self, column):
- col = self.left._refresh_for_new_column(column)
- if col is None:
- col = self.right._refresh_for_new_column(column)
- if col is not None:
- if self._cols_populated:
- self._columns[col._label] = col
- self.foreign_keys.add(col)
- if col.primary_key:
- self.primary_key.add(col)
- return col
- return None
-
- def _copy_internals(self, clone=_clone, **kw):
- self._reset_exported()
- self.left = clone(self.left, **kw)
- self.right = clone(self.right, **kw)
- self.onclause = clone(self.onclause, **kw)
-
- def get_children(self, **kwargs):
- return self.left, self.right, self.onclause
-
- def _match_primaries(self, left, right):
- if isinstance(left, Join):
- left_right = left.right
- else:
- left_right = None
- return sqlutil.join_condition(left, right, a_subset=left_right)
-
- def select(self, whereclause=None, **kwargs):
- """Create a :class:`.Select` from this :class:`.Join`.
-
- The equivalent long-hand form, given a :class:`.Join` object
- ``j``, is::
-
- from sqlalchemy import select
- j = select([j.left, j.right], **kw).\\
- where(whereclause).\\
- select_from(j)
-
- :param whereclause: the WHERE criterion that will be sent to
- the :func:`select()` function
-
- :param \**kwargs: all other kwargs are sent to the
- underlying :func:`select()` function.
-
- """
- collist = [self.left, self.right]
-
- return select(collist, whereclause, from_obj=[self], **kwargs)
-
- @property
- def bind(self):
- return self.left.bind or self.right.bind
-
- def alias(self, name=None, flat=False):
- """return an alias of this :class:`.Join`.
-
- The default behavior here is to first produce a SELECT
- construct from this :class:`.Join`, then to produce a
- :class:`.Alias` from that. So given a join of the form::
-
- j = table_a.join(table_b, table_a.c.id == table_b.c.a_id)
-
- The JOIN by itself would look like::
-
- table_a JOIN table_b ON table_a.id = table_b.a_id
-
- Whereas the alias of the above, ``j.alias()``, would in a
- SELECT context look like::
-
- (SELECT table_a.id AS table_a_id, table_b.id AS table_b_id,
- table_b.a_id AS table_b_a_id
- FROM table_a
- JOIN table_b ON table_a.id = table_b.a_id) AS anon_1
-
- The equivalent long-hand form, given a :class:`.Join` object
- ``j``, is::
-
- from sqlalchemy import select, alias
- j = alias(
- select([j.left, j.right]).\\
- select_from(j).\\
- with_labels(True).\\
- correlate(False),
- name=name
- )
-
- The selectable produced by :meth:`.Join.alias` features the same
- columns as that of the two individual selectables presented under
- a single name - the individual columns are "auto-labeled", meaning
- the ``.c.`` collection of the resulting :class:`.Alias` represents
- the names of the individual columns using a ``<tablename>_<columname>``
- scheme::
-
- j.c.table_a_id
- j.c.table_b_a_id
-
- :meth:`.Join.alias` also features an alternate
- option for aliasing joins which produces no enclosing SELECT and
- does not normally apply labels to the column names. The
- ``flat=True`` option will call :meth:`.FromClause.alias`
- against the left and right sides individually.
- Using this option, no new ``SELECT`` is produced;
- we instead, from a construct as below::
-
- j = table_a.join(table_b, table_a.c.id == table_b.c.a_id)
- j = j.alias(flat=True)
-
- we get a result like this::
-
- table_a AS table_a_1 JOIN table_b AS table_b_1 ON
- table_a_1.id = table_b_1.a_id
-
- The ``flat=True`` argument is also propagated to the contained
- selectables, so that a composite join such as::
-
- j = table_a.join(
- table_b.join(table_c,
- table_b.c.id == table_c.c.b_id),
- table_b.c.a_id == table_a.c.id
- ).alias(flat=True)
-
- Will produce an expression like::
-
- table_a AS table_a_1 JOIN (
- table_b AS table_b_1 JOIN table_c AS table_c_1
- ON table_b_1.id = table_c_1.b_id
- ) ON table_a_1.id = table_b_1.a_id
-
- The standalone :func:`experssion.alias` function as well as the
- base :meth:`.FromClause.alias` method also support the ``flat=True``
- argument as a no-op, so that the argument can be passed to the
- ``alias()`` method of any selectable.
-
- .. versionadded:: 0.9.0 Added the ``flat=True`` option to create
- "aliases" of joins without enclosing inside of a SELECT
- subquery.
-
- :param name: name given to the alias.
-
- :param flat: if True, produce an alias of the left and right
- sides of this :class:`.Join` and return the join of those
- two selectables. This produces join expression that does not
- include an enclosing SELECT.
-
- .. versionadded:: 0.9.0
-
- .. seealso::
-
- :func:`~.expression.alias`
-
- """
- if flat:
- assert name is None, "Can't send name argument with flat"
- left_a, right_a = self.left.alias(flat=True), \
- self.right.alias(flat=True)
- adapter = sqlutil.ClauseAdapter(left_a).\
- chain(sqlutil.ClauseAdapter(right_a))
-
- return left_a.join(right_a,
- adapter.traverse(self.onclause), isouter=self.isouter)
- else:
- return self.select(use_labels=True, correlate=False).alias(name)
-
- @property
- def _hide_froms(self):
- return itertools.chain(*[_from_objects(x.left, x.right)
- for x in self._cloned_set])
-
- @property
- def _from_objects(self):
- return [self] + \
- self.onclause._from_objects + \
- self.left._from_objects + \
- self.right._from_objects
-
-
-class Alias(FromClause):
- """Represents an table or selectable alias (AS).
-
- Represents an alias, as typically applied to any table or
- sub-select within a SQL statement using the ``AS`` keyword (or
- without the keyword on certain databases such as Oracle).
-
- This object is constructed from the :func:`~.expression.alias` module level
- function as well as the :meth:`.FromClause.alias` method available on all
- :class:`.FromClause` subclasses.
-
- """
-
- __visit_name__ = 'alias'
- named_with_column = True
-
- def __init__(self, selectable, name=None):
- baseselectable = selectable
- while isinstance(baseselectable, Alias):
- baseselectable = baseselectable.element
- self.original = baseselectable
- self.supports_execution = baseselectable.supports_execution
- if self.supports_execution:
- self._execution_options = baseselectable._execution_options
- self.element = selectable
- if name is None:
- if self.original.named_with_column:
- name = getattr(self.original, 'name', None)
- name = _anonymous_label('%%(%d %s)s' % (id(self), name
- or 'anon'))
- self.name = name
-
- @property
- def description(self):
- if util.py3k:
- return self.name
- else:
- return self.name.encode('ascii', 'backslashreplace')
-
- def as_scalar(self):
- try:
- return self.element.as_scalar()
- except AttributeError:
- raise AttributeError("Element %s does not support "
- "'as_scalar()'" % self.element)
-
- def is_derived_from(self, fromclause):
- if fromclause in self._cloned_set:
- return True
- return self.element.is_derived_from(fromclause)
-
- def _populate_column_collection(self):
- for col in self.element.columns:
- col._make_proxy(self)
-
- def _refresh_for_new_column(self, column):
- col = self.element._refresh_for_new_column(column)
- if col is not None:
- if not self._cols_populated:
- return None
- else:
- return col._make_proxy(self)
- else:
- return None
-
- def _copy_internals(self, clone=_clone, **kw):
- # don't apply anything to an aliased Table
- # for now. May want to drive this from
- # the given **kw.
- if isinstance(self.element, TableClause):
- return
- self._reset_exported()
- self.element = clone(self.element, **kw)
- baseselectable = self.element
- while isinstance(baseselectable, Alias):
- baseselectable = baseselectable.element
- self.original = baseselectable
-
- def get_children(self, column_collections=True, **kw):
- if column_collections:
- for c in self.c:
- yield c
- yield self.element
-
- @property
- def _from_objects(self):
- return [self]
-
- @property
- def bind(self):
- return self.element.bind
-
-
-class CTE(Alias):
- """Represent a Common Table Expression.
-
- The :class:`.CTE` object is obtained using the
- :meth:`.SelectBase.cte` method from any selectable.
- See that method for complete examples.
-
- .. versionadded:: 0.7.6
-
- """
- __visit_name__ = 'cte'
-
- def __init__(self, selectable,
- name=None,
- recursive=False,
- _cte_alias=None,
- _restates=frozenset()):
- self.recursive = recursive
- self._cte_alias = _cte_alias
- self._restates = _restates
- super(CTE, self).__init__(selectable, name=name)
-
- def alias(self, name=None, flat=False):
- return CTE(
- self.original,
- name=name,
- recursive=self.recursive,
- _cte_alias=self,
- )
-
- def union(self, other):
- return CTE(
- self.original.union(other),
- name=self.name,
- recursive=self.recursive,
- _restates=self._restates.union([self])
- )
-
- def union_all(self, other):
- return CTE(
- self.original.union_all(other),
- name=self.name,
- recursive=self.recursive,
- _restates=self._restates.union([self])
- )
-
-
-class Grouping(ColumnElement):
- """Represent a grouping within a column expression"""
-
- __visit_name__ = 'grouping'
-
- def __init__(self, element):
- self.element = element
- self.type = getattr(element, 'type', sqltypes.NULLTYPE)
-
- @property
- def _label(self):
- return getattr(self.element, '_label', None) or self.anon_label
-
- def _copy_internals(self, clone=_clone, **kw):
- self.element = clone(self.element, **kw)
-
- def get_children(self, **kwargs):
- return self.element,
-
- @property
- def _from_objects(self):
- return self.element._from_objects
-
- def __getattr__(self, attr):
- return getattr(self.element, attr)
-
- def __getstate__(self):
- return {'element': self.element, 'type': self.type}
-
- def __setstate__(self, state):
- self.element = state['element']
- self.type = state['type']
-
- def compare(self, other, **kw):
- return isinstance(other, Grouping) and \
- self.element.compare(other.element)
-
-
-class FromGrouping(FromClause):
- """Represent a grouping of a FROM clause"""
- __visit_name__ = 'grouping'
-
- def __init__(self, element):
- self.element = element
-
- def _init_collections(self):
- pass
-
- @property
- def columns(self):
- return self.element.columns
-
- @property
- def primary_key(self):
- return self.element.primary_key
-
- @property
- def foreign_keys(self):
- return self.element.foreign_keys
-
- def is_derived_from(self, element):
- return self.element.is_derived_from(element)
-
- def alias(self, **kw):
- return FromGrouping(self.element.alias(**kw))
-
- @property
- def _hide_froms(self):
- return self.element._hide_froms
-
- def get_children(self, **kwargs):
- return self.element,
-
- def _copy_internals(self, clone=_clone, **kw):
- self.element = clone(self.element, **kw)
-
- @property
- def _from_objects(self):
- return self.element._from_objects
-
- def __getattr__(self, attr):
- return getattr(self.element, attr)
-
- def __getstate__(self):
- return {'element': self.element}
-
- def __setstate__(self, state):
- self.element = state['element']
-
-
-class Over(ColumnElement):
- """Represent an OVER clause.
-
- This is a special operator against a so-called
- "window" function, as well as any aggregate function,
- which produces results relative to the result set
- itself. It's supported only by certain database
- backends.
-
- """
- __visit_name__ = 'over'
-
- order_by = None
- partition_by = None
-
- def __init__(self, func, partition_by=None, order_by=None):
- self.func = func
- if order_by is not None:
- self.order_by = ClauseList(*util.to_list(order_by))
- if partition_by is not None:
- self.partition_by = ClauseList(*util.to_list(partition_by))
-
- @util.memoized_property
- def type(self):
- return self.func.type
-
- def get_children(self, **kwargs):
- return [c for c in
- (self.func, self.partition_by, self.order_by)
- if c is not None]
-
- def _copy_internals(self, clone=_clone, **kw):
- self.func = clone(self.func, **kw)
- if self.partition_by is not None:
- self.partition_by = clone(self.partition_by, **kw)
- if self.order_by is not None:
- self.order_by = clone(self.order_by, **kw)
-
- @property
- def _from_objects(self):
- return list(itertools.chain(
- *[c._from_objects for c in
- (self.func, self.partition_by, self.order_by)
- if c is not None]
- ))
-
-
-class Label(ColumnElement):
- """Represents a column label (AS).
-
- Represent a label, as typically applied to any column-level
- element using the ``AS`` sql keyword.
-
- This object is constructed from the :func:`label()` module level
- function as well as the :func:`label()` method available on all
- :class:`.ColumnElement` subclasses.
-
- """
-
- __visit_name__ = 'label'
-
- def __init__(self, name, element, type_=None):
- while isinstance(element, Label):
- element = element.element
- if name:
- self.name = name
- else:
- self.name = _anonymous_label('%%(%d %s)s' % (id(self),
- getattr(element, 'name', 'anon')))
- self.key = self._label = self._key_label = self.name
- self._element = element
- self._type = type_
- self.quote = element.quote
- self._proxies = [element]
-
- @util.memoized_property
- def _order_by_label_element(self):
- return self
-
- @util.memoized_property
- def type(self):
- return sqltypes.to_instance(
- self._type or getattr(self._element, 'type', None)
- )
-
- @util.memoized_property
- def element(self):
- return self._element.self_group(against=operators.as_)
-
- def self_group(self, against=None):
- sub_element = self._element.self_group(against=against)
- if sub_element is not self._element:
- return Label(self.name,
- sub_element,
- type_=self._type)
- else:
- return self
-
- @property
- def primary_key(self):
- return self.element.primary_key
-
- @property
- def foreign_keys(self):
- return self.element.foreign_keys
-
- def get_children(self, **kwargs):
- return self.element,
-
- def _copy_internals(self, clone=_clone, **kw):
- self.element = clone(self.element, **kw)
-
- @property
- def _from_objects(self):
- return self.element._from_objects
-
- def _make_proxy(self, selectable, name=None, **kw):
- e = self.element._make_proxy(selectable,
- name=name if name else self.name)
- e._proxies.append(self)
- if self._type is not None:
- e.type = self._type
- return e
-
-
-class ColumnClause(Immutable, ColumnElement):
- """Represents a generic column expression from any textual string.
-
- This includes columns associated with tables, aliases and select
- statements, but also any arbitrary text. May or may not be bound
- to an underlying :class:`.Selectable`.
-
- :class:`.ColumnClause` is constructed by itself typically via
- the :func:`~.expression.column` function. It may be placed directly
- into constructs such as :func:`.select` constructs::
-
- from sqlalchemy.sql import column, select
-
- c1, c2 = column("c1"), column("c2")
- s = select([c1, c2]).where(c1==5)
-
- There is also a variant on :func:`~.expression.column` known
- as :func:`~.expression.literal_column` - the difference is that
- in the latter case, the string value is assumed to be an exact
- expression, rather than a column name, so that no quoting rules
- or similar are applied::
-
- from sqlalchemy.sql import literal_column, select
-
- s = select([literal_column("5 + 7")])
-
- :class:`.ColumnClause` can also be used in a table-like
- fashion by combining the :func:`~.expression.column` function
- with the :func:`~.expression.table` function, to produce
- a "lightweight" form of table metadata::
-
- from sqlalchemy.sql import table, column
-
- user = table("user",
- column("id"),
- column("name"),
- column("description"),
- )
-
- The above construct can be created in an ad-hoc fashion and is
- not associated with any :class:`.schema.MetaData`, unlike it's
- more full fledged :class:`.schema.Table` counterpart.
-
- :param text: the text of the element.
-
- :param selectable: parent selectable.
-
- :param type: :class:`.types.TypeEngine` object which can associate
- this :class:`.ColumnClause` with a type.
-
- :param is_literal: if True, the :class:`.ColumnClause` is assumed to
- be an exact expression that will be delivered to the output with no
- quoting rules applied regardless of case sensitive settings. the
- :func:`literal_column()` function is usually used to create such a
- :class:`.ColumnClause`.
-
-
- """
- __visit_name__ = 'column'
-
- onupdate = default = server_default = server_onupdate = None
-
- _memoized_property = util.group_expirable_memoized_property()
-
- def __init__(self, text, selectable=None, type_=None, is_literal=False):
- self.key = self.name = text
- self.table = selectable
- self.type = sqltypes.to_instance(type_)
- self.is_literal = is_literal
-
- def _compare_name_for_result(self, other):
- if self.is_literal or \
- self.table is None or \
- not hasattr(other, 'proxy_set') or (
- isinstance(other, ColumnClause) and other.is_literal
- ):
- return super(ColumnClause, self).\
- _compare_name_for_result(other)
- else:
- return other.proxy_set.intersection(self.proxy_set)
-
- def _get_table(self):
- return self.__dict__['table']
-
- def _set_table(self, table):
- self._memoized_property.expire_instance(self)
- self.__dict__['table'] = table
- table = property(_get_table, _set_table)
-
- @_memoized_property
- def _from_objects(self):
- t = self.table
- if t is not None:
- return [t]
- else:
- return []
-
- @util.memoized_property
- def description(self):
- if util.py3k:
- return self.name
- else:
- return self.name.encode('ascii', 'backslashreplace')
-
- @_memoized_property
- def _key_label(self):
- if self.key != self.name:
- return self._gen_label(self.key)
- else:
- return self._label
-
- @_memoized_property
- def _label(self):
- return self._gen_label(self.name)
-
- def _gen_label(self, name):
- t = self.table
- if self.is_literal:
- return None
-
- elif t is not None and t.named_with_column:
- if getattr(t, 'schema', None):
- label = t.schema.replace('.', '_') + "_" + \
- t.name + "_" + name
- else:
- label = t.name + "_" + name
-
- # ensure the label name doesn't conflict with that
- # of an existing column
- if label in t.c:
- _label = label
- counter = 1
- while _label in t.c:
- _label = label + "_" + str(counter)
- counter += 1
- label = _label
-
- return _as_truncated(label)
-
- else:
- return name
-
- def _bind_param(self, operator, obj):
- return BindParameter(self.name, obj,
- _compared_to_operator=operator,
- _compared_to_type=self.type,
- unique=True)
-
- def _make_proxy(self, selectable, name=None, attach=True,
- name_is_truncatable=False, **kw):
- # propagate the "is_literal" flag only if we are keeping our name,
- # otherwise its considered to be a label
- is_literal = self.is_literal and (name is None or name == self.name)
- c = self._constructor(
- _as_truncated(name or self.name) if \
- name_is_truncatable else \
- (name or self.name),
- selectable=selectable,
- type_=self.type,
- is_literal=is_literal
- )
- if name is None:
- c.key = self.key
- c._proxies = [self]
- if selectable._is_clone_of is not None:
- c._is_clone_of = \
- selectable._is_clone_of.columns.get(c.key)
-
- if attach:
- selectable._columns[c.key] = c
- return c
-
-
-class TableClause(Immutable, FromClause):
- """Represents a minimal "table" construct.
-
- The constructor for :class:`.TableClause` is the
- :func:`~.expression.table` function. This produces
- a lightweight table object that has only a name and a
- collection of columns, which are typically produced
- by the :func:`~.expression.column` function::
-
- from sqlalchemy.sql import table, column
-
- user = table("user",
- column("id"),
- column("name"),
- column("description"),
- )
-
- The :class:`.TableClause` construct serves as the base for
- the more commonly used :class:`~.schema.Table` object, providing
- the usual set of :class:`~.expression.FromClause` services including
- the ``.c.`` collection and statement generation methods.
-
- It does **not** provide all the additional schema-level services
- of :class:`~.schema.Table`, including constraints, references to other
- tables, or support for :class:`.MetaData`-level services. It's useful
- on its own as an ad-hoc construct used to generate quick SQL
- statements when a more fully fledged :class:`~.schema.Table`
- is not on hand.
-
- """
-
- __visit_name__ = 'table'
-
- named_with_column = True
-
- implicit_returning = False
- """:class:`.TableClause` doesn't support having a primary key or column
- -level defaults, so implicit returning doesn't apply."""
-
- _autoincrement_column = None
- """No PK or default support so no autoincrement column."""
-
- def __init__(self, name, *columns):
- super(TableClause, self).__init__()
- self.name = self.fullname = name
- self._columns = ColumnCollection()
- self.primary_key = ColumnSet()
- self.foreign_keys = set()
- for c in columns:
- self.append_column(c)
-
- def _init_collections(self):
- pass
-
- @util.memoized_property
- def description(self):
- if util.py3k:
- return self.name
- else:
- return self.name.encode('ascii', 'backslashreplace')
-
- def append_column(self, c):
- self._columns[c.key] = c
- c.table = self
-
- def get_children(self, column_collections=True, **kwargs):
- if column_collections:
- return [c for c in self.c]
- else:
- return []
-
- def count(self, whereclause=None, **params):
- """return a SELECT COUNT generated against this
- :class:`.TableClause`."""
-
- if self.primary_key:
- col = list(self.primary_key)[0]
- else:
- col = list(self.columns)[0]
- return select(
- [func.count(col).label('tbl_row_count')],
- whereclause,
- from_obj=[self],
- **params)
-
- def insert(self, values=None, inline=False, **kwargs):
- """Generate an :func:`.insert` construct against this
- :class:`.TableClause`.
-
- E.g.::
-
- table.insert().values(name='foo')
-
- See :func:`.insert` for argument and usage information.
-
- """
-
- return insert(self, values=values, inline=inline, **kwargs)
-
- def update(self, whereclause=None, values=None, inline=False, **kwargs):
- """Generate an :func:`.update` construct against this
- :class:`.TableClause`.
-
- E.g.::
-
- table.update().where(table.c.id==7).values(name='foo')
-
- See :func:`.update` for argument and usage information.
-
- """
-
- return update(self, whereclause=whereclause,
- values=values, inline=inline, **kwargs)
-
- def delete(self, whereclause=None, **kwargs):
- """Generate a :func:`.delete` construct against this
- :class:`.TableClause`.
-
- E.g.::
-
- table.delete().where(table.c.id==7)
-
- See :func:`.delete` for argument and usage information.
-
- """
-
- return delete(self, whereclause, **kwargs)
-
- @property
- def _from_objects(self):
- return [self]
-
-
-class SelectBase(Executable, FromClause):
- """Base class for :class:`.Select` and ``CompoundSelects``."""
-
- _order_by_clause = ClauseList()
- _group_by_clause = ClauseList()
- _limit = None
- _offset = None
-
- def __init__(self,
- use_labels=False,
- for_update=False,
- limit=None,
- offset=None,
- order_by=None,
- group_by=None,
- bind=None,
- autocommit=None):
- self.use_labels = use_labels
- self.for_update = for_update
- if autocommit is not None:
- util.warn_deprecated('autocommit on select() is '
- 'deprecated. Use .execution_options(a'
- 'utocommit=True)')
- self._execution_options = \
- self._execution_options.union(
- {'autocommit': autocommit})
- if limit is not None:
- self._limit = util.asint(limit)
- if offset is not None:
- self._offset = util.asint(offset)
- self._bind = bind
-
- if order_by is not None:
- self._order_by_clause = ClauseList(*util.to_list(order_by))
- if group_by is not None:
- self._group_by_clause = ClauseList(*util.to_list(group_by))
-
- def as_scalar(self):
- """return a 'scalar' representation of this selectable, which can be
- used as a column expression.
-
- Typically, a select statement which has only one column in its columns
- clause is eligible to be used as a scalar expression.
-
- The returned object is an instance of
- :class:`ScalarSelect`.
-
- """
- return ScalarSelect(self)
-
- @_generative
- def apply_labels(self):
- """return a new selectable with the 'use_labels' flag set to True.
-
- This will result in column expressions being generated using labels
- against their table name, such as "SELECT somecolumn AS
- tablename_somecolumn". This allows selectables which contain multiple
- FROM clauses to produce a unique set of column names regardless of
- name conflicts among the individual FROM clauses.
-
- """
- self.use_labels = True
-
- def label(self, name):
- """return a 'scalar' representation of this selectable, embedded as a
- subquery with a label.
-
- .. seealso::
-
- :meth:`~.SelectBase.as_scalar`.
-
- """
- return self.as_scalar().label(name)
-
- def cte(self, name=None, recursive=False):
- """Return a new :class:`.CTE`, or Common Table Expression instance.
-
- Common table expressions are a SQL standard whereby SELECT
- statements can draw upon secondary statements specified along
- with the primary statement, using a clause called "WITH".
- Special semantics regarding UNION can also be employed to
- allow "recursive" queries, where a SELECT statement can draw
- upon the set of rows that have previously been selected.
-
- SQLAlchemy detects :class:`.CTE` objects, which are treated
- similarly to :class:`.Alias` objects, as special elements
- to be delivered to the FROM clause of the statement as well
- as to a WITH clause at the top of the statement.
-
- .. versionadded:: 0.7.6
-
- :param name: name given to the common table expression. Like
- :meth:`._FromClause.alias`, the name can be left as ``None``
- in which case an anonymous symbol will be used at query
- compile time.
- :param recursive: if ``True``, will render ``WITH RECURSIVE``.
- A recursive common table expression is intended to be used in
- conjunction with UNION ALL in order to derive rows
- from those already selected.
-
- The following examples illustrate two examples from
- Postgresql's documentation at
- http://www.postgresql.org/docs/8.4/static/queries-with.html.
-
- Example 1, non recursive::
-
- from sqlalchemy import Table, Column, String, Integer, MetaData, \\
- select, func
-
- metadata = MetaData()
-
- orders = Table('orders', metadata,
- Column('region', String),
- Column('amount', Integer),
- Column('product', String),
- Column('quantity', Integer)
- )
-
- regional_sales = select([
- orders.c.region,
- func.sum(orders.c.amount).label('total_sales')
- ]).group_by(orders.c.region).cte("regional_sales")
-
-
- top_regions = select([regional_sales.c.region]).\\
- where(
- regional_sales.c.total_sales >
- select([
- func.sum(regional_sales.c.total_sales)/10
- ])
- ).cte("top_regions")
-
- statement = select([
- orders.c.region,
- orders.c.product,
- func.sum(orders.c.quantity).label("product_units"),
- func.sum(orders.c.amount).label("product_sales")
- ]).where(orders.c.region.in_(
- select([top_regions.c.region])
- )).group_by(orders.c.region, orders.c.product)
-
- result = conn.execute(statement).fetchall()
-
- Example 2, WITH RECURSIVE::
-
- from sqlalchemy import Table, Column, String, Integer, MetaData, \\
- select, func
-
- metadata = MetaData()
-
- parts = Table('parts', metadata,
- Column('part', String),
- Column('sub_part', String),
- Column('quantity', Integer),
- )
-
- included_parts = select([
- parts.c.sub_part,
- parts.c.part,
- parts.c.quantity]).\\
- where(parts.c.part=='our part').\\
- cte(recursive=True)
-
-
- incl_alias = included_parts.alias()
- parts_alias = parts.alias()
- included_parts = included_parts.union_all(
- select([
- parts_alias.c.part,
- parts_alias.c.sub_part,
- parts_alias.c.quantity
- ]).
- where(parts_alias.c.part==incl_alias.c.sub_part)
- )
-
- statement = select([
- included_parts.c.sub_part,
- func.sum(included_parts.c.quantity).
- label('total_quantity')
- ]).\
- select_from(included_parts.join(parts,
- included_parts.c.part==parts.c.part)).\\
- group_by(included_parts.c.sub_part)
-
- result = conn.execute(statement).fetchall()
-
-
- .. seealso::
-
- :meth:`.orm.query.Query.cte` - ORM version of :meth:`.SelectBase.cte`.
-
- """
- return CTE(self, name=name, recursive=recursive)
-
- @_generative
- @util.deprecated('0.6',
- message=":func:`.autocommit` is deprecated. Use "
- ":func:`.Executable.execution_options` with the "
- "'autocommit' flag.")
- def autocommit(self):
- """return a new selectable with the 'autocommit' flag set to
- True."""
-
- self._execution_options = \
- self._execution_options.union({'autocommit': True})
-
- def _generate(self):
- """Override the default _generate() method to also clear out
- exported collections."""
-
- s = self.__class__.__new__(self.__class__)
- s.__dict__ = self.__dict__.copy()
- s._reset_exported()
- return s
-
- @_generative
- def limit(self, limit):
- """return a new selectable with the given LIMIT criterion
- applied."""
-
- self._limit = util.asint(limit)
-
- @_generative
- def offset(self, offset):
- """return a new selectable with the given OFFSET criterion
- applied."""
-
- self._offset = util.asint(offset)
-
- @_generative
- def order_by(self, *clauses):
- """return a new selectable with the given list of ORDER BY
- criterion applied.
-
- The criterion will be appended to any pre-existing ORDER BY
- criterion.
-
- """
-
- self.append_order_by(*clauses)
-
- @_generative
- def group_by(self, *clauses):
- """return a new selectable with the given list of GROUP BY
- criterion applied.
-
- The criterion will be appended to any pre-existing GROUP BY
- criterion.
-
- """
-
- self.append_group_by(*clauses)
-
- def append_order_by(self, *clauses):
- """Append the given ORDER BY criterion applied to this selectable.
-
- The criterion will be appended to any pre-existing ORDER BY criterion.
-
- This is an **in-place** mutation method; the
- :meth:`~.SelectBase.order_by` method is preferred, as it provides standard
- :term:`method chaining`.
-
- """
- if len(clauses) == 1 and clauses[0] is None:
- self._order_by_clause = ClauseList()
- else:
- if getattr(self, '_order_by_clause', None) is not None:
- clauses = list(self._order_by_clause) + list(clauses)
- self._order_by_clause = ClauseList(*clauses)
-
- def append_group_by(self, *clauses):
- """Append the given GROUP BY criterion applied to this selectable.
-
- The criterion will be appended to any pre-existing GROUP BY criterion.
-
- This is an **in-place** mutation method; the
- :meth:`~.SelectBase.group_by` method is preferred, as it provides standard
- :term:`method chaining`.
-
- """
- if len(clauses) == 1 and clauses[0] is None:
- self._group_by_clause = ClauseList()
- else:
- if getattr(self, '_group_by_clause', None) is not None:
- clauses = list(self._group_by_clause) + list(clauses)
- self._group_by_clause = ClauseList(*clauses)
-
- @property
- def _from_objects(self):
- return [self]
-
-
-class ScalarSelect(Generative, Grouping):
- _from_objects = []
-
- def __init__(self, element):
- self.element = element
- self.type = element._scalar_type()
-
- @property
- def columns(self):
- raise exc.InvalidRequestError('Scalar Select expression has no '
- 'columns; use this object directly within a '
- 'column-level expression.')
- c = columns
-
- @_generative
- def where(self, crit):
- """Apply a WHERE clause to the SELECT statement referred to
- by this :class:`.ScalarSelect`.
-
- """
- self.element = self.element.where(crit)
-
- def self_group(self, **kwargs):
- return self
-
-
-class CompoundSelect(SelectBase):
- """Forms the basis of ``UNION``, ``UNION ALL``, and other
- SELECT-based set operations."""
-
- __visit_name__ = 'compound_select'
-
- UNION = util.symbol('UNION')
- UNION_ALL = util.symbol('UNION ALL')
- EXCEPT = util.symbol('EXCEPT')
- EXCEPT_ALL = util.symbol('EXCEPT ALL')
- INTERSECT = util.symbol('INTERSECT')
- INTERSECT_ALL = util.symbol('INTERSECT ALL')
-
- def __init__(self, keyword, *selects, **kwargs):
- self._auto_correlate = kwargs.pop('correlate', False)
- self.keyword = keyword
- self.selects = []
-
- numcols = None
-
- # some DBs do not like ORDER BY in the inner queries of a UNION, etc.
- for n, s in enumerate(selects):
- s = _clause_element_as_expr(s)
-
- if not numcols:
- numcols = len(s.c)
- elif len(s.c) != numcols:
- raise exc.ArgumentError('All selectables passed to '
- 'CompoundSelect must have identical numbers of '
- 'columns; select #%d has %d columns, select '
- '#%d has %d' % (1, len(self.selects[0].c), n
- + 1, len(s.c)))
-
- self.selects.append(s.self_group(self))
-
- SelectBase.__init__(self, **kwargs)
-
- def _scalar_type(self):
- return self.selects[0]._scalar_type()
-
- def self_group(self, against=None):
- return FromGrouping(self)
-
- def is_derived_from(self, fromclause):
- for s in self.selects:
- if s.is_derived_from(fromclause):
- return True
- return False
-
- def _populate_column_collection(self):
- for cols in zip(*[s.c for s in self.selects]):
-
- # this is a slightly hacky thing - the union exports a
- # column that resembles just that of the *first* selectable.
- # to get at a "composite" column, particularly foreign keys,
- # you have to dig through the proxies collection which we
- # generate below. We may want to improve upon this, such as
- # perhaps _make_proxy can accept a list of other columns
- # that are "shared" - schema.column can then copy all the
- # ForeignKeys in. this would allow the union() to have all
- # those fks too.
-
- proxy = cols[0]._make_proxy(self,
- name=cols[0]._label if self.use_labels else None,
- key=cols[0]._key_label if self.use_labels else None)
-
- # hand-construct the "_proxies" collection to include all
- # derived columns place a 'weight' annotation corresponding
- # to how low in the list of select()s the column occurs, so
- # that the corresponding_column() operation can resolve
- # conflicts
-
- proxy._proxies = [c._annotate({'weight': i + 1}) for (i,
- c) in enumerate(cols)]
-
- def _refresh_for_new_column(self, column):
- for s in self.selects:
- s._refresh_for_new_column(column)
-
- if not self._cols_populated:
- return None
-
- raise NotImplementedError("CompoundSelect constructs don't support "
- "addition of columns to underlying selectables")
-
- def _copy_internals(self, clone=_clone, **kw):
- self._reset_exported()
- self.selects = [clone(s, **kw) for s in self.selects]
- if hasattr(self, '_col_map'):
- del self._col_map
- for attr in ('_order_by_clause', '_group_by_clause'):
- if getattr(self, attr) is not None:
- setattr(self, attr, clone(getattr(self, attr), **kw))
-
- def get_children(self, column_collections=True, **kwargs):
- return (column_collections and list(self.c) or []) \
- + [self._order_by_clause, self._group_by_clause] \
- + list(self.selects)
-
- def bind(self):
- if self._bind:
- return self._bind
- for s in self.selects:
- e = s.bind
- if e:
- return e
- else:
- return None
-
- def _set_bind(self, bind):
- self._bind = bind
- bind = property(bind, _set_bind)
-
-
-class HasPrefixes(object):
- _prefixes = ()
-
- @_generative
- def prefix_with(self, *expr, **kw):
- """Add one or more expressions following the statement keyword, i.e.
- SELECT, INSERT, UPDATE, or DELETE. Generative.
-
- This is used to support backend-specific prefix keywords such as those
- provided by MySQL.
-
- E.g.::
-
- stmt = table.insert().prefix_with("LOW_PRIORITY", dialect="mysql")
-
- Multiple prefixes can be specified by multiple calls
- to :meth:`.prefix_with`.
-
- :param \*expr: textual or :class:`.ClauseElement` construct which
- will be rendered following the INSERT, UPDATE, or DELETE
- keyword.
- :param \**kw: A single keyword 'dialect' is accepted. This is an
- optional string dialect name which will
- limit rendering of this prefix to only that dialect.
-
- """
- dialect = kw.pop('dialect', None)
- if kw:
- raise exc.ArgumentError("Unsupported argument(s): %s" %
- ",".join(kw))
- self._setup_prefixes(expr, dialect)
-
- def _setup_prefixes(self, prefixes, dialect=None):
- self._prefixes = self._prefixes + tuple(
- [(_literal_as_text(p), dialect) for p in prefixes])
-
-
-class Select(HasPrefixes, SelectBase):
- """Represents a ``SELECT`` statement.
-
- .. seealso::
-
- :func:`~.expression.select` - the function which creates
- a :class:`.Select` object.
-
- :ref:`coretutorial_selecting` - Core Tutorial description
- of :func:`.select`.
-
- """
-
- __visit_name__ = 'select'
-
- _prefixes = ()
- _hints = util.immutabledict()
- _distinct = False
- _from_cloned = None
- _correlate = ()
- _correlate_except = None
- _memoized_property = SelectBase._memoized_property
-
- def __init__(self,
- columns,
- whereclause=None,
- from_obj=None,
- distinct=False,
- having=None,
- correlate=True,
- prefixes=None,
- **kwargs):
- """Construct a Select object.
-
- The public constructor for Select is the
- :func:`select` function; see that function for
- argument descriptions.
-
- Additional generative and mutator methods are available on the
- :class:`SelectBase` superclass.
-
- """
- self._auto_correlate = correlate
- if distinct is not False:
- if distinct is True:
- self._distinct = True
- else:
- self._distinct = [
- _literal_as_text(e)
- for e in util.to_list(distinct)
- ]
-
- if from_obj is not None:
- self._from_obj = util.OrderedSet(
- _interpret_as_from(f)
- for f in util.to_list(from_obj))
- else:
- self._from_obj = util.OrderedSet()
-
- try:
- cols_present = bool(columns)
- except TypeError:
- raise exc.ArgumentError("columns argument to select() must "
- "be a Python list or other iterable")
-
- if cols_present:
- self._raw_columns = []
- for c in columns:
- c = _interpret_as_column_or_from(c)
- if isinstance(c, ScalarSelect):
- c = c.self_group(against=operators.comma_op)
- self._raw_columns.append(c)
- else:
- self._raw_columns = []
-
- if whereclause is not None:
- self._whereclause = _literal_as_text(whereclause)
- else:
- self._whereclause = None
-
- if having is not None:
- self._having = _literal_as_text(having)
- else:
- self._having = None
-
- if prefixes:
- self._setup_prefixes(prefixes)
-
- SelectBase.__init__(self, **kwargs)
-
- @property
- def _froms(self):
- # would love to cache this,
- # but there's just enough edge cases, particularly now that
- # declarative encourages construction of SQL expressions
- # without tables present, to just regen this each time.
- froms = []
- seen = set()
- translate = self._from_cloned
-
- def add(items):
- for item in items:
- if translate and item in translate:
- item = translate[item]
- if not seen.intersection(item._cloned_set):
- froms.append(item)
- seen.update(item._cloned_set)
-
- add(_from_objects(*self._raw_columns))
- if self._whereclause is not None:
- add(_from_objects(self._whereclause))
- add(self._from_obj)
-
- return froms
-
- def _get_display_froms(self, explicit_correlate_froms=None,
- implicit_correlate_froms=None):
- """Return the full list of 'from' clauses to be displayed.
-
- Takes into account a set of existing froms which may be
- rendered in the FROM clause of enclosing selects; this Select
- may want to leave those absent if it is automatically
- correlating.
-
- """
- froms = self._froms
-
- toremove = set(itertools.chain(*[
- _expand_cloned(f._hide_froms)
- for f in froms]))
- if toremove:
- # if we're maintaining clones of froms,
- # add the copies out to the toremove list. only include
- # clones that are lexical equivalents.
- if self._from_cloned:
- toremove.update(
- self._from_cloned[f] for f in
- toremove.intersection(self._from_cloned)
- if self._from_cloned[f]._is_lexical_equivalent(f)
- )
- # filter out to FROM clauses not in the list,
- # using a list to maintain ordering
- froms = [f for f in froms if f not in toremove]
-
- if self._correlate:
- to_correlate = self._correlate
- if to_correlate:
- froms = [
- f for f in froms if f not in
- _cloned_intersection(
- _cloned_intersection(froms, explicit_correlate_froms or ()),
- to_correlate
- )
- ]
-
- if self._correlate_except is not None:
-
- froms = [
- f for f in froms if f not in
- _cloned_difference(
- _cloned_intersection(froms, explicit_correlate_froms or ()),
- self._correlate_except
- )
- ]
-
- if self._auto_correlate and \
- implicit_correlate_froms and \
- len(froms) > 1:
-
- froms = [
- f for f in froms if f not in
- _cloned_intersection(froms, implicit_correlate_froms)
- ]
-
- if not len(froms):
- raise exc.InvalidRequestError("Select statement '%s"
- "' returned no FROM clauses due to "
- "auto-correlation; specify "
- "correlate(<tables>) to control "
- "correlation manually." % self)
-
- return froms
-
- def _scalar_type(self):
- elem = self._raw_columns[0]
- cols = list(elem._select_iterable)
- return cols[0].type
-
- @property
- def froms(self):
- """Return the displayed list of FromClause elements."""
-
- return self._get_display_froms()
-
- @_generative
- def with_hint(self, selectable, text, dialect_name='*'):
- """Add an indexing hint for the given selectable to this
- :class:`.Select`.
-
- The text of the hint is rendered in the appropriate
- location for the database backend in use, relative
- to the given :class:`.Table` or :class:`.Alias` passed as the
- ``selectable`` argument. The dialect implementation
- typically uses Python string substitution syntax
- with the token ``%(name)s`` to render the name of
- the table or alias. E.g. when using Oracle, the
- following::
-
- select([mytable]).\\
- with_hint(mytable, "+ index(%(name)s ix_mytable)")
-
- Would render SQL as::
-
- select /*+ index(mytable ix_mytable) */ ... from mytable
-
- The ``dialect_name`` option will limit the rendering of a particular
- hint to a particular backend. Such as, to add hints for both Oracle
- and Sybase simultaneously::
-
- select([mytable]).\\
- with_hint(mytable, "+ index(%(name)s ix_mytable)", 'oracle').\\
- with_hint(mytable, "WITH INDEX ix_mytable", 'sybase')
-
- """
- self._hints = self._hints.union(
- {(selectable, dialect_name): text})
-
- @property
- def type(self):
- raise exc.InvalidRequestError("Select objects don't have a type. "
- "Call as_scalar() on this Select object "
- "to return a 'scalar' version of this Select.")
-
- @_memoized_property.method
- def locate_all_froms(self):
- """return a Set of all FromClause elements referenced by this Select.
-
- This set is a superset of that returned by the ``froms`` property,
- which is specifically for those FromClause elements that would
- actually be rendered.
-
- """
- froms = self._froms
- return froms + list(_from_objects(*froms))
-
- @property
- def inner_columns(self):
- """an iterator of all ColumnElement expressions which would
- be rendered into the columns clause of the resulting SELECT statement.
-
- """
- return _select_iterables(self._raw_columns)
-
- def is_derived_from(self, fromclause):
- if self in fromclause._cloned_set:
- return True
-
- for f in self.locate_all_froms():
- if f.is_derived_from(fromclause):
- return True
- return False
-
- def _copy_internals(self, clone=_clone, **kw):
-
- # Select() object has been cloned and probably adapted by the
- # given clone function. Apply the cloning function to internal
- # objects
-
- # 1. keep a dictionary of the froms we've cloned, and what
- # they've become. This is consulted later when we derive
- # additional froms from "whereclause" and the columns clause,
- # which may still reference the uncloned parent table.
- # as of 0.7.4 we also put the current version of _froms, which
- # gets cleared on each generation. previously we were "baking"
- # _froms into self._from_obj.
- self._from_cloned = from_cloned = dict((f, clone(f, **kw))
- for f in self._from_obj.union(self._froms))
-
- # 3. update persistent _from_obj with the cloned versions.
- self._from_obj = util.OrderedSet(from_cloned[f] for f in
- self._from_obj)
-
- # the _correlate collection is done separately, what can happen
- # here is the same item is _correlate as in _from_obj but the
- # _correlate version has an annotation on it - (specifically
- # RelationshipProperty.Comparator._criterion_exists() does
- # this). Also keep _correlate liberally open with it's previous
- # contents, as this set is used for matching, not rendering.
- self._correlate = set(clone(f) for f in
- self._correlate).union(self._correlate)
-
- # 4. clone other things. The difficulty here is that Column
- # objects are not actually cloned, and refer to their original
- # .table, resulting in the wrong "from" parent after a clone
- # operation. Hence _from_cloned and _from_obj supercede what is
- # present here.
- self._raw_columns = [clone(c, **kw) for c in self._raw_columns]
- for attr in '_whereclause', '_having', '_order_by_clause', \
- '_group_by_clause':
- if getattr(self, attr) is not None:
- setattr(self, attr, clone(getattr(self, attr), **kw))
-
- # erase exported column list, _froms collection,
- # etc.
- self._reset_exported()
-
- def get_children(self, column_collections=True, **kwargs):
- """return child elements as per the ClauseElement specification."""
-
- return (column_collections and list(self.columns) or []) + \
- self._raw_columns + list(self._froms) + \
- [x for x in
- (self._whereclause, self._having,
- self._order_by_clause, self._group_by_clause)
- if x is not None]
-
- @_generative
- def column(self, column):
- """return a new select() construct with the given column expression
- added to its columns clause.
-
- """
- self.append_column(column)
-
- def reduce_columns(self, only_synonyms=True):
- """Return a new :func`.select` construct with redundantly
- named, equivalently-valued columns removed from the columns clause.
-
- "Redundant" here means two columns where one refers to the
- other either based on foreign key, or via a simple equality
- comparison in the WHERE clause of the statement. The primary purpose
- of this method is to automatically construct a select statement
- with all uniquely-named columns, without the need to use
- table-qualified labels as :meth:`.apply_labels` does.
-
- When columns are omitted based on foreign key, the referred-to
- column is the one that's kept. When columns are omitted based on
- WHERE eqivalence, the first column in the columns clause is the
- one that's kept.
-
- :param only_synonyms: when True, limit the removal of columns
- to those which have the same name as the equivalent. Otherwise,
- all columns that are equivalent to another are removed.
-
- .. versionadded:: 0.8
-
- """
- return self.with_only_columns(
- sqlutil.reduce_columns(
- self.inner_columns,
- only_synonyms=only_synonyms,
- *(self._whereclause, ) + tuple(self._from_obj)
- )
- )
-
- @_generative
- def with_only_columns(self, columns):
- """Return a new :func:`.select` construct with its columns
- clause replaced with the given columns.
-
- .. versionchanged:: 0.7.3
- Due to a bug fix, this method has a slight
- behavioral change as of version 0.7.3.
- Prior to version 0.7.3, the FROM clause of
- a :func:`.select` was calculated upfront and as new columns
- were added; in 0.7.3 and later it's calculated
- at compile time, fixing an issue regarding late binding
- of columns to parent tables. This changes the behavior of
- :meth:`.Select.with_only_columns` in that FROM clauses no
- longer represented in the new list are dropped,
- but this behavior is more consistent in
- that the FROM clauses are consistently derived from the
- current columns clause. The original intent of this method
- is to allow trimming of the existing columns list to be fewer
- columns than originally present; the use case of replacing
- the columns list with an entirely different one hadn't
- been anticipated until 0.7.3 was released; the usage
- guidelines below illustrate how this should be done.
-
- This method is exactly equivalent to as if the original
- :func:`.select` had been called with the given columns
- clause. I.e. a statement::
-
- s = select([table1.c.a, table1.c.b])
- s = s.with_only_columns([table1.c.b])
-
- should be exactly equivalent to::
-
- s = select([table1.c.b])
-
- This means that FROM clauses which are only derived
- from the column list will be discarded if the new column
- list no longer contains that FROM::
-
- >>> table1 = table('t1', column('a'), column('b'))
- >>> table2 = table('t2', column('a'), column('b'))
- >>> s1 = select([table1.c.a, table2.c.b])
- >>> print s1
- SELECT t1.a, t2.b FROM t1, t2
- >>> s2 = s1.with_only_columns([table2.c.b])
- >>> print s2
- SELECT t2.b FROM t1
-
- The preferred way to maintain a specific FROM clause
- in the construct, assuming it won't be represented anywhere
- else (i.e. not in the WHERE clause, etc.) is to set it using
- :meth:`.Select.select_from`::
-
- >>> s1 = select([table1.c.a, table2.c.b]).\\
- ... select_from(table1.join(table2,
- ... table1.c.a==table2.c.a))
- >>> s2 = s1.with_only_columns([table2.c.b])
- >>> print s2
- SELECT t2.b FROM t1 JOIN t2 ON t1.a=t2.a
-
- Care should also be taken to use the correct
- set of column objects passed to :meth:`.Select.with_only_columns`.
- Since the method is essentially equivalent to calling the
- :func:`.select` construct in the first place with the given
- columns, the columns passed to :meth:`.Select.with_only_columns`
- should usually be a subset of those which were passed
- to the :func:`.select` construct, not those which are available
- from the ``.c`` collection of that :func:`.select`. That
- is::
-
- s = select([table1.c.a, table1.c.b]).select_from(table1)
- s = s.with_only_columns([table1.c.b])
-
- and **not**::
-
- # usually incorrect
- s = s.with_only_columns([s.c.b])
-
- The latter would produce the SQL::
-
- SELECT b
- FROM (SELECT t1.a AS a, t1.b AS b
- FROM t1), t1
-
- Since the :func:`.select` construct is essentially being
- asked to select both from ``table1`` as well as itself.
-
- """
- self._reset_exported()
- rc = []
- for c in columns:
- c = _interpret_as_column_or_from(c)
- if isinstance(c, ScalarSelect):
- c = c.self_group(against=operators.comma_op)
- rc.append(c)
- self._raw_columns = rc
-
- @_generative
- def where(self, whereclause):
- """return a new select() construct with the given expression added to
- its WHERE clause, joined to the existing clause via AND, if any.
-
- """
-
- self.append_whereclause(whereclause)
-
- @_generative
- def having(self, having):
- """return a new select() construct with the given expression added to
- its HAVING clause, joined to the existing clause via AND, if any.
-
- """
- self.append_having(having)
-
- @_generative
- def distinct(self, *expr):
- """Return a new select() construct which will apply DISTINCT to its
- columns clause.
-
- :param \*expr: optional column expressions. When present,
- the Postgresql dialect will render a ``DISTINCT ON (<expressions>>)``
- construct.
-
- """
- if expr:
- expr = [_literal_as_text(e) for e in expr]
- if isinstance(self._distinct, list):
- self._distinct = self._distinct + expr
- else:
- self._distinct = expr
- else:
- self._distinct = True
-
- @_generative
- def select_from(self, fromclause):
- """return a new :func:`.select` construct with the
- given FROM expression
- merged into its list of FROM objects.
-
- E.g.::
-
- table1 = table('t1', column('a'))
- table2 = table('t2', column('b'))
- s = select([table1.c.a]).\\
- select_from(
- table1.join(table2, table1.c.a==table2.c.b)
- )
-
- The "from" list is a unique set on the identity of each element,
- so adding an already present :class:`.Table` or other selectable
- will have no effect. Passing a :class:`.Join` that refers
- to an already present :class:`.Table` or other selectable will have
- the effect of concealing the presence of that selectable as
- an individual element in the rendered FROM list, instead
- rendering it into a JOIN clause.
-
- While the typical purpose of :meth:`.Select.select_from` is to
- replace the default, derived FROM clause with a join, it can
- also be called with individual table elements, multiple times
- if desired, in the case that the FROM clause cannot be fully
- derived from the columns clause::
-
- select([func.count('*')]).select_from(table1)
-
- """
- self.append_from(fromclause)
-
- @_generative
- def correlate(self, *fromclauses):
- """return a new :class:`.Select` which will correlate the given FROM
- clauses to that of an enclosing :class:`.Select`.
-
- Calling this method turns off the :class:`.Select` object's
- default behavior of "auto-correlation". Normally, FROM elements
- which appear in a :class:`.Select` that encloses this one via
- its :term:`WHERE clause`, ORDER BY, HAVING or
- :term:`columns clause` will be omitted from this :class:`.Select`
- object's :term:`FROM clause`.
- Setting an explicit correlation collection using the
- :meth:`.Select.correlate` method provides a fixed list of FROM objects
- that can potentially take place in this process.
-
- When :meth:`.Select.correlate` is used to apply specific FROM clauses
- for correlation, the FROM elements become candidates for
- correlation regardless of how deeply nested this :class:`.Select`
- object is, relative to an enclosing :class:`.Select` which refers to
- the same FROM object. This is in contrast to the behavior of
- "auto-correlation" which only correlates to an immediate enclosing
- :class:`.Select`. Multi-level correlation ensures that the link
- between enclosed and enclosing :class:`.Select` is always via
- at least one WHERE/ORDER BY/HAVING/columns clause in order for
- correlation to take place.
-
- If ``None`` is passed, the :class:`.Select` object will correlate
- none of its FROM entries, and all will render unconditionally
- in the local FROM clause.
-
- :param \*fromclauses: a list of one or more :class:`.FromClause`
- constructs, or other compatible constructs (i.e. ORM-mapped
- classes) to become part of the correlate collection.
-
- .. versionchanged:: 0.8.0 ORM-mapped classes are accepted by
- :meth:`.Select.correlate`.
-
- .. versionchanged:: 0.8.0 The :meth:`.Select.correlate` method no
- longer unconditionally removes entries from the FROM clause; instead,
- the candidate FROM entries must also be matched by a FROM entry
- located in an enclosing :class:`.Select`, which ultimately encloses
- this one as present in the WHERE clause, ORDER BY clause, HAVING
- clause, or columns clause of an enclosing :meth:`.Select`.
-
- .. versionchanged:: 0.8.2 explicit correlation takes place
- via any level of nesting of :class:`.Select` objects; in previous
- 0.8 versions, correlation would only occur relative to the immediate
- enclosing :class:`.Select` construct.
-
- .. seealso::
-
- :meth:`.Select.correlate_except`
-
- :ref:`correlated_subqueries`
-
- """
- self._auto_correlate = False
- if fromclauses and fromclauses[0] is None:
- self._correlate = ()
- else:
- self._correlate = set(self._correlate).union(
- _interpret_as_from(f) for f in fromclauses)
-
- @_generative
- def correlate_except(self, *fromclauses):
- """return a new :class:`.Select` which will omit the given FROM
- clauses from the auto-correlation process.
-
- Calling :meth:`.Select.correlate_except` turns off the
- :class:`.Select` object's default behavior of
- "auto-correlation" for the given FROM elements. An element
- specified here will unconditionally appear in the FROM list, while
- all other FROM elements remain subject to normal auto-correlation
- behaviors.
-
- .. versionchanged:: 0.8.2 The :meth:`.Select.correlate_except`
- method was improved to fully prevent FROM clauses specified here
- from being omitted from the immediate FROM clause of this
- :class:`.Select`.
-
- If ``None`` is passed, the :class:`.Select` object will correlate
- all of its FROM entries.
-
- .. versionchanged:: 0.8.2 calling ``correlate_except(None)`` will
- correctly auto-correlate all FROM clauses.
-
- :param \*fromclauses: a list of one or more :class:`.FromClause`
- constructs, or other compatible constructs (i.e. ORM-mapped
- classes) to become part of the correlate-exception collection.
-
- .. seealso::
-
- :meth:`.Select.correlate`
-
- :ref:`correlated_subqueries`
-
- """
-
- self._auto_correlate = False
- if fromclauses and fromclauses[0] is None:
- self._correlate_except = ()
- else:
- self._correlate_except = set(self._correlate_except or ()).union(
- _interpret_as_from(f) for f in fromclauses)
-
- def append_correlation(self, fromclause):
- """append the given correlation expression to this select()
- construct.
-
- This is an **in-place** mutation method; the
- :meth:`~.Select.correlate` method is preferred, as it provides standard
- :term:`method chaining`.
-
- """
-
- self._auto_correlate = False
- self._correlate = set(self._correlate).union(
- _interpret_as_from(f) for f in fromclause)
-
- def append_column(self, column):
- """append the given column expression to the columns clause of this
- select() construct.
-
- This is an **in-place** mutation method; the
- :meth:`~.Select.column` method is preferred, as it provides standard
- :term:`method chaining`.
-
- """
- self._reset_exported()
- column = _interpret_as_column_or_from(column)
-
- if isinstance(column, ScalarSelect):
- column = column.self_group(against=operators.comma_op)
-
- self._raw_columns = self._raw_columns + [column]
-
- def append_prefix(self, clause):
- """append the given columns clause prefix expression to this select()
- construct.
-
- This is an **in-place** mutation method; the
- :meth:`~.Select.prefix_with` method is preferred, as it provides standard
- :term:`method chaining`.
-
- """
- clause = _literal_as_text(clause)
- self._prefixes = self._prefixes + (clause,)
-
- def append_whereclause(self, whereclause):
- """append the given expression to this select() construct's WHERE
- criterion.
-
- The expression will be joined to existing WHERE criterion via AND.
-
- This is an **in-place** mutation method; the
- :meth:`~.Select.where` method is preferred, as it provides standard
- :term:`method chaining`.
-
- """
- self._reset_exported()
- whereclause = _literal_as_text(whereclause)
-
- if self._whereclause is not None:
- self._whereclause = and_(self._whereclause, whereclause)
- else:
- self._whereclause = whereclause
-
- def append_having(self, having):
- """append the given expression to this select() construct's HAVING
- criterion.
-
- The expression will be joined to existing HAVING criterion via AND.
-
- This is an **in-place** mutation method; the
- :meth:`~.Select.having` method is preferred, as it provides standard
- :term:`method chaining`.
-
- """
- if self._having is not None:
- self._having = and_(self._having, _literal_as_text(having))
- else:
- self._having = _literal_as_text(having)
-
- def append_from(self, fromclause):
- """append the given FromClause expression to this select() construct's
- FROM clause.
-
- This is an **in-place** mutation method; the
- :meth:`~.Select.select_from` method is preferred, as it provides standard
- :term:`method chaining`.
-
- """
- self._reset_exported()
- fromclause = _interpret_as_from(fromclause)
- self._from_obj = self._from_obj.union([fromclause])
-
-
- @_memoized_property
- def _columns_plus_names(self):
- if self.use_labels:
- names = set()
- def name_for_col(c):
- if c._label is None:
- return (None, c)
- name = c._label
- if name in names:
- name = c.anon_label
- else:
- names.add(name)
- return name, c
-
- return [
- name_for_col(c)
- for c in util.unique_list(_select_iterables(self._raw_columns))
- ]
- else:
- return [
- (None, c)
- for c in util.unique_list(_select_iterables(self._raw_columns))
- ]
-
- def _populate_column_collection(self):
- for name, c in self._columns_plus_names:
- if not hasattr(c, '_make_proxy'):
- continue
- if name is None:
- key = None
- elif self.use_labels:
- key = c._key_label
- if key is not None and key in self.c:
- key = c.anon_label
- else:
- key = None
-
- c._make_proxy(self, key=key,
- name=name,
- name_is_truncatable=True)
-
- def _refresh_for_new_column(self, column):
- for fromclause in self._froms:
- col = fromclause._refresh_for_new_column(column)
- if col is not None:
- if col in self.inner_columns and self._cols_populated:
- our_label = col._key_label if self.use_labels else col.key
- if our_label not in self.c:
- return col._make_proxy(self,
- name=col._label if self.use_labels else None,
- key=col._key_label if self.use_labels else None,
- name_is_truncatable=True)
- return None
- return None
-
- def self_group(self, against=None):
- """return a 'grouping' construct as per the ClauseElement
- specification.
-
- This produces an element that can be embedded in an expression. Note
- that this method is called automatically as needed when constructing
- expressions and should not require explicit use.
-
- """
- if isinstance(against, CompoundSelect):
- return self
- return FromGrouping(self)
-
- def union(self, other, **kwargs):
- """return a SQL UNION of this select() construct against the given
- selectable."""
-
- return union(self, other, **kwargs)
-
- def union_all(self, other, **kwargs):
- """return a SQL UNION ALL of this select() construct against the given
- selectable.
-
- """
- return union_all(self, other, **kwargs)
-
- def except_(self, other, **kwargs):
- """return a SQL EXCEPT of this select() construct against the given
- selectable."""
-
- return except_(self, other, **kwargs)
-
- def except_all(self, other, **kwargs):
- """return a SQL EXCEPT ALL of this select() construct against the
- given selectable.
-
- """
- return except_all(self, other, **kwargs)
-
- def intersect(self, other, **kwargs):
- """return a SQL INTERSECT of this select() construct against the given
- selectable.
-
- """
- return intersect(self, other, **kwargs)
-
- def intersect_all(self, other, **kwargs):
- """return a SQL INTERSECT ALL of this select() construct against the
- given selectable.
-
- """
- return intersect_all(self, other, **kwargs)
-
- def bind(self):
- if self._bind:
- return self._bind
- froms = self._froms
- if not froms:
- for c in self._raw_columns:
- e = c.bind
- if e:
- self._bind = e
- return e
- else:
- e = list(froms)[0].bind
- if e:
- self._bind = e
- return e
-
- return None
-
- def _set_bind(self, bind):
- self._bind = bind
- bind = property(bind, _set_bind)
-
-
-class UpdateBase(HasPrefixes, Executable, ClauseElement):
- """Form the base for ``INSERT``, ``UPDATE``, and ``DELETE`` statements.
-
- """
-
- __visit_name__ = 'update_base'
-
- _execution_options = \
- Executable._execution_options.union({'autocommit': True})
- kwargs = util.immutabledict()
- _hints = util.immutabledict()
- _prefixes = ()
-
- def _process_colparams(self, parameters):
- def process_single(p):
- if isinstance(p, (list, tuple)):
- return dict(
- (c.key, pval)
- for c, pval in zip(self.table.c, p)
- )
- else:
- return p
-
- if isinstance(parameters, (list, tuple)) and \
- isinstance(parameters[0], (list, tuple, dict)):
-
- if not self._supports_multi_parameters:
- raise exc.InvalidRequestError(
- "This construct does not support "
- "multiple parameter sets.")
-
- return [process_single(p) for p in parameters], True
- else:
- return process_single(parameters), False
-
- def params(self, *arg, **kw):
- """Set the parameters for the statement.
-
- This method raises ``NotImplementedError`` on the base class,
- and is overridden by :class:`.ValuesBase` to provide the
- SET/VALUES clause of UPDATE and INSERT.
-
- """
- raise NotImplementedError(
- "params() is not supported for INSERT/UPDATE/DELETE statements."
- " To set the values for an INSERT or UPDATE statement, use"
- " stmt.values(**parameters).")
-
- def bind(self):
- """Return a 'bind' linked to this :class:`.UpdateBase`
- or a :class:`.Table` associated with it.
-
- """
- return self._bind or self.table.bind
-
- def _set_bind(self, bind):
- self._bind = bind
- bind = property(bind, _set_bind)
-
- @_generative
- def returning(self, *cols):
- """Add a RETURNING or equivalent clause to this statement.
-
- The given list of columns represent columns within the table that is
- the target of the INSERT, UPDATE, or DELETE. Each element can be any
- column expression. :class:`~sqlalchemy.schema.Table` objects will be
- expanded into their individual columns.
-
- Upon compilation, a RETURNING clause, or database equivalent,
- will be rendered within the statement. For INSERT and UPDATE,
- the values are the newly inserted/updated values. For DELETE,
- the values are those of the rows which were deleted.
-
- Upon execution, the values of the columns to be returned
- are made available via the result set and can be iterated
- using ``fetchone()`` and similar. For DBAPIs which do not
- natively support returning values (i.e. cx_oracle),
- SQLAlchemy will approximate this behavior at the result level
- so that a reasonable amount of behavioral neutrality is
- provided.
-
- Note that not all databases/DBAPIs
- support RETURNING. For those backends with no support,
- an exception is raised upon compilation and/or execution.
- For those who do support it, the functionality across backends
- varies greatly, including restrictions on executemany()
- and other statements which return multiple rows. Please
- read the documentation notes for the database in use in
- order to determine the availability of RETURNING.
-
- """
- self._returning = cols
-
- @_generative
- def with_hint(self, text, selectable=None, dialect_name="*"):
- """Add a table hint for a single table to this
- INSERT/UPDATE/DELETE statement.
-
- .. note::
-
- :meth:`.UpdateBase.with_hint` currently applies only to
- Microsoft SQL Server. For MySQL INSERT/UPDATE/DELETE hints, use
- :meth:`.UpdateBase.prefix_with`.
-
- The text of the hint is rendered in the appropriate
- location for the database backend in use, relative
- to the :class:`.Table` that is the subject of this
- statement, or optionally to that of the given
- :class:`.Table` passed as the ``selectable`` argument.
-
- The ``dialect_name`` option will limit the rendering of a particular
- hint to a particular backend. Such as, to add a hint
- that only takes effect for SQL Server::
-
- mytable.insert().with_hint("WITH (PAGLOCK)", dialect_name="mssql")
-
- .. versionadded:: 0.7.6
-
- :param text: Text of the hint.
- :param selectable: optional :class:`.Table` that specifies
- an element of the FROM clause within an UPDATE or DELETE
- to be the subject of the hint - applies only to certain backends.
- :param dialect_name: defaults to ``*``, if specified as the name
- of a particular dialect, will apply these hints only when
- that dialect is in use.
- """
- if selectable is None:
- selectable = self.table
-
- self._hints = self._hints.union(
- {(selectable, dialect_name): text})
-
-
-class ValuesBase(UpdateBase):
- """Supplies support for :meth:`.ValuesBase.values` to
- INSERT and UPDATE constructs."""
-
- __visit_name__ = 'values_base'
-
- _supports_multi_parameters = False
- _has_multi_parameters = False
- select = None
-
- def __init__(self, table, values, prefixes):
- self.table = _interpret_as_from(table)
- self.parameters, self._has_multi_parameters = \
- self._process_colparams(values)
- if prefixes:
- self._setup_prefixes(prefixes)
-
- @_generative
- def values(self, *args, **kwargs):
- """specify a fixed VALUES clause for an INSERT statement, or the SET
- clause for an UPDATE.
-
- Note that the :class:`.Insert` and :class:`.Update` constructs support
- per-execution time formatting of the VALUES and/or SET clauses,
- based on the arguments passed to :meth:`.Connection.execute`. However,
- the :meth:`.ValuesBase.values` method can be used to "fix" a particular
- set of parameters into the statement.
-
- Multiple calls to :meth:`.ValuesBase.values` will produce a new
- construct, each one with the parameter list modified to include
- the new parameters sent. In the typical case of a single
- dictionary of parameters, the newly passed keys will replace
- the same keys in the previous construct. In the case of a list-based
- "multiple values" construct, each new list of values is extended
- onto the existing list of values.
-
- :param \**kwargs: key value pairs representing the string key
- of a :class:`.Column` mapped to the value to be rendered into the
- VALUES or SET clause::
-
- users.insert().values(name="some name")
-
- users.update().where(users.c.id==5).values(name="some name")
-
- :param \*args: Alternatively, a dictionary, tuple or list
- of dictionaries or tuples can be passed as a single positional
- argument in order to form the VALUES or
- SET clause of the statement. The single dictionary form
- works the same as the kwargs form::
-
- users.insert().values({"name": "some name"})
-
- If a tuple is passed, the tuple should contain the same number
- of columns as the target :class:`.Table`::
-
- users.insert().values((5, "some name"))
-
- The :class:`.Insert` construct also supports multiply-rendered VALUES
- construct, for those backends which support this SQL syntax
- (SQLite, Postgresql, MySQL). This mode is indicated by passing a list
- of one or more dictionaries/tuples::
-
- users.insert().values([
- {"name": "some name"},
- {"name": "some other name"},
- {"name": "yet another name"},
- ])
-
- In the case of an :class:`.Update`
- construct, only the single dictionary/tuple form is accepted,
- else an exception is raised. It is also an exception case to
- attempt to mix the single-/multiple- value styles together,
- either through multiple :meth:`.ValuesBase.values` calls
- or by sending a list + kwargs at the same time.
-
- .. note::
-
- Passing a multiple values list is *not* the same
- as passing a multiple values list to the :meth:`.Connection.execute`
- method. Passing a list of parameter sets to :meth:`.ValuesBase.values`
- produces a construct of this form::
-
- INSERT INTO table (col1, col2, col3) VALUES
- (col1_0, col2_0, col3_0),
- (col1_1, col2_1, col3_1),
- ...
-
- whereas a multiple list passed to :meth:`.Connection.execute`
- has the effect of using the DBAPI
- `executemany() <http://www.python.org/dev/peps/pep-0249/#id18>`_
- method, which provides a high-performance system of invoking
- a single-row INSERT statement many times against a series
- of parameter sets. The "executemany" style is supported by
- all database backends, as it does not depend on a special SQL
- syntax.
-
- .. versionadded:: 0.8
- Support for multiple-VALUES INSERT statements.
-
-
- .. seealso::
-
- :ref:`inserts_and_updates` - SQL Expression
- Language Tutorial
-
- :func:`~.expression.insert` - produce an ``INSERT`` statement
-
- :func:`~.expression.update` - produce an ``UPDATE`` statement
-
- """
- if self.select is not None:
- raise exc.InvalidRequestError(
- "This construct already inserts from a SELECT")
- if self._has_multi_parameters and kwargs:
- raise exc.InvalidRequestError(
- "This construct already has multiple parameter sets.")
-
- if args:
- if len(args) > 1:
- raise exc.ArgumentError(
- "Only a single dictionary/tuple or list of "
- "dictionaries/tuples is accepted positionally.")
- v = args[0]
- else:
- v = {}
-
- if self.parameters is None:
- self.parameters, self._has_multi_parameters = \
- self._process_colparams(v)
- else:
- if self._has_multi_parameters:
- self.parameters = list(self.parameters)
- p, self._has_multi_parameters = self._process_colparams(v)
- if not self._has_multi_parameters:
- raise exc.ArgumentError(
- "Can't mix single-values and multiple values "
- "formats in one statement")
-
- self.parameters.extend(p)
- else:
- self.parameters = self.parameters.copy()
- p, self._has_multi_parameters = self._process_colparams(v)
- if self._has_multi_parameters:
- raise exc.ArgumentError(
- "Can't mix single-values and multiple values "
- "formats in one statement")
- self.parameters.update(p)
-
- if kwargs:
- if self._has_multi_parameters:
- raise exc.ArgumentError(
- "Can't pass kwargs and multiple parameter sets "
- "simultaenously")
- else:
- self.parameters.update(kwargs)
-
-
-class Insert(ValuesBase):
- """Represent an INSERT construct.
-
- The :class:`.Insert` object is created using the
- :func:`~.expression.insert()` function.
-
- .. seealso::
-
- :ref:`coretutorial_insert_expressions`
-
- """
- __visit_name__ = 'insert'
-
- _supports_multi_parameters = True
-
- def __init__(self,
- table,
- values=None,
- inline=False,
- bind=None,
- prefixes=None,
- returning=None,
- **kwargs):
- ValuesBase.__init__(self, table, values, prefixes)
- self._bind = bind
- self.select = None
- self.inline = inline
- self._returning = returning
- self.kwargs = kwargs
-
- def get_children(self, **kwargs):
- if self.select is not None:
- return self.select,
- else:
- return ()
-
- @_generative
- def from_select(self, names, select):
- """Return a new :class:`.Insert` construct which represents
- an ``INSERT...FROM SELECT`` statement.
-
- e.g.::
-
- sel = select([table1.c.a, table1.c.b]).where(table1.c.c > 5)
- ins = table2.insert().from_select(['a', 'b'], sel)
-
- :param names: a sequence of string column names or :class:`.Column`
- objects representing the target columns.
- :param select: a :func:`.select` construct, :class:`.FromClause`
- or other construct which resolves into a :class:`.FromClause`,
- such as an ORM :class:`.Query` object, etc. The order of
- columns returned from this FROM clause should correspond to the
- order of columns sent as the ``names`` parameter; while this
- is not checked before passing along to the database, the database
- would normally raise an exception if these column lists don't
- correspond.
-
- .. note::
-
- Depending on backend, it may be necessary for the :class:`.Insert`
- statement to be constructed using the ``inline=True`` flag; this
- flag will prevent the implicit usage of ``RETURNING`` when the
- ``INSERT`` statement is rendered, which isn't supported on a backend
- such as Oracle in conjunction with an ``INSERT..SELECT`` combination::
-
- sel = select([table1.c.a, table1.c.b]).where(table1.c.c > 5)
- ins = table2.insert(inline=True).from_select(['a', 'b'], sel)
-
- .. versionadded:: 0.8.3
-
- """
- if self.parameters:
- raise exc.InvalidRequestError(
- "This construct already inserts value expressions")
-
- self.parameters, self._has_multi_parameters = \
- self._process_colparams(dict((n, null()) for n in names))
-
- self.select = _interpret_as_select(select)
-
- def _copy_internals(self, clone=_clone, **kw):
- # TODO: coverage
- self.parameters = self.parameters.copy()
- if self.select is not None:
- self.select = _clone(self.select)
-
-
-class Update(ValuesBase):
- """Represent an Update construct.
-
- The :class:`.Update` object is created using the :func:`update()` function.
-
- """
- __visit_name__ = 'update'
-
- def __init__(self,
- table,
- whereclause,
- values=None,
- inline=False,
- bind=None,
- prefixes=None,
- returning=None,
- **kwargs):
- ValuesBase.__init__(self, table, values, prefixes)
- self._bind = bind
- self._returning = returning
- if whereclause is not None:
- self._whereclause = _literal_as_text(whereclause)
- else:
- self._whereclause = None
- self.inline = inline
- self.kwargs = kwargs
-
-
- def get_children(self, **kwargs):
- if self._whereclause is not None:
- return self._whereclause,
- else:
- return ()
-
- def _copy_internals(self, clone=_clone, **kw):
- # TODO: coverage
- self._whereclause = clone(self._whereclause, **kw)
- self.parameters = self.parameters.copy()
-
- @_generative
- def where(self, whereclause):
- """return a new update() construct with the given expression added to
- its WHERE clause, joined to the existing clause via AND, if any.
-
- """
- if self._whereclause is not None:
- self._whereclause = and_(self._whereclause,
- _literal_as_text(whereclause))
- else:
- self._whereclause = _literal_as_text(whereclause)
-
- @property
- def _extra_froms(self):
- # TODO: this could be made memoized
- # if the memoization is reset on each generative call.
- froms = []
- seen = set([self.table])
-
- if self._whereclause is not None:
- for item in _from_objects(self._whereclause):
- if not seen.intersection(item._cloned_set):
- froms.append(item)
- seen.update(item._cloned_set)
-
- return froms
-
-
-class Delete(UpdateBase):
- """Represent a DELETE construct.
-
- The :class:`.Delete` object is created using the :func:`delete()` function.
-
- """
-
- __visit_name__ = 'delete'
-
- def __init__(self,
- table,
- whereclause,
- bind=None,
- returning=None,
- prefixes=None,
- **kwargs):
- self._bind = bind
- self.table = _interpret_as_from(table)
- self._returning = returning
-
- if prefixes:
- self._setup_prefixes(prefixes)
-
- if whereclause is not None:
- self._whereclause = _literal_as_text(whereclause)
- else:
- self._whereclause = None
-
- self.kwargs = kwargs
-
- def get_children(self, **kwargs):
- if self._whereclause is not None:
- return self._whereclause,
- else:
- return ()
-
- @_generative
- def where(self, whereclause):
- """Add the given WHERE clause to a newly returned delete construct."""
-
- if self._whereclause is not None:
- self._whereclause = and_(self._whereclause,
- _literal_as_text(whereclause))
- else:
- self._whereclause = _literal_as_text(whereclause)
-
- def _copy_internals(self, clone=_clone, **kw):
- # TODO: coverage
- self._whereclause = clone(self._whereclause, **kw)
-
-
-class _IdentifiedClause(Executable, ClauseElement):
-
- __visit_name__ = 'identified'
- _execution_options = \
- Executable._execution_options.union({'autocommit': False})
- quote = None
-
- def __init__(self, ident):
- self.ident = ident
-
-
-class SavepointClause(_IdentifiedClause):
- __visit_name__ = 'savepoint'
-
-
-class RollbackToSavepointClause(_IdentifiedClause):
- __visit_name__ = 'rollback_to_savepoint'
-
-
-class ReleaseSavepointClause(_IdentifiedClause):
- __visit_name__ = 'release_savepoint'
# old names for compatibility
+_Executable = Executable
_BindParamClause = BindParameter
_Label = Label
_SelectBase = SelectBase
diff --git a/lib/sqlalchemy/sql/functions.py b/lib/sqlalchemy/sql/functions.py
index 5e2d0792c..24f79466c 100644
--- a/lib/sqlalchemy/sql/functions.py
+++ b/lib/sqlalchemy/sql/functions.py
@@ -4,11 +4,16 @@
# This module is part of SQLAlchemy and is released under
# the MIT License: http://www.opensource.org/licenses/mit-license.php
-from .. import types as sqltypes, schema
-from .expression import (
- ClauseList, Function, _literal_as_binds, literal_column, _type_from_args,
- cast, extract
- )
+"""SQL function API, factories, and built-in functions.
+
+"""
+from . import sqltypes, schema
+from .base import Executable
+from .elements import ClauseList, Cast, Extract, _literal_as_binds, \
+ literal_column, _type_from_args, ColumnElement, _clone,\
+ Over, BindParameter
+from .selectable import FromClause, Select
+
from . import operators
from .visitors import VisitableType
from .. import util
@@ -29,6 +34,279 @@ def register_function(identifier, fn, package="_default"):
reg[identifier] = fn
+class FunctionElement(Executable, ColumnElement, FromClause):
+ """Base for SQL function-oriented constructs.
+
+ .. seealso::
+
+ :class:`.Function` - named SQL function.
+
+ :data:`.func` - namespace which produces registered or ad-hoc
+ :class:`.Function` instances.
+
+ :class:`.GenericFunction` - allows creation of registered function
+ types.
+
+ """
+
+ packagenames = ()
+
+ def __init__(self, *clauses, **kwargs):
+ """Construct a :class:`.FunctionElement`.
+ """
+ args = [_literal_as_binds(c, self.name) for c in clauses]
+ self.clause_expr = ClauseList(
+ operator=operators.comma_op,
+ group_contents=True, *args).\
+ self_group()
+
+ @property
+ def columns(self):
+ """Fulfill the 'columns' contract of :class:`.ColumnElement`.
+
+ Returns a single-element list consisting of this object.
+
+ """
+ return [self]
+
+ @util.memoized_property
+ def clauses(self):
+ """Return the underlying :class:`.ClauseList` which contains
+ the arguments for this :class:`.FunctionElement`.
+
+ """
+ return self.clause_expr.element
+
+ def over(self, partition_by=None, order_by=None):
+ """Produce an OVER clause against this function.
+
+ Used against aggregate or so-called "window" functions,
+ for database backends that support window functions.
+
+ The expression::
+
+ func.row_number().over(order_by='x')
+
+ is shorthand for::
+
+ from sqlalchemy import over
+ over(func.row_number(), order_by='x')
+
+ See :func:`~.expression.over` for a full description.
+
+ .. versionadded:: 0.7
+
+ """
+ return Over(self, partition_by=partition_by, order_by=order_by)
+
+ @property
+ def _from_objects(self):
+ return self.clauses._from_objects
+
+ def get_children(self, **kwargs):
+ return self.clause_expr,
+
+ def _copy_internals(self, clone=_clone, **kw):
+ self.clause_expr = clone(self.clause_expr, **kw)
+ self._reset_exported()
+ FunctionElement.clauses._reset(self)
+
+ def select(self):
+ """Produce a :func:`~.expression.select` construct
+ against this :class:`.FunctionElement`.
+
+ This is shorthand for::
+
+ s = select([function_element])
+
+ """
+ s = Select([self])
+ if self._execution_options:
+ s = s.execution_options(**self._execution_options)
+ return s
+
+ def scalar(self):
+ """Execute this :class:`.FunctionElement` against an embedded
+ 'bind' and return a scalar value.
+
+ This first calls :meth:`~.FunctionElement.select` to
+ produce a SELECT construct.
+
+ Note that :class:`.FunctionElement` can be passed to
+ the :meth:`.Connectable.scalar` method of :class:`.Connection`
+ or :class:`.Engine`.
+
+ """
+ return self.select().execute().scalar()
+
+ def execute(self):
+ """Execute this :class:`.FunctionElement` against an embedded
+ 'bind'.
+
+ This first calls :meth:`~.FunctionElement.select` to
+ produce a SELECT construct.
+
+ Note that :class:`.FunctionElement` can be passed to
+ the :meth:`.Connectable.execute` method of :class:`.Connection`
+ or :class:`.Engine`.
+
+ """
+ return self.select().execute()
+
+ def _bind_param(self, operator, obj):
+ return BindParameter(None, obj, _compared_to_operator=operator,
+ _compared_to_type=self.type, unique=True)
+
+
+class _FunctionGenerator(object):
+ """Generate :class:`.Function` objects based on getattr calls."""
+
+ def __init__(self, **opts):
+ self.__names = []
+ self.opts = opts
+
+ def __getattr__(self, name):
+ # passthru __ attributes; fixes pydoc
+ if name.startswith('__'):
+ try:
+ return self.__dict__[name]
+ except KeyError:
+ raise AttributeError(name)
+
+ elif name.endswith('_'):
+ name = name[0:-1]
+ f = _FunctionGenerator(**self.opts)
+ f.__names = list(self.__names) + [name]
+ return f
+
+ def __call__(self, *c, **kwargs):
+ o = self.opts.copy()
+ o.update(kwargs)
+
+ tokens = len(self.__names)
+
+ if tokens == 2:
+ package, fname = self.__names
+ elif tokens == 1:
+ package, fname = "_default", self.__names[0]
+ else:
+ package = None
+
+ if package is not None and \
+ package in _registry and \
+ fname in _registry[package]:
+ func = _registry[package][fname]
+ return func(*c, **o)
+
+ return Function(self.__names[-1],
+ packagenames=self.__names[0:-1], *c, **o)
+
+
+func = _FunctionGenerator()
+"""Generate SQL function expressions.
+
+ :data:`.func` is a special object instance which generates SQL
+ functions based on name-based attributes, e.g.::
+
+ >>> print func.count(1)
+ count(:param_1)
+
+ The element is a column-oriented SQL element like any other, and is
+ used in that way::
+
+ >>> print select([func.count(table.c.id)])
+ SELECT count(sometable.id) FROM sometable
+
+ Any name can be given to :data:`.func`. If the function name is unknown to
+ SQLAlchemy, it will be rendered exactly as is. For common SQL functions
+ which SQLAlchemy is aware of, the name may be interpreted as a *generic
+ function* which will be compiled appropriately to the target database::
+
+ >>> print func.current_timestamp()
+ CURRENT_TIMESTAMP
+
+ To call functions which are present in dot-separated packages,
+ specify them in the same manner::
+
+ >>> print func.stats.yield_curve(5, 10)
+ stats.yield_curve(:yield_curve_1, :yield_curve_2)
+
+ SQLAlchemy can be made aware of the return type of functions to enable
+ type-specific lexical and result-based behavior. For example, to ensure
+ that a string-based function returns a Unicode value and is similarly
+ treated as a string in expressions, specify
+ :class:`~sqlalchemy.types.Unicode` as the type:
+
+ >>> print func.my_string(u'hi', type_=Unicode) + ' ' + \
+ ... func.my_string(u'there', type_=Unicode)
+ my_string(:my_string_1) || :my_string_2 || my_string(:my_string_3)
+
+ The object returned by a :data:`.func` call is usually an instance of
+ :class:`.Function`.
+ This object meets the "column" interface, including comparison and labeling
+ functions. The object can also be passed the :meth:`~.Connectable.execute`
+ method of a :class:`.Connection` or :class:`.Engine`, where it will be
+ wrapped inside of a SELECT statement first::
+
+ print connection.execute(func.current_timestamp()).scalar()
+
+ In a few exception cases, the :data:`.func` accessor
+ will redirect a name to a built-in expression such as :func:`.cast`
+ or :func:`.extract`, as these names have well-known meaning
+ but are not exactly the same as "functions" from a SQLAlchemy
+ perspective.
+
+ .. versionadded:: 0.8 :data:`.func` can return non-function expression
+ constructs for common quasi-functional names like :func:`.cast`
+ and :func:`.extract`.
+
+ Functions which are interpreted as "generic" functions know how to
+ calculate their return type automatically. For a listing of known generic
+ functions, see :ref:`generic_functions`.
+
+"""
+
+modifier = _FunctionGenerator(group=False)
+
+class Function(FunctionElement):
+ """Describe a named SQL function.
+
+ See the superclass :class:`.FunctionElement` for a description
+ of public methods.
+
+ .. seealso::
+
+ :data:`.func` - namespace which produces registered or ad-hoc
+ :class:`.Function` instances.
+
+ :class:`.GenericFunction` - allows creation of registered function
+ types.
+
+ """
+
+ __visit_name__ = 'function'
+
+ def __init__(self, name, *clauses, **kw):
+ """Construct a :class:`.Function`.
+
+ The :data:`.func` construct is normally used to construct
+ new :class:`.Function` instances.
+
+ """
+ self.packagenames = kw.pop('packagenames', None) or []
+ self.name = name
+ self._bind = kw.get('bind', None)
+ self.type = sqltypes.to_instance(kw.get('type_', None))
+
+ FunctionElement.__init__(self, *clauses, **kw)
+
+ def _bind_param(self, operator, obj):
+ return BindParameter(self.name, obj,
+ _compared_to_operator=operator,
+ _compared_to_type=self.type,
+ unique=True)
+
+
class _GenericMeta(VisitableType):
def __init__(cls, clsname, bases, clsdict):
cls.name = name = clsdict.get('name', clsname)
@@ -128,8 +406,8 @@ class GenericFunction(util.with_metaclass(_GenericMeta, Function)):
kwargs.pop("type_", None) or getattr(self, 'type', None))
-register_function("cast", cast)
-register_function("extract", extract)
+register_function("cast", Cast)
+register_function("extract", Extract)
class next_value(GenericFunction):
diff --git a/lib/sqlalchemy/sql/schema.py b/lib/sqlalchemy/sql/schema.py
new file mode 100644
index 000000000..dbd7ec03d
--- /dev/null
+++ b/lib/sqlalchemy/sql/schema.py
@@ -0,0 +1,3025 @@
+# sqlalchemy/schema.py
+# Copyright (C) 2005-2013 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
+
+"""The schema module provides the building blocks for database metadata.
+
+Each element within this module describes a database entity which can be
+created and dropped, or is otherwise part of such an entity. Examples include
+tables, columns, sequences, and indexes.
+
+All entities are subclasses of :class:`~sqlalchemy.schema.SchemaItem`, and as
+defined in this module they are intended to be agnostic of any vendor-specific
+constructs.
+
+A collection of entities are grouped into a unit called
+:class:`~sqlalchemy.schema.MetaData`. MetaData serves as a logical grouping of
+schema elements, and can also be associated with an actual database connection
+such that operations involving the contained elements can contact the database
+as needed.
+
+Two of the elements here also build upon their "syntactic" counterparts, which
+are defined in :class:`~sqlalchemy.sql.expression.`, specifically
+:class:`~sqlalchemy.schema.Table` and :class:`~sqlalchemy.schema.Column`.
+Since these objects are part of the SQL expression language, they are usable
+as components in SQL expressions.
+
+"""
+
+import re
+import inspect
+from .. import exc, util, event, inspection
+from ..events import SchemaEventTarget
+from . import visitors
+from . import type_api
+from .base import _bind_or_error, ColumnCollection
+from .elements import ClauseElement, ColumnClause, _truncated_label, \
+ _as_truncated, TextClause, _literal_as_text,\
+ ColumnElement, _find_columns
+from .selectable import TableClause
+import collections
+import sqlalchemy
+from . import ddl
+
+RETAIN_SCHEMA = util.symbol('retain_schema')
+
+
+def _get_table_key(name, schema):
+ if schema is None:
+ return name
+ else:
+ return schema + "." + name
+
+
+def _validate_dialect_kwargs(kwargs, name):
+ # validate remaining kwargs that they all specify DB prefixes
+
+ for k in kwargs:
+ m = re.match('^(.+?)_.*', k)
+ if m is None:
+ raise TypeError("Additional arguments should be "
+ "named <dialectname>_<argument>, got '%s'" % k)
+
+@inspection._self_inspects
+class SchemaItem(SchemaEventTarget, visitors.Visitable):
+ """Base class for items that define a database schema."""
+
+ __visit_name__ = 'schema_item'
+ quote = None
+
+ def _init_items(self, *args):
+ """Initialize the list of child items for this SchemaItem."""
+
+ for item in args:
+ if item is not None:
+ item._set_parent_with_dispatch(self)
+
+ def get_children(self, **kwargs):
+ """used to allow SchemaVisitor access"""
+ return []
+
+ def __repr__(self):
+ return util.generic_repr(self)
+
+ @util.memoized_property
+ def info(self):
+ """Info dictionary associated with the object, allowing user-defined
+ data to be associated with this :class:`.SchemaItem`.
+
+ The dictionary is automatically generated when first accessed.
+ It can also be specified in the constructor of some objects,
+ such as :class:`.Table` and :class:`.Column`.
+
+ """
+ return {}
+
+
+
+class Table(SchemaItem, TableClause):
+ """Represent a table in a database.
+
+ e.g.::
+
+ mytable = Table("mytable", metadata,
+ Column('mytable_id', Integer, primary_key=True),
+ Column('value', String(50))
+ )
+
+ The :class:`.Table` object constructs a unique instance of itself based
+ on its name and optional schema name within the given
+ :class:`.MetaData` object. Calling the :class:`.Table`
+ constructor with the same name and same :class:`.MetaData` argument
+ a second time will return the *same* :class:`.Table` object - in this way
+ the :class:`.Table` constructor acts as a registry function.
+
+ See also:
+
+ :ref:`metadata_describing` - Introduction to database metadata
+
+ Constructor arguments are as follows:
+
+ :param name: The name of this table as represented in the database.
+
+ This property, along with the *schema*, indicates the *singleton
+ identity* of this table in relation to its parent :class:`.MetaData`.
+ Additional calls to :class:`.Table` with the same name, metadata,
+ and schema name will return the same :class:`.Table` object.
+
+ Names which contain no upper case characters
+ will be treated as case insensitive names, and will not be quoted
+ unless they are a reserved word. Names with any number of upper
+ case characters will be quoted and sent exactly. Note that this
+ behavior applies even for databases which standardize upper
+ case names as case insensitive such as Oracle.
+
+ :param metadata: a :class:`.MetaData` object which will contain this
+ table. The metadata is used as a point of association of this table
+ with other tables which are referenced via foreign key. It also
+ may be used to associate this table with a particular
+ :class:`.Connectable`.
+
+ :param \*args: Additional positional arguments are used primarily
+ to add the list of :class:`.Column` objects contained within this
+ table. Similar to the style of a CREATE TABLE statement, other
+ :class:`.SchemaItem` constructs may be added here, including
+ :class:`.PrimaryKeyConstraint`, and :class:`.ForeignKeyConstraint`.
+
+ :param autoload: Defaults to False: the Columns for this table should
+ be reflected from the database. Usually there will be no Column
+ objects in the constructor if this property is set.
+
+ :param autoload_replace: If ``True``, when using ``autoload=True``
+ and ``extend_existing=True``,
+ replace ``Column`` objects already present in the ``Table`` that's
+ in the ``MetaData`` registry with
+ what's reflected. Otherwise, all existing columns will be
+ excluded from the reflection process. Note that this does
+ not impact ``Column`` objects specified in the same call to ``Table``
+ which includes ``autoload``, those always take precedence.
+ Defaults to ``True``.
+
+ .. versionadded:: 0.7.5
+
+ :param autoload_with: If autoload==True, this is an optional Engine
+ or Connection instance to be used for the table reflection. If
+ ``None``, the underlying MetaData's bound connectable will be used.
+
+ :param extend_existing: When ``True``, indicates that if this
+ :class:`.Table` is already present in the given :class:`.MetaData`,
+ apply further arguments within the constructor to the existing
+ :class:`.Table`.
+
+ If ``extend_existing`` or ``keep_existing`` are not set, an error is
+ raised if additional table modifiers are specified when
+ the given :class:`.Table` is already present in the :class:`.MetaData`.
+
+ .. versionchanged:: 0.7.4
+ ``extend_existing`` will work in conjunction
+ with ``autoload=True`` to run a new reflection operation against
+ the database; new :class:`.Column` objects will be produced
+ from database metadata to replace those existing with the same
+ name, and additional :class:`.Column` objects not present
+ in the :class:`.Table` will be added.
+
+ As is always the case with ``autoload=True``, :class:`.Column`
+ objects can be specified in the same :class:`.Table` constructor,
+ which will take precedence. I.e.::
+
+ Table("mytable", metadata,
+ Column('y', Integer),
+ extend_existing=True,
+ autoload=True,
+ autoload_with=engine
+ )
+
+ The above will overwrite all columns within ``mytable`` which
+ are present in the database, except for ``y`` which will be used as is
+ from the above definition. If the ``autoload_replace`` flag
+ is set to False, no existing columns will be replaced.
+
+ :param implicit_returning: True by default - indicates that
+ RETURNING can be used by default to fetch newly inserted primary key
+ values, for backends which support this. Note that
+ create_engine() also provides an implicit_returning flag.
+
+ :param include_columns: A list of strings indicating a subset of
+ columns to be loaded via the ``autoload`` operation; table columns who
+ aren't present in this list will not be represented on the resulting
+ ``Table`` object. Defaults to ``None`` which indicates all columns
+ should be reflected.
+
+ :param info: Optional data dictionary which will be populated into the
+ :attr:`.SchemaItem.info` attribute of this object.
+
+ :param keep_existing: When ``True``, indicates that if this Table
+ is already present in the given :class:`.MetaData`, ignore
+ further arguments within the constructor to the existing
+ :class:`.Table`, and return the :class:`.Table` object as
+ originally created. This is to allow a function that wishes
+ to define a new :class:`.Table` on first call, but on
+ subsequent calls will return the same :class:`.Table`,
+ without any of the declarations (particularly constraints)
+ being applied a second time. Also see extend_existing.
+
+ If extend_existing or keep_existing are not set, an error is
+ raised if additional table modifiers are specified when
+ the given :class:`.Table` is already present in the :class:`.MetaData`.
+
+ :param listeners: A list of tuples of the form ``(<eventname>, <fn>)``
+ which will be passed to :func:`.event.listen` upon construction.
+ This alternate hook to :func:`.event.listen` allows the establishment
+ of a listener function specific to this :class:`.Table` before
+ the "autoload" process begins. Particularly useful for
+ the :meth:`.DDLEvents.column_reflect` event::
+
+ def listen_for_reflect(table, column_info):
+ "handle the column reflection event"
+ # ...
+
+ t = Table(
+ 'sometable',
+ autoload=True,
+ listeners=[
+ ('column_reflect', listen_for_reflect)
+ ])
+
+ :param mustexist: When ``True``, indicates that this Table must already
+ be present in the given :class:`.MetaData` collection, else
+ an exception is raised.
+
+ :param prefixes:
+ A list of strings to insert after CREATE in the CREATE TABLE
+ statement. They will be separated by spaces.
+
+ :param quote: Force quoting of this table's name on or off, corresponding
+ to ``True`` or ``False``. When left at its default of ``None``,
+ the column identifier will be quoted according to whether the name is
+ case sensitive (identifiers with at least one upper case character are
+ treated as case sensitive), or if it's a reserved word. This flag
+ is only needed to force quoting of a reserved word which is not known
+ by the SQLAlchemy dialect.
+
+ :param quote_schema: same as 'quote' but applies to the schema identifier.
+
+ :param schema: The *schema name* for this table, which is required if
+ the table resides in a schema other than the default selected schema
+ for the engine's database connection. Defaults to ``None``.
+
+ :param useexisting: Deprecated. Use extend_existing.
+
+ """
+
+ __visit_name__ = 'table'
+
+ def __new__(cls, *args, **kw):
+ if not args:
+ # python3k pickle seems to call this
+ return object.__new__(cls)
+
+ try:
+ name, metadata, args = args[0], args[1], args[2:]
+ except IndexError:
+ raise TypeError("Table() takes at least two arguments")
+
+ schema = kw.get('schema', None)
+ if schema is None:
+ schema = metadata.schema
+ keep_existing = kw.pop('keep_existing', False)
+ extend_existing = kw.pop('extend_existing', False)
+ if 'useexisting' in kw:
+ msg = "useexisting is deprecated. Use extend_existing."
+ util.warn_deprecated(msg)
+ if extend_existing:
+ msg = "useexisting is synonymous with extend_existing."
+ raise exc.ArgumentError(msg)
+ extend_existing = kw.pop('useexisting', False)
+
+ if keep_existing and extend_existing:
+ msg = "keep_existing and extend_existing are mutually exclusive."
+ raise exc.ArgumentError(msg)
+
+ mustexist = kw.pop('mustexist', False)
+ key = _get_table_key(name, schema)
+ if key in metadata.tables:
+ if not keep_existing and not extend_existing and bool(args):
+ raise exc.InvalidRequestError(
+ "Table '%s' is already defined for this MetaData "
+ "instance. Specify 'extend_existing=True' "
+ "to redefine "
+ "options and columns on an "
+ "existing Table object." % key)
+ table = metadata.tables[key]
+ if extend_existing:
+ table._init_existing(*args, **kw)
+ return table
+ else:
+ if mustexist:
+ raise exc.InvalidRequestError(
+ "Table '%s' not defined" % (key))
+ table = object.__new__(cls)
+ table.dispatch.before_parent_attach(table, metadata)
+ metadata._add_table(name, schema, table)
+ try:
+ table._init(name, metadata, *args, **kw)
+ table.dispatch.after_parent_attach(table, metadata)
+ return table
+ except:
+ #metadata._remove_table(name, schema)
+ raise
+
+ def __init__(self, *args, **kw):
+ """Constructor for :class:`~.schema.Table`.
+
+ This method is a no-op. See the top-level
+ documentation for :class:`~.schema.Table`
+ for constructor arguments.
+
+ """
+ # __init__ is overridden to prevent __new__ from
+ # calling the superclass constructor.
+
+ def _init(self, name, metadata, *args, **kwargs):
+ super(Table, self).__init__(name)
+ self.metadata = metadata
+ self.schema = kwargs.pop('schema', None)
+ if self.schema is None:
+ self.schema = metadata.schema
+ self.quote_schema = kwargs.pop(
+ 'quote_schema', metadata.quote_schema)
+ else:
+ self.quote_schema = kwargs.pop('quote_schema', None)
+
+ self.indexes = set()
+ self.constraints = set()
+ self._columns = ColumnCollection()
+ PrimaryKeyConstraint()._set_parent_with_dispatch(self)
+ self.foreign_keys = set()
+ self._extra_dependencies = set()
+ self.kwargs = {}
+ if self.schema is not None:
+ self.fullname = "%s.%s" % (self.schema, self.name)
+ else:
+ self.fullname = self.name
+
+ autoload = kwargs.pop('autoload', False)
+ autoload_with = kwargs.pop('autoload_with', None)
+ # this argument is only used with _init_existing()
+ kwargs.pop('autoload_replace', True)
+ include_columns = kwargs.pop('include_columns', None)
+
+ self.implicit_returning = kwargs.pop('implicit_returning', True)
+ self.quote = kwargs.pop('quote', None)
+ if 'info' in kwargs:
+ self.info = kwargs.pop('info')
+ if 'listeners' in kwargs:
+ listeners = kwargs.pop('listeners')
+ for evt, fn in listeners:
+ event.listen(self, evt, fn)
+
+ self._prefixes = kwargs.pop('prefixes', [])
+
+ self._extra_kwargs(**kwargs)
+
+ # load column definitions from the database if 'autoload' is defined
+ # we do it after the table is in the singleton dictionary to support
+ # circular foreign keys
+ if autoload:
+ self._autoload(metadata, autoload_with, include_columns)
+
+ # initialize all the column, etc. objects. done after reflection to
+ # allow user-overrides
+ self._init_items(*args)
+
+ def _autoload(self, metadata, autoload_with, include_columns,
+ exclude_columns=()):
+ if self.primary_key.columns:
+ PrimaryKeyConstraint(*[
+ c for c in self.primary_key.columns
+ if c.key in exclude_columns
+ ])._set_parent_with_dispatch(self)
+
+ if autoload_with:
+ autoload_with.run_callable(
+ autoload_with.dialect.reflecttable,
+ self, include_columns, exclude_columns
+ )
+ else:
+ bind = _bind_or_error(metadata,
+ msg="No engine is bound to this Table's MetaData. "
+ "Pass an engine to the Table via "
+ "autoload_with=<someengine>, "
+ "or associate the MetaData with an engine via "
+ "metadata.bind=<someengine>")
+ bind.run_callable(
+ bind.dialect.reflecttable,
+ self, include_columns, exclude_columns
+ )
+
+ @property
+ def _sorted_constraints(self):
+ """Return the set of constraints as a list, sorted by creation
+ order.
+
+ """
+ return sorted(self.constraints, key=lambda c: c._creation_order)
+
+ def _init_existing(self, *args, **kwargs):
+ autoload = kwargs.pop('autoload', False)
+ autoload_with = kwargs.pop('autoload_with', None)
+ autoload_replace = kwargs.pop('autoload_replace', True)
+ schema = kwargs.pop('schema', None)
+ if schema and schema != self.schema:
+ raise exc.ArgumentError(
+ "Can't change schema of existing table from '%s' to '%s'",
+ (self.schema, schema))
+
+ include_columns = kwargs.pop('include_columns', None)
+
+ if include_columns is not None:
+ for c in self.c:
+ if c.name not in include_columns:
+ self._columns.remove(c)
+
+ for key in ('quote', 'quote_schema'):
+ if key in kwargs:
+ setattr(self, key, kwargs.pop(key))
+
+ if 'info' in kwargs:
+ self.info = kwargs.pop('info')
+
+ if autoload:
+ if not autoload_replace:
+ exclude_columns = [c.name for c in self.c]
+ else:
+ exclude_columns = ()
+ self._autoload(
+ self.metadata, autoload_with, include_columns, exclude_columns)
+
+ self._extra_kwargs(**kwargs)
+ self._init_items(*args)
+
+ def _extra_kwargs(self, **kwargs):
+ # validate remaining kwargs that they all specify DB prefixes
+ _validate_dialect_kwargs(kwargs, "Table")
+ self.kwargs.update(kwargs)
+
+ def _init_collections(self):
+ pass
+
+ @util.memoized_property
+ def _autoincrement_column(self):
+ for col in self.primary_key:
+ if col.autoincrement and \
+ col.type._type_affinity is not None and \
+ issubclass(col.type._type_affinity, type_api.INTEGERTYPE._type_affinity) and \
+ (not col.foreign_keys or col.autoincrement == 'ignore_fk') and \
+ isinstance(col.default, (type(None), Sequence)) and \
+ (col.server_default is None or col.server_default.reflected):
+ return col
+
+ @property
+ def key(self):
+ return _get_table_key(self.name, self.schema)
+
+ def __repr__(self):
+ return "Table(%s)" % ', '.join(
+ [repr(self.name)] + [repr(self.metadata)] +
+ [repr(x) for x in self.columns] +
+ ["%s=%s" % (k, repr(getattr(self, k))) for k in ['schema']])
+
+ def __str__(self):
+ return _get_table_key(self.description, self.schema)
+
+ @property
+ def bind(self):
+ """Return the connectable associated with this Table."""
+
+ return self.metadata and self.metadata.bind or None
+
+ def add_is_dependent_on(self, table):
+ """Add a 'dependency' for this Table.
+
+ This is another Table object which must be created
+ first before this one can, or dropped after this one.
+
+ Usually, dependencies between tables are determined via
+ ForeignKey objects. However, for other situations that
+ create dependencies outside of foreign keys (rules, inheriting),
+ this method can manually establish such a link.
+
+ """
+ self._extra_dependencies.add(table)
+
+ def append_column(self, column):
+ """Append a :class:`~.schema.Column` to this :class:`~.schema.Table`.
+
+ The "key" of the newly added :class:`~.schema.Column`, i.e. the
+ value of its ``.key`` attribute, will then be available
+ in the ``.c`` collection of this :class:`~.schema.Table`, and the
+ column definition will be included in any CREATE TABLE, SELECT,
+ UPDATE, etc. statements generated from this :class:`~.schema.Table`
+ construct.
+
+ Note that this does **not** change the definition of the table
+ as it exists within any underlying database, assuming that
+ table has already been created in the database. Relational
+ databases support the addition of columns to existing tables
+ using the SQL ALTER command, which would need to be
+ emitted for an already-existing table that doesn't contain
+ the newly added column.
+
+ """
+
+ column._set_parent_with_dispatch(self)
+
+ def append_constraint(self, constraint):
+ """Append a :class:`~.schema.Constraint` to this
+ :class:`~.schema.Table`.
+
+ This has the effect of the constraint being included in any
+ future CREATE TABLE statement, assuming specific DDL creation
+ events have not been associated with the given
+ :class:`~.schema.Constraint` object.
+
+ Note that this does **not** produce the constraint within the
+ relational database automatically, for a table that already exists
+ in the database. To add a constraint to an
+ existing relational database table, the SQL ALTER command must
+ be used. SQLAlchemy also provides the
+ :class:`.AddConstraint` construct which can produce this SQL when
+ invoked as an executable clause.
+
+ """
+
+ constraint._set_parent_with_dispatch(self)
+
+ def append_ddl_listener(self, event_name, listener):
+ """Append a DDL event listener to this ``Table``.
+
+ .. deprecated:: 0.7
+ See :class:`.DDLEvents`.
+
+ """
+
+ def adapt_listener(target, connection, **kw):
+ listener(event_name, target, connection)
+
+ event.listen(self, "" + event_name.replace('-', '_'), adapt_listener)
+
+ def _set_parent(self, metadata):
+ metadata._add_table(self.name, self.schema, self)
+ self.metadata = metadata
+
+ def get_children(self, column_collections=True,
+ schema_visitor=False, **kw):
+ if not schema_visitor:
+ return TableClause.get_children(
+ self, column_collections=column_collections, **kw)
+ else:
+ if column_collections:
+ return list(self.columns)
+ else:
+ return []
+
+ def exists(self, bind=None):
+ """Return True if this table exists."""
+
+ if bind is None:
+ bind = _bind_or_error(self)
+
+ return bind.run_callable(bind.dialect.has_table,
+ self.name, schema=self.schema)
+
+ def create(self, bind=None, checkfirst=False):
+ """Issue a ``CREATE`` statement for this
+ :class:`.Table`, using the given :class:`.Connectable`
+ for connectivity.
+
+ See also :meth:`.MetaData.create_all`.
+
+ """
+
+ if bind is None:
+ bind = _bind_or_error(self)
+ bind._run_visitor(ddl.SchemaGenerator,
+ self,
+ checkfirst=checkfirst)
+
+ def drop(self, bind=None, checkfirst=False):
+ """Issue a ``DROP`` statement for this
+ :class:`.Table`, using the given :class:`.Connectable`
+ for connectivity.
+
+ See also :meth:`.MetaData.drop_all`.
+
+ """
+ if bind is None:
+ bind = _bind_or_error(self)
+ bind._run_visitor(ddl.SchemaDropper,
+ self,
+ checkfirst=checkfirst)
+
+ def tometadata(self, metadata, schema=RETAIN_SCHEMA):
+ """Return a copy of this :class:`.Table` associated with a different
+ :class:`.MetaData`.
+
+ E.g.::
+
+ some_engine = create_engine("sqlite:///some.db")
+
+ # create two metadata
+ meta1 = MetaData()
+ meta2 = MetaData()
+
+ # load 'users' from the sqlite engine
+ users_table = Table('users', meta1, autoload=True,
+ autoload_with=some_engine)
+
+ # create the same Table object for the plain metadata
+ users_table_2 = users_table.tometadata(meta2)
+
+ :param metadata: Target :class:`.MetaData` object.
+ :param schema: Optional string name of a target schema, or
+ ``None`` for no schema. The :class:`.Table` object will be
+ given this schema name upon copy. Defaults to the special
+ symbol :attr:`.RETAIN_SCHEMA` which indicates no change should be
+ made to the schema name of the resulting :class:`.Table`.
+
+ """
+
+ if schema is RETAIN_SCHEMA:
+ schema = self.schema
+ elif schema is None:
+ schema = metadata.schema
+ key = _get_table_key(self.name, schema)
+ if key in metadata.tables:
+ util.warn("Table '%s' already exists within the given "
+ "MetaData - not copying." % self.description)
+ return metadata.tables[key]
+
+ args = []
+ for c in self.columns:
+ args.append(c.copy(schema=schema))
+ table = Table(
+ self.name, metadata, schema=schema,
+ *args, **self.kwargs
+ )
+ for c in self.constraints:
+ table.append_constraint(c.copy(schema=schema, target_table=table))
+
+ for index in self.indexes:
+ # skip indexes that would be generated
+ # by the 'index' flag on Column
+ if len(index.columns) == 1 and \
+ list(index.columns)[0].index:
+ continue
+ Index(index.name,
+ unique=index.unique,
+ *[table.c[col] for col in index.columns.keys()],
+ **index.kwargs)
+ table.dispatch._update(self.dispatch)
+ return table
+
+
+class Column(SchemaItem, ColumnClause):
+ """Represents a column in a database table."""
+
+ __visit_name__ = 'column'
+
+ def __init__(self, *args, **kwargs):
+ """
+ Construct a new ``Column`` object.
+
+ :param name: The name of this column as represented in the database.
+ This argument may be the first positional argument, or specified
+ via keyword.
+
+ Names which contain no upper case characters
+ will be treated as case insensitive names, and will not be quoted
+ unless they are a reserved word. Names with any number of upper
+ case characters will be quoted and sent exactly. Note that this
+ behavior applies even for databases which standardize upper
+ case names as case insensitive such as Oracle.
+
+ The name field may be omitted at construction time and applied
+ later, at any time before the Column is associated with a
+ :class:`.Table`. This is to support convenient
+ usage within the :mod:`~sqlalchemy.ext.declarative` extension.
+
+ :param type\_: The column's type, indicated using an instance which
+ subclasses :class:`~sqlalchemy.types.TypeEngine`. If no arguments
+ are required for the type, the class of the type can be sent
+ as well, e.g.::
+
+ # use a type with arguments
+ Column('data', String(50))
+
+ # use no arguments
+ Column('level', Integer)
+
+ The ``type`` argument may be the second positional argument
+ or specified by keyword.
+
+ If the ``type`` is ``None`` or is omitted, it will first default to the special
+ type :class:`.NullType`. If and when this :class:`.Column` is
+ made to refer to another column using :class:`.ForeignKey`
+ and/or :class:`.ForeignKeyConstraint`, the type of the remote-referenced
+ column will be copied to this column as well, at the moment that
+ the foreign key is resolved against that remote :class:`.Column`
+ object.
+
+ .. versionchanged:: 0.9.0
+ Support for propagation of type to a :class:`.Column` from its
+ :class:`.ForeignKey` object has been improved and should be
+ more reliable and timely.
+
+ :param \*args: Additional positional arguments include various
+ :class:`.SchemaItem` derived constructs which will be applied
+ as options to the column. These include instances of
+ :class:`.Constraint`, :class:`.ForeignKey`, :class:`.ColumnDefault`,
+ and :class:`.Sequence`. In some cases an equivalent keyword
+ argument is available such as ``server_default``, ``default``
+ and ``unique``.
+
+ :param autoincrement: This flag may be set to ``False`` to
+ indicate an integer primary key column that should not be
+ considered to be the "autoincrement" column, that is
+ the integer primary key column which generates values
+ implicitly upon INSERT and whose value is usually returned
+ via the DBAPI cursor.lastrowid attribute. It defaults
+ to ``True`` to satisfy the common use case of a table
+ with a single integer primary key column. If the table
+ has a composite primary key consisting of more than one
+ integer column, set this flag to True only on the
+ column that should be considered "autoincrement".
+
+ The setting *only* has an effect for columns which are:
+
+ * Integer derived (i.e. INT, SMALLINT, BIGINT).
+
+ * Part of the primary key
+
+ * Are not referenced by any foreign keys, unless
+ the value is specified as ``'ignore_fk'``
+
+ .. versionadded:: 0.7.4
+
+ * have no server side or client side defaults (with the exception
+ of Postgresql SERIAL).
+
+ The setting has these two effects on columns that meet the
+ above criteria:
+
+ * DDL issued for the column will include database-specific
+ keywords intended to signify this column as an
+ "autoincrement" column, such as AUTO INCREMENT on MySQL,
+ SERIAL on Postgresql, and IDENTITY on MS-SQL. It does
+ *not* issue AUTOINCREMENT for SQLite since this is a
+ special SQLite flag that is not required for autoincrementing
+ behavior. See the SQLite dialect documentation for
+ information on SQLite's AUTOINCREMENT.
+
+ * The column will be considered to be available as
+ cursor.lastrowid or equivalent, for those dialects which
+ "post fetch" newly inserted identifiers after a row has
+ been inserted (SQLite, MySQL, MS-SQL). It does not have
+ any effect in this regard for databases that use sequences
+ to generate primary key identifiers (i.e. Firebird, Postgresql,
+ Oracle).
+
+ .. versionchanged:: 0.7.4
+ ``autoincrement`` accepts a special value ``'ignore_fk'``
+ to indicate that autoincrementing status regardless of foreign
+ key references. This applies to certain composite foreign key
+ setups, such as the one demonstrated in the ORM documentation
+ at :ref:`post_update`.
+
+ :param default: A scalar, Python callable, or
+ :class:`.ColumnElement` expression representing the
+ *default value* for this column, which will be invoked upon insert
+ if this column is otherwise not specified in the VALUES clause of
+ the insert. This is a shortcut to using :class:`.ColumnDefault` as
+ a positional argument; see that class for full detail on the
+ structure of the argument.
+
+ Contrast this argument to ``server_default`` which creates a
+ default generator on the database side.
+
+ :param doc: optional String that can be used by the ORM or similar
+ to document attributes. This attribute does not render SQL
+ comments (a future attribute 'comment' will achieve that).
+
+ :param key: An optional string identifier which will identify this
+ ``Column`` object on the :class:`.Table`. When a key is provided,
+ this is the only identifier referencing the ``Column`` within the
+ application, including ORM attribute mapping; the ``name`` field
+ is used only when rendering SQL.
+
+ :param index: When ``True``, indicates that the column is indexed.
+ This is a shortcut for using a :class:`.Index` construct on the
+ table. To specify indexes with explicit names or indexes that
+ contain multiple columns, use the :class:`.Index` construct
+ instead.
+
+ :param info: Optional data dictionary which will be populated into the
+ :attr:`.SchemaItem.info` attribute of this object.
+
+ :param nullable: If set to the default of ``True``, indicates the
+ column will be rendered as allowing NULL, else it's rendered as
+ NOT NULL. This parameter is only used when issuing CREATE TABLE
+ statements.
+
+ :param onupdate: A scalar, Python callable, or
+ :class:`~sqlalchemy.sql.expression.ClauseElement` representing a
+ default value to be applied to the column within UPDATE
+ statements, which wil be invoked upon update if this column is not
+ present in the SET clause of the update. This is a shortcut to
+ using :class:`.ColumnDefault` as a positional argument with
+ ``for_update=True``.
+
+ :param primary_key: If ``True``, marks this column as a primary key
+ column. Multiple columns can have this flag set to specify
+ composite primary keys. As an alternative, the primary key of a
+ :class:`.Table` can be specified via an explicit
+ :class:`.PrimaryKeyConstraint` object.
+
+ :param server_default: A :class:`.FetchedValue` instance, str, Unicode
+ or :func:`~sqlalchemy.sql.expression.text` construct representing
+ the DDL DEFAULT value for the column.
+
+ String types will be emitted as-is, surrounded by single quotes::
+
+ Column('x', Text, server_default="val")
+
+ x TEXT DEFAULT 'val'
+
+ A :func:`~sqlalchemy.sql.expression.text` expression will be
+ rendered as-is, without quotes::
+
+ Column('y', DateTime, server_default=text('NOW()'))0
+
+ y DATETIME DEFAULT NOW()
+
+ Strings and text() will be converted into a :class:`.DefaultClause`
+ object upon initialization.
+
+ Use :class:`.FetchedValue` to indicate that an already-existing
+ column will generate a default value on the database side which
+ will be available to SQLAlchemy for post-fetch after inserts. This
+ construct does not specify any DDL and the implementation is left
+ to the database, such as via a trigger.
+
+ :param server_onupdate: A :class:`.FetchedValue` instance
+ representing a database-side default generation function. This
+ indicates to SQLAlchemy that a newly generated value will be
+ available after updates. This construct does not specify any DDL
+ and the implementation is left to the database, such as via a
+ trigger.
+
+ :param quote: Force quoting of this column's name on or off,
+ corresponding to ``True`` or ``False``. When left at its default
+ of ``None``, the column identifier will be quoted according to
+ whether the name is case sensitive (identifiers with at least one
+ upper case character are treated as case sensitive), or if it's a
+ reserved word. This flag is only needed to force quoting of a
+ reserved word which is not known by the SQLAlchemy dialect.
+
+ :param unique: When ``True``, indicates that this column contains a
+ unique constraint, or if ``index`` is ``True`` as well, indicates
+ that the :class:`.Index` should be created with the unique flag.
+ To specify multiple columns in the constraint/index or to specify
+ an explicit name, use the :class:`.UniqueConstraint` or
+ :class:`.Index` constructs explicitly.
+
+ """
+
+ name = kwargs.pop('name', None)
+ type_ = kwargs.pop('type_', None)
+ args = list(args)
+ if args:
+ if isinstance(args[0], util.string_types):
+ if name is not None:
+ raise exc.ArgumentError(
+ "May not pass name positionally and as a keyword.")
+ name = args.pop(0)
+ if args:
+ coltype = args[0]
+
+ if hasattr(coltype, "_sqla_type"):
+ if type_ is not None:
+ raise exc.ArgumentError(
+ "May not pass type_ positionally and as a keyword.")
+ type_ = args.pop(0)
+
+ super(Column, self).__init__(name, type_)
+ self.key = kwargs.pop('key', name)
+ self.primary_key = kwargs.pop('primary_key', False)
+ self.nullable = kwargs.pop('nullable', not self.primary_key)
+ self.default = kwargs.pop('default', None)
+ self.server_default = kwargs.pop('server_default', None)
+ self.server_onupdate = kwargs.pop('server_onupdate', None)
+ self.index = kwargs.pop('index', None)
+ self.unique = kwargs.pop('unique', None)
+ self.quote = kwargs.pop('quote', None)
+ self.doc = kwargs.pop('doc', None)
+ self.onupdate = kwargs.pop('onupdate', None)
+ self.autoincrement = kwargs.pop('autoincrement', True)
+ self.constraints = set()
+ self.foreign_keys = set()
+
+ # check if this Column is proxying another column
+ if '_proxies' in kwargs:
+ self._proxies = kwargs.pop('_proxies')
+ # otherwise, add DDL-related events
+ elif isinstance(self.type, SchemaEventTarget):
+ self.type._set_parent_with_dispatch(self)
+
+ if self.default is not None:
+ if isinstance(self.default, (ColumnDefault, Sequence)):
+ args.append(self.default)
+ else:
+ if getattr(self.type, '_warn_on_bytestring', False):
+ if isinstance(self.default, util.binary_type):
+ util.warn("Unicode column received non-unicode "
+ "default value.")
+ args.append(ColumnDefault(self.default))
+
+ if self.server_default is not None:
+ if isinstance(self.server_default, FetchedValue):
+ args.append(self.server_default._as_for_update(False))
+ else:
+ args.append(DefaultClause(self.server_default))
+
+ if self.onupdate is not None:
+ if isinstance(self.onupdate, (ColumnDefault, Sequence)):
+ args.append(self.onupdate)
+ else:
+ args.append(ColumnDefault(self.onupdate, for_update=True))
+
+ if self.server_onupdate is not None:
+ if isinstance(self.server_onupdate, FetchedValue):
+ args.append(self.server_onupdate._as_for_update(True))
+ else:
+ args.append(DefaultClause(self.server_onupdate,
+ for_update=True))
+ self._init_items(*args)
+
+ util.set_creation_order(self)
+
+ if 'info' in kwargs:
+ self.info = kwargs.pop('info')
+
+ if kwargs:
+ raise exc.ArgumentError(
+ "Unknown arguments passed to Column: " + repr(list(kwargs)))
+
+ def __str__(self):
+ if self.name is None:
+ return "(no name)"
+ elif self.table is not None:
+ if self.table.named_with_column:
+ return (self.table.description + "." + self.description)
+ else:
+ return self.description
+ else:
+ return self.description
+
+ def references(self, column):
+ """Return True if this Column references the given column via foreign
+ key."""
+
+ for fk in self.foreign_keys:
+ if fk.column.proxy_set.intersection(column.proxy_set):
+ return True
+ else:
+ return False
+
+ def append_foreign_key(self, fk):
+ fk._set_parent_with_dispatch(self)
+
+ def __repr__(self):
+ kwarg = []
+ if self.key != self.name:
+ kwarg.append('key')
+ if self.primary_key:
+ kwarg.append('primary_key')
+ if not self.nullable:
+ kwarg.append('nullable')
+ if self.onupdate:
+ kwarg.append('onupdate')
+ if self.default:
+ kwarg.append('default')
+ if self.server_default:
+ kwarg.append('server_default')
+ return "Column(%s)" % ', '.join(
+ [repr(self.name)] + [repr(self.type)] +
+ [repr(x) for x in self.foreign_keys if x is not None] +
+ [repr(x) for x in self.constraints] +
+ [(self.table is not None and "table=<%s>" %
+ self.table.description or "table=None")] +
+ ["%s=%s" % (k, repr(getattr(self, k))) for k in kwarg])
+
+ def _set_parent(self, table):
+ if not self.name:
+ raise exc.ArgumentError(
+ "Column must be constructed with a non-blank name or "
+ "assign a non-blank .name before adding to a Table.")
+ if self.key is None:
+ self.key = self.name
+
+ existing = getattr(self, 'table', None)
+ if existing is not None and existing is not table:
+ raise exc.ArgumentError(
+ "Column object already assigned to Table '%s'" %
+ existing.description)
+
+ if self.key in table._columns:
+ col = table._columns.get(self.key)
+ if col is not self:
+ for fk in col.foreign_keys:
+ table.foreign_keys.remove(fk)
+ if fk.constraint in table.constraints:
+ # this might have been removed
+ # already, if it's a composite constraint
+ # and more than one col being replaced
+ table.constraints.remove(fk.constraint)
+
+ table._columns.replace(self)
+
+ if self.primary_key:
+ table.primary_key._replace(self)
+ Table._autoincrement_column._reset(table)
+ elif self.key in table.primary_key:
+ raise exc.ArgumentError(
+ "Trying to redefine primary-key column '%s' as a "
+ "non-primary-key column on table '%s'" % (
+ self.key, table.fullname))
+ self.table = table
+
+ if self.index:
+ if isinstance(self.index, util.string_types):
+ raise exc.ArgumentError(
+ "The 'index' keyword argument on Column is boolean only. "
+ "To create indexes with a specific name, create an "
+ "explicit Index object external to the Table.")
+ Index(_truncated_label('ix_%s' % self._label),
+ self, unique=self.unique)
+ elif self.unique:
+ if isinstance(self.unique, util.string_types):
+ raise exc.ArgumentError(
+ "The 'unique' keyword argument on Column is boolean "
+ "only. To create unique constraints or indexes with a "
+ "specific name, append an explicit UniqueConstraint to "
+ "the Table's list of elements, or create an explicit "
+ "Index object external to the Table.")
+ table.append_constraint(UniqueConstraint(self.key))
+
+ fk_key = (table.key, self.key)
+ if fk_key in self.table.metadata._fk_memos:
+ for fk in self.table.metadata._fk_memos[fk_key]:
+ fk._set_remote_table(table)
+
+ def _on_table_attach(self, fn):
+ if self.table is not None:
+ fn(self, self.table)
+ event.listen(self, 'after_parent_attach', fn)
+
+ def copy(self, **kw):
+ """Create a copy of this ``Column``, unitialized.
+
+ This is used in ``Table.tometadata``.
+
+ """
+
+ # Constraint objects plus non-constraint-bound ForeignKey objects
+ args = \
+ [c.copy(**kw) for c in self.constraints] + \
+ [c.copy(**kw) for c in self.foreign_keys if not c.constraint]
+
+ type_ = self.type
+ if isinstance(type_, SchemaEventTarget):
+ type_ = type_.copy(**kw)
+
+ c = self._constructor(
+ name=self.name,
+ type_=type_,
+ key=self.key,
+ primary_key=self.primary_key,
+ nullable=self.nullable,
+ unique=self.unique,
+ quote=self.quote,
+ index=self.index,
+ autoincrement=self.autoincrement,
+ default=self.default,
+ server_default=self.server_default,
+ onupdate=self.onupdate,
+ server_onupdate=self.server_onupdate,
+ info=self.info,
+ doc=self.doc,
+ *args
+ )
+ c.dispatch._update(self.dispatch)
+ return c
+
+ def _make_proxy(self, selectable, name=None, key=None,
+ name_is_truncatable=False, **kw):
+ """Create a *proxy* for this column.
+
+ This is a copy of this ``Column`` referenced by a different parent
+ (such as an alias or select statement). The column should
+ be used only in select scenarios, as its full DDL/default
+ information is not transferred.
+
+ """
+ fk = [ForeignKey(f.column, _constraint=f.constraint)
+ for f in self.foreign_keys]
+ if name is None and self.name is None:
+ raise exc.InvalidRequestError("Cannot initialize a sub-selectable"
+ " with this Column object until it's 'name' has "
+ "been assigned.")
+ try:
+ c = self._constructor(
+ _as_truncated(name or self.name) if \
+ name_is_truncatable else (name or self.name),
+ self.type,
+ key=key if key else name if name else self.key,
+ primary_key=self.primary_key,
+ nullable=self.nullable,
+ quote=self.quote,
+ _proxies=[self], *fk)
+ except TypeError:
+ util.raise_from_cause(
+ TypeError(
+ "Could not create a copy of this %r object. "
+ "Ensure the class includes a _constructor() "
+ "attribute or method which accepts the "
+ "standard Column constructor arguments, or "
+ "references the Column class itself." % self.__class__)
+ )
+
+ c.table = selectable
+ selectable._columns.add(c)
+ if selectable._is_clone_of is not None:
+ c._is_clone_of = selectable._is_clone_of.columns[c.key]
+ if self.primary_key:
+ selectable.primary_key.add(c)
+ c.dispatch.after_parent_attach(c, selectable)
+ return c
+
+ def get_children(self, schema_visitor=False, **kwargs):
+ if schema_visitor:
+ return [x for x in (self.default, self.onupdate)
+ if x is not None] + \
+ list(self.foreign_keys) + list(self.constraints)
+ else:
+ return ColumnClause.get_children(self, **kwargs)
+
+
+class ForeignKey(SchemaItem):
+ """Defines a dependency between two columns.
+
+ ``ForeignKey`` is specified as an argument to a :class:`.Column` object,
+ e.g.::
+
+ t = Table("remote_table", metadata,
+ Column("remote_id", ForeignKey("main_table.id"))
+ )
+
+ Note that ``ForeignKey`` is only a marker object that defines
+ a dependency between two columns. The actual constraint
+ is in all cases represented by the :class:`.ForeignKeyConstraint`
+ object. This object will be generated automatically when
+ a ``ForeignKey`` is associated with a :class:`.Column` which
+ in turn is associated with a :class:`.Table`. Conversely,
+ when :class:`.ForeignKeyConstraint` is applied to a :class:`.Table`,
+ ``ForeignKey`` markers are automatically generated to be
+ present on each associated :class:`.Column`, which are also
+ associated with the constraint object.
+
+ Note that you cannot define a "composite" foreign key constraint,
+ that is a constraint between a grouping of multiple parent/child
+ columns, using ``ForeignKey`` objects. To define this grouping,
+ the :class:`.ForeignKeyConstraint` object must be used, and applied
+ to the :class:`.Table`. The associated ``ForeignKey`` objects
+ are created automatically.
+
+ The ``ForeignKey`` objects associated with an individual
+ :class:`.Column` object are available in the `foreign_keys` collection
+ of that column.
+
+ Further examples of foreign key configuration are in
+ :ref:`metadata_foreignkeys`.
+
+ """
+
+ __visit_name__ = 'foreign_key'
+
+ def __init__(self, column, _constraint=None, use_alter=False, name=None,
+ onupdate=None, ondelete=None, deferrable=None,
+ schema=None,
+ initially=None, link_to_name=False, match=None):
+ """
+ Construct a column-level FOREIGN KEY.
+
+ The :class:`.ForeignKey` object when constructed generates a
+ :class:`.ForeignKeyConstraint` which is associated with the parent
+ :class:`.Table` object's collection of constraints.
+
+ :param column: A single target column for the key relationship. A
+ :class:`.Column` object or a column name as a string:
+ ``tablename.columnkey`` or ``schema.tablename.columnkey``.
+ ``columnkey`` is the ``key`` which has been assigned to the column
+ (defaults to the column name itself), unless ``link_to_name`` is
+ ``True`` in which case the rendered name of the column is used.
+
+ .. versionadded:: 0.7.4
+ Note that if the schema name is not included, and the
+ underlying :class:`.MetaData` has a "schema", that value will
+ be used.
+
+ :param name: Optional string. An in-database name for the key if
+ `constraint` is not provided.
+
+ :param onupdate: Optional string. If set, emit ON UPDATE <value> when
+ issuing DDL for this constraint. Typical values include CASCADE,
+ DELETE and RESTRICT.
+
+ :param ondelete: Optional string. If set, emit ON DELETE <value> when
+ issuing DDL for this constraint. Typical values include CASCADE,
+ DELETE and RESTRICT.
+
+ :param deferrable: Optional bool. If set, emit DEFERRABLE or NOT
+ DEFERRABLE when issuing DDL for this constraint.
+
+ :param initially: Optional string. If set, emit INITIALLY <value> when
+ issuing DDL for this constraint.
+
+ :param link_to_name: if True, the string name given in ``column`` is
+ the rendered name of the referenced column, not its locally
+ assigned ``key``.
+
+ :param use_alter: passed to the underlying
+ :class:`.ForeignKeyConstraint` to indicate the constraint should be
+ generated/dropped externally from the CREATE TABLE/ DROP TABLE
+ statement. See that classes' constructor for details.
+
+ :param match: Optional string. If set, emit MATCH <value> when issuing
+ DDL for this constraint. Typical values include SIMPLE, PARTIAL
+ and FULL.
+
+ """
+
+ self._colspec = column
+
+ # the linked ForeignKeyConstraint.
+ # ForeignKey will create this when parent Column
+ # is attached to a Table, *or* ForeignKeyConstraint
+ # object passes itself in when creating ForeignKey
+ # markers.
+ self.constraint = _constraint
+ self.parent = None
+ self.use_alter = use_alter
+ self.name = name
+ self.onupdate = onupdate
+ self.ondelete = ondelete
+ self.deferrable = deferrable
+ self.initially = initially
+ self.link_to_name = link_to_name
+ self.match = match
+
+ def __repr__(self):
+ return "ForeignKey(%r)" % self._get_colspec()
+
+ def copy(self, schema=None):
+ """Produce a copy of this :class:`.ForeignKey` object.
+
+ The new :class:`.ForeignKey` will not be bound
+ to any :class:`.Column`.
+
+ This method is usually used by the internal
+ copy procedures of :class:`.Column`, :class:`.Table`,
+ and :class:`.MetaData`.
+
+ :param schema: The returned :class:`.ForeignKey` will
+ reference the original table and column name, qualified
+ by the given string schema name.
+
+ """
+
+ fk = ForeignKey(
+ self._get_colspec(schema=schema),
+ use_alter=self.use_alter,
+ name=self.name,
+ onupdate=self.onupdate,
+ ondelete=self.ondelete,
+ deferrable=self.deferrable,
+ initially=self.initially,
+ link_to_name=self.link_to_name,
+ match=self.match
+ )
+ fk.dispatch._update(self.dispatch)
+ return fk
+
+ def _get_colspec(self, schema=None):
+ """Return a string based 'column specification' for this
+ :class:`.ForeignKey`.
+
+ This is usually the equivalent of the string-based "tablename.colname"
+ argument first passed to the object's constructor.
+
+ """
+ if schema:
+ return schema + "." + self.column.table.name + \
+ "." + self.column.key
+ elif isinstance(self._colspec, util.string_types):
+ return self._colspec
+ elif hasattr(self._colspec, '__clause_element__'):
+ _column = self._colspec.__clause_element__()
+ else:
+ _column = self._colspec
+
+ return "%s.%s" % (_column.table.fullname, _column.key)
+
+
+ target_fullname = property(_get_colspec)
+
+ def references(self, table):
+ """Return True if the given :class:`.Table` is referenced by this
+ :class:`.ForeignKey`."""
+
+ return table.corresponding_column(self.column) is not None
+
+ def get_referent(self, table):
+ """Return the :class:`.Column` in the given :class:`.Table`
+ referenced by this :class:`.ForeignKey`.
+
+ Returns None if this :class:`.ForeignKey` does not reference the given
+ :class:`.Table`.
+
+ """
+
+ return table.corresponding_column(self.column)
+
+ @util.memoized_property
+ def _column_tokens(self):
+ """parse a string-based _colspec into its component parts."""
+
+ m = self._colspec.split('.')
+ if m is None:
+ raise exc.ArgumentError(
+ "Invalid foreign key column specification: %s" %
+ self._colspec)
+ if (len(m) == 1):
+ tname = m.pop()
+ colname = None
+ else:
+ colname = m.pop()
+ tname = m.pop()
+
+ # A FK between column 'bar' and table 'foo' can be
+ # specified as 'foo', 'foo.bar', 'dbo.foo.bar',
+ # 'otherdb.dbo.foo.bar'. Once we have the column name and
+ # the table name, treat everything else as the schema
+ # name. Some databases (e.g. Sybase) support
+ # inter-database foreign keys. See tickets#1341 and --
+ # indirectly related -- Ticket #594. This assumes that '.'
+ # will never appear *within* any component of the FK.
+
+ if (len(m) > 0):
+ schema = '.'.join(m)
+ else:
+ schema = None
+ return schema, tname, colname
+
+ def _table_key(self):
+ if isinstance(self._colspec, util.string_types):
+ schema, tname, colname = self._column_tokens
+ return _get_table_key(tname, schema)
+ elif hasattr(self._colspec, '__clause_element__'):
+ _column = self._colspec.__clause_element__()
+ else:
+ _column = self._colspec
+
+ if _column.table is None:
+ return None
+ else:
+ return _column.table.key
+
+ def _resolve_col_tokens(self):
+ if self.parent is None:
+ raise exc.InvalidRequestError(
+ "this ForeignKey object does not yet have a "
+ "parent Column associated with it.")
+
+ elif self.parent.table is None:
+ raise exc.InvalidRequestError(
+ "this ForeignKey's parent column is not yet associated "
+ "with a Table.")
+
+ parenttable = self.parent.table
+
+ # assertion, can be commented out.
+ # basically Column._make_proxy() sends the actual
+ # target Column to the ForeignKey object, so the
+ # string resolution here is never called.
+ for c in self.parent.base_columns:
+ if isinstance(c, Column):
+ assert c.table is parenttable
+ break
+ else:
+ assert False
+ ######################
+
+ schema, tname, colname = self._column_tokens
+
+ if schema is None and parenttable.metadata.schema is not None:
+ schema = parenttable.metadata.schema
+
+ tablekey = _get_table_key(tname, schema)
+ return parenttable, tablekey, colname
+
+
+ def _link_to_col_by_colstring(self, parenttable, table, colname):
+ if not hasattr(self.constraint, '_referred_table'):
+ self.constraint._referred_table = table
+ else:
+ assert self.constraint._referred_table is table
+
+ _column = None
+ if colname is None:
+ # colname is None in the case that ForeignKey argument
+ # was specified as table name only, in which case we
+ # match the column name to the same column on the
+ # parent.
+ key = self.parent
+ _column = table.c.get(self.parent.key, None)
+ elif self.link_to_name:
+ key = colname
+ for c in table.c:
+ if c.name == colname:
+ _column = c
+ else:
+ key = colname
+ _column = table.c.get(colname, None)
+
+ if _column is None:
+ raise exc.NoReferencedColumnError(
+ "Could not initialize target column for ForeignKey '%s' on table '%s': "
+ "table '%s' has no column named '%s'" % (
+ self._colspec, parenttable.name, table.name, key),
+ table.name, key)
+
+ self._set_target_column(_column)
+
+ def _set_target_column(self, column):
+ # propagate TypeEngine to parent if it didn't have one
+ if self.parent.type._isnull:
+ self.parent.type = column.type
+
+ # super-edgy case, if other FKs point to our column,
+ # they'd get the type propagated out also.
+ if isinstance(self.parent.table, Table):
+ fk_key = (self.parent.table.key, self.parent.key)
+ if fk_key in self.parent.table.metadata._fk_memos:
+ for fk in self.parent.table.metadata._fk_memos[fk_key]:
+ if fk.parent.type._isnull:
+ fk.parent.type = column.type
+
+ self.column = column
+
+ @util.memoized_property
+ def column(self):
+ """Return the target :class:`.Column` referenced by this
+ :class:`.ForeignKey`.
+
+ If no target column has been established, an exception
+ is raised.
+
+ .. versionchanged:: 0.9.0
+ Foreign key target column resolution now occurs as soon as both
+ the ForeignKey object and the remote Column to which it refers
+ are both associated with the same MetaData object.
+
+ """
+
+ if isinstance(self._colspec, util.string_types):
+
+ parenttable, tablekey, colname = self._resolve_col_tokens()
+
+ if tablekey not in parenttable.metadata:
+ raise exc.NoReferencedTableError(
+ "Foreign key associated with column '%s' could not find "
+ "table '%s' with which to generate a "
+ "foreign key to target column '%s'" %
+ (self.parent, tablekey, colname),
+ tablekey)
+ elif parenttable.key not in parenttable.metadata:
+ raise exc.InvalidRequestError(
+ "Table %s is no longer associated with its "
+ "parent MetaData" % parenttable)
+ else:
+ raise exc.NoReferencedColumnError(
+ "Could not initialize target column for "
+ "ForeignKey '%s' on table '%s': "
+ "table '%s' has no column named '%s'" % (
+ self._colspec, parenttable.name, tablekey, colname),
+ tablekey, colname)
+ elif hasattr(self._colspec, '__clause_element__'):
+ _column = self._colspec.__clause_element__()
+ return _column
+ else:
+ _column = self._colspec
+ return _column
+
+ def _set_parent(self, column):
+ if self.parent is not None and self.parent is not column:
+ raise exc.InvalidRequestError(
+ "This ForeignKey already has a parent !")
+ self.parent = column
+ self.parent.foreign_keys.add(self)
+ self.parent._on_table_attach(self._set_table)
+
+ def _set_remote_table(self, table):
+ parenttable, tablekey, colname = self._resolve_col_tokens()
+ self._link_to_col_by_colstring(parenttable, table, colname)
+ self.constraint._validate_dest_table(table)
+
+ def _remove_from_metadata(self, metadata):
+ parenttable, table_key, colname = self._resolve_col_tokens()
+ fk_key = (table_key, colname)
+
+ if self in metadata._fk_memos[fk_key]:
+ # TODO: no test coverage for self not in memos
+ metadata._fk_memos[fk_key].remove(self)
+
+ def _set_table(self, column, table):
+ # standalone ForeignKey - create ForeignKeyConstraint
+ # on the hosting Table when attached to the Table.
+ if self.constraint is None and isinstance(table, Table):
+ self.constraint = ForeignKeyConstraint(
+ [], [], use_alter=self.use_alter, name=self.name,
+ onupdate=self.onupdate, ondelete=self.ondelete,
+ deferrable=self.deferrable, initially=self.initially,
+ match=self.match,
+ )
+ self.constraint._elements[self.parent] = self
+ self.constraint._set_parent_with_dispatch(table)
+ table.foreign_keys.add(self)
+
+ # set up remote ".column" attribute, or a note to pick it
+ # up when the other Table/Column shows up
+ if isinstance(self._colspec, util.string_types):
+ parenttable, table_key, colname = self._resolve_col_tokens()
+ fk_key = (table_key, colname)
+ if table_key in parenttable.metadata.tables:
+ table = parenttable.metadata.tables[table_key]
+ try:
+ self._link_to_col_by_colstring(parenttable, table, colname)
+ except exc.NoReferencedColumnError:
+ # this is OK, we'll try later
+ pass
+ parenttable.metadata._fk_memos[fk_key].append(self)
+ elif hasattr(self._colspec, '__clause_element__'):
+ _column = self._colspec.__clause_element__()
+ self._set_target_column(_column)
+ else:
+ _column = self._colspec
+ self._set_target_column(_column)
+
+
+
+class _NotAColumnExpr(object):
+ def _not_a_column_expr(self):
+ raise exc.InvalidRequestError(
+ "This %s cannot be used directly "
+ "as a column expression." % self.__class__.__name__)
+
+ __clause_element__ = self_group = lambda self: self._not_a_column_expr()
+ _from_objects = property(lambda self: self._not_a_column_expr())
+
+
+class DefaultGenerator(_NotAColumnExpr, SchemaItem):
+ """Base class for column *default* values."""
+
+ __visit_name__ = 'default_generator'
+
+ is_sequence = False
+ is_server_default = False
+ column = None
+
+ def __init__(self, for_update=False):
+ self.for_update = for_update
+
+ def _set_parent(self, column):
+ self.column = column
+ if self.for_update:
+ self.column.onupdate = self
+ else:
+ self.column.default = self
+
+ def execute(self, bind=None, **kwargs):
+ if bind is None:
+ bind = _bind_or_error(self)
+ return bind._execute_default(self, **kwargs)
+
+ @property
+ def bind(self):
+ """Return the connectable associated with this default."""
+ if getattr(self, 'column', None) is not None:
+ return self.column.table.bind
+ else:
+ return None
+
+
+class ColumnDefault(DefaultGenerator):
+ """A plain default value on a column.
+
+ This could correspond to a constant, a callable function,
+ or a SQL clause.
+
+ :class:`.ColumnDefault` is generated automatically
+ whenever the ``default``, ``onupdate`` arguments of
+ :class:`.Column` are used. A :class:`.ColumnDefault`
+ can be passed positionally as well.
+
+ For example, the following::
+
+ Column('foo', Integer, default=50)
+
+ Is equivalent to::
+
+ Column('foo', Integer, ColumnDefault(50))
+
+
+ """
+
+ def __init__(self, arg, **kwargs):
+ """"Construct a new :class:`.ColumnDefault`.
+
+
+ :param arg: argument representing the default value.
+ May be one of the following:
+
+ * a plain non-callable Python value, such as a
+ string, integer, boolean, or other simple type.
+ The default value will be used as is each time.
+ * a SQL expression, that is one which derives from
+ :class:`.ColumnElement`. The SQL expression will
+ be rendered into the INSERT or UPDATE statement,
+ or in the case of a primary key column when
+ RETURNING is not used may be
+ pre-executed before an INSERT within a SELECT.
+ * A Python callable. The function will be invoked for each
+ new row subject to an INSERT or UPDATE.
+ The callable must accept exactly
+ zero or one positional arguments. The one-argument form
+ will receive an instance of the :class:`.ExecutionContext`,
+ which provides contextual information as to the current
+ :class:`.Connection` in use as well as the current
+ statement and parameters.
+
+ """
+ super(ColumnDefault, self).__init__(**kwargs)
+ if isinstance(arg, FetchedValue):
+ raise exc.ArgumentError(
+ "ColumnDefault may not be a server-side default type.")
+ if util.callable(arg):
+ arg = self._maybe_wrap_callable(arg)
+ self.arg = arg
+
+ @util.memoized_property
+ def is_callable(self):
+ return util.callable(self.arg)
+
+ @util.memoized_property
+ def is_clause_element(self):
+ return isinstance(self.arg, ClauseElement)
+
+ @util.memoized_property
+ def is_scalar(self):
+ return not self.is_callable and \
+ not self.is_clause_element and \
+ not self.is_sequence
+
+ def _maybe_wrap_callable(self, fn):
+ """Wrap callables that don't accept a context.
+
+ The alternative here is to require that
+ a simple callable passed to "default" would need
+ to be of the form "default=lambda ctx: datetime.now".
+ That is the more "correct" way to go, but the case
+ of using a zero-arg callable for "default" is so
+ much more prominent than the context-specific one
+ I'm having trouble justifying putting that inconvenience
+ on everyone.
+
+ """
+ if inspect.isfunction(fn):
+ inspectable = fn
+ elif inspect.isclass(fn):
+ inspectable = fn.__init__
+ elif hasattr(fn, '__call__'):
+ inspectable = fn.__call__
+ else:
+ # probably not inspectable, try anyways.
+ inspectable = fn
+ try:
+ argspec = inspect.getargspec(inspectable)
+ except TypeError:
+ return lambda ctx: fn()
+
+ defaulted = argspec[3] is not None and len(argspec[3]) or 0
+ positionals = len(argspec[0]) - defaulted
+
+ # Py3K compat - no unbound methods
+ if inspect.ismethod(inspectable) or inspect.isclass(fn):
+ positionals -= 1
+
+ if positionals == 0:
+ return lambda ctx: fn()
+ elif positionals == 1:
+ return fn
+ else:
+ raise exc.ArgumentError(
+ "ColumnDefault Python function takes zero or one "
+ "positional arguments")
+
+ def _visit_name(self):
+ if self.for_update:
+ return "column_onupdate"
+ else:
+ return "column_default"
+ __visit_name__ = property(_visit_name)
+
+ def __repr__(self):
+ return "ColumnDefault(%r)" % self.arg
+
+
+class Sequence(DefaultGenerator):
+ """Represents a named database sequence.
+
+ The :class:`.Sequence` object represents the name and configurational
+ parameters of a database sequence. It also represents
+ a construct that can be "executed" by a SQLAlchemy :class:`.Engine`
+ or :class:`.Connection`, rendering the appropriate "next value" function
+ for the target database and returning a result.
+
+ The :class:`.Sequence` is typically associated with a primary key column::
+
+ some_table = Table('some_table', metadata,
+ Column('id', Integer, Sequence('some_table_seq'), primary_key=True)
+ )
+
+ When CREATE TABLE is emitted for the above :class:`.Table`, if the
+ target platform supports sequences, a CREATE SEQUENCE statement will
+ be emitted as well. For platforms that don't support sequences,
+ the :class:`.Sequence` construct is ignored.
+
+ See also: :class:`.CreateSequence` :class:`.DropSequence`
+
+ """
+
+ __visit_name__ = 'sequence'
+
+ is_sequence = True
+
+ def __init__(self, name, start=None, increment=None, schema=None,
+ optional=False, quote=None, metadata=None,
+ quote_schema=None,
+ for_update=False):
+ """Construct a :class:`.Sequence` object.
+
+ :param name: The name of the sequence.
+ :param start: the starting index of the sequence. This value is
+ used when the CREATE SEQUENCE command is emitted to the database
+ as the value of the "START WITH" clause. If ``None``, the
+ clause is omitted, which on most platforms indicates a starting
+ value of 1.
+ :param increment: the increment value of the sequence. This
+ value is used when the CREATE SEQUENCE command is emitted to
+ the database as the value of the "INCREMENT BY" clause. If ``None``,
+ the clause is omitted, which on most platforms indicates an
+ increment of 1.
+ :param schema: Optional schema name for the sequence, if located
+ in a schema other than the default.
+ :param optional: boolean value, when ``True``, indicates that this
+ :class:`.Sequence` object only needs to be explicitly generated
+ on backends that don't provide another way to generate primary
+ key identifiers. Currently, it essentially means, "don't create
+ this sequence on the Postgresql backend, where the SERIAL keyword
+ creates a sequence for us automatically".
+ :param quote: boolean value, when ``True`` or ``False``, explicitly
+ forces quoting of the schema name on or off. When left at its
+ default of ``None``, normal quoting rules based on casing and reserved
+ words take place.
+ :param metadata: optional :class:`.MetaData` object which will be
+ associated with this :class:`.Sequence`. A :class:`.Sequence`
+ that is associated with a :class:`.MetaData` gains access to the
+ ``bind`` of that :class:`.MetaData`, meaning the
+ :meth:`.Sequence.create` and :meth:`.Sequence.drop` methods will
+ make usage of that engine automatically.
+
+ .. versionchanged:: 0.7
+ Additionally, the appropriate CREATE SEQUENCE/
+ DROP SEQUENCE DDL commands will be emitted corresponding to this
+ :class:`.Sequence` when :meth:`.MetaData.create_all` and
+ :meth:`.MetaData.drop_all` are invoked.
+
+ Note that when a :class:`.Sequence` is applied to a :class:`.Column`,
+ the :class:`.Sequence` is automatically associated with the
+ :class:`.MetaData` object of that column's parent :class:`.Table`,
+ when that association is made. The :class:`.Sequence` will then
+ be subject to automatic CREATE SEQUENCE/DROP SEQUENCE corresponding
+ to when the :class:`.Table` object itself is created or dropped,
+ rather than that of the :class:`.MetaData` object overall.
+ :param for_update: Indicates this :class:`.Sequence`, when associated
+ with a :class:`.Column`, should be invoked for UPDATE statements
+ on that column's table, rather than for INSERT statements, when
+ no value is otherwise present for that column in the statement.
+
+ """
+ super(Sequence, self).__init__(for_update=for_update)
+ self.name = name
+ self.start = start
+ self.increment = increment
+ self.optional = optional
+ self.quote = quote
+ if metadata is not None and schema is None and metadata.schema:
+ self.schema = schema = metadata.schema
+ self.quote_schema = metadata.quote_schema
+ else:
+ self.schema = schema
+ self.quote_schema = quote_schema
+ self.metadata = metadata
+ self._key = _get_table_key(name, schema)
+ if metadata:
+ self._set_metadata(metadata)
+
+ @util.memoized_property
+ def is_callable(self):
+ return False
+
+ @util.memoized_property
+ def is_clause_element(self):
+ return False
+
+ @util.dependencies("sqlalchemy.sql.functions.func")
+ def next_value(self, func):
+ """Return a :class:`.next_value` function element
+ which will render the appropriate increment function
+ for this :class:`.Sequence` within any SQL expression.
+
+ """
+ return func.next_value(self, bind=self.bind)
+
+ def _set_parent(self, column):
+ super(Sequence, self)._set_parent(column)
+ column._on_table_attach(self._set_table)
+
+ def _set_table(self, column, table):
+ self._set_metadata(table.metadata)
+
+ def _set_metadata(self, metadata):
+ self.metadata = metadata
+ self.metadata._sequences[self._key] = self
+
+ @property
+ def bind(self):
+ if self.metadata:
+ return self.metadata.bind
+ else:
+ return None
+
+ def create(self, bind=None, checkfirst=True):
+ """Creates this sequence in the database."""
+
+ if bind is None:
+ bind = _bind_or_error(self)
+ bind._run_visitor(ddl.SchemaGenerator,
+ self,
+ checkfirst=checkfirst)
+
+ def drop(self, bind=None, checkfirst=True):
+ """Drops this sequence from the database."""
+
+ if bind is None:
+ bind = _bind_or_error(self)
+ bind._run_visitor(ddl.SchemaDropper,
+ self,
+ checkfirst=checkfirst)
+
+ def _not_a_column_expr(self):
+ raise exc.InvalidRequestError(
+ "This %s cannot be used directly "
+ "as a column expression. Use func.next_value(sequence) "
+ "to produce a 'next value' function that's usable "
+ "as a column element."
+ % self.__class__.__name__)
+
+
+@inspection._self_inspects
+class FetchedValue(_NotAColumnExpr, SchemaEventTarget):
+ """A marker for a transparent database-side default.
+
+ Use :class:`.FetchedValue` when the database is configured
+ to provide some automatic default for a column.
+
+ E.g.::
+
+ Column('foo', Integer, FetchedValue())
+
+ Would indicate that some trigger or default generator
+ will create a new value for the ``foo`` column during an
+ INSERT.
+
+ .. seealso::
+
+ :ref:`triggered_columns`
+
+ """
+ is_server_default = True
+ reflected = False
+ has_argument = False
+
+ def __init__(self, for_update=False):
+ self.for_update = for_update
+
+ def _as_for_update(self, for_update):
+ if for_update == self.for_update:
+ return self
+ else:
+ return self._clone(for_update)
+
+ def _clone(self, for_update):
+ n = self.__class__.__new__(self.__class__)
+ n.__dict__.update(self.__dict__)
+ n.__dict__.pop('column', None)
+ n.for_update = for_update
+ return n
+
+ def _set_parent(self, column):
+ self.column = column
+ if self.for_update:
+ self.column.server_onupdate = self
+ else:
+ self.column.server_default = self
+
+ def __repr__(self):
+ return util.generic_repr(self)
+
+
+class DefaultClause(FetchedValue):
+ """A DDL-specified DEFAULT column value.
+
+ :class:`.DefaultClause` is a :class:`.FetchedValue`
+ that also generates a "DEFAULT" clause when
+ "CREATE TABLE" is emitted.
+
+ :class:`.DefaultClause` is generated automatically
+ whenever the ``server_default``, ``server_onupdate`` arguments of
+ :class:`.Column` are used. A :class:`.DefaultClause`
+ can be passed positionally as well.
+
+ For example, the following::
+
+ Column('foo', Integer, server_default="50")
+
+ Is equivalent to::
+
+ Column('foo', Integer, DefaultClause("50"))
+
+ """
+
+ has_argument = True
+
+ def __init__(self, arg, for_update=False, _reflected=False):
+ util.assert_arg_type(arg, (util.string_types[0],
+ ClauseElement,
+ TextClause), 'arg')
+ super(DefaultClause, self).__init__(for_update)
+ self.arg = arg
+ self.reflected = _reflected
+
+ def __repr__(self):
+ return "DefaultClause(%r, for_update=%r)" % \
+ (self.arg, self.for_update)
+
+
+class PassiveDefault(DefaultClause):
+ """A DDL-specified DEFAULT column value.
+
+ .. deprecated:: 0.6
+ :class:`.PassiveDefault` is deprecated.
+ Use :class:`.DefaultClause`.
+ """
+ @util.deprecated("0.6",
+ ":class:`.PassiveDefault` is deprecated. "
+ "Use :class:`.DefaultClause`.",
+ False)
+ def __init__(self, *arg, **kw):
+ DefaultClause.__init__(self, *arg, **kw)
+
+
+class Constraint(SchemaItem):
+ """A table-level SQL constraint."""
+
+ __visit_name__ = 'constraint'
+
+ def __init__(self, name=None, deferrable=None, initially=None,
+ _create_rule=None,
+ **kw):
+ """Create a SQL constraint.
+
+ :param name:
+ Optional, the in-database name of this ``Constraint``.
+
+ :param deferrable:
+ Optional bool. If set, emit DEFERRABLE or NOT DEFERRABLE when
+ issuing DDL for this constraint.
+
+ :param initially:
+ Optional string. If set, emit INITIALLY <value> when issuing DDL
+ for this constraint.
+
+ :param _create_rule:
+ a callable which is passed the DDLCompiler object during
+ compilation. Returns True or False to signal inline generation of
+ this Constraint.
+
+ The AddConstraint and DropConstraint DDL constructs provide
+ DDLElement's more comprehensive "conditional DDL" approach that is
+ passed a database connection when DDL is being issued. _create_rule
+ is instead called during any CREATE TABLE compilation, where there
+ may not be any transaction/connection in progress. However, it
+ allows conditional compilation of the constraint even for backends
+ which do not support addition of constraints through ALTER TABLE,
+ which currently includes SQLite.
+
+ _create_rule is used by some types to create constraints.
+ Currently, its call signature is subject to change at any time.
+
+ :param \**kwargs:
+ Dialect-specific keyword parameters, see the documentation
+ for various dialects and constraints regarding options here.
+
+ """
+
+ self.name = name
+ self.deferrable = deferrable
+ self.initially = initially
+ self._create_rule = _create_rule
+ util.set_creation_order(self)
+ _validate_dialect_kwargs(kw, self.__class__.__name__)
+ self.kwargs = kw
+
+ @property
+ def table(self):
+ try:
+ if isinstance(self.parent, Table):
+ return self.parent
+ except AttributeError:
+ pass
+ raise exc.InvalidRequestError(
+ "This constraint is not bound to a table. Did you "
+ "mean to call table.append_constraint(constraint) ?")
+
+ def _set_parent(self, parent):
+ self.parent = parent
+ parent.constraints.add(self)
+
+ def copy(self, **kw):
+ raise NotImplementedError()
+
+
+def _to_schema_column(element):
+ if hasattr(element, '__clause_element__'):
+ element = element.__clause_element__()
+ if not isinstance(element, Column):
+ raise exc.ArgumentError("schema.Column object expected")
+ return element
+
+
+def _to_schema_column_or_string(element):
+ if hasattr(element, '__clause_element__'):
+ element = element.__clause_element__()
+ if not isinstance(element, util.string_types + (ColumnElement, )):
+ msg = "Element %r is not a string name or column element"
+ raise exc.ArgumentError(msg % element)
+ return element
+
+
+class ColumnCollectionMixin(object):
+ def __init__(self, *columns):
+ self.columns = ColumnCollection()
+ self._pending_colargs = [_to_schema_column_or_string(c)
+ for c in columns]
+ if self._pending_colargs and \
+ isinstance(self._pending_colargs[0], Column) and \
+ isinstance(self._pending_colargs[0].table, Table):
+ self._set_parent_with_dispatch(self._pending_colargs[0].table)
+
+ def _set_parent(self, table):
+ for col in self._pending_colargs:
+ if isinstance(col, util.string_types):
+ col = table.c[col]
+ self.columns.add(col)
+
+
+class ColumnCollectionConstraint(ColumnCollectionMixin, Constraint):
+ """A constraint that proxies a ColumnCollection."""
+
+ def __init__(self, *columns, **kw):
+ """
+ :param \*columns:
+ A sequence of column names or Column objects.
+
+ :param name:
+ Optional, the in-database name of this constraint.
+
+ :param deferrable:
+ Optional bool. If set, emit DEFERRABLE or NOT DEFERRABLE when
+ issuing DDL for this constraint.
+
+ :param initially:
+ Optional string. If set, emit INITIALLY <value> when issuing DDL
+ for this constraint.
+
+ """
+ ColumnCollectionMixin.__init__(self, *columns)
+ Constraint.__init__(self, **kw)
+
+ def _set_parent(self, table):
+ ColumnCollectionMixin._set_parent(self, table)
+ Constraint._set_parent(self, table)
+
+ def __contains__(self, x):
+ return x in self.columns
+
+ def copy(self, **kw):
+ c = self.__class__(name=self.name, deferrable=self.deferrable,
+ initially=self.initially, *self.columns.keys())
+ c.dispatch._update(self.dispatch)
+ return c
+
+ def contains_column(self, col):
+ return self.columns.contains_column(col)
+
+ def __iter__(self):
+ # inlining of
+ # return iter(self.columns)
+ # ColumnCollection->OrderedProperties->OrderedDict
+ ordered_dict = self.columns._data
+ return (ordered_dict[key] for key in ordered_dict._list)
+
+ def __len__(self):
+ return len(self.columns._data)
+
+
+class CheckConstraint(Constraint):
+ """A table- or column-level CHECK constraint.
+
+ Can be included in the definition of a Table or Column.
+ """
+
+ def __init__(self, sqltext, name=None, deferrable=None,
+ initially=None, table=None, _create_rule=None,
+ _autoattach=True):
+ """Construct a CHECK constraint.
+
+ :param sqltext:
+ A string containing the constraint definition, which will be used
+ verbatim, or a SQL expression construct.
+
+ :param name:
+ Optional, the in-database name of the constraint.
+
+ :param deferrable:
+ Optional bool. If set, emit DEFERRABLE or NOT DEFERRABLE when
+ issuing DDL for this constraint.
+
+ :param initially:
+ Optional string. If set, emit INITIALLY <value> when issuing DDL
+ for this constraint.
+
+ """
+
+ super(CheckConstraint, self).\
+ __init__(name, deferrable, initially, _create_rule)
+ self.sqltext = _literal_as_text(sqltext)
+ if table is not None:
+ self._set_parent_with_dispatch(table)
+ elif _autoattach:
+ cols = _find_columns(self.sqltext)
+ tables = set([c.table for c in cols
+ if isinstance(c.table, Table)])
+ if len(tables) == 1:
+ self._set_parent_with_dispatch(
+ tables.pop())
+
+ def __visit_name__(self):
+ if isinstance(self.parent, Table):
+ return "check_constraint"
+ else:
+ return "column_check_constraint"
+ __visit_name__ = property(__visit_name__)
+
+ def copy(self, target_table=None, **kw):
+ if target_table is not None:
+ def replace(col):
+ if self.table.c.contains_column(col):
+ return target_table.c[col.key]
+ else:
+ return None
+ sqltext = visitors.replacement_traverse(self.sqltext, {}, replace)
+ else:
+ sqltext = self.sqltext
+ c = CheckConstraint(sqltext,
+ name=self.name,
+ initially=self.initially,
+ deferrable=self.deferrable,
+ _create_rule=self._create_rule,
+ table=target_table,
+ _autoattach=False)
+ c.dispatch._update(self.dispatch)
+ return c
+
+
+class ForeignKeyConstraint(Constraint):
+ """A table-level FOREIGN KEY constraint.
+
+ Defines a single column or composite FOREIGN KEY ... REFERENCES
+ constraint. For a no-frills, single column foreign key, adding a
+ :class:`.ForeignKey` to the definition of a :class:`.Column` is a shorthand
+ equivalent for an unnamed, single column :class:`.ForeignKeyConstraint`.
+
+ Examples of foreign key configuration are in :ref:`metadata_foreignkeys`.
+
+ """
+ __visit_name__ = 'foreign_key_constraint'
+
+ def __init__(self, columns, refcolumns, name=None, onupdate=None,
+ ondelete=None, deferrable=None, initially=None, use_alter=False,
+ link_to_name=False, match=None, table=None):
+ """Construct a composite-capable FOREIGN KEY.
+
+ :param columns: A sequence of local column names. The named columns
+ must be defined and present in the parent Table. The names should
+ match the ``key`` given to each column (defaults to the name) unless
+ ``link_to_name`` is True.
+
+ :param refcolumns: A sequence of foreign column names or Column
+ objects. The columns must all be located within the same Table.
+
+ :param name: Optional, the in-database name of the key.
+
+ :param onupdate: Optional string. If set, emit ON UPDATE <value> when
+ issuing DDL for this constraint. Typical values include CASCADE,
+ DELETE and RESTRICT.
+
+ :param ondelete: Optional string. If set, emit ON DELETE <value> when
+ issuing DDL for this constraint. Typical values include CASCADE,
+ DELETE and RESTRICT.
+
+ :param deferrable: Optional bool. If set, emit DEFERRABLE or NOT
+ DEFERRABLE when issuing DDL for this constraint.
+
+ :param initially: Optional string. If set, emit INITIALLY <value> when
+ issuing DDL for this constraint.
+
+ :param link_to_name: if True, the string name given in ``column`` is
+ the rendered name of the referenced column, not its locally assigned
+ ``key``.
+
+ :param use_alter: If True, do not emit the DDL for this constraint as
+ part of the CREATE TABLE definition. Instead, generate it via an
+ ALTER TABLE statement issued after the full collection of tables
+ have been created, and drop it via an ALTER TABLE statement before
+ the full collection of tables are dropped. This is shorthand for the
+ usage of :class:`.AddConstraint` and :class:`.DropConstraint` applied
+ as "after-create" and "before-drop" events on the MetaData object.
+ This is normally used to generate/drop constraints on objects that
+ are mutually dependent on each other.
+
+ :param match: Optional string. If set, emit MATCH <value> when issuing
+ DDL for this constraint. Typical values include SIMPLE, PARTIAL
+ and FULL.
+
+ """
+ super(ForeignKeyConstraint, self).\
+ __init__(name, deferrable, initially)
+
+ self.onupdate = onupdate
+ self.ondelete = ondelete
+ self.link_to_name = link_to_name
+ if self.name is None and use_alter:
+ raise exc.ArgumentError("Alterable Constraint requires a name")
+ self.use_alter = use_alter
+ self.match = match
+
+ self._elements = util.OrderedDict()
+
+ # standalone ForeignKeyConstraint - create
+ # associated ForeignKey objects which will be applied to hosted
+ # Column objects (in col.foreign_keys), either now or when attached
+ # to the Table for string-specified names
+ for col, refcol in zip(columns, refcolumns):
+ self._elements[col] = ForeignKey(
+ refcol,
+ _constraint=self,
+ name=self.name,
+ onupdate=self.onupdate,
+ ondelete=self.ondelete,
+ use_alter=self.use_alter,
+ link_to_name=self.link_to_name,
+ match=self.match
+ )
+
+ if table is not None:
+ self._set_parent_with_dispatch(table)
+ elif columns and \
+ isinstance(columns[0], Column) and \
+ columns[0].table is not None:
+ self._set_parent_with_dispatch(columns[0].table)
+
+ def _validate_dest_table(self, table):
+ table_keys = set([elem._table_key() for elem in self._elements.values()])
+ if None not in table_keys and len(table_keys) > 1:
+ elem0, elem1 = sorted(table_keys)[0:2]
+ raise exc.ArgumentError(
+ 'ForeignKeyConstraint on %s(%s) refers to '
+ 'multiple remote tables: %s and %s' % (
+ table.fullname,
+ self._col_description,
+ elem0,
+ elem1
+ ))
+
+ @property
+ def _col_description(self):
+ return ", ".join(self._elements)
+
+ @property
+ def columns(self):
+ return list(self._elements)
+
+ @property
+ def elements(self):
+ return list(self._elements.values())
+
+ def _set_parent(self, table):
+ super(ForeignKeyConstraint, self)._set_parent(table)
+
+ self._validate_dest_table(table)
+
+ for col, fk in self._elements.items():
+ # string-specified column names now get
+ # resolved to Column objects
+ if isinstance(col, util.string_types):
+ try:
+ col = table.c[col]
+ except KeyError:
+ raise exc.ArgumentError(
+ "Can't create ForeignKeyConstraint "
+ "on table '%s': no column "
+ "named '%s' is present." % (table.description, col))
+
+ if not hasattr(fk, 'parent') or \
+ fk.parent is not col:
+ fk._set_parent_with_dispatch(col)
+
+ if self.use_alter:
+ def supports_alter(ddl, event, schema_item, bind, **kw):
+ return table in set(kw['tables']) and \
+ bind.dialect.supports_alter
+
+ event.listen(table.metadata, "after_create",
+ ddl.AddConstraint(self, on=supports_alter))
+ event.listen(table.metadata, "before_drop",
+ ddl.DropConstraint(self, on=supports_alter))
+
+ def copy(self, schema=None, **kw):
+ fkc = ForeignKeyConstraint(
+ [x.parent.key for x in self._elements.values()],
+ [x._get_colspec(schema=schema) for x in self._elements.values()],
+ name=self.name,
+ onupdate=self.onupdate,
+ ondelete=self.ondelete,
+ use_alter=self.use_alter,
+ deferrable=self.deferrable,
+ initially=self.initially,
+ link_to_name=self.link_to_name,
+ match=self.match
+ )
+ fkc.dispatch._update(self.dispatch)
+ return fkc
+
+
+class PrimaryKeyConstraint(ColumnCollectionConstraint):
+ """A table-level PRIMARY KEY constraint.
+
+ Defines a single column or composite PRIMARY KEY constraint. For a
+ no-frills primary key, adding ``primary_key=True`` to one or more
+ ``Column`` definitions is a shorthand equivalent for an unnamed single- or
+ multiple-column PrimaryKeyConstraint.
+ """
+
+ __visit_name__ = 'primary_key_constraint'
+
+ def _set_parent(self, table):
+ super(PrimaryKeyConstraint, self)._set_parent(table)
+
+ if table.primary_key in table.constraints:
+ table.constraints.remove(table.primary_key)
+ table.primary_key = self
+ table.constraints.add(self)
+
+ for c in self.columns:
+ c.primary_key = True
+
+ def _replace(self, col):
+ self.columns.replace(col)
+
+
+class UniqueConstraint(ColumnCollectionConstraint):
+ """A table-level UNIQUE constraint.
+
+ Defines a single column or composite UNIQUE constraint. For a no-frills,
+ single column constraint, adding ``unique=True`` to the ``Column``
+ definition is a shorthand equivalent for an unnamed, single column
+ UniqueConstraint.
+ """
+
+ __visit_name__ = 'unique_constraint'
+
+
+class Index(ColumnCollectionMixin, SchemaItem):
+ """A table-level INDEX.
+
+ Defines a composite (one or more column) INDEX.
+
+ E.g.::
+
+ sometable = Table("sometable", metadata,
+ Column("name", String(50)),
+ Column("address", String(100))
+ )
+
+ Index("some_index", sometable.c.name)
+
+ For a no-frills, single column index, adding
+ :class:`.Column` also supports ``index=True``::
+
+ sometable = Table("sometable", metadata,
+ Column("name", String(50), index=True)
+ )
+
+ For a composite index, multiple columns can be specified::
+
+ Index("some_index", sometable.c.name, sometable.c.address)
+
+ Functional indexes are supported as well, keeping in mind that at least
+ one :class:`.Column` must be present::
+
+ Index("some_index", func.lower(sometable.c.name))
+
+ .. versionadded:: 0.8 support for functional and expression-based indexes.
+
+ .. seealso::
+
+ :ref:`schema_indexes` - General information on :class:`.Index`.
+
+ :ref:`postgresql_indexes` - PostgreSQL-specific options available for the
+ :class:`.Index` construct.
+
+ :ref:`mysql_indexes` - MySQL-specific options available for the
+ :class:`.Index` construct.
+
+ :ref:`mssql_indexes` - MSSQL-specific options available for the
+ :class:`.Index` construct.
+
+ """
+
+ __visit_name__ = 'index'
+
+ def __init__(self, name, *expressions, **kw):
+ """Construct an index object.
+
+ :param name:
+ The name of the index
+
+ :param \*expressions: Column or SQL expressions.
+
+ :param unique:
+ Defaults to False: create a unique index.
+
+ :param \**kw:
+ Other keyword arguments may be interpreted by specific dialects.
+
+ """
+ self.table = None
+
+ columns = []
+ for expr in expressions:
+ if not isinstance(expr, ClauseElement):
+ columns.append(expr)
+ else:
+ cols = []
+ visitors.traverse(expr, {}, {'column': cols.append})
+ if cols:
+ columns.append(cols[0])
+ else:
+ columns.append(expr)
+
+ self.expressions = expressions
+
+ # will call _set_parent() if table-bound column
+ # objects are present
+ ColumnCollectionMixin.__init__(self, *columns)
+
+ self.name = name
+ self.unique = kw.pop('unique', False)
+ self.kwargs = kw
+
+ def _set_parent(self, table):
+ ColumnCollectionMixin._set_parent(self, table)
+
+ if self.table is not None and table is not self.table:
+ raise exc.ArgumentError(
+ "Index '%s' is against table '%s', and "
+ "cannot be associated with table '%s'." % (
+ self.name,
+ self.table.description,
+ table.description
+ )
+ )
+ self.table = table
+ for c in self.columns:
+ if c.table != self.table:
+ raise exc.ArgumentError(
+ "Column '%s' is not part of table '%s'." %
+ (c, self.table.description)
+ )
+ table.indexes.add(self)
+
+ self.expressions = [
+ expr if isinstance(expr, ClauseElement)
+ else colexpr
+ for expr, colexpr in zip(self.expressions, self.columns)
+ ]
+
+ @property
+ def bind(self):
+ """Return the connectable associated with this Index."""
+
+ return self.table.bind
+
+ def create(self, bind=None):
+ """Issue a ``CREATE`` statement for this
+ :class:`.Index`, using the given :class:`.Connectable`
+ for connectivity.
+
+ See also :meth:`.MetaData.create_all`.
+
+ """
+ if bind is None:
+ bind = _bind_or_error(self)
+ bind._run_visitor(ddl.SchemaGenerator, self)
+ return self
+
+ def drop(self, bind=None):
+ """Issue a ``DROP`` statement for this
+ :class:`.Index`, using the given :class:`.Connectable`
+ for connectivity.
+
+ See also :meth:`.MetaData.drop_all`.
+
+ """
+ if bind is None:
+ bind = _bind_or_error(self)
+ bind._run_visitor(ddl.SchemaDropper, self)
+
+ def __repr__(self):
+ return 'Index(%s)' % (
+ ", ".join(
+ [repr(self.name)] +
+ [repr(c) for c in self.columns] +
+ (self.unique and ["unique=True"] or [])
+ ))
+
+
+class MetaData(SchemaItem):
+ """A collection of :class:`.Table` objects and their associated schema
+ constructs.
+
+ Holds a collection of :class:`.Table` objects as well as
+ an optional binding to an :class:`.Engine` or
+ :class:`.Connection`. If bound, the :class:`.Table` objects
+ in the collection and their columns may participate in implicit SQL
+ execution.
+
+ The :class:`.Table` objects themselves are stored in the
+ ``metadata.tables`` dictionary.
+
+ The ``bind`` property may be assigned to dynamically. A common pattern is
+ to start unbound and then bind later when an engine is available::
+
+ metadata = MetaData()
+ # define tables
+ Table('mytable', metadata, ...)
+ # connect to an engine later, perhaps after loading a URL from a
+ # configuration file
+ metadata.bind = an_engine
+
+ MetaData is a thread-safe object after tables have been explicitly defined
+ or loaded via reflection.
+
+ See also:
+
+ :ref:`metadata_describing` - Introduction to database metadata
+
+ .. index::
+ single: thread safety; MetaData
+
+ """
+
+ __visit_name__ = 'metadata'
+
+ def __init__(self, bind=None, reflect=False, schema=None,
+ quote_schema=None):
+ """Create a new MetaData object.
+
+ :param bind:
+ An Engine or Connection to bind to. May also be a string or URL
+ instance, these are passed to create_engine() and this MetaData will
+ be bound to the resulting engine.
+
+ :param reflect:
+ Optional, automatically load all tables from the bound database.
+ Defaults to False. ``bind`` is required when this option is set.
+
+ .. deprecated:: 0.8
+ Please use the :meth:`.MetaData.reflect` method.
+
+ :param schema:
+ The default schema to use for the :class:`.Table`,
+ :class:`.Sequence`, and other objects associated with this
+ :class:`.MetaData`. Defaults to ``None``.
+
+ :param quote_schema:
+ Sets the ``quote_schema`` flag for those :class:`.Table`,
+ :class:`.Sequence`, and other objects which make usage of the
+ local ``schema`` name.
+
+ .. versionadded:: 0.7.4
+ ``schema`` and ``quote_schema`` parameters.
+
+ """
+ self.tables = util.immutabledict()
+ self.schema = schema
+ self.quote_schema = quote_schema
+ self._schemas = set()
+ self._sequences = {}
+ self._fk_memos = collections.defaultdict(list)
+
+ self.bind = bind
+ if reflect:
+ util.warn("reflect=True is deprecate; please "
+ "use the reflect() method.")
+ if not bind:
+ raise exc.ArgumentError(
+ "A bind must be supplied in conjunction "
+ "with reflect=True")
+ self.reflect()
+
+ def __repr__(self):
+ return 'MetaData(bind=%r)' % self.bind
+
+ def __contains__(self, table_or_key):
+ if not isinstance(table_or_key, util.string_types):
+ table_or_key = table_or_key.key
+ return table_or_key in self.tables
+
+ def _add_table(self, name, schema, table):
+ key = _get_table_key(name, schema)
+ dict.__setitem__(self.tables, key, table)
+ if schema:
+ self._schemas.add(schema)
+
+
+
+ def _remove_table(self, name, schema):
+ key = _get_table_key(name, schema)
+ removed = dict.pop(self.tables, key, None)
+ if removed is not None:
+ for fk in removed.foreign_keys:
+ fk._remove_from_metadata(self)
+ if self._schemas:
+ self._schemas = set([t.schema
+ for t in self.tables.values()
+ if t.schema is not None])
+
+
+ def __getstate__(self):
+ return {'tables': self.tables,
+ 'schema': self.schema,
+ 'quote_schema': self.quote_schema,
+ 'schemas': self._schemas,
+ 'sequences': self._sequences,
+ 'fk_memos': self._fk_memos}
+
+ def __setstate__(self, state):
+ self.tables = state['tables']
+ self.schema = state['schema']
+ self.quote_schema = state['quote_schema']
+ self._bind = None
+ self._sequences = state['sequences']
+ self._schemas = state['schemas']
+ self._fk_memos = state['fk_memos']
+
+ def is_bound(self):
+ """True if this MetaData is bound to an Engine or Connection."""
+
+ return self._bind is not None
+
+ def bind(self):
+ """An :class:`.Engine` or :class:`.Connection` to which this
+ :class:`.MetaData` is bound.
+
+ Typically, a :class:`.Engine` is assigned to this attribute
+ so that "implicit execution" may be used, or alternatively
+ as a means of providing engine binding information to an
+ ORM :class:`.Session` object::
+
+ engine = create_engine("someurl://")
+ metadata.bind = engine
+
+ .. seealso::
+
+ :ref:`dbengine_implicit` - background on "bound metadata"
+
+ """
+ return self._bind
+
+ @util.dependencies("sqlalchemy.engine.url")
+ def _bind_to(self, url, bind):
+ """Bind this MetaData to an Engine, Connection, string or URL."""
+
+ if isinstance(bind, util.string_types + (url.URL, )):
+ self._bind = sqlalchemy.create_engine(bind)
+ else:
+ self._bind = bind
+ bind = property(bind, _bind_to)
+
+ def clear(self):
+ """Clear all Table objects from this MetaData."""
+
+ dict.clear(self.tables)
+ self._schemas.clear()
+ self._fk_memos.clear()
+
+ def remove(self, table):
+ """Remove the given Table object from this MetaData."""
+
+ self._remove_table(table.name, table.schema)
+
+ @property
+ def sorted_tables(self):
+ """Returns a list of :class:`.Table` objects sorted in order of
+ foreign key dependency.
+
+ The sorting will place :class:`.Table` objects that have dependencies
+ first, before the dependencies themselves, representing the
+ order in which they can be created. To get the order in which
+ the tables would be dropped, use the ``reversed()`` Python built-in.
+
+ .. seealso::
+
+ :meth:`.Inspector.sorted_tables`
+
+ """
+ return ddl.sort_tables(self.tables.values())
+
+ def reflect(self, bind=None, schema=None, views=False, only=None):
+ """Load all available table definitions from the database.
+
+ Automatically creates ``Table`` entries in this ``MetaData`` for any
+ table available in the database but not yet present in the
+ ``MetaData``. May be called multiple times to pick up tables recently
+ added to the database, however no special action is taken if a table
+ in this ``MetaData`` no longer exists in the database.
+
+ :param bind:
+ A :class:`.Connectable` used to access the database; if None, uses
+ the existing bind on this ``MetaData``, if any.
+
+ :param schema:
+ Optional, query and reflect tables from an alterate schema.
+ If None, the schema associated with this :class:`.MetaData`
+ is used, if any.
+
+ :param views:
+ If True, also reflect views.
+
+ :param only:
+ Optional. Load only a sub-set of available named tables. May be
+ specified as a sequence of names or a callable.
+
+ If a sequence of names is provided, only those tables will be
+ reflected. An error is raised if a table is requested but not
+ available. Named tables already present in this ``MetaData`` are
+ ignored.
+
+ If a callable is provided, it will be used as a boolean predicate to
+ filter the list of potential table names. The callable is called
+ with a table name and this ``MetaData`` instance as positional
+ arguments and should return a true value for any table to reflect.
+
+ """
+ if bind is None:
+ bind = _bind_or_error(self)
+
+ with bind.connect() as conn:
+
+ reflect_opts = {
+ 'autoload': True,
+ 'autoload_with': conn
+ }
+
+ if schema is None:
+ schema = self.schema
+
+ if schema is not None:
+ reflect_opts['schema'] = schema
+
+ available = util.OrderedSet(bind.engine.table_names(schema,
+ connection=conn))
+ if views:
+ available.update(
+ bind.dialect.get_view_names(conn, schema)
+ )
+
+ if schema is not None:
+ available_w_schema = util.OrderedSet(["%s.%s" % (schema, name)
+ for name in available])
+ else:
+ available_w_schema = available
+
+ current = set(self.tables)
+
+ if only is None:
+ load = [name for name, schname in
+ zip(available, available_w_schema)
+ if schname not in current]
+ elif util.callable(only):
+ load = [name for name, schname in
+ zip(available, available_w_schema)
+ if schname not in current and only(name, self)]
+ else:
+ missing = [name for name in only if name not in available]
+ if missing:
+ s = schema and (" schema '%s'" % schema) or ''
+ raise exc.InvalidRequestError(
+ 'Could not reflect: requested table(s) not available '
+ 'in %s%s: (%s)' %
+ (bind.engine.url, s, ', '.join(missing)))
+ load = [name for name in only if name not in current]
+
+ for name in load:
+ Table(name, self, **reflect_opts)
+
+ def append_ddl_listener(self, event_name, listener):
+ """Append a DDL event listener to this ``MetaData``.
+
+ .. deprecated:: 0.7
+ See :class:`.DDLEvents`.
+
+ """
+ def adapt_listener(target, connection, **kw):
+ tables = kw['tables']
+ listener(event, target, connection, tables=tables)
+
+ event.listen(self, "" + event_name.replace('-', '_'), adapt_listener)
+
+ def create_all(self, bind=None, tables=None, checkfirst=True):
+ """Create all tables stored in this metadata.
+
+ Conditional by default, will not attempt to recreate tables already
+ present in the target database.
+
+ :param bind:
+ A :class:`.Connectable` used to access the
+ database; if None, uses the existing bind on this ``MetaData``, if
+ any.
+
+ :param tables:
+ Optional list of ``Table`` objects, which is a subset of the total
+ tables in the ``MetaData`` (others are ignored).
+
+ :param checkfirst:
+ Defaults to True, don't issue CREATEs for tables already present
+ in the target database.
+
+ """
+ if bind is None:
+ bind = _bind_or_error(self)
+ bind._run_visitor(ddl.SchemaGenerator,
+ self,
+ checkfirst=checkfirst,
+ tables=tables)
+
+ def drop_all(self, bind=None, tables=None, checkfirst=True):
+ """Drop all tables stored in this metadata.
+
+ Conditional by default, will not attempt to drop tables not present in
+ the target database.
+
+ :param bind:
+ A :class:`.Connectable` used to access the
+ database; if None, uses the existing bind on this ``MetaData``, if
+ any.
+
+ :param tables:
+ Optional list of ``Table`` objects, which is a subset of the
+ total tables in the ``MetaData`` (others are ignored).
+
+ :param checkfirst:
+ Defaults to True, only issue DROPs for tables confirmed to be
+ present in the target database.
+
+ """
+ if bind is None:
+ bind = _bind_or_error(self)
+ bind._run_visitor(ddl.SchemaDropper,
+ self,
+ checkfirst=checkfirst,
+ tables=tables)
+
+
+class ThreadLocalMetaData(MetaData):
+ """A MetaData variant that presents a different ``bind`` in every thread.
+
+ Makes the ``bind`` property of the MetaData a thread-local value, allowing
+ this collection of tables to be bound to different ``Engine``
+ implementations or connections in each thread.
+
+ The ThreadLocalMetaData starts off bound to None in each thread. Binds
+ must be made explicitly by assigning to the ``bind`` property or using
+ ``connect()``. You can also re-bind dynamically multiple times per
+ thread, just like a regular ``MetaData``.
+
+ """
+
+ __visit_name__ = 'metadata'
+
+ def __init__(self):
+ """Construct a ThreadLocalMetaData."""
+
+ self.context = util.threading.local()
+ self.__engines = {}
+ super(ThreadLocalMetaData, self).__init__()
+
+ def bind(self):
+ """The bound Engine or Connection for this thread.
+
+ This property may be assigned an Engine or Connection, or assigned a
+ string or URL to automatically create a basic Engine for this bind
+ with ``create_engine()``."""
+
+ return getattr(self.context, '_engine', None)
+
+ @util.dependencies("sqlalchemy.engine.url")
+ def _bind_to(self, url, bind):
+ """Bind to a Connectable in the caller's thread."""
+
+ if isinstance(bind, util.string_types + (url.URL, )):
+ try:
+ self.context._engine = self.__engines[bind]
+ except KeyError:
+ e = sqlalchemy.create_engine(bind)
+ self.__engines[bind] = e
+ self.context._engine = e
+ else:
+ # TODO: this is squirrely. we shouldnt have to hold onto engines
+ # in a case like this
+ if bind not in self.__engines:
+ self.__engines[bind] = bind
+ self.context._engine = bind
+
+ bind = property(bind, _bind_to)
+
+ def is_bound(self):
+ """True if there is a bind for this thread."""
+ return (hasattr(self.context, '_engine') and
+ self.context._engine is not None)
+
+ def dispose(self):
+ """Dispose all bound engines, in all thread contexts."""
+
+ for e in self.__engines.values():
+ if hasattr(e, 'dispose'):
+ e.dispose()
+
+
+
diff --git a/lib/sqlalchemy/sql/selectable.py b/lib/sqlalchemy/sql/selectable.py
new file mode 100644
index 000000000..c372e1287
--- /dev/null
+++ b/lib/sqlalchemy/sql/selectable.py
@@ -0,0 +1,2775 @@
+# sql/selectable.py
+# Copyright (C) 2005-2013 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
+
+"""The :class:`.FromClause` class of SQL expression elements, representing
+SQL tables and derived rowsets.
+
+"""
+
+from .elements import ClauseElement, TextClause, ClauseList, \
+ and_, Grouping, UnaryExpression, literal_column
+from .elements import _clone, \
+ _literal_as_text, _interpret_as_column_or_from, _expand_cloned,\
+ _select_iterables, _anonymous_label, _clause_element_as_expr,\
+ _cloned_intersection, _cloned_difference
+from .base import Immutable, Executable, _generative, \
+ ColumnCollection, ColumnSet, _from_objects, Generative
+from . import type_api
+from .. import inspection
+from .. import util
+from .. import exc
+from operator import attrgetter
+from . import operators
+import operator
+from .annotation import Annotated
+import itertools
+
+def _interpret_as_from(element):
+ insp = inspection.inspect(element, raiseerr=False)
+ if insp is None:
+ if isinstance(element, util.string_types):
+ return TextClause(util.text_type(element))
+ elif hasattr(insp, "selectable"):
+ return insp.selectable
+ raise exc.ArgumentError("FROM expression expected")
+
+def _interpret_as_select(element):
+ element = _interpret_as_from(element)
+ if isinstance(element, Alias):
+ element = element.original
+ if not isinstance(element, Select):
+ element = element.select()
+ return element
+
+def subquery(alias, *args, **kwargs):
+ """Return an :class:`.Alias` object derived
+ from a :class:`.Select`.
+
+ name
+ alias name
+
+ \*args, \**kwargs
+
+ all other arguments are delivered to the
+ :func:`select` function.
+
+ """
+ return Select(*args, **kwargs).alias(alias)
+
+
+
+def alias(selectable, name=None, flat=False):
+ """Return an :class:`.Alias` object.
+
+ An :class:`.Alias` represents any :class:`.FromClause`
+ with an alternate name assigned within SQL, typically using the ``AS``
+ clause when generated, e.g. ``SELECT * FROM table AS aliasname``.
+
+ Similar functionality is available via the
+ :meth:`~.FromClause.alias` method
+ available on all :class:`.FromClause` subclasses.
+
+ When an :class:`.Alias` is created from a :class:`.Table` object,
+ this has the effect of the table being rendered
+ as ``tablename AS aliasname`` in a SELECT statement.
+
+ For :func:`.select` objects, the effect is that of creating a named
+ subquery, i.e. ``(select ...) AS aliasname``.
+
+ The ``name`` parameter is optional, and provides the name
+ to use in the rendered SQL. If blank, an "anonymous" name
+ will be deterministically generated at compile time.
+ Deterministic means the name is guaranteed to be unique against
+ other constructs used in the same statement, and will also be the
+ same name for each successive compilation of the same statement
+ object.
+
+ :param selectable: any :class:`.FromClause` subclass,
+ such as a table, select statement, etc.
+
+ :param name: string name to be assigned as the alias.
+ If ``None``, a name will be deterministically generated
+ at compile time.
+
+ :param flat: Will be passed through to if the given selectable
+ is an instance of :class:`.Join` - see :meth:`.Join.alias`
+ for details.
+
+ .. versionadded:: 0.9.0
+
+ """
+ return selectable.alias(name=name, flat=flat)
+
+
+class Selectable(ClauseElement):
+ """mark a class as being selectable"""
+ __visit_name__ = 'selectable'
+
+ is_selectable = True
+
+ @property
+ def selectable(self):
+ return self
+
+
+class FromClause(Selectable):
+ """Represent an element that can be used within the ``FROM``
+ clause of a ``SELECT`` statement.
+
+ The most common forms of :class:`.FromClause` are the
+ :class:`.Table` and the :func:`.select` constructs. Key
+ features common to all :class:`.FromClause` objects include:
+
+ * a :attr:`.c` collection, which provides per-name access to a collection
+ of :class:`.ColumnElement` objects.
+ * a :attr:`.primary_key` attribute, which is a collection of all those
+ :class:`.ColumnElement` objects that indicate the ``primary_key`` flag.
+ * Methods to generate various derivations of a "from" clause, including
+ :meth:`.FromClause.alias`, :meth:`.FromClause.join`,
+ :meth:`.FromClause.select`.
+
+
+ """
+ __visit_name__ = 'fromclause'
+ named_with_column = False
+ _hide_froms = []
+ quote = None
+ schema = None
+ _memoized_property = util.group_expirable_memoized_property(["_columns"])
+
+ @util.dependencies("sqlalchemy.sql.functions")
+ def count(self, functions, whereclause=None, **params):
+ """return a SELECT COUNT generated against this
+ :class:`.FromClause`."""
+
+ if self.primary_key:
+ col = list(self.primary_key)[0]
+ else:
+ col = list(self.columns)[0]
+ return Select(
+ [functions.func.count(col).label('tbl_row_count')],
+ whereclause,
+ from_obj=[self],
+ **params)
+
+ def select(self, whereclause=None, **params):
+ """return a SELECT of this :class:`.FromClause`.
+
+ .. seealso::
+
+ :func:`~.sql.expression.select` - general purpose
+ method which allows for arbitrary column lists.
+
+ """
+
+ return Select([self], whereclause, **params)
+
+ def join(self, right, onclause=None, isouter=False):
+ """return a join of this :class:`.FromClause` against another
+ :class:`.FromClause`."""
+
+ return Join(self, right, onclause, isouter)
+
+ def outerjoin(self, right, onclause=None):
+ """return an outer join of this :class:`.FromClause` against another
+ :class:`.FromClause`."""
+
+ return Join(self, right, onclause, True)
+
+ def alias(self, name=None, flat=False):
+ """return an alias of this :class:`.FromClause`.
+
+ This is shorthand for calling::
+
+ from sqlalchemy import alias
+ a = alias(self, name=name)
+
+ See :func:`~.expression.alias` for details.
+
+ """
+
+ return Alias(self, name)
+
+ def is_derived_from(self, fromclause):
+ """Return True if this FromClause is 'derived' from the given
+ FromClause.
+
+ An example would be an Alias of a Table is derived from that Table.
+
+ """
+ # this is essentially an "identity" check in the base class.
+ # Other constructs override this to traverse through
+ # contained elements.
+ return fromclause in self._cloned_set
+
+ def _is_lexical_equivalent(self, other):
+ """Return True if this FromClause and the other represent
+ the same lexical identity.
+
+ This tests if either one is a copy of the other, or
+ if they are the same via annotation identity.
+
+ """
+ return self._cloned_set.intersection(other._cloned_set)
+
+ @util.dependencies("sqlalchemy.sql.util")
+ def replace_selectable(self, sqlutil, old, alias):
+ """replace all occurrences of FromClause 'old' with the given Alias
+ object, returning a copy of this :class:`.FromClause`.
+
+ """
+
+ return sqlutil.ClauseAdapter(alias).traverse(self)
+
+ def correspond_on_equivalents(self, column, equivalents):
+ """Return corresponding_column for the given column, or if None
+ search for a match in the given dictionary.
+
+ """
+ col = self.corresponding_column(column, require_embedded=True)
+ if col is None and col in equivalents:
+ for equiv in equivalents[col]:
+ nc = self.corresponding_column(equiv, require_embedded=True)
+ if nc:
+ return nc
+ return col
+
+ def corresponding_column(self, column, require_embedded=False):
+ """Given a :class:`.ColumnElement`, return the exported
+ :class:`.ColumnElement` object from this :class:`.Selectable`
+ which corresponds to that original
+ :class:`~sqlalchemy.schema.Column` via a common ancestor
+ column.
+
+ :param column: the target :class:`.ColumnElement` to be matched
+
+ :param require_embedded: only return corresponding columns for
+ the given :class:`.ColumnElement`, if the given
+ :class:`.ColumnElement` is actually present within a sub-element
+ of this :class:`.FromClause`. Normally the column will match if
+ it merely shares a common ancestor with one of the exported
+ columns of this :class:`.FromClause`.
+
+ """
+
+ def embedded(expanded_proxy_set, target_set):
+ for t in target_set.difference(expanded_proxy_set):
+ if not set(_expand_cloned([t])
+ ).intersection(expanded_proxy_set):
+ return False
+ return True
+
+ # don't dig around if the column is locally present
+ if self.c.contains_column(column):
+ return column
+ col, intersect = None, None
+ target_set = column.proxy_set
+ cols = self.c
+ for c in cols:
+ expanded_proxy_set = set(_expand_cloned(c.proxy_set))
+ i = target_set.intersection(expanded_proxy_set)
+ if i and (not require_embedded
+ or embedded(expanded_proxy_set, target_set)):
+ if col is None:
+
+ # no corresponding column yet, pick this one.
+
+ col, intersect = c, i
+ elif len(i) > len(intersect):
+
+ # 'c' has a larger field of correspondence than
+ # 'col'. i.e. selectable.c.a1_x->a1.c.x->table.c.x
+ # matches a1.c.x->table.c.x better than
+ # selectable.c.x->table.c.x does.
+
+ col, intersect = c, i
+ elif i == intersect:
+
+ # they have the same field of correspondence. see
+ # which proxy_set has fewer columns in it, which
+ # indicates a closer relationship with the root
+ # column. Also take into account the "weight"
+ # attribute which CompoundSelect() uses to give
+ # higher precedence to columns based on vertical
+ # position in the compound statement, and discard
+ # columns that have no reference to the target
+ # column (also occurs with CompoundSelect)
+
+ col_distance = util.reduce(operator.add,
+ [sc._annotations.get('weight', 1) for sc in
+ col.proxy_set if sc.shares_lineage(column)])
+ c_distance = util.reduce(operator.add,
+ [sc._annotations.get('weight', 1) for sc in
+ c.proxy_set if sc.shares_lineage(column)])
+ if c_distance < col_distance:
+ col, intersect = c, i
+ return col
+
+ @property
+ def description(self):
+ """a brief description of this FromClause.
+
+ Used primarily for error message formatting.
+
+ """
+ return getattr(self, 'name', self.__class__.__name__ + " object")
+
+ def _reset_exported(self):
+ """delete memoized collections when a FromClause is cloned."""
+
+ self._memoized_property.expire_instance(self)
+
+ @_memoized_property
+ def columns(self):
+ """A named-based collection of :class:`.ColumnElement` objects
+ maintained by this :class:`.FromClause`.
+
+ The :attr:`.columns`, or :attr:`.c` collection, is the gateway
+ to the construction of SQL expressions using table-bound or
+ other selectable-bound columns::
+
+ select([mytable]).where(mytable.c.somecolumn == 5)
+
+ """
+
+ if '_columns' not in self.__dict__:
+ self._init_collections()
+ self._populate_column_collection()
+ return self._columns.as_immutable()
+
+ @_memoized_property
+ def primary_key(self):
+ """Return the collection of Column objects which comprise the
+ primary key of this FromClause."""
+
+ self._init_collections()
+ self._populate_column_collection()
+ return self.primary_key
+
+ @_memoized_property
+ def foreign_keys(self):
+ """Return the collection of ForeignKey objects which this
+ FromClause references."""
+
+ self._init_collections()
+ self._populate_column_collection()
+ return self.foreign_keys
+
+ c = property(attrgetter('columns'),
+ doc="An alias for the :attr:`.columns` attribute.")
+ _select_iterable = property(attrgetter('columns'))
+
+ def _init_collections(self):
+ assert '_columns' not in self.__dict__
+ assert 'primary_key' not in self.__dict__
+ assert 'foreign_keys' not in self.__dict__
+
+ self._columns = ColumnCollection()
+ self.primary_key = ColumnSet()
+ self.foreign_keys = set()
+
+ @property
+ def _cols_populated(self):
+ return '_columns' in self.__dict__
+
+ def _populate_column_collection(self):
+ """Called on subclasses to establish the .c collection.
+
+ Each implementation has a different way of establishing
+ this collection.
+
+ """
+
+ def _refresh_for_new_column(self, column):
+ """Given a column added to the .c collection of an underlying
+ selectable, produce the local version of that column, assuming this
+ selectable ultimately should proxy this column.
+
+ this is used to "ping" a derived selectable to add a new column
+ to its .c. collection when a Column has been added to one of the
+ Table objects it ultimtely derives from.
+
+ If the given selectable hasn't populated it's .c. collection yet,
+ it should at least pass on the message to the contained selectables,
+ but it will return None.
+
+ This method is currently used by Declarative to allow Table
+ columns to be added to a partially constructed inheritance
+ mapping that may have already produced joins. The method
+ isn't public right now, as the full span of implications
+ and/or caveats aren't yet clear.
+
+ It's also possible that this functionality could be invoked by
+ default via an event, which would require that
+ selectables maintain a weak referencing collection of all
+ derivations.
+
+ """
+ if not self._cols_populated:
+ return None
+ elif column.key in self.columns and self.columns[column.key] is column:
+ return column
+ else:
+ return None
+
+
+class Join(FromClause):
+ """represent a ``JOIN`` construct between two :class:`.FromClause`
+ elements.
+
+ The public constructor function for :class:`.Join` is the module-level
+ :func:`join()` function, as well as the :func:`join()` method available
+ off all :class:`.FromClause` subclasses.
+
+ """
+ __visit_name__ = 'join'
+
+ def __init__(self, left, right, onclause=None, isouter=False):
+ """Construct a new :class:`.Join`.
+
+ The usual entrypoint here is the :func:`~.expression.join`
+ function or the :meth:`.FromClause.join` method of any
+ :class:`.FromClause` object.
+
+ """
+ self.left = _interpret_as_from(left)
+ self.right = _interpret_as_from(right).self_group()
+
+ if onclause is None:
+ self.onclause = self._match_primaries(self.left, self.right)
+ else:
+ self.onclause = onclause
+
+ self.isouter = isouter
+
+ @classmethod
+ def _create_outerjoin(cls, left, right, onclause=None):
+ """Return an ``OUTER JOIN`` clause element.
+
+ The returned object is an instance of :class:`.Join`.
+
+ Similar functionality is also available via the
+ :meth:`~.FromClause.outerjoin()` method on any
+ :class:`.FromClause`.
+
+ :param left: The left side of the join.
+
+ :param right: The right side of the join.
+
+ :param onclause: Optional criterion for the ``ON`` clause, is
+ derived from foreign key relationships established between
+ left and right otherwise.
+
+ To chain joins together, use the :meth:`.FromClause.join` or
+ :meth:`.FromClause.outerjoin` methods on the resulting
+ :class:`.Join` object.
+
+ """
+ return cls(left, right, onclause, isouter=True)
+
+
+ @classmethod
+ def _create_join(cls, left, right, onclause=None, isouter=False):
+ """Return a ``JOIN`` clause element (regular inner join).
+
+ The returned object is an instance of :class:`.Join`.
+
+ Similar functionality is also available via the
+ :meth:`~.FromClause.join()` method on any
+ :class:`.FromClause`.
+
+ :param left: The left side of the join.
+
+ :param right: The right side of the join.
+
+ :param onclause: Optional criterion for the ``ON`` clause, is
+ derived from foreign key relationships established between
+ left and right otherwise.
+
+ :param isouter: if True, produce an outer join; synonymous
+ with :func:`.outerjoin`.
+
+ To chain joins together, use the :meth:`.FromClause.join` or
+ :meth:`.FromClause.outerjoin` methods on the resulting
+ :class:`.Join` object.
+
+
+ """
+ return cls(left, right, onclause, isouter)
+
+
+ @property
+ def description(self):
+ return "Join object on %s(%d) and %s(%d)" % (
+ self.left.description,
+ id(self.left),
+ self.right.description,
+ id(self.right))
+
+ def is_derived_from(self, fromclause):
+ return fromclause is self or \
+ self.left.is_derived_from(fromclause) or \
+ self.right.is_derived_from(fromclause)
+
+ def self_group(self, against=None):
+ return FromGrouping(self)
+
+ @util.dependencies("sqlalchemy.sql.util")
+ def _populate_column_collection(self, sqlutil):
+ columns = [c for c in self.left.columns] + \
+ [c for c in self.right.columns]
+
+ self.primary_key.extend(sqlutil.reduce_columns(
+ (c for c in columns if c.primary_key), self.onclause))
+ self._columns.update((col._label, col) for col in columns)
+ self.foreign_keys.update(itertools.chain(
+ *[col.foreign_keys for col in columns]))
+
+ def _refresh_for_new_column(self, column):
+ col = self.left._refresh_for_new_column(column)
+ if col is None:
+ col = self.right._refresh_for_new_column(column)
+ if col is not None:
+ if self._cols_populated:
+ self._columns[col._label] = col
+ self.foreign_keys.add(col)
+ if col.primary_key:
+ self.primary_key.add(col)
+ return col
+ return None
+
+ def _copy_internals(self, clone=_clone, **kw):
+ self._reset_exported()
+ self.left = clone(self.left, **kw)
+ self.right = clone(self.right, **kw)
+ self.onclause = clone(self.onclause, **kw)
+
+ def get_children(self, **kwargs):
+ return self.left, self.right, self.onclause
+
+ def _match_primaries(self, left, right):
+ if isinstance(left, Join):
+ left_right = left.right
+ else:
+ left_right = None
+ return self._join_condition(left, right, a_subset=left_right)
+
+ @classmethod
+ def _join_condition(cls, a, b, ignore_nonexistent_tables=False,
+ a_subset=None,
+ consider_as_foreign_keys=None):
+ """create a join condition between two tables or selectables.
+
+ e.g.::
+
+ join_condition(tablea, tableb)
+
+ would produce an expression along the lines of::
+
+ tablea.c.id==tableb.c.tablea_id
+
+ The join is determined based on the foreign key relationships
+ between the two selectables. If there are multiple ways
+ to join, or no way to join, an error is raised.
+
+ :param ignore_nonexistent_tables: Deprecated - this
+ flag is no longer used. Only resolution errors regarding
+ the two given tables are propagated.
+
+ :param a_subset: An optional expression that is a sub-component
+ of ``a``. An attempt will be made to join to just this sub-component
+ first before looking at the full ``a`` construct, and if found
+ will be successful even if there are other ways to join to ``a``.
+ This allows the "right side" of a join to be passed thereby
+ providing a "natural join".
+
+ """
+ crit = []
+ constraints = set()
+
+ for left in (a_subset, a):
+ if left is None:
+ continue
+ for fk in sorted(
+ b.foreign_keys,
+ key=lambda fk: fk.parent._creation_order):
+ if consider_as_foreign_keys is not None and \
+ fk.parent not in consider_as_foreign_keys:
+ continue
+ try:
+ col = fk.get_referent(left)
+ except exc.NoReferenceError as nrte:
+ if nrte.table_name == left.name:
+ raise
+ else:
+ continue
+
+ if col is not None:
+ crit.append(col == fk.parent)
+ constraints.add(fk.constraint)
+ if left is not b:
+ for fk in sorted(
+ left.foreign_keys,
+ key=lambda fk: fk.parent._creation_order):
+ if consider_as_foreign_keys is not None and \
+ fk.parent not in consider_as_foreign_keys:
+ continue
+ try:
+ col = fk.get_referent(b)
+ except exc.NoReferenceError as nrte:
+ if nrte.table_name == b.name:
+ raise
+ else:
+ # this is totally covered. can't get
+ # coverage to mark it.
+ continue
+
+ if col is not None:
+ crit.append(col == fk.parent)
+ constraints.add(fk.constraint)
+ if crit:
+ break
+
+ if len(crit) == 0:
+ if isinstance(b, FromGrouping):
+ hint = " Perhaps you meant to convert the right side to a "\
+ "subquery using alias()?"
+ else:
+ hint = ""
+ raise exc.NoForeignKeysError(
+ "Can't find any foreign key relationships "
+ "between '%s' and '%s'.%s" % (a.description, b.description, hint))
+ elif len(constraints) > 1:
+ raise exc.AmbiguousForeignKeysError(
+ "Can't determine join between '%s' and '%s'; "
+ "tables have more than one foreign key "
+ "constraint relationship between them. "
+ "Please specify the 'onclause' of this "
+ "join explicitly." % (a.description, b.description))
+ elif len(crit) == 1:
+ return (crit[0])
+ else:
+ return and_(*crit)
+
+
+ def select(self, whereclause=None, **kwargs):
+ """Create a :class:`.Select` from this :class:`.Join`.
+
+ The equivalent long-hand form, given a :class:`.Join` object
+ ``j``, is::
+
+ from sqlalchemy import select
+ j = select([j.left, j.right], **kw).\\
+ where(whereclause).\\
+ select_from(j)
+
+ :param whereclause: the WHERE criterion that will be sent to
+ the :func:`select()` function
+
+ :param \**kwargs: all other kwargs are sent to the
+ underlying :func:`select()` function.
+
+ """
+ collist = [self.left, self.right]
+
+ return Select(collist, whereclause, from_obj=[self], **kwargs)
+
+ @property
+ def bind(self):
+ return self.left.bind or self.right.bind
+
+ @util.dependencies("sqlalchemy.sql.util")
+ def alias(self, sqlutil, name=None, flat=False):
+ """return an alias of this :class:`.Join`.
+
+ The default behavior here is to first produce a SELECT
+ construct from this :class:`.Join`, then to produce a
+ :class:`.Alias` from that. So given a join of the form::
+
+ j = table_a.join(table_b, table_a.c.id == table_b.c.a_id)
+
+ The JOIN by itself would look like::
+
+ table_a JOIN table_b ON table_a.id = table_b.a_id
+
+ Whereas the alias of the above, ``j.alias()``, would in a
+ SELECT context look like::
+
+ (SELECT table_a.id AS table_a_id, table_b.id AS table_b_id,
+ table_b.a_id AS table_b_a_id
+ FROM table_a
+ JOIN table_b ON table_a.id = table_b.a_id) AS anon_1
+
+ The equivalent long-hand form, given a :class:`.Join` object
+ ``j``, is::
+
+ from sqlalchemy import select, alias
+ j = alias(
+ select([j.left, j.right]).\\
+ select_from(j).\\
+ with_labels(True).\\
+ correlate(False),
+ name=name
+ )
+
+ The selectable produced by :meth:`.Join.alias` features the same
+ columns as that of the two individual selectables presented under
+ a single name - the individual columns are "auto-labeled", meaning
+ the ``.c.`` collection of the resulting :class:`.Alias` represents
+ the names of the individual columns using a ``<tablename>_<columname>``
+ scheme::
+
+ j.c.table_a_id
+ j.c.table_b_a_id
+
+ :meth:`.Join.alias` also features an alternate
+ option for aliasing joins which produces no enclosing SELECT and
+ does not normally apply labels to the column names. The
+ ``flat=True`` option will call :meth:`.FromClause.alias`
+ against the left and right sides individually.
+ Using this option, no new ``SELECT`` is produced;
+ we instead, from a construct as below::
+
+ j = table_a.join(table_b, table_a.c.id == table_b.c.a_id)
+ j = j.alias(flat=True)
+
+ we get a result like this::
+
+ table_a AS table_a_1 JOIN table_b AS table_b_1 ON
+ table_a_1.id = table_b_1.a_id
+
+ The ``flat=True`` argument is also propagated to the contained
+ selectables, so that a composite join such as::
+
+ j = table_a.join(
+ table_b.join(table_c,
+ table_b.c.id == table_c.c.b_id),
+ table_b.c.a_id == table_a.c.id
+ ).alias(flat=True)
+
+ Will produce an expression like::
+
+ table_a AS table_a_1 JOIN (
+ table_b AS table_b_1 JOIN table_c AS table_c_1
+ ON table_b_1.id = table_c_1.b_id
+ ) ON table_a_1.id = table_b_1.a_id
+
+ The standalone :func:`experssion.alias` function as well as the
+ base :meth:`.FromClause.alias` method also support the ``flat=True``
+ argument as a no-op, so that the argument can be passed to the
+ ``alias()`` method of any selectable.
+
+ .. versionadded:: 0.9.0 Added the ``flat=True`` option to create
+ "aliases" of joins without enclosing inside of a SELECT
+ subquery.
+
+ :param name: name given to the alias.
+
+ :param flat: if True, produce an alias of the left and right
+ sides of this :class:`.Join` and return the join of those
+ two selectables. This produces join expression that does not
+ include an enclosing SELECT.
+
+ .. versionadded:: 0.9.0
+
+ .. seealso::
+
+ :func:`~.expression.alias`
+
+ """
+ if flat:
+ assert name is None, "Can't send name argument with flat"
+ left_a, right_a = self.left.alias(flat=True), \
+ self.right.alias(flat=True)
+ adapter = sqlutil.ClauseAdapter(left_a).\
+ chain(sqlutil.ClauseAdapter(right_a))
+
+ return left_a.join(right_a,
+ adapter.traverse(self.onclause), isouter=self.isouter)
+ else:
+ return self.select(use_labels=True, correlate=False).alias(name)
+
+ @property
+ def _hide_froms(self):
+ return itertools.chain(*[_from_objects(x.left, x.right)
+ for x in self._cloned_set])
+
+ @property
+ def _from_objects(self):
+ return [self] + \
+ self.onclause._from_objects + \
+ self.left._from_objects + \
+ self.right._from_objects
+
+
+class Alias(FromClause):
+ """Represents an table or selectable alias (AS).
+
+ Represents an alias, as typically applied to any table or
+ sub-select within a SQL statement using the ``AS`` keyword (or
+ without the keyword on certain databases such as Oracle).
+
+ This object is constructed from the :func:`~.expression.alias` module level
+ function as well as the :meth:`.FromClause.alias` method available on all
+ :class:`.FromClause` subclasses.
+
+ """
+
+ __visit_name__ = 'alias'
+ named_with_column = True
+
+ def __init__(self, selectable, name=None):
+ baseselectable = selectable
+ while isinstance(baseselectable, Alias):
+ baseselectable = baseselectable.element
+ self.original = baseselectable
+ self.supports_execution = baseselectable.supports_execution
+ if self.supports_execution:
+ self._execution_options = baseselectable._execution_options
+ self.element = selectable
+ if name is None:
+ if self.original.named_with_column:
+ name = getattr(self.original, 'name', None)
+ name = _anonymous_label('%%(%d %s)s' % (id(self), name
+ or 'anon'))
+ self.name = name
+
+ @property
+ def description(self):
+ if util.py3k:
+ return self.name
+ else:
+ return self.name.encode('ascii', 'backslashreplace')
+
+ def as_scalar(self):
+ try:
+ return self.element.as_scalar()
+ except AttributeError:
+ raise AttributeError("Element %s does not support "
+ "'as_scalar()'" % self.element)
+
+ def is_derived_from(self, fromclause):
+ if fromclause in self._cloned_set:
+ return True
+ return self.element.is_derived_from(fromclause)
+
+ def _populate_column_collection(self):
+ for col in self.element.columns:
+ col._make_proxy(self)
+
+ def _refresh_for_new_column(self, column):
+ col = self.element._refresh_for_new_column(column)
+ if col is not None:
+ if not self._cols_populated:
+ return None
+ else:
+ return col._make_proxy(self)
+ else:
+ return None
+
+ def _copy_internals(self, clone=_clone, **kw):
+ # don't apply anything to an aliased Table
+ # for now. May want to drive this from
+ # the given **kw.
+ if isinstance(self.element, TableClause):
+ return
+ self._reset_exported()
+ self.element = clone(self.element, **kw)
+ baseselectable = self.element
+ while isinstance(baseselectable, Alias):
+ baseselectable = baseselectable.element
+ self.original = baseselectable
+
+ def get_children(self, column_collections=True, **kw):
+ if column_collections:
+ for c in self.c:
+ yield c
+ yield self.element
+
+ @property
+ def _from_objects(self):
+ return [self]
+
+ @property
+ def bind(self):
+ return self.element.bind
+
+
+class CTE(Alias):
+ """Represent a Common Table Expression.
+
+ The :class:`.CTE` object is obtained using the
+ :meth:`.SelectBase.cte` method from any selectable.
+ See that method for complete examples.
+
+ .. versionadded:: 0.7.6
+
+ """
+ __visit_name__ = 'cte'
+
+ def __init__(self, selectable,
+ name=None,
+ recursive=False,
+ _cte_alias=None,
+ _restates=frozenset()):
+ self.recursive = recursive
+ self._cte_alias = _cte_alias
+ self._restates = _restates
+ super(CTE, self).__init__(selectable, name=name)
+
+ def alias(self, name=None, flat=False):
+ return CTE(
+ self.original,
+ name=name,
+ recursive=self.recursive,
+ _cte_alias=self,
+ )
+
+ def union(self, other):
+ return CTE(
+ self.original.union(other),
+ name=self.name,
+ recursive=self.recursive,
+ _restates=self._restates.union([self])
+ )
+
+ def union_all(self, other):
+ return CTE(
+ self.original.union_all(other),
+ name=self.name,
+ recursive=self.recursive,
+ _restates=self._restates.union([self])
+ )
+
+
+
+
+class FromGrouping(FromClause):
+ """Represent a grouping of a FROM clause"""
+ __visit_name__ = 'grouping'
+
+ def __init__(self, element):
+ self.element = element
+
+ def _init_collections(self):
+ pass
+
+ @property
+ def columns(self):
+ return self.element.columns
+
+ @property
+ def primary_key(self):
+ return self.element.primary_key
+
+ @property
+ def foreign_keys(self):
+ return self.element.foreign_keys
+
+ def is_derived_from(self, element):
+ return self.element.is_derived_from(element)
+
+ def alias(self, **kw):
+ return FromGrouping(self.element.alias(**kw))
+
+ @property
+ def _hide_froms(self):
+ return self.element._hide_froms
+
+ def get_children(self, **kwargs):
+ return self.element,
+
+ def _copy_internals(self, clone=_clone, **kw):
+ self.element = clone(self.element, **kw)
+
+ @property
+ def _from_objects(self):
+ return self.element._from_objects
+
+ def __getattr__(self, attr):
+ return getattr(self.element, attr)
+
+ def __getstate__(self):
+ return {'element': self.element}
+
+ def __setstate__(self, state):
+ self.element = state['element']
+
+class TableClause(Immutable, FromClause):
+ """Represents a minimal "table" construct.
+
+ This is a lightweight table object that has only a name and a
+ collection of columns, which are typically produced
+ by the :func:`.expression.column` function::
+
+ from sqlalchemy.sql import table, column
+
+ user = table("user",
+ column("id"),
+ column("name"),
+ column("description"),
+ )
+
+ The :class:`.TableClause` construct serves as the base for
+ the more commonly used :class:`~.schema.Table` object, providing
+ the usual set of :class:`~.expression.FromClause` services including
+ the ``.c.`` collection and statement generation methods.
+
+ It does **not** provide all the additional schema-level services
+ of :class:`~.schema.Table`, including constraints, references to other
+ tables, or support for :class:`.MetaData`-level services. It's useful
+ on its own as an ad-hoc construct used to generate quick SQL
+ statements when a more fully fledged :class:`~.schema.Table`
+ is not on hand.
+
+ """
+
+ __visit_name__ = 'table'
+
+ named_with_column = True
+
+ implicit_returning = False
+ """:class:`.TableClause` doesn't support having a primary key or column
+ -level defaults, so implicit returning doesn't apply."""
+
+ _autoincrement_column = None
+ """No PK or default support so no autoincrement column."""
+
+ def __init__(self, name, *columns):
+ """Produce a new :class:`.TableClause`.
+
+ The object returned is an instance of :class:`.TableClause`, which
+ represents the "syntactical" portion of the schema-level
+ :class:`~.schema.Table` object.
+ It may be used to construct lightweight table constructs.
+
+ Note that the :func:`.expression.table` function is not part of
+ the ``sqlalchemy`` namespace. It must be imported from the
+ ``sql`` package::
+
+ from sqlalchemy.sql import table, column
+
+ :param name: Name of the table.
+
+ :param columns: A collection of :func:`.expression.column` constructs.
+
+ """
+
+ super(TableClause, self).__init__()
+ self.name = self.fullname = name
+ self._columns = ColumnCollection()
+ self.primary_key = ColumnSet()
+ self.foreign_keys = set()
+ for c in columns:
+ self.append_column(c)
+
+ def _init_collections(self):
+ pass
+
+ @util.memoized_property
+ def description(self):
+ if util.py3k:
+ return self.name
+ else:
+ return self.name.encode('ascii', 'backslashreplace')
+
+ def append_column(self, c):
+ self._columns[c.key] = c
+ c.table = self
+
+ def get_children(self, column_collections=True, **kwargs):
+ if column_collections:
+ return [c for c in self.c]
+ else:
+ return []
+
+ @util.dependencies("sqlalchemy.sql.functions")
+ def count(self, functions, whereclause=None, **params):
+ """return a SELECT COUNT generated against this
+ :class:`.TableClause`."""
+
+ if self.primary_key:
+ col = list(self.primary_key)[0]
+ else:
+ col = list(self.columns)[0]
+ return Select(
+ [functions.func.count(col).label('tbl_row_count')],
+ whereclause,
+ from_obj=[self],
+ **params)
+
+ @util.dependencies("sqlalchemy.sql.dml")
+ def insert(self, dml, values=None, inline=False, **kwargs):
+ """Generate an :func:`.insert` construct against this
+ :class:`.TableClause`.
+
+ E.g.::
+
+ table.insert().values(name='foo')
+
+ See :func:`.insert` for argument and usage information.
+
+ """
+
+ return dml.Insert(self, values=values, inline=inline, **kwargs)
+
+ @util.dependencies("sqlalchemy.sql.dml")
+ def update(self, dml, whereclause=None, values=None, inline=False, **kwargs):
+ """Generate an :func:`.update` construct against this
+ :class:`.TableClause`.
+
+ E.g.::
+
+ table.update().where(table.c.id==7).values(name='foo')
+
+ See :func:`.update` for argument and usage information.
+
+ """
+
+ return dml.Update(self, whereclause=whereclause,
+ values=values, inline=inline, **kwargs)
+
+ @util.dependencies("sqlalchemy.sql.dml")
+ def delete(self, dml, whereclause=None, **kwargs):
+ """Generate a :func:`.delete` construct against this
+ :class:`.TableClause`.
+
+ E.g.::
+
+ table.delete().where(table.c.id==7)
+
+ See :func:`.delete` for argument and usage information.
+
+ """
+
+ return dml.Delete(self, whereclause, **kwargs)
+
+ @property
+ def _from_objects(self):
+ return [self]
+
+
+class SelectBase(Executable, FromClause):
+ """Base class for :class:`.Select` and ``CompoundSelects``."""
+
+ _order_by_clause = ClauseList()
+ _group_by_clause = ClauseList()
+ _limit = None
+ _offset = None
+
+ def __init__(self,
+ use_labels=False,
+ for_update=False,
+ limit=None,
+ offset=None,
+ order_by=None,
+ group_by=None,
+ bind=None,
+ autocommit=None):
+ self.use_labels = use_labels
+ self.for_update = for_update
+ if autocommit is not None:
+ util.warn_deprecated('autocommit on select() is '
+ 'deprecated. Use .execution_options(a'
+ 'utocommit=True)')
+ self._execution_options = \
+ self._execution_options.union(
+ {'autocommit': autocommit})
+ if limit is not None:
+ self._limit = util.asint(limit)
+ if offset is not None:
+ self._offset = util.asint(offset)
+ self._bind = bind
+
+ if order_by is not None:
+ self._order_by_clause = ClauseList(*util.to_list(order_by))
+ if group_by is not None:
+ self._group_by_clause = ClauseList(*util.to_list(group_by))
+
+ def as_scalar(self):
+ """return a 'scalar' representation of this selectable, which can be
+ used as a column expression.
+
+ Typically, a select statement which has only one column in its columns
+ clause is eligible to be used as a scalar expression.
+
+ The returned object is an instance of
+ :class:`ScalarSelect`.
+
+ """
+ return ScalarSelect(self)
+
+ @_generative
+ def apply_labels(self):
+ """return a new selectable with the 'use_labels' flag set to True.
+
+ This will result in column expressions being generated using labels
+ against their table name, such as "SELECT somecolumn AS
+ tablename_somecolumn". This allows selectables which contain multiple
+ FROM clauses to produce a unique set of column names regardless of
+ name conflicts among the individual FROM clauses.
+
+ """
+ self.use_labels = True
+
+ def label(self, name):
+ """return a 'scalar' representation of this selectable, embedded as a
+ subquery with a label.
+
+ .. seealso::
+
+ :meth:`~.SelectBase.as_scalar`.
+
+ """
+ return self.as_scalar().label(name)
+
+ def cte(self, name=None, recursive=False):
+ """Return a new :class:`.CTE`, or Common Table Expression instance.
+
+ Common table expressions are a SQL standard whereby SELECT
+ statements can draw upon secondary statements specified along
+ with the primary statement, using a clause called "WITH".
+ Special semantics regarding UNION can also be employed to
+ allow "recursive" queries, where a SELECT statement can draw
+ upon the set of rows that have previously been selected.
+
+ SQLAlchemy detects :class:`.CTE` objects, which are treated
+ similarly to :class:`.Alias` objects, as special elements
+ to be delivered to the FROM clause of the statement as well
+ as to a WITH clause at the top of the statement.
+
+ .. versionadded:: 0.7.6
+
+ :param name: name given to the common table expression. Like
+ :meth:`._FromClause.alias`, the name can be left as ``None``
+ in which case an anonymous symbol will be used at query
+ compile time.
+ :param recursive: if ``True``, will render ``WITH RECURSIVE``.
+ A recursive common table expression is intended to be used in
+ conjunction with UNION ALL in order to derive rows
+ from those already selected.
+
+ The following examples illustrate two examples from
+ Postgresql's documentation at
+ http://www.postgresql.org/docs/8.4/static/queries-with.html.
+
+ Example 1, non recursive::
+
+ from sqlalchemy import Table, Column, String, Integer, MetaData, \\
+ select, func
+
+ metadata = MetaData()
+
+ orders = Table('orders', metadata,
+ Column('region', String),
+ Column('amount', Integer),
+ Column('product', String),
+ Column('quantity', Integer)
+ )
+
+ regional_sales = select([
+ orders.c.region,
+ func.sum(orders.c.amount).label('total_sales')
+ ]).group_by(orders.c.region).cte("regional_sales")
+
+
+ top_regions = select([regional_sales.c.region]).\\
+ where(
+ regional_sales.c.total_sales >
+ select([
+ func.sum(regional_sales.c.total_sales)/10
+ ])
+ ).cte("top_regions")
+
+ statement = select([
+ orders.c.region,
+ orders.c.product,
+ func.sum(orders.c.quantity).label("product_units"),
+ func.sum(orders.c.amount).label("product_sales")
+ ]).where(orders.c.region.in_(
+ select([top_regions.c.region])
+ )).group_by(orders.c.region, orders.c.product)
+
+ result = conn.execute(statement).fetchall()
+
+ Example 2, WITH RECURSIVE::
+
+ from sqlalchemy import Table, Column, String, Integer, MetaData, \\
+ select, func
+
+ metadata = MetaData()
+
+ parts = Table('parts', metadata,
+ Column('part', String),
+ Column('sub_part', String),
+ Column('quantity', Integer),
+ )
+
+ included_parts = select([
+ parts.c.sub_part,
+ parts.c.part,
+ parts.c.quantity]).\\
+ where(parts.c.part=='our part').\\
+ cte(recursive=True)
+
+
+ incl_alias = included_parts.alias()
+ parts_alias = parts.alias()
+ included_parts = included_parts.union_all(
+ select([
+ parts_alias.c.part,
+ parts_alias.c.sub_part,
+ parts_alias.c.quantity
+ ]).
+ where(parts_alias.c.part==incl_alias.c.sub_part)
+ )
+
+ statement = select([
+ included_parts.c.sub_part,
+ func.sum(included_parts.c.quantity).
+ label('total_quantity')
+ ]).\
+ select_from(included_parts.join(parts,
+ included_parts.c.part==parts.c.part)).\\
+ group_by(included_parts.c.sub_part)
+
+ result = conn.execute(statement).fetchall()
+
+
+ .. seealso::
+
+ :meth:`.orm.query.Query.cte` - ORM version of :meth:`.SelectBase.cte`.
+
+ """
+ return CTE(self, name=name, recursive=recursive)
+
+ @_generative
+ @util.deprecated('0.6',
+ message=":func:`.autocommit` is deprecated. Use "
+ ":func:`.Executable.execution_options` with the "
+ "'autocommit' flag.")
+ def autocommit(self):
+ """return a new selectable with the 'autocommit' flag set to
+ True."""
+
+ self._execution_options = \
+ self._execution_options.union({'autocommit': True})
+
+ def _generate(self):
+ """Override the default _generate() method to also clear out
+ exported collections."""
+
+ s = self.__class__.__new__(self.__class__)
+ s.__dict__ = self.__dict__.copy()
+ s._reset_exported()
+ return s
+
+ @_generative
+ def limit(self, limit):
+ """return a new selectable with the given LIMIT criterion
+ applied."""
+
+ self._limit = util.asint(limit)
+
+ @_generative
+ def offset(self, offset):
+ """return a new selectable with the given OFFSET criterion
+ applied."""
+
+ self._offset = util.asint(offset)
+
+ @_generative
+ def order_by(self, *clauses):
+ """return a new selectable with the given list of ORDER BY
+ criterion applied.
+
+ The criterion will be appended to any pre-existing ORDER BY
+ criterion.
+
+ """
+
+ self.append_order_by(*clauses)
+
+ @_generative
+ def group_by(self, *clauses):
+ """return a new selectable with the given list of GROUP BY
+ criterion applied.
+
+ The criterion will be appended to any pre-existing GROUP BY
+ criterion.
+
+ """
+
+ self.append_group_by(*clauses)
+
+ def append_order_by(self, *clauses):
+ """Append the given ORDER BY criterion applied to this selectable.
+
+ The criterion will be appended to any pre-existing ORDER BY criterion.
+
+ This is an **in-place** mutation method; the
+ :meth:`~.SelectBase.order_by` method is preferred, as it provides standard
+ :term:`method chaining`.
+
+ """
+ if len(clauses) == 1 and clauses[0] is None:
+ self._order_by_clause = ClauseList()
+ else:
+ if getattr(self, '_order_by_clause', None) is not None:
+ clauses = list(self._order_by_clause) + list(clauses)
+ self._order_by_clause = ClauseList(*clauses)
+
+ def append_group_by(self, *clauses):
+ """Append the given GROUP BY criterion applied to this selectable.
+
+ The criterion will be appended to any pre-existing GROUP BY criterion.
+
+ This is an **in-place** mutation method; the
+ :meth:`~.SelectBase.group_by` method is preferred, as it provides standard
+ :term:`method chaining`.
+
+ """
+ if len(clauses) == 1 and clauses[0] is None:
+ self._group_by_clause = ClauseList()
+ else:
+ if getattr(self, '_group_by_clause', None) is not None:
+ clauses = list(self._group_by_clause) + list(clauses)
+ self._group_by_clause = ClauseList(*clauses)
+
+ @property
+ def _from_objects(self):
+ return [self]
+
+
+class CompoundSelect(SelectBase):
+ """Forms the basis of ``UNION``, ``UNION ALL``, and other
+ SELECT-based set operations."""
+
+ __visit_name__ = 'compound_select'
+
+ UNION = util.symbol('UNION')
+ UNION_ALL = util.symbol('UNION ALL')
+ EXCEPT = util.symbol('EXCEPT')
+ EXCEPT_ALL = util.symbol('EXCEPT ALL')
+ INTERSECT = util.symbol('INTERSECT')
+ INTERSECT_ALL = util.symbol('INTERSECT ALL')
+
+ def __init__(self, keyword, *selects, **kwargs):
+ self._auto_correlate = kwargs.pop('correlate', False)
+ self.keyword = keyword
+ self.selects = []
+
+ numcols = None
+
+ # some DBs do not like ORDER BY in the inner queries of a UNION, etc.
+ for n, s in enumerate(selects):
+ s = _clause_element_as_expr(s)
+
+ if not numcols:
+ numcols = len(s.c)
+ elif len(s.c) != numcols:
+ raise exc.ArgumentError('All selectables passed to '
+ 'CompoundSelect must have identical numbers of '
+ 'columns; select #%d has %d columns, select '
+ '#%d has %d' % (1, len(self.selects[0].c), n
+ + 1, len(s.c)))
+
+ self.selects.append(s.self_group(self))
+
+ SelectBase.__init__(self, **kwargs)
+
+ @classmethod
+ def _create_union(cls, *selects, **kwargs):
+ """Return a ``UNION`` of multiple selectables.
+
+ The returned object is an instance of
+ :class:`.CompoundSelect`.
+
+ A similar :func:`union()` method is available on all
+ :class:`.FromClause` subclasses.
+
+ \*selects
+ a list of :class:`.Select` instances.
+
+ \**kwargs
+ available keyword arguments are the same as those of
+ :func:`select`.
+
+ """
+ return CompoundSelect(CompoundSelect.UNION, *selects, **kwargs)
+
+ @classmethod
+ def _create_union_all(cls, *selects, **kwargs):
+ """Return a ``UNION ALL`` of multiple selectables.
+
+ The returned object is an instance of
+ :class:`.CompoundSelect`.
+
+ A similar :func:`union_all()` method is available on all
+ :class:`.FromClause` subclasses.
+
+ \*selects
+ a list of :class:`.Select` instances.
+
+ \**kwargs
+ available keyword arguments are the same as those of
+ :func:`select`.
+
+ """
+ return CompoundSelect(CompoundSelect.UNION_ALL, *selects, **kwargs)
+
+
+ @classmethod
+ def _create_except(cls, *selects, **kwargs):
+ """Return an ``EXCEPT`` of multiple selectables.
+
+ The returned object is an instance of
+ :class:`.CompoundSelect`.
+
+ \*selects
+ a list of :class:`.Select` instances.
+
+ \**kwargs
+ available keyword arguments are the same as those of
+ :func:`select`.
+
+ """
+ return CompoundSelect(CompoundSelect.EXCEPT, *selects, **kwargs)
+
+
+ @classmethod
+ def _create_except_all(cls, *selects, **kwargs):
+ """Return an ``EXCEPT ALL`` of multiple selectables.
+
+ The returned object is an instance of
+ :class:`.CompoundSelect`.
+
+ \*selects
+ a list of :class:`.Select` instances.
+
+ \**kwargs
+ available keyword arguments are the same as those of
+ :func:`select`.
+
+ """
+ return CompoundSelect(CompoundSelect.EXCEPT_ALL, *selects, **kwargs)
+
+
+ @classmethod
+ def _create_intersect(cls, *selects, **kwargs):
+ """Return an ``INTERSECT`` of multiple selectables.
+
+ The returned object is an instance of
+ :class:`.CompoundSelect`.
+
+ \*selects
+ a list of :class:`.Select` instances.
+
+ \**kwargs
+ available keyword arguments are the same as those of
+ :func:`select`.
+
+ """
+ return CompoundSelect(CompoundSelect.INTERSECT, *selects, **kwargs)
+
+
+ @classmethod
+ def _create_intersect_all(cls, *selects, **kwargs):
+ """Return an ``INTERSECT ALL`` of multiple selectables.
+
+ The returned object is an instance of
+ :class:`.CompoundSelect`.
+
+ \*selects
+ a list of :class:`.Select` instances.
+
+ \**kwargs
+ available keyword arguments are the same as those of
+ :func:`select`.
+
+ """
+ return CompoundSelect(CompoundSelect.INTERSECT_ALL, *selects, **kwargs)
+
+
+ def _scalar_type(self):
+ return self.selects[0]._scalar_type()
+
+ def self_group(self, against=None):
+ return FromGrouping(self)
+
+ def is_derived_from(self, fromclause):
+ for s in self.selects:
+ if s.is_derived_from(fromclause):
+ return True
+ return False
+
+ def _populate_column_collection(self):
+ for cols in zip(*[s.c for s in self.selects]):
+
+ # this is a slightly hacky thing - the union exports a
+ # column that resembles just that of the *first* selectable.
+ # to get at a "composite" column, particularly foreign keys,
+ # you have to dig through the proxies collection which we
+ # generate below. We may want to improve upon this, such as
+ # perhaps _make_proxy can accept a list of other columns
+ # that are "shared" - schema.column can then copy all the
+ # ForeignKeys in. this would allow the union() to have all
+ # those fks too.
+
+ proxy = cols[0]._make_proxy(self,
+ name=cols[0]._label if self.use_labels else None,
+ key=cols[0]._key_label if self.use_labels else None)
+
+ # hand-construct the "_proxies" collection to include all
+ # derived columns place a 'weight' annotation corresponding
+ # to how low in the list of select()s the column occurs, so
+ # that the corresponding_column() operation can resolve
+ # conflicts
+
+ proxy._proxies = [c._annotate({'weight': i + 1}) for (i,
+ c) in enumerate(cols)]
+
+ def _refresh_for_new_column(self, column):
+ for s in self.selects:
+ s._refresh_for_new_column(column)
+
+ if not self._cols_populated:
+ return None
+
+ raise NotImplementedError("CompoundSelect constructs don't support "
+ "addition of columns to underlying selectables")
+
+ def _copy_internals(self, clone=_clone, **kw):
+ self._reset_exported()
+ self.selects = [clone(s, **kw) for s in self.selects]
+ if hasattr(self, '_col_map'):
+ del self._col_map
+ for attr in ('_order_by_clause', '_group_by_clause'):
+ if getattr(self, attr) is not None:
+ setattr(self, attr, clone(getattr(self, attr), **kw))
+
+ def get_children(self, column_collections=True, **kwargs):
+ return (column_collections and list(self.c) or []) \
+ + [self._order_by_clause, self._group_by_clause] \
+ + list(self.selects)
+
+ def bind(self):
+ if self._bind:
+ return self._bind
+ for s in self.selects:
+ e = s.bind
+ if e:
+ return e
+ else:
+ return None
+
+ def _set_bind(self, bind):
+ self._bind = bind
+ bind = property(bind, _set_bind)
+
+
+class HasPrefixes(object):
+ _prefixes = ()
+
+ @_generative
+ def prefix_with(self, *expr, **kw):
+ """Add one or more expressions following the statement keyword, i.e.
+ SELECT, INSERT, UPDATE, or DELETE. Generative.
+
+ This is used to support backend-specific prefix keywords such as those
+ provided by MySQL.
+
+ E.g.::
+
+ stmt = table.insert().prefix_with("LOW_PRIORITY", dialect="mysql")
+
+ Multiple prefixes can be specified by multiple calls
+ to :meth:`.prefix_with`.
+
+ :param \*expr: textual or :class:`.ClauseElement` construct which
+ will be rendered following the INSERT, UPDATE, or DELETE
+ keyword.
+ :param \**kw: A single keyword 'dialect' is accepted. This is an
+ optional string dialect name which will
+ limit rendering of this prefix to only that dialect.
+
+ """
+ dialect = kw.pop('dialect', None)
+ if kw:
+ raise exc.ArgumentError("Unsupported argument(s): %s" %
+ ",".join(kw))
+ self._setup_prefixes(expr, dialect)
+
+ def _setup_prefixes(self, prefixes, dialect=None):
+ self._prefixes = self._prefixes + tuple(
+ [(_literal_as_text(p), dialect) for p in prefixes])
+
+class Select(HasPrefixes, SelectBase):
+ """Represents a ``SELECT`` statement.
+
+ """
+
+ __visit_name__ = 'select'
+
+ _prefixes = ()
+ _hints = util.immutabledict()
+ _distinct = False
+ _from_cloned = None
+ _correlate = ()
+ _correlate_except = None
+ _memoized_property = SelectBase._memoized_property
+
+ def __init__(self,
+ columns=None,
+ whereclause=None,
+ from_obj=None,
+ distinct=False,
+ having=None,
+ correlate=True,
+ prefixes=None,
+ **kwargs):
+ """Construct a new :class:`.Select`.
+
+ Similar functionality is also available via the :meth:`.FromClause.select`
+ method on any :class:`.FromClause`.
+
+ All arguments which accept :class:`.ClauseElement` arguments also accept
+ string arguments, which will be converted as appropriate into
+ either :func:`text()` or :func:`literal_column()` constructs.
+
+ .. seealso::
+
+ :ref:`coretutorial_selecting` - Core Tutorial description of
+ :func:`.select`.
+
+ :param columns:
+ A list of :class:`.ClauseElement` objects, typically
+ :class:`.ColumnElement` objects or subclasses, which will form the
+ columns clause of the resulting statement. For all members which are
+ instances of :class:`.Selectable`, the individual :class:`.ColumnElement`
+ members of the :class:`.Selectable` will be added individually to the
+ columns clause. For example, specifying a
+ :class:`~sqlalchemy.schema.Table` instance will result in all the
+ contained :class:`~sqlalchemy.schema.Column` objects within to be added
+ to the columns clause.
+
+ This argument is not present on the form of :func:`select()`
+ available on :class:`~sqlalchemy.schema.Table`.
+
+ :param whereclause:
+ A :class:`.ClauseElement` expression which will be used to form the
+ ``WHERE`` clause.
+
+ :param from_obj:
+ A list of :class:`.ClauseElement` objects which will be added to the
+ ``FROM`` clause of the resulting statement. Note that "from" objects are
+ automatically located within the columns and whereclause ClauseElements.
+ Use this parameter to explicitly specify "from" objects which are not
+ automatically locatable. This could include
+ :class:`~sqlalchemy.schema.Table` objects that aren't otherwise present,
+ or :class:`.Join` objects whose presence will supercede that of the
+ :class:`~sqlalchemy.schema.Table` objects already located in the other
+ clauses.
+
+ :param autocommit:
+ Deprecated. Use .execution_options(autocommit=<True|False>)
+ to set the autocommit option.
+
+ :param bind=None:
+ an :class:`~.base.Engine` or :class:`~.base.Connection` instance
+ to which the
+ resulting :class:`.Select` object will be bound. The :class:`.Select`
+ object will otherwise automatically bind to whatever
+ :class:`~.base.Connectable` instances can be located within its contained
+ :class:`.ClauseElement` members.
+
+ :param correlate=True:
+ indicates that this :class:`.Select` object should have its
+ contained :class:`.FromClause` elements "correlated" to an enclosing
+ :class:`.Select` object. This means that any :class:`.ClauseElement`
+ instance within the "froms" collection of this :class:`.Select`
+ which is also present in the "froms" collection of an
+ enclosing select will not be rendered in the ``FROM`` clause
+ of this select statement.
+
+ :param distinct=False:
+ when ``True``, applies a ``DISTINCT`` qualifier to the columns
+ clause of the resulting statement.
+
+ The boolean argument may also be a column expression or list
+ of column expressions - this is a special calling form which
+ is understood by the Postgresql dialect to render the
+ ``DISTINCT ON (<columns>)`` syntax.
+
+ ``distinct`` is also available via the :meth:`~.Select.distinct`
+ generative method.
+
+ :param for_update=False:
+ when ``True``, applies ``FOR UPDATE`` to the end of the
+ resulting statement.
+
+ Certain database dialects also support
+ alternate values for this parameter:
+
+ * With the MySQL dialect, the value ``"read"`` translates to
+ ``LOCK IN SHARE MODE``.
+ * With the Oracle and Postgresql dialects, the value ``"nowait"``
+ translates to ``FOR UPDATE NOWAIT``.
+ * With the Postgresql dialect, the values "read" and ``"read_nowait"``
+ translate to ``FOR SHARE`` and ``FOR SHARE NOWAIT``, respectively.
+
+ .. versionadded:: 0.7.7
+
+ :param group_by:
+ a list of :class:`.ClauseElement` objects which will comprise the
+ ``GROUP BY`` clause of the resulting select.
+
+ :param having:
+ a :class:`.ClauseElement` that will comprise the ``HAVING`` clause
+ of the resulting select when ``GROUP BY`` is used.
+
+ :param limit=None:
+ a numerical value which usually compiles to a ``LIMIT``
+ expression in the resulting select. Databases that don't
+ support ``LIMIT`` will attempt to provide similar
+ functionality.
+
+ :param offset=None:
+ a numeric value which usually compiles to an ``OFFSET``
+ expression in the resulting select. Databases that don't
+ support ``OFFSET`` will attempt to provide similar
+ functionality.
+
+ :param order_by:
+ a scalar or list of :class:`.ClauseElement` objects which will
+ comprise the ``ORDER BY`` clause of the resulting select.
+
+ :param use_labels=False:
+ when ``True``, the statement will be generated using labels
+ for each column in the columns clause, which qualify each
+ column with its parent table's (or aliases) name so that name
+ conflicts between columns in different tables don't occur.
+ The format of the label is <tablename>_<column>. The "c"
+ collection of the resulting :class:`.Select` object will use these
+ names as well for targeting column members.
+
+ use_labels is also available via the :meth:`~.SelectBase.apply_labels`
+ generative method.
+
+ """
+ self._auto_correlate = correlate
+ if distinct is not False:
+ if distinct is True:
+ self._distinct = True
+ else:
+ self._distinct = [
+ _literal_as_text(e)
+ for e in util.to_list(distinct)
+ ]
+
+ if from_obj is not None:
+ self._from_obj = util.OrderedSet(
+ _interpret_as_from(f)
+ for f in util.to_list(from_obj))
+ else:
+ self._from_obj = util.OrderedSet()
+
+ try:
+ cols_present = bool(columns)
+ except TypeError:
+ raise exc.ArgumentError("columns argument to select() must "
+ "be a Python list or other iterable")
+
+ if cols_present:
+ self._raw_columns = []
+ for c in columns:
+ c = _interpret_as_column_or_from(c)
+ if isinstance(c, ScalarSelect):
+ c = c.self_group(against=operators.comma_op)
+ self._raw_columns.append(c)
+ else:
+ self._raw_columns = []
+
+ if whereclause is not None:
+ self._whereclause = _literal_as_text(whereclause)
+ else:
+ self._whereclause = None
+
+ if having is not None:
+ self._having = _literal_as_text(having)
+ else:
+ self._having = None
+
+ if prefixes:
+ self._setup_prefixes(prefixes)
+
+ SelectBase.__init__(self, **kwargs)
+
+ @property
+ def _froms(self):
+ # would love to cache this,
+ # but there's just enough edge cases, particularly now that
+ # declarative encourages construction of SQL expressions
+ # without tables present, to just regen this each time.
+ froms = []
+ seen = set()
+ translate = self._from_cloned
+
+ def add(items):
+ for item in items:
+ if translate and item in translate:
+ item = translate[item]
+ if not seen.intersection(item._cloned_set):
+ froms.append(item)
+ seen.update(item._cloned_set)
+
+ add(_from_objects(*self._raw_columns))
+ if self._whereclause is not None:
+ add(_from_objects(self._whereclause))
+ add(self._from_obj)
+
+ return froms
+
+ def _get_display_froms(self, explicit_correlate_froms=None,
+ implicit_correlate_froms=None):
+ """Return the full list of 'from' clauses to be displayed.
+
+ Takes into account a set of existing froms which may be
+ rendered in the FROM clause of enclosing selects; this Select
+ may want to leave those absent if it is automatically
+ correlating.
+
+ """
+ froms = self._froms
+
+ toremove = set(itertools.chain(*[
+ _expand_cloned(f._hide_froms)
+ for f in froms]))
+ if toremove:
+ # if we're maintaining clones of froms,
+ # add the copies out to the toremove list. only include
+ # clones that are lexical equivalents.
+ if self._from_cloned:
+ toremove.update(
+ self._from_cloned[f] for f in
+ toremove.intersection(self._from_cloned)
+ if self._from_cloned[f]._is_lexical_equivalent(f)
+ )
+ # filter out to FROM clauses not in the list,
+ # using a list to maintain ordering
+ froms = [f for f in froms if f not in toremove]
+
+ if self._correlate:
+ to_correlate = self._correlate
+ if to_correlate:
+ froms = [
+ f for f in froms if f not in
+ _cloned_intersection(
+ _cloned_intersection(froms, explicit_correlate_froms or ()),
+ to_correlate
+ )
+ ]
+
+ if self._correlate_except is not None:
+
+ froms = [
+ f for f in froms if f not in
+ _cloned_difference(
+ _cloned_intersection(froms, explicit_correlate_froms or ()),
+ self._correlate_except
+ )
+ ]
+
+ if self._auto_correlate and \
+ implicit_correlate_froms and \
+ len(froms) > 1:
+
+ froms = [
+ f for f in froms if f not in
+ _cloned_intersection(froms, implicit_correlate_froms)
+ ]
+
+ if not len(froms):
+ raise exc.InvalidRequestError("Select statement '%s"
+ "' returned no FROM clauses due to "
+ "auto-correlation; specify "
+ "correlate(<tables>) to control "
+ "correlation manually." % self)
+
+ return froms
+
+ def _scalar_type(self):
+ elem = self._raw_columns[0]
+ cols = list(elem._select_iterable)
+ return cols[0].type
+
+ @property
+ def froms(self):
+ """Return the displayed list of FromClause elements."""
+
+ return self._get_display_froms()
+
+ @_generative
+ def with_hint(self, selectable, text, dialect_name='*'):
+ """Add an indexing hint for the given selectable to this
+ :class:`.Select`.
+
+ The text of the hint is rendered in the appropriate
+ location for the database backend in use, relative
+ to the given :class:`.Table` or :class:`.Alias` passed as the
+ ``selectable`` argument. The dialect implementation
+ typically uses Python string substitution syntax
+ with the token ``%(name)s`` to render the name of
+ the table or alias. E.g. when using Oracle, the
+ following::
+
+ select([mytable]).\\
+ with_hint(mytable, "+ index(%(name)s ix_mytable)")
+
+ Would render SQL as::
+
+ select /*+ index(mytable ix_mytable) */ ... from mytable
+
+ The ``dialect_name`` option will limit the rendering of a particular
+ hint to a particular backend. Such as, to add hints for both Oracle
+ and Sybase simultaneously::
+
+ select([mytable]).\\
+ with_hint(mytable, "+ index(%(name)s ix_mytable)", 'oracle').\\
+ with_hint(mytable, "WITH INDEX ix_mytable", 'sybase')
+
+ """
+ self._hints = self._hints.union(
+ {(selectable, dialect_name): text})
+
+ @property
+ def type(self):
+ raise exc.InvalidRequestError("Select objects don't have a type. "
+ "Call as_scalar() on this Select object "
+ "to return a 'scalar' version of this Select.")
+
+ @_memoized_property.method
+ def locate_all_froms(self):
+ """return a Set of all FromClause elements referenced by this Select.
+
+ This set is a superset of that returned by the ``froms`` property,
+ which is specifically for those FromClause elements that would
+ actually be rendered.
+
+ """
+ froms = self._froms
+ return froms + list(_from_objects(*froms))
+
+ @property
+ def inner_columns(self):
+ """an iterator of all ColumnElement expressions which would
+ be rendered into the columns clause of the resulting SELECT statement.
+
+ """
+ return _select_iterables(self._raw_columns)
+
+ def is_derived_from(self, fromclause):
+ if self in fromclause._cloned_set:
+ return True
+
+ for f in self.locate_all_froms():
+ if f.is_derived_from(fromclause):
+ return True
+ return False
+
+ def _copy_internals(self, clone=_clone, **kw):
+
+ # Select() object has been cloned and probably adapted by the
+ # given clone function. Apply the cloning function to internal
+ # objects
+
+ # 1. keep a dictionary of the froms we've cloned, and what
+ # they've become. This is consulted later when we derive
+ # additional froms from "whereclause" and the columns clause,
+ # which may still reference the uncloned parent table.
+ # as of 0.7.4 we also put the current version of _froms, which
+ # gets cleared on each generation. previously we were "baking"
+ # _froms into self._from_obj.
+ self._from_cloned = from_cloned = dict((f, clone(f, **kw))
+ for f in self._from_obj.union(self._froms))
+
+ # 3. update persistent _from_obj with the cloned versions.
+ self._from_obj = util.OrderedSet(from_cloned[f] for f in
+ self._from_obj)
+
+ # the _correlate collection is done separately, what can happen
+ # here is the same item is _correlate as in _from_obj but the
+ # _correlate version has an annotation on it - (specifically
+ # RelationshipProperty.Comparator._criterion_exists() does
+ # this). Also keep _correlate liberally open with it's previous
+ # contents, as this set is used for matching, not rendering.
+ self._correlate = set(clone(f) for f in
+ self._correlate).union(self._correlate)
+
+ # 4. clone other things. The difficulty here is that Column
+ # objects are not actually cloned, and refer to their original
+ # .table, resulting in the wrong "from" parent after a clone
+ # operation. Hence _from_cloned and _from_obj supercede what is
+ # present here.
+ self._raw_columns = [clone(c, **kw) for c in self._raw_columns]
+ for attr in '_whereclause', '_having', '_order_by_clause', \
+ '_group_by_clause':
+ if getattr(self, attr) is not None:
+ setattr(self, attr, clone(getattr(self, attr), **kw))
+
+ # erase exported column list, _froms collection,
+ # etc.
+ self._reset_exported()
+
+ def get_children(self, column_collections=True, **kwargs):
+ """return child elements as per the ClauseElement specification."""
+
+ return (column_collections and list(self.columns) or []) + \
+ self._raw_columns + list(self._froms) + \
+ [x for x in
+ (self._whereclause, self._having,
+ self._order_by_clause, self._group_by_clause)
+ if x is not None]
+
+ @_generative
+ def column(self, column):
+ """return a new select() construct with the given column expression
+ added to its columns clause.
+
+ """
+ self.append_column(column)
+
+ @util.dependencies("sqlalchemy.sql.util")
+ def reduce_columns(self, sqlutil, only_synonyms=True):
+ """Return a new :func`.select` construct with redundantly
+ named, equivalently-valued columns removed from the columns clause.
+
+ "Redundant" here means two columns where one refers to the
+ other either based on foreign key, or via a simple equality
+ comparison in the WHERE clause of the statement. The primary purpose
+ of this method is to automatically construct a select statement
+ with all uniquely-named columns, without the need to use
+ table-qualified labels as :meth:`.apply_labels` does.
+
+ When columns are omitted based on foreign key, the referred-to
+ column is the one that's kept. When columns are omitted based on
+ WHERE eqivalence, the first column in the columns clause is the
+ one that's kept.
+
+ :param only_synonyms: when True, limit the removal of columns
+ to those which have the same name as the equivalent. Otherwise,
+ all columns that are equivalent to another are removed.
+
+ .. versionadded:: 0.8
+
+ """
+ return self.with_only_columns(
+ sqlutil.reduce_columns(
+ self.inner_columns,
+ only_synonyms=only_synonyms,
+ *(self._whereclause, ) + tuple(self._from_obj)
+ )
+ )
+
+ @_generative
+ def with_only_columns(self, columns):
+ """Return a new :func:`.select` construct with its columns
+ clause replaced with the given columns.
+
+ .. versionchanged:: 0.7.3
+ Due to a bug fix, this method has a slight
+ behavioral change as of version 0.7.3.
+ Prior to version 0.7.3, the FROM clause of
+ a :func:`.select` was calculated upfront and as new columns
+ were added; in 0.7.3 and later it's calculated
+ at compile time, fixing an issue regarding late binding
+ of columns to parent tables. This changes the behavior of
+ :meth:`.Select.with_only_columns` in that FROM clauses no
+ longer represented in the new list are dropped,
+ but this behavior is more consistent in
+ that the FROM clauses are consistently derived from the
+ current columns clause. The original intent of this method
+ is to allow trimming of the existing columns list to be fewer
+ columns than originally present; the use case of replacing
+ the columns list with an entirely different one hadn't
+ been anticipated until 0.7.3 was released; the usage
+ guidelines below illustrate how this should be done.
+
+ This method is exactly equivalent to as if the original
+ :func:`.select` had been called with the given columns
+ clause. I.e. a statement::
+
+ s = select([table1.c.a, table1.c.b])
+ s = s.with_only_columns([table1.c.b])
+
+ should be exactly equivalent to::
+
+ s = select([table1.c.b])
+
+ This means that FROM clauses which are only derived
+ from the column list will be discarded if the new column
+ list no longer contains that FROM::
+
+ >>> table1 = table('t1', column('a'), column('b'))
+ >>> table2 = table('t2', column('a'), column('b'))
+ >>> s1 = select([table1.c.a, table2.c.b])
+ >>> print s1
+ SELECT t1.a, t2.b FROM t1, t2
+ >>> s2 = s1.with_only_columns([table2.c.b])
+ >>> print s2
+ SELECT t2.b FROM t1
+
+ The preferred way to maintain a specific FROM clause
+ in the construct, assuming it won't be represented anywhere
+ else (i.e. not in the WHERE clause, etc.) is to set it using
+ :meth:`.Select.select_from`::
+
+ >>> s1 = select([table1.c.a, table2.c.b]).\\
+ ... select_from(table1.join(table2,
+ ... table1.c.a==table2.c.a))
+ >>> s2 = s1.with_only_columns([table2.c.b])
+ >>> print s2
+ SELECT t2.b FROM t1 JOIN t2 ON t1.a=t2.a
+
+ Care should also be taken to use the correct
+ set of column objects passed to :meth:`.Select.with_only_columns`.
+ Since the method is essentially equivalent to calling the
+ :func:`.select` construct in the first place with the given
+ columns, the columns passed to :meth:`.Select.with_only_columns`
+ should usually be a subset of those which were passed
+ to the :func:`.select` construct, not those which are available
+ from the ``.c`` collection of that :func:`.select`. That
+ is::
+
+ s = select([table1.c.a, table1.c.b]).select_from(table1)
+ s = s.with_only_columns([table1.c.b])
+
+ and **not**::
+
+ # usually incorrect
+ s = s.with_only_columns([s.c.b])
+
+ The latter would produce the SQL::
+
+ SELECT b
+ FROM (SELECT t1.a AS a, t1.b AS b
+ FROM t1), t1
+
+ Since the :func:`.select` construct is essentially being
+ asked to select both from ``table1`` as well as itself.
+
+ """
+ self._reset_exported()
+ rc = []
+ for c in columns:
+ c = _interpret_as_column_or_from(c)
+ if isinstance(c, ScalarSelect):
+ c = c.self_group(against=operators.comma_op)
+ rc.append(c)
+ self._raw_columns = rc
+
+ @_generative
+ def where(self, whereclause):
+ """return a new select() construct with the given expression added to
+ its WHERE clause, joined to the existing clause via AND, if any.
+
+ """
+
+ self.append_whereclause(whereclause)
+
+ @_generative
+ def having(self, having):
+ """return a new select() construct with the given expression added to
+ its HAVING clause, joined to the existing clause via AND, if any.
+
+ """
+ self.append_having(having)
+
+ @_generative
+ def distinct(self, *expr):
+ """Return a new select() construct which will apply DISTINCT to its
+ columns clause.
+
+ :param \*expr: optional column expressions. When present,
+ the Postgresql dialect will render a ``DISTINCT ON (<expressions>>)``
+ construct.
+
+ """
+ if expr:
+ expr = [_literal_as_text(e) for e in expr]
+ if isinstance(self._distinct, list):
+ self._distinct = self._distinct + expr
+ else:
+ self._distinct = expr
+ else:
+ self._distinct = True
+
+ @_generative
+ def select_from(self, fromclause):
+ """return a new :func:`.select` construct with the
+ given FROM expression
+ merged into its list of FROM objects.
+
+ E.g.::
+
+ table1 = table('t1', column('a'))
+ table2 = table('t2', column('b'))
+ s = select([table1.c.a]).\\
+ select_from(
+ table1.join(table2, table1.c.a==table2.c.b)
+ )
+
+ The "from" list is a unique set on the identity of each element,
+ so adding an already present :class:`.Table` or other selectable
+ will have no effect. Passing a :class:`.Join` that refers
+ to an already present :class:`.Table` or other selectable will have
+ the effect of concealing the presence of that selectable as
+ an individual element in the rendered FROM list, instead
+ rendering it into a JOIN clause.
+
+ While the typical purpose of :meth:`.Select.select_from` is to
+ replace the default, derived FROM clause with a join, it can
+ also be called with individual table elements, multiple times
+ if desired, in the case that the FROM clause cannot be fully
+ derived from the columns clause::
+
+ select([func.count('*')]).select_from(table1)
+
+ """
+ self.append_from(fromclause)
+
+ @_generative
+ def correlate(self, *fromclauses):
+ """return a new :class:`.Select` which will correlate the given FROM
+ clauses to that of an enclosing :class:`.Select`.
+
+ Calling this method turns off the :class:`.Select` object's
+ default behavior of "auto-correlation". Normally, FROM elements
+ which appear in a :class:`.Select` that encloses this one via
+ its :term:`WHERE clause`, ORDER BY, HAVING or
+ :term:`columns clause` will be omitted from this :class:`.Select`
+ object's :term:`FROM clause`.
+ Setting an explicit correlation collection using the
+ :meth:`.Select.correlate` method provides a fixed list of FROM objects
+ that can potentially take place in this process.
+
+ When :meth:`.Select.correlate` is used to apply specific FROM clauses
+ for correlation, the FROM elements become candidates for
+ correlation regardless of how deeply nested this :class:`.Select`
+ object is, relative to an enclosing :class:`.Select` which refers to
+ the same FROM object. This is in contrast to the behavior of
+ "auto-correlation" which only correlates to an immediate enclosing
+ :class:`.Select`. Multi-level correlation ensures that the link
+ between enclosed and enclosing :class:`.Select` is always via
+ at least one WHERE/ORDER BY/HAVING/columns clause in order for
+ correlation to take place.
+
+ If ``None`` is passed, the :class:`.Select` object will correlate
+ none of its FROM entries, and all will render unconditionally
+ in the local FROM clause.
+
+ :param \*fromclauses: a list of one or more :class:`.FromClause`
+ constructs, or other compatible constructs (i.e. ORM-mapped
+ classes) to become part of the correlate collection.
+
+ .. versionchanged:: 0.8.0 ORM-mapped classes are accepted by
+ :meth:`.Select.correlate`.
+
+ .. versionchanged:: 0.8.0 The :meth:`.Select.correlate` method no
+ longer unconditionally removes entries from the FROM clause; instead,
+ the candidate FROM entries must also be matched by a FROM entry
+ located in an enclosing :class:`.Select`, which ultimately encloses
+ this one as present in the WHERE clause, ORDER BY clause, HAVING
+ clause, or columns clause of an enclosing :meth:`.Select`.
+
+ .. versionchanged:: 0.8.2 explicit correlation takes place
+ via any level of nesting of :class:`.Select` objects; in previous
+ 0.8 versions, correlation would only occur relative to the immediate
+ enclosing :class:`.Select` construct.
+
+ .. seealso::
+
+ :meth:`.Select.correlate_except`
+
+ :ref:`correlated_subqueries`
+
+ """
+ self._auto_correlate = False
+ if fromclauses and fromclauses[0] is None:
+ self._correlate = ()
+ else:
+ self._correlate = set(self._correlate).union(
+ _interpret_as_from(f) for f in fromclauses)
+
+ @_generative
+ def correlate_except(self, *fromclauses):
+ """return a new :class:`.Select` which will omit the given FROM
+ clauses from the auto-correlation process.
+
+ Calling :meth:`.Select.correlate_except` turns off the
+ :class:`.Select` object's default behavior of
+ "auto-correlation" for the given FROM elements. An element
+ specified here will unconditionally appear in the FROM list, while
+ all other FROM elements remain subject to normal auto-correlation
+ behaviors.
+
+ .. versionchanged:: 0.8.2 The :meth:`.Select.correlate_except`
+ method was improved to fully prevent FROM clauses specified here
+ from being omitted from the immediate FROM clause of this
+ :class:`.Select`.
+
+ If ``None`` is passed, the :class:`.Select` object will correlate
+ all of its FROM entries.
+
+ .. versionchanged:: 0.8.2 calling ``correlate_except(None)`` will
+ correctly auto-correlate all FROM clauses.
+
+ :param \*fromclauses: a list of one or more :class:`.FromClause`
+ constructs, or other compatible constructs (i.e. ORM-mapped
+ classes) to become part of the correlate-exception collection.
+
+ .. seealso::
+
+ :meth:`.Select.correlate`
+
+ :ref:`correlated_subqueries`
+
+ """
+
+ self._auto_correlate = False
+ if fromclauses and fromclauses[0] is None:
+ self._correlate_except = ()
+ else:
+ self._correlate_except = set(self._correlate_except or ()).union(
+ _interpret_as_from(f) for f in fromclauses)
+
+ def append_correlation(self, fromclause):
+ """append the given correlation expression to this select()
+ construct.
+
+ This is an **in-place** mutation method; the
+ :meth:`~.Select.correlate` method is preferred, as it provides standard
+ :term:`method chaining`.
+
+ """
+
+ self._auto_correlate = False
+ self._correlate = set(self._correlate).union(
+ _interpret_as_from(f) for f in fromclause)
+
+ def append_column(self, column):
+ """append the given column expression to the columns clause of this
+ select() construct.
+
+ This is an **in-place** mutation method; the
+ :meth:`~.Select.column` method is preferred, as it provides standard
+ :term:`method chaining`.
+
+ """
+ self._reset_exported()
+ column = _interpret_as_column_or_from(column)
+
+ if isinstance(column, ScalarSelect):
+ column = column.self_group(against=operators.comma_op)
+
+ self._raw_columns = self._raw_columns + [column]
+
+ def append_prefix(self, clause):
+ """append the given columns clause prefix expression to this select()
+ construct.
+
+ This is an **in-place** mutation method; the
+ :meth:`~.Select.prefix_with` method is preferred, as it provides standard
+ :term:`method chaining`.
+
+ """
+ clause = _literal_as_text(clause)
+ self._prefixes = self._prefixes + (clause,)
+
+ def append_whereclause(self, whereclause):
+ """append the given expression to this select() construct's WHERE
+ criterion.
+
+ The expression will be joined to existing WHERE criterion via AND.
+
+ This is an **in-place** mutation method; the
+ :meth:`~.Select.where` method is preferred, as it provides standard
+ :term:`method chaining`.
+
+ """
+ self._reset_exported()
+ whereclause = _literal_as_text(whereclause)
+
+ if self._whereclause is not None:
+ self._whereclause = and_(self._whereclause, whereclause)
+ else:
+ self._whereclause = whereclause
+
+ def append_having(self, having):
+ """append the given expression to this select() construct's HAVING
+ criterion.
+
+ The expression will be joined to existing HAVING criterion via AND.
+
+ This is an **in-place** mutation method; the
+ :meth:`~.Select.having` method is preferred, as it provides standard
+ :term:`method chaining`.
+
+ """
+ if self._having is not None:
+ self._having = and_(self._having, _literal_as_text(having))
+ else:
+ self._having = _literal_as_text(having)
+
+ def append_from(self, fromclause):
+ """append the given FromClause expression to this select() construct's
+ FROM clause.
+
+ This is an **in-place** mutation method; the
+ :meth:`~.Select.select_from` method is preferred, as it provides standard
+ :term:`method chaining`.
+
+ """
+ self._reset_exported()
+ fromclause = _interpret_as_from(fromclause)
+ self._from_obj = self._from_obj.union([fromclause])
+
+
+ @_memoized_property
+ def _columns_plus_names(self):
+ if self.use_labels:
+ names = set()
+ def name_for_col(c):
+ if c._label is None:
+ return (None, c)
+ name = c._label
+ if name in names:
+ name = c.anon_label
+ else:
+ names.add(name)
+ return name, c
+
+ return [
+ name_for_col(c)
+ for c in util.unique_list(_select_iterables(self._raw_columns))
+ ]
+ else:
+ return [
+ (None, c)
+ for c in util.unique_list(_select_iterables(self._raw_columns))
+ ]
+
+ def _populate_column_collection(self):
+ for name, c in self._columns_plus_names:
+ if not hasattr(c, '_make_proxy'):
+ continue
+ if name is None:
+ key = None
+ elif self.use_labels:
+ key = c._key_label
+ if key is not None and key in self.c:
+ key = c.anon_label
+ else:
+ key = None
+
+ c._make_proxy(self, key=key,
+ name=name,
+ name_is_truncatable=True)
+
+ def _refresh_for_new_column(self, column):
+ for fromclause in self._froms:
+ col = fromclause._refresh_for_new_column(column)
+ if col is not None:
+ if col in self.inner_columns and self._cols_populated:
+ our_label = col._key_label if self.use_labels else col.key
+ if our_label not in self.c:
+ return col._make_proxy(self,
+ name=col._label if self.use_labels else None,
+ key=col._key_label if self.use_labels else None,
+ name_is_truncatable=True)
+ return None
+ return None
+
+ def self_group(self, against=None):
+ """return a 'grouping' construct as per the ClauseElement
+ specification.
+
+ This produces an element that can be embedded in an expression. Note
+ that this method is called automatically as needed when constructing
+ expressions and should not require explicit use.
+
+ """
+ if isinstance(against, CompoundSelect):
+ return self
+ return FromGrouping(self)
+
+ def union(self, other, **kwargs):
+ """return a SQL UNION of this select() construct against the given
+ selectable."""
+
+ return CompoundSelect._create_union(self, other, **kwargs)
+
+ def union_all(self, other, **kwargs):
+ """return a SQL UNION ALL of this select() construct against the given
+ selectable.
+
+ """
+ return CompoundSelect._create_union_all(self, other, **kwargs)
+
+ def except_(self, other, **kwargs):
+ """return a SQL EXCEPT of this select() construct against the given
+ selectable."""
+
+ return CompoundSelect._create_except(self, other, **kwargs)
+
+ def except_all(self, other, **kwargs):
+ """return a SQL EXCEPT ALL of this select() construct against the
+ given selectable.
+
+ """
+ return CompoundSelect._create_except_all(self, other, **kwargs)
+
+ def intersect(self, other, **kwargs):
+ """return a SQL INTERSECT of this select() construct against the given
+ selectable.
+
+ """
+ return CompoundSelect._create_intersect(self, other, **kwargs)
+
+ def intersect_all(self, other, **kwargs):
+ """return a SQL INTERSECT ALL of this select() construct against the
+ given selectable.
+
+ """
+ return CompoundSelect._create_intersect_all(self, other, **kwargs)
+
+ def bind(self):
+ if self._bind:
+ return self._bind
+ froms = self._froms
+ if not froms:
+ for c in self._raw_columns:
+ e = c.bind
+ if e:
+ self._bind = e
+ return e
+ else:
+ e = list(froms)[0].bind
+ if e:
+ self._bind = e
+ return e
+
+ return None
+
+ def _set_bind(self, bind):
+ self._bind = bind
+ bind = property(bind, _set_bind)
+
+
+class ScalarSelect(Generative, Grouping):
+ _from_objects = []
+
+ def __init__(self, element):
+ self.element = element
+ self.type = element._scalar_type()
+
+ @property
+ def columns(self):
+ raise exc.InvalidRequestError('Scalar Select expression has no '
+ 'columns; use this object directly within a '
+ 'column-level expression.')
+ c = columns
+
+ @_generative
+ def where(self, crit):
+ """Apply a WHERE clause to the SELECT statement referred to
+ by this :class:`.ScalarSelect`.
+
+ """
+ self.element = self.element.where(crit)
+
+ def self_group(self, **kwargs):
+ return self
+
+
+class Exists(UnaryExpression):
+ """Represent an ``EXISTS`` clause.
+
+ """
+ __visit_name__ = UnaryExpression.__visit_name__
+ _from_objects = []
+
+
+ def __init__(self, *args, **kwargs):
+ """Construct a new :class:`.Exists` against an existing
+ :class:`.Select` object.
+
+ Calling styles are of the following forms::
+
+ # use on an existing select()
+ s = select([table.c.col1]).where(table.c.col2==5)
+ s = exists(s)
+
+ # construct a select() at once
+ exists(['*'], **select_arguments).where(criterion)
+
+ # columns argument is optional, generates "EXISTS (SELECT *)"
+ # by default.
+ exists().where(table.c.col2==5)
+
+ """
+ if args and isinstance(args[0], (SelectBase, ScalarSelect)):
+ s = args[0]
+ else:
+ if not args:
+ args = ([literal_column('*')],)
+ s = Select(*args, **kwargs).as_scalar().self_group()
+
+ UnaryExpression.__init__(self, s, operator=operators.exists,
+ type_=type_api.BOOLEANTYPE)
+
+ def select(self, whereclause=None, **params):
+ return Select([self], whereclause, **params)
+
+ def correlate(self, *fromclause):
+ e = self._clone()
+ e.element = self.element.correlate(*fromclause).self_group()
+ return e
+
+ def correlate_except(self, *fromclause):
+ e = self._clone()
+ e.element = self.element.correlate_except(*fromclause).self_group()
+ return e
+
+ def select_from(self, clause):
+ """return a new :class:`.Exists` construct, applying the given
+ expression to the :meth:`.Select.select_from` method of the select
+ statement contained.
+
+ """
+ e = self._clone()
+ e.element = self.element.select_from(clause).self_group()
+ return e
+
+ def where(self, clause):
+ """return a new exists() construct with the given expression added to
+ its WHERE clause, joined to the existing clause via AND, if any.
+
+ """
+ e = self._clone()
+ e.element = self.element.where(clause).self_group()
+ return e
+
+
+class AnnotatedFromClause(Annotated):
+ def __init__(self, element, values):
+ # force FromClause to generate their internal
+ # collections into __dict__
+ element.c
+ Annotated.__init__(self, element, values)
+
+
+
diff --git a/lib/sqlalchemy/sql/sqltypes.py b/lib/sqlalchemy/sql/sqltypes.py
new file mode 100644
index 000000000..a9ffab639
--- /dev/null
+++ b/lib/sqlalchemy/sql/sqltypes.py
@@ -0,0 +1,1559 @@
+# sqlalchemy/types.py
+# Copyright (C) 2005-2013 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
+
+"""SQL specific types.
+
+"""
+
+import datetime as dt
+import codecs
+
+from .type_api import TypeEngine, TypeDecorator, to_instance
+from .default_comparator import _DefaultColumnComparator
+from .. import exc, util, processors
+from .base import _bind_or_error
+from . import operators
+from .. import events, event
+from ..util import pickle
+import decimal
+
+if util.jython:
+ import array
+
+class _DateAffinity(object):
+ """Mixin date/time specific expression adaptations.
+
+ Rules are implemented within Date,Time,Interval,DateTime, Numeric,
+ Integer. Based on http://www.postgresql.org/docs/current/static
+ /functions-datetime.html.
+
+ """
+
+ @property
+ def _expression_adaptations(self):
+ raise NotImplementedError()
+
+ class Comparator(TypeEngine.Comparator):
+ _blank_dict = util.immutabledict()
+
+ def _adapt_expression(self, op, other_comparator):
+ othertype = other_comparator.type._type_affinity
+ return op, \
+ to_instance(self.type._expression_adaptations.get(op, self._blank_dict).\
+ get(othertype, NULLTYPE))
+ comparator_factory = Comparator
+
+class Concatenable(object):
+ """A mixin that marks a type as supporting 'concatenation',
+ typically strings."""
+
+ class Comparator(TypeEngine.Comparator):
+ def _adapt_expression(self, op, other_comparator):
+ if op is operators.add and isinstance(other_comparator,
+ (Concatenable.Comparator, NullType.Comparator)):
+ return operators.concat_op, self.expr.type
+ else:
+ return op, self.expr.type
+
+ comparator_factory = Comparator
+
+
+class String(Concatenable, TypeEngine):
+ """The base for all string and character types.
+
+ In SQL, corresponds to VARCHAR. Can also take Python unicode objects
+ and encode to the database's encoding in bind params (and the reverse for
+ result sets.)
+
+ The `length` field is usually required when the `String` type is
+ used within a CREATE TABLE statement, as VARCHAR requires a length
+ on most databases.
+
+ """
+
+ __visit_name__ = 'string'
+
+ def __init__(self, length=None, collation=None,
+ convert_unicode=False,
+ unicode_error=None,
+ _warn_on_bytestring=False
+ ):
+ """
+ Create a string-holding type.
+
+ :param length: optional, a length for the column for use in
+ DDL and CAST expressions. May be safely omitted if no ``CREATE
+ TABLE`` will be issued. Certain databases may require a
+ ``length`` for use in DDL, and will raise an exception when
+ the ``CREATE TABLE`` DDL is issued if a ``VARCHAR``
+ with no length is included. Whether the value is
+ interpreted as bytes or characters is database specific.
+
+ :param collation: Optional, a column-level collation for
+ use in DDL and CAST expressions. Renders using the
+ COLLATE keyword supported by SQLite, MySQL, and Postgresql.
+ E.g.::
+
+ >>> from sqlalchemy import cast, select, String
+ >>> print select([cast('some string', String(collation='utf8'))])
+ SELECT CAST(:param_1 AS VARCHAR COLLATE utf8) AS anon_1
+
+ .. versionadded:: 0.8 Added support for COLLATE to all
+ string types.
+
+ :param convert_unicode: When set to ``True``, the
+ :class:`.String` type will assume that
+ input is to be passed as Python ``unicode`` objects,
+ and results returned as Python ``unicode`` objects.
+ If the DBAPI in use does not support Python unicode
+ (which is fewer and fewer these days), SQLAlchemy
+ will encode/decode the value, using the
+ value of the ``encoding`` parameter passed to
+ :func:`.create_engine` as the encoding.
+
+ When using a DBAPI that natively supports Python
+ unicode objects, this flag generally does not
+ need to be set. For columns that are explicitly
+ intended to store non-ASCII data, the :class:`.Unicode`
+ or :class:`UnicodeText`
+ types should be used regardless, which feature
+ the same behavior of ``convert_unicode`` but
+ also indicate an underlying column type that
+ directly supports unicode, such as ``NVARCHAR``.
+
+ For the extremely rare case that Python ``unicode``
+ is to be encoded/decoded by SQLAlchemy on a backend
+ that does natively support Python ``unicode``,
+ the value ``force`` can be passed here which will
+ cause SQLAlchemy's encode/decode services to be
+ used unconditionally.
+
+ :param unicode_error: Optional, a method to use to handle Unicode
+ conversion errors. Behaves like the ``errors`` keyword argument to
+ the standard library's ``string.decode()`` functions. This flag
+ requires that ``convert_unicode`` is set to ``force`` - otherwise,
+ SQLAlchemy is not guaranteed to handle the task of unicode
+ conversion. Note that this flag adds significant performance
+ overhead to row-fetching operations for backends that already
+ return unicode objects natively (which most DBAPIs do). This
+ flag should only be used as a last resort for reading
+ strings from a column with varied or corrupted encodings.
+
+ """
+ if unicode_error is not None and convert_unicode != 'force':
+ raise exc.ArgumentError("convert_unicode must be 'force' "
+ "when unicode_error is set.")
+
+ self.length = length
+ self.collation = collation
+ self.convert_unicode = convert_unicode
+ self.unicode_error = unicode_error
+ self._warn_on_bytestring = _warn_on_bytestring
+
+ def bind_processor(self, dialect):
+ if self.convert_unicode or dialect.convert_unicode:
+ if dialect.supports_unicode_binds and \
+ self.convert_unicode != 'force':
+ if self._warn_on_bytestring:
+ def process(value):
+ if isinstance(value, util.binary_type):
+ util.warn("Unicode type received non-unicode bind "
+ "param value.")
+ return value
+ return process
+ else:
+ return None
+ else:
+ encoder = codecs.getencoder(dialect.encoding)
+ warn_on_bytestring = self._warn_on_bytestring
+
+ def process(value):
+ if isinstance(value, util.text_type):
+ return encoder(value, self.unicode_error)[0]
+ elif warn_on_bytestring and value is not None:
+ util.warn("Unicode type received non-unicode bind "
+ "param value")
+ return value
+ return process
+ else:
+ return None
+
+ def result_processor(self, dialect, coltype):
+ wants_unicode = self.convert_unicode or dialect.convert_unicode
+ needs_convert = wants_unicode and \
+ (dialect.returns_unicode_strings is not True or
+ self.convert_unicode == 'force')
+
+ if needs_convert:
+ to_unicode = processors.to_unicode_processor_factory(
+ dialect.encoding, self.unicode_error)
+
+ if dialect.returns_unicode_strings:
+ # we wouldn't be here unless convert_unicode='force'
+ # was specified, or the driver has erratic unicode-returning
+ # habits. since we will be getting back unicode
+ # in most cases, we check for it (decode will fail).
+ def process(value):
+ if isinstance(value, util.text_type):
+ return value
+ else:
+ return to_unicode(value)
+ return process
+ else:
+ # here, we assume that the object is not unicode,
+ # avoiding expensive isinstance() check.
+ return to_unicode
+ else:
+ return None
+
+ @property
+ def python_type(self):
+ if self.convert_unicode:
+ return util.text_type
+ else:
+ return str
+
+ def get_dbapi_type(self, dbapi):
+ return dbapi.STRING
+
+
+class Text(String):
+ """A variably sized string type.
+
+ In SQL, usually corresponds to CLOB or TEXT. Can also take Python
+ unicode objects and encode to the database's encoding in bind
+ params (and the reverse for result sets.) In general, TEXT objects
+ do not have a length; while some databases will accept a length
+ argument here, it will be rejected by others.
+
+ """
+ __visit_name__ = 'text'
+
+
+class Unicode(String):
+ """A variable length Unicode string type.
+
+ The :class:`.Unicode` type is a :class:`.String` subclass
+ that assumes input and output as Python ``unicode`` data,
+ and in that regard is equivalent to the usage of the
+ ``convert_unicode`` flag with the :class:`.String` type.
+ However, unlike plain :class:`.String`, it also implies an
+ underlying column type that is explicitly supporting of non-ASCII
+ data, such as ``NVARCHAR`` on Oracle and SQL Server.
+ This can impact the output of ``CREATE TABLE`` statements
+ and ``CAST`` functions at the dialect level, and can
+ also affect the handling of bound parameters in some
+ specific DBAPI scenarios.
+
+ The encoding used by the :class:`.Unicode` type is usually
+ determined by the DBAPI itself; most modern DBAPIs
+ feature support for Python ``unicode`` objects as bound
+ values and result set values, and the encoding should
+ be configured as detailed in the notes for the target
+ DBAPI in the :ref:`dialect_toplevel` section.
+
+ For those DBAPIs which do not support, or are not configured
+ to accommodate Python ``unicode`` objects
+ directly, SQLAlchemy does the encoding and decoding
+ outside of the DBAPI. The encoding in this scenario
+ is determined by the ``encoding`` flag passed to
+ :func:`.create_engine`.
+
+ When using the :class:`.Unicode` type, it is only appropriate
+ to pass Python ``unicode`` objects, and not plain ``str``.
+ If a plain ``str`` is passed under Python 2, a warning
+ is emitted. If you notice your application emitting these warnings but
+ you're not sure of the source of them, the Python
+ ``warnings`` filter, documented at
+ http://docs.python.org/library/warnings.html,
+ can be used to turn these warnings into exceptions
+ which will illustrate a stack trace::
+
+ import warnings
+ warnings.simplefilter('error')
+
+ For an application that wishes to pass plain bytestrings
+ and Python ``unicode`` objects to the ``Unicode`` type
+ equally, the bytestrings must first be decoded into
+ unicode. The recipe at :ref:`coerce_to_unicode` illustrates
+ how this is done.
+
+ See also:
+
+ :class:`.UnicodeText` - unlengthed textual counterpart
+ to :class:`.Unicode`.
+
+ """
+
+ __visit_name__ = 'unicode'
+
+ def __init__(self, length=None, **kwargs):
+ """
+ Create a :class:`.Unicode` object.
+
+ Parameters are the same as that of :class:`.String`,
+ with the exception that ``convert_unicode``
+ defaults to ``True``.
+
+ """
+ kwargs.setdefault('convert_unicode', True)
+ kwargs.setdefault('_warn_on_bytestring', True)
+ super(Unicode, self).__init__(length=length, **kwargs)
+
+
+class UnicodeText(Text):
+ """An unbounded-length Unicode string type.
+
+ See :class:`.Unicode` for details on the unicode
+ behavior of this object.
+
+ Like :class:`.Unicode`, usage the :class:`.UnicodeText` type implies a
+ unicode-capable type being used on the backend, such as
+ ``NCLOB``, ``NTEXT``.
+
+ """
+
+ __visit_name__ = 'unicode_text'
+
+ def __init__(self, length=None, **kwargs):
+ """
+ Create a Unicode-converting Text type.
+
+ Parameters are the same as that of :class:`.Text`,
+ with the exception that ``convert_unicode``
+ defaults to ``True``.
+
+ """
+ kwargs.setdefault('convert_unicode', True)
+ kwargs.setdefault('_warn_on_bytestring', True)
+ super(UnicodeText, self).__init__(length=length, **kwargs)
+
+
+class Integer(_DateAffinity, TypeEngine):
+ """A type for ``int`` integers."""
+
+ __visit_name__ = 'integer'
+
+ def get_dbapi_type(self, dbapi):
+ return dbapi.NUMBER
+
+ @property
+ def python_type(self):
+ return int
+
+ @util.memoized_property
+ def _expression_adaptations(self):
+ # TODO: need a dictionary object that will
+ # handle operators generically here, this is incomplete
+ return {
+ operators.add: {
+ Date: Date,
+ Integer: self.__class__,
+ Numeric: Numeric,
+ },
+ operators.mul: {
+ Interval: Interval,
+ Integer: self.__class__,
+ Numeric: Numeric,
+ },
+ operators.div: {
+ Integer: self.__class__,
+ Numeric: Numeric,
+ },
+ operators.truediv: {
+ Integer: self.__class__,
+ Numeric: Numeric,
+ },
+ operators.sub: {
+ Integer: self.__class__,
+ Numeric: Numeric,
+ },
+ }
+
+
+
+class SmallInteger(Integer):
+ """A type for smaller ``int`` integers.
+
+ Typically generates a ``SMALLINT`` in DDL, and otherwise acts like
+ a normal :class:`.Integer` on the Python side.
+
+ """
+
+ __visit_name__ = 'small_integer'
+
+
+class BigInteger(Integer):
+ """A type for bigger ``int`` integers.
+
+ Typically generates a ``BIGINT`` in DDL, and otherwise acts like
+ a normal :class:`.Integer` on the Python side.
+
+ """
+
+ __visit_name__ = 'big_integer'
+
+
+class Numeric(_DateAffinity, TypeEngine):
+ """A type for fixed precision numbers.
+
+ Typically generates DECIMAL or NUMERIC. Returns
+ ``decimal.Decimal`` objects by default, applying
+ conversion as needed.
+
+ .. note::
+
+ The `cdecimal <http://pypi.python.org/pypi/cdecimal/>`_ library
+ is a high performing alternative to Python's built-in
+ ``decimal.Decimal`` type, which performs very poorly in high volume
+ situations. SQLAlchemy 0.7 is tested against ``cdecimal`` and supports
+ it fully. The type is not necessarily supported by DBAPI
+ implementations however, most of which contain an import for plain
+ ``decimal`` in their source code, even though some such as psycopg2
+ provide hooks for alternate adapters. SQLAlchemy imports ``decimal``
+ globally as well. The most straightforward and
+ foolproof way to use "cdecimal" given current DBAPI and Python support
+ is to patch it directly into sys.modules before anything else is
+ imported::
+
+ import sys
+ import cdecimal
+ sys.modules["decimal"] = cdecimal
+
+ While the global patch is a little ugly, it's particularly
+ important to use just one decimal library at a time since
+ Python Decimal and cdecimal Decimal objects
+ are not currently compatible *with each other*::
+
+ >>> import cdecimal
+ >>> import decimal
+ >>> decimal.Decimal("10") == cdecimal.Decimal("10")
+ False
+
+ SQLAlchemy will provide more natural support of
+ cdecimal if and when it becomes a standard part of Python
+ installations and is supported by all DBAPIs.
+
+ """
+
+ __visit_name__ = 'numeric'
+
+ def __init__(self, precision=None, scale=None, asdecimal=True):
+ """
+ Construct a Numeric.
+
+ :param precision: the numeric precision for use in DDL ``CREATE
+ TABLE``.
+
+ :param scale: the numeric scale for use in DDL ``CREATE TABLE``.
+
+ :param asdecimal: default True. Return whether or not
+ values should be sent as Python Decimal objects, or
+ as floats. Different DBAPIs send one or the other based on
+ datatypes - the Numeric type will ensure that return values
+ are one or the other across DBAPIs consistently.
+
+ When using the ``Numeric`` type, care should be taken to ensure
+ that the asdecimal setting is apppropriate for the DBAPI in use -
+ when Numeric applies a conversion from Decimal->float or float->
+ Decimal, this conversion incurs an additional performance overhead
+ for all result columns received.
+
+ DBAPIs that return Decimal natively (e.g. psycopg2) will have
+ better accuracy and higher performance with a setting of ``True``,
+ as the native translation to Decimal reduces the amount of floating-
+ point issues at play, and the Numeric type itself doesn't need
+ to apply any further conversions. However, another DBAPI which
+ returns floats natively *will* incur an additional conversion
+ overhead, and is still subject to floating point data loss - in
+ which case ``asdecimal=False`` will at least remove the extra
+ conversion overhead.
+
+ """
+ self.precision = precision
+ self.scale = scale
+ self.asdecimal = asdecimal
+
+ def get_dbapi_type(self, dbapi):
+ return dbapi.NUMBER
+
+ @property
+ def python_type(self):
+ if self.asdecimal:
+ return decimal.Decimal
+ else:
+ return float
+
+ def bind_processor(self, dialect):
+ if dialect.supports_native_decimal:
+ return None
+ else:
+ return processors.to_float
+
+ def result_processor(self, dialect, coltype):
+ if self.asdecimal:
+ if dialect.supports_native_decimal:
+ # we're a "numeric", DBAPI will give us Decimal directly
+ return None
+ else:
+ util.warn('Dialect %s+%s does *not* support Decimal '
+ 'objects natively, and SQLAlchemy must '
+ 'convert from floating point - rounding '
+ 'errors and other issues may occur. Please '
+ 'consider storing Decimal numbers as strings '
+ 'or integers on this platform for lossless '
+ 'storage.' % (dialect.name, dialect.driver))
+
+ # we're a "numeric", DBAPI returns floats, convert.
+ if self.scale is not None:
+ return processors.to_decimal_processor_factory(
+ decimal.Decimal, self.scale)
+ else:
+ return processors.to_decimal_processor_factory(
+ decimal.Decimal)
+ else:
+ if dialect.supports_native_decimal:
+ return processors.to_float
+ else:
+ return None
+
+ @util.memoized_property
+ def _expression_adaptations(self):
+ return {
+ operators.mul: {
+ Interval: Interval,
+ Numeric: self.__class__,
+ Integer: self.__class__,
+ },
+ operators.div: {
+ Numeric: self.__class__,
+ Integer: self.__class__,
+ },
+ operators.truediv: {
+ Numeric: self.__class__,
+ Integer: self.__class__,
+ },
+ operators.add: {
+ Numeric: self.__class__,
+ Integer: self.__class__,
+ },
+ operators.sub: {
+ Numeric: self.__class__,
+ Integer: self.__class__,
+ }
+ }
+
+
+class Float(Numeric):
+ """A type for ``float`` numbers.
+
+ Returns Python ``float`` objects by default, applying
+ conversion as needed.
+
+ """
+
+ __visit_name__ = 'float'
+
+ scale = None
+
+ def __init__(self, precision=None, asdecimal=False, **kwargs):
+ """
+ Construct a Float.
+
+ :param precision: the numeric precision for use in DDL ``CREATE
+ TABLE``.
+
+ :param asdecimal: the same flag as that of :class:`.Numeric`, but
+ defaults to ``False``. Note that setting this flag to ``True``
+ results in floating point conversion.
+
+ :param \**kwargs: deprecated. Additional arguments here are ignored
+ by the default :class:`.Float` type. For database specific
+ floats that support additional arguments, see that dialect's
+ documentation for details, such as
+ :class:`sqlalchemy.dialects.mysql.FLOAT`.
+
+ """
+ self.precision = precision
+ self.asdecimal = asdecimal
+ if kwargs:
+ util.warn_deprecated("Additional keyword arguments "
+ "passed to Float ignored.")
+
+ def result_processor(self, dialect, coltype):
+ if self.asdecimal:
+ return processors.to_decimal_processor_factory(decimal.Decimal)
+ else:
+ return None
+
+ @util.memoized_property
+ def _expression_adaptations(self):
+ return {
+ operators.mul: {
+ Interval: Interval,
+ Numeric: self.__class__,
+ },
+ operators.div: {
+ Numeric: self.__class__,
+ },
+ operators.truediv: {
+ Numeric: self.__class__,
+ },
+ operators.add: {
+ Numeric: self.__class__,
+ },
+ operators.sub: {
+ Numeric: self.__class__,
+ }
+ }
+
+
+class DateTime(_DateAffinity, TypeEngine):
+ """A type for ``datetime.datetime()`` objects.
+
+ Date and time types return objects from the Python ``datetime``
+ module. Most DBAPIs have built in support for the datetime
+ module, with the noted exception of SQLite. In the case of
+ SQLite, date and time types are stored as strings which are then
+ converted back to datetime objects when rows are returned.
+
+ """
+
+ __visit_name__ = 'datetime'
+
+ def __init__(self, timezone=False):
+ """Construct a new :class:`.DateTime`.
+
+ :param timezone: boolean. If True, and supported by the
+ backend, will produce 'TIMESTAMP WITH TIMEZONE'. For backends
+ that don't support timezone aware timestamps, has no
+ effect.
+
+ """
+ self.timezone = timezone
+
+ def get_dbapi_type(self, dbapi):
+ return dbapi.DATETIME
+
+ @property
+ def python_type(self):
+ return dt.datetime
+
+ @util.memoized_property
+ def _expression_adaptations(self):
+ return {
+ operators.add: {
+ Interval: self.__class__,
+ },
+ operators.sub: {
+ Interval: self.__class__,
+ DateTime: Interval,
+ },
+ }
+
+
+class Date(_DateAffinity, TypeEngine):
+ """A type for ``datetime.date()`` objects."""
+
+ __visit_name__ = 'date'
+
+ def get_dbapi_type(self, dbapi):
+ return dbapi.DATETIME
+
+ @property
+ def python_type(self):
+ return dt.date
+
+ @util.memoized_property
+ def _expression_adaptations(self):
+ return {
+ operators.add: {
+ Integer: self.__class__,
+ Interval: DateTime,
+ Time: DateTime,
+ },
+ operators.sub: {
+ # date - integer = date
+ Integer: self.__class__,
+
+ # date - date = integer.
+ Date: Integer,
+
+ Interval: DateTime,
+
+ # date - datetime = interval,
+ # this one is not in the PG docs
+ # but works
+ DateTime: Interval,
+ },
+ }
+
+
+class Time(_DateAffinity, TypeEngine):
+ """A type for ``datetime.time()`` objects."""
+
+ __visit_name__ = 'time'
+
+ def __init__(self, timezone=False):
+ self.timezone = timezone
+
+ def get_dbapi_type(self, dbapi):
+ return dbapi.DATETIME
+
+ @property
+ def python_type(self):
+ return dt.time
+
+ @util.memoized_property
+ def _expression_adaptations(self):
+ return {
+ operators.add: {
+ Date: DateTime,
+ Interval: self.__class__
+ },
+ operators.sub: {
+ Time: Interval,
+ Interval: self.__class__,
+ },
+ }
+
+
+class _Binary(TypeEngine):
+ """Define base behavior for binary types."""
+
+ def __init__(self, length=None):
+ self.length = length
+
+ @property
+ def python_type(self):
+ return util.binary_type
+
+ # Python 3 - sqlite3 doesn't need the `Binary` conversion
+ # here, though pg8000 does to indicate "bytea"
+ def bind_processor(self, dialect):
+ DBAPIBinary = dialect.dbapi.Binary
+
+ def process(value):
+ if value is not None:
+ return DBAPIBinary(value)
+ else:
+ return None
+ return process
+
+ # Python 3 has native bytes() type
+ # both sqlite3 and pg8000 seem to return it,
+ # psycopg2 as of 2.5 returns 'memoryview'
+ if util.py2k:
+ def result_processor(self, dialect, coltype):
+ if util.jython:
+ def process(value):
+ if value is not None:
+ if isinstance(value, array.array):
+ return value.tostring()
+ return str(value)
+ else:
+ return None
+ else:
+ process = processors.to_str
+ return process
+ else:
+ def result_processor(self, dialect, coltype):
+ def process(value):
+ if value is not None:
+ value = bytes(value)
+ return value
+ return process
+
+ def coerce_compared_value(self, op, value):
+ """See :meth:`.TypeEngine.coerce_compared_value` for a description."""
+
+ if isinstance(value, util.string_types):
+ return self
+ else:
+ return super(_Binary, self).coerce_compared_value(op, value)
+
+ def get_dbapi_type(self, dbapi):
+ return dbapi.BINARY
+
+
+class LargeBinary(_Binary):
+ """A type for large binary byte data.
+
+ The Binary type generates BLOB or BYTEA when tables are created,
+ and also converts incoming values using the ``Binary`` callable
+ provided by each DB-API.
+
+ """
+
+ __visit_name__ = 'large_binary'
+
+ def __init__(self, length=None):
+ """
+ Construct a LargeBinary type.
+
+ :param length: optional, a length for the column for use in
+ DDL statements, for those BLOB types that accept a length
+ (i.e. MySQL). It does *not* produce a small BINARY/VARBINARY
+ type - use the BINARY/VARBINARY types specifically for those.
+ May be safely omitted if no ``CREATE
+ TABLE`` will be issued. Certain databases may require a
+ *length* for use in DDL, and will raise an exception when
+ the ``CREATE TABLE`` DDL is issued.
+
+ """
+ _Binary.__init__(self, length=length)
+
+
+class Binary(LargeBinary):
+ """Deprecated. Renamed to LargeBinary."""
+
+ def __init__(self, *arg, **kw):
+ util.warn_deprecated('The Binary type has been renamed to '
+ 'LargeBinary.')
+ LargeBinary.__init__(self, *arg, **kw)
+
+
+
+class SchemaType(events.SchemaEventTarget):
+ """Mark a type as possibly requiring schema-level DDL for usage.
+
+ Supports types that must be explicitly created/dropped (i.e. PG ENUM type)
+ as well as types that are complimented by table or schema level
+ constraints, triggers, and other rules.
+
+ :class:`.SchemaType` classes can also be targets for the
+ :meth:`.DDLEvents.before_parent_attach` and
+ :meth:`.DDLEvents.after_parent_attach` events, where the events fire off
+ surrounding the association of the type object with a parent
+ :class:`.Column`.
+
+ .. seealso::
+
+ :class:`.Enum`
+
+ :class:`.Boolean`
+
+
+ """
+
+ def __init__(self, **kw):
+ self.name = kw.pop('name', None)
+ self.quote = kw.pop('quote', None)
+ self.schema = kw.pop('schema', None)
+ self.metadata = kw.pop('metadata', None)
+ self.inherit_schema = kw.pop('inherit_schema', False)
+ if self.metadata:
+ event.listen(
+ self.metadata,
+ "before_create",
+ util.portable_instancemethod(self._on_metadata_create)
+ )
+ event.listen(
+ self.metadata,
+ "after_drop",
+ util.portable_instancemethod(self._on_metadata_drop)
+ )
+
+ def _set_parent(self, column):
+ column._on_table_attach(util.portable_instancemethod(self._set_table))
+
+ def _set_table(self, column, table):
+ if self.inherit_schema:
+ self.schema = table.schema
+
+ event.listen(
+ table,
+ "before_create",
+ util.portable_instancemethod(
+ self._on_table_create)
+ )
+ event.listen(
+ table,
+ "after_drop",
+ util.portable_instancemethod(self._on_table_drop)
+ )
+ if self.metadata is None:
+ # TODO: what's the difference between self.metadata
+ # and table.metadata here ?
+ event.listen(
+ table.metadata,
+ "before_create",
+ util.portable_instancemethod(self._on_metadata_create)
+ )
+ event.listen(
+ table.metadata,
+ "after_drop",
+ util.portable_instancemethod(self._on_metadata_drop)
+ )
+
+ def copy(self, **kw):
+ return self.adapt(self.__class__)
+
+ def adapt(self, impltype, **kw):
+ schema = kw.pop('schema', self.schema)
+ metadata = kw.pop('metadata', self.metadata)
+ return impltype(name=self.name,
+ quote=self.quote,
+ schema=schema,
+ metadata=metadata,
+ inherit_schema=self.inherit_schema,
+ **kw
+ )
+
+ @property
+ def bind(self):
+ return self.metadata and self.metadata.bind or None
+
+ def create(self, bind=None, checkfirst=False):
+ """Issue CREATE ddl for this type, if applicable."""
+
+ if bind is None:
+ bind = _bind_or_error(self)
+ t = self.dialect_impl(bind.dialect)
+ if t.__class__ is not self.__class__ and isinstance(t, SchemaType):
+ t.create(bind=bind, checkfirst=checkfirst)
+
+ def drop(self, bind=None, checkfirst=False):
+ """Issue DROP ddl for this type, if applicable."""
+
+ if bind is None:
+ bind = _bind_or_error(self)
+ t = self.dialect_impl(bind.dialect)
+ if t.__class__ is not self.__class__ and isinstance(t, SchemaType):
+ t.drop(bind=bind, checkfirst=checkfirst)
+
+ def _on_table_create(self, target, bind, **kw):
+ t = self.dialect_impl(bind.dialect)
+ if t.__class__ is not self.__class__ and isinstance(t, SchemaType):
+ t._on_table_create(target, bind, **kw)
+
+ def _on_table_drop(self, target, bind, **kw):
+ t = self.dialect_impl(bind.dialect)
+ if t.__class__ is not self.__class__ and isinstance(t, SchemaType):
+ t._on_table_drop(target, bind, **kw)
+
+ def _on_metadata_create(self, target, bind, **kw):
+ t = self.dialect_impl(bind.dialect)
+ if t.__class__ is not self.__class__ and isinstance(t, SchemaType):
+ t._on_metadata_create(target, bind, **kw)
+
+ def _on_metadata_drop(self, target, bind, **kw):
+ t = self.dialect_impl(bind.dialect)
+ if t.__class__ is not self.__class__ and isinstance(t, SchemaType):
+ t._on_metadata_drop(target, bind, **kw)
+
+class Enum(String, SchemaType):
+ """Generic Enum Type.
+
+ The Enum type provides a set of possible string values which the
+ column is constrained towards.
+
+ By default, uses the backend's native ENUM type if available,
+ else uses VARCHAR + a CHECK constraint.
+
+ .. seealso::
+
+ :class:`~.postgresql.ENUM` - PostgreSQL-specific type,
+ which has additional functionality.
+
+ """
+
+ __visit_name__ = 'enum'
+
+ def __init__(self, *enums, **kw):
+ """Construct an enum.
+
+ Keyword arguments which don't apply to a specific backend are ignored
+ by that backend.
+
+ :param \*enums: string or unicode enumeration labels. If unicode
+ labels are present, the `convert_unicode` flag is auto-enabled.
+
+ :param convert_unicode: Enable unicode-aware bind parameter and
+ result-set processing for this Enum's data. This is set
+ automatically based on the presence of unicode label strings.
+
+ :param metadata: Associate this type directly with a ``MetaData``
+ object. For types that exist on the target database as an
+ independent schema construct (Postgresql), this type will be
+ created and dropped within ``create_all()`` and ``drop_all()``
+ operations. If the type is not associated with any ``MetaData``
+ object, it will associate itself with each ``Table`` in which it is
+ used, and will be created when any of those individual tables are
+ created, after a check is performed for it's existence. The type is
+ only dropped when ``drop_all()`` is called for that ``Table``
+ object's metadata, however.
+
+ :param name: The name of this type. This is required for Postgresql
+ and any future supported database which requires an explicitly
+ named type, or an explicitly named constraint in order to generate
+ the type and/or a table that uses it.
+
+ :param native_enum: Use the database's native ENUM type when
+ available. Defaults to True. When False, uses VARCHAR + check
+ constraint for all backends.
+
+ :param schema: Schema name of this type. For types that exist on the
+ target database as an independent schema construct (Postgresql),
+ this parameter specifies the named schema in which the type is
+ present.
+
+ .. note::
+
+ The ``schema`` of the :class:`.Enum` type does not
+ by default make use of the ``schema`` established on the
+ owning :class:`.Table`. If this behavior is desired,
+ set the ``inherit_schema`` flag to ``True``.
+
+ :param quote: Force quoting to be on or off on the type's name. If
+ left as the default of `None`, the usual schema-level "case
+ sensitive"/"reserved name" rules are used to determine if this
+ type's name should be quoted.
+
+ :param inherit_schema: When ``True``, the "schema" from the owning
+ :class:`.Table` will be copied to the "schema" attribute of this
+ :class:`.Enum`, replacing whatever value was passed for the
+ ``schema`` attribute. This also takes effect when using the
+ :meth:`.Table.tometadata` operation.
+
+ .. versionadded:: 0.8
+
+ """
+ self.enums = enums
+ self.native_enum = kw.pop('native_enum', True)
+ convert_unicode = kw.pop('convert_unicode', None)
+ if convert_unicode is None:
+ for e in enums:
+ if isinstance(e, util.text_type):
+ convert_unicode = True
+ break
+ else:
+ convert_unicode = False
+
+ if self.enums:
+ length = max(len(x) for x in self.enums)
+ else:
+ length = 0
+ String.__init__(self,
+ length=length,
+ convert_unicode=convert_unicode,
+ )
+ SchemaType.__init__(self, **kw)
+
+ def __repr__(self):
+ return util.generic_repr(self, [
+ ("native_enum", True),
+ ("name", None)
+ ])
+
+ def _should_create_constraint(self, compiler):
+ return not self.native_enum or \
+ not compiler.dialect.supports_native_enum
+
+ @util.dependencies("sqlalchemy.sql.schema")
+ def _set_table(self, schema, column, table):
+ if self.native_enum:
+ SchemaType._set_table(self, column, table)
+
+ e = schema.CheckConstraint(
+ column.in_(self.enums),
+ name=self.name,
+ _create_rule=util.portable_instancemethod(
+ self._should_create_constraint)
+ )
+ table.append_constraint(e)
+
+ def adapt(self, impltype, **kw):
+ schema = kw.pop('schema', self.schema)
+ metadata = kw.pop('metadata', self.metadata)
+ if issubclass(impltype, Enum):
+ return impltype(name=self.name,
+ quote=self.quote,
+ schema=schema,
+ metadata=metadata,
+ convert_unicode=self.convert_unicode,
+ native_enum=self.native_enum,
+ inherit_schema=self.inherit_schema,
+ *self.enums,
+ **kw
+ )
+ else:
+ return super(Enum, self).adapt(impltype, **kw)
+
+
+class PickleType(TypeDecorator):
+ """Holds Python objects, which are serialized using pickle.
+
+ PickleType builds upon the Binary type to apply Python's
+ ``pickle.dumps()`` to incoming objects, and ``pickle.loads()`` on
+ the way out, allowing any pickleable Python object to be stored as
+ a serialized binary field.
+
+ To allow ORM change events to propagate for elements associated
+ with :class:`.PickleType`, see :ref:`mutable_toplevel`.
+
+ """
+
+ impl = LargeBinary
+
+ def __init__(self, protocol=pickle.HIGHEST_PROTOCOL,
+ pickler=None, comparator=None):
+ """
+ Construct a PickleType.
+
+ :param protocol: defaults to ``pickle.HIGHEST_PROTOCOL``.
+
+ :param pickler: defaults to cPickle.pickle or pickle.pickle if
+ cPickle is not available. May be any object with
+ pickle-compatible ``dumps` and ``loads`` methods.
+
+ :param comparator: a 2-arg callable predicate used
+ to compare values of this type. If left as ``None``,
+ the Python "equals" operator is used to compare values.
+
+ """
+ self.protocol = protocol
+ self.pickler = pickler or pickle
+ self.comparator = comparator
+ super(PickleType, self).__init__()
+
+ def __reduce__(self):
+ return PickleType, (self.protocol,
+ None,
+ self.comparator)
+
+ def bind_processor(self, dialect):
+ impl_processor = self.impl.bind_processor(dialect)
+ dumps = self.pickler.dumps
+ protocol = self.protocol
+ if impl_processor:
+ def process(value):
+ if value is not None:
+ value = dumps(value, protocol)
+ return impl_processor(value)
+ else:
+ def process(value):
+ if value is not None:
+ value = dumps(value, protocol)
+ return value
+ return process
+
+ def result_processor(self, dialect, coltype):
+ impl_processor = self.impl.result_processor(dialect, coltype)
+ loads = self.pickler.loads
+ if impl_processor:
+ def process(value):
+ value = impl_processor(value)
+ if value is None:
+ return None
+ return loads(value)
+ else:
+ def process(value):
+ if value is None:
+ return None
+ return loads(value)
+ return process
+
+ def compare_values(self, x, y):
+ if self.comparator:
+ return self.comparator(x, y)
+ else:
+ return x == y
+
+
+class Boolean(TypeEngine, SchemaType):
+ """A bool datatype.
+
+ Boolean typically uses BOOLEAN or SMALLINT on the DDL side, and on
+ the Python side deals in ``True`` or ``False``.
+
+ """
+
+ __visit_name__ = 'boolean'
+
+ def __init__(self, create_constraint=True, name=None):
+ """Construct a Boolean.
+
+ :param create_constraint: defaults to True. If the boolean
+ is generated as an int/smallint, also create a CHECK constraint
+ on the table that ensures 1 or 0 as a value.
+
+ :param name: if a CHECK constraint is generated, specify
+ the name of the constraint.
+
+ """
+ self.create_constraint = create_constraint
+ self.name = name
+
+ def _should_create_constraint(self, compiler):
+ return not compiler.dialect.supports_native_boolean
+
+ @util.dependencies("sqlalchemy.sql.schema")
+ def _set_table(self, schema, column, table):
+ if not self.create_constraint:
+ return
+
+ e = schema.CheckConstraint(
+ column.in_([0, 1]),
+ name=self.name,
+ _create_rule=util.portable_instancemethod(
+ self._should_create_constraint)
+ )
+ table.append_constraint(e)
+
+ @property
+ def python_type(self):
+ return bool
+
+ def bind_processor(self, dialect):
+ if dialect.supports_native_boolean:
+ return None
+ else:
+ return processors.boolean_to_int
+
+ def result_processor(self, dialect, coltype):
+ if dialect.supports_native_boolean:
+ return None
+ else:
+ return processors.int_to_boolean
+
+
+class Interval(_DateAffinity, TypeDecorator):
+ """A type for ``datetime.timedelta()`` objects.
+
+ The Interval type deals with ``datetime.timedelta`` objects. In
+ PostgreSQL, the native ``INTERVAL`` type is used; for others, the
+ value is stored as a date which is relative to the "epoch"
+ (Jan. 1, 1970).
+
+ Note that the ``Interval`` type does not currently provide date arithmetic
+ operations on platforms which do not support interval types natively. Such
+ operations usually require transformation of both sides of the expression
+ (such as, conversion of both sides into integer epoch values first) which
+ currently is a manual procedure (such as via
+ :attr:`~sqlalchemy.sql.expression.func`).
+
+ """
+
+ impl = DateTime
+ epoch = dt.datetime.utcfromtimestamp(0)
+
+ def __init__(self, native=True,
+ second_precision=None,
+ day_precision=None):
+ """Construct an Interval object.
+
+ :param native: when True, use the actual
+ INTERVAL type provided by the database, if
+ supported (currently Postgresql, Oracle).
+ Otherwise, represent the interval data as
+ an epoch value regardless.
+
+ :param second_precision: For native interval types
+ which support a "fractional seconds precision" parameter,
+ i.e. Oracle and Postgresql
+
+ :param day_precision: for native interval types which
+ support a "day precision" parameter, i.e. Oracle.
+
+ """
+ super(Interval, self).__init__()
+ self.native = native
+ self.second_precision = second_precision
+ self.day_precision = day_precision
+
+ def adapt(self, cls, **kw):
+ if self.native and hasattr(cls, '_adapt_from_generic_interval'):
+ return cls._adapt_from_generic_interval(self, **kw)
+ else:
+ return self.__class__(
+ native=self.native,
+ second_precision=self.second_precision,
+ day_precision=self.day_precision,
+ **kw)
+
+ @property
+ def python_type(self):
+ return dt.timedelta
+
+ def bind_processor(self, dialect):
+ impl_processor = self.impl.bind_processor(dialect)
+ epoch = self.epoch
+ if impl_processor:
+ def process(value):
+ if value is not None:
+ value = epoch + value
+ return impl_processor(value)
+ else:
+ def process(value):
+ if value is not None:
+ value = epoch + value
+ return value
+ return process
+
+ def result_processor(self, dialect, coltype):
+ impl_processor = self.impl.result_processor(dialect, coltype)
+ epoch = self.epoch
+ if impl_processor:
+ def process(value):
+ value = impl_processor(value)
+ if value is None:
+ return None
+ return value - epoch
+ else:
+ def process(value):
+ if value is None:
+ return None
+ return value - epoch
+ return process
+
+ @util.memoized_property
+ def _expression_adaptations(self):
+ return {
+ operators.add: {
+ Date: DateTime,
+ Interval: self.__class__,
+ DateTime: DateTime,
+ Time: Time,
+ },
+ operators.sub: {
+ Interval: self.__class__
+ },
+ operators.mul: {
+ Numeric: self.__class__
+ },
+ operators.truediv: {
+ Numeric: self.__class__
+ },
+ operators.div: {
+ Numeric: self.__class__
+ }
+ }
+
+ @property
+ def _type_affinity(self):
+ return Interval
+
+ def coerce_compared_value(self, op, value):
+ """See :meth:`.TypeEngine.coerce_compared_value` for a description."""
+
+ return self.impl.coerce_compared_value(op, value)
+
+
+class REAL(Float):
+ """The SQL REAL type."""
+
+ __visit_name__ = 'REAL'
+
+
+class FLOAT(Float):
+ """The SQL FLOAT type."""
+
+ __visit_name__ = 'FLOAT'
+
+
+class NUMERIC(Numeric):
+ """The SQL NUMERIC type."""
+
+ __visit_name__ = 'NUMERIC'
+
+
+class DECIMAL(Numeric):
+ """The SQL DECIMAL type."""
+
+ __visit_name__ = 'DECIMAL'
+
+
+class INTEGER(Integer):
+ """The SQL INT or INTEGER type."""
+
+ __visit_name__ = 'INTEGER'
+INT = INTEGER
+
+
+class SMALLINT(SmallInteger):
+ """The SQL SMALLINT type."""
+
+ __visit_name__ = 'SMALLINT'
+
+
+class BIGINT(BigInteger):
+ """The SQL BIGINT type."""
+
+ __visit_name__ = 'BIGINT'
+
+
+class TIMESTAMP(DateTime):
+ """The SQL TIMESTAMP type."""
+
+ __visit_name__ = 'TIMESTAMP'
+
+ def get_dbapi_type(self, dbapi):
+ return dbapi.TIMESTAMP
+
+
+class DATETIME(DateTime):
+ """The SQL DATETIME type."""
+
+ __visit_name__ = 'DATETIME'
+
+
+class DATE(Date):
+ """The SQL DATE type."""
+
+ __visit_name__ = 'DATE'
+
+
+class TIME(Time):
+ """The SQL TIME type."""
+
+ __visit_name__ = 'TIME'
+
+
+class TEXT(Text):
+ """The SQL TEXT type."""
+
+ __visit_name__ = 'TEXT'
+
+
+class CLOB(Text):
+ """The CLOB type.
+
+ This type is found in Oracle and Informix.
+ """
+
+ __visit_name__ = 'CLOB'
+
+
+class VARCHAR(String):
+ """The SQL VARCHAR type."""
+
+ __visit_name__ = 'VARCHAR'
+
+
+class NVARCHAR(Unicode):
+ """The SQL NVARCHAR type."""
+
+ __visit_name__ = 'NVARCHAR'
+
+
+class CHAR(String):
+ """The SQL CHAR type."""
+
+ __visit_name__ = 'CHAR'
+
+
+class NCHAR(Unicode):
+ """The SQL NCHAR type."""
+
+ __visit_name__ = 'NCHAR'
+
+
+class BLOB(LargeBinary):
+ """The SQL BLOB type."""
+
+ __visit_name__ = 'BLOB'
+
+
+class BINARY(_Binary):
+ """The SQL BINARY type."""
+
+ __visit_name__ = 'BINARY'
+
+
+class VARBINARY(_Binary):
+ """The SQL VARBINARY type."""
+
+ __visit_name__ = 'VARBINARY'
+
+
+class BOOLEAN(Boolean):
+ """The SQL BOOLEAN type."""
+
+ __visit_name__ = 'BOOLEAN'
+
+class NullType(TypeEngine):
+ """An unknown type.
+
+ :class:`.NullType` is used as a default type for those cases where
+ a type cannot be determined, including:
+
+ * During table reflection, when the type of a column is not recognized
+ by the :class:`.Dialect`
+ * When constructing SQL expressions using plain Python objects of
+ unknown types (e.g. ``somecolumn == my_special_object``)
+ * When a new :class:`.Column` is created, and the given type is passed
+ as ``None`` or is not passed at all.
+
+ The :class:`.NullType` can be used within SQL expression invocation
+ without issue, it just has no behavior either at the expression construction
+ level or at the bind-parameter/result processing level. :class:`.NullType`
+ will result in a :class:`.CompileException` if the compiler is asked to render
+ the type itself, such as if it is used in a :func:`.cast` operation
+ or within a schema creation operation such as that invoked by
+ :meth:`.MetaData.create_all` or the :class:`.CreateTable` construct.
+
+ """
+ __visit_name__ = 'null'
+
+ _isnull = True
+
+ class Comparator(TypeEngine.Comparator):
+ def _adapt_expression(self, op, other_comparator):
+ if isinstance(other_comparator, NullType.Comparator) or \
+ not operators.is_commutative(op):
+ return op, self.expr.type
+ else:
+ return other_comparator._adapt_expression(op, self)
+ comparator_factory = Comparator
+
+
+NULLTYPE = NullType()
+BOOLEANTYPE = Boolean()
+STRINGTYPE = String()
+INTEGERTYPE = Integer()
+
+_type_map = {
+ int: Integer(),
+ float: Numeric(),
+ bool: BOOLEANTYPE,
+ decimal.Decimal: Numeric(),
+ dt.date: Date(),
+ dt.datetime: DateTime(),
+ dt.time: Time(),
+ dt.timedelta: Interval(),
+ util.NoneType: NULLTYPE
+}
+
+if util.py3k:
+ _type_map[bytes] = LargeBinary()
+ _type_map[str] = Unicode()
+else:
+ _type_map[unicode] = Unicode()
+ _type_map[str] = String()
+
+
+# back-assign to type_api
+from . import type_api
+type_api.BOOLEANTYPE = BOOLEANTYPE
+type_api.STRINGTYPE = STRINGTYPE
+type_api.INTEGERTYPE = INTEGERTYPE
+type_api.NULLTYPE = NULLTYPE
+type_api._type_map = _type_map
+
+# this one, there's all kinds of ways to play it, but at the EOD
+# there's just a giant dependency cycle between the typing system and
+# the expression element system, as you might expect. We can use
+# importlaters or whatnot, but the typing system just necessarily has
+# to have some kind of connection like this. right now we're injecting the
+# _DefaultColumnComparator implementation into the TypeEngine.Comparator interface.
+# Alternatively TypeEngine.Comparator could have an "impl" injected, though
+# just injecting the base is simpler, error free, and more performant.
+class Comparator(_DefaultColumnComparator):
+ BOOLEANTYPE = BOOLEANTYPE
+
+TypeEngine.Comparator.__bases__ = (Comparator, ) + TypeEngine.Comparator.__bases__
+
diff --git a/lib/sqlalchemy/sql/type_api.py b/lib/sqlalchemy/sql/type_api.py
new file mode 100644
index 000000000..eb39c622c
--- /dev/null
+++ b/lib/sqlalchemy/sql/type_api.py
@@ -0,0 +1,968 @@
+# sqlalchemy/types_base.py
+# Copyright (C) 2005-2013 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
+
+"""Base types API.
+
+"""
+
+
+from .. import exc, util
+from . import operators
+from .visitors import Visitable
+
+# these are back-assigned by sqltypes.
+BOOLEANTYPE = None
+INTEGERTYPE = None
+NULLTYPE = None
+STRINGTYPE = None
+
+class TypeEngine(Visitable):
+ """Base for built-in types."""
+
+ _sqla_type = True
+ _isnull = False
+
+ class Comparator(operators.ColumnOperators):
+ """Base class for custom comparison operations defined at the
+ type level. See :attr:`.TypeEngine.comparator_factory`.
+
+
+ """
+
+ def __init__(self, expr):
+ self.expr = expr
+
+ def __reduce__(self):
+ return _reconstitute_comparator, (self.expr, )
+
+
+ hashable = True
+ """Flag, if False, means values from this type aren't hashable.
+
+ Used by the ORM when uniquing result lists.
+
+ """
+
+ comparator_factory = Comparator
+ """A :class:`.TypeEngine.Comparator` class which will apply
+ to operations performed by owning :class:`.ColumnElement` objects.
+
+ The :attr:`.comparator_factory` attribute is a hook consulted by
+ the core expression system when column and SQL expression operations
+ are performed. When a :class:`.TypeEngine.Comparator` class is
+ associated with this attribute, it allows custom re-definition of
+ all existing operators, as well as definition of new operators.
+ Existing operators include those provided by Python operator overloading
+ such as :meth:`.operators.ColumnOperators.__add__` and
+ :meth:`.operators.ColumnOperators.__eq__`,
+ those provided as standard
+ attributes of :class:`.operators.ColumnOperators` such as
+ :meth:`.operators.ColumnOperators.like`
+ and :meth:`.operators.ColumnOperators.in_`.
+
+ Rudimentary usage of this hook is allowed through simple subclassing
+ of existing types, or alternatively by using :class:`.TypeDecorator`.
+ See the documentation section :ref:`types_operators` for examples.
+
+ .. versionadded:: 0.8 The expression system was enhanced to support
+ customization of operators on a per-type level.
+
+ """
+
+ def copy_value(self, value):
+ return value
+
+ def bind_processor(self, dialect):
+ """Return a conversion function for processing bind values.
+
+ Returns a callable which will receive a bind parameter value
+ as the sole positional argument and will return a value to
+ send to the DB-API.
+
+ If processing is not necessary, the method should return ``None``.
+
+ :param dialect: Dialect instance in use.
+
+ """
+ return None
+
+ def result_processor(self, dialect, coltype):
+ """Return a conversion function for processing result row values.
+
+ Returns a callable which will receive a result row column
+ value as the sole positional argument and will return a value
+ to return to the user.
+
+ If processing is not necessary, the method should return ``None``.
+
+ :param dialect: Dialect instance in use.
+
+ :param coltype: DBAPI coltype argument received in cursor.description.
+
+ """
+ return None
+
+ def column_expression(self, colexpr):
+ """Given a SELECT column expression, return a wrapping SQL expression.
+
+ This is typically a SQL function that wraps a column expression
+ as rendered in the columns clause of a SELECT statement.
+ It is used for special data types that require
+ columns to be wrapped in some special database function in order
+ to coerce the value before being sent back to the application.
+ It is the SQL analogue of the :meth:`.TypeEngine.result_processor`
+ method.
+
+ The method is evaluated at statement compile time, as opposed
+ to statement construction time.
+
+ See also:
+
+ :ref:`types_sql_value_processing`
+
+ """
+
+ return None
+
+ @util.memoized_property
+ def _has_column_expression(self):
+ """memoized boolean, check if column_expression is implemented.
+
+ Allows the method to be skipped for the vast majority of expression
+ types that don't use this feature.
+
+ """
+
+ return self.__class__.column_expression.__code__ \
+ is not TypeEngine.column_expression.__code__
+
+ def bind_expression(self, bindvalue):
+ """"Given a bind value (i.e. a :class:`.BindParameter` instance),
+ return a SQL expression in its place.
+
+ This is typically a SQL function that wraps the existing bound
+ parameter within the statement. It is used for special data types
+ that require literals being wrapped in some special database function
+ in order to coerce an application-level value into a database-specific
+ format. It is the SQL analogue of the
+ :meth:`.TypeEngine.bind_processor` method.
+
+ The method is evaluated at statement compile time, as opposed
+ to statement construction time.
+
+ Note that this method, when implemented, should always return
+ the exact same structure, without any conditional logic, as it
+ may be used in an executemany() call against an arbitrary number
+ of bound parameter sets.
+
+ See also:
+
+ :ref:`types_sql_value_processing`
+
+ """
+ return None
+
+ @util.memoized_property
+ def _has_bind_expression(self):
+ """memoized boolean, check if bind_expression is implemented.
+
+ Allows the method to be skipped for the vast majority of expression
+ types that don't use this feature.
+
+ """
+
+ return self.__class__.bind_expression.__code__ \
+ is not TypeEngine.bind_expression.__code__
+
+ def compare_values(self, x, y):
+ """Compare two values for equality."""
+
+ return x == y
+
+ def get_dbapi_type(self, dbapi):
+ """Return the corresponding type object from the underlying DB-API, if
+ any.
+
+ This can be useful for calling ``setinputsizes()``, for example.
+
+ """
+ return None
+
+ @property
+ def python_type(self):
+ """Return the Python type object expected to be returned
+ by instances of this type, if known.
+
+ Basically, for those types which enforce a return type,
+ or are known across the board to do such for all common
+ DBAPIs (like ``int`` for example), will return that type.
+
+ If a return type is not defined, raises
+ ``NotImplementedError``.
+
+ Note that any type also accommodates NULL in SQL which
+ means you can also get back ``None`` from any type
+ in practice.
+
+ """
+ raise NotImplementedError()
+
+ def with_variant(self, type_, dialect_name):
+ """Produce a new type object that will utilize the given
+ type when applied to the dialect of the given name.
+
+ e.g.::
+
+ from sqlalchemy.types import String
+ from sqlalchemy.dialects import mysql
+
+ s = String()
+
+ s = s.with_variant(mysql.VARCHAR(collation='foo'), 'mysql')
+
+ The construction of :meth:`.TypeEngine.with_variant` is always
+ from the "fallback" type to that which is dialect specific.
+ The returned type is an instance of :class:`.Variant`, which
+ itself provides a :meth:`~sqlalchemy.types.Variant.with_variant`
+ that can be called repeatedly.
+
+ :param type_: a :class:`.TypeEngine` that will be selected
+ as a variant from the originating type, when a dialect
+ of the given name is in use.
+ :param dialect_name: base name of the dialect which uses
+ this type. (i.e. ``'postgresql'``, ``'mysql'``, etc.)
+
+ .. versionadded:: 0.7.2
+
+ """
+ return Variant(self, {dialect_name: type_})
+
+
+ @util.memoized_property
+ def _type_affinity(self):
+ """Return a rudimental 'affinity' value expressing the general class
+ of type."""
+
+ typ = None
+ for t in self.__class__.__mro__:
+ if t in (TypeEngine, UserDefinedType):
+ return typ
+ elif issubclass(t, (TypeEngine, UserDefinedType)):
+ typ = t
+ else:
+ return self.__class__
+
+ def dialect_impl(self, dialect):
+ """Return a dialect-specific implementation for this
+ :class:`.TypeEngine`.
+
+ """
+ try:
+ return dialect._type_memos[self]['impl']
+ except KeyError:
+ return self._dialect_info(dialect)['impl']
+
+ def _cached_bind_processor(self, dialect):
+ """Return a dialect-specific bind processor for this type."""
+
+ try:
+ return dialect._type_memos[self]['bind']
+ except KeyError:
+ d = self._dialect_info(dialect)
+ d['bind'] = bp = d['impl'].bind_processor(dialect)
+ return bp
+
+ def _cached_result_processor(self, dialect, coltype):
+ """Return a dialect-specific result processor for this type."""
+
+ try:
+ return dialect._type_memos[self][coltype]
+ except KeyError:
+ d = self._dialect_info(dialect)
+ # key assumption: DBAPI type codes are
+ # constants. Else this dictionary would
+ # grow unbounded.
+ d[coltype] = rp = d['impl'].result_processor(dialect, coltype)
+ return rp
+
+ def _dialect_info(self, dialect):
+ """Return a dialect-specific registry which
+ caches a dialect-specific implementation, bind processing
+ function, and one or more result processing functions."""
+
+ if self in dialect._type_memos:
+ return dialect._type_memos[self]
+ else:
+ impl = self._gen_dialect_impl(dialect)
+ if impl is self:
+ impl = self.adapt(type(self))
+ # this can't be self, else we create a cycle
+ assert impl is not self
+ dialect._type_memos[self] = d = {'impl': impl}
+ return d
+
+ def _gen_dialect_impl(self, dialect):
+ return dialect.type_descriptor(self)
+
+ def adapt(self, cls, **kw):
+ """Produce an "adapted" form of this type, given an "impl" class
+ to work with.
+
+ This method is used internally to associate generic
+ types with "implementation" types that are specific to a particular
+ dialect.
+ """
+ return util.constructor_copy(self, cls, **kw)
+
+
+ def coerce_compared_value(self, op, value):
+ """Suggest a type for a 'coerced' Python value in an expression.
+
+ Given an operator and value, gives the type a chance
+ to return a type which the value should be coerced into.
+
+ The default behavior here is conservative; if the right-hand
+ side is already coerced into a SQL type based on its
+ Python type, it is usually left alone.
+
+ End-user functionality extension here should generally be via
+ :class:`.TypeDecorator`, which provides more liberal behavior in that
+ it defaults to coercing the other side of the expression into this
+ type, thus applying special Python conversions above and beyond those
+ needed by the DBAPI to both ides. It also provides the public method
+ :meth:`.TypeDecorator.coerce_compared_value` which is intended for
+ end-user customization of this behavior.
+
+ """
+ _coerced_type = _type_map.get(type(value), NULLTYPE)
+ if _coerced_type is NULLTYPE or _coerced_type._type_affinity \
+ is self._type_affinity:
+ return self
+ else:
+ return _coerced_type
+
+ def _compare_type_affinity(self, other):
+ return self._type_affinity is other._type_affinity
+
+ def compile(self, dialect=None):
+ """Produce a string-compiled form of this :class:`.TypeEngine`.
+
+ When called with no arguments, uses a "default" dialect
+ to produce a string result.
+
+ :param dialect: a :class:`.Dialect` instance.
+
+ """
+ # arg, return value is inconsistent with
+ # ClauseElement.compile()....this is a mistake.
+
+ if not dialect:
+ dialect = self._default_dialect()
+
+ return dialect.type_compiler.process(self)
+
+ @util.dependencies("sqlalchemy.engine.default")
+ def _default_dialect(self, default):
+ if self.__class__.__module__.startswith("sqlalchemy.dialects"):
+ tokens = self.__class__.__module__.split(".")[0:3]
+ mod = ".".join(tokens)
+ return getattr(__import__(mod).dialects, tokens[-1]).dialect()
+ else:
+ return default.DefaultDialect()
+
+ def __str__(self):
+ if util.py2k:
+ return unicode(self.compile()).\
+ encode('ascii', 'backslashreplace')
+ else:
+ return str(self.compile())
+
+ def __init__(self, *args, **kwargs):
+ """Support implementations that were passing arguments"""
+ if args or kwargs:
+ util.warn_deprecated("Passing arguments to type object "
+ "constructor %s is deprecated" % self.__class__)
+
+ def __repr__(self):
+ return util.generic_repr(self)
+
+class UserDefinedType(TypeEngine):
+ """Base for user defined types.
+
+ This should be the base of new types. Note that
+ for most cases, :class:`.TypeDecorator` is probably
+ more appropriate::
+
+ import sqlalchemy.types as types
+
+ class MyType(types.UserDefinedType):
+ def __init__(self, precision = 8):
+ self.precision = precision
+
+ def get_col_spec(self):
+ return "MYTYPE(%s)" % self.precision
+
+ def bind_processor(self, dialect):
+ def process(value):
+ return value
+ return process
+
+ def result_processor(self, dialect, coltype):
+ def process(value):
+ return value
+ return process
+
+ Once the type is made, it's immediately usable::
+
+ table = Table('foo', meta,
+ Column('id', Integer, primary_key=True),
+ Column('data', MyType(16))
+ )
+
+ """
+ __visit_name__ = "user_defined"
+
+
+ class Comparator(TypeEngine.Comparator):
+ def _adapt_expression(self, op, other_comparator):
+ if hasattr(self.type, 'adapt_operator'):
+ util.warn_deprecated(
+ "UserDefinedType.adapt_operator is deprecated. Create "
+ "a UserDefinedType.Comparator subclass instead which "
+ "generates the desired expression constructs, given a "
+ "particular operator."
+ )
+ return self.type.adapt_operator(op), self.type
+ else:
+ return op, self.type
+
+ comparator_factory = Comparator
+
+ def coerce_compared_value(self, op, value):
+ """Suggest a type for a 'coerced' Python value in an expression.
+
+ Default behavior for :class:`.UserDefinedType` is the
+ same as that of :class:`.TypeDecorator`; by default it returns
+ ``self``, assuming the compared value should be coerced into
+ the same type as this one. See
+ :meth:`.TypeDecorator.coerce_compared_value` for more detail.
+
+ .. versionchanged:: 0.8 :meth:`.UserDefinedType.coerce_compared_value`
+ now returns ``self`` by default, rather than falling onto the
+ more fundamental behavior of
+ :meth:`.TypeEngine.coerce_compared_value`.
+
+ """
+
+ return self
+
+
+class TypeDecorator(TypeEngine):
+ """Allows the creation of types which add additional functionality
+ to an existing type.
+
+ This method is preferred to direct subclassing of SQLAlchemy's
+ built-in types as it ensures that all required functionality of
+ the underlying type is kept in place.
+
+ Typical usage::
+
+ import sqlalchemy.types as types
+
+ class MyType(types.TypeDecorator):
+ '''Prefixes Unicode values with "PREFIX:" on the way in and
+ strips it off on the way out.
+ '''
+
+ impl = types.Unicode
+
+ def process_bind_param(self, value, dialect):
+ return "PREFIX:" + value
+
+ def process_result_value(self, value, dialect):
+ return value[7:]
+
+ def copy(self):
+ return MyType(self.impl.length)
+
+ The class-level "impl" attribute is required, and can reference any
+ TypeEngine class. Alternatively, the load_dialect_impl() method
+ can be used to provide different type classes based on the dialect
+ given; in this case, the "impl" variable can reference
+ ``TypeEngine`` as a placeholder.
+
+ Types that receive a Python type that isn't similar to the ultimate type
+ used may want to define the :meth:`TypeDecorator.coerce_compared_value`
+ method. This is used to give the expression system a hint when coercing
+ Python objects into bind parameters within expressions. Consider this
+ expression::
+
+ mytable.c.somecol + datetime.date(2009, 5, 15)
+
+ Above, if "somecol" is an ``Integer`` variant, it makes sense that
+ we're doing date arithmetic, where above is usually interpreted
+ by databases as adding a number of days to the given date.
+ The expression system does the right thing by not attempting to
+ coerce the "date()" value into an integer-oriented bind parameter.
+
+ However, in the case of ``TypeDecorator``, we are usually changing an
+ incoming Python type to something new - ``TypeDecorator`` by default will
+ "coerce" the non-typed side to be the same type as itself. Such as below,
+ we define an "epoch" type that stores a date value as an integer::
+
+ class MyEpochType(types.TypeDecorator):
+ impl = types.Integer
+
+ epoch = datetime.date(1970, 1, 1)
+
+ def process_bind_param(self, value, dialect):
+ return (value - self.epoch).days
+
+ def process_result_value(self, value, dialect):
+ return self.epoch + timedelta(days=value)
+
+ Our expression of ``somecol + date`` with the above type will coerce the
+ "date" on the right side to also be treated as ``MyEpochType``.
+
+ This behavior can be overridden via the
+ :meth:`~TypeDecorator.coerce_compared_value` method, which returns a type
+ that should be used for the value of the expression. Below we set it such
+ that an integer value will be treated as an ``Integer``, and any other
+ value is assumed to be a date and will be treated as a ``MyEpochType``::
+
+ def coerce_compared_value(self, op, value):
+ if isinstance(value, int):
+ return Integer()
+ else:
+ return self
+
+ """
+
+ __visit_name__ = "type_decorator"
+
+ def __init__(self, *args, **kwargs):
+ """Construct a :class:`.TypeDecorator`.
+
+ Arguments sent here are passed to the constructor
+ of the class assigned to the ``impl`` class level attribute,
+ assuming the ``impl`` is a callable, and the resulting
+ object is assigned to the ``self.impl`` instance attribute
+ (thus overriding the class attribute of the same name).
+
+ If the class level ``impl`` is not a callable (the unusual case),
+ it will be assigned to the same instance attribute 'as-is',
+ ignoring those arguments passed to the constructor.
+
+ Subclasses can override this to customize the generation
+ of ``self.impl`` entirely.
+
+ """
+
+ if not hasattr(self.__class__, 'impl'):
+ raise AssertionError("TypeDecorator implementations "
+ "require a class-level variable "
+ "'impl' which refers to the class of "
+ "type being decorated")
+ self.impl = to_instance(self.__class__.impl, *args, **kwargs)
+
+ coerce_to_is_types = (util.NoneType, )
+ """Specify those Python types which should be coerced at the expression
+ level to "IS <constant>" when compared using ``==`` (and same for
+ ``IS NOT`` in conjunction with ``!=``.
+
+ For most SQLAlchemy types, this includes ``NoneType``, as well as ``bool``.
+
+ :class:`.TypeDecorator` modifies this list to only include ``NoneType``,
+ as typedecorator implementations that deal with boolean types are common.
+
+ Custom :class:`.TypeDecorator` classes can override this attribute to
+ return an empty tuple, in which case no values will be coerced to
+ constants.
+
+ ..versionadded:: 0.8.2
+ Added :attr:`.TypeDecorator.coerce_to_is_types` to allow for easier
+ control of ``__eq__()`` ``__ne__()`` operations.
+
+ """
+
+ class Comparator(TypeEngine.Comparator):
+
+ def operate(self, op, *other, **kwargs):
+ kwargs['_python_is_types'] = self.expr.type.coerce_to_is_types
+ return super(TypeDecorator.Comparator, self).operate(
+ op, *other, **kwargs)
+
+ def reverse_operate(self, op, other, **kwargs):
+ kwargs['_python_is_types'] = self.expr.type.coerce_to_is_types
+ return super(TypeDecorator.Comparator, self).reverse_operate(
+ op, other, **kwargs)
+
+ @property
+ def comparator_factory(self):
+ return type("TDComparator",
+ (TypeDecorator.Comparator, self.impl.comparator_factory),
+ {})
+
+ def _gen_dialect_impl(self, dialect):
+ """
+ #todo
+ """
+ adapted = dialect.type_descriptor(self)
+ if adapted is not self:
+ return adapted
+
+ # otherwise adapt the impl type, link
+ # to a copy of this TypeDecorator and return
+ # that.
+ typedesc = self.load_dialect_impl(dialect).dialect_impl(dialect)
+ tt = self.copy()
+ if not isinstance(tt, self.__class__):
+ raise AssertionError('Type object %s does not properly '
+ 'implement the copy() method, it must '
+ 'return an object of type %s' % (self,
+ self.__class__))
+ tt.impl = typedesc
+ return tt
+
+ @property
+ def _type_affinity(self):
+ """
+ #todo
+ """
+ return self.impl._type_affinity
+
+ def type_engine(self, dialect):
+ """Return a dialect-specific :class:`.TypeEngine` instance
+ for this :class:`.TypeDecorator`.
+
+ In most cases this returns a dialect-adapted form of
+ the :class:`.TypeEngine` type represented by ``self.impl``.
+ Makes usage of :meth:`dialect_impl` but also traverses
+ into wrapped :class:`.TypeDecorator` instances.
+ Behavior can be customized here by overriding
+ :meth:`load_dialect_impl`.
+
+ """
+ adapted = dialect.type_descriptor(self)
+ if type(adapted) is not type(self):
+ return adapted
+ elif isinstance(self.impl, TypeDecorator):
+ return self.impl.type_engine(dialect)
+ else:
+ return self.load_dialect_impl(dialect)
+
+ def load_dialect_impl(self, dialect):
+ """Return a :class:`.TypeEngine` object corresponding to a dialect.
+
+ This is an end-user override hook that can be used to provide
+ differing types depending on the given dialect. It is used
+ by the :class:`.TypeDecorator` implementation of :meth:`type_engine`
+ to help determine what type should ultimately be returned
+ for a given :class:`.TypeDecorator`.
+
+ By default returns ``self.impl``.
+
+ """
+ return self.impl
+
+ def __getattr__(self, key):
+ """Proxy all other undefined accessors to the underlying
+ implementation."""
+ return getattr(self.impl, key)
+
+ def process_bind_param(self, value, dialect):
+ """Receive a bound parameter value to be converted.
+
+ Subclasses override this method to return the
+ value that should be passed along to the underlying
+ :class:`.TypeEngine` object, and from there to the
+ DBAPI ``execute()`` method.
+
+ The operation could be anything desired to perform custom
+ behavior, such as transforming or serializing data.
+ This could also be used as a hook for validating logic.
+
+ This operation should be designed with the reverse operation
+ in mind, which would be the process_result_value method of
+ this class.
+
+ :param value: Data to operate upon, of any type expected by
+ this method in the subclass. Can be ``None``.
+ :param dialect: the :class:`.Dialect` in use.
+
+ """
+
+ raise NotImplementedError()
+
+ def process_result_value(self, value, dialect):
+ """Receive a result-row column value to be converted.
+
+ Subclasses should implement this method to operate on data
+ fetched from the database.
+
+ Subclasses override this method to return the
+ value that should be passed back to the application,
+ given a value that is already processed by
+ the underlying :class:`.TypeEngine` object, originally
+ from the DBAPI cursor method ``fetchone()`` or similar.
+
+ The operation could be anything desired to perform custom
+ behavior, such as transforming or serializing data.
+ This could also be used as a hook for validating logic.
+
+ :param value: Data to operate upon, of any type expected by
+ this method in the subclass. Can be ``None``.
+ :param dialect: the :class:`.Dialect` in use.
+
+ This operation should be designed to be reversible by
+ the "process_bind_param" method of this class.
+
+ """
+
+ raise NotImplementedError()
+
+ @util.memoized_property
+ def _has_bind_processor(self):
+ """memoized boolean, check if process_bind_param is implemented.
+
+ Allows the base process_bind_param to raise
+ NotImplementedError without needing to test an expensive
+ exception throw.
+
+ """
+
+ return self.__class__.process_bind_param.__code__ \
+ is not TypeDecorator.process_bind_param.__code__
+
+ def bind_processor(self, dialect):
+ """Provide a bound value processing function for the
+ given :class:`.Dialect`.
+
+ This is the method that fulfills the :class:`.TypeEngine`
+ contract for bound value conversion. :class:`.TypeDecorator`
+ will wrap a user-defined implementation of
+ :meth:`process_bind_param` here.
+
+ User-defined code can override this method directly,
+ though its likely best to use :meth:`process_bind_param` so that
+ the processing provided by ``self.impl`` is maintained.
+
+ :param dialect: Dialect instance in use.
+
+ This method is the reverse counterpart to the
+ :meth:`result_processor` method of this class.
+
+ """
+ if self._has_bind_processor:
+ process_param = self.process_bind_param
+ impl_processor = self.impl.bind_processor(dialect)
+ if impl_processor:
+ def process(value):
+ return impl_processor(process_param(value, dialect))
+
+ else:
+ def process(value):
+ return process_param(value, dialect)
+
+ return process
+ else:
+ return self.impl.bind_processor(dialect)
+
+ @util.memoized_property
+ def _has_result_processor(self):
+ """memoized boolean, check if process_result_value is implemented.
+
+ Allows the base process_result_value to raise
+ NotImplementedError without needing to test an expensive
+ exception throw.
+
+ """
+ return self.__class__.process_result_value.__code__ \
+ is not TypeDecorator.process_result_value.__code__
+
+ def result_processor(self, dialect, coltype):
+ """Provide a result value processing function for the given
+ :class:`.Dialect`.
+
+ This is the method that fulfills the :class:`.TypeEngine`
+ contract for result value conversion. :class:`.TypeDecorator`
+ will wrap a user-defined implementation of
+ :meth:`process_result_value` here.
+
+ User-defined code can override this method directly,
+ though its likely best to use :meth:`process_result_value` so that
+ the processing provided by ``self.impl`` is maintained.
+
+ :param dialect: Dialect instance in use.
+ :param coltype: An SQLAlchemy data type
+
+ This method is the reverse counterpart to the
+ :meth:`bind_processor` method of this class.
+
+ """
+ if self._has_result_processor:
+ process_value = self.process_result_value
+ impl_processor = self.impl.result_processor(dialect,
+ coltype)
+ if impl_processor:
+ def process(value):
+ return process_value(impl_processor(value), dialect)
+
+ else:
+ def process(value):
+ return process_value(value, dialect)
+
+ return process
+ else:
+ return self.impl.result_processor(dialect, coltype)
+
+ def coerce_compared_value(self, op, value):
+ """Suggest a type for a 'coerced' Python value in an expression.
+
+ By default, returns self. This method is called by
+ the expression system when an object using this type is
+ on the left or right side of an expression against a plain Python
+ object which does not yet have a SQLAlchemy type assigned::
+
+ expr = table.c.somecolumn + 35
+
+ Where above, if ``somecolumn`` uses this type, this method will
+ be called with the value ``operator.add``
+ and ``35``. The return value is whatever SQLAlchemy type should
+ be used for ``35`` for this particular operation.
+
+ """
+ return self
+
+ def copy(self):
+ """Produce a copy of this :class:`.TypeDecorator` instance.
+
+ This is a shallow copy and is provided to fulfill part of
+ the :class:`.TypeEngine` contract. It usually does not
+ need to be overridden unless the user-defined :class:`.TypeDecorator`
+ has local state that should be deep-copied.
+
+ """
+
+ instance = self.__class__.__new__(self.__class__)
+ instance.__dict__.update(self.__dict__)
+ return instance
+
+ def get_dbapi_type(self, dbapi):
+ """Return the DBAPI type object represented by this
+ :class:`.TypeDecorator`.
+
+ By default this calls upon :meth:`.TypeEngine.get_dbapi_type` of the
+ underlying "impl".
+ """
+ return self.impl.get_dbapi_type(dbapi)
+
+ def compare_values(self, x, y):
+ """Given two values, compare them for equality.
+
+ By default this calls upon :meth:`.TypeEngine.compare_values`
+ of the underlying "impl", which in turn usually
+ uses the Python equals operator ``==``.
+
+ This function is used by the ORM to compare
+ an original-loaded value with an intercepted
+ "changed" value, to determine if a net change
+ has occurred.
+
+ """
+ return self.impl.compare_values(x, y)
+
+ def __repr__(self):
+ return util.generic_repr(self, to_inspect=self.impl)
+
+
+class Variant(TypeDecorator):
+ """A wrapping type that selects among a variety of
+ implementations based on dialect in use.
+
+ The :class:`.Variant` type is typically constructed
+ using the :meth:`.TypeEngine.with_variant` method.
+
+ .. versionadded:: 0.7.2
+
+ .. seealso:: :meth:`.TypeEngine.with_variant` for an example of use.
+
+ """
+
+ def __init__(self, base, mapping):
+ """Construct a new :class:`.Variant`.
+
+ :param base: the base 'fallback' type
+ :param mapping: dictionary of string dialect names to
+ :class:`.TypeEngine` instances.
+
+ """
+ self.impl = base
+ self.mapping = mapping
+
+ def load_dialect_impl(self, dialect):
+ if dialect.name in self.mapping:
+ return self.mapping[dialect.name]
+ else:
+ return self.impl
+
+ def with_variant(self, type_, dialect_name):
+ """Return a new :class:`.Variant` which adds the given
+ type + dialect name to the mapping, in addition to the
+ mapping present in this :class:`.Variant`.
+
+ :param type_: a :class:`.TypeEngine` that will be selected
+ as a variant from the originating type, when a dialect
+ of the given name is in use.
+ :param dialect_name: base name of the dialect which uses
+ this type. (i.e. ``'postgresql'``, ``'mysql'``, etc.)
+
+ """
+
+ if dialect_name in self.mapping:
+ raise exc.ArgumentError(
+ "Dialect '%s' is already present in "
+ "the mapping for this Variant" % dialect_name)
+ mapping = self.mapping.copy()
+ mapping[dialect_name] = type_
+ return Variant(self.impl, mapping)
+
+def _reconstitute_comparator(expression):
+ return expression.comparator
+
+
+def to_instance(typeobj, *arg, **kw):
+ if typeobj is None:
+ return NULLTYPE
+
+ if util.callable(typeobj):
+ return typeobj(*arg, **kw)
+ else:
+ return typeobj
+
+
+def adapt_type(typeobj, colspecs):
+ if isinstance(typeobj, type):
+ typeobj = typeobj()
+ for t in typeobj.__class__.__mro__[0:-1]:
+ try:
+ impltype = colspecs[t]
+ break
+ except KeyError:
+ pass
+ else:
+ # couldnt adapt - so just return the type itself
+ # (it may be a user-defined type)
+ return typeobj
+ # if we adapted the given generic type to a database-specific type,
+ # but it turns out the originally given "generic" type
+ # is actually a subclass of our resulting type, then we were already
+ # given a more specific type than that required; so use that.
+ if (issubclass(typeobj.__class__, impltype)):
+ return typeobj
+ return typeobj.adapt(impltype)
+
+
diff --git a/lib/sqlalchemy/sql/util.py b/lib/sqlalchemy/sql/util.py
index 6796d7edb..a9bd22cc6 100644
--- a/lib/sqlalchemy/sql/util.py
+++ b/lib/sqlalchemy/sql/util.py
@@ -4,45 +4,27 @@
# This module is part of SQLAlchemy and is released under
# the MIT License: http://www.opensource.org/licenses/mit-license.php
-from .. import exc, schema, util, sql
-from ..util import topological
-from . import expression, operators, visitors
-from itertools import chain
-from collections import deque
+"""High level utilities which build upon other modules here.
-"""Utility functions that build upon SQL and Schema constructs."""
+"""
+from .. import exc, util
+from .base import _from_objects, ColumnSet
+from . import operators, visitors
+from itertools import chain
+from collections import deque
-def sort_tables(tables, skip_fn=None, extra_dependencies=None):
- """sort a collection of Table objects in order of
- their foreign-key dependency."""
-
- tables = list(tables)
- tuples = []
- if extra_dependencies is not None:
- tuples.extend(extra_dependencies)
+from .elements import BindParameter, ColumnClause, ColumnElement, \
+ Null, UnaryExpression, literal_column, Label
+from .selectable import ScalarSelect, Join, FromClause, FromGrouping
+from .schema import Column
- def visit_foreign_key(fkey):
- if fkey.use_alter:
- return
- elif skip_fn and skip_fn(fkey):
- return
- parent_table = fkey.column.table
- if parent_table in tables:
- child_table = fkey.parent.table
- if parent_table is not child_table:
- tuples.append((parent_table, child_table))
-
- for table in tables:
- visitors.traverse(table,
- {'schema_visitor': True},
- {'foreign_key': visit_foreign_key})
-
- tuples.extend(
- [parent, table] for parent in table._extra_dependencies
- )
+join_condition = util.langhelpers.public_factory(Join._join_condition)
- return list(topological.sort(tuples, tables))
+# names that are still being imported from the outside
+from .annotation import _shallow_annotate, _deep_annotate, _deep_deannotate
+from .elements import _find_columns
+from .ddl import sort_tables
def find_join_source(clauses, join_to):
@@ -62,7 +44,7 @@ def find_join_source(clauses, join_to):
"""
- selectables = list(expression._from_objects(join_to))
+ selectables = list(_from_objects(join_to))
for i, f in enumerate(clauses):
for s in selectables:
if f.is_derived_from(s):
@@ -109,7 +91,7 @@ def visit_binary_product(fn, expr):
stack = []
def visit(element):
- if isinstance(element, (expression.ScalarSelect)):
+ if isinstance(element, ScalarSelect):
# we dont want to dig into correlated subqueries,
# those are just column elements by themselves
yield element
@@ -123,7 +105,7 @@ def visit_binary_product(fn, expr):
for elem in element.get_children():
visit(elem)
else:
- if isinstance(element, expression.ColumnClause):
+ if isinstance(element, ColumnClause):
yield element
for elem in element.get_children():
for e in visit(elem):
@@ -163,13 +145,6 @@ def find_tables(clause, check_columns=False,
return tables
-def find_columns(clause):
- """locate Column objects within the given expression."""
-
- cols = util.column_set()
- visitors.traverse(clause, {}, {'column': cols.add})
- return cols
-
def unwrap_order_by(clause):
"""Break up an 'order by' expression into individual column-expressions,
@@ -179,9 +154,9 @@ def unwrap_order_by(clause):
stack = deque([clause])
while stack:
t = stack.popleft()
- if isinstance(t, expression.ColumnElement) and \
+ if isinstance(t, ColumnElement) and \
(
- not isinstance(t, expression.UnaryExpression) or \
+ not isinstance(t, UnaryExpression) or \
not operators.is_ordering_modifier(t.modifier)
):
cols.add(t)
@@ -211,9 +186,9 @@ def surface_selectables(clause):
while stack:
elem = stack.pop()
yield elem
- if isinstance(elem, expression.Join):
+ if isinstance(elem, Join):
stack.extend((elem.left, elem.right))
- elif isinstance(elem, expression.FromGrouping):
+ elif isinstance(elem, FromGrouping):
stack.append(elem.element)
def selectables_overlap(left, right):
@@ -287,11 +262,11 @@ def expression_as_ddl(clause):
"""
def repl(element):
- if isinstance(element, expression.BindParameter):
- return expression.literal_column(_quote_ddl_expr(element.value))
- elif isinstance(element, expression.ColumnClause) and \
+ if isinstance(element, BindParameter):
+ return literal_column(_quote_ddl_expr(element.value))
+ elif isinstance(element, ColumnClause) and \
element.table is not None:
- col = expression.column(element.name)
+ col = ColumnClause(element.name)
col.quote = element.quote
return col
else:
@@ -307,308 +282,22 @@ def adapt_criterion_to_null(crit, nulls):
"""
def visit_binary(binary):
- if isinstance(binary.left, expression.BindParameter) \
+ if isinstance(binary.left, BindParameter) \
and binary.left._identifying_key in nulls:
# reverse order if the NULL is on the left side
binary.left = binary.right
- binary.right = expression.null()
+ binary.right = Null()
binary.operator = operators.is_
binary.negate = operators.isnot
- elif isinstance(binary.right, expression.BindParameter) \
+ elif isinstance(binary.right, BindParameter) \
and binary.right._identifying_key in nulls:
- binary.right = expression.null()
+ binary.right = Null()
binary.operator = operators.is_
binary.negate = operators.isnot
return visitors.cloned_traverse(crit, {}, {'binary': visit_binary})
-def join_condition(a, b, ignore_nonexistent_tables=False,
- a_subset=None,
- consider_as_foreign_keys=None):
- """create a join condition between two tables or selectables.
-
- e.g.::
-
- join_condition(tablea, tableb)
-
- would produce an expression along the lines of::
-
- tablea.c.id==tableb.c.tablea_id
-
- The join is determined based on the foreign key relationships
- between the two selectables. If there are multiple ways
- to join, or no way to join, an error is raised.
-
- :param ignore_nonexistent_tables: Deprecated - this
- flag is no longer used. Only resolution errors regarding
- the two given tables are propagated.
-
- :param a_subset: An optional expression that is a sub-component
- of ``a``. An attempt will be made to join to just this sub-component
- first before looking at the full ``a`` construct, and if found
- will be successful even if there are other ways to join to ``a``.
- This allows the "right side" of a join to be passed thereby
- providing a "natural join".
-
- """
- crit = []
- constraints = set()
-
- for left in (a_subset, a):
- if left is None:
- continue
- for fk in sorted(
- b.foreign_keys,
- key=lambda fk: fk.parent._creation_order):
- if consider_as_foreign_keys is not None and \
- fk.parent not in consider_as_foreign_keys:
- continue
- try:
- col = fk.get_referent(left)
- except exc.NoReferenceError as nrte:
- if nrte.table_name == left.name:
- raise
- else:
- continue
-
- if col is not None:
- crit.append(col == fk.parent)
- constraints.add(fk.constraint)
- if left is not b:
- for fk in sorted(
- left.foreign_keys,
- key=lambda fk: fk.parent._creation_order):
- if consider_as_foreign_keys is not None and \
- fk.parent not in consider_as_foreign_keys:
- continue
- try:
- col = fk.get_referent(b)
- except exc.NoReferenceError as nrte:
- if nrte.table_name == b.name:
- raise
- else:
- # this is totally covered. can't get
- # coverage to mark it.
- continue
-
- if col is not None:
- crit.append(col == fk.parent)
- constraints.add(fk.constraint)
- if crit:
- break
-
- if len(crit) == 0:
- if isinstance(b, expression.FromGrouping):
- hint = " Perhaps you meant to convert the right side to a "\
- "subquery using alias()?"
- else:
- hint = ""
- raise exc.NoForeignKeysError(
- "Can't find any foreign key relationships "
- "between '%s' and '%s'.%s" % (a.description, b.description, hint))
- elif len(constraints) > 1:
- raise exc.AmbiguousForeignKeysError(
- "Can't determine join between '%s' and '%s'; "
- "tables have more than one foreign key "
- "constraint relationship between them. "
- "Please specify the 'onclause' of this "
- "join explicitly." % (a.description, b.description))
- elif len(crit) == 1:
- return (crit[0])
- else:
- return sql.and_(*crit)
-
-
-class Annotated(object):
- """clones a ClauseElement and applies an 'annotations' dictionary.
-
- Unlike regular clones, this clone also mimics __hash__() and
- __cmp__() of the original element so that it takes its place
- in hashed collections.
-
- A reference to the original element is maintained, for the important
- reason of keeping its hash value current. When GC'ed, the
- hash value may be reused, causing conflicts.
-
- """
-
- def __new__(cls, *args):
- if not args:
- # clone constructor
- return object.__new__(cls)
- else:
- element, values = args
- # pull appropriate subclass from registry of annotated
- # classes
- try:
- cls = annotated_classes[element.__class__]
- except KeyError:
- cls = annotated_classes[element.__class__] = type.__new__(type,
- "Annotated%s" % element.__class__.__name__,
- (cls, element.__class__), {})
- return object.__new__(cls)
-
- def __init__(self, element, values):
- # force FromClause to generate their internal
- # collections into __dict__
- if isinstance(element, expression.FromClause):
- element.c
-
- self.__dict__ = element.__dict__.copy()
- expression.ColumnElement.comparator._reset(self)
- self.__element = element
- self._annotations = values
-
- def _annotate(self, values):
- _values = self._annotations.copy()
- _values.update(values)
- return self._with_annotations(_values)
-
- def _with_annotations(self, values):
- clone = self.__class__.__new__(self.__class__)
- clone.__dict__ = self.__dict__.copy()
- expression.ColumnElement.comparator._reset(clone)
- clone._annotations = values
- return clone
-
- def _deannotate(self, values=None, clone=True):
- if values is None:
- return self.__element
- else:
- _values = self._annotations.copy()
- for v in values:
- _values.pop(v, None)
- return self._with_annotations(_values)
-
- def _compiler_dispatch(self, visitor, **kw):
- return self.__element.__class__._compiler_dispatch(self, visitor, **kw)
-
- @property
- def _constructor(self):
- return self.__element._constructor
-
- def _clone(self):
- clone = self.__element._clone()
- if clone is self.__element:
- # detect immutable, don't change anything
- return self
- else:
- # update the clone with any changes that have occurred
- # to this object's __dict__.
- clone.__dict__.update(self.__dict__)
- return self.__class__(clone, self._annotations)
-
- def __hash__(self):
- return hash(self.__element)
-
- def __eq__(self, other):
- if isinstance(self.__element, expression.ColumnOperators):
- return self.__element.__class__.__eq__(self, other)
- else:
- return hash(other) == hash(self)
-
-
-class AnnotatedColumnElement(Annotated):
- def __init__(self, element, values):
- Annotated.__init__(self, element, values)
- for attr in ('name', 'key'):
- if self.__dict__.get(attr, False) is None:
- self.__dict__.pop(attr)
-
- @util.memoized_property
- def name(self):
- """pull 'name' from parent, if not present"""
- return self._Annotated__element.name
-
- @util.memoized_property
- def key(self):
- """pull 'key' from parent, if not present"""
- return self._Annotated__element.key
-
- @util.memoized_property
- def info(self):
- return self._Annotated__element.info
-
-# hard-generate Annotated subclasses. this technique
-# is used instead of on-the-fly types (i.e. type.__new__())
-# so that the resulting objects are pickleable.
-annotated_classes = {}
-
-for cls in list(expression.__dict__.values()) + [schema.Column, schema.Table]:
- if isinstance(cls, type) and issubclass(cls, expression.ClauseElement):
- if issubclass(cls, expression.ColumnElement):
- annotation_cls = "AnnotatedColumnElement"
- else:
- annotation_cls = "Annotated"
- exec("class Annotated%s(%s, cls):\n" \
- " pass" % (cls.__name__, annotation_cls), locals())
- exec("annotated_classes[cls] = Annotated%s" % (cls.__name__,))
-
-
-def _deep_annotate(element, annotations, exclude=None):
- """Deep copy the given ClauseElement, annotating each element
- with the given annotations dictionary.
-
- Elements within the exclude collection will be cloned but not annotated.
-
- """
- def clone(elem):
- if exclude and \
- hasattr(elem, 'proxy_set') and \
- elem.proxy_set.intersection(exclude):
- newelem = elem._clone()
- elif annotations != elem._annotations:
- newelem = elem._annotate(annotations)
- else:
- newelem = elem
- newelem._copy_internals(clone=clone)
- return newelem
-
- if element is not None:
- element = clone(element)
- return element
-
-
-def _deep_deannotate(element, values=None):
- """Deep copy the given element, removing annotations."""
-
- cloned = util.column_dict()
-
- def clone(elem):
- # if a values dict is given,
- # the elem must be cloned each time it appears,
- # as there may be different annotations in source
- # elements that are remaining. if totally
- # removing all annotations, can assume the same
- # slate...
- if values or elem not in cloned:
- newelem = elem._deannotate(values=values, clone=True)
- newelem._copy_internals(clone=clone)
- if not values:
- cloned[elem] = newelem
- return newelem
- else:
- return cloned[elem]
-
- if element is not None:
- element = clone(element)
- return element
-
-
-def _shallow_annotate(element, annotations):
- """Annotate the given ClauseElement and copy its internals so that
- internal objects refer to the new annotated object.
-
- Basically used to apply a "dont traverse" annotation to a
- selectable, without digging throughout the whole
- structure wasting time.
- """
- element = element._annotate(annotations)
- element._copy_internals()
- return element
-
-
def splice_joins(left, right, stop_on=None):
if left is None:
return right
@@ -619,7 +308,7 @@ def splice_joins(left, right, stop_on=None):
ret = None
while stack:
(right, prevright) = stack.pop()
- if isinstance(right, expression.Join) and right is not stop_on:
+ if isinstance(right, Join) and right is not stop_on:
right = right._clone()
right._reset_exported()
right.onclause = adapter.traverse(right.onclause)
@@ -703,7 +392,7 @@ def reduce_columns(columns, *clauses, **kw):
if clause is not None:
visitors.traverse(clause, {}, {'binary': visit_binary})
- return expression.ColumnSet(columns.difference(omit))
+ return ColumnSet(columns.difference(omit))
def criterion_as_pairs(expression, consider_as_foreign_keys=None,
@@ -722,8 +411,8 @@ def criterion_as_pairs(expression, consider_as_foreign_keys=None,
def visit_binary(binary):
if not any_operator and binary.operator is not operators.eq:
return
- if not isinstance(binary.left, sql.ColumnElement) or \
- not isinstance(binary.right, sql.ColumnElement):
+ if not isinstance(binary.left, ColumnElement) or \
+ not isinstance(binary.right, ColumnElement):
return
if consider_as_foreign_keys:
@@ -745,8 +434,8 @@ def criterion_as_pairs(expression, consider_as_foreign_keys=None,
binary.left not in consider_as_referenced_keys):
pairs.append((binary.right, binary.left))
else:
- if isinstance(binary.left, schema.Column) and \
- isinstance(binary.right, schema.Column):
+ if isinstance(binary.left, Column) and \
+ isinstance(binary.right, Column):
if binary.left.references(binary.right):
pairs.append((binary.right, binary.left))
elif binary.right.references(binary.left):
@@ -756,6 +445,7 @@ def criterion_as_pairs(expression, consider_as_foreign_keys=None,
return pairs
+
class AliasedRow(object):
"""Wrap a RowProxy with a translation map.
@@ -848,10 +538,10 @@ class ClauseAdapter(visitors.ReplacingCloningVisitor):
magic_flag = False
def replace(self, col):
- if not self.magic_flag and isinstance(col, expression.FromClause) and \
+ if not self.magic_flag and isinstance(col, FromClause) and \
self.selectable.is_derived_from(col):
return self.selectable
- elif not isinstance(col, expression.ColumnElement):
+ elif not isinstance(col, ColumnElement):
return None
elif self.include_fn and not self.include_fn(col):
return None
@@ -903,7 +593,7 @@ class ColumnAdapter(ClauseAdapter):
c = self.adapt_clause(col)
# anonymize labels in case they have a hardcoded name
- if isinstance(c, expression.Label):
+ if isinstance(c, Label):
c = c.label(None)
# adapt_required used by eager loading to indicate that
@@ -927,3 +617,4 @@ class ColumnAdapter(ClauseAdapter):
def __setstate__(self, state):
self.__dict__.update(state)
self.columns = util.PopulateDict(self._locate_col)
+
diff --git a/lib/sqlalchemy/testing/plugin/noseplugin.py b/lib/sqlalchemy/testing/plugin/noseplugin.py
index b3cd3a4e3..319aefa96 100644
--- a/lib/sqlalchemy/testing/plugin/noseplugin.py
+++ b/lib/sqlalchemy/testing/plugin/noseplugin.py
@@ -351,6 +351,8 @@ class NoseSQLAlchemy(Plugin):
return ""
def wantFunction(self, fn):
+ if fn.__module__ is None:
+ return False
if fn.__module__.startswith('sqlalchemy.testing'):
return False
diff --git a/lib/sqlalchemy/types.py b/lib/sqlalchemy/types.py
index 017c8dd04..3a2154fc5 100644
--- a/lib/sqlalchemy/types.py
+++ b/lib/sqlalchemy/types.py
@@ -1,16 +1,13 @@
-# sqlalchemy/types.py
+# types.py
# Copyright (C) 2005-2013 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
-"""defines genericized SQL types, each represented by a subclass of
-:class:`~sqlalchemy.types.AbstractType`. Dialects define further subclasses
-of these types.
-
-For more information see the SQLAlchemy documentation on types.
+"""Compatiblity namespace for sqlalchemy.sql.types.
"""
+
__all__ = ['TypeEngine', 'TypeDecorator', 'AbstractType', 'UserDefinedType',
'INT', 'CHAR', 'VARCHAR', 'NCHAR', 'NVARCHAR', 'TEXT', 'Text',
'FLOAT', 'NUMERIC', 'REAL', 'DECIMAL', 'TIMESTAMP', 'DATETIME',
@@ -20,2478 +17,60 @@ __all__ = ['TypeEngine', 'TypeDecorator', 'AbstractType', 'UserDefinedType',
'Date', 'Time', 'LargeBinary', 'Binary', 'Boolean', 'Unicode',
'Concatenable', 'UnicodeText', 'PickleType', 'Interval', 'Enum']
-import datetime as dt
-import codecs
-
-from . import exc, schema, util, processors, events, event
-from .sql import operators
-from .sql.expression import _DefaultColumnComparator
-from .util import pickle
-from .sql.visitors import Visitable
-import decimal
-default = util.importlater("sqlalchemy.engine", "default")
-
-NoneType = type(None)
-if util.jython:
- import array
-
-
-class AbstractType(Visitable):
- """Base for all types - not needed except for backwards
- compatibility."""
-
-
-class TypeEngine(AbstractType):
- """Base for built-in types."""
-
- class Comparator(_DefaultColumnComparator):
- """Base class for custom comparison operations defined at the
- type level. See :attr:`.TypeEngine.comparator_factory`.
-
- The public base class for :class:`.TypeEngine.Comparator`
- is :class:`.ColumnOperators`.
-
- """
-
- def __init__(self, expr):
- self.expr = expr
-
- def __reduce__(self):
- return _reconstitute_comparator, (self.expr, )
-
- hashable = True
- """Flag, if False, means values from this type aren't hashable.
-
- Used by the ORM when uniquing result lists.
-
- """
-
- comparator_factory = Comparator
- """A :class:`.TypeEngine.Comparator` class which will apply
- to operations performed by owning :class:`.ColumnElement` objects.
-
- The :attr:`.comparator_factory` attribute is a hook consulted by
- the core expression system when column and SQL expression operations
- are performed. When a :class:`.TypeEngine.Comparator` class is
- associated with this attribute, it allows custom re-definition of
- all existing operators, as well as definition of new operators.
- Existing operators include those provided by Python operator overloading
- such as :meth:`.operators.ColumnOperators.__add__` and
- :meth:`.operators.ColumnOperators.__eq__`,
- those provided as standard
- attributes of :class:`.operators.ColumnOperators` such as
- :meth:`.operators.ColumnOperators.like`
- and :meth:`.operators.ColumnOperators.in_`.
-
- Rudimentary usage of this hook is allowed through simple subclassing
- of existing types, or alternatively by using :class:`.TypeDecorator`.
- See the documentation section :ref:`types_operators` for examples.
-
- .. versionadded:: 0.8 The expression system was enhanced to support
- customization of operators on a per-type level.
-
- """
-
- def copy_value(self, value):
- return value
-
- def bind_processor(self, dialect):
- """Return a conversion function for processing bind values.
-
- Returns a callable which will receive a bind parameter value
- as the sole positional argument and will return a value to
- send to the DB-API.
-
- If processing is not necessary, the method should return ``None``.
-
- :param dialect: Dialect instance in use.
-
- """
- return None
-
- def result_processor(self, dialect, coltype):
- """Return a conversion function for processing result row values.
-
- Returns a callable which will receive a result row column
- value as the sole positional argument and will return a value
- to return to the user.
-
- If processing is not necessary, the method should return ``None``.
-
- :param dialect: Dialect instance in use.
-
- :param coltype: DBAPI coltype argument received in cursor.description.
-
- """
- return None
-
- def column_expression(self, colexpr):
- """Given a SELECT column expression, return a wrapping SQL expression.
-
- This is typically a SQL function that wraps a column expression
- as rendered in the columns clause of a SELECT statement.
- It is used for special data types that require
- columns to be wrapped in some special database function in order
- to coerce the value before being sent back to the application.
- It is the SQL analogue of the :meth:`.TypeEngine.result_processor`
- method.
-
- The method is evaluated at statement compile time, as opposed
- to statement construction time.
-
- See also:
-
- :ref:`types_sql_value_processing`
-
- """
-
- return None
-
- @util.memoized_property
- def _has_column_expression(self):
- """memoized boolean, check if column_expression is implemented.
-
- Allows the method to be skipped for the vast majority of expression
- types that don't use this feature.
-
- """
-
- return self.__class__.column_expression.__code__ \
- is not TypeEngine.column_expression.__code__
-
- def bind_expression(self, bindvalue):
- """"Given a bind value (i.e. a :class:`.BindParameter` instance),
- return a SQL expression in its place.
-
- This is typically a SQL function that wraps the existing bound
- parameter within the statement. It is used for special data types
- that require literals being wrapped in some special database function
- in order to coerce an application-level value into a database-specific
- format. It is the SQL analogue of the
- :meth:`.TypeEngine.bind_processor` method.
-
- The method is evaluated at statement compile time, as opposed
- to statement construction time.
-
- Note that this method, when implemented, should always return
- the exact same structure, without any conditional logic, as it
- may be used in an executemany() call against an arbitrary number
- of bound parameter sets.
-
- See also:
-
- :ref:`types_sql_value_processing`
-
- """
- return None
-
- @util.memoized_property
- def _has_bind_expression(self):
- """memoized boolean, check if bind_expression is implemented.
-
- Allows the method to be skipped for the vast majority of expression
- types that don't use this feature.
-
- """
-
- return self.__class__.bind_expression.__code__ \
- is not TypeEngine.bind_expression.__code__
-
- def compare_values(self, x, y):
- """Compare two values for equality."""
-
- return x == y
-
- def get_dbapi_type(self, dbapi):
- """Return the corresponding type object from the underlying DB-API, if
- any.
-
- This can be useful for calling ``setinputsizes()``, for example.
-
- """
- return None
-
- @property
- def python_type(self):
- """Return the Python type object expected to be returned
- by instances of this type, if known.
-
- Basically, for those types which enforce a return type,
- or are known across the board to do such for all common
- DBAPIs (like ``int`` for example), will return that type.
-
- If a return type is not defined, raises
- ``NotImplementedError``.
-
- Note that any type also accommodates NULL in SQL which
- means you can also get back ``None`` from any type
- in practice.
-
- """
- raise NotImplementedError()
-
- def with_variant(self, type_, dialect_name):
- """Produce a new type object that will utilize the given
- type when applied to the dialect of the given name.
-
- e.g.::
-
- from sqlalchemy.types import String
- from sqlalchemy.dialects import mysql
-
- s = String()
-
- s = s.with_variant(mysql.VARCHAR(collation='foo'), 'mysql')
-
- The construction of :meth:`.TypeEngine.with_variant` is always
- from the "fallback" type to that which is dialect specific.
- The returned type is an instance of :class:`.Variant`, which
- itself provides a :meth:`~sqlalchemy.types.Variant.with_variant`
- that can be called repeatedly.
-
- :param type_: a :class:`.TypeEngine` that will be selected
- as a variant from the originating type, when a dialect
- of the given name is in use.
- :param dialect_name: base name of the dialect which uses
- this type. (i.e. ``'postgresql'``, ``'mysql'``, etc.)
-
- .. versionadded:: 0.7.2
-
- """
- return Variant(self, {dialect_name: type_})
-
- @util.memoized_property
- def _type_affinity(self):
- """Return a rudimental 'affinity' value expressing the general class
- of type."""
-
- typ = None
- for t in self.__class__.__mro__:
- if t is TypeEngine or t is UserDefinedType:
- return typ
- elif issubclass(t, TypeEngine):
- typ = t
- else:
- return self.__class__
-
- def dialect_impl(self, dialect):
- """Return a dialect-specific implementation for this
- :class:`.TypeEngine`.
-
- """
- try:
- return dialect._type_memos[self]['impl']
- except KeyError:
- return self._dialect_info(dialect)['impl']
-
- def _cached_bind_processor(self, dialect):
- """Return a dialect-specific bind processor for this type."""
-
- try:
- return dialect._type_memos[self]['bind']
- except KeyError:
- d = self._dialect_info(dialect)
- d['bind'] = bp = d['impl'].bind_processor(dialect)
- return bp
-
- def _cached_result_processor(self, dialect, coltype):
- """Return a dialect-specific result processor for this type."""
-
- try:
- return dialect._type_memos[self][coltype]
- except KeyError:
- d = self._dialect_info(dialect)
- # key assumption: DBAPI type codes are
- # constants. Else this dictionary would
- # grow unbounded.
- d[coltype] = rp = d['impl'].result_processor(dialect, coltype)
- return rp
-
- def _dialect_info(self, dialect):
- """Return a dialect-specific registry which
- caches a dialect-specific implementation, bind processing
- function, and one or more result processing functions."""
-
- if self in dialect._type_memos:
- return dialect._type_memos[self]
- else:
- impl = self._gen_dialect_impl(dialect)
- if impl is self:
- impl = self.adapt(type(self))
- # this can't be self, else we create a cycle
- assert impl is not self
- dialect._type_memos[self] = d = {'impl': impl}
- return d
-
- def _gen_dialect_impl(self, dialect):
- return dialect.type_descriptor(self)
-
- def adapt(self, cls, **kw):
- """Produce an "adapted" form of this type, given an "impl" class
- to work with.
-
- This method is used internally to associate generic
- types with "implementation" types that are specific to a particular
- dialect.
- """
- return util.constructor_copy(self, cls, **kw)
-
- def coerce_compared_value(self, op, value):
- """Suggest a type for a 'coerced' Python value in an expression.
-
- Given an operator and value, gives the type a chance
- to return a type which the value should be coerced into.
-
- The default behavior here is conservative; if the right-hand
- side is already coerced into a SQL type based on its
- Python type, it is usually left alone.
-
- End-user functionality extension here should generally be via
- :class:`.TypeDecorator`, which provides more liberal behavior in that
- it defaults to coercing the other side of the expression into this
- type, thus applying special Python conversions above and beyond those
- needed by the DBAPI to both ides. It also provides the public method
- :meth:`.TypeDecorator.coerce_compared_value` which is intended for
- end-user customization of this behavior.
-
- """
- _coerced_type = _type_map.get(type(value), NULLTYPE)
- if _coerced_type is NULLTYPE or _coerced_type._type_affinity \
- is self._type_affinity:
- return self
- else:
- return _coerced_type
-
- def _compare_type_affinity(self, other):
- return self._type_affinity is other._type_affinity
-
- def compile(self, dialect=None):
- """Produce a string-compiled form of this :class:`.TypeEngine`.
-
- When called with no arguments, uses a "default" dialect
- to produce a string result.
-
- :param dialect: a :class:`.Dialect` instance.
-
- """
- # arg, return value is inconsistent with
- # ClauseElement.compile()....this is a mistake.
-
- if not dialect:
- dialect = self._default_dialect
-
- return dialect.type_compiler.process(self)
-
- @property
- def _default_dialect(self):
- if self.__class__.__module__.startswith("sqlalchemy.dialects"):
- tokens = self.__class__.__module__.split(".")[0:3]
- mod = ".".join(tokens)
- return getattr(__import__(mod).dialects, tokens[-1]).dialect()
- else:
- return default.DefaultDialect()
-
- def __str__(self):
- if util.py2k:
- return unicode(self.compile()).\
- encode('ascii', 'backslashreplace')
- else:
- return str(self.compile())
-
- def __init__(self, *args, **kwargs):
- """Support implementations that were passing arguments"""
- if args or kwargs:
- util.warn_deprecated("Passing arguments to type object "
- "constructor %s is deprecated" % self.__class__)
-
- def __repr__(self):
- return util.generic_repr(self)
-
-
-def _reconstitute_comparator(expression):
- return expression.comparator
-
-
-class UserDefinedType(TypeEngine):
- """Base for user defined types.
-
- This should be the base of new types. Note that
- for most cases, :class:`.TypeDecorator` is probably
- more appropriate::
-
- import sqlalchemy.types as types
-
- class MyType(types.UserDefinedType):
- def __init__(self, precision = 8):
- self.precision = precision
-
- def get_col_spec(self):
- return "MYTYPE(%s)" % self.precision
-
- def bind_processor(self, dialect):
- def process(value):
- return value
- return process
-
- def result_processor(self, dialect, coltype):
- def process(value):
- return value
- return process
-
- Once the type is made, it's immediately usable::
-
- table = Table('foo', meta,
- Column('id', Integer, primary_key=True),
- Column('data', MyType(16))
- )
-
- """
- __visit_name__ = "user_defined"
-
- class Comparator(TypeEngine.Comparator):
- def _adapt_expression(self, op, other_comparator):
- if hasattr(self.type, 'adapt_operator'):
- util.warn_deprecated(
- "UserDefinedType.adapt_operator is deprecated. Create "
- "a UserDefinedType.Comparator subclass instead which "
- "generates the desired expression constructs, given a "
- "particular operator."
- )
- return self.type.adapt_operator(op), self.type
- else:
- return op, self.type
-
- comparator_factory = Comparator
-
- def coerce_compared_value(self, op, value):
- """Suggest a type for a 'coerced' Python value in an expression.
-
- Default behavior for :class:`.UserDefinedType` is the
- same as that of :class:`.TypeDecorator`; by default it returns
- ``self``, assuming the compared value should be coerced into
- the same type as this one. See
- :meth:`.TypeDecorator.coerce_compared_value` for more detail.
-
- .. versionchanged:: 0.8 :meth:`.UserDefinedType.coerce_compared_value`
- now returns ``self`` by default, rather than falling onto the
- more fundamental behavior of
- :meth:`.TypeEngine.coerce_compared_value`.
-
- """
-
- return self
-
-
-class TypeDecorator(TypeEngine):
- """Allows the creation of types which add additional functionality
- to an existing type.
-
- This method is preferred to direct subclassing of SQLAlchemy's
- built-in types as it ensures that all required functionality of
- the underlying type is kept in place.
-
- Typical usage::
-
- import sqlalchemy.types as types
-
- class MyType(types.TypeDecorator):
- '''Prefixes Unicode values with "PREFIX:" on the way in and
- strips it off on the way out.
- '''
-
- impl = types.Unicode
-
- def process_bind_param(self, value, dialect):
- return "PREFIX:" + value
-
- def process_result_value(self, value, dialect):
- return value[7:]
-
- def copy(self):
- return MyType(self.impl.length)
-
- The class-level "impl" attribute is required, and can reference any
- TypeEngine class. Alternatively, the load_dialect_impl() method
- can be used to provide different type classes based on the dialect
- given; in this case, the "impl" variable can reference
- ``TypeEngine`` as a placeholder.
-
- Types that receive a Python type that isn't similar to the ultimate type
- used may want to define the :meth:`TypeDecorator.coerce_compared_value`
- method. This is used to give the expression system a hint when coercing
- Python objects into bind parameters within expressions. Consider this
- expression::
-
- mytable.c.somecol + datetime.date(2009, 5, 15)
-
- Above, if "somecol" is an ``Integer`` variant, it makes sense that
- we're doing date arithmetic, where above is usually interpreted
- by databases as adding a number of days to the given date.
- The expression system does the right thing by not attempting to
- coerce the "date()" value into an integer-oriented bind parameter.
-
- However, in the case of ``TypeDecorator``, we are usually changing an
- incoming Python type to something new - ``TypeDecorator`` by default will
- "coerce" the non-typed side to be the same type as itself. Such as below,
- we define an "epoch" type that stores a date value as an integer::
-
- class MyEpochType(types.TypeDecorator):
- impl = types.Integer
-
- epoch = datetime.date(1970, 1, 1)
-
- def process_bind_param(self, value, dialect):
- return (value - self.epoch).days
-
- def process_result_value(self, value, dialect):
- return self.epoch + timedelta(days=value)
-
- Our expression of ``somecol + date`` with the above type will coerce the
- "date" on the right side to also be treated as ``MyEpochType``.
-
- This behavior can be overridden via the
- :meth:`~TypeDecorator.coerce_compared_value` method, which returns a type
- that should be used for the value of the expression. Below we set it such
- that an integer value will be treated as an ``Integer``, and any other
- value is assumed to be a date and will be treated as a ``MyEpochType``::
-
- def coerce_compared_value(self, op, value):
- if isinstance(value, int):
- return Integer()
- else:
- return self
-
- """
-
- __visit_name__ = "type_decorator"
-
- def __init__(self, *args, **kwargs):
- """Construct a :class:`.TypeDecorator`.
-
- Arguments sent here are passed to the constructor
- of the class assigned to the ``impl`` class level attribute,
- assuming the ``impl`` is a callable, and the resulting
- object is assigned to the ``self.impl`` instance attribute
- (thus overriding the class attribute of the same name).
-
- If the class level ``impl`` is not a callable (the unusual case),
- it will be assigned to the same instance attribute 'as-is',
- ignoring those arguments passed to the constructor.
-
- Subclasses can override this to customize the generation
- of ``self.impl`` entirely.
-
- """
-
- if not hasattr(self.__class__, 'impl'):
- raise AssertionError("TypeDecorator implementations "
- "require a class-level variable "
- "'impl' which refers to the class of "
- "type being decorated")
- self.impl = to_instance(self.__class__.impl, *args, **kwargs)
-
- coerce_to_is_types = (util.NoneType, )
- """Specify those Python types which should be coerced at the expression
- level to "IS <constant>" when compared using ``==`` (and same for
- ``IS NOT`` in conjunction with ``!=``.
-
- For most SQLAlchemy types, this includes ``NoneType``, as well as ``bool``.
-
- :class:`.TypeDecorator` modifies this list to only include ``NoneType``,
- as typedecorator implementations that deal with boolean types are common.
-
- Custom :class:`.TypeDecorator` classes can override this attribute to
- return an empty tuple, in which case no values will be coerced to
- constants.
-
- ..versionadded:: 0.8.2
- Added :attr:`.TypeDecorator.coerce_to_is_types` to allow for easier
- control of ``__eq__()`` ``__ne__()`` operations.
-
- """
-
- class Comparator(TypeEngine.Comparator):
- def operate(self, op, *other, **kwargs):
- kwargs['_python_is_types'] = self.expr.type.coerce_to_is_types
- return super(TypeDecorator.Comparator, self).operate(
- op, *other, **kwargs)
-
- def reverse_operate(self, op, other, **kwargs):
- kwargs['_python_is_types'] = self.expr.type.coerce_to_is_types
- return super(TypeDecorator.Comparator, self).reverse_operate(
- op, other, **kwargs)
-
- @property
- def comparator_factory(self):
- return type("TDComparator",
- (TypeDecorator.Comparator, self.impl.comparator_factory),
- {})
-
- def _gen_dialect_impl(self, dialect):
- """
- #todo
- """
- adapted = dialect.type_descriptor(self)
- if adapted is not self:
- return adapted
-
- # otherwise adapt the impl type, link
- # to a copy of this TypeDecorator and return
- # that.
- typedesc = self.load_dialect_impl(dialect).dialect_impl(dialect)
- tt = self.copy()
- if not isinstance(tt, self.__class__):
- raise AssertionError('Type object %s does not properly '
- 'implement the copy() method, it must '
- 'return an object of type %s' % (self,
- self.__class__))
- tt.impl = typedesc
- return tt
-
- @property
- def _type_affinity(self):
- """
- #todo
- """
- return self.impl._type_affinity
-
- def type_engine(self, dialect):
- """Return a dialect-specific :class:`.TypeEngine` instance
- for this :class:`.TypeDecorator`.
-
- In most cases this returns a dialect-adapted form of
- the :class:`.TypeEngine` type represented by ``self.impl``.
- Makes usage of :meth:`dialect_impl` but also traverses
- into wrapped :class:`.TypeDecorator` instances.
- Behavior can be customized here by overriding
- :meth:`load_dialect_impl`.
-
- """
- adapted = dialect.type_descriptor(self)
- if type(adapted) is not type(self):
- return adapted
- elif isinstance(self.impl, TypeDecorator):
- return self.impl.type_engine(dialect)
- else:
- return self.load_dialect_impl(dialect)
-
- def load_dialect_impl(self, dialect):
- """Return a :class:`.TypeEngine` object corresponding to a dialect.
-
- This is an end-user override hook that can be used to provide
- differing types depending on the given dialect. It is used
- by the :class:`.TypeDecorator` implementation of :meth:`type_engine`
- to help determine what type should ultimately be returned
- for a given :class:`.TypeDecorator`.
-
- By default returns ``self.impl``.
-
- """
- return self.impl
-
- def __getattr__(self, key):
- """Proxy all other undefined accessors to the underlying
- implementation."""
- return getattr(self.impl, key)
-
- def process_bind_param(self, value, dialect):
- """Receive a bound parameter value to be converted.
-
- Subclasses override this method to return the
- value that should be passed along to the underlying
- :class:`.TypeEngine` object, and from there to the
- DBAPI ``execute()`` method.
-
- The operation could be anything desired to perform custom
- behavior, such as transforming or serializing data.
- This could also be used as a hook for validating logic.
-
- This operation should be designed with the reverse operation
- in mind, which would be the process_result_value method of
- this class.
-
- :param value: Data to operate upon, of any type expected by
- this method in the subclass. Can be ``None``.
- :param dialect: the :class:`.Dialect` in use.
-
- """
-
- raise NotImplementedError()
-
- def process_result_value(self, value, dialect):
- """Receive a result-row column value to be converted.
-
- Subclasses should implement this method to operate on data
- fetched from the database.
-
- Subclasses override this method to return the
- value that should be passed back to the application,
- given a value that is already processed by
- the underlying :class:`.TypeEngine` object, originally
- from the DBAPI cursor method ``fetchone()`` or similar.
-
- The operation could be anything desired to perform custom
- behavior, such as transforming or serializing data.
- This could also be used as a hook for validating logic.
-
- :param value: Data to operate upon, of any type expected by
- this method in the subclass. Can be ``None``.
- :param dialect: the :class:`.Dialect` in use.
-
- This operation should be designed to be reversible by
- the "process_bind_param" method of this class.
-
- """
-
- raise NotImplementedError()
-
- @util.memoized_property
- def _has_bind_processor(self):
- """memoized boolean, check if process_bind_param is implemented.
-
- Allows the base process_bind_param to raise
- NotImplementedError without needing to test an expensive
- exception throw.
-
- """
-
- return self.__class__.process_bind_param.__code__ \
- is not TypeDecorator.process_bind_param.__code__
-
- def bind_processor(self, dialect):
- """Provide a bound value processing function for the
- given :class:`.Dialect`.
-
- This is the method that fulfills the :class:`.TypeEngine`
- contract for bound value conversion. :class:`.TypeDecorator`
- will wrap a user-defined implementation of
- :meth:`process_bind_param` here.
-
- User-defined code can override this method directly,
- though its likely best to use :meth:`process_bind_param` so that
- the processing provided by ``self.impl`` is maintained.
-
- :param dialect: Dialect instance in use.
-
- This method is the reverse counterpart to the
- :meth:`result_processor` method of this class.
-
- """
- if self._has_bind_processor:
- process_param = self.process_bind_param
- impl_processor = self.impl.bind_processor(dialect)
- if impl_processor:
- def process(value):
- return impl_processor(process_param(value, dialect))
-
- else:
- def process(value):
- return process_param(value, dialect)
-
- return process
- else:
- return self.impl.bind_processor(dialect)
-
- @util.memoized_property
- def _has_result_processor(self):
- """memoized boolean, check if process_result_value is implemented.
-
- Allows the base process_result_value to raise
- NotImplementedError without needing to test an expensive
- exception throw.
-
- """
- return self.__class__.process_result_value.__code__ \
- is not TypeDecorator.process_result_value.__code__
-
- def result_processor(self, dialect, coltype):
- """Provide a result value processing function for the given
- :class:`.Dialect`.
-
- This is the method that fulfills the :class:`.TypeEngine`
- contract for result value conversion. :class:`.TypeDecorator`
- will wrap a user-defined implementation of
- :meth:`process_result_value` here.
-
- User-defined code can override this method directly,
- though its likely best to use :meth:`process_result_value` so that
- the processing provided by ``self.impl`` is maintained.
-
- :param dialect: Dialect instance in use.
- :param coltype: An SQLAlchemy data type
-
- This method is the reverse counterpart to the
- :meth:`bind_processor` method of this class.
-
- """
- if self._has_result_processor:
- process_value = self.process_result_value
- impl_processor = self.impl.result_processor(dialect,
- coltype)
- if impl_processor:
- def process(value):
- return process_value(impl_processor(value), dialect)
-
- else:
- def process(value):
- return process_value(value, dialect)
-
- return process
- else:
- return self.impl.result_processor(dialect, coltype)
-
- def coerce_compared_value(self, op, value):
- """Suggest a type for a 'coerced' Python value in an expression.
-
- By default, returns self. This method is called by
- the expression system when an object using this type is
- on the left or right side of an expression against a plain Python
- object which does not yet have a SQLAlchemy type assigned::
-
- expr = table.c.somecolumn + 35
-
- Where above, if ``somecolumn`` uses this type, this method will
- be called with the value ``operator.add``
- and ``35``. The return value is whatever SQLAlchemy type should
- be used for ``35`` for this particular operation.
-
- """
- return self
-
- def copy(self):
- """Produce a copy of this :class:`.TypeDecorator` instance.
-
- This is a shallow copy and is provided to fulfill part of
- the :class:`.TypeEngine` contract. It usually does not
- need to be overridden unless the user-defined :class:`.TypeDecorator`
- has local state that should be deep-copied.
-
- """
-
- instance = self.__class__.__new__(self.__class__)
- instance.__dict__.update(self.__dict__)
- return instance
-
- def get_dbapi_type(self, dbapi):
- """Return the DBAPI type object represented by this
- :class:`.TypeDecorator`.
-
- By default this calls upon :meth:`.TypeEngine.get_dbapi_type` of the
- underlying "impl".
- """
- return self.impl.get_dbapi_type(dbapi)
-
- def compare_values(self, x, y):
- """Given two values, compare them for equality.
-
- By default this calls upon :meth:`.TypeEngine.compare_values`
- of the underlying "impl", which in turn usually
- uses the Python equals operator ``==``.
-
- This function is used by the ORM to compare
- an original-loaded value with an intercepted
- "changed" value, to determine if a net change
- has occurred.
-
- """
- return self.impl.compare_values(x, y)
-
- def __repr__(self):
- return util.generic_repr(self, to_inspect=self.impl)
-
-
-class Variant(TypeDecorator):
- """A wrapping type that selects among a variety of
- implementations based on dialect in use.
-
- The :class:`.Variant` type is typically constructed
- using the :meth:`.TypeEngine.with_variant` method.
-
- .. versionadded:: 0.7.2
-
- .. seealso:: :meth:`.TypeEngine.with_variant` for an example of use.
-
- """
-
- def __init__(self, base, mapping):
- """Construct a new :class:`.Variant`.
-
- :param base: the base 'fallback' type
- :param mapping: dictionary of string dialect names to
- :class:`.TypeEngine` instances.
-
- """
- self.impl = base
- self.mapping = mapping
-
- def load_dialect_impl(self, dialect):
- if dialect.name in self.mapping:
- return self.mapping[dialect.name]
- else:
- return self.impl
-
- def with_variant(self, type_, dialect_name):
- """Return a new :class:`.Variant` which adds the given
- type + dialect name to the mapping, in addition to the
- mapping present in this :class:`.Variant`.
-
- :param type_: a :class:`.TypeEngine` that will be selected
- as a variant from the originating type, when a dialect
- of the given name is in use.
- :param dialect_name: base name of the dialect which uses
- this type. (i.e. ``'postgresql'``, ``'mysql'``, etc.)
-
- """
-
- if dialect_name in self.mapping:
- raise exc.ArgumentError(
- "Dialect '%s' is already present in "
- "the mapping for this Variant" % dialect_name)
- mapping = self.mapping.copy()
- mapping[dialect_name] = type_
- return Variant(self.impl, mapping)
-
-
-def to_instance(typeobj, *arg, **kw):
- if typeobj is None:
- return NULLTYPE
-
- if util.callable(typeobj):
- return typeobj(*arg, **kw)
- else:
- return typeobj
-
-
-def adapt_type(typeobj, colspecs):
- if isinstance(typeobj, type):
- typeobj = typeobj()
- for t in typeobj.__class__.__mro__[0:-1]:
- try:
- impltype = colspecs[t]
- break
- except KeyError:
- pass
- else:
- # couldnt adapt - so just return the type itself
- # (it may be a user-defined type)
- return typeobj
- # if we adapted the given generic type to a database-specific type,
- # but it turns out the originally given "generic" type
- # is actually a subclass of our resulting type, then we were already
- # given a more specific type than that required; so use that.
- if (issubclass(typeobj.__class__, impltype)):
- return typeobj
- return typeobj.adapt(impltype)
-
-
-class NullType(TypeEngine):
- """An unknown type.
-
- :class:`.NullType` is used as a default type for those cases where
- a type cannot be determined, including:
-
- * During table reflection, when the type of a column is not recognized
- by the :class:`.Dialect`
- * When constructing SQL expressions using plain Python objects of
- unknown types (e.g. ``somecolumn == my_special_object``)
- * When a new :class:`.Column` is created, and the given type is passed
- as ``None`` or is not passed at all.
-
- The :class:`.NullType` can be used within SQL expression invocation
- without issue, it just has no behavior either at the expression construction
- level or at the bind-parameter/result processing level. :class:`.NullType`
- will result in a :class:`.CompileException` if the compiler is asked to render
- the type itself, such as if it is used in a :func:`.cast` operation
- or within a schema creation operation such as that invoked by
- :meth:`.MetaData.create_all` or the :class:`.CreateTable` construct.
-
- """
- __visit_name__ = 'null'
-
- class Comparator(TypeEngine.Comparator):
- def _adapt_expression(self, op, other_comparator):
- if isinstance(other_comparator, NullType.Comparator) or \
- not operators.is_commutative(op):
- return op, self.expr.type
- else:
- return other_comparator._adapt_expression(op, self)
- comparator_factory = Comparator
-
-NullTypeEngine = NullType
-
-
-class Concatenable(object):
- """A mixin that marks a type as supporting 'concatenation',
- typically strings."""
-
- class Comparator(TypeEngine.Comparator):
- def _adapt_expression(self, op, other_comparator):
- if op is operators.add and isinstance(other_comparator,
- (Concatenable.Comparator, NullType.Comparator)):
- return operators.concat_op, self.expr.type
- else:
- return op, self.expr.type
-
- comparator_factory = Comparator
-
-
-class _DateAffinity(object):
- """Mixin date/time specific expression adaptations.
-
- Rules are implemented within Date,Time,Interval,DateTime, Numeric,
- Integer. Based on http://www.postgresql.org/docs/current/static
- /functions-datetime.html.
-
- """
-
- @property
- def _expression_adaptations(self):
- raise NotImplementedError()
-
- class Comparator(TypeEngine.Comparator):
- _blank_dict = util.immutabledict()
-
- def _adapt_expression(self, op, other_comparator):
- othertype = other_comparator.type._type_affinity
- return op, \
- self.type._expression_adaptations.get(op, self._blank_dict).\
- get(othertype, NULLTYPE)
- comparator_factory = Comparator
-
-
-class String(Concatenable, TypeEngine):
- """The base for all string and character types.
-
- In SQL, corresponds to VARCHAR. Can also take Python unicode objects
- and encode to the database's encoding in bind params (and the reverse for
- result sets.)
-
- The `length` field is usually required when the `String` type is
- used within a CREATE TABLE statement, as VARCHAR requires a length
- on most databases.
-
- """
-
- __visit_name__ = 'string'
-
- def __init__(self, length=None, collation=None,
- convert_unicode=False,
- unicode_error=None,
- _warn_on_bytestring=False
- ):
- """
- Create a string-holding type.
-
- :param length: optional, a length for the column for use in
- DDL and CAST expressions. May be safely omitted if no ``CREATE
- TABLE`` will be issued. Certain databases may require a
- ``length`` for use in DDL, and will raise an exception when
- the ``CREATE TABLE`` DDL is issued if a ``VARCHAR``
- with no length is included. Whether the value is
- interpreted as bytes or characters is database specific.
-
- :param collation: Optional, a column-level collation for
- use in DDL and CAST expressions. Renders using the
- COLLATE keyword supported by SQLite, MySQL, and Postgresql.
- E.g.::
-
- >>> from sqlalchemy import cast, select, String
- >>> print select([cast('some string', String(collation='utf8'))])
- SELECT CAST(:param_1 AS VARCHAR COLLATE utf8) AS anon_1
-
- .. versionadded:: 0.8 Added support for COLLATE to all
- string types.
-
- :param convert_unicode: When set to ``True``, the
- :class:`.String` type will assume that
- input is to be passed as Python ``unicode`` objects,
- and results returned as Python ``unicode`` objects.
- If the DBAPI in use does not support Python unicode
- (which is fewer and fewer these days), SQLAlchemy
- will encode/decode the value, using the
- value of the ``encoding`` parameter passed to
- :func:`.create_engine` as the encoding.
-
- When using a DBAPI that natively supports Python
- unicode objects, this flag generally does not
- need to be set. For columns that are explicitly
- intended to store non-ASCII data, the :class:`.Unicode`
- or :class:`UnicodeText`
- types should be used regardless, which feature
- the same behavior of ``convert_unicode`` but
- also indicate an underlying column type that
- directly supports unicode, such as ``NVARCHAR``.
-
- For the extremely rare case that Python ``unicode``
- is to be encoded/decoded by SQLAlchemy on a backend
- that does natively support Python ``unicode``,
- the value ``force`` can be passed here which will
- cause SQLAlchemy's encode/decode services to be
- used unconditionally.
-
- :param unicode_error: Optional, a method to use to handle Unicode
- conversion errors. Behaves like the ``errors`` keyword argument to
- the standard library's ``string.decode()`` functions. This flag
- requires that ``convert_unicode`` is set to ``force`` - otherwise,
- SQLAlchemy is not guaranteed to handle the task of unicode
- conversion. Note that this flag adds significant performance
- overhead to row-fetching operations for backends that already
- return unicode objects natively (which most DBAPIs do). This
- flag should only be used as a last resort for reading
- strings from a column with varied or corrupted encodings.
-
- """
- if unicode_error is not None and convert_unicode != 'force':
- raise exc.ArgumentError("convert_unicode must be 'force' "
- "when unicode_error is set.")
-
- self.length = length
- self.collation = collation
- self.convert_unicode = convert_unicode
- self.unicode_error = unicode_error
- self._warn_on_bytestring = _warn_on_bytestring
-
- def bind_processor(self, dialect):
- if self.convert_unicode or dialect.convert_unicode:
- if dialect.supports_unicode_binds and \
- self.convert_unicode != 'force':
- if self._warn_on_bytestring:
- def process(value):
- if isinstance(value, util.binary_type):
- util.warn("Unicode type received non-unicode bind "
- "param value.")
- return value
- return process
- else:
- return None
- else:
- encoder = codecs.getencoder(dialect.encoding)
- warn_on_bytestring = self._warn_on_bytestring
-
- def process(value):
- if isinstance(value, util.text_type):
- return encoder(value, self.unicode_error)[0]
- elif warn_on_bytestring and value is not None:
- util.warn("Unicode type received non-unicode bind "
- "param value")
- return value
- return process
- else:
- return None
-
- def result_processor(self, dialect, coltype):
- wants_unicode = self.convert_unicode or dialect.convert_unicode
- needs_convert = wants_unicode and \
- (dialect.returns_unicode_strings is not True or
- self.convert_unicode == 'force')
-
- if needs_convert:
- to_unicode = processors.to_unicode_processor_factory(
- dialect.encoding, self.unicode_error)
-
- if dialect.returns_unicode_strings:
- # we wouldn't be here unless convert_unicode='force'
- # was specified, or the driver has erratic unicode-returning
- # habits. since we will be getting back unicode
- # in most cases, we check for it (decode will fail).
- def process(value):
- if isinstance(value, util.text_type):
- return value
- else:
- return to_unicode(value)
- return process
- else:
- # here, we assume that the object is not unicode,
- # avoiding expensive isinstance() check.
- return to_unicode
- else:
- return None
-
- @property
- def python_type(self):
- if self.convert_unicode:
- return util.text_type
- else:
- return str
-
- def get_dbapi_type(self, dbapi):
- return dbapi.STRING
-
-
-class Text(String):
- """A variably sized string type.
-
- In SQL, usually corresponds to CLOB or TEXT. Can also take Python
- unicode objects and encode to the database's encoding in bind
- params (and the reverse for result sets.) In general, TEXT objects
- do not have a length; while some databases will accept a length
- argument here, it will be rejected by others.
-
- """
- __visit_name__ = 'text'
-
-
-class Unicode(String):
- """A variable length Unicode string type.
-
- The :class:`.Unicode` type is a :class:`.String` subclass
- that assumes input and output as Python ``unicode`` data,
- and in that regard is equivalent to the usage of the
- ``convert_unicode`` flag with the :class:`.String` type.
- However, unlike plain :class:`.String`, it also implies an
- underlying column type that is explicitly supporting of non-ASCII
- data, such as ``NVARCHAR`` on Oracle and SQL Server.
- This can impact the output of ``CREATE TABLE`` statements
- and ``CAST`` functions at the dialect level, and can
- also affect the handling of bound parameters in some
- specific DBAPI scenarios.
-
- The encoding used by the :class:`.Unicode` type is usually
- determined by the DBAPI itself; most modern DBAPIs
- feature support for Python ``unicode`` objects as bound
- values and result set values, and the encoding should
- be configured as detailed in the notes for the target
- DBAPI in the :ref:`dialect_toplevel` section.
-
- For those DBAPIs which do not support, or are not configured
- to accommodate Python ``unicode`` objects
- directly, SQLAlchemy does the encoding and decoding
- outside of the DBAPI. The encoding in this scenario
- is determined by the ``encoding`` flag passed to
- :func:`.create_engine`.
-
- When using the :class:`.Unicode` type, it is only appropriate
- to pass Python ``unicode`` objects, and not plain ``str``.
- If a plain ``str`` is passed under Python 2, a warning
- is emitted. If you notice your application emitting these warnings but
- you're not sure of the source of them, the Python
- ``warnings`` filter, documented at
- http://docs.python.org/library/warnings.html,
- can be used to turn these warnings into exceptions
- which will illustrate a stack trace::
-
- import warnings
- warnings.simplefilter('error')
-
- For an application that wishes to pass plain bytestrings
- and Python ``unicode`` objects to the ``Unicode`` type
- equally, the bytestrings must first be decoded into
- unicode. The recipe at :ref:`coerce_to_unicode` illustrates
- how this is done.
-
- See also:
-
- :class:`.UnicodeText` - unlengthed textual counterpart
- to :class:`.Unicode`.
-
- """
-
- __visit_name__ = 'unicode'
-
- def __init__(self, length=None, **kwargs):
- """
- Create a :class:`.Unicode` object.
-
- Parameters are the same as that of :class:`.String`,
- with the exception that ``convert_unicode``
- defaults to ``True``.
-
- """
- kwargs.setdefault('convert_unicode', True)
- kwargs.setdefault('_warn_on_bytestring', True)
- super(Unicode, self).__init__(length=length, **kwargs)
-
-
-class UnicodeText(Text):
- """An unbounded-length Unicode string type.
-
- See :class:`.Unicode` for details on the unicode
- behavior of this object.
-
- Like :class:`.Unicode`, usage the :class:`.UnicodeText` type implies a
- unicode-capable type being used on the backend, such as
- ``NCLOB``, ``NTEXT``.
-
- """
-
- __visit_name__ = 'unicode_text'
-
- def __init__(self, length=None, **kwargs):
- """
- Create a Unicode-converting Text type.
-
- Parameters are the same as that of :class:`.Text`,
- with the exception that ``convert_unicode``
- defaults to ``True``.
-
- """
- kwargs.setdefault('convert_unicode', True)
- kwargs.setdefault('_warn_on_bytestring', True)
- super(UnicodeText, self).__init__(length=length, **kwargs)
-
-
-class Integer(_DateAffinity, TypeEngine):
- """A type for ``int`` integers."""
-
- __visit_name__ = 'integer'
-
- def get_dbapi_type(self, dbapi):
- return dbapi.NUMBER
-
- @property
- def python_type(self):
- return int
-
- @util.memoized_property
- def _expression_adaptations(self):
- # TODO: need a dictionary object that will
- # handle operators generically here, this is incomplete
- return {
- operators.add: {
- Date: Date,
- Integer: self.__class__,
- Numeric: Numeric,
- },
- operators.mul: {
- Interval: Interval,
- Integer: self.__class__,
- Numeric: Numeric,
- },
- operators.div: {
- Integer: self.__class__,
- Numeric: Numeric,
- },
- operators.truediv: {
- Integer: self.__class__,
- Numeric: Numeric,
- },
- operators.sub: {
- Integer: self.__class__,
- Numeric: Numeric,
- },
- }
-
-
-class SmallInteger(Integer):
- """A type for smaller ``int`` integers.
-
- Typically generates a ``SMALLINT`` in DDL, and otherwise acts like
- a normal :class:`.Integer` on the Python side.
-
- """
-
- __visit_name__ = 'small_integer'
-
-
-class BigInteger(Integer):
- """A type for bigger ``int`` integers.
-
- Typically generates a ``BIGINT`` in DDL, and otherwise acts like
- a normal :class:`.Integer` on the Python side.
-
- """
-
- __visit_name__ = 'big_integer'
-
-
-class Numeric(_DateAffinity, TypeEngine):
- """A type for fixed precision numbers.
-
- Typically generates DECIMAL or NUMERIC. Returns
- ``decimal.Decimal`` objects by default, applying
- conversion as needed.
-
- .. note::
-
- The `cdecimal <http://pypi.python.org/pypi/cdecimal/>`_ library
- is a high performing alternative to Python's built-in
- ``decimal.Decimal`` type, which performs very poorly in high volume
- situations. SQLAlchemy 0.7 is tested against ``cdecimal`` and supports
- it fully. The type is not necessarily supported by DBAPI
- implementations however, most of which contain an import for plain
- ``decimal`` in their source code, even though some such as psycopg2
- provide hooks for alternate adapters. SQLAlchemy imports ``decimal``
- globally as well. The most straightforward and
- foolproof way to use "cdecimal" given current DBAPI and Python support
- is to patch it directly into sys.modules before anything else is
- imported::
-
- import sys
- import cdecimal
- sys.modules["decimal"] = cdecimal
-
- While the global patch is a little ugly, it's particularly
- important to use just one decimal library at a time since
- Python Decimal and cdecimal Decimal objects
- are not currently compatible *with each other*::
-
- >>> import cdecimal
- >>> import decimal
- >>> decimal.Decimal("10") == cdecimal.Decimal("10")
- False
-
- SQLAlchemy will provide more natural support of
- cdecimal if and when it becomes a standard part of Python
- installations and is supported by all DBAPIs.
-
- """
-
- __visit_name__ = 'numeric'
-
- def __init__(self, precision=None, scale=None, asdecimal=True):
- """
- Construct a Numeric.
-
- :param precision: the numeric precision for use in DDL ``CREATE
- TABLE``.
-
- :param scale: the numeric scale for use in DDL ``CREATE TABLE``.
-
- :param asdecimal: default True. Return whether or not
- values should be sent as Python Decimal objects, or
- as floats. Different DBAPIs send one or the other based on
- datatypes - the Numeric type will ensure that return values
- are one or the other across DBAPIs consistently.
-
- When using the ``Numeric`` type, care should be taken to ensure
- that the asdecimal setting is apppropriate for the DBAPI in use -
- when Numeric applies a conversion from Decimal->float or float->
- Decimal, this conversion incurs an additional performance overhead
- for all result columns received.
-
- DBAPIs that return Decimal natively (e.g. psycopg2) will have
- better accuracy and higher performance with a setting of ``True``,
- as the native translation to Decimal reduces the amount of floating-
- point issues at play, and the Numeric type itself doesn't need
- to apply any further conversions. However, another DBAPI which
- returns floats natively *will* incur an additional conversion
- overhead, and is still subject to floating point data loss - in
- which case ``asdecimal=False`` will at least remove the extra
- conversion overhead.
-
- """
- self.precision = precision
- self.scale = scale
- self.asdecimal = asdecimal
-
- def get_dbapi_type(self, dbapi):
- return dbapi.NUMBER
-
- @property
- def python_type(self):
- if self.asdecimal:
- return decimal.Decimal
- else:
- return float
-
- def bind_processor(self, dialect):
- if dialect.supports_native_decimal:
- return None
- else:
- return processors.to_float
-
- def result_processor(self, dialect, coltype):
- if self.asdecimal:
- if dialect.supports_native_decimal:
- # we're a "numeric", DBAPI will give us Decimal directly
- return None
- else:
- util.warn('Dialect %s+%s does *not* support Decimal '
- 'objects natively, and SQLAlchemy must '
- 'convert from floating point - rounding '
- 'errors and other issues may occur. Please '
- 'consider storing Decimal numbers as strings '
- 'or integers on this platform for lossless '
- 'storage.' % (dialect.name, dialect.driver))
-
- # we're a "numeric", DBAPI returns floats, convert.
- if self.scale is not None:
- return processors.to_decimal_processor_factory(
- decimal.Decimal, self.scale)
- else:
- return processors.to_decimal_processor_factory(
- decimal.Decimal)
- else:
- if dialect.supports_native_decimal:
- return processors.to_float
- else:
- return None
-
- @util.memoized_property
- def _expression_adaptations(self):
- return {
- operators.mul: {
- Interval: Interval,
- Numeric: self.__class__,
- Integer: self.__class__,
- },
- operators.div: {
- Numeric: self.__class__,
- Integer: self.__class__,
- },
- operators.truediv: {
- Numeric: self.__class__,
- Integer: self.__class__,
- },
- operators.add: {
- Numeric: self.__class__,
- Integer: self.__class__,
- },
- operators.sub: {
- Numeric: self.__class__,
- Integer: self.__class__,
- }
- }
-
-
-class Float(Numeric):
- """A type for ``float`` numbers.
-
- Returns Python ``float`` objects by default, applying
- conversion as needed.
-
- """
-
- __visit_name__ = 'float'
-
- scale = None
-
- def __init__(self, precision=None, asdecimal=False, **kwargs):
- """
- Construct a Float.
-
- :param precision: the numeric precision for use in DDL ``CREATE
- TABLE``.
-
- :param asdecimal: the same flag as that of :class:`.Numeric`, but
- defaults to ``False``. Note that setting this flag to ``True``
- results in floating point conversion.
-
- :param \**kwargs: deprecated. Additional arguments here are ignored
- by the default :class:`.Float` type. For database specific
- floats that support additional arguments, see that dialect's
- documentation for details, such as
- :class:`sqlalchemy.dialects.mysql.FLOAT`.
-
- """
- self.precision = precision
- self.asdecimal = asdecimal
- if kwargs:
- util.warn_deprecated("Additional keyword arguments "
- "passed to Float ignored.")
-
- def result_processor(self, dialect, coltype):
- if self.asdecimal:
- return processors.to_decimal_processor_factory(decimal.Decimal)
- else:
- return None
-
- @util.memoized_property
- def _expression_adaptations(self):
- return {
- operators.mul: {
- Interval: Interval,
- Numeric: self.__class__,
- },
- operators.div: {
- Numeric: self.__class__,
- },
- operators.truediv: {
- Numeric: self.__class__,
- },
- operators.add: {
- Numeric: self.__class__,
- },
- operators.sub: {
- Numeric: self.__class__,
- }
- }
-
-
-class DateTime(_DateAffinity, TypeEngine):
- """A type for ``datetime.datetime()`` objects.
-
- Date and time types return objects from the Python ``datetime``
- module. Most DBAPIs have built in support for the datetime
- module, with the noted exception of SQLite. In the case of
- SQLite, date and time types are stored as strings which are then
- converted back to datetime objects when rows are returned.
-
- """
-
- __visit_name__ = 'datetime'
-
- def __init__(self, timezone=False):
- """Construct a new :class:`.DateTime`.
-
- :param timezone: boolean. If True, and supported by the
- backend, will produce 'TIMESTAMP WITH TIMEZONE'. For backends
- that don't support timezone aware timestamps, has no
- effect.
-
- """
- self.timezone = timezone
-
- def get_dbapi_type(self, dbapi):
- return dbapi.DATETIME
-
- @property
- def python_type(self):
- return dt.datetime
-
- @util.memoized_property
- def _expression_adaptations(self):
- return {
- operators.add: {
- Interval: self.__class__,
- },
- operators.sub: {
- Interval: self.__class__,
- DateTime: Interval,
- },
- }
-
-
-class Date(_DateAffinity, TypeEngine):
- """A type for ``datetime.date()`` objects."""
-
- __visit_name__ = 'date'
-
- def get_dbapi_type(self, dbapi):
- return dbapi.DATETIME
-
- @property
- def python_type(self):
- return dt.date
-
- @util.memoized_property
- def _expression_adaptations(self):
- return {
- operators.add: {
- Integer: self.__class__,
- Interval: DateTime,
- Time: DateTime,
- },
- operators.sub: {
- # date - integer = date
- Integer: self.__class__,
-
- # date - date = integer.
- Date: Integer,
-
- Interval: DateTime,
-
- # date - datetime = interval,
- # this one is not in the PG docs
- # but works
- DateTime: Interval,
- },
- }
-
-
-class Time(_DateAffinity, TypeEngine):
- """A type for ``datetime.time()`` objects."""
-
- __visit_name__ = 'time'
-
- def __init__(self, timezone=False):
- self.timezone = timezone
-
- def get_dbapi_type(self, dbapi):
- return dbapi.DATETIME
-
- @property
- def python_type(self):
- return dt.time
-
- @util.memoized_property
- def _expression_adaptations(self):
- return {
- operators.add: {
- Date: DateTime,
- Interval: self.__class__
- },
- operators.sub: {
- Time: Interval,
- Interval: self.__class__,
- },
- }
-
-
-class _Binary(TypeEngine):
- """Define base behavior for binary types."""
-
- def __init__(self, length=None):
- self.length = length
-
- @property
- def python_type(self):
- return util.binary_type
-
- # Python 3 - sqlite3 doesn't need the `Binary` conversion
- # here, though pg8000 does to indicate "bytea"
- def bind_processor(self, dialect):
- DBAPIBinary = dialect.dbapi.Binary
-
- def process(value):
- x = self
- if value is not None:
- return DBAPIBinary(value)
- else:
- return None
- return process
-
- # Python 3 has native bytes() type
- # both sqlite3 and pg8000 seem to return it,
- # psycopg2 as of 2.5 returns 'memoryview'
- if util.py2k:
- def result_processor(self, dialect, coltype):
- if util.jython:
- def process(value):
- if value is not None:
- if isinstance(value, array.array):
- return value.tostring()
- return str(value)
- else:
- return None
- else:
- process = processors.to_str
- return process
- else:
- def result_processor(self, dialect, coltype):
- def process(value):
- if value is not None:
- value = bytes(value)
- return value
- return process
-
- def coerce_compared_value(self, op, value):
- """See :meth:`.TypeEngine.coerce_compared_value` for a description."""
-
- if isinstance(value, util.string_types):
- return self
- else:
- return super(_Binary, self).coerce_compared_value(op, value)
-
- def get_dbapi_type(self, dbapi):
- return dbapi.BINARY
-
-
-class LargeBinary(_Binary):
- """A type for large binary byte data.
-
- The Binary type generates BLOB or BYTEA when tables are created,
- and also converts incoming values using the ``Binary`` callable
- provided by each DB-API.
-
- """
-
- __visit_name__ = 'large_binary'
-
- def __init__(self, length=None):
- """
- Construct a LargeBinary type.
-
- :param length: optional, a length for the column for use in
- DDL statements, for those BLOB types that accept a length
- (i.e. MySQL). It does *not* produce a small BINARY/VARBINARY
- type - use the BINARY/VARBINARY types specifically for those.
- May be safely omitted if no ``CREATE
- TABLE`` will be issued. Certain databases may require a
- *length* for use in DDL, and will raise an exception when
- the ``CREATE TABLE`` DDL is issued.
-
- """
- _Binary.__init__(self, length=length)
-
-
-class Binary(LargeBinary):
- """Deprecated. Renamed to LargeBinary."""
-
- def __init__(self, *arg, **kw):
- util.warn_deprecated('The Binary type has been renamed to '
- 'LargeBinary.')
- LargeBinary.__init__(self, *arg, **kw)
-
-
-class SchemaType(events.SchemaEventTarget):
- """Mark a type as possibly requiring schema-level DDL for usage.
-
- Supports types that must be explicitly created/dropped (i.e. PG ENUM type)
- as well as types that are complimented by table or schema level
- constraints, triggers, and other rules.
-
- :class:`.SchemaType` classes can also be targets for the
- :meth:`.DDLEvents.before_parent_attach` and
- :meth:`.DDLEvents.after_parent_attach` events, where the events fire off
- surrounding the association of the type object with a parent
- :class:`.Column`.
-
- .. seealso::
-
- :class:`.Enum`
-
- :class:`.Boolean`
-
-
- """
-
- def __init__(self, **kw):
- self.name = kw.pop('name', None)
- self.quote = kw.pop('quote', None)
- self.schema = kw.pop('schema', None)
- self.metadata = kw.pop('metadata', None)
- self.inherit_schema = kw.pop('inherit_schema', False)
- if self.metadata:
- event.listen(
- self.metadata,
- "before_create",
- util.portable_instancemethod(self._on_metadata_create)
- )
- event.listen(
- self.metadata,
- "after_drop",
- util.portable_instancemethod(self._on_metadata_drop)
- )
-
- def _set_parent(self, column):
- column._on_table_attach(util.portable_instancemethod(self._set_table))
-
- def _set_table(self, column, table):
- if self.inherit_schema:
- self.schema = table.schema
-
- event.listen(
- table,
- "before_create",
- util.portable_instancemethod(
- self._on_table_create)
- )
- event.listen(
- table,
- "after_drop",
- util.portable_instancemethod(self._on_table_drop)
- )
- if self.metadata is None:
- # TODO: what's the difference between self.metadata
- # and table.metadata here ?
- event.listen(
- table.metadata,
- "before_create",
- util.portable_instancemethod(self._on_metadata_create)
- )
- event.listen(
- table.metadata,
- "after_drop",
- util.portable_instancemethod(self._on_metadata_drop)
- )
-
- def copy(self, **kw):
- return self.adapt(self.__class__)
-
- def adapt(self, impltype, **kw):
- schema = kw.pop('schema', self.schema)
- metadata = kw.pop('metadata', self.metadata)
- return impltype(name=self.name,
- quote=self.quote,
- schema=schema,
- metadata=metadata,
- inherit_schema=self.inherit_schema,
- **kw
- )
-
- @property
- def bind(self):
- return self.metadata and self.metadata.bind or None
-
- def create(self, bind=None, checkfirst=False):
- """Issue CREATE ddl for this type, if applicable."""
-
- if bind is None:
- bind = schema._bind_or_error(self)
- t = self.dialect_impl(bind.dialect)
- if t.__class__ is not self.__class__ and isinstance(t, SchemaType):
- t.create(bind=bind, checkfirst=checkfirst)
-
- def drop(self, bind=None, checkfirst=False):
- """Issue DROP ddl for this type, if applicable."""
-
- if bind is None:
- bind = schema._bind_or_error(self)
- t = self.dialect_impl(bind.dialect)
- if t.__class__ is not self.__class__ and isinstance(t, SchemaType):
- t.drop(bind=bind, checkfirst=checkfirst)
-
- def _on_table_create(self, target, bind, **kw):
- t = self.dialect_impl(bind.dialect)
- if t.__class__ is not self.__class__ and isinstance(t, SchemaType):
- t._on_table_create(target, bind, **kw)
-
- def _on_table_drop(self, target, bind, **kw):
- t = self.dialect_impl(bind.dialect)
- if t.__class__ is not self.__class__ and isinstance(t, SchemaType):
- t._on_table_drop(target, bind, **kw)
-
- def _on_metadata_create(self, target, bind, **kw):
- t = self.dialect_impl(bind.dialect)
- if t.__class__ is not self.__class__ and isinstance(t, SchemaType):
- t._on_metadata_create(target, bind, **kw)
-
- def _on_metadata_drop(self, target, bind, **kw):
- t = self.dialect_impl(bind.dialect)
- if t.__class__ is not self.__class__ and isinstance(t, SchemaType):
- t._on_metadata_drop(target, bind, **kw)
-
-
-class Enum(String, SchemaType):
- """Generic Enum Type.
-
- The Enum type provides a set of possible string values which the
- column is constrained towards.
-
- By default, uses the backend's native ENUM type if available,
- else uses VARCHAR + a CHECK constraint.
-
- .. seealso::
-
- :class:`~.postgresql.ENUM` - PostgreSQL-specific type,
- which has additional functionality.
-
- """
-
- __visit_name__ = 'enum'
-
- def __init__(self, *enums, **kw):
- """Construct an enum.
-
- Keyword arguments which don't apply to a specific backend are ignored
- by that backend.
-
- :param \*enums: string or unicode enumeration labels. If unicode
- labels are present, the `convert_unicode` flag is auto-enabled.
-
- :param convert_unicode: Enable unicode-aware bind parameter and
- result-set processing for this Enum's data. This is set
- automatically based on the presence of unicode label strings.
-
- :param metadata: Associate this type directly with a ``MetaData``
- object. For types that exist on the target database as an
- independent schema construct (Postgresql), this type will be
- created and dropped within ``create_all()`` and ``drop_all()``
- operations. If the type is not associated with any ``MetaData``
- object, it will associate itself with each ``Table`` in which it is
- used, and will be created when any of those individual tables are
- created, after a check is performed for it's existence. The type is
- only dropped when ``drop_all()`` is called for that ``Table``
- object's metadata, however.
-
- :param name: The name of this type. This is required for Postgresql
- and any future supported database which requires an explicitly
- named type, or an explicitly named constraint in order to generate
- the type and/or a table that uses it.
-
- :param native_enum: Use the database's native ENUM type when
- available. Defaults to True. When False, uses VARCHAR + check
- constraint for all backends.
-
- :param schema: Schema name of this type. For types that exist on the
- target database as an independent schema construct (Postgresql),
- this parameter specifies the named schema in which the type is
- present.
-
- .. note::
-
- The ``schema`` of the :class:`.Enum` type does not
- by default make use of the ``schema`` established on the
- owning :class:`.Table`. If this behavior is desired,
- set the ``inherit_schema`` flag to ``True``.
-
- :param quote: Force quoting to be on or off on the type's name. If
- left as the default of `None`, the usual schema-level "case
- sensitive"/"reserved name" rules are used to determine if this
- type's name should be quoted.
-
- :param inherit_schema: When ``True``, the "schema" from the owning
- :class:`.Table` will be copied to the "schema" attribute of this
- :class:`.Enum`, replacing whatever value was passed for the
- ``schema`` attribute. This also takes effect when using the
- :meth:`.Table.tometadata` operation.
-
- .. versionadded:: 0.8
-
- """
- self.enums = enums
- self.native_enum = kw.pop('native_enum', True)
- convert_unicode = kw.pop('convert_unicode', None)
- if convert_unicode is None:
- for e in enums:
- if isinstance(e, util.text_type):
- convert_unicode = True
- break
- else:
- convert_unicode = False
-
- if self.enums:
- length = max(len(x) for x in self.enums)
- else:
- length = 0
- String.__init__(self,
- length=length,
- convert_unicode=convert_unicode,
- )
- SchemaType.__init__(self, **kw)
-
- def __repr__(self):
- return util.generic_repr(self, [
- ("native_enum", True),
- ("name", None)
- ])
-
- def _should_create_constraint(self, compiler):
- return not self.native_enum or \
- not compiler.dialect.supports_native_enum
-
- def _set_table(self, column, table):
- if self.native_enum:
- SchemaType._set_table(self, column, table)
-
- e = schema.CheckConstraint(
- column.in_(self.enums),
- name=self.name,
- _create_rule=util.portable_instancemethod(
- self._should_create_constraint)
- )
- table.append_constraint(e)
-
- def adapt(self, impltype, **kw):
- schema = kw.pop('schema', self.schema)
- metadata = kw.pop('metadata', self.metadata)
- if issubclass(impltype, Enum):
- return impltype(name=self.name,
- quote=self.quote,
- schema=schema,
- metadata=metadata,
- convert_unicode=self.convert_unicode,
- native_enum=self.native_enum,
- inherit_schema=self.inherit_schema,
- *self.enums,
- **kw
- )
- else:
- return super(Enum, self).adapt(impltype, **kw)
-
-
-class PickleType(TypeDecorator):
- """Holds Python objects, which are serialized using pickle.
-
- PickleType builds upon the Binary type to apply Python's
- ``pickle.dumps()`` to incoming objects, and ``pickle.loads()`` on
- the way out, allowing any pickleable Python object to be stored as
- a serialized binary field.
-
- To allow ORM change events to propagate for elements associated
- with :class:`.PickleType`, see :ref:`mutable_toplevel`.
-
- """
-
- impl = LargeBinary
-
- def __init__(self, protocol=pickle.HIGHEST_PROTOCOL,
- pickler=None, comparator=None):
- """
- Construct a PickleType.
-
- :param protocol: defaults to ``pickle.HIGHEST_PROTOCOL``.
-
- :param pickler: defaults to cPickle.pickle or pickle.pickle if
- cPickle is not available. May be any object with
- pickle-compatible ``dumps` and ``loads`` methods.
-
- :param comparator: a 2-arg callable predicate used
- to compare values of this type. If left as ``None``,
- the Python "equals" operator is used to compare values.
-
- """
- self.protocol = protocol
- self.pickler = pickler or pickle
- self.comparator = comparator
- super(PickleType, self).__init__()
-
- def __reduce__(self):
- return PickleType, (self.protocol,
- None,
- self.comparator)
-
- def bind_processor(self, dialect):
- impl_processor = self.impl.bind_processor(dialect)
- dumps = self.pickler.dumps
- protocol = self.protocol
- if impl_processor:
- def process(value):
- if value is not None:
- value = dumps(value, protocol)
- return impl_processor(value)
- else:
- def process(value):
- if value is not None:
- value = dumps(value, protocol)
- return value
- return process
-
- def result_processor(self, dialect, coltype):
- impl_processor = self.impl.result_processor(dialect, coltype)
- loads = self.pickler.loads
- if impl_processor:
- def process(value):
- value = impl_processor(value)
- if value is None:
- return None
- return loads(value)
- else:
- def process(value):
- if value is None:
- return None
- return loads(value)
- return process
-
- def compare_values(self, x, y):
- if self.comparator:
- return self.comparator(x, y)
- else:
- return x == y
-
-
-class Boolean(TypeEngine, SchemaType):
- """A bool datatype.
-
- Boolean typically uses BOOLEAN or SMALLINT on the DDL side, and on
- the Python side deals in ``True`` or ``False``.
-
- """
-
- __visit_name__ = 'boolean'
-
- def __init__(self, create_constraint=True, name=None):
- """Construct a Boolean.
-
- :param create_constraint: defaults to True. If the boolean
- is generated as an int/smallint, also create a CHECK constraint
- on the table that ensures 1 or 0 as a value.
-
- :param name: if a CHECK constraint is generated, specify
- the name of the constraint.
-
- """
- self.create_constraint = create_constraint
- self.name = name
-
- def _should_create_constraint(self, compiler):
- return not compiler.dialect.supports_native_boolean
-
- def _set_table(self, column, table):
- if not self.create_constraint:
- return
-
- e = schema.CheckConstraint(
- column.in_([0, 1]),
- name=self.name,
- _create_rule=util.portable_instancemethod(
- self._should_create_constraint)
- )
- table.append_constraint(e)
-
- @property
- def python_type(self):
- return bool
-
- def bind_processor(self, dialect):
- if dialect.supports_native_boolean:
- return None
- else:
- return processors.boolean_to_int
-
- def result_processor(self, dialect, coltype):
- if dialect.supports_native_boolean:
- return None
- else:
- return processors.int_to_boolean
-
-
-class Interval(_DateAffinity, TypeDecorator):
- """A type for ``datetime.timedelta()`` objects.
-
- The Interval type deals with ``datetime.timedelta`` objects. In
- PostgreSQL, the native ``INTERVAL`` type is used; for others, the
- value is stored as a date which is relative to the "epoch"
- (Jan. 1, 1970).
-
- Note that the ``Interval`` type does not currently provide date arithmetic
- operations on platforms which do not support interval types natively. Such
- operations usually require transformation of both sides of the expression
- (such as, conversion of both sides into integer epoch values first) which
- currently is a manual procedure (such as via
- :attr:`~sqlalchemy.sql.expression.func`).
-
- """
-
- impl = DateTime
- epoch = dt.datetime.utcfromtimestamp(0)
-
- def __init__(self, native=True,
- second_precision=None,
- day_precision=None):
- """Construct an Interval object.
-
- :param native: when True, use the actual
- INTERVAL type provided by the database, if
- supported (currently Postgresql, Oracle).
- Otherwise, represent the interval data as
- an epoch value regardless.
-
- :param second_precision: For native interval types
- which support a "fractional seconds precision" parameter,
- i.e. Oracle and Postgresql
-
- :param day_precision: for native interval types which
- support a "day precision" parameter, i.e. Oracle.
-
- """
- super(Interval, self).__init__()
- self.native = native
- self.second_precision = second_precision
- self.day_precision = day_precision
-
- def adapt(self, cls, **kw):
- if self.native and hasattr(cls, '_adapt_from_generic_interval'):
- return cls._adapt_from_generic_interval(self, **kw)
- else:
- return self.__class__(
- native=self.native,
- second_precision=self.second_precision,
- day_precision=self.day_precision,
- **kw)
-
- @property
- def python_type(self):
- return dt.timedelta
-
- def bind_processor(self, dialect):
- impl_processor = self.impl.bind_processor(dialect)
- epoch = self.epoch
- if impl_processor:
- def process(value):
- if value is not None:
- value = epoch + value
- return impl_processor(value)
- else:
- def process(value):
- if value is not None:
- value = epoch + value
- return value
- return process
-
- def result_processor(self, dialect, coltype):
- impl_processor = self.impl.result_processor(dialect, coltype)
- epoch = self.epoch
- if impl_processor:
- def process(value):
- value = impl_processor(value)
- if value is None:
- return None
- return value - epoch
- else:
- def process(value):
- if value is None:
- return None
- return value - epoch
- return process
-
- @util.memoized_property
- def _expression_adaptations(self):
- return {
- operators.add: {
- Date: DateTime,
- Interval: self.__class__,
- DateTime: DateTime,
- Time: Time,
- },
- operators.sub: {
- Interval: self.__class__
- },
- operators.mul: {
- Numeric: self.__class__
- },
- operators.truediv: {
- Numeric: self.__class__
- },
- operators.div: {
- Numeric: self.__class__
- }
- }
-
- @property
- def _type_affinity(self):
- return Interval
-
- def coerce_compared_value(self, op, value):
- """See :meth:`.TypeEngine.coerce_compared_value` for a description."""
-
- return self.impl.coerce_compared_value(op, value)
-
-
-class REAL(Float):
- """The SQL REAL type."""
-
- __visit_name__ = 'REAL'
-
-
-class FLOAT(Float):
- """The SQL FLOAT type."""
-
- __visit_name__ = 'FLOAT'
-
-
-class NUMERIC(Numeric):
- """The SQL NUMERIC type."""
-
- __visit_name__ = 'NUMERIC'
-
-
-class DECIMAL(Numeric):
- """The SQL DECIMAL type."""
-
- __visit_name__ = 'DECIMAL'
-
-
-class INTEGER(Integer):
- """The SQL INT or INTEGER type."""
-
- __visit_name__ = 'INTEGER'
-INT = INTEGER
-
-
-class SMALLINT(SmallInteger):
- """The SQL SMALLINT type."""
-
- __visit_name__ = 'SMALLINT'
-
-
-class BIGINT(BigInteger):
- """The SQL BIGINT type."""
-
- __visit_name__ = 'BIGINT'
-
-
-class TIMESTAMP(DateTime):
- """The SQL TIMESTAMP type."""
-
- __visit_name__ = 'TIMESTAMP'
-
- def get_dbapi_type(self, dbapi):
- return dbapi.TIMESTAMP
-
-
-class DATETIME(DateTime):
- """The SQL DATETIME type."""
-
- __visit_name__ = 'DATETIME'
-
-
-class DATE(Date):
- """The SQL DATE type."""
-
- __visit_name__ = 'DATE'
-
-
-class TIME(Time):
- """The SQL TIME type."""
-
- __visit_name__ = 'TIME'
-
-
-class TEXT(Text):
- """The SQL TEXT type."""
-
- __visit_name__ = 'TEXT'
-
-
-class CLOB(Text):
- """The CLOB type.
-
- This type is found in Oracle and Informix.
- """
-
- __visit_name__ = 'CLOB'
-
-
-class VARCHAR(String):
- """The SQL VARCHAR type."""
-
- __visit_name__ = 'VARCHAR'
-
-
-class NVARCHAR(Unicode):
- """The SQL NVARCHAR type."""
-
- __visit_name__ = 'NVARCHAR'
-
-
-class CHAR(String):
- """The SQL CHAR type."""
-
- __visit_name__ = 'CHAR'
-
-
-class NCHAR(Unicode):
- """The SQL NCHAR type."""
-
- __visit_name__ = 'NCHAR'
-
-
-class BLOB(LargeBinary):
- """The SQL BLOB type."""
-
- __visit_name__ = 'BLOB'
-
-
-class BINARY(_Binary):
- """The SQL BINARY type."""
-
- __visit_name__ = 'BINARY'
-
-
-class VARBINARY(_Binary):
- """The SQL VARBINARY type."""
-
- __visit_name__ = 'VARBINARY'
-
-
-class BOOLEAN(Boolean):
- """The SQL BOOLEAN type."""
-
- __visit_name__ = 'BOOLEAN'
-
-NULLTYPE = NullType()
-BOOLEANTYPE = Boolean()
-STRINGTYPE = String()
-
-_type_map = {
- int: Integer(),
- float: Numeric(),
- bool: BOOLEANTYPE,
- decimal.Decimal: Numeric(),
- dt.date: Date(),
- dt.datetime: DateTime(),
- dt.time: Time(),
- dt.timedelta: Interval(),
- NoneType: NULLTYPE
-}
-
-if util.py3k:
- _type_map[bytes] = LargeBinary()
- _type_map[str] = Unicode()
-else:
- _type_map[unicode] = Unicode()
- _type_map[str] = String()
-
+from .sql.type_api import (
+ adapt_type,
+ TypeEngine,
+ TypeDecorator,
+ Variant,
+ to_instance,
+ UserDefinedType
+)
+from .sql.sqltypes import (
+ BIGINT,
+ BINARY,
+ BLOB,
+ BOOLEAN,
+ BigInteger,
+ Binary,
+ _Binary,
+ Boolean,
+ CHAR,
+ CLOB,
+ Concatenable,
+ DATE,
+ DATETIME,
+ DECIMAL,
+ Date,
+ DateTime,
+ Enum,
+ FLOAT,
+ Float,
+ INT,
+ INTEGER,
+ Integer,
+ Interval,
+ LargeBinary,
+ NCHAR,
+ NVARCHAR,
+ NullType,
+ NULLTYPE,
+ NUMERIC,
+ Numeric,
+ PickleType,
+ REAL,
+ SchemaType,
+ SMALLINT,
+ SmallInteger,
+ String,
+ TEXT,
+ TIME,
+ TIMESTAMP,
+ Text,
+ Time,
+ Unicode,
+ UnicodeText,
+ VARBINARY,
+ VARCHAR,
+ _type_map
+ )
diff --git a/lib/sqlalchemy/util/__init__.py b/lib/sqlalchemy/util/__init__.py
index 104566215..2237e1f13 100644
--- a/lib/sqlalchemy/util/__init__.py
+++ b/lib/sqlalchemy/util/__init__.py
@@ -25,7 +25,7 @@ from .langhelpers import iterate_attributes, class_hierarchy, \
getargspec_init, format_argspec_init, format_argspec_plus, \
get_func_kwargs, get_cls_kwargs, decorator, as_interface, \
memoized_property, memoized_instancemethod, md5_hex, \
- group_expirable_memoized_property, importlater, decode_slice, \
+ group_expirable_memoized_property, importlater, dependencies, decode_slice, \
monkeypatch_proxied_specials, asbool, bool_or_str, coerce_kw_type,\
duck_type_collection, assert_arg_type, symbol, dictlike_iteritems,\
classproperty, set_creation_order, warn_exception, warn, NoneType,\
diff --git a/lib/sqlalchemy/util/langhelpers.py b/lib/sqlalchemy/util/langhelpers.py
index d8e469a2b..e10a10e27 100644
--- a/lib/sqlalchemy/util/langhelpers.py
+++ b/lib/sqlalchemy/util/langhelpers.py
@@ -107,6 +107,30 @@ def decorator(target):
return update_wrapper(decorate, target)
+def public_factory(target):
+ """Produce a wrapping function for the given cls or classmethod.
+
+ Rationale here is so that the __init__ method of the
+ class can serve as documentation for the function.
+
+ """
+ if isinstance(target, type):
+ fn = target.__init__
+ callable_ = target
+ else:
+ fn = callable_ = target
+ spec = compat.inspect_getfullargspec(fn)
+ del spec[0][0]
+ #import pdb
+ #pdb.set_trace()
+ metadata = format_argspec_plus(spec, grouped=False)
+ code = 'lambda %(args)s: cls(%(apply_kw)s)' % metadata
+ decorated = eval(code, {'cls': callable_, 'symbol': symbol})
+ decorated.__doc__ = fn.__doc__
+ return decorated
+ #return update_wrapper(decorated, fn)
+
+
class PluginLoader(object):
def __init__(self, group, auto_fn=None):
@@ -619,7 +643,11 @@ class memoized_property(object):
return result
def _reset(self, obj):
- obj.__dict__.pop(self.__name__, None)
+ memoized_property.reset(obj, self.__name__)
+
+ @classmethod
+ def reset(cls, obj, name):
+ obj.__dict__.pop(name, None)
class memoized_instancemethod(object):
@@ -698,15 +726,26 @@ class importlater(object):
_unresolved = set()
+ _by_key = {}
+
+ def __new__(cls, path, addtl):
+ key = path + "." + addtl
+ if key in importlater._by_key:
+ return importlater._by_key[key]
+ else:
+ importlater._by_key[key] = imp = object.__new__(cls)
+ return imp
+
def __init__(self, path, addtl):
self._il_path = path
self._il_addtl = addtl
importlater._unresolved.add(self)
@classmethod
- def resolve_all(cls):
+ def resolve_all(cls, path):
for m in list(importlater._unresolved):
- m._resolve()
+ if m._full_path.startswith(path):
+ m._resolve()
@property
def _full_path(self):
@@ -742,6 +781,61 @@ class importlater(object):
self.__dict__[key] = attr
return attr
+def dependencies(*deps):
+ """Apply imported dependencies as arguments to a function.
+
+ E.g.::
+
+ @util.dependencies(
+ "sqlalchemy.sql.widget",
+ "sqlalchemy.engine.default"
+ );
+ def some_func(self, widget, default, arg1, arg2, **kw):
+ # ...
+
+ Rationale is so that the impact of a dependency cycle can be
+ associated directly with the few functions that cause the cycle,
+ and not pollute the module-level namespace.
+
+ """
+ import_deps = []
+ for dep in deps:
+ tokens = dep.split(".")
+ import_deps.append(
+ importlater(
+ ".".join(tokens[0:-1]),
+ tokens[-1]
+ )
+ )
+
+ def decorate(fn):
+ spec = compat.inspect_getfullargspec(fn)
+
+ spec_zero = list(spec[0])
+ hasself = spec_zero[0] in ('self', 'cls')
+
+ for i in range(len(import_deps)):
+ spec[0][i + (1 if hasself else 0)] = "import_deps[%r]" % i
+
+ inner_spec = format_argspec_plus(spec, grouped=False)
+
+ for impname in import_deps:
+ del spec_zero[1 if hasself else 0]
+ spec[0][:] = spec_zero
+
+ outer_spec = format_argspec_plus(spec, grouped=False)
+
+ code = 'lambda %(args)s: fn(%(apply_kw)s)' % {
+ "args": outer_spec['args'],
+ "apply_kw": inner_spec['apply_kw']
+ }
+
+ decorated = eval(code, locals())
+ decorated.__defaults__ = getattr(fn, 'im_func', fn).__defaults__
+ return update_wrapper(decorated, fn)
+ return decorate
+
+
# from paste.deploy.converters
def asbool(obj):
if isinstance(obj, compat.string_types):
@@ -943,7 +1037,7 @@ class _symbol(int):
return repr(self)
def __repr__(self):
- return "<symbol '%s>" % self.name
+ return "symbol(%r)" % self.name
_symbol.__name__ = 'symbol'
diff --git a/test/engine/test_bind.py b/test/engine/test_bind.py
index 973cf4d84..8f6c547f1 100644
--- a/test/engine/test_bind.py
+++ b/test/engine/test_bind.py
@@ -1,7 +1,7 @@
"""tests the "bind" attribute/argument across schema and SQL,
including the deprecated versions of these arguments"""
-from sqlalchemy.testing import eq_, assert_raises
+from sqlalchemy.testing import assert_raises, assert_raises_message
from sqlalchemy import engine, exc
from sqlalchemy import MetaData, ThreadLocalMetaData
from sqlalchemy import Integer, text
@@ -44,7 +44,7 @@ class BindTest(fixtures.TestBase):
testing.db.connect()
):
for args in [
- ([], {'bind':bind}),
+ ([], {'bind': bind}),
([bind], {})
]:
metadata.create_all(*args[0], **args[1])
@@ -56,18 +56,13 @@ class BindTest(fixtures.TestBase):
def test_create_drop_err_metadata(self):
metadata = MetaData()
- table = Table('test_table', metadata, Column('foo', Integer))
+ Table('test_table', metadata, Column('foo', Integer))
for meth in [metadata.create_all, metadata.drop_all]:
- try:
- meth()
- assert False
- except exc.UnboundExecutionError as e:
- eq_(str(e),
- "The MetaData is not bound to an Engine or "
- "Connection. Execution can not proceed without a "
- "database to execute against. Either execute with "
- "an explicit connection or assign the MetaData's "
- ".bind to enable implicit execution.")
+ assert_raises_message(
+ exc.UnboundExecutionError,
+ "MetaData object is not bound to an Engine or Connection.",
+ meth
+ )
def test_create_drop_err_table(self):
metadata = MetaData()
@@ -79,23 +74,16 @@ class BindTest(fixtures.TestBase):
table.create,
table.drop,
]:
- try:
- meth()
- assert False
- except exc.UnboundExecutionError as e:
- eq_(
- str(e),
- "The Table 'test_table' "
- "is not bound to an Engine or Connection. "
- "Execution can not proceed without a database to execute "
- "against. Either execute with an explicit connection or "
- "assign this Table's .metadata.bind to enable implicit "
- "execution.")
+ assert_raises_message(
+ exc.UnboundExecutionError,
+ "Table object 'test_table' is not bound to an Engine or Connection.",
+ meth
+ )
@testing.uses_deprecated()
def test_create_drop_bound(self):
- for meta in (MetaData,ThreadLocalMetaData):
+ for meta in (MetaData, ThreadLocalMetaData):
for bind in (
testing.db,
testing.db.connect()
@@ -136,7 +124,7 @@ class BindTest(fixtures.TestBase):
try:
for args in (
([bind], {}),
- ([], {'bind':bind}),
+ ([], {'bind': bind}),
):
metadata = MetaData(*args[0], **args[1])
table = Table('test_table', metadata,
diff --git a/test/orm/test_relationships.py b/test/orm/test_relationships.py
index 8dc9c3c52..4cd278e16 100644
--- a/test/orm/test_relationships.py
+++ b/test/orm/test_relationships.py
@@ -2232,7 +2232,7 @@ class InvalidRemoteSideTest(fixtures.MappedTest):
assert_raises_message(sa.exc.ArgumentError,
"T1.t1s and back-reference T1.parent are "
- "both of the same direction <symbol 'ONETOMANY>. Did you "
+ r"both of the same direction symbol\('ONETOMANY'\). Did you "
"mean to set remote_side on the many-to-one side ?",
configure_mappers)
@@ -2247,7 +2247,7 @@ class InvalidRemoteSideTest(fixtures.MappedTest):
assert_raises_message(sa.exc.ArgumentError,
"T1.t1s and back-reference T1.parent are "
- "both of the same direction <symbol 'MANYTOONE>. Did you "
+ r"both of the same direction symbol\('MANYTOONE'\). Did you "
"mean to set remote_side on the many-to-one side ?",
configure_mappers)
@@ -2261,7 +2261,7 @@ class InvalidRemoteSideTest(fixtures.MappedTest):
# can't be sure of ordering here
assert_raises_message(sa.exc.ArgumentError,
- "both of the same direction <symbol 'ONETOMANY>. Did you "
+ r"both of the same direction symbol\('ONETOMANY'\). Did you "
"mean to set remote_side on the many-to-one side ?",
configure_mappers)
@@ -2277,7 +2277,7 @@ class InvalidRemoteSideTest(fixtures.MappedTest):
# can't be sure of ordering here
assert_raises_message(sa.exc.ArgumentError,
- "both of the same direction <symbol 'MANYTOONE>. Did you "
+ r"both of the same direction symbol\('MANYTOONE'\). Did you "
"mean to set remote_side on the many-to-one side ?",
configure_mappers)
diff --git a/test/engine/test_ddlemit.py b/test/sql/test_ddlemit.py
index e773d0ced..be75f63ec 100644
--- a/test/engine/test_ddlemit.py
+++ b/test/sql/test_ddlemit.py
@@ -1,5 +1,5 @@
from sqlalchemy.testing import fixtures
-from sqlalchemy.engine.ddl import SchemaGenerator, SchemaDropper
+from sqlalchemy.sql.ddl import SchemaGenerator, SchemaDropper
from sqlalchemy.engine import default
from sqlalchemy import MetaData, Table, Column, Integer, Sequence
from sqlalchemy import schema