[Sqlalchemy-commits] [1513] sqlalchemy/branches/schema/lib/sqlalchemy/databases/mssql.py: 0.2 update
Brought to you by:
zzzeek
From: <co...@sq...> - 2006-05-25 21:14:14
|
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd"> <html xmlns="http://www.w3.org/1999/xhtml"> <head><style type="text/css"><!-- #msg dl { border: 1px #006 solid; background: #369; padding: 6px; color: #fff; } #msg dt { float: left; width: 6em; font-weight: bold; } #msg dt:after { content:':';} #msg dl, #msg dt, #msg ul, #msg li { font-family: verdana,arial,helvetica,sans-serif; font-size: 10pt; } #msg dl a { font-weight: bold} #msg dl a:link { color:#fc3; } #msg dl a:active { color:#ff0; } #msg dl a:visited { color:#cc6; } h3 { font-family: verdana,arial,helvetica,sans-serif; font-size: 10pt; font-weight: bold; } #msg pre { overflow: auto; background: #ffc; border: 1px #fc0 solid; padding: 6px; } #msg ul, pre { overflow: auto; } #patch { width: 100%; } #patch h4 {font-family: verdana,arial,helvetica,sans-serif;font-size:10pt;padding:8px;background:#369;color:#fff;margin:0;} #patch .propset h4, #patch .binary h4 {margin:0;} #patch pre {padding:0;line-height:1.2em;margin:0;} #patch .diff {width:100%;background:#eee;padding: 0 0 10px 0;overflow:auto;} #patch .propset .diff, #patch .binary .diff {padding:10px 0;} #patch span {display:block;padding:0 10px;} #patch .modfile, #patch .addfile, #patch .delfile, #patch .propset, #patch .binary, #patch .copfile {border:1px solid #ccc;margin:10px 0;} #patch ins {background:#dfd;text-decoration:none;display:block;padding:0 10px;} #patch del {background:#fdd;text-decoration:none;display:block;padding:0 10px;} #patch .lines, .info {color:#888;background:#fff;} --></style> <title>[1513] sqlalchemy/branches/schema/lib/sqlalchemy/databases/mssql.py: 0.2 update</title> </head> <body> <div id="msg"> <dl> <dt>Revision</dt> <dd>1513</dd> <dt>Author</dt> <dd>ram</dd> <dt>Date</dt> <dd>2006-05-25 16:14:00 -0500 (Thu, 25 May 2006)</dd> </dl> <h3>Log Message</h3> <pre>0.2 update</pre> <h3>Modified Paths</h3> <ul> <li><a href="#sqlalchemybranchesschemalibsqlalchemydatabasesmssqlpy">sqlalchemy/branches/schema/lib/sqlalchemy/databases/mssql.py</a></li> </ul> </div> <div id="patch"> <h3>Diff</h3> <a id="sqlalchemybranchesschemalibsqlalchemydatabasesmssqlpy"></a> <div class="modfile"><h4>Modified: sqlalchemy/branches/schema/lib/sqlalchemy/databases/mssql.py (1512 => 1513)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/branches/schema/lib/sqlalchemy/databases/mssql.py 2006-05-25 18:30:36 UTC (rev 1512) +++ sqlalchemy/branches/schema/lib/sqlalchemy/databases/mssql.py 2006-05-25 21:14:00 UTC (rev 1513) </span><span class="lines">@@ -38,10 +38,11 @@ </span><span class="cx"> </span><span class="cx"> import sqlalchemy.sql as sql </span><span class="cx"> import sqlalchemy.engine as engine </span><ins>+import sqlalchemy.engine.default as default </ins><span class="cx"> import sqlalchemy.schema as schema </span><span class="cx"> import sqlalchemy.ansisql as ansisql </span><span class="cx"> import sqlalchemy.types as sqltypes </span><del>-from sqlalchemy import * </del><ins>+import sqlalchemy.exceptions as exceptions </ins><span class="cx"> </span><span class="cx"> try: </span><span class="cx"> import adodbapi as dbmodule </span><span class="lines">@@ -65,10 +66,10 @@ </span><span class="cx"> make_connect_string = lambda keys: [[],{}] </span><span class="cx"> </span><span class="cx"> class MSNumeric(sqltypes.Numeric): </span><del>- def convert_result_value(self, value, engine): </del><ins>+ def convert_result_value(self, value, dialect): </ins><span class="cx"> return value </span><span class="cx"> </span><del>- def convert_bind_param(self, value, engine): </del><ins>+ def convert_bind_param(self, value, dialect): </ins><span class="cx"> if value is None: </span><span class="cx"> # Not sure that this exception is needed </span><span class="cx"> return value </span><span class="lines">@@ -81,7 +82,7 @@ </span><span class="cx"> class MSFloat(sqltypes.Float): </span><span class="cx"> def get_col_spec(self): </span><span class="cx"> return "FLOAT(%(precision)s)" % {'precision': self.precision} </span><del>- def convert_bind_param(self, value, engine): </del><ins>+ def convert_bind_param(self, value, dialect): </ins><span class="cx"> """By converting to string, we can use Decimal types round-trip.""" </span><span class="cx"> return str(value) </span><span class="cx"> </span><span class="lines">@@ -97,13 +98,14 @@ </span><span class="cx"> def get_col_spec(self): </span><span class="cx"> return "DATETIME" </span><span class="cx"> </span><del>- def convert_bind_param(self, value, engine): </del><ins>+ def convert_bind_param(self, value, dialect): </ins><span class="cx"> if hasattr(value, "isoformat"): </span><del>- return value.isoformat(' ') </del><ins>+ #return value.isoformat(' ') + return value.strftime('%Y-%m-%d %H:%M:%S') # isoformat() bings on apodbapi -- reported/suggested by Peter Buschman </ins><span class="cx"> else: </span><span class="cx"> return value </span><span class="cx"> </span><del>- def convert_result_value(self, value, engine): </del><ins>+ def convert_result_value(self, value, dialect): </ins><span class="cx"> # adodbapi will return datetimes with empty time values as datetime.date() objects. Promote them back to full datetime.datetime() </span><span class="cx"> if value and not hasattr(value, 'second'): </span><span class="cx"> return datetime.datetime(value.year, value.month, value.day) </span><span class="lines">@@ -113,12 +115,12 @@ </span><span class="cx"> def get_col_spec(self): </span><span class="cx"> return "SMALLDATETIME" </span><span class="cx"> </span><del>- def convert_bind_param(self, value, engine): </del><ins>+ def convert_bind_param(self, value, dialect): </ins><span class="cx"> if value and hasattr(value, "isoformat"): </span><span class="cx"> return value.isoformat() </span><span class="cx"> return value </span><span class="cx"> </span><del>- def convert_result_value(self, value, engine): </del><ins>+ def convert_result_value(self, value, dialect): </ins><span class="cx"> # pymssql will return SMALLDATETIME values as datetime.datetime(), truncate it back to datetime.date() </span><span class="cx"> if value and hasattr(value, 'second'): </span><span class="cx"> return value.date() </span><span class="lines">@@ -184,16 +186,49 @@ </span><span class="cx"> ('host',"Hostname", None), </span><span class="cx"> ]} </span><span class="cx"> </span><del>-class MSSQLEngine(ansisql.ANSISQLEngine): - def __init__(self, opts, module = None, **params): - if module is None: - self.module = dbmodule - self.opts = opts or {} - ansisql.ANSISQLEngine.__init__(self, **params) </del><ins>+class MSSQLExecutionContext(default.DefaultExecutionContext): + def pre_exec(self, engine, proxy, compiled, parameters, **kwargs): + """ MS-SQL has a special mode for inserting non-NULL values into IDENTITY columns. Activate it if needed. """ + if getattr(compiled, "isinsert", False): + self.IINSERT = False + self.HASIDENT = False + for c in compiled.statement.table.c: + if hasattr(c,'sequence'): + self.HASIDENT = True + if parameters.has_key(c.name): + self.IINSERT = True + break + if self.IINSERT: + proxy("SET IDENTITY_INSERT %s ON" % compiled.statement.table.name) </ins><span class="cx"> </span><ins>+ def post_exec(self, engine, proxy, compiled, parameters, **kwargs): + """ Turn off the INDENTITY_INSERT mode if it's been activated, and fetch recently inserted IDENTIFY values (works only for one column) """ + if getattr(compiled, "isinsert", False): + if self.IINSERT: + proxy("SET IDENTITY_INSERT %s OFF" % compiled.statement.table.name) + self.IINSERT = False + elif self.HASIDENT: + cursor = proxy("SELECT @@IDENTITY AS lastrowid") + row = cursor.fetchone() + self.last_inserted_ids = [row[0]] + self.HASIDENT = False + +class MSSQLDialect(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', 'port']) + return ([], self.opts) + </ins><span class="cx"> def connect_args(self): </span><span class="cx"> return make_connect_string(self.opts) </span><span class="cx"> </span><ins>+ def create_execution_context(self): + return MSSQLExecutionContext(self) + </ins><span class="cx"> def type_descriptor(self, typeobj): </span><span class="cx"> return sqltypes.adapt_type(typeobj, colspecs) </span><span class="cx"> </span><span class="lines">@@ -204,14 +239,17 @@ </span><span class="cx"> return True </span><span class="cx"> </span><span class="cx"> def compiler(self, statement, bindparams, **kwargs): </span><del>- return MSSQLCompiler(statement, bindparams, engine=self, **kwargs) </del><ins>+ return MSSQLCompiler(self, statement, bindparams, **kwargs) </ins><span class="cx"> </span><del>- def schemagenerator(self, **params): - return MSSQLSchemaGenerator(self, **params) </del><ins>+ def schemagenerator(self, *args, **kwargs): + return MSSQLSchemaGenerator(*args, **kwargs) </ins><span class="cx"> </span><del>- def schemadropper(self, **params): - return MSSQLSchemaDropper(self, **params) </del><ins>+ def schemadropper(self, *args, **kwargs): + return MSSQLSchemaDropper(*args, **kwargs) </ins><span class="cx"> </span><ins>+ def defaultrunner(self, engine, proxy): + return MSSQLDefaultRunner(engine, proxy) + </ins><span class="cx"> def get_default_schema_name(self): </span><span class="cx"> return "dbo" </span><span class="cx"> </span><span class="lines">@@ -229,10 +267,10 @@ </span><span class="cx"> self.context.rowcount = c.rowcount </span><span class="cx"> c.DBPROP_COMMITPRESERVE = "Y" </span><span class="cx"> except Exception, e: </span><del>- # del c.parent # Close the Parent Connection, delete it from the pool </del><ins>+ # del c.parent # Close the Parent Connection, delete it from the pool columns = ischema.columns.toengine(self) + </ins><span class="cx"> raise exceptions.SQLError(statement, parameters, e) </span><span class="cx"> </span><del>- </del><span class="cx"> def do_rollback(self, connection): </span><span class="cx"> """implementations might want to put logic here for turning autocommit on/off, etc.""" </span><span class="cx"> if do_commit: </span><span class="lines">@@ -288,36 +326,11 @@ </span><span class="cx"> c.supportsTransactions = 0 </span><span class="cx"> return c </span><span class="cx"> </span><del>- def pre_exec(self, proxy, compiled, parameters, **kwargs): - """ MS-SQL has a special mode for inserting non-NULL values into IDENTITY columns. Activate it if needed. """ - if getattr(compiled, "isinsert", False): - self.context.IINSERT = False - self.context.HASIDENT = False - for c in compiled.statement.table.c: - if hasattr(c,'sequence'): - self.context.HASIDENT = True - if parameters.has_key(c.name): - self.context.IINSERT = True - break - if self.context.IINSERT: - proxy("SET IDENTITY_INSERT %s ON" % compiled.statement.table.name) - - def post_exec(self, proxy, compiled, parameters, **kwargs): - """ Turn off the INDENTITY_INSERT mode if it's been activated, and fetch recently inserted IDENTIFY values (works only for one column) """ - if getattr(compiled, "isinsert", False): - if self.context.IINSERT: - proxy("SET IDENTITY_INSERT %s OFF" % compiled.statement.table.name) - self.context.IINSERT = False - elif self.context.HASIDENT: - cursor = proxy("SELECT @@IDENTITY AS lastrowid") - row = cursor.fetchone() - self.context.last_inserted_ids = [row[0]] - self.context.HASIDENT = False - </del><ins>+ </ins><span class="cx"> def dbapi(self): </span><span class="cx"> return self.module </span><span class="cx"> </span><del>- def reflecttable(self, table): </del><ins>+ def reflecttable(self, connection, table): </ins><span class="cx"> import sqlalchemy.databases.information_schema as ischema </span><span class="cx"> </span><span class="cx"> # Get base columns </span><span class="lines">@@ -326,12 +339,12 @@ </span><span class="cx"> else: </span><span class="cx"> current_schema = self.get_default_schema_name() </span><span class="cx"> </span><del>- columns = ischema.gen_columns.toengine(self) </del><ins>+ columns = ischema.columns.toengine(self) </ins><span class="cx"> s = select([columns], </span><span class="cx"> current_schema and sql.and_(columns.c.table_name==table.name, columns.c.table_schema==current_schema) or columns.c.table_name==table.name, </span><span class="cx"> order_by=[columns.c.ordinal_position]) </span><span class="cx"> </span><del>- c = s.execute() </del><ins>+ c = connection.execute(s) </ins><span class="cx"> while True: </span><span class="cx"> row = c.fetchone() </span><span class="cx"> if row is None: </span><span class="lines">@@ -352,7 +365,6 @@ </span><span class="cx"> if a is not None: </span><span class="cx"> args.append(a) </span><span class="cx"> coltype = ischema_names[type] </span><del>- </del><span class="cx"> coltype = coltype(*args) </span><span class="cx"> colargs= [] </span><span class="cx"> if default is not None: </span><span class="lines">@@ -363,7 +375,9 @@ </span><span class="cx"> </span><span class="cx"> # We also run an sp_columns to check for identity columns: </span><span class="cx"> # FIXME: note that this only fetches the existence of an identity column, not it's properties like (seed, increment) </span><del>- cursor = table.engine.execute("sp_columns " + table.name, {}) </del><ins>+ # also, add a check to make sure we specify the schema name of the table + # cursor = table.engine.execute("sp_columns " + table.name, {}) + cursor = connection.execute("sp_columns " + table.name) </ins><span class="cx"> while True: </span><span class="cx"> row = cursor.fetchone() </span><span class="cx"> if row is None: </span><span class="lines">@@ -375,10 +389,10 @@ </span><span class="cx"> ic.sequence = schema.Sequence(ic.name + '_identity') </span><span class="cx"> </span><span class="cx"> # Add constraints </span><del>- RR = ischema.gen_ref_constraints.toengine(self) #information_schema.referential_constraints - TC = ischema.gen_constraints.toengine(self) #information_schema.table_constraints - C = ischema.gen_column_constraints.toengine(self).alias('C') #information_schema.constraint_column_usage: the constrained column - R = ischema.gen_column_constraints.toengine(self).alias('R') #information_schema.constraint_column_usage: the referenced column </del><ins>+ RR = ischema.ref_constraints.toengine(self) #information_schema.referential_constraints + TC = ischema.constraints.toengine(self) #information_schema.table_constraints + C = ischema.column_constraints.toengine(self).alias('C') #information_schema.constraint_column_usage: the constrained column + R = ischema.column_constraints.toengine(self).alias('R') #information_schema.constraint_column_usage: the referenced column </ins><span class="cx"> </span><span class="cx"> fromjoin = TC.join(RR, RR.c.constraint_name == TC.c.constraint_name).join(C, C.c.constraint_name == RR.c.constraint_name) </span><span class="cx"> fromjoin = fromjoin.join(R, R.c.constraint_name == RR.c.unique_constraint_name) </span><span class="lines">@@ -389,7 +403,7 @@ </span><span class="cx"> from_obj = [fromjoin] </span><span class="cx"> ) </span><span class="cx"> </span><del>- c = s.execute() </del><ins>+ c = connection.execute(s) </ins><span class="cx"> </span><span class="cx"> while True: </span><span class="cx"> row = c.fetchone() </span><span class="lines">@@ -412,8 +426,8 @@ </span><span class="cx"> </span><span class="cx"> </span><span class="cx"> class MSSQLCompiler(ansisql.ANSICompiler): </span><del>- def __init__(self, *args, **kwargs): - super(MSSQLCompiler, self).__init__(*args, **kwargs) </del><ins>+ def __init__(self, dialect, statement, parameters, **kwargs): + super(MSSQLCompiler, self).__init__(dialect, statement, parameters, **kwargs) </ins><span class="cx"> self.tablealiases = {} </span><span class="cx"> </span><span class="cx"> def visit_select_precolumns(self, select): </span><span class="lines">@@ -463,7 +477,7 @@ </span><span class="cx"> colspec = column.name + " " + column.type.engine_impl(self.engine).get_col_spec() </span><span class="cx"> </span><span class="cx"> # install a IDENTITY Sequence if we have an implicit IDENTITY column </span><del>- if column.primary_key and isinstance(column.type, types.Integer): </del><ins>+ if column.primary_key and isinstance(column.type, sqltypes.Integer): </ins><span class="cx"> if column.default is None or (isinstance(column.default, schema.Sequence) and column.default.optional): </span><span class="cx"> column.sequence = schema.Sequence(column.name + '_seq') </span><span class="cx"> </span><span class="lines">@@ -490,3 +504,8 @@ </span><span class="cx"> def visit_index(self, index): </span><span class="cx"> self.append("\nDROP INDEX " + index.table.name + "." + index.name) </span><span class="cx"> self.execute() </span><ins>+ +class MSSQLDefaultRunner(ansisql.ANSIDefaultRunner): + pass + +dialect = MSSQLDialect </ins></span></pre> </div> </div> </body> </html> |