From: Mike Bayer Date: Tue, 17 Oct 2006 02:03:00 +0000 (+0000) Subject: reorganizing classnames a bit, flagging "private" classes in the sql package, X-Git-Tag: rel_0_3_0~37 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=180d3741997c1471b9199334d6076e45cd581d62;p=thirdparty%2Fsqlalchemy%2Fsqlalchemy.git reorganizing classnames a bit, flagging "private" classes in the sql package, getting the generated docs to look a little nicer. fixes to extensions, sqlsoup etc. to be compatible with recent API tweaks --- diff --git a/doc/build/compile_docstrings.py b/doc/build/compile_docstrings.py index fc6003c0e7..fdc3bd672e 100644 --- a/doc/build/compile_docstrings.py +++ b/doc/build/compile_docstrings.py @@ -20,10 +20,12 @@ import sqlalchemy.ext.selectresults as selectresults objects = [] def make_doc(obj, classes=None, functions=None): objects.append(docstring.ObjectDoc(obj, classes=classes, functions=functions)) - -make_doc(obj=sql, classes=[]) + +# basically, if you see make_doc(obj=XXX) and thats it, we have separated out the public interface. otherwise +# still a work in progress. +make_doc(obj=sql) make_doc(obj=schema) -make_doc(obj=engine, classes=[engine.Connectable, engine.ComposedSQLEngine, engine.Connection, engine.Transaction, engine.Dialect, engine.ConnectionProvider, engine.ExecutionContext, engine.ResultProxy, engine.RowProxy]) +make_doc(obj=engine) make_doc(obj=engine.url) make_doc(obj=orm, classes=[orm.MapperExtension]) make_doc(obj=orm.mapperlib, classes=[orm.mapperlib.Mapper, orm.mapperlib.SelectionContext]) diff --git a/doc/build/content/dbengine.txt b/doc/build/content/dbengine.txt index 2da90a28ac..5feee49509 100644 --- a/doc/build/content/dbengine.txt +++ b/doc/build/content/dbengine.txt @@ -3,20 +3,19 @@ Database Engines {@name=dbengine} A database engine is a subclass of `sqlalchemy.sql.Engine`, and is the starting point for where SQLAlchemy provides a layer of abstraction on top of the various DBAPI2 database modules. For all databases supported by SA, there is a specific "implementation" module, found in the `sqlalchemy.databases` package, that provides all the objects an `Engine` needs in order to perform its job. A typical user of SQLAlchemy never needs to deal with these modules directly. For many purposes, the only knowledge that's needed is how to create an Engine for a particular connection URL. When dealing with direct execution of SQL statements, one would also be aware of Result, Connection, and Transaction objects. The primary public facing objects are: -* **URL** - represents the identifier for a particular database. URL objects are usually created automatically based on a given connect string passed to the `create_engine()` function. -* **Engine** - Combines a connection-providing resource with implementation-provided objects that know how to generate, execute, and gather information about SQL statements. It also provides the primary interface by which Connections are obtained, as well as a context for constructed SQL objects and schema constructs to "implicitly execute" themselves, which is an optional feature of SQLAlchemy. The Engine object that is normally dealt with is an instance of `sqlalchemy.engine.base.ComposedSQLEngine`. -* **Connection** - represents a connection to the database. The underlying connection object returned by a DBAPI's connect() method is referenced internally by the Connection object. Connection provides methods that handle the execution of SQLAlchemy's own SQL constructs, as well as literal string-based statements. -* **Transaction** - represents a transaction on a single Connection. Includes `begin()`, `commit()` and `rollback()` methods that support basic "nestable" behavior, meaning an outermost transaction is maintained against multiple nested calls to begin/commit. -* **ResultProxy** - Represents the results of an execution, and is most analgous to the cursor object in DBAPI. It primarily allows iteration over result sets, but also provides an interface to information about inserts/updates/deletes, such as the count of rows affected, last inserted IDs, etc. -* **RowProxy** - Represents a single row returned by the fetchone() method on ResultProxy. +* [URL](boldrel:docstrings_sqlalchemy.engine.url_URL) - represents the identifier for a particular database. URL objects are usually created automatically based on a given connect string passed to the `create_engine()` function. +* [Engine](boldrel:docstrings_sqlalchemy.engine_Engine) - Combines a connection-providing resource with implementation-provided objects that know how to generate, execute, and gather information about SQL statements. It also provides the primary interface by which Connections are obtained, as well as a context for constructed SQL objects and schema constructs to "implicitly execute" themselves, which is an optional feature of SQLAlchemy. +* [Connection](boldrel:docstrings_sqlalchemy.engine_Connection) - represents a connection to the database. The underlying connection object returned by a DBAPI's connect() method is referenced internally by the Connection object. Connection provides methods that handle the execution of SQLAlchemy's own SQL constructs, as well as literal string-based statements. +* [Transaction](boldrel:docstrings_sqlalchemy.engine_Transaction) - represents a transaction on a single Connection. Includes `begin()`, `commit()` and `rollback()` methods that support basic "nestable" behavior, meaning an outermost transaction is maintained against multiple nested calls to begin/commit. +* [ResultProxy](boldrel:docstrings_sqlalchemy.engine_ResultProxy) - Represents the results of an execution, and is most analgous to the cursor object in DBAPI. It primarily allows iteration over result sets, but also provides an interface to information about inserts/updates/deletes, such as the count of rows affected, last inserted IDs, etc. +* [RowProxy](boldrel:docstrings_sqlalchemy.engine_RowProxy) - Represents a single row returned by the fetchone() method on ResultProxy. -Underneath the public-facing API of `ComposedSQLEngine`, several components are provided by database implementations to provide the full behavior, including: +Underneath the public-facing API of `Engine`, several components are provided by database implementations to provide the full behavior, including: -* **Dialect** - this object is provided by database implementations to describe the behavior of a particular database. It acts as a repository for metadata about a database's characteristics, and provides factory methods for other objects that deal with generating SQL strings and objects that handle some of the details of statement execution. -* **ConnectionProvider** - this object knows how to return a DBAPI connection object. It typically talks to a connection pool which maintains one or more connections in memory for quick re-use. -* **ExecutionContext** - this object is created for each execution of a single SQL statement, and tracks information about its execution such as primary keys inserted, the total count of rows affected, etc. It also may implement any special logic that various DBAPI implementations may require before or after a statement execution. -* **Compiler** - receives SQL expression objects and assembles them into strings that are suitable for direct execution, as well as collecting bind parameters into a dictionary or list to be sent along with the statement. -* **SchemaGenerator** - receives collections of Schema objects and knows how to generate the appropriate SQL for `CREATE` and `DROP` statements. +* [Dialect](boldrel:docstrings_sqlalchemy.engine_Dialect) - this object is provided by database implementations to describe the behavior of a particular database. It acts as a repository for metadata about a database's characteristics, and provides factory methods for other objects that deal with generating SQL strings and objects that handle some of the details of statement execution. +* [ConnectionProvider](boldrel:docstrings_sqlalchemy.engine_ConnectionProvider) - this object knows how to return a DBAPI connection object. It typically talks to a connection pool which maintains one or more connections in memory for quick re-use. +* [ExecutionContext](boldrel:docstrings_sqlalchemy.engine_ExecutionContext) - this object is created for each execution of a single SQL statement, and tracks information about its execution such as primary keys inserted, the total count of rows affected, etc. It also may implement any special logic that various DBAPI implementations may require before or after a statement execution. +* [Compiled](boldrel:docstrings_sqlalchemy.sql_Compiled) - represents a "compiled" SQL expression object. Includes a `compile()` method which receives SQL expression objects and assembles them into strings that are suitable for direct execution. Also collects default bind parameters into a datastructure that will be converted at execution time into a dictionary or list, depending on the dialect's paramstyle. ### Supported Databases {@name=supported} @@ -111,7 +110,7 @@ Example of a manual invocation of `pool.QueuePool` (which is the pool instance u * max_overflow=10 : the number of connections to allow in "overflow", that is connections that can be opened above and beyond the initial five. this is only used with `QueuePool`. * pool_timeout=30 : number of seconds to wait before giving up on getting a connection from the pool. This is only used with `QueuePool`. * pool_recycle=-1 : this setting causes the pool to recycle connections after the given number of seconds has passed. It defaults to -1, or no timeout. For example, setting to 3600 means connections will be recycled after one hour. Note that MySQL in particular will disconnect automatically if no activity is detected on a connection for eight hours (although this is configurable with the MySQLDB connection itself and the server configuration as well). -* echo=False : if True, the Engine will log all statements as well as a repr() of their parameter lists to the engines logger, which defaults to sys.stdout. The `echo` attribute of `ComposedSQLEngine` can be modified at any time to turn logging on and off. If set to the string `"debug"`, result rows will be printed to the standard output as well. +* echo=False : if True, the Engine will log all statements as well as a repr() of their parameter lists to the engines logger, which defaults to sys.stdout. The `echo` attribute of `Engine` can be modified at any time to turn logging on and off. If set to the string `"debug"`, result rows will be printed to the standard output as well. * logger=None : a file-like object where logging output can be sent, if echo is set to True. Newlines will not be sent with log messages. This defaults to an internal logging object which references `sys.stdout`. * module=None : used by database implementations which support multiple DBAPI modules, this is a reference to a DBAPI2 module to be used instead of the engine's default module. For Postgres, the default is psycopg2, or psycopg1 if 2 cannot be found. For Oracle, its cx_Oracle. * use_ansi=True : used only by Oracle; when False, the Oracle driver attempts to support a particular "quirk" of Oracle versions 8 and previous, that the LEFT OUTER JOIN SQL syntax is not supported, and the "Oracle join" syntax of using `<column1>(+)=<column2>` must be used in order to achieve a LEFT OUTER JOIN. diff --git a/doc/build/lib/docstring.py b/doc/build/lib/docstring.py index 0855cf1387..8f771c20ef 100644 --- a/doc/build/lib/docstring.py +++ b/doc/build/lib/docstring.py @@ -51,7 +51,7 @@ class ObjectDoc(object): classes = [] if self.isclass: - self.description = "Class " + self.name + self.description = "class " + self.name if hasattr(obj, '__mro__'): l = [] mro = list(obj.__mro__[1:]) @@ -63,7 +63,7 @@ class ObjectDoc(object): l.insert(0, x) self.description += "(" + string.join([x.__name__ for x in l], ',') + ")" else: - self.description = "Module " + self.name + self.description = "module " + self.name self.doc = obj.__doc__ diff --git a/lib/sqlalchemy/ansisql.py b/lib/sqlalchemy/ansisql.py index b6923c7da0..43c6afd8e0 100644 --- a/lib/sqlalchemy/ansisql.py +++ b/lib/sqlalchemy/ansisql.py @@ -319,7 +319,7 @@ class ANSICompiler(sql.Compiled): inner_columns[co._label] = l # TODO: figure this out, a ColumnClause with a select as a parent # is different from any other kind of parent - elif select.issubquery and isinstance(co, sql.ColumnClause) and co.table is not None and not isinstance(co.table, sql.Select): + elif select.issubquery and isinstance(co, sql._ColumnClause) and co.table is not None and not isinstance(co.table, sql.Select): # SQLite doesnt like selecting from a subquery where the column # names look like table.colname, so add a label synonomous with # the column name @@ -489,7 +489,7 @@ class ANSICompiler(sql.Compiled): colparams = self._get_colparams(insert_stmt, default_params) def create_param(p): - if isinstance(p, sql.BindParamClause): + if isinstance(p, sql._BindParamClause): self.binds[p.key] = p if p.shortname is not None: self.binds[p.shortname] = p @@ -521,7 +521,7 @@ class ANSICompiler(sql.Compiled): self.isupdate = True colparams = self._get_colparams(update_stmt, default_params) def create_param(p): - if isinstance(p, sql.BindParamClause): + if isinstance(p, sql._BindParamClause): self.binds[p.key] = p self.binds[p.shortname] = p return self.bindparam_string(p.key) @@ -577,7 +577,7 @@ class ANSICompiler(sql.Compiled): # now go thru compiled params, get the Column object for each key d = {} for key, value in parameters.iteritems(): - if isinstance(key, sql.ColumnClause): + if isinstance(key, sql._ColumnClause): d[key] = value else: try: diff --git a/lib/sqlalchemy/databases/oracle.py b/lib/sqlalchemy/databases/oracle.py index b9aa096952..91e54ea759 100644 --- a/lib/sqlalchemy/databases/oracle.py +++ b/lib/sqlalchemy/databases/oracle.py @@ -364,7 +364,7 @@ class OracleCompiler(ansisql.ANSICompiler): orderby = self.strings[orderby] class SelectVisitor(sql.ClauseVisitor): def visit_select(self, select): - select.append_column(sql.ColumnClause("ROW_NUMBER() OVER (ORDER BY %s)" % orderby).label("ora_rn")) + select.append_column(sql.column("ROW_NUMBER() OVER (ORDER BY %s)" % orderby).label("ora_rn")) select.accept_visitor(SelectVisitor()) limitselect = sql.select([c for c in select.c if c.key!='ora_rn']) if select.offset is not None: @@ -399,7 +399,7 @@ class OracleCompiler(ansisql.ANSICompiler): orderby = select.oid_column orderby.accept_visitor(self) orderby = self.strings[orderby] - select.append_column(sql.ColumnClause("ROW_NUMBER() OVER (ORDER BY %s)" % orderby).label("ora_rn")) + select.append_column(sql.column("ROW_NUMBER() OVER (ORDER BY %s)" % orderby).label("ora_rn")) limitselect = sql.select([c for c in select.c if c.key!='ora_rn']) if select.offset is not None: limitselect.append_whereclause("ora_rn>%d" % select.offset) diff --git a/lib/sqlalchemy/engine/base.py b/lib/sqlalchemy/engine/base.py index 1af957f745..e348b19ee2 100644 --- a/lib/sqlalchemy/engine/base.py +++ b/lib/sqlalchemy/engine/base.py @@ -383,8 +383,8 @@ class Transaction(object): if self.__parent is self: self.__connection._commit_impl() self.__is_active = False - -class ComposedSQLEngine(sql.Engine, Connectable): + +class Engine(sql.Executor, Connectable): """ Connects a ConnectionProvider, a Dialect and a CompilerFactory together to provide a default implementation of SchemaEngine. @@ -415,7 +415,7 @@ class ComposedSQLEngine(sql.Engine, Connectable): connection.close() def _func(self): - return sql.FunctionGenerator(self) + return sql._FunctionGenerator(self) func = property(_func) def text(self, text, *args, **kwargs): """returns a sql.text() object for performing literal queries.""" diff --git a/lib/sqlalchemy/engine/strategies.py b/lib/sqlalchemy/engine/strategies.py index e401591b20..fe30aeb8d7 100644 --- a/lib/sqlalchemy/engine/strategies.py +++ b/lib/sqlalchemy/engine/strategies.py @@ -11,7 +11,7 @@ strategies = {} class EngineStrategy(object): """defines a function that receives input arguments and produces an instance of sql.Engine, typically - an instance sqlalchemy.engine.base.ComposedSQLEngine or a subclass.""" + an instance sqlalchemy.engine.base.Engine or a subclass.""" def __init__(self, name): """construct a new EngineStrategy object and sets it in the list of available strategies under this name.""" @@ -55,7 +55,7 @@ class PlainEngineStrategy(DefaultEngineStrategy): def get_pool_provider(self, dialect, url, **poolargs): return default.PoolConnectionProvider(dialect, url, **poolargs) def get_engine(self, provider, dialect, **kwargs): - return base.ComposedSQLEngine(provider, dialect, **kwargs) + return base.Engine(provider, dialect, **kwargs) PlainEngineStrategy() class ThreadLocalEngineStrategy(DefaultEngineStrategy): diff --git a/lib/sqlalchemy/engine/threadlocal.py b/lib/sqlalchemy/engine/threadlocal.py index d868287162..a04e8edbe9 100644 --- a/lib/sqlalchemy/engine/threadlocal.py +++ b/lib/sqlalchemy/engine/threadlocal.py @@ -83,14 +83,14 @@ class TLTransaction(base.Transaction): def rollback(self): self.connection.session.rollback() -class TLEngine(base.ComposedSQLEngine): - """a ComposedSQLEngine that includes support for thread-local managed transactions. This engine +class TLEngine(base.Engine): + """an Engine that includes support for thread-local managed transactions. This engine is better suited to be used with threadlocal Pool object.""" def __init__(self, *args, **kwargs): """the TLEngine relies upon the ConnectionProvider having "threadlocal" behavior, so that once a connection is checked out for the current thread, you get that same connection repeatedly.""" - base.ComposedSQLEngine.__init__(self, *args, **kwargs) + super(TLEngine, self).__init__(*args, **kwargs) self.context = util.ThreadLocal() def raw_connection(self): """returns a DBAPI connection.""" diff --git a/lib/sqlalchemy/ext/assignmapper.py b/lib/sqlalchemy/ext/assignmapper.py index 5b77052e24..e672d835e5 100644 --- a/lib/sqlalchemy/ext/assignmapper.py +++ b/lib/sqlalchemy/ext/assignmapper.py @@ -1,9 +1,9 @@ -from sqlalchemy import mapper, util +from sqlalchemy import mapper, util, Query import types def monkeypatch_query_method(ctx, class_, name): def do(self, *args, **kwargs): - query = class_.mapper.query(session=ctx.current) + query = Query(class_, session=ctx.current) return getattr(query, name)(*args, **kwargs) setattr(class_, name, classmethod(do)) diff --git a/lib/sqlalchemy/ext/proxy.py b/lib/sqlalchemy/ext/proxy.py index 60972a6d58..c7e707f8d8 100644 --- a/lib/sqlalchemy/ext/proxy.py +++ b/lib/sqlalchemy/ext/proxy.py @@ -4,11 +4,11 @@ except ImportError: from sqlalchemy.util import ThreadLocal as local from sqlalchemy import sql -from sqlalchemy.engine import create_engine +from sqlalchemy.engine import create_engine, Engine __all__ = ['BaseProxyEngine', 'AutoConnectEngine', 'ProxyEngine'] -class BaseProxyEngine(sql.Engine): +class BaseProxyEngine(sql.Executor): """Basis for all proxy engines.""" def get_engine(self): diff --git a/lib/sqlalchemy/ext/sqlsoup.py b/lib/sqlalchemy/ext/sqlsoup.py index be772555d1..a166495731 100644 --- a/lib/sqlalchemy/ext/sqlsoup.py +++ b/lib/sqlalchemy/ext/sqlsoup.py @@ -252,10 +252,10 @@ class TableClassType(type): def update(cls, whereclause=None, values=None, **kwargs): cls._table.update(whereclause, values).execute(**kwargs) def __getattr__(cls, attr): - if attr == '_mapper': + if attr == '_query': # called during mapper init raise AttributeError() - return getattr(cls._mapper, attr) + return getattr(cls._query, attr) def _is_outer_join(selectable): @@ -273,7 +273,10 @@ def _selectable_name(selectable): elif isinstance(selectable, schema.Table): return selectable.name.capitalize() else: - return selectable.__class__.__name__ + x = selectable.__class__.__name__ + if x[0] == '_': + x = x[1:] + return x def class_for_table(selectable): if not hasattr(selectable, '_selectable') \ @@ -307,6 +310,7 @@ def class_for_table(selectable): selectable, extension=objectstore.mapper_extension, allow_null_pks=_is_outer_join(selectable)) + klass._query = Query(klass._mapper) return klass class SqlSoup: diff --git a/lib/sqlalchemy/logging.py b/lib/sqlalchemy/logging.py index 3a5a19fa81..876785b0d9 100644 --- a/lib/sqlalchemy/logging.py +++ b/lib/sqlalchemy/logging.py @@ -20,7 +20,7 @@ E.g.: is equivalent to: import logging - logging.getLogger('sqlalchemy.engine.ComposedSQLEngine.%s' % hex(id(engine))).setLevel(logging.DEBUG) + logging.getLogger('sqlalchemy.engine.Engine.%s' % hex(id(engine))).setLevel(logging.DEBUG) """ diff --git a/lib/sqlalchemy/orm/mapper.py b/lib/sqlalchemy/orm/mapper.py index dd1ca0f757..e1491a4a84 100644 --- a/lib/sqlalchemy/orm/mapper.py +++ b/lib/sqlalchemy/orm/mapper.py @@ -129,7 +129,7 @@ class Mapper(object): raise exceptions.ArgumentError("Class '%s' is not a new-style class" % class_.__name__) for table in (local_table, select_table): - if table is not None and isinstance(table, sql.SelectBaseMixin): + if table is not None and isinstance(table, sql._SelectBaseMixin): # some db's, noteably postgres, dont want to select from a select # without an alias. also if we make our own alias internally, then # the configured properties on the mapper are not matched against the alias diff --git a/lib/sqlalchemy/orm/session.py b/lib/sqlalchemy/orm/session.py index fef887f7bf..73052f11e2 100644 --- a/lib/sqlalchemy/orm/session.py +++ b/lib/sqlalchemy/orm/session.py @@ -30,7 +30,7 @@ class SessionTransaction(object): return self.get_or_add(connectable) def get_or_add(self, connectable): # we reference the 'engine' attribute on the given object, which in the case of - # Connection, ProxyEngine, Engine, ComposedSQLEngine, whatever, should return the original + # Connection, ProxyEngine, Engine, whatever, should return the original # "Engine" object that is handling the connection. if self.connections.has_key(connectable.engine): return self.connections[connectable.engine][0] diff --git a/lib/sqlalchemy/orm/strategies.py b/lib/sqlalchemy/orm/strategies.py index 991ff66cc3..4b04bffdcd 100644 --- a/lib/sqlalchemy/orm/strategies.py +++ b/lib/sqlalchemy/orm/strategies.py @@ -247,13 +247,13 @@ class LazyLoader(AbstractRelationLoader): if isinstance(binary.left, schema.Column) and isinstance(binary.right, schema.Column) and ((not circular and column_in_table(table, binary.left)) or (circular and binary.right in foreignkey)): col = binary.left binary.left = binds.setdefault(binary.left, - sql.BindParamClause(bind_label(), None, shortname=binary.left.name, type=binary.right.type)) + sql.bindparam(bind_label(), None, shortname=binary.left.name, type=binary.right.type)) reverse[binary.right] = binds[col] if isinstance(binary.right, schema.Column) and isinstance(binary.left, schema.Column) and ((not circular and column_in_table(table, binary.right)) or (circular and binary.left in foreignkey)): col = binary.right binary.right = binds.setdefault(binary.right, - sql.BindParamClause(bind_label(), None, shortname=binary.right.name, type=binary.left.type)) + sql.bindparam(bind_label(), None, shortname=binary.right.name, type=binary.left.type)) reverse[binary.left] = binds[col] lazywhere = primaryjoin.copy_container() diff --git a/lib/sqlalchemy/schema.py b/lib/sqlalchemy/schema.py index 88d52f0753..ebdecc77a4 100644 --- a/lib/sqlalchemy/schema.py +++ b/lib/sqlalchemy/schema.py @@ -96,10 +96,10 @@ def _get_table_key(name, schema): else: return schema + "." + name -class TableSingleton(type): +class _TableSingleton(type): """a metaclass used by the Table object to provide singleton behavior.""" def __call__(self, name, metadata, *args, **kwargs): - if isinstance(metadata, sql.Engine): + if isinstance(metadata, sql.Executor): # backwards compatibility - get a BoundSchema associated with the engine engine = metadata if not hasattr(engine, '_legacy_metadata'): @@ -150,13 +150,13 @@ class TableSingleton(type): return table -class Table(SchemaItem, sql.TableClause): +class Table(SchemaItem, sql._TableClause): """represents a relational database table. This subclasses sql.TableClause to provide a table that is "wired" to an engine. Whereas TableClause represents a table as its used in a SQL expression, Table represents a table as its created in the database. Be sure to look at sqlalchemy.sql.TableImpl for additional methods defined on a Table.""" - __metaclass__ = TableSingleton + __metaclass__ = _TableSingleton def __init__(self, name, metadata, **kwargs): """Construct a Table. @@ -302,7 +302,7 @@ class Table(SchemaItem, sql.TableClause): args.append(c.copy()) return Table(self.name, metadata, schema=schema, *args) -class Column(SchemaItem, sql.ColumnClause): +class Column(SchemaItem, sql._ColumnClause): """represents a column in a database table. this is a subclass of sql.ColumnClause and represents an actual existing table in the database, in a similar fashion as TableClause/Table.""" def __init__(self, name, type, *args, **kwargs): diff --git a/lib/sqlalchemy/sql.py b/lib/sqlalchemy/sql.py index 6f51ccbe99..990291ff14 100644 --- a/lib/sqlalchemy/sql.py +++ b/lib/sqlalchemy/sql.py @@ -10,21 +10,21 @@ from sqlalchemy import types as sqltypes import string, re, random, sets types = __import__('types') -__all__ = ['text', 'table', 'column', 'func', 'select', 'update', 'insert', 'delete', 'join', 'and_', 'or_', 'not_', 'between_', 'case', 'cast', 'union', 'union_all', 'null', 'desc', 'asc', 'outerjoin', 'alias', 'subquery', 'literal', 'bindparam', 'exists', 'extract'] +__all__ = ['text', 'table', 'column', 'func', 'select', 'update', 'insert', 'delete', 'join', 'and_', 'or_', 'not_', 'between_', 'case', 'cast', 'union', 'union_all', 'null', 'desc', 'asc', 'outerjoin', 'alias', 'subquery', 'literal', 'bindparam', 'exists', 'extract','AbstractDialect', 'ClauseParameters', 'ClauseVisitor', 'Executor', 'Compiled', 'ClauseElement', 'ColumnElement', 'ColumnCollection', 'FromClause', 'Select', 'Alias', 'CompoundSelect','Join'] def desc(column): """return a descending ORDER BY clause element, e.g.: order_by = [desc(table1.mycol)] """ - return CompoundClause(None, column, "DESC") + return _CompoundClause(None, column, "DESC") def asc(column): """return an ascending ORDER BY clause element, e.g.: order_by = [asc(table1.mycol)] """ - return CompoundClause(None, column, "ASC") + return _CompoundClause(None, column, "ASC") def outerjoin(left, right, onclause=None, **kwargs): """return an OUTER JOIN clause element. @@ -89,7 +89,7 @@ def insert(table, values = None, **kwargs): or a SELECT statement. If a SELECT statement is specified which references this INSERT statement's table, the statement will be correlated against the INSERT statement. """ - return Insert(table, values, **kwargs) + return _Insert(table, values, **kwargs) def update(table, whereclause = None, values = None, **kwargs): """returns an UPDATE clause element. @@ -112,7 +112,7 @@ def update(table, whereclause = None, values = None, **kwargs): object, or a SELECT statement. If a SELECT statement is specified which references this UPDATE statement's table, the statement will be correlated against the UPDATE statement. """ - return Update(table, whereclause, values, **kwargs) + return _Update(table, whereclause, values, **kwargs) def delete(table, whereclause = None, **kwargs): """returns a DELETE clause element. @@ -122,7 +122,7 @@ def delete(table, whereclause = None, **kwargs): 'table' is the table to be updated. 'whereclause' is a ClauseElement describing the WHERE condition of the UPDATE statement. """ - return Delete(table, whereclause, **kwargs) + return _Delete(table, whereclause, **kwargs) def and_(*clauses): """joins a list of clauses together by the AND operator. the & operator can be used as well.""" @@ -143,16 +143,16 @@ def between(ctest, cleft, cright): column.between(value1, value2). """ - return BooleanExpression(ctest, and_(_check_literal(cleft, ctest.type), _check_literal(cright, ctest.type)), 'BETWEEN') + return _BooleanExpression(ctest, and_(_check_literal(cleft, ctest.type), _check_literal(cright, ctest.type)), 'BETWEEN') between_ = between def case(whens, value=None, else_=None): """ SQL CASE statement -- whens are a sequence of pairs to be translated into "when / then" clauses; optional [value] for simple case statements, and [else_] for case defaults """ - whenlist = [CompoundClause(None, 'WHEN', c, 'THEN', r) for (c,r) in whens] + whenlist = [_CompoundClause(None, 'WHEN', c, 'THEN', r) for (c,r) in whens] if else_: - whenlist.append(CompoundClause(None, 'ELSE', else_)) - cc = CalculatedClause(None, 'CASE', value, *whenlist + ['END']) + whenlist.append(_CompoundClause(None, 'ELSE', else_)) + cc = _CalculatedClause(None, 'CASE', value, *whenlist + ['END']) for c in cc.clauses: c.parens = False return cc @@ -164,17 +164,17 @@ def cast(clause, totype, **kwargs): or cast(table.c.timestamp, DATE) """ - return Cast(clause, totype, **kwargs) + return _Cast(clause, totype, **kwargs) def extract(field, expr): """return extract(field FROM expr)""" - expr = BinaryClause(text(field), expr, "FROM") + expr = _BinaryClause(text(field), expr, "FROM") return func.extract(expr) def exists(*args, **params): params['correlate'] = True s = select(*args, **params) - return BooleanExpression(TextClause("EXISTS"), s, None) + return _BooleanExpression(_TextClause("EXISTS"), s, None) def union(*selects, **params): return _compound_select('UNION', *selects, **params) @@ -201,21 +201,21 @@ def literal(value, type=None): the optional type parameter is a sqlalchemy.types.TypeEngine object which indicates bind-parameter and result-set translation for this literal. """ - return BindParamClause('literal', value, type=type) + return _BindParamClause('literal', value, type=type) def label(name, obj): - """returns a Label object for the given selectable, used in the column list for a select statement.""" - return Label(name, obj) + """returns a _Label object for the given selectable, used in the column list for a select statement.""" + return _Label(name, obj) def column(text, table=None, type=None): """returns a textual column clause, relative to a table. this is also the primitive version of a schema.Column which is a subclass. """ - return ColumnClause(text, table, type) + return _ColumnClause(text, table, type) def table(name, *columns): """returns a table clause. this is a primitive version of the schema.Table object, which is a subclass of this object.""" - return TableClause(name, *columns) + return _TableClause(name, *columns) def bindparam(key, value=None, type=None, shortname=None): """creates a bind parameter clause with the given key. @@ -223,10 +223,10 @@ def bindparam(key, value=None, type=None, shortname=None): An optional default value can be specified by the value parameter, and the optional type parameter is a sqlalchemy.types.TypeEngine object which indicates bind-parameter and result-set translation for this bind parameter.""" - if isinstance(key, ColumnClause): - return BindParamClause(key.name, value, type=key.type, shortname=shortname) + if isinstance(key, _ColumnClause): + return _BindParamClause(key.name, value, type=key.type, shortname=shortname) else: - return BindParamClause(key, value, type=type, shortname=shortname) + return _BindParamClause(key, value, type=type, shortname=shortname) def text(text, engine=None, *args, **kwargs): """creates literal text to be inserted into a query. @@ -252,21 +252,21 @@ def text(text, engine=None, *args, **kwargs): clause of the textual statement to type objects, which will be used to perform post-processing on columns within the result set (for textual statements that produce result sets).""" - return TextClause(text, engine=engine, *args, **kwargs) + return _TextClause(text, engine=engine, *args, **kwargs) def null(): """returns a Null object, which compiles to NULL in a sql statement.""" - return Null() + return _Null() -class FunctionGateway(object): - """returns a callable based on an attribute name, which then returns a Function +class _FunctionGateway(object): + """returns a callable based on an attribute name, which then returns a _Function object with that name.""" def __getattr__(self, name): - return getattr(FunctionGenerator(), name) -func = FunctionGateway() + return getattr(_FunctionGenerator(), name) +func = _FunctionGateway() def _compound_clause(keyword, *clauses): - return CompoundClause(keyword, *clauses) + return _CompoundClause(keyword, *clauses) def _compound_select(keyword, *selects, **kwargs): return CompoundSelect(keyword, *selects, **kwargs) @@ -277,12 +277,6 @@ def _is_literal(element): def is_column(col): return isinstance(col, ColumnElement) -class Engine(object): - """represents a 'thing that can produce Compiled objects and execute them'.""" - def execute_compiled(self, compiled, parameters, echo=None, **kwargs): - raise NotImplementedError() - def compiler(self, statement, parameters, **kwargs): - raise NotImplementedError() class AbstractDialect(object): """represents the behavior of a particular database. Used by Compiled objects.""" @@ -342,6 +336,15 @@ class ClauseVisitor(object): def visit_cast(self, cast):pass def visit_label(self, label):pass def visit_typeclause(self, typeclause):pass + +class Executor(object): + """represents a 'thing that can produce Compiled objects and execute them'.""" + def execute_compiled(self, compiled, parameters, echo=None, **kwargs): + """execute a Compiled object.""" + raise NotImplementedError() + def compiler(self, statement, parameters, **kwargs): + """return a Compiled object for the given statement and parameters.""" + raise NotImplementedError() class Compiled(ClauseVisitor): """represents a compiled SQL expression. the __str__ method of the Compiled object @@ -358,12 +361,12 @@ class Compiled(ClauseVisitor): parameters - optional dictionary indicating a set of bind parameters specified with this Compiled object. These parameters are the "default" - values corresponding to the ClauseElement's BindParamClauses when the Compiled + values corresponding to the ClauseElement's _BindParamClauses when the Compiled is executed. In the case of an INSERT or UPDATE statement, these parameters - will also result in the creation of new BindParamClause objects for each key + 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 ColumnClause objects. + either be the string names of columns or _ColumnClause objects. engine - optional Engine to compile this statement against""" self.dialect = dialect @@ -384,8 +387,8 @@ class Compiled(ClauseVisitor): Will start with the default parameters specified when this Compiled object was first constructed, and will override those values with those sent via **params, which are key/value pairs. Each key should match one of the - BindParamClause objects compiled into this object; either the "key" or - "shortname" property of the BindParamClause. + _BindParamClause objects compiled into this object; either the "key" or + "shortname" property of the _BindParamClause. """ raise NotImplementedError() @@ -417,19 +420,20 @@ class ClauseElement(object): if asfrom: data[self] = self def compare(self, other): - """compares this ClauseElement to the given ClauseElement. + """compare this ClauseElement to the given ClauseElement. Subclasses should override the default behavior, which is a straight identity comparison.""" return self is other def accept_visitor(self, visitor): - """accepts a ClauseVisitor and calls the appropriate visit_xxx method.""" + """accept a ClauseVisitor and call the appropriate visit_xxx method.""" raise NotImplementedError(repr(self)) def copy_container(self): - """should return a copy of this ClauseElement, iff this ClauseElement contains other - ClauseElements. Otherwise, it should be left alone to return self. This is used to + """return a copy of this ClauseElement, iff this ClauseElement contains other ClauseElements. + + If this ClauseElement is not a container, it should return self. This is used to create copies of expression trees that still reference the same "leaf nodes". The new structure can then be restructured without affecting the original.""" return self @@ -437,7 +441,7 @@ class ClauseElement(object): def _find_engine(self): """default strategy for locating an engine within the clause element. relies upon a local engine property, or looks in the "from" objects which - ultimately have to contain Tables or TableClauses. """ + ultimately have to contain Tables or _TableClauses. """ try: if self._engine is not None: return self._engine @@ -508,9 +512,9 @@ class ClauseElement(object): return self._negate() def _negate(self): self.parens=True - return BooleanExpression(TextClause("NOT"), self, None) + return _BooleanExpression(_TextClause("NOT"), self, None) -class CompareMixin(object): +class _CompareMixin(object): """defines comparison operations for ClauseElements.""" def __lt__(self, other): return self._compare('<', other) @@ -543,11 +547,11 @@ class CompareMixin(object): def endswith(self, other): return self._compare('LIKE', "%" + other) def label(self, name): - return Label(name, self, self.type) + return _Label(name, self, self.type) def distinct(self): - return CompoundClause(None,"DISTINCT", self) + return _CompoundClause(None,"DISTINCT", self) def between(self, cleft, cright): - return BooleanExpression(self, and_(self._check_literal(cleft), self._check_literal(cright)), 'BETWEEN') + return _BooleanExpression(self, and_(self._check_literal(cleft), self._check_literal(cright)), 'BETWEEN') def op(self, operator): return lambda other: self._compare(operator, other) # and here come the math operators: @@ -564,34 +568,34 @@ class CompareMixin(object): def __truediv__(self, other): return self._operate('/', other) def _bind_param(self, obj): - return BindParamClause('literal', obj, shortname=None, type=self.type) + return _BindParamClause('literal', obj, shortname=None, type=self.type) def _check_literal(self, other): if _is_literal(other): return self._bind_param(other) else: return other def _compare(self, operator, obj, negate=None): - if obj is None or isinstance(obj, Null): + if obj is None or isinstance(obj, _Null): if operator == '=': - return BooleanExpression(self._compare_self(), null(), 'IS', negate='IS NOT') + return _BooleanExpression(self._compare_self(), null(), 'IS', negate='IS NOT') elif operator == '!=': - return BooleanExpression(self._compare_self(), null(), 'IS NOT', negate='IS') + return _BooleanExpression(self._compare_self(), null(), 'IS NOT', negate='IS') else: raise exceptions.ArgumentError("Only '='/'!=' operators can be used with NULL") else: obj = self._check_literal(obj) - return BooleanExpression(self._compare_self(), obj, operator, type=self._compare_type(obj), negate=negate) + return _BooleanExpression(self._compare_self(), obj, operator, type=self._compare_type(obj), negate=negate) def _operate(self, operator, obj): if _is_literal(obj): obj = self._bind_param(obj) - return BinaryExpression(self._compare_self(), obj, operator, type=self._compare_type(obj)) + return _BinaryExpression(self._compare_self(), obj, operator, type=self._compare_type(obj)) def _compare_self(self): """allows ColumnImpl to return its Column object for usage in ClauseElements, all others to just return self""" return self def _compare_type(self, obj): - """allows subclasses to override the type used in constructing BinaryClause objects. Default return + """allows subclasses to override the type used in constructing _BinaryClause objects. Default return value is the type of the given object.""" return obj.type @@ -609,13 +613,13 @@ class Selectable(ClauseElement): statement""" return True -class ColumnElement(Selectable, CompareMixin): +class ColumnElement(Selectable, _CompareMixin): """represents a column element within the list of a Selectable's columns. - A ColumnElement can either be directly associated with a TableClause, or + A ColumnElement can either be directly associated with a _TableClause, or a free-standing textual column with no table, or is a "proxy" column, indicating it is placed on a Selectable such as an Alias or Select statement and ultimately corresponds - to a TableClause-attached column (or in the case of a CompositeSelect, a proxy ColumnElement - may correspond to several TableClause-attached columns).""" + to a _TableClause-attached column (or in the case of a CompositeSelect, a proxy ColumnElement + may correspond to several _TableClause-attached columns).""" primary_key = property(lambda self:getattr(self, '_primary_key', False), doc="primary key flag. indicates if this Column represents part or whole of a primary key.") foreign_keys = property(lambda self:getattr(self, '_foreign_keys', []), doc="foreign key accessor. points to a ForeignKey object which represents a Foreign Key placed on this column's ultimate ancestor.") @@ -637,7 +641,7 @@ class ColumnElement(Selectable, CompareMixin): if len(s) == 0: s.add(self) self.__orig_set = s - orig_set = property(_get_orig_set, _set_orig_set,doc="""a Set containing TableClause-bound, non-proxied ColumnElements for which this ColumnElement is a proxy. In all cases except for a column proxied from a Union (i.e. CompoundSelect), this set will be just one element.""") + orig_set = property(_get_orig_set, _set_orig_set,doc="""a Set containing _TableClause-bound, non-proxied ColumnElements for which this ColumnElement is a proxy. In all cases except for a column proxied from a Union (i.e. CompoundSelect), this set will be just one element.""") def shares_lineage(self, othercolumn): """returns True if the given ColumnElement has a common ancestor to this ColumnElement.""" @@ -648,10 +652,10 @@ class ColumnElement(Selectable, CompareMixin): return False def _make_proxy(self, selectable, name=None): """creates a new ColumnElement representing this ColumnElement as it appears in the select list - of a descending selectable. The default implementation returns a ColumnClause if a name is given, + of a descending selectable. The default implementation returns a _ColumnClause if a name is given, else just returns self.""" if name is not None: - co = ColumnClause(name, selectable) + co = _ColumnClause(name, selectable) co.orig_set = self.orig_set selectable.columns[name]= co return co @@ -659,7 +663,15 @@ class ColumnElement(Selectable, CompareMixin): return self class ColumnCollection(util.OrderedProperties): + """an ordered dictionary that stores a list of ColumnElement instances. + + overrides the __eq__() method to produce SQL clauses between sets of + correlated columns.""" def add(self, column): + """add a column to this collection. + + the key attribute of the column will be used as the hash key for this + dictionary.""" self[column.key] = column def __eq__(self, other): l = [] @@ -768,14 +780,14 @@ class FromClause(Selectable): def _proxy_column(self, column): return column._make_proxy(self) -class BindParamClause(ClauseElement, CompareMixin): +class _BindParamClause(ClauseElement, _CompareMixin): """represents a bind parameter. public constructor is the bindparam() function.""" def __init__(self, key, value, shortname=None, type=None): - """construct a BindParamClause. + """construct a _BindParamClause. key - the key for this bind param. will be used in the generated SQL statement for dialects that use named parameters. this value may be modified when part of a - compilation operation, if other BindParamClause objects exist with the same key, or if + compilation operation, if other _BindParamClause objects exist with the same key, or if its length is too long and truncation is required. value - initial value for this bind param. This value may be overridden by the @@ -784,10 +796,10 @@ class BindParamClause(ClauseElement, CompareMixin): shortname - defaults to the key, a 'short name' that will also identify this bind parameter, similar to an alias. the bind parameter keys sent to a statement compilation or compiled execution may match either the key or the shortname of the - corresponding BindParamClause objects. + corresponding _BindParamClause objects. type - a TypeEngine object that will be used to pre-process the value corresponding - to this BindParamClause at execution time.""" + to this _BindParamClause at execution time.""" self.key = key self.value = value self.shortname = shortname or key @@ -797,20 +809,20 @@ class BindParamClause(ClauseElement, CompareMixin): def _get_from_objects(self): return [] def copy_container(self): - return BindParamClause(self.key, self.value, self.shortname, self.type) + return _BindParamClause(self.key, self.value, self.shortname, self.type) def typeprocess(self, value, dialect): return self.type.dialect_impl(dialect).convert_bind_param(value, dialect) def compare(self, other): - """compares this BindParamClause to the given clause. + """compares this _BindParamClause to the given clause. Since compare() is meant to compare statement syntax, this method - returns True if the two BindParamClauses have just the same type.""" - return isinstance(other, BindParamClause) and other.type.__class__ == self.type.__class__ + returns True if the two _BindParamClauses have just the same type.""" + return isinstance(other, _BindParamClause) and other.type.__class__ == self.type.__class__ def _make_proxy(self, selectable, name = None): return self # return self.obj._make_proxy(selectable, name=self.name) -class TypeClause(ClauseElement): +class _TypeClause(ClauseElement): """handles a type keyword in a SQL statement. used by the Case statement.""" def __init__(self, type): self.type = type @@ -819,7 +831,7 @@ class TypeClause(ClauseElement): def _get_from_objects(self): return [] -class TextClause(ClauseElement): +class _TextClause(ClauseElement): """represents literal a SQL text fragment. public constructor is the text() function. @@ -849,7 +861,7 @@ class TextClause(ClauseElement): def _get_from_objects(self): return [] -class Null(ColumnElement): +class _Null(ColumnElement): """represents the NULL keyword in a SQL statement. public contstructor is the null() function.""" def __init__(self): @@ -873,7 +885,7 @@ class ClauseList(ClauseElement): return ClauseList(parens=self.parens, *clauses) def append(self, clause): if _is_literal(clause): - clause = TextClause(str(clause)) + clause = _TextClause(str(clause)) self.clauses.append(clause) def accept_visitor(self, visitor): for c in self.clauses: @@ -896,7 +908,7 @@ class ClauseList(ClauseElement): else: return False -class CompoundClause(ClauseList): +class _CompoundClause(ClauseList): """represents a list of clauses joined by an operator, such as AND or OR. extends ClauseList to add the operator as well as a from_objects accessor to help determine FROM objects in a SELECT statement.""" @@ -905,9 +917,9 @@ class CompoundClause(ClauseList): self.operator = operator def copy_container(self): clauses = [clause.copy_container() for clause in self.clauses] - return CompoundClause(self.operator, *clauses) + return _CompoundClause(self.operator, *clauses) def append(self, clause): - if isinstance(clause, CompoundClause): + if isinstance(clause, _CompoundClause): clause.parens = True ClauseList.append(self, clause) def accept_visitor(self, visitor): @@ -920,12 +932,12 @@ class CompoundClause(ClauseList): f += c._get_from_objects() return f def compare(self, other): - """compares this CompoundClause to the given item. + """compares this _CompoundClause to the given item. In addition to the regular comparison, has the special case that it - returns True if this CompoundClause has only one item, and that + returns True if this _CompoundClause has only one item, and that item matches the given item.""" - if not isinstance(other, CompoundClause): + if not isinstance(other, _CompoundClause): if len(self.clauses) == 1: return self.clauses[0].compare(other) if ClauseList.compare(self, other): @@ -933,7 +945,7 @@ class CompoundClause(ClauseList): else: return False -class CalculatedClause(ClauseList, ColumnElement): +class _CalculatedClause(ClauseList, ColumnElement): """ describes a calculated SQL expression that has a type, like CASE. extends ColumnElement to provide column-level comparison operators. """ def __init__(self, name, *clauses, **kwargs): @@ -943,19 +955,19 @@ class CalculatedClause(ClauseList, ColumnElement): ClauseList.__init__(self, *clauses) key = property(lambda self:self.name or "_calc_") def _process_from_dict(self, data, asfrom): - super(CalculatedClause, self)._process_from_dict(data, asfrom) + super(_CalculatedClause, self)._process_from_dict(data, asfrom) # this helps a Select object get the engine from us if asfrom: data.setdefault(self, self) def copy_container(self): clauses = [clause.copy_container() for clause in self.clauses] - return CalculatedClause(type=self.type, engine=self._engine, *clauses) + return _CalculatedClause(type=self.type, engine=self._engine, *clauses) def accept_visitor(self, visitor): for c in self.clauses: c.accept_visitor(visitor) visitor.visit_calculatedclause(self) def _bind_param(self, obj): - return BindParamClause(self.name, obj, type=self.type) + return _BindParamClause(self.name, obj, type=self.type) def select(self): return select([self]) def scalar(self): @@ -966,8 +978,8 @@ class CalculatedClause(ClauseList, ColumnElement): return self.type -class Function(CalculatedClause, FromClause): - """describes a SQL function. extends CalculatedClause turn the "clauselist" into function +class _Function(_CalculatedClause, FromClause): + """describes a SQL function. extends _CalculatedClause turn the "clauselist" into function arguments, also adds a "packagenames" argument""" def __init__(self, name, *clauses, **kwargs): self.name = name @@ -981,23 +993,23 @@ class Function(CalculatedClause, FromClause): if clause is None: clause = null() else: - clause = BindParamClause(self.name, clause, shortname=self.name, type=None) + clause = _BindParamClause(self.name, clause, shortname=self.name, type=None) self.clauses.append(clause) def copy_container(self): clauses = [clause.copy_container() for clause in self.clauses] - return Function(self.name, type=self.type, packagenames=self.packagenames, engine=self._engine, *clauses) + return _Function(self.name, type=self.type, packagenames=self.packagenames, engine=self._engine, *clauses) def accept_visitor(self, visitor): for c in self.clauses: c.accept_visitor(visitor) visitor.visit_function(self) -class Cast(ColumnElement): +class _Cast(ColumnElement): def __init__(self, clause, totype, **kwargs): if not hasattr(clause, 'label'): clause = literal(clause) self.type = sqltypes.to_instance(totype) self.clause = clause - self.typeclause = TypeClause(self.type) + self.typeclause = _TypeClause(self.type) def accept_visitor(self, visitor): self.clause.accept_visitor(visitor) self.typeclause.accept_visitor(visitor) @@ -1006,15 +1018,15 @@ class Cast(ColumnElement): return self.clause._get_from_objects() def _make_proxy(self, selectable, name=None): if name is not None: - co = ColumnClause(name, selectable, type=self.type) + co = _ColumnClause(name, selectable, type=self.type) co.orig_set = self.orig_set selectable.columns[name]= co return co else: return self -class FunctionGenerator(object): - """generates Function objects based on getattr calls""" +class _FunctionGenerator(object): + """generates _Function objects based on getattr calls""" def __init__(self, engine=None): self.__engine = engine self.__names = [] @@ -1023,9 +1035,9 @@ class FunctionGenerator(object): return self def __call__(self, *c, **kwargs): kwargs.setdefault('engine', self.__engine) - return Function(self.__names[-1], packagenames=self.__names[0:-1], *c, **kwargs) + return _Function(self.__names[-1], packagenames=self.__names[0:-1], *c, **kwargs) -class BinaryClause(ClauseElement): +class _BinaryClause(ClauseElement): """represents two clauses with an operator in between""" def __init__(self, left, right, operator, type=None): self.left = left @@ -1033,9 +1045,9 @@ class BinaryClause(ClauseElement): self.operator = operator self.type = sqltypes.to_instance(type) self.parens = False - if isinstance(self.left, BinaryClause) or hasattr(self.left, '_selectable'): + if isinstance(self.left, _BinaryClause) or hasattr(self.left, '_selectable'): self.left.parens = True - if isinstance(self.right, BinaryClause) or hasattr(self.right, '_selectable'): + if isinstance(self.right, _BinaryClause) or hasattr(self.right, '_selectable'): self.right.parens = True def copy_container(self): return self.__class__(self.left.copy_container(), self.right.copy_container(), self.operator) @@ -1050,24 +1062,24 @@ class BinaryClause(ClauseElement): self.left = self.right self.right = c def compare(self, other): - """compares this BinaryClause against the given BinaryClause.""" + """compares this _BinaryClause against the given _BinaryClause.""" return ( - isinstance(other, BinaryClause) and self.operator == other.operator and + isinstance(other, _BinaryClause) and self.operator == other.operator and self.left.compare(other.left) and self.right.compare(other.right) ) -class BooleanExpression(BinaryClause): +class _BooleanExpression(_BinaryClause): """represents a boolean expression, which is only useable in WHERE criterion.""" def __init__(self, *args, **kwargs): self.negate = kwargs.pop('negate', None) - super(BooleanExpression, self).__init__(*args, **kwargs) + super(_BooleanExpression, self).__init__(*args, **kwargs) def _negate(self): if self.negate is not None: - return BooleanExpression(self.left, self.right, self.negate, negate=self.operator, type=self.type) + return _BooleanExpression(self.left, self.right, self.negate, negate=self.operator, type=self.type) else: - return super(BooleanExpression, self)._negate() + return super(_BooleanExpression, self)._negate() -class BinaryExpression(BinaryClause, ColumnElement): +class _BinaryExpression(_BinaryClause, ColumnElement): """represents a binary expression, which can be in a WHERE criterion or in the column list of a SELECT. By adding "ColumnElement" to its inherited list, it becomes a Selectable unit which can be placed in the column list of a SELECT.""" @@ -1197,10 +1209,10 @@ class Alias(FromClause): engine = property(lambda s: s.selectable.engine) -class Label(ColumnElement): +class _Label(ColumnElement): def __init__(self, name, obj, type=None): self.name = name - while isinstance(obj, Label): + while isinstance(obj, _Label): obj = obj.obj self.obj = obj self.case_sensitive = getattr(obj, "case_sensitive", name.lower() != name) @@ -1218,7 +1230,7 @@ class Label(ColumnElement): return self.obj._make_proxy(selectable, name=self.name) legal_characters = util.Set(string.ascii_letters + string.digits + '_') -class ColumnClause(ColumnElement): +class _ColumnClause(ColumnElement): """represents a textual column clause in a SQL statement. May or may not be bound to an underlying Selectable.""" def __init__(self, text, selectable=None, type=None, hidden=False): @@ -1252,9 +1264,9 @@ class ColumnClause(ColumnElement): else: return [] def _bind_param(self, obj): - return BindParamClause(self._label, obj, shortname = self.name, type=self.type) + return _BindParamClause(self._label, obj, shortname = self.name, type=self.type) def _make_proxy(self, selectable, name = None): - c = ColumnClause(name or self.name, selectable, hidden=self.hidden, type=self.type) + c = _ColumnClause(name or self.name, selectable, hidden=self.hidden, type=self.type) c.orig_set = self.orig_set if not self.hidden: selectable.columns[c.name] = c @@ -1264,16 +1276,16 @@ class ColumnClause(ColumnElement): def _group_parenthesized(self): return False -class TableClause(FromClause): +class _TableClause(FromClause): def __init__(self, name, *columns): - super(TableClause, self).__init__(name) + super(_TableClause, self).__init__(name) self.name = self.fullname = name self._columns = ColumnCollection() self._foreign_keys = util.Set() self._primary_key = util.Set() for c in columns: self.append_column(c) - self._oid_column = ColumnClause('oid', self, hidden=True) + self._oid_column = _ColumnClause('oid', self, hidden=True) def named_with_column(self): return True @@ -1327,7 +1339,7 @@ class TableClause(FromClause): def _get_from_objects(self): return [self] -class SelectBaseMixin(object): +class _SelectBaseMixin(object): """base class for Select and CompoundSelects""" def order_by(self, *clauses): if len(clauses) == 1 and clauses[0] is None: @@ -1351,9 +1363,9 @@ class SelectBaseMixin(object): else: return [self] -class CompoundSelect(SelectBaseMixin, FromClause): +class CompoundSelect(_SelectBaseMixin, FromClause): def __init__(self, keyword, *selects, **kwargs): - SelectBaseMixin.__init__(self) + _SelectBaseMixin.__init__(self) self.keyword = keyword self.selects = selects self.use_labels = kwargs.pop('use_labels', False) @@ -1406,11 +1418,11 @@ class CompoundSelect(SelectBaseMixin, FromClause): else: return None -class Select(SelectBaseMixin, FromClause): +class Select(_SelectBaseMixin, FromClause): """represents a SELECT statement, with appendable clauses, as well as the ability to execute itself and return a result set.""" def __init__(self, columns=None, whereclause = None, from_obj = [], order_by = None, group_by=None, having=None, use_labels = False, distinct=False, for_update=False, nowait=False, engine=None, limit=None, offset=None, scalar=False, correlate=True): - SelectBaseMixin.__init__(self) + _SelectBaseMixin.__init__(self) self._froms = util.OrderedDict() self.use_labels = use_labels self.whereclause = None @@ -1441,8 +1453,8 @@ class Select(SelectBaseMixin, FromClause): self._text = None self._raw_columns = [] self._correlated = None - self._correlator = Select.CorrelatedVisitor(self, False) - self._wherecorrelator = Select.CorrelatedVisitor(self, True) + self._correlator = Select._CorrelatedVisitor(self, False) + self._wherecorrelator = Select._CorrelatedVisitor(self, True) self.group_by(*(group_by or [None])) self.order_by(*(order_by or [None])) @@ -1463,7 +1475,7 @@ class Select(SelectBaseMixin, FromClause): raise "this is a temporary assertion while we refactor SQL to not call 'name' on non-table Selectables" name = property(lambda s:s._foo()) #"SELECT statement") - class CorrelatedVisitor(ClauseVisitor): + class _CorrelatedVisitor(ClauseVisitor): """visits a clause, locates any Select clauses, and tells them that they should correlate their FROM list to that of their parent.""" def __init__(self, select, is_where): @@ -1488,7 +1500,7 @@ class Select(SelectBaseMixin, FromClause): def append_column(self, column): if _is_literal(column): - column = ColumnClause(str(column), self) + column = _ColumnClause(str(column), self) self._raw_columns.append(column) @@ -1513,7 +1525,7 @@ class Select(SelectBaseMixin, FromClause): self._append_condition('having', having) def _append_condition(self, attribute, condition): if type(condition) == str: - condition = TextClause(condition) + condition = _TextClause(condition) condition.accept_visitor(self._wherecorrelator) condition._process_from_dict(self._froms, False) if getattr(self, attribute) is not None: @@ -1526,7 +1538,7 @@ class Select(SelectBaseMixin, FromClause): def append_from(self, fromclause): if type(fromclause) == str: - fromclause = TextClause(fromclause) + fromclause = _TextClause(fromclause) fromclause.accept_visitor(self._correlator) fromclause._process_from_dict(self._froms, True) def _locate_oid_column(self): @@ -1576,7 +1588,7 @@ class Select(SelectBaseMixin, FromClause): self._engine = e return e # look through the columns (largely synomous with looking - # through the FROMs except in the case of CalculatedClause/Function) + # through the FROMs except in the case of _CalculatedClause/_Function) for cc in self._raw_columns: for c in cc.columns: if getattr(c, 'table', None) is self: @@ -1587,7 +1599,7 @@ class Select(SelectBaseMixin, FromClause): return e return None -class UpdateBase(ClauseElement): +class _UpdateBase(ClauseElement): """forms the base for INSERT, UPDATE, and DELETE statements.""" def _process_colparams(self, parameters): """receives the "values" of an INSERT or UPDATE statement and constructs @@ -1620,7 +1632,7 @@ class UpdateBase(ClauseElement): def _find_engine(self): return self.table.engine -class Insert(UpdateBase): +class _Insert(_UpdateBase): def __init__(self, table, values=None): self.table = table self.select = None @@ -1632,7 +1644,7 @@ class Insert(UpdateBase): visitor.visit_insert(self) -class Update(UpdateBase): +class _Update(_UpdateBase): def __init__(self, table, whereclause, values=None): self.table = table self.whereclause = whereclause @@ -1643,7 +1655,7 @@ class Update(UpdateBase): self.whereclause.accept_visitor(visitor) visitor.visit_update(self) -class Delete(UpdateBase): +class _Delete(_UpdateBase): def __init__(self, table, whereclause, **params): self.table = table self.whereclause = whereclause diff --git a/test/ext/selectresults.py b/test/ext/selectresults.py index 15d88e7b67..d82ad96fb6 100644 --- a/test/ext/selectresults.py +++ b/test/ext/selectresults.py @@ -25,7 +25,7 @@ class SelectResultsTest(PersistTest): objectstore.flush() def setUp(self): - self.query = Foo.mapper.query() + self.query = Query(Foo) self.orig = self.query.select_whereclause() self.res = self.query.select() @@ -106,7 +106,7 @@ class SelectResultsTest2(PersistTest): {'num':4,'t1id':2},{'num':5,'t1id':2},{'num':6,'t1id':3}) def setUp(self): - self.query = Obj1.mapper.query() + self.query = Query(Obj1) #self.orig = self.query.select_whereclause() #self.res = self.query.select() @@ -189,7 +189,7 @@ class CaseSensitiveTest(PersistTest): {'NUM':4,'T1ID':2},{'NUM':5,'T1ID':2},{'NUM':6,'T1ID':3}) def setUp(self): - self.query = Obj1.mapper.query() + self.query = Query(Obj1) #self.orig = self.query.select_whereclause() #self.res = self.query.select() diff --git a/test/orm/unitofwork.py b/test/orm/unitofwork.py index f9b512af15..aebb98f57d 100644 --- a/test/orm/unitofwork.py +++ b/test/orm/unitofwork.py @@ -347,7 +347,7 @@ class PKTest(UnitOfWorkTest): e.multi_rev = 2 ctx.current.flush() ctx.current.clear() - e2 = Entry.mapper.get((e.multi_id, 2)) + e2 = Query(Entry).get((e.multi_id, 2)) self.assert_(e is not e2 and e._instance_key == e2._instance_key) # this one works with sqlite since we are manually setting up pk values @@ -1325,7 +1325,7 @@ class SaveTest2(UnitOfWorkTest): Column('email_address', String(20)), redefine=True ) - x = sql.Join(self.users, self.addresses) + x = sql.join(self.users, self.addresses) # raise repr(self.users) + repr(self.users.primary_key) # raise repr(self.addresses) + repr(self.addresses.foreign_keys) self.users.create() diff --git a/test/zblog/mappers.py b/test/zblog/mappers.py index 07fce71be5..244a53d0e9 100644 --- a/test/zblog/mappers.py +++ b/test/zblog/mappers.py @@ -66,7 +66,7 @@ def zblog_mappers(): 'user':relation(user.User, lazy=True, backref=backref('posts', cascade="all, delete-orphan")), 'blog':relation(Blog, lazy=True, backref=backref('posts', cascade="all, delete-orphan")), 'topics':relation(TopicAssociation, lazy=False, private=True, association=Topic, backref='post') - }, is_primary=True, order_by=[desc(posts_with_ccount.c.datetime)]) + }, order_by=[desc(posts_with_ccount.c.datetime)]) # comment mapper. This mapper is handling a hierarchical relationship on itself, and contains @@ -77,7 +77,7 @@ def zblog_mappers(): 'user':relation(user.User, lazy=False, backref=backref('comments', cascade="all, delete-orphan")), 'parent':relation(Comment, primaryjoin=tables.comments.c.parent_comment_id==tables.comments.c.comment_id, foreignkey=tables.comments.c.comment_id, lazy=True, uselist=False), 'replies':relation(Comment,primaryjoin=tables.comments.c.parent_comment_id==tables.comments.c.comment_id, lazy=True, uselist=True, cascade="all"), - }, is_primary=True) + }) # we define one special find-by for the comments of a post, which is going to make its own "noload" # mapper and organize the comments into their correct hierarchy in one pass. hierarchical