# ansisql.py # Copyright (C) 2005,2006 Michael Bayer mike_mp@zzzcomputing.com # # This module is part of SQLAlchemy and is released under # the MIT License: http://www.opensource.org/licenses/mit-license.php """defines ANSI SQL operations.""" import sqlalchemy.schema as schema from sqlalchemy.schema import * import sqlalchemy.sql as sql import sqlalchemy.engine from sqlalchemy.sql import * from sqlalchemy.util import * import string, re def engine(**params): return ANSISQLEngine(**params) class ANSISQLEngine(sqlalchemy.engine.SQLEngine): def schemagenerator(self, **params): return ANSISchemaGenerator(self, **params) def schemadropper(self, **params): return ANSISchemaDropper(self, **params) def compiler(self, statement, parameters, **kwargs): return ANSICompiler(statement, parameters, engine=self, **kwargs) def connect_args(self): return ([],{}) def dbapi(self): return None class ANSICompiler(sql.Compiled): """default implementation of Compiled, which compiles ClauseElements into ANSI-compliant SQL strings.""" def __init__(self, statement, parameters=None, typemap=None, engine=None, positional=None, paramstyle=None, **kwargs): """constructs a new ANSICompiler object. engine - SQLEngine to compile against statement - ClauseElement to be compiled parameters - optional dictionary indicating a set of bind parameters specified with this Compiled object. These parameters are the "default" key/value pairs when the Compiled is executed, and also may affect the actual compilation, as in the case of an INSERT where the actual columns inserted will correspond to the keys present in the parameters.""" sql.Compiled.__init__(self, statement, parameters, engine=engine) self.binds = {} self.froms = {} self.wheres = {} self.strings = {} self.select_stack = [] self.typemap = typemap or {} self.isinsert = False self.bindtemplate = ":%s" if engine is not None: self.paramstyle = engine.paramstyle self.positional = engine.positional else: self.positional = False self.paramstyle = 'named' def after_compile(self): # this re will search for params like :param # it has a negative lookbehind for an extra ':' so that it doesnt match # postgres '::text' tokens match = r'(?1, first_pk=column.primary_key and not first_pk)) if column.primary_key: first_pk = True # if multiple primary keys, specify it at the bottom if len(pks) > 1: self.append(", \n") self.append("\tPRIMARY KEY (%s)" % string.join([c.name for c in pks],', ')) self.append("\n)%s\n\n" % self.post_create_table(table)) self.execute() if hasattr(table, 'indexes'): for index in table.indexes: self.visit_index(index) def post_create_table(self, table): return '' def get_column_default_string(self, column): if isinstance(column.default, schema.PassiveDefault): if isinstance(column.default.arg, str): return repr(column.default.arg) else: return str(column.default.arg.compile(self.engine)) else: return None def visit_column(self, column): pass def visit_index(self, index): self.append('CREATE ') if index.unique: self.append('UNIQUE ') self.append('INDEX %s ON %s (%s)' \ % (index.name, index.table.name, string.join([c.name for c in index.columns], ', '))) self.execute() class ANSISchemaDropper(sqlalchemy.engine.SchemaIterator): def visit_index(self, index): self.append("\nDROP INDEX " + index.name) self.execute() def visit_table(self, table): # NOTE: indexes on the table will be automatically dropped, so # no need to drop them individually self.append("\nDROP TABLE " + table.fullname) self.execute() class ANSIDefaultRunner(sqlalchemy.engine.DefaultRunner): pass