morro
This commit is contained in:
306
sqlalchemy/dialects/informix/base.py
Normal file
306
sqlalchemy/dialects/informix/base.py
Normal file
@@ -0,0 +1,306 @@
|
||||
# informix.py
|
||||
# Copyright (C) 2005,2006, 2007, 2008, 2009, 2010 Michael Bayer mike_mp@zzzcomputing.com
|
||||
#
|
||||
# coding: gbk
|
||||
#
|
||||
# This module is part of SQLAlchemy and is released under
|
||||
# the MIT License: http://www.opensource.org/licenses/mit-license.php
|
||||
"""Support for the Informix database.
|
||||
|
||||
This dialect is *not* tested on SQLAlchemy 0.6.
|
||||
|
||||
|
||||
"""
|
||||
|
||||
|
||||
import datetime
|
||||
|
||||
from sqlalchemy import sql, schema, exc, pool, util
|
||||
from sqlalchemy.sql import compiler
|
||||
from sqlalchemy.engine import default, reflection
|
||||
from sqlalchemy import types as sqltypes
|
||||
|
||||
|
||||
class InfoDateTime(sqltypes.DateTime):
|
||||
def bind_processor(self, dialect):
|
||||
def process(value):
|
||||
if value is not None:
|
||||
if value.microsecond:
|
||||
value = value.replace(microsecond=0)
|
||||
return value
|
||||
return process
|
||||
|
||||
class InfoTime(sqltypes.Time):
|
||||
def bind_processor(self, dialect):
|
||||
def process(value):
|
||||
if value is not None:
|
||||
if value.microsecond:
|
||||
value = value.replace(microsecond=0)
|
||||
return value
|
||||
return process
|
||||
|
||||
def result_processor(self, dialect, coltype):
|
||||
def process(value):
|
||||
if isinstance(value, datetime.datetime):
|
||||
return value.time()
|
||||
else:
|
||||
return value
|
||||
return process
|
||||
|
||||
|
||||
colspecs = {
|
||||
sqltypes.DateTime : InfoDateTime,
|
||||
sqltypes.Time: InfoTime,
|
||||
}
|
||||
|
||||
|
||||
ischema_names = {
|
||||
0 : sqltypes.CHAR, # CHAR
|
||||
1 : sqltypes.SMALLINT, # SMALLINT
|
||||
2 : sqltypes.INTEGER, # INT
|
||||
3 : sqltypes.FLOAT, # Float
|
||||
3 : sqltypes.Float, # SmallFloat
|
||||
5 : sqltypes.DECIMAL, # DECIMAL
|
||||
6 : sqltypes.Integer, # Serial
|
||||
7 : sqltypes.DATE, # DATE
|
||||
8 : sqltypes.Numeric, # MONEY
|
||||
10 : sqltypes.DATETIME, # DATETIME
|
||||
11 : sqltypes.LargeBinary, # BYTE
|
||||
12 : sqltypes.TEXT, # TEXT
|
||||
13 : sqltypes.VARCHAR, # VARCHAR
|
||||
15 : sqltypes.NCHAR, # NCHAR
|
||||
16 : sqltypes.NVARCHAR, # NVARCHAR
|
||||
17 : sqltypes.Integer, # INT8
|
||||
18 : sqltypes.Integer, # Serial8
|
||||
43 : sqltypes.String, # LVARCHAR
|
||||
-1 : sqltypes.BLOB, # BLOB
|
||||
-1 : sqltypes.CLOB, # CLOB
|
||||
}
|
||||
|
||||
|
||||
class InfoTypeCompiler(compiler.GenericTypeCompiler):
|
||||
def visit_DATETIME(self, type_):
|
||||
return "DATETIME YEAR TO SECOND"
|
||||
|
||||
def visit_TIME(self, type_):
|
||||
return "DATETIME HOUR TO SECOND"
|
||||
|
||||
def visit_large_binary(self, type_):
|
||||
return "BYTE"
|
||||
|
||||
def visit_boolean(self, type_):
|
||||
return "SMALLINT"
|
||||
|
||||
class InfoSQLCompiler(compiler.SQLCompiler):
|
||||
|
||||
def default_from(self):
|
||||
return " from systables where tabname = 'systables' "
|
||||
|
||||
def get_select_precolumns(self, select):
|
||||
s = select._distinct and "DISTINCT " or ""
|
||||
# only has limit
|
||||
if select._limit:
|
||||
s += " FIRST %s " % select._limit
|
||||
else:
|
||||
s += ""
|
||||
return s
|
||||
|
||||
def visit_select(self, select):
|
||||
# the column in order by clause must in select too
|
||||
|
||||
def __label(c):
|
||||
try:
|
||||
return c._label.lower()
|
||||
except:
|
||||
return ''
|
||||
|
||||
# TODO: dont modify the original select, generate a new one
|
||||
a = [__label(c) for c in select._raw_columns]
|
||||
for c in select._order_by_clause.clauses:
|
||||
if __label(c) not in a:
|
||||
select.append_column(c)
|
||||
|
||||
return compiler.SQLCompiler.visit_select(self, select)
|
||||
|
||||
def limit_clause(self, select):
|
||||
if select._offset is not None and select._offset > 0:
|
||||
raise NotImplementedError("Informix does not support OFFSET")
|
||||
return ""
|
||||
|
||||
def visit_function(self, func):
|
||||
if func.name.lower() == 'current_date':
|
||||
return "today"
|
||||
elif func.name.lower() == 'current_time':
|
||||
return "CURRENT HOUR TO SECOND"
|
||||
elif func.name.lower() in ('current_timestamp', 'now'):
|
||||
return "CURRENT YEAR TO SECOND"
|
||||
else:
|
||||
return compiler.SQLCompiler.visit_function(self, func)
|
||||
|
||||
|
||||
class InfoDDLCompiler(compiler.DDLCompiler):
|
||||
def get_column_specification(self, column, first_pk=False):
|
||||
colspec = self.preparer.format_column(column)
|
||||
if column.primary_key and len(column.foreign_keys)==0 and column.autoincrement and \
|
||||
isinstance(column.type, sqltypes.Integer) and first_pk:
|
||||
colspec += " SERIAL"
|
||||
else:
|
||||
colspec += " " + self.dialect.type_compiler.process(column.type)
|
||||
default = self.get_column_default_string(column)
|
||||
if default is not None:
|
||||
colspec += " DEFAULT " + default
|
||||
|
||||
if not column.nullable:
|
||||
colspec += " NOT NULL"
|
||||
|
||||
return colspec
|
||||
|
||||
|
||||
class InfoIdentifierPreparer(compiler.IdentifierPreparer):
|
||||
def __init__(self, dialect):
|
||||
super(InfoIdentifierPreparer, self).__init__(dialect, initial_quote="'")
|
||||
|
||||
def format_constraint(self, constraint):
|
||||
# informix doesnt support names for constraints
|
||||
return ''
|
||||
|
||||
def _requires_quotes(self, value):
|
||||
return False
|
||||
|
||||
class InformixDialect(default.DefaultDialect):
|
||||
name = 'informix'
|
||||
|
||||
max_identifier_length = 128 # adjusts at runtime based on server version
|
||||
|
||||
type_compiler = InfoTypeCompiler
|
||||
statement_compiler = InfoSQLCompiler
|
||||
ddl_compiler = InfoDDLCompiler
|
||||
preparer = InfoIdentifierPreparer
|
||||
colspecs = colspecs
|
||||
ischema_names = ischema_names
|
||||
|
||||
def initialize(self, connection):
|
||||
super(InformixDialect, self).initialize(connection)
|
||||
|
||||
# http://www.querix.com/support/knowledge-base/error_number_message/error_200
|
||||
if self.server_version_info < (9, 2):
|
||||
self.max_identifier_length = 18
|
||||
else:
|
||||
self.max_identifier_length = 128
|
||||
|
||||
def do_begin(self, connect):
|
||||
cu = connect.cursor()
|
||||
cu.execute('SET LOCK MODE TO WAIT')
|
||||
#cu.execute('SET ISOLATION TO REPEATABLE READ')
|
||||
|
||||
@reflection.cache
|
||||
def get_table_names(self, connection, schema=None, **kw):
|
||||
s = "select tabname from systables"
|
||||
return [row[0] for row in connection.execute(s)]
|
||||
|
||||
def has_table(self, connection, table_name, schema=None):
|
||||
cursor = connection.execute("""select tabname from systables where tabname=?""", table_name.lower())
|
||||
return cursor.first() is not None
|
||||
|
||||
@reflection.cache
|
||||
def get_columns(self, connection, table_name, schema=None, **kw):
|
||||
c = connection.execute ("""select colname , coltype , collength , t3.default , t1.colno from
|
||||
syscolumns as t1 , systables as t2 , OUTER sysdefaults as t3
|
||||
where t1.tabid = t2.tabid and t2.tabname=?
|
||||
and t3.tabid = t2.tabid and t3.colno = t1.colno
|
||||
order by t1.colno""", table.name.lower())
|
||||
columns = []
|
||||
for name, colattr, collength, default, colno in rows:
|
||||
name = name.lower()
|
||||
if include_columns and name not in include_columns:
|
||||
continue
|
||||
|
||||
# in 7.31, coltype = 0x000
|
||||
# ^^-- column type
|
||||
# ^-- 1 not null, 0 null
|
||||
nullable, coltype = divmod(colattr, 256)
|
||||
if coltype not in (0, 13) and default:
|
||||
default = default.split()[-1]
|
||||
|
||||
if coltype == 0 or coltype == 13: # char, varchar
|
||||
coltype = ischema_names[coltype](collength)
|
||||
if default:
|
||||
default = "'%s'" % default
|
||||
elif coltype == 5: # decimal
|
||||
precision, scale = (collength & 0xFF00) >> 8, collength & 0xFF
|
||||
if scale == 255:
|
||||
scale = 0
|
||||
coltype = sqltypes.Numeric(precision, scale)
|
||||
else:
|
||||
try:
|
||||
coltype = ischema_names[coltype]
|
||||
except KeyError:
|
||||
util.warn("Did not recognize type '%s' of column '%s'" %
|
||||
(coltype, name))
|
||||
coltype = sqltypes.NULLTYPE
|
||||
|
||||
# TODO: nullability ??
|
||||
nullable = True
|
||||
|
||||
column_info = dict(name=name, type=coltype, nullable=nullable,
|
||||
default=default)
|
||||
columns.append(column_info)
|
||||
return columns
|
||||
|
||||
@reflection.cache
|
||||
def get_foreign_keys(self, connection, table_name, schema=None, **kw):
|
||||
# FK
|
||||
c = connection.execute("""select t1.constrname as cons_name , t1.constrtype as cons_type ,
|
||||
t4.colname as local_column , t7.tabname as remote_table ,
|
||||
t6.colname as remote_column
|
||||
from sysconstraints as t1 , systables as t2 ,
|
||||
sysindexes as t3 , syscolumns as t4 ,
|
||||
sysreferences as t5 , syscolumns as t6 , systables as t7 ,
|
||||
sysconstraints as t8 , sysindexes as t9
|
||||
where t1.tabid = t2.tabid and t2.tabname=? and t1.constrtype = 'R'
|
||||
and t3.tabid = t2.tabid and t3.idxname = t1.idxname
|
||||
and t4.tabid = t2.tabid and t4.colno = t3.part1
|
||||
and t5.constrid = t1.constrid and t8.constrid = t5.primary
|
||||
and t6.tabid = t5.ptabid and t6.colno = t9.part1 and t9.idxname = t8.idxname
|
||||
and t7.tabid = t5.ptabid""", table.name.lower())
|
||||
|
||||
|
||||
def fkey_rec():
|
||||
return {
|
||||
'name' : None,
|
||||
'constrained_columns' : [],
|
||||
'referred_schema' : None,
|
||||
'referred_table' : None,
|
||||
'referred_columns' : []
|
||||
}
|
||||
|
||||
fkeys = util.defaultdict(fkey_rec)
|
||||
|
||||
for cons_name, cons_type, local_column, remote_table, remote_column in rows:
|
||||
|
||||
rec = fkeys[cons_name]
|
||||
rec['name'] = cons_name
|
||||
local_cols, remote_cols = rec['constrained_columns'], rec['referred_columns']
|
||||
|
||||
if not rec['referred_table']:
|
||||
rec['referred_table'] = remote_table
|
||||
|
||||
local_cols.append(local_column)
|
||||
remote_cols.append(remote_column)
|
||||
|
||||
return fkeys.values()
|
||||
|
||||
@reflection.cache
|
||||
def get_primary_keys(self, connection, table_name, schema=None, **kw):
|
||||
c = connection.execute("""select t4.colname as local_column
|
||||
from sysconstraints as t1 , systables as t2 ,
|
||||
sysindexes as t3 , syscolumns as t4
|
||||
where t1.tabid = t2.tabid and t2.tabname=? and t1.constrtype = 'P'
|
||||
and t3.tabid = t2.tabid and t3.idxname = t1.idxname
|
||||
and t4.tabid = t2.tabid and t4.colno = t3.part1""", table.name.lower())
|
||||
return [r[0] for r in c.fetchall()]
|
||||
|
||||
@reflection.cache
|
||||
def get_indexes(self, connection, table_name, schema, **kw):
|
||||
# TODO
|
||||
return []
|
Reference in New Issue
Block a user