]> git.ipfire.org Git - thirdparty/sqlalchemy/sqlalchemy.git/commitdiff
Try to reflect also the Sequence on the PK under Firebird
authorLele Gaifax <lele@metapensiero.it>
Fri, 11 Jan 2008 15:27:02 +0000 (15:27 +0000)
committerLele Gaifax <lele@metapensiero.it>
Fri, 11 Jan 2008 15:27:02 +0000 (15:27 +0000)
CHANGES
lib/sqlalchemy/databases/firebird.py
test/dialect/firebird.py

diff --git a/CHANGES b/CHANGES
index ab87481c54cf9a7b306e98bdc1568ebb98cd3a75..01fa8d2714b827e7e0a993c71f750edbca6ca6f3 100644 (file)
--- a/CHANGES
+++ b/CHANGES
@@ -20,6 +20,9 @@ CHANGES
 - dialects
     - finally added PGMacAddr type to postgres [ticket:580]
 
+    - reflect the sequence associated to a PK field (tipically
+      with a BEFORE INSERT trigger) under Firebird
+
 0.4.2p3
 ------
 - general
index c83a44e016c5e7dd8e605e6818d48ae3ff90ff61..17232f5d4ebc1a311e7dde5fd3e2ff76ebe9218b 100644 (file)
@@ -262,7 +262,7 @@ class FBExecutionContext(default.DefaultExecutionContext):
 class FBDialect(default.DefaultDialect):
     """Firebird dialect"""
 
-    supports_sane_rowcount = False
+    supports_sane_rowcount = True
     supports_sane_multi_rowcount = False
     max_identifier_length = 31
     preexecute_pk_sequences = True
@@ -434,6 +434,22 @@ class FBDialect(default.DefaultDialect):
         WHERE rc.rdb$constraint_type=? AND rc.rdb$relation_name=?
         ORDER BY se.rdb$index_name, se.rdb$field_position
         """
+        # Heuristic-query to determine the generator associated to a PK field
+        genqry = """
+        SELECT trigdep.rdb$depended_on_name AS fgenerator
+        FROM rdb$dependencies tabdep
+             JOIN rdb$dependencies trigdep ON (tabdep.rdb$dependent_name=trigdep.rdb$dependent_name
+                                               AND trigdep.rdb$depended_on_type=14
+                                               AND trigdep.rdb$dependent_type=2)
+             JOIN rdb$triggers trig ON (trig.rdb$trigger_name=tabdep.rdb$dependent_name)
+        WHERE tabdep.rdb$depended_on_name=?
+          AND tabdep.rdb$depended_on_type=0
+          AND trig.rdb$trigger_type=1
+          AND tabdep.rdb$field_name=?
+          AND (SELECT count(*)
+               FROM rdb$dependencies trigdep2
+               WHERE trigdep2.rdb$dependent_name = trigdep.rdb$dependent_name) = 2
+        """
 
         tablename = self._denormalize_name(table.name)
 
@@ -457,7 +473,7 @@ class FBDialect(default.DefaultDialect):
             args = [name]
 
             kw = {}
-            # get the data types and lengths
+            # get the data type
             coltype = ischema_names.get(row['ftype'].rstrip())
             if coltype is None:
                 util.warn("Did not recognize type '%s' of column '%s'" %
@@ -476,10 +492,21 @@ class FBDialect(default.DefaultDialect):
             # does it have a default value?
             if row['fdefault'] is not None:
                 # the value comes down as "DEFAULT 'value'"
+                assert row['fdefault'].startswith('DEFAULT ')
                 defvalue = row['fdefault'][8:]
                 args.append(schema.PassiveDefault(sql.text(defvalue)))
 
-            table.append_column(schema.Column(*args, **kw))
+            col = schema.Column(*args, **kw)
+            if kw['primary_key']:
+                # if the PK is a single field, try to see if its linked to
+                # a sequence thru a trigger
+                if len(pkfields)==1:
+                    genc = connection.execute(genqry, [tablename, row['fname']])
+                    genr = genc.fetchone()
+                    if genr is not None:
+                        col.sequence = schema.Sequence(self._normalize_name(genr['fgenerator']))
+
+            table.append_column(col)
 
         if not found_table:
             raise exceptions.NoSuchTableError(table.name)
index a377a1caffbba7015eda653f36dd1475b3028a30..6b2a35c872c9b8929147791c13f50cc02eb26025 100644 (file)
@@ -21,13 +21,22 @@ class DomainReflectionTest(AssertMixin):
         except ProgrammingError, e:
             if not "attempt to store duplicate value" in str(e):
                 raise e
+        con.execute('''CREATE GENERATOR gen_testtable_id''')
         con.execute('''CREATE TABLE testtable (question int_domain,
                                                answer str_domain DEFAULT 'no answer',
-                                               remark rem_domain,
+                                               remark rem_domain DEFAULT '',
                                                photo img_domain,
                                                d date,
                                                t time,
                                                dt timestamp)''')
+        con.execute('''ALTER TABLE testtable
+                       ADD CONSTRAINT testtable_pk PRIMARY KEY (question)''')
+        con.execute('''CREATE TRIGGER testtable_autoid FOR testtable
+                       ACTIVE BEFORE INSERT AS
+                       BEGIN
+                         IF (NEW.question IS NULL) THEN
+                           NEW.question = gen_id(gen_testtable_id, 1);
+                       END''')
 
     def tearDownAll(self):
         con = testbase.db.connect()
@@ -36,6 +45,7 @@ class DomainReflectionTest(AssertMixin):
         con.execute('DROP DOMAIN str_domain')
         con.execute('DROP DOMAIN rem_domain')
         con.execute('DROP DOMAIN img_domain')
+        con.execute('DROP GENERATOR gen_testtable_id')
 
     def test_table_is_reflected(self):
         metadata = MetaData(testbase.db)
@@ -43,11 +53,14 @@ class DomainReflectionTest(AssertMixin):
         self.assertEquals(set(table.columns.keys()),
                           set(['question', 'answer', 'remark', 'photo', 'd', 't', 'dt']),
                           "Columns of reflected table didn't equal expected columns")
+        self.assertEquals(table.c.question.primary_key, True)
+        self.assertEquals(table.c.question.sequence.name, 'gen_testtable_id')
         self.assertEquals(table.c.question.type.__class__, firebird.FBInteger)
         self.assertEquals(table.c.question.default.arg.text, "42")
         self.assertEquals(table.c.answer.type.__class__, firebird.FBString)
         self.assertEquals(table.c.answer.default.arg.text, "'no answer'")
         self.assertEquals(table.c.remark.type.__class__, firebird.FBText)
+        self.assertEquals(table.c.remark.default.arg.text, "''")
         self.assertEquals(table.c.photo.type.__class__, firebird.FBBinary)
         # The following assume a Dialect 3 database
         self.assertEquals(table.c.d.type.__class__, firebird.FBDate)