diff options
author | Mike Bayer <mike_mp@zzzcomputing.com> | 2006-03-04 18:53:35 +0000 |
---|---|---|
committer | Mike Bayer <mike_mp@zzzcomputing.com> | 2006-03-04 18:53:35 +0000 |
commit | 7ad8cc9420f796dfcafbc84dc06453fc3eb51abe (patch) | |
tree | 1be784c617d361f40e29d1b5ceb2b7ef5f8e1893 /lib/sqlalchemy | |
parent | e9999c74e6d476685f5c0ca741d86be3ef5050a0 (diff) | |
download | sqlalchemy-7ad8cc9420f796dfcafbc84dc06453fc3eb51abe.tar.gz |
removed the dependency of ANSICompiler on SQLEngine. you can now make ANSICompilers and compile SQL with no engine at all.
Diffstat (limited to 'lib/sqlalchemy')
-rw-r--r-- | lib/sqlalchemy/ansisql.py | 54 | ||||
-rw-r--r-- | lib/sqlalchemy/databases/firebird.py | 2 | ||||
-rw-r--r-- | lib/sqlalchemy/databases/information_schema.py | 2 | ||||
-rw-r--r-- | lib/sqlalchemy/databases/mysql.py | 2 | ||||
-rw-r--r-- | lib/sqlalchemy/databases/oracle.py | 4 | ||||
-rw-r--r-- | lib/sqlalchemy/databases/postgres.py | 2 | ||||
-rw-r--r-- | lib/sqlalchemy/databases/sqlite.py | 2 | ||||
-rw-r--r-- | lib/sqlalchemy/engine.py | 6 | ||||
-rw-r--r-- | lib/sqlalchemy/sql.py | 49 |
9 files changed, 71 insertions, 52 deletions
diff --git a/lib/sqlalchemy/ansisql.py b/lib/sqlalchemy/ansisql.py index 1b600a4a8..7c0002aa5 100644 --- a/lib/sqlalchemy/ansisql.py +++ b/lib/sqlalchemy/ansisql.py @@ -27,7 +27,7 @@ class ANSISQLEngine(sqlalchemy.engine.SQLEngine): return ANSISchemaDropper(self, **params) def compiler(self, statement, parameters, **kwargs): - return ANSICompiler(self, statement, parameters, **kwargs) + return ANSICompiler(statement, parameters, engine=self, **kwargs) def connect_args(self): return ([],{}) @@ -37,7 +37,7 @@ class ANSISQLEngine(sqlalchemy.engine.SQLEngine): class ANSICompiler(sql.Compiled): """default implementation of Compiled, which compiles ClauseElements into ANSI-compliant SQL strings.""" - def __init__(self, engine, statement, parameters=None, typemap=None, **kwargs): + 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 @@ -49,7 +49,7 @@ class ANSICompiler(sql.Compiled): 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, engine, statement, parameters) + sql.Compiled.__init__(self, statement, parameters, engine=engine) self.binds = {} self.froms = {} self.wheres = {} @@ -57,19 +57,31 @@ class ANSICompiler(sql.Compiled): 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): - if self.engine.positional: + # 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'(?<!:):([\w_]+)' + if self.paramstyle=='pyformat': + self.strings[self.statement] = re.sub(match, lambda m:'%(' + m.group(1) +')s', self.strings[self.statement]) + elif self.positional: self.positiontup = [] - match = r'%\(([\w_]+)\)s' params = re.finditer(match, self.strings[self.statement]) for p in params: self.positiontup.append(p.group(1)) - if self.engine.paramstyle=='qmark': + if self.paramstyle=='qmark': self.strings[self.statement] = re.sub(match, '?', self.strings[self.statement]) - elif self.engine.paramstyle=='format': + elif self.paramstyle=='format': self.strings[self.statement] = re.sub(match, '%s', self.strings[self.statement]) - elif self.engine.paramstyle=='numeric': + elif self.paramstyle=='numeric': i = [0] def getnum(x): i[0] += 1 @@ -104,28 +116,33 @@ class ANSICompiler(sql.Compiled): bindparams = {} bindparams.update(params) - if self.engine.positional: + if self.positional: d = OrderedDict() for k in self.positiontup: b = self.binds[k] - d[k] = b.typeprocess(b.value, self.engine) + if self.engine is not None: + d[k] = b.typeprocess(b.value, self.engine) + else: + d[k] = b.value else: d = {} for b in self.binds.values(): - d[b.key] = b.typeprocess(b.value, self.engine) + if self.engine is not None: + d[b.key] = b.typeprocess(b.value, self.engine) + else: + d[b.key] = b.value for key, value in bindparams.iteritems(): try: b = self.binds[key] except KeyError: continue - d[b.key] = b.typeprocess(value, self.engine) + if self.engine is not None: + d[b.key] = b.typeprocess(value, self.engine) + else: + d[b.key] = value return d - if self.engine.positional: - return d.values() - else: - return d def get_named_params(self, parameters): """given the results of the get_params method, returns the parameters @@ -133,8 +150,7 @@ class ANSICompiler(sql.Compiled): same dictionary. For a positional paramstyle, the given parameters are assumed to be in list format and are converted back to a dictionary. """ -# return parameters - if self.engine.positional: + if self.positional: p = {} for i in range(0, len(self.positiontup)): p[self.positiontup[i]] = parameters[i] @@ -231,7 +247,7 @@ class ANSICompiler(sql.Compiled): self.strings[bindparam] = self.bindparam_string(key) def bindparam_string(self, name): - return self.engine.bindtemplate % name + return self.bindtemplate % name def visit_alias(self, alias): self.froms[alias] = self.get_from_text(alias.original) + " AS " + alias.name diff --git a/lib/sqlalchemy/databases/firebird.py b/lib/sqlalchemy/databases/firebird.py index 250914813..4dd4aa2a6 100644 --- a/lib/sqlalchemy/databases/firebird.py +++ b/lib/sqlalchemy/databases/firebird.py @@ -102,7 +102,7 @@ class FBSQLEngine(ansisql.ANSISQLEngine): return self.context.last_inserted_ids def compiler(self, statement, bindparams, **kwargs): - return FBCompiler(self, statement, bindparams, use_ansi=self._use_ansi, **kwargs) + return FBCompiler(statement, bindparams, engine=self, use_ansi=self._use_ansi, **kwargs) def schemagenerator(self, **params): return FBSchemaGenerator(self, **params) diff --git a/lib/sqlalchemy/databases/information_schema.py b/lib/sqlalchemy/databases/information_schema.py index 825e0017a..feb3cf0c5 100644 --- a/lib/sqlalchemy/databases/information_schema.py +++ b/lib/sqlalchemy/databases/information_schema.py @@ -132,7 +132,7 @@ def reflecttable(engine, table, ischema_names, use_mysql=False): coltype = coltype(*args) colargs= [] if default is not None: - colargs.append(PassiveDefault(sql.text(default, escape=False))) + colargs.append(PassiveDefault(sql.text(default))) table.append_item(schema.Column(name, coltype, nullable=nullable, *colargs)) s = select([constraints.c.constraint_name, constraints.c.constraint_type, constraints.c.table_name, key_constraints], use_labels=True, from_obj=[constraints.join(column_constraints, column_constraints.c.constraint_name==constraints.c.constraint_name).join(key_constraints, key_constraints.c.constraint_name==column_constraints.c.constraint_name)]) diff --git a/lib/sqlalchemy/databases/mysql.py b/lib/sqlalchemy/databases/mysql.py index b29078ca2..8e305a697 100644 --- a/lib/sqlalchemy/databases/mysql.py +++ b/lib/sqlalchemy/databases/mysql.py @@ -132,7 +132,7 @@ class MySQLEngine(ansisql.ANSISQLEngine): return False def compiler(self, statement, bindparams, **kwargs): - return MySQLCompiler(self, statement, bindparams, **kwargs) + return MySQLCompiler(statement, bindparams, engine=self, **kwargs) def schemagenerator(self, **params): return MySQLSchemaGenerator(self, **params) diff --git a/lib/sqlalchemy/databases/oracle.py b/lib/sqlalchemy/databases/oracle.py index b26298c77..6f5e98265 100644 --- a/lib/sqlalchemy/databases/oracle.py +++ b/lib/sqlalchemy/databases/oracle.py @@ -151,7 +151,7 @@ class OracleSQLEngine(ansisql.ANSISQLEngine): colargs = [] if default is not None: - colargs.append(PassiveDefault(sql.text(default, escape=False))) + colargs.append(PassiveDefault(sql.text(default))) name = name.lower() @@ -207,7 +207,7 @@ class OracleCompiler(ansisql.ANSICompiler): def __init__(self, engine, statement, parameters, use_ansi = True, **kwargs): self._outertable = None self._use_ansi = use_ansi - ansisql.ANSICompiler.__init__(self, engine, statement, parameters, **kwargs) + ansisql.ANSICompiler.__init__(self, statement, parameters, engine=engine, **kwargs) def visit_join(self, join): if self._use_ansi: diff --git a/lib/sqlalchemy/databases/postgres.py b/lib/sqlalchemy/databases/postgres.py index 92407637f..592bac79c 100644 --- a/lib/sqlalchemy/databases/postgres.py +++ b/lib/sqlalchemy/databases/postgres.py @@ -210,7 +210,7 @@ class PGSQLEngine(ansisql.ANSISQLEngine): return sqltypes.adapt_type(typeobj, pg1_colspecs) def compiler(self, statement, bindparams, **kwargs): - return PGCompiler(self, statement, bindparams, **kwargs) + return PGCompiler(statement, bindparams, engine=self, **kwargs) def schemagenerator(self, **params): return PGSchemaGenerator(self, **params) diff --git a/lib/sqlalchemy/databases/sqlite.py b/lib/sqlalchemy/databases/sqlite.py index 2e366e432..6dc880b05 100644 --- a/lib/sqlalchemy/databases/sqlite.py +++ b/lib/sqlalchemy/databases/sqlite.py @@ -147,7 +147,7 @@ class SQLiteSQLEngine(ansisql.ANSISQLEngine): return ([self.filename], self.opts) def compiler(self, statement, bindparams, **kwargs): - return SQLiteCompiler(self, statement, bindparams, **kwargs) + return SQLiteCompiler(statement, bindparams, engine=self, **kwargs) def dbapi(self): return sqlite diff --git a/lib/sqlalchemy/engine.py b/lib/sqlalchemy/engine.py index e57cc7bc3..d07dd5734 100644 --- a/lib/sqlalchemy/engine.py +++ b/lib/sqlalchemy/engine.py @@ -227,15 +227,12 @@ class SQLEngine(schema.SchemaEngine): self._paramstyle = 'named' if self._paramstyle == 'named': - self.bindtemplate = ':%s' self.positional=False elif self._paramstyle == 'pyformat': - self.bindtemplate = "%%(%s)s" self.positional=False elif self._paramstyle == 'qmark' or self._paramstyle == 'format' or self._paramstyle == 'numeric': # for positional, use pyformat internally, ANSICompiler will convert # to appropriate character upon compilation - self.bindtemplate = "%%(%s)s" self.positional = True else: raise DBAPIError("Unsupported paramstyle '%s'" % self._paramstyle) @@ -310,8 +307,7 @@ class SQLEngine(schema.SchemaEngine): instance of this engine's SQLCompiler, compiles the ClauseElement, and returns the newly compiled object.""" compiler = self.compiler(statement, parameters, **kwargs) - statement.accept_visitor(compiler) - compiler.after_compile() + compiler.compile() return compiler def reflecttable(self, table): diff --git a/lib/sqlalchemy/sql.py b/lib/sqlalchemy/sql.py index 6b574861b..f05310e42 100644 --- a/lib/sqlalchemy/sql.py +++ b/lib/sqlalchemy/sql.py @@ -257,11 +257,9 @@ class Compiled(ClauseVisitor): object be dependent on the actual values of those bind parameters, even though it may reference those values as defaults.""" - def __init__(self, engine, statement, parameters): + def __init__(self, statement, parameters, engine=None): """constructs a new Compiled object. - engine - SQLEngine to compile against - statement - ClauseElement to be compiled parameters - optional dictionary indicating a set of bind parameters @@ -271,10 +269,12 @@ class Compiled(ClauseVisitor): will also result in the creation of new BindParamClause objects for each key and will also affect the generated column list in an INSERT statement and the SET clauses of an UPDATE statement. The keys of the parameter dictionary can - either be the string names of columns or actual sqlalchemy.schema.Column objects.""" - self.engine = engine + either be the string names of columns or ColumnClause objects. + + engine - optional SQLEngine to compile this statement against""" self.parameters = parameters self.statement = statement + self.engine = engine def __str__(self): """returns the string text of the generated SQL statement.""" @@ -290,6 +290,10 @@ class Compiled(ClauseVisitor): """ raise NotImplementedError() + def compile(self): + self.statement.accept_visitor(self) + self.after_compile() + def execute(self, *multiparams, **params): """executes this compiled object using the underlying SQLEngine""" if len(multiparams): @@ -367,20 +371,25 @@ class ClauseElement(object): return None engine = property(lambda s: s._find_engine(), doc="attempts to locate a SQLEngine within this ClauseElement structure, or returns None if none found.") - - def compile(self, engine = None, parameters = None, typemap=None): + + + def compile(self, engine = None, parameters = None, typemap=None, compiler=None): """compiles this SQL expression using its underlying SQLEngine to produce a Compiled object. If no engine can be found, an ansisql engine is used. bindparams is a dictionary representing the default bind parameters to be used with the statement. """ - if engine is None: - engine = self.engine - - if engine is None: + + if compiler is None: + if engine is not None: + compiler = engine.compiler(self, parameters) + elif self.engine is not None: + compiler = self.engine.compiler(self, parameters) + + if compiler is None: import sqlalchemy.ansisql as ansisql - engine = ansisql.engine() - - return engine.compile(self, parameters=parameters, typemap=typemap) + compiler = ansisql.ANSICompiler(self, parameters=parameters, typemap=typemap) + compiler.compile() + return compiler def __str__(self): return str(self.compile()) @@ -638,7 +647,7 @@ class TextClause(ClauseElement): being specified as a bind parameter via the bindparam() method, since it provides more information about what it is, including an optional type, as well as providing comparison operations.""" - def __init__(self, text = "", engine=None, bindparams=None, typemap=None, escape=True): + def __init__(self, text = "", engine=None, bindparams=None, typemap=None): self.parens = False self._engine = engine self.id = id(self) @@ -649,12 +658,10 @@ class TextClause(ClauseElement): typemap[key] = engine.type_descriptor(typemap[key]) def repl(m): self.bindparams[m.group(1)] = bindparam(m.group(1)) - return self.engine.bindtemplate % m.group(1) - - if escape: - self.text = re.compile(r':([\w_]+)', re.S).sub(repl, text) - else: - self.text = text + return ":%s" % m.group(1) + # scan the string and search for bind parameter names, add them + # to the list of bindparams + self.text = re.compile(r'(?<!:):([\w_]+)', re.S).sub(repl, text) if bindparams is not None: for b in bindparams: self.bindparams[b.key] = b |