From a6e3ff785f7eeabc4e611d83bca1122ce9a0524f Mon Sep 17 00:00:00 2001 From: Mike Bayer Date: Thu, 27 Oct 2005 05:12:52 +0000 Subject: [PATCH] --- doc/style.css | 5 +++-- lib/sqlalchemy/ansisql.py | 4 ++-- lib/sqlalchemy/databases/oracle.py | 6 ++++-- lib/sqlalchemy/engine.py | 32 ++++++++++++++++++++---------- lib/sqlalchemy/sql.py | 23 +++++++++++---------- test/select.py | 10 +++++----- 6 files changed, 47 insertions(+), 33 deletions(-) diff --git a/doc/style.css b/doc/style.css index ddef5196d8..858a557190 100644 --- a/doc/style.css +++ b/doc/style.css @@ -114,8 +114,9 @@ a:hover {color:#700000;} .codepoplink { font-weight:bold; font-family: verdana, sans-serif; - font-size:12px; + font-size:11px; color:#000000; border:1px solid; - padding:2px 8px 2px 8px; + padding:1px 2px 1px 2px; + margin:0px 0px 0px 15px; } diff --git a/lib/sqlalchemy/ansisql.py b/lib/sqlalchemy/ansisql.py index 963e52a1f4..22ea7e61d3 100644 --- a/lib/sqlalchemy/ansisql.py +++ b/lib/sqlalchemy/ansisql.py @@ -37,8 +37,8 @@ class ANSISQLEngine(sqlalchemy.engine.SQLEngine): def schemadropper(self, proxy, **params): return ANSISchemaDropper(proxy, **params) - def compiler(self, statement, bindparams): - return ANSICompiler(self, statement, bindparams) + def compiler(self, statement, bindparams, **kwargs): + return ANSICompiler(self, statement, bindparams, **kwargs) def connect_args(self): return ([],{}) diff --git a/lib/sqlalchemy/databases/oracle.py b/lib/sqlalchemy/databases/oracle.py index ff5178a0f4..696c59dda8 100644 --- a/lib/sqlalchemy/databases/oracle.py +++ b/lib/sqlalchemy/databases/oracle.py @@ -94,7 +94,7 @@ class OracleSQLEngine(ansisql.ANSISQLEngine): return "rowid" def compiler(self, statement, bindparams, **kwargs): - return OracleCompiler(self, statement, bindparams, **kwargs) + return OracleCompiler(self, statement, bindparams, use_ansi=self._use_ansi, **kwargs) def schemagenerator(self, proxy, **params): return OracleSchemaGenerator(proxy, **params) @@ -153,8 +153,10 @@ class OracleCompiler(ansisql.ANSICompiler): self.froms[join] = self.get_from_text(join.left) + ", " + self.get_from_text(join.right) self.wheres[join] = join.onclause - + + print "check1" if join.isouter: + print "check2" # if outer join, push on the right side table as the current "outertable" outertable = self._outertable self._outertable = join.right diff --git a/lib/sqlalchemy/engine.py b/lib/sqlalchemy/engine.py index c9bdf893b4..0175a1c7bb 100644 --- a/lib/sqlalchemy/engine.py +++ b/lib/sqlalchemy/engine.py @@ -15,8 +15,7 @@ # along with this library; if not, write to the Free Software # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. -"""builds upon the schema and sql packages to provide a central object for tying schema objects -and sql constructs to database-specific query compilation and execution""" +"""builds upon the schema and sql packages to provide a central object for tying schema objects and sql constructs to database-specific query compilation and execution""" import sqlalchemy.schema as schema import sqlalchemy.pool @@ -26,30 +25,37 @@ import StringIO, sys import sqlalchemy.types as types def create_engine(name, *args ,**kwargs): + """creates a new SQLEngine instance. + + name - the type of engine to load, i.e. 'sqlite', 'postgres', 'oracle' + *args, **kwargs - sent directly to the specific engine instance as connect arguments, options. + """ module = getattr(__import__('sqlalchemy.databases.%s' % name).databases, name) return module.engine(*args, **kwargs) class SchemaIterator(schema.SchemaVisitor): """a visitor that can gather text into a buffer and execute the contents of the buffer.""" def __init__(self, sqlproxy, **params): + """initializes this SchemaIterator and initializes its buffer. + + sqlproxy - a callable function returned by SQLEngine.proxy(), which executes a statement plus optional parameters. + """ self.sqlproxy = sqlproxy self.buffer = StringIO.StringIO() - def run(self): - raise NotImplementedError() - def append(self, s): + """appends content to the SchemaIterator's query buffer.""" self.buffer.write(s) def execute(self): + """executes the contents of the SchemaIterator's buffer using its sql proxy and clears out the buffer.""" try: return self.sqlproxy(self.buffer.getvalue()) finally: self.buffer.truncate(0) class SQLEngine(schema.SchemaEngine): - """base class for a series of database-specific engines. serves as an abstract factory for - implementation objects as well as database connections, transactions, SQL generators, etc.""" + """base class for a series of database-specific engines. serves as an abstract factory for implementation objects as well as database connections, transactions, SQL generators, etc.""" def __init__(self, pool = None, echo = False, logger = None, **params): # get a handle on the connection pool via the connect arguments @@ -89,30 +95,34 @@ class SQLEngine(schema.SchemaEngine): return "oid" def create(self, table, **params): + """creates a table given a schema.Table object.""" table.accept_visitor(self.schemagenerator(self.proxy(), **params)) def drop(self, table, **params): + """drops a table given a schema.Table object.""" table.accept_visitor(self.schemadropper(self.proxy(), **params)) def compile(self, statement, bindparams, **kwargs): + """given a sql.ClauseElement statement plus optional bind parameters, creates a new instance of this engine's SQLCompiler, compiles the ClauseElement, and returns the newly compiled object.""" compiler = self.compiler(statement, bindparams, **kwargs) statement.accept_visitor(compiler) return compiler def reflecttable(self, table): + """given a Table object, reflects its columns and properties from the database.""" raise NotImplementedError() def tableimpl(self, table): + """returns a new sql.TableImpl object to correspond to the given Table object.""" return sql.TableImpl(table) def columnimpl(self, column): - return sql.ColumnSelectable(column) + """returns a new sql.ColumnImpl object to correspond to the given Column object.""" + return sql.ColumnImpl(column) def last_inserted_ids(self): """returns a thread-local list of the primary keys for the last insert statement executed. - This does not apply to straight textual clauses; only to sql.Insert objects compiled against - a schema.Table object, which are executed via statement.execute(). The order of items in the list - is the same as that of the Table's 'primary_keys' attribute.""" + This does not apply to straight textual clauses; only to sql.Insert objects compiled against a schema.Table object, which are executed via statement.execute(). The order of items in the list is the same as that of the Table's 'primary_keys' attribute.""" raise NotImplementedError() def connect_args(self): diff --git a/lib/sqlalchemy/sql.py b/lib/sqlalchemy/sql.py index a56beeb3e7..e7f8a8ae17 100644 --- a/lib/sqlalchemy/sql.py +++ b/lib/sqlalchemy/sql.py @@ -301,7 +301,7 @@ class CompareMixin(object): def in_(self, *other): if len(other) == 0: return self.__eq__(None) - elif len(other) == 1: + elif len(other) == 1 and not isinstance(other[0], Selectable): return self.__eq__(other[0]) elif _is_literal(other[0]): return self._compare('IN', CompoundClause(',', other)) @@ -321,7 +321,7 @@ class ColumnClause(ClauseElement, CompareMixin): def __init__(self, text, selectable): self.text = text self.table = selectable - self._impl = ColumnSelectable(self) + self._impl = ColumnImpl(self) self.type = types.NullTypeEngine() columns = property(lambda self: [self]) @@ -355,7 +355,7 @@ class ColumnClause(ClauseElement, CompareMixin): def _make_proxy(self, selectable, name = None): c = ColumnClause(self.text or name, selectable) selectable.columns[c.key] = c - c._impl = ColumnSelectable(c) + c._impl = ColumnImpl(c) return c class FromClause(ClauseElement): @@ -494,8 +494,7 @@ class BinaryClause(ClauseElement): self.right = c class Selectable(FromClause): - """represents a column list-holding object, like a table or subquery. can be used anywhere - a Table is used.""" + """represents a column list-holding object, like a table, alias or subquery. can be used anywhere a Table is used.""" c = property(lambda self: self.columns) @@ -503,7 +502,7 @@ class Selectable(FromClause): raise NotImplementedError() def select(self, whereclauses = None, **params): - raise NotImplementedError() + return select([self], whereclauses, **params) def join(self, right, *args, **kwargs): return Join(self, right, *args, **kwargs) @@ -534,10 +533,9 @@ class Join(Selectable): primary_keys = property (lambda self: [c for c in self.left.columns if c.primary_key] + [c for c in self.right.columns if c.primary_key]) - def group_parenthesized(self): """indicates if this Selectable requires parenthesis when grouped into a compound statement""" - return False + return True def hash_key(self): return "Join(%s, %s, %s, %s)" % (repr(self.left.hash_key()), repr(self.right.hash_key()), repr(self.onclause.hash_key()), repr(self.isouter)) @@ -593,12 +591,10 @@ class Alias(Selectable): engine = property(lambda s: s.selectable.engine) - def select(self, whereclauses = None, **params): - return select([self], whereclauses, **params) -class ColumnSelectable(Selectable, CompareMixin): +class ColumnImpl(Selectable, CompareMixin): """Selectable implementation that gets attached to a schema.Column object.""" def __init__(self, column): @@ -613,6 +609,8 @@ class ColumnSelectable(Selectable, CompareMixin): self.label = self.column.name self.fullname = self.column.name + engine = property(lambda s: s.column.engine) + def copy_container(self): return self.column @@ -696,6 +694,7 @@ class Select(Selectable): self.name = None self.whereclause = whereclause self._engine = engine + self.rowid_column = None # indicates if this select statement is a subquery inside of a WHERE clause # note this is different from a subquery inside the FROM list @@ -728,6 +727,8 @@ class Select(Selectable): for f in column._get_from_objects(): self.froms.setdefault(f.id, f) + if self.rowid_column is None and hasattr(f, 'rowid_column'): + self.rowid_column = f.rowid_column._make_proxy(self) for co in column.columns: if self.use_labels: diff --git a/test/select.py b/test/select.py index 0549d20930..4db9447454 100644 --- a/test/select.py +++ b/test/select.py @@ -35,7 +35,7 @@ class SQLTest(PersistTest): c = clause.compile(engine, params) self.echo("\n" + str(c) + repr(c.get_params())) cc = re.sub(r'\n', '', str(c)) - self.assert_(cc == result) + self.assert_(cc == result, str(c) + "\n does not match \n" + result) class SelectTest(SQLTest): @@ -271,10 +271,10 @@ FROM myothertable UNION SELECT thirdtable.userid, thirdtable.otherstuff FROM thi self.runtest(query, "SELECT mytable.myid, mytable.name, mytable.description, myothertable.otherid, myothertable.othername \ FROM mytable LEFT OUTER JOIN myothertable ON mytable.myid = myothertable.otherid \ -WHERE mytable.name = :mytable_name AND mytable.myid = :mytable_myid AND \ -myothertable.othername != :myothertable_othername AND \ +WHERE mytable.name = %(mytable_name)s AND mytable.myid = %(mytable_myid)s AND \ +myothertable.othername != %(myothertable_othername)s AND \ EXISTS (select yay from foo where boo = lar)", - engine = postgres.engine()) + engine = postgres.engine({})) self.runtest(query, @@ -282,7 +282,7 @@ EXISTS (select yay from foo where boo = lar)", FROM mytable, myothertable WHERE mytable.myid = myothertable.otherid(+) AND \ mytable.name = :mytable_name AND mytable.myid = :mytable_myid AND \ myothertable.othername != :myothertable_othername AND EXISTS (select yay from foo where boo = lar)", - engine = oracle.engine(use_ansi = False)) + engine = oracle.engine({}, use_ansi = False)) def testbindparam(self): self.runtest(select( -- 2.47.2