diff options
author | Lele Gaifax <lele@metapensiero.it> | 2007-12-09 23:08:59 +0000 |
---|---|---|
committer | Lele Gaifax <lele@metapensiero.it> | 2007-12-09 23:08:59 +0000 |
commit | 7cbfbba9496fc41c21390aad8d8731c45fbacbce (patch) | |
tree | 5e14773d96a388fd8329ea0de3949f6c958e7b4d | |
parent | 7758edfd7427a0ad429e23bc57878422840d2d6d (diff) | |
download | sqlalchemy-7cbfbba9496fc41c21390aad8d8731c45fbacbce.tar.gz |
Better reflection of Firebird data types.
Instead of relying on internal numeric code, lookup the associated real
name. This has the extra benefit of properly handling of DOMAINs.
-rw-r--r-- | CHANGES | 2 | ||||
-rw-r--r-- | lib/sqlalchemy/databases/firebird.py | 42 | ||||
-rw-r--r-- | test/dialect/firebird.py | 48 |
3 files changed, 73 insertions, 19 deletions
@@ -149,6 +149,8 @@ CHANGES - MSSQL/PyODBC no longer has a global "set nocount on". + - Firebird backend does properly reflect domains (partially fixing [ticket:410]). + 0.4.1 ----- diff --git a/lib/sqlalchemy/databases/firebird.py b/lib/sqlalchemy/databases/firebird.py index 3a13c4b69..1da5e4738 100644 --- a/lib/sqlalchemy/databases/firebird.py +++ b/lib/sqlalchemy/databases/firebird.py @@ -123,6 +123,23 @@ colspecs = { } +ischema_names = { + 'SHORT': lambda r: FBSmallInteger(), + 'LONG': lambda r: FBInteger(), + 'QUAD': lambda r: FBFloat(), + 'FLOAT': lambda r: FBFloat(), + 'DATE': lambda r: FBDate(), + 'TIME': lambda r: FBTime(), + 'TEXT': lambda r: FBString(r['FLEN']), + 'INT64': lambda r: FBNumeric(precision=r['FPREC'], length=r['FSCALE'] * -1), # This generically handles NUMERIC() + 'DOUBLE': lambda r: FBFloat(), + 'TIMESTAMP': lambda r: FBDateTime(), + 'VARYING': lambda r: FBString(r['FLEN']), + 'CSTRING': lambda r: FBChar(r['FLEN']), + 'BLOB': lambda r: r['STYPE']==1 and FBText() or FBBinary + } + + def descriptor(): return {'name':'firebird', 'description':'Firebird', @@ -220,33 +237,20 @@ class FBDialect(default.DefaultDialect): return False def reflecttable(self, connection, table, include_columns): - #TODO: map these better - column_func = { - 14 : lambda r: sqltypes.String(r['FLEN']), # TEXT - 7 : lambda r: sqltypes.Integer(), # SHORT - 8 : lambda r: r['FPREC']==0 and sqltypes.Integer() or sqltypes.Numeric(precision=r['FPREC'], length=r['FSCALE'] * -1), #INT or NUMERIC - 9 : lambda r: sqltypes.Float(), # QUAD - 10 : lambda r: sqltypes.Float(), # FLOAT - 27 : lambda r: sqltypes.Float(), # DOUBLE - 35 : lambda r: sqltypes.DateTime(), # TIMESTAMP - 37 : lambda r: sqltypes.String(r['FLEN']), # VARYING - 261: lambda r: sqltypes.TEXT(), # BLOB - 40 : lambda r: sqltypes.Char(r['FLEN']), # CSTRING - 12 : lambda r: sqltypes.Date(), # DATE - 13 : lambda r: sqltypes.Time(), # TIME - 16 : lambda r: sqltypes.Numeric(precision=r['FPREC'], length=r['FSCALE'] * -1) #INT64 - } tblqry = """ SELECT DISTINCT R.RDB$FIELD_NAME AS FNAME, R.RDB$NULL_FLAG AS NULL_FLAG, R.RDB$FIELD_POSITION, - F.RDB$FIELD_TYPE AS FTYPE, + T.RDB$TYPE_NAME AS FTYPE, F.RDB$FIELD_SUB_TYPE AS STYPE, F.RDB$FIELD_LENGTH AS FLEN, F.RDB$FIELD_PRECISION AS FPREC, F.RDB$FIELD_SCALE AS FSCALE FROM RDB$RELATION_FIELDS R - JOIN RDB$FIELDS F ON R.RDB$FIELD_SOURCE=F.RDB$FIELD_NAME + JOIN RDB$FIELDS F + ON R.RDB$FIELD_SOURCE=F.RDB$FIELD_NAME + JOIN RDB$TYPES T + ON T.RDB$TYPE=F.RDB$FIELD_TYPE AND T.RDB$FIELD_NAME='RDB$FIELD_TYPE' WHERE F.RDB$SYSTEM_FLAG=0 and R.RDB$RELATION_NAME=? ORDER BY R.RDB$FIELD_POSITION""" keyqry = """ @@ -293,7 +297,7 @@ class FBDialect(default.DefaultDialect): kw = {} # get the data types and lengths - coltype = column_func.get(row['FTYPE'], None) + coltype = ischema_names.get(row['FTYPE'].rstrip()) if coltype is None: warnings.warn(RuntimeWarning("Did not recognize type '%s' of column '%s'" % (str(row['FTYPE']), name))) coltype = sqltypes.NULLTYPE diff --git a/test/dialect/firebird.py b/test/dialect/firebird.py index 636ab2d05..f3cd57aa7 100644 --- a/test/dialect/firebird.py +++ b/test/dialect/firebird.py @@ -1,6 +1,7 @@ import testbase from sqlalchemy import * from sqlalchemy.databases import firebird +from sqlalchemy.exceptions import ProgrammingError from sqlalchemy.sql import table, column from testlib import * @@ -11,6 +12,53 @@ class BasicTest(AssertMixin): return True +class DomainReflectionTest(AssertMixin): + "Test Firebird domains" + + @testing.supported('firebird') + def setUpAll(self): + con = testbase.db.connect() + try: + con.execute('CREATE DOMAIN int_domain AS INTEGER DEFAULT 42 NOT NULL') + con.execute('CREATE DOMAIN str_domain AS VARCHAR(255)') + con.execute('CREATE DOMAIN rem_domain AS BLOB SUB_TYPE TEXT') + con.execute('CREATE DOMAIN img_domain AS BLOB SUB_TYPE BINARY') + except ProgrammingError, e: + if not "attempt to store duplicate value" in str(e): + raise e + con.execute('''CREATE TABLE testtable (question int_domain, + answer str_domain, + remark rem_domain, + photo img_domain, + d date, + t time, + dt timestamp)''') + + @testing.supported('firebird') + def tearDownAll(self): + con = testbase.db.connect() + con.execute('DROP TABLE testtable') + con.execute('DROP DOMAIN int_domain') + con.execute('DROP DOMAIN str_domain') + con.execute('DROP DOMAIN rem_domain') + con.execute('DROP DOMAIN img_domain') + + @testing.supported('firebird') + def test_table_is_reflected(self): + metadata = MetaData(testbase.db) + table = Table('testtable', metadata, autoload=True) + 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.type.__class__, firebird.FBInteger) + self.assertEquals(table.c.answer.type.__class__, firebird.FBString) + self.assertEquals(table.c.remark.type.__class__, firebird.FBText) + 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) + self.assertEquals(table.c.t.type.__class__, firebird.FBTime) + self.assertEquals(table.c.dt.type.__class__, firebird.FBDateTime) + class CompileTest(SQLCompileTest): __dialect__ = firebird.FBDialect() |