]> git.ipfire.org Git - thirdparty/sqlalchemy/sqlalchemy.git/commitdiff
- [feature] Added "MATCH" clause to ForeignKey,
authorMike Bayer <mike_mp@zzzcomputing.com>
Thu, 21 Jun 2012 19:39:48 +0000 (15:39 -0400)
committerMike Bayer <mike_mp@zzzcomputing.com>
Thu, 21 Jun 2012 19:39:48 +0000 (15:39 -0400)
ForeignKeyConstraint, courtesy Ryan Kelly.
[ticket:2502]

CHANGES
lib/sqlalchemy/schema.py
lib/sqlalchemy/sql/compiler.py
test/sql/test_constraints.py

diff --git a/CHANGES b/CHANGES
index 757972a7532022b3d64cb3fc61b3df4ca4542b75..7e59d638a13653f753e2b0f1e6d6a570e9ac2968 100644 (file)
--- a/CHANGES
+++ b/CHANGES
@@ -199,6 +199,10 @@ underneath "0.7.xx".
     that aren't in the target table is now an exception.
     [ticket:2415]
 
+  - [feature] Added "MATCH" clause to ForeignKey,
+    ForeignKeyConstraint, courtesy Ryan Kelly.
+    [ticket:2502]
+
   - [feature] select() features a correlate_except()
     method, auto correlates all selectables except those
     passed.
index 231da7d035765fb3efe172ac8ecf06948150c0e9..d50b8af2a6f2fe5f8fc09ea743554ac0e196ce9d 100644 (file)
@@ -1191,7 +1191,7 @@ class ForeignKey(SchemaItem):
     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):
+                    initially=None, link_to_name=False, match=None):
         """
         Construct a column-level FOREIGN KEY.
 
@@ -1236,6 +1236,10 @@ class ForeignKey(SchemaItem):
             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
@@ -1255,6 +1259,7 @@ class ForeignKey(SchemaItem):
         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()
@@ -1283,7 +1288,8 @@ class ForeignKey(SchemaItem):
                 ondelete=self.ondelete,
                 deferrable=self.deferrable,
                 initially=self.initially,
-                link_to_name=self.link_to_name
+                link_to_name=self.link_to_name,
+                match=self.match
                 )
         fk.dispatch._update(self.dispatch)
         return fk
@@ -1445,6 +1451,7 @@ class ForeignKey(SchemaItem):
                 [], [], 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)
@@ -2031,7 +2038,7 @@ class ForeignKeyConstraint(Constraint):
 
     def __init__(self, columns, refcolumns, name=None, onupdate=None,
             ondelete=None, deferrable=None, initially=None, use_alter=False,
-            link_to_name=False, table=None):
+            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
@@ -2072,6 +2079,10 @@ class ForeignKeyConstraint(Constraint):
           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)
@@ -2082,6 +2093,7 @@ class ForeignKeyConstraint(Constraint):
         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()
 
@@ -2097,7 +2109,8 @@ class ForeignKeyConstraint(Constraint):
                     onupdate=self.onupdate, 
                     ondelete=self.ondelete, 
                     use_alter=self.use_alter, 
-                    link_to_name=self.link_to_name
+                    link_to_name=self.link_to_name,
+                    match=self.match
                 )
 
         if table is not None:
@@ -2153,7 +2166,8 @@ class ForeignKeyConstraint(Constraint):
                     use_alter=self.use_alter,
                     deferrable=self.deferrable,
                     initially=self.initially,
-                    link_to_name=self.link_to_name
+                    link_to_name=self.link_to_name,
+                    match=self.match
                 )
         fkc.dispatch._update(self.dispatch)
         return fkc
index fd7e7d7733242632cda74eda4937343ddf6c4f6a..8a8c773f82ef7c882705334e759ff62d065f24c3 100644 (file)
@@ -1726,6 +1726,7 @@ class DDLCompiler(engine.Compiled):
             ', '.join(preparer.quote(f.column.name, f.column.quote)
                       for f in constraint._elements.values())
         )
+        text += self.define_constraint_match(constraint)
         text += self.define_constraint_cascades(constraint)
         text += self.define_constraint_deferrability(constraint)
         return text
@@ -1765,6 +1766,12 @@ class DDLCompiler(engine.Compiled):
             text += " INITIALLY %s" % constraint.initially
         return text
 
+    def define_constraint_match(self, constraint):
+        text = ""
+        if constraint.match is not None:
+            text += " MATCH %s" % constraint.match
+        return text
+
 
 class GenericTypeCompiler(engine.TypeCompiler):
     def visit_CHAR(self, type_):
index 546a14ca2f9e452ddbce2e58b370b4cd4c06245c..fcc6c085e46c37a085ab306a0619c45a6138c973 100644 (file)
@@ -355,6 +355,25 @@ class ConstraintCompilationTest(fixtures.TestBase, AssertsCompiledSQL):
             "(a) DEFERRABLE INITIALLY DEFERRED)",
         )
 
+    def test_fk_match_clause(self):
+        t = Table('tbl', MetaData(),
+                  Column('a', Integer),
+                  Column('b', Integer,
+                         ForeignKey('tbl.a', match="SIMPLE")))
+
+        self.assert_compile(
+            schema.CreateTable(t),
+            "CREATE TABLE tbl (a INTEGER, b INTEGER, "
+            "FOREIGN KEY(b) REFERENCES tbl "
+            "(a) MATCH SIMPLE)",
+        )
+
+        self.assert_compile(
+            schema.AddConstraint(list(t.foreign_keys)[0].constraint),
+            "ALTER TABLE tbl ADD FOREIGN KEY(b) "
+            "REFERENCES tbl (a) MATCH SIMPLE"
+        )
+
     def test_deferrable_unique(self):
         factory = lambda **kw: UniqueConstraint('b', **kw)
         self._test_deferrable(factory)
@@ -554,4 +573,4 @@ class ConstraintCompilationTest(fixtures.TestBase, AssertsCompiledSQL):
         )
         c = CheckConstraint(t.c.a > t2.c.b)
         assert c not in t.constraints
-        assert c not in t2.constraints
\ No newline at end of file
+        assert c not in t2.constraints