]> git.ipfire.org Git - thirdparty/sqlalchemy/sqlalchemy.git/commitdiff
Added a new argument to :class:`.Enum` and its base
authorMike Bayer <mike_mp@zzzcomputing.com>
Sat, 2 Feb 2013 01:47:02 +0000 (20:47 -0500)
committerMike Bayer <mike_mp@zzzcomputing.com>
Sat, 2 Feb 2013 01:47:02 +0000 (20:47 -0500)
:class:`.SchemaType` ``inherit_schema``.  When set to ``True``,
the type will set its ``schema`` attribute of that of the
:class:`.Table` to which it is associated.  This also occurs
during a :meth:`.Table.tometadata` operation; the :class:`.SchemaType`
is now copied in all cases when :meth:`.Table.tometadata` happens,
and if ``inherit_schema=True``, the type will take on the new
schema name passed to the method.   The ``schema`` is important
when used with the Postgresql backend, as the type results in
a ``CREATE TYPE`` statement. [ticket:2657]

doc/build/changelog/changelog_08.rst
lib/sqlalchemy/dialects/mysql/base.py
lib/sqlalchemy/schema.py
lib/sqlalchemy/types.py
test/sql/test_metadata.py

index bcdd77fa16922ed87f50cb240abe4902ac076b4f..e6cc169b1def98bb2cf33cdeb99313264ebdc97f 100644 (file)
@@ -6,6 +6,21 @@
 .. changelog::
     :version: 0.8.0
 
+    .. change::
+        :tags: feature, sql
+        :tickets: 2657
+
+      Added a new argument to :class:`.Enum` and its base
+      :class:`.SchemaType` ``inherit_schema``.  When set to ``True``,
+      the type will set its ``schema`` attribute of that of the
+      :class:`.Table` to which it is associated.  This also occurs
+      during a :meth:`.Table.tometadata` operation; the :class:`.SchemaType`
+      is now copied in all cases when :meth:`.Table.tometadata` happens,
+      and if ``inherit_schema=True``, the type will take on the new
+      schema name passed to the method.   The ``schema`` is important
+      when used with the Postgresql backend, as the type results in
+      a ``CREATE TYPE`` statement.
+
     .. change::
         :tags: feature, postgresql
         :pullreq: 40
index 5f324e5dd9ebc322faeed35a17c61e020c537511..566a71da7a68f4be692f1c7f84552d6c1e179cf6 100644 (file)
@@ -1061,6 +1061,7 @@ class ENUM(sqltypes.Enum, _StringType):
         kw.pop('name', None)
         kw.pop('quote', None)
         kw.pop('native_enum', None)
+        kw.pop('inherit_schema', None)
         _StringType.__init__(self, length=length, **kw)
         sqltypes.Enum.__init__(self, *enums)
 
index b9ee55abf88f5295199a65fba6e61a16dacb1926..9d14bd3cabee59d1c8173b87f0f46d1cdffd19eb 100644 (file)
@@ -634,16 +634,26 @@ class Table(SchemaItem, expression.TableClause):
 
         E.g.::
 
+            some_engine = create_engine("sqlite:///some.db")
+
             # create two metadata
-            meta1 = MetaData('sqlite:///querytest.db')
+            meta1 = MetaData()
             meta2 = MetaData()
 
             # load 'users' from the sqlite engine
-            users_table = Table('users', meta1, autoload=True)
+            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:
@@ -1094,9 +1104,13 @@ class Column(SchemaItem, expression.ColumnClause):
             [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_=self.type,
+                type_=type_,
                 key=self.key,
                 primary_key=self.primary_key,
                 nullable=self.nullable,
index ce64bb83e83ffdb5af4aa05ea435df13d618292e..b9f7b94442466e73014e5bd287c20dfb4e4fee4c 100644 (file)
@@ -1791,6 +1791,13 @@ class SchemaType(events.SchemaEventTarget):
     surrounding the association of the type object with a parent
     :class:`.Column`.
 
+    .. seealso::
+
+        :class:`.Enum`
+
+        :class:`.Boolean`
+
+
     """
 
     def __init__(self, **kw):
@@ -1798,6 +1805,7 @@ class SchemaType(events.SchemaEventTarget):
         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,
@@ -1814,6 +1822,9 @@ class SchemaType(events.SchemaEventTarget):
         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",
@@ -1839,6 +1850,20 @@ class SchemaType(events.SchemaEventTarget):
                 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
@@ -1891,7 +1916,7 @@ class Enum(String, SchemaType):
     By default, uses the backend's native ENUM type if available,
     else uses VARCHAR + a CHECK constraint.
 
-    See also:
+    .. seealso::
 
         :class:`~.postgresql.ENUM` - PostgreSQL-specific type,
         which has additional functionality.
@@ -1933,16 +1958,31 @@ class Enum(String, SchemaType):
            available. Defaults to True. When False, uses VARCHAR + check
            constraint for all backends.
 
-        :param schema: Schemaname of this type. For types that exist on the
+        :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)
@@ -1988,13 +2028,16 @@ class Enum(String, SchemaType):
         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=self.schema,
-                        metadata=self.metadata,
+                        schema=schema,
+                        metadata=metadata,
                         convert_unicode=self.convert_unicode,
                         native_enum=self.native_enum,
+                        inherit_schema=self.inherit_schema,
                         *self.enums,
                         **kw
                         )
index f8256bca7f4a0155a63bea86bd604d86342973e5..1b8068f22840207284001542f521144053c6da05 100644 (file)
@@ -6,7 +6,7 @@ import pickle
 from sqlalchemy import Integer, String, UniqueConstraint, \
     CheckConstraint, ForeignKey, MetaData, Sequence, \
     ForeignKeyConstraint, ColumnDefault, Index, event,\
-    events, Unicode
+    events, Unicode, types as sqltypes
 from sqlalchemy.testing.schema import Table, Column
 from sqlalchemy import schema, exc
 import sqlalchemy as tsa
@@ -361,6 +361,7 @@ class MetaDataTest(fixtures.TestBase, ComparesTables):
         a2 = a.tometadata(m2)
         assert b2.c.y.references(a2.c.x)
 
+
     def test_pickle_metadata_sequence_restated(self):
         m1 = MetaData()
         Table('a', m1,
@@ -748,6 +749,102 @@ class TableTest(fixtures.TestBase, AssertsCompiledSQL):
         )
         is_(t._autoincrement_column, t.c.id)
 
+class SchemaTypeTest(fixtures.TestBase):
+    class MyType(sqltypes.SchemaType, sqltypes.TypeEngine):
+        column = None
+        table = None
+        evt_targets = ()
+
+        def _set_table(self, column, table):
+            super(SchemaTypeTest.MyType, self)._set_table(column, table)
+            self.column = column
+            self.table = table
+
+        def _on_table_create(self, target, bind, **kw):
+            self.evt_targets += (target,)
+
+    def test_independent_schema(self):
+        m = MetaData()
+        type_ = self.MyType(schema="q")
+        t1 = Table('x', m, Column("y", type_), schema="z")
+        eq_(t1.c.y.type.schema, "q")
+
+    def test_inherit_schema(self):
+        m = MetaData()
+        type_ = self.MyType(schema="q", inherit_schema=True)
+        t1 = Table('x', m, Column("y", type_), schema="z")
+        eq_(t1.c.y.type.schema, "z")
+
+    def test_independent_schema_enum(self):
+        m = MetaData()
+        type_ = sqltypes.Enum("a", schema="q")
+        t1 = Table('x', m, Column("y", type_), schema="z")
+        eq_(t1.c.y.type.schema, "q")
+
+    def test_inherit_schema_enum(self):
+        m = MetaData()
+        type_ = sqltypes.Enum("a", "b", "c", schema="q", inherit_schema=True)
+        t1 = Table('x', m, Column("y", type_), schema="z")
+        eq_(t1.c.y.type.schema, "z")
+
+    def test_tometadata_copy_type(self):
+        m1 = MetaData()
+
+        type_ = self.MyType()
+        t1 = Table('x', m1, Column("y", type_))
+
+        m2 = MetaData()
+        t2 = t1.tometadata(m2)
+
+        # metadata isn't set
+        is_(t2.c.y.type.metadata, None)
+
+        # our test type sets table, though
+        is_(t2.c.y.type.table, t2)
+
+    def test_tometadata_independent_schema(self):
+        m1 = MetaData()
+
+        type_ = self.MyType()
+        t1 = Table('x', m1, Column("y", type_))
+
+        m2 = MetaData()
+        t2 = t1.tometadata(m2, schema="bar")
+
+        eq_(t2.c.y.type.schema, None)
+
+    def test_tometadata_inherit_schema(self):
+        m1 = MetaData()
+
+        type_ = self.MyType(inherit_schema=True)
+        t1 = Table('x', m1, Column("y", type_))
+
+        m2 = MetaData()
+        t2 = t1.tometadata(m2, schema="bar")
+
+        eq_(t1.c.y.type.schema, None)
+        eq_(t2.c.y.type.schema, "bar")
+
+    def test_tometadata_independent_events(self):
+        m1 = MetaData()
+
+        type_ = self.MyType()
+        t1 = Table('x', m1, Column("y", type_))
+
+        m2 = MetaData()
+        t2 = t1.tometadata(m2)
+
+        t1.dispatch.before_create(t1, testing.db)
+        eq_(t1.c.y.type.evt_targets, (t1,))
+        eq_(t2.c.y.type.evt_targets, ())
+
+        t2.dispatch.before_create(t2, testing.db)
+        t2.dispatch.before_create(t2, testing.db)
+        eq_(t1.c.y.type.evt_targets, (t1,))
+        eq_(t2.c.y.type.evt_targets, (t2, t2))
+
+
+
 class SchemaTest(fixtures.TestBase, AssertsCompiledSQL):
 
     def test_default_schema_metadata_fk(self):