diff options
-rw-r--r-- | CHANGES | 3 | ||||
-rw-r--r-- | lib/sqlalchemy/databases/firebird.py | 33 | ||||
-rw-r--r-- | test/dialect/firebird.py | 15 |
3 files changed, 47 insertions, 4 deletions
@@ -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 diff --git a/lib/sqlalchemy/databases/firebird.py b/lib/sqlalchemy/databases/firebird.py index c83a44e01..17232f5d4 100644 --- a/lib/sqlalchemy/databases/firebird.py +++ b/lib/sqlalchemy/databases/firebird.py @@ -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) diff --git a/test/dialect/firebird.py b/test/dialect/firebird.py index a377a1caf..6b2a35c87 100644 --- a/test/dialect/firebird.py +++ b/test/dialect/firebird.py @@ -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) |