summaryrefslogtreecommitdiff
path: root/lib
diff options
context:
space:
mode:
authorMike Bayer <mike_mp@zzzcomputing.com>2014-07-14 20:26:38 -0400
committerMike Bayer <mike_mp@zzzcomputing.com>2014-07-14 20:26:38 -0400
commitd2193f53c10d29a7a9b1846ebf322fbced55041f (patch)
treec4f97c44c32abfe913b0bc5c05c332e696f05c7a /lib
parent6fd0bc7c62849a1dc34ca7d93aeeaaff25897648 (diff)
downloadsqlalchemy-d2193f53c10d29a7a9b1846ebf322fbced55041f.tar.gz
- Fix bug in naming convention feature where using a check
constraint convention that includes ``constraint_name`` would then force all :class:`.Boolean` and :class:`.Enum` types to require names as well, as these implicitly create a constraint, even if the ultimate target backend were one that does not require generation of the constraint such as Postgresql. The mechanics of naming conventions for these particular constraints has been reorganized such that the naming determination is done at DDL compile time, rather than at constraint/table construction time. fixes #3067
Diffstat (limited to 'lib')
-rw-r--r--lib/sqlalchemy/sql/compiler.py8
-rw-r--r--lib/sqlalchemy/sql/elements.py16
-rw-r--r--lib/sqlalchemy/sql/naming.py38
-rw-r--r--lib/sqlalchemy/sql/sqltypes.py6
4 files changed, 49 insertions, 19 deletions
diff --git a/lib/sqlalchemy/sql/compiler.py b/lib/sqlalchemy/sql/compiler.py
index 7a8b07f8f..99b74b3c8 100644
--- a/lib/sqlalchemy/sql/compiler.py
+++ b/lib/sqlalchemy/sql/compiler.py
@@ -2902,7 +2902,13 @@ class IdentifierPreparer(object):
def format_savepoint(self, savepoint, name=None):
return self.quote(name or savepoint.ident)
- def format_constraint(self, constraint):
+ @util.dependencies("sqlalchemy.sql.naming")
+ def format_constraint(self, naming, constraint):
+ if isinstance(constraint.name, elements._defer_name):
+ name = naming._constraint_name_for_table(
+ constraint, constraint.table)
+ if name:
+ return self.quote(name)
return self.quote(constraint.name)
def format_table(self, table, use_schema=True, name=None):
diff --git a/lib/sqlalchemy/sql/elements.py b/lib/sqlalchemy/sql/elements.py
index 3882d9e5b..d273d9881 100644
--- a/lib/sqlalchemy/sql/elements.py
+++ b/lib/sqlalchemy/sql/elements.py
@@ -3201,6 +3201,22 @@ class _truncated_label(quoted_name):
def apply_map(self, map_):
return self
+
+class _defer_name(_truncated_label):
+ """mark a name as 'deferred' for the purposes of automated name
+ generation.
+
+ """
+ def __new__(cls, value):
+ if value is None:
+ return _defer_none_name('_unnamed_')
+ else:
+ return super(_defer_name, cls).__new__(cls, value)
+
+
+class _defer_none_name(_defer_name):
+ """indicate a 'deferred' name that was ultimately the value None."""
+
# for backwards compatibility in case
# someone is re-implementing the
# _truncated_identifier() sequence in a custom
diff --git a/lib/sqlalchemy/sql/naming.py b/lib/sqlalchemy/sql/naming.py
index d05054bc6..bb838c542 100644
--- a/lib/sqlalchemy/sql/naming.py
+++ b/lib/sqlalchemy/sql/naming.py
@@ -14,7 +14,7 @@ from .schema import Constraint, ForeignKeyConstraint, PrimaryKeyConstraint, \
UniqueConstraint, CheckConstraint, Index, Table, Column
from .. import event, events
from .. import exc
-from .elements import _truncated_label
+from .elements import _truncated_label, _defer_name, _defer_none_name
import re
class conv(_truncated_label):
@@ -77,12 +77,12 @@ class ConventionDict(object):
return list(self.const.columns)[idx]
def _key_constraint_name(self):
- if not self._const_name:
+ if isinstance(self._const_name, (type(None), _defer_none_name)):
raise exc.InvalidRequestError(
- "Naming convention including "
- "%(constraint_name)s token requires that "
- "constraint is explicitly named."
- )
+ "Naming convention including "
+ "%(constraint_name)s token requires that "
+ "constraint is explicitly named."
+ )
if not isinstance(self._const_name, conv):
self.const.name = None
return self._const_name
@@ -144,6 +144,17 @@ def _get_convention(dict_, key):
else:
return None
+def _constraint_name_for_table(const, table):
+ metadata = table.metadata
+ convention = _get_convention(metadata.naming_convention, type(const))
+ if convention is not None and (
+ const.name is None or not isinstance(const.name, conv) and
+ "constraint_name" in convention
+ ):
+ return conv(
+ convention % ConventionDict(const, table,
+ metadata.naming_convention)
+ )
@event.listens_for(Constraint, "after_parent_attach")
@event.listens_for(Index, "after_parent_attach")
@@ -156,12 +167,9 @@ def _constraint_name(const, table):
lambda col, table: _constraint_name(const, table)
)
elif isinstance(table, Table):
- metadata = table.metadata
- convention = _get_convention(metadata.naming_convention, type(const))
- if convention is not None:
- if const.name is None or "constraint_name" in convention:
- newname = conv(
- convention % ConventionDict(const, table, metadata.naming_convention)
- )
- if const.name is None:
- const.name = newname
+ if isinstance(const.name, (conv, _defer_name)):
+ return
+
+ newname = _constraint_name_for_table(const, table)
+ if newname is not None:
+ const.name = newname
diff --git a/lib/sqlalchemy/sql/sqltypes.py b/lib/sqlalchemy/sql/sqltypes.py
index b4d2d2390..52e5053ef 100644
--- a/lib/sqlalchemy/sql/sqltypes.py
+++ b/lib/sqlalchemy/sql/sqltypes.py
@@ -13,7 +13,7 @@ import datetime as dt
import codecs
from .type_api import TypeEngine, TypeDecorator, to_instance
-from .elements import quoted_name, type_coerce
+from .elements import quoted_name, type_coerce, _defer_name
from .default_comparator import _DefaultColumnComparator
from .. import exc, util, processors
from .base import _bind_or_error, SchemaEventTarget
@@ -1132,7 +1132,7 @@ class Enum(String, SchemaType):
e = schema.CheckConstraint(
type_coerce(column, self).in_(self.enums),
- name=self.name,
+ name=_defer_name(self.name),
_create_rule=util.portable_instancemethod(
self._should_create_constraint)
)
@@ -1268,7 +1268,7 @@ class Boolean(TypeEngine, SchemaType):
e = schema.CheckConstraint(
type_coerce(column, self).in_([0, 1]),
- name=self.name,
+ name=_defer_name(self.name),
_create_rule=util.portable_instancemethod(
self._should_create_constraint)
)