summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLele Gaifax <lele@metapensiero.it>2007-12-09 23:08:59 +0000
committerLele Gaifax <lele@metapensiero.it>2007-12-09 23:08:59 +0000
commit7cbfbba9496fc41c21390aad8d8731c45fbacbce (patch)
tree5e14773d96a388fd8329ea0de3949f6c958e7b4d
parent7758edfd7427a0ad429e23bc57878422840d2d6d (diff)
downloadsqlalchemy-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--CHANGES2
-rw-r--r--lib/sqlalchemy/databases/firebird.py42
-rw-r--r--test/dialect/firebird.py48
3 files changed, 73 insertions, 19 deletions
diff --git a/CHANGES b/CHANGES
index ae9409498..aa0c33c4b 100644
--- a/CHANGES
+++ b/CHANGES
@@ -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()