Index: E:/prj/src/sqlalchemy/lib/sqlalchemy/databases/firebird.py =================================================================== --- E:/prj/src/sqlalchemy/lib/sqlalchemy/databases/firebird.py (revision 1550) +++ E:/prj/src/sqlalchemy/lib/sqlalchemy/databases/firebird.py (working copy) @@ -7,17 +7,20 @@ import sys, StringIO, string -import sqlalchemy.sql as sql +import sqlalchemy.engine.default as default +# import sqlalchemy.sql as sql import sqlalchemy.schema as schema import sqlalchemy.ansisql as ansisql -from sqlalchemy import * +# from sqlalchemy import * import sqlalchemy.types as sqltypes - +import sqlalchemy.exceptions as exceptions try: import kinterbasdb except: kinterbasdb = None +dbmodule = kinterbasdb + class FBNumeric(sqltypes.Numeric): def get_col_spec(self): return "NUMERIC(%(precision)s, %(length)s)" % {'precision': self.precision, 'length' : self.length} @@ -73,36 +76,12 @@ ('password', 'Password', None) ]} -class FBSQLEngine(ansisql.ANSISQLEngine): - def __init__(self, opts, use_ansi = True, module = None, **params): - self._use_ansi = use_ansi - self.opts = opts or {} - if module is None: - self.module = kinterbasdb - else: - self.module = module - ansisql.ANSISQLEngine.__init__(self, **params) - - def do_commit(self, connection): - connection.commit(True) - - def do_rollback(self, connection): - connection.rollback(True) - - def dbapi(self): - return self.module - - def connect_args(self): - return [[], self.opts] - - def type_descriptor(self, typeobj): - return sqltypes.adapt_type(typeobj, colspecs) - - def last_inserted_ids(self): - return self.context.last_inserted_ids - +class FireBirdExecutionContext(default.DefaultExecutionContext): + def supports_sane_rowcount(self): + return True + def compiler(self, statement, bindparams, **kwargs): - return FBCompiler(statement, bindparams, engine=self, use_ansi=self._use_ansi, **kwargs) + return FBCompiler(statement, bindparams, **kwargs) def schemagenerator(self, **params): return FBSchemaGenerator(self, **params) @@ -113,7 +92,48 @@ def defaultrunner(self, proxy): return FBDefaultRunner(self, proxy) - def reflecttable(self, table): +class FireBirdDialect(ansisql.ANSIDialect): + def __init__(self, module = None, **params): + self.module = module or dbmodule + self.opts = {} + ansisql.ANSIDialect.__init__(self, **params) + + def create_connect_args(self, url): +# self.opts = url.translate_connect_args(['host', 'database', 'user', 'password']) + opts = url.translate_connect_args(['host', 'database', 'user', 'password', 'port']) + if opts.get('port'): + opts['host'] = "%s:%s" % (opts['host'], opts['port']) + del opts['port'] + self.opts = opts + + print "opts %r" % self.opts + return ([], self.opts) + + def connect_args(self): + return make_connect_string(self.opts) + + def create_execution_context(self): + return FireBirdExecutionContext(self) + + def type_descriptor(self, typeobj): + return sqltypes.adapt_type(typeobj, colspecs) + + def supports_sane_rowcount(self): + return True + + def compiler(self, statement, bindparams, **kwargs): + return FBCompiler(self, statement, bindparams, **kwargs) + + def schemagenerator(self, *args, **kwargs): + return FBSchemaGenerator(*args, **kwargs) + + def schemadropper(self, *args, **kwargs): + return FBSchemaDropper(*args, **kwargs) + + def defaultrunner(self, engine, proxy): + return FBDefaultRunner(engine, proxy) + + def reflecttable(self, connection, table): #TODO: map these better column_func = { 14 : lambda r: String(r['FLEN']), # TEXT @@ -156,7 +176,7 @@ #import pdb;pdb.set_trace() # get all of the fields for this table - c = self.execute(tblqry, [table.name.upper()]) + c = connection.execute(tblqry, [table.name.upper()]) while True: row = c.fetchone() if not row: break @@ -174,34 +194,154 @@ def last_inserted_ids(self): return self.context.last_inserted_ids + - def pre_exec(self, proxy, compiled, parameters, **kwargs): - pass - - def _executemany(self, c, statement, parameters): - rowcount = 0 - for param in parameters: - c.execute(statement, param) - rowcount += c.rowcount - self.context.rowcount = rowcount + def do_rollback(self, connection): + connection.rollback(True) + def do_commit(self, connection): + connection.commit(True) + + def connection(self): + """returns a managed DBAPI connection from this SQLEngine's connection pool.""" + c = self._pool.connect() + c.supportsTransactions = 0 + return c + + + def dbapi(self): + return self.module + + + +#class FBSQLEngine(ansisql.ANSISQLEngine): +# def __init__(self, opts, use_ansi = True, module = None, **params): +# self._use_ansi = use_ansi +# self.opts = opts or {} +# if module is None: +# self.module = kinterbasdb +# else: +# self.module = module +# ansisql.ANSISQLEngine.__init__(self, **params) +# +# def do_commit(self, connection): +# connection.commit(True) +# +# def do_rollback(self, connection): +# connection.rollback(True) +# +# def dbapi(self): +# return self.module +# +# def connect_args(self): +# return [[], self.opts] +# +# def type_descriptor(self, typeobj): +# return sqltypes.adapt_type(typeobj, colspecs) +# +# def last_inserted_ids(self): +# return self.context.last_inserted_ids +# +# def compiler(self, statement, bindparams, **kwargs): +# return FBCompiler(statement, bindparams, engine=self, use_ansi=self._use_ansi, **kwargs) +# +# def schemagenerator(self, **params): +# return FBSchemaGenerator(self, **params) +# +# def schemadropper(self, **params): +# return FBSchemaDropper(self, **params) +# +# def defaultrunner(self, proxy): +# return FBDefaultRunner(self, proxy) +# +# def reflecttable(self, table): +# #TODO: map these better +# column_func = { +# 14 : lambda r: String(r['FLEN']), # TEXT +# 7 : lambda r: Integer(), # SHORT +# 8 : lambda r: Integer(), # LONG +# 9 : lambda r: Float(), # QUAD +# 10 : lambda r: Float(), # FLOAT +# 27 : lambda r: Double(), # DOUBLE +# 35 : lambda r: DateTime(), # TIMESTAMP +# 37 : lambda r: String(r['FLEN']), # VARYING +# 261: lambda r: TEXT(), # BLOB +# 40 : lambda r: Char(r['FLEN']), # CSTRING +# 12 : lambda r: Date(), # DATE +# 13 : lambda r: Time(), # TIME +# 16 : lambda r: 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, +# 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 +# WHERE F.RDB$SYSTEM_FLAG=0 and R.RDB$RELATION_NAME=? +# ORDER BY R.RDB$FIELD_POSITION;""" +# keyqry = """ +# SELECT RC.RDB$CONSTRAINT_TYPE KEYTYPE, +# RC.RDB$CONSTRAINT_NAME CNAME, +# RC.RDB$INDEX_NAME INAME, +# SE.RDB$FIELD_NAME SENAME, +# FROM RDB$RELATION_CONSTRAINTS RC +# LEFT JOIN RDB$INDEX_SEGMENTS SE +# ON RC.RDB$INDEX_NAME=SE.RDB$INDEX_NAME +# WHERE RC.RDB$RELATION_NAME=? AND SE.RDB$FIELD_NAME=? +# """ +# +# #import pdb;pdb.set_trace() +# # get all of the fields for this table +# c = self.execute(tblqry, [table.name.upper()]) +# while True: +# row = c.fetchone() +# if not row: break +# args = [row['FNAME']] +# kw = {} +# # get the data types and lengths +# args.append(column_func[row['FTYPE']](row)) +# +# # is it a foreign key (and what is it linked to) +# +# # is it a primary key? +# +# table.append_item(Column(*args, **kw)) +# # does the field have indexes +# +# def last_inserted_ids(self): +# return self.context.last_inserted_ids +# +# def pre_exec(self, proxy, compiled, parameters, **kwargs): +# pass +# +# def _executemany(self, c, statement, parameters): +# rowcount = 0 +# for param in parameters: +# c.execute(statement, param) +# rowcount += c.rowcount +# self.context.rowcount = rowcount + class FBCompiler(ansisql.ANSICompiler): """firebird compiler modifies the lexical structure of Select statements to work under non-ANSI configured Firebird databases, if the use_ansi flag is False.""" - def __init__(self, engine, statement, parameters, use_ansi = True, **kwargs): + def __init__(self, dialect, statement, parameters, **kwargs): self._outertable = None - self._use_ansi = use_ansi - ansisql.ANSICompiler.__init__(self, engine, statement, parameters, **kwargs) + super(FBCompiler, self).__init__(dialect, statement, parameters, **kwargs) + def visit_column(self, column): - if self._use_ansi: - return ansisql.ANSICompiler.visit_column(self, column) + return ansisql.ANSICompiler.visit_column(self, column) - if column.table is self._outertable: - self.strings[column] = "%s.%s(+)" % (column.table.name, column.name) - else: - self.strings[column] = "%s.%s" % (column.table.name, column.name) +# if column.table is self._outertable: +# self.strings[column] = "%s.%s(+)" % (column.table.name, column.name) +# else: +# self.strings[column] = "%s.%s" % (column.table.name, column.name) def visit_function(self, func): if len(func.clauses): @@ -223,6 +363,7 @@ """ called when building a SELECT statment, position is just before column list Firebird puts the limit and offset right after the select...thanks for adding the visit_select_precolumns!!!""" + result = "" if select.offset: result +=" FIRST " + select.offset if select.limit: @@ -269,3 +410,4 @@ return self.proxy("SELECT gen_id(" + seq.name + ", 1) FROM rdb$database").fetchone()[0] +dialect = FireBirdDialect