From: Mike Bayer Date: Sun, 5 Sep 2010 18:44:58 +0000 (-0400) Subject: - rewrote the "connections" section X-Git-Tag: rel_0_6_4~12 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=703ce7f1791c1143eb983c38e3bd627984ea1953;p=thirdparty%2Fsqlalchemy%2Fsqlalchemy.git - rewrote the "connections" section - improved pool docs - typos etc. - ClauseElement.execute() and scalar() make no sense - these are depreacted. The official home is Executable. - alias() is not executable, allowing it is sloppy so this goes under the deprecated umbrella --- diff --git a/doc/build/core/connections.rst b/doc/build/core/connections.rst index 92e84c468b..1bd7278328 100644 --- a/doc/build/core/connections.rst +++ b/doc/build/core/connections.rst @@ -1,67 +1,113 @@ +.. _connections_toplevel: + ===================================== Working with Engines and Connections ===================================== .. module:: sqlalchemy.engine.base -Recall from the beginning of :ref:`engines_toplevel` that the :class:`.Engine` provides a -``connect()`` method which returns a -:class:`~sqlalchemy.engine.base.Connection` object. -:class:`~sqlalchemy.engine.base.Connection` is a *proxy* object which -maintains a reference to a DBAPI connection instance. The ``close()`` method -on :class:`~sqlalchemy.engine.base.Connection` does not actually close the -DBAPI connection, but instead returns it to the connection pool referenced by -the :class:`~sqlalchemy.engine.base.Engine`. -:class:`~sqlalchemy.engine.base.Connection` will also automatically return its -resources to the connection pool when the object is garbage collected, i.e. -its ``__del__()`` method is called. When using the standard C implementation -of Python, this method is usually called immediately as soon as the object is -dereferenced. With other Python implementations such as Jython, this is not so -guaranteed. - -The ``execute()`` methods on both :class:`~sqlalchemy.engine.base.Engine` and -:class:`~sqlalchemy.engine.base.Connection` can also receive SQL clause -constructs as well:: +This section details direct usage of the :class:`.Engine`, +:class:`.Connection`, and related objects. Its important to note that when +using the SQLAlchemy ORM, these objects are not generally accessed; instead, +the :class:`.Session` object is used as the interface to the database. +However, for applications that are built around direct usage of textual SQL +statements and/or SQL expression constructs without involvement by the ORM's +higher level management services, the :class:`.Engine` and +:class:`.Connection` are king (and queen?) - read on. - connection = engine.connect() - result = connection.execute(select([table1], table1.c.col1==5)) - for row in result: - print row['col1'], row['col2'] - connection.close() - -The above SQL construct is known as a ``select()``. The full range of SQL -constructs available are described in :ref:`sqlexpression_toplevel`. +Basic Usage +=========== -Both :class:`~sqlalchemy.engine.base.Connection` and -:class:`~sqlalchemy.engine.base.Engine` fulfill an interface known as -:class:`~sqlalchemy.engine.base.Connectable` which specifies common -functionality between the two objects, namely being able to call ``connect()`` -to return a :class:`~sqlalchemy.engine.base.Connection` object -(:class:`~sqlalchemy.engine.base.Connection` just returns itself), and being -able to call ``execute()`` to get a result set. Following this, most -SQLAlchemy functions and objects which accept an -:class:`~sqlalchemy.engine.base.Engine` as a parameter or attribute with which -to execute SQL will also accept a :class:`~sqlalchemy.engine.base.Connection`. -This argument is named ``bind``:: +Recall from :ref:`engines_toplevel` that an :class:`.Engine` is created via +the :func:`.create_engine` call:: - engine = create_engine('sqlite:///:memory:') + engine = create_engine('mysql://scott:tiger@localhost/test') + +The typical usage of :func:`.create_engine()` is once per particular database +URL, held globally for the lifetime of a single application process. A single +:class:`.Engine` manages many individual DBAPI connections on behalf of the +process and is intended to be called upon in a concurrent fashion. The +:class:`.Engine` is **not** synonymous to the DBAPI ``connect`` function, +which represents just one connection resource - the :class:`.Engine` is most +efficient when created just once at the module level of an application, not +per-object or per-function call. + +For a multiple-process application that uses the ``os.fork`` system call, or +for example the Python ``multiprocessing`` module, it's usually required that a +separate :class:`.Engine` be used for each child process. This is because the +:class:`.Engine` maintains a reference to a connection pool that ultimately +references DBAPI connections - these tend to not be portable across process +boundaries. An :class:`.Engine` that is configured not to use pooling (which +is achieved via the usage of :class:`.NullPool`) does not have this +requirement. + +The engine can be used directly to issue SQL to the database. The most generic +way is first procure a connection resource, which you get via the :class:`connect` method:: - # specify some Table metadata - metadata = MetaData() - table = Table('sometable', metadata, Column('col1', Integer)) + connection = engine.connect() + result = connection.execute("select username from users") + for row in result: + print "username:", row['username'] + connection.close() - # create the table with the Engine - table.create(bind=engine) +The connection is an instance of :class:`.Connection`, +which is a **proxy** object for an actual DBAPI connection. The DBAPI +connection is retrieved from the connection pool at the point at which +:class:`.Connection` is created. + +The returned result is an instance of :class:`.ResultProxy`, which +references a DBAPI cursor and provides a largely compatible interface +with that of the DBAPI cursor. The DBAPI cursor will be closed +by the :class:`.ResultProxy` when all of its result rows (if any) are +exhausted. A :class:`.ResultProxy` that returns no rows, such as that of +an UPDATE statement (without any returned rows), +releases cursor resources immediately upon construction. + +When the :meth:`~.Connection.close` method is called, the referenced DBAPI +connection is returned to the connection pool. From the perspective +of the database itself, nothing is actually "closed", assuming pooling is +in use. The pooling mechanism issues a ``rollback()`` call on the DBAPI +connection so that any transactional state or locks are removed, and +the connection is ready for its next usage. + +The above procedure can be performed in a shorthand way by using the +:meth:`~.Engine.execute` method of :class:`.Engine` itself:: + + result = engine.execute("select username from users") + for row in result: + print "username:", row['username'] - # drop the table with a Connection off the Engine - connection = engine.connect() - table.drop(bind=connection) +Where above, the :meth:`~.Engine.execute` method acquires a new +:class:`.Connection` on its own, executes the statement with that object, +and returns the :class:`.ResultProxy`. In this case, the :class:`.ResultProxy` +contains a special flag known as ``close_with_result``, which indicates +that when its underlying DBAPI cursor is closed, the :class:`.Connection` +object itself is also closed, which again returns the DBAPI connection +to the connection pool, releasing transactional resources. -.. index:: - single: thread safety; connections +If the :class:`.ResultProxy` potentially has rows remaining, it can be +instructed to close out its resources explicitly:: -Connection API -=============== + result.close() + +If the :class:`.ResultProxy` has pending rows remaining and is dereferenced by +the application without being closed, Python garbage collection will +ultimately close out the cursor as well as trigger a return of the pooled +DBAPI connection resource to the pool (SQLAlchemy achieves this by the usage +of weakref callbacks - *never* the ``__del__`` method) - however it's never a +good idea to rely upon Python garbage collection to manage resources. + +We have just summarized two of three usage patterns that are possible +with the :class:`.Engine`. When the :class:`.Connection` object is used +explicitly, it's referred to as **explicit execution**. When the +:meth:`~.Engine.execute` method of :class:`.Engine` is used, this +pattern is referred to as **explicit, connectionless execution**. The +third pattern is known as **implicit execution** and is described later. + +Our example above illustrated the execution of a textual SQL string. +The :meth:`~.Connection.execute` method can of course accommodate more than +that, including the variety of SQL expression constructs described +in :ref:`sqlexpression_toplevel`. .. autoclass:: Connection :show-inheritance: @@ -71,16 +117,10 @@ Connection API :show-inheritance: :members: -Engine API -=========== - .. autoclass:: Engine :show-inheritance: :members: -Result Object API -================= - .. autoclass:: sqlalchemy.engine.base.ResultProxy :members: @@ -164,50 +204,49 @@ one exists. Understanding Autocommit ======================== -The previous transaction example illustrates how to use -:class:`~sqlalchemy.engine.base.Transaction` so that several executions can -take part in the same transaction. What happens when we issue an INSERT, -UPDATE or DELETE call without using -:class:`~sqlalchemy.engine.base.Transaction`? The answer is **autocommit**. -While many DBAPIs implement a flag called ``autocommit``, the current -SQLAlchemy behavior is such that it implements its own autocommit. This is -achieved by detecting statements which represent data-changing operations, -i.e. INSERT, UPDATE, DELETE, etc., and then issuing a COMMIT automatically if -no transaction is in progress. The detection is based on compiled statement -attributes, or in the case of a text-only statement via regular expressions. - -.. sourcecode:: python+sql +The previous transaction example illustrates how to use :class:`.Transaction` +so that several executions can take part in the same transaction. What happens +when we issue an INSERT, UPDATE or DELETE call without using +:class:`.Transaction`? The answer is **autocommit**. While many DBAPI +implementation provide various special "non-transactional" modes, the current +SQLAlchemy behavior is such that it implements its own "autocommit" which +works completely consistently across all backends. This is achieved by +detecting statements which represent data-changing operations, i.e. INSERT, +UPDATE, DELETE, as well as data definition language (DDL) statements such as +CREATE TABLE, ALTER TABLE, and then issuing a COMMIT automatically if no +transaction is in progress. The detection is based on compiled statement +attributes, or in the case of a text-only statement via regular expressions:: conn = engine.connect() conn.execute("INSERT INTO users VALUES (1, 'john')") # autocommits +Full control of the "autocommit" behavior is available using the generative +:meth:`.Connection.execution_options` method provided on :class:`.Connection`, +:class:`.Engine`, :class:`.Executable`, using the "autocommit" flag which will +turn on or off the autocommit for the selected scope. For example, a +:func:`.text` construct representing a stored procedure that commits might use +it so that a SELECT statement will issue a COMMIT:: + + engine.execute(text("SELECT my_mutating_procedure()").execution_options(autocommit=True)) + .. _dbengine_implicit: Connectionless Execution, Implicit Execution ============================================= -Recall from the first section we mentioned executing with and without a -:class:`~sqlalchemy.engine.base.Connection`. ``Connectionless`` execution -refers to calling the ``execute()`` method on an object which is not a -:class:`~sqlalchemy.engine.base.Connection`, which could be on the -:class:`~sqlalchemy.engine.base.Engine` itself, or could be a constructed SQL -object. When we say "implicit", we mean that we are calling the ``execute()`` -method on an object which is neither a -:class:`~sqlalchemy.engine.base.Connection` nor an -:class:`~sqlalchemy.engine.base.Engine` object; this can only be used with -constructed SQL objects which have their own ``execute()`` method, and can be -"bound" to an :class:`~sqlalchemy.engine.base.Engine`. A description of -"constructed SQL objects" may be found in :ref:`sqlexpression_toplevel`. - -A summary of all three methods follows below. First, assume the usage of the -following :class:`~sqlalchemy.schema.MetaData` and -:class:`~sqlalchemy.schema.Table` objects; while we haven't yet introduced -these concepts, for now you only need to know that we are representing a -database table, and are creating an "executable" SQL construct which issues a -statement to the database. These objects are described in -:ref:`metadata_toplevel`. +Recall from the first section we mentioned executing with and without explicit +usage of :class:`.Connection`. "Connectionless" execution +refers to the usage of the ``execute()`` method on an object which is not a +:class:`.Connection`. This was illustrated using the :meth:`~.Engine.execute` method +of :class:`.Engine`. -.. sourcecode:: python+sql +A third case exists, which is to use the :meth:`~.Executable.execute` method of +any :class:`.Executable` construct, which is a marker for SQL expression objects +that support execution. The SQL expression object itself references an +:class:`.Engine` or :class:`.Connection` known as the **bind**, which it uses +in order to provide so-called "implicit" execution services. + +Given a table as below:: meta = MetaData() users_table = Table('users', meta, @@ -242,7 +281,7 @@ Implicit execution is also connectionless, and calls the ``execute()`` method on the expression itself, utilizing the fact that either an :class:`~sqlalchemy.engine.base.Engine` or :class:`~sqlalchemy.engine.base.Connection` has been *bound* to the expression -object (binding is discussed further in the next section, +object (binding is discussed further in :ref:`metadata_toplevel`): .. sourcecode:: python+sql @@ -258,21 +297,20 @@ In both "connectionless" examples, the :class:`~sqlalchemy.engine.base.Connection` is created behind the scenes; the :class:`~sqlalchemy.engine.base.ResultProxy` returned by the ``execute()`` call references the :class:`~sqlalchemy.engine.base.Connection` used to issue -the SQL statement. When we issue ``close()`` on the -:class:`~sqlalchemy.engine.base.ResultProxy`, or if the result set object -falls out of scope and is garbage collected, the underlying -:class:`~sqlalchemy.engine.base.Connection` is closed for us, resulting in the -DBAPI connection being returned to the pool. +the SQL statement. When the :class:`.ResultProxy` is closed, the underlying +:class:`.Connection` is closed for us, resulting in the +DBAPI connection being returned to the pool with transactional resources removed. .. _threadlocal_strategy: Using the Threadlocal Execution Strategy ------------------------------------------ +======================================== -The "threadlocal" engine strategy is used by non-ORM applications which wish -to bind a transaction to the current thread, such that all parts of the +The "threadlocal" engine strategy is an optional feature which +can be used by non-ORM applications to associate transactions +with the current thread, such that all parts of the application can participate in that transaction implicitly without the need to -explicitly reference a :class:`~sqlalchemy.engine.base.Connection`. +explicitly reference a :class:`.Connection`. "threadlocal" is designed for a very specific pattern of use, and is not appropriate unless this very specfic pattern, described below, is what's desired. It has **no impact** on the "thread safety" of SQLAlchemy components @@ -282,59 +320,22 @@ or one's application. It also should not be used when using an ORM transaction and itself handles the job of maintaining connection and transactional resources. -Enabling ``threadlocal`` is achieved as follows: - -.. sourcecode:: python+sql +Enabling ``threadlocal`` is achieved as follows:: db = create_engine('mysql://localhost/test', strategy='threadlocal') -When the engine above is used in a "connectionless" style, meaning -``engine.execute()`` is called, a DBAPI connection is retrieved from the -connection pool and then associated with the current thread. Subsequent -operations on the :class:`~sqlalchemy.engine.base.Engine` while the DBAPI -connection remains checked out will make use of the *same* DBAPI connection -object. The connection stays allocated until all returned -:class:`~sqlalchemy.engine.base.ResultProxy` objects are closed, which occurs -for a particular :class:`~sqlalchemy.engine.base.ResultProxy` after all -pending results are fetched, or immediately for an operation which returns no -rows (such as an INSERT). - -.. sourcecode:: python+sql +The above :class:`.Engine` will now acquire a :class:`.Connection` using +connection resources derived from a thread-local variable whenever +:meth:`.Engine.execute` or :meth:`.Engine.contextual_connect` is called. This +connection resource is maintained as long as it is referenced, which allows +multiple points of an application to share a transaction while using +connectionless, explicit execution:: - # execute one statement and receive results. r1 now references a DBAPI connection resource. - r1 = db.execute("select * from table1") - - # execute a second statement and receive results. r2 now references the *same* resource as r1 - r2 = db.execute("select * from table2") - - # fetch a row on r1 (assume more results are pending) - row1 = r1.fetchone() - - # fetch a row on r2 (same) - row2 = r2.fetchone() - - # close r1. the connection is still held by r2. - r1.close() - - # close r2. with no more references to the underlying connection resources, they - # are returned to the pool. - r2.close() - -The above example does not illustrate any pattern that is particularly useful, -as it is not a frequent occurence that two execute/result fetching operations -"leapfrog" one another. There is a slight savings of connection pool checkout -overhead between the two operations, and an implicit sharing of the same -transactional context, but since there is no explicitly declared transaction, -this association is short lived. - -The real usage of "threadlocal" comes when we want several operations to occur -within the scope of a shared transaction. The -:class:`~sqlalchemy.engine.base.Engine` now has ``begin()``, ``commit()`` and -``rollback()`` methods which will retrieve a connection resource from the pool -and establish a new transaction, maintaining the connection against the -current thread until the transaction is committed or rolled back: + def call_operation1(): + engine.execute("insert into users values (?, ?)", 1, "john") -.. sourcecode:: python+sql + def call_operation2(): + users.update(users.c.user_id==5).execute(name='ed') db.begin() try: @@ -344,25 +345,9 @@ current thread until the transaction is committed or rolled back: except: db.rollback() -``call_operation1()`` and ``call_operation2()`` can make use of the -:class:`~sqlalchemy.engine.base.Engine` as a global variable, using the -"connectionless" execution style, and their operations will participate in the -same transaction: - -.. sourcecode:: python+sql - - def call_operation1(): - engine.execute("insert into users values (?, ?)", 1, "john") - - def call_operation2(): - users.update(users.c.user_id==5).execute(name='ed') - -When using threadlocal, operations that do call upon the ``engine.connect()`` -method will receive a :class:`~sqlalchemy.engine.base.Connection` that is -**outside** the scope of the transaction. This can be used for operations such -as logging the status of an operation regardless of transaction success: - -.. sourcecode:: python+sql +Explicit execution can be mixed with connectionless execution by +using the :class:`.Engine.connect` method to acquire a :class:`.Connection` +that is not part of the threadlocal scope:: db.begin() conn = db.connect() @@ -378,27 +363,13 @@ as logging the status of an operation regardless of transaction success: finally: conn.close() -Functions which are written to use an explicit -:class:`~sqlalchemy.engine.base.Connection` object, but wish to participate in -the threadlocal transaction, can receive their -:class:`~sqlalchemy.engine.base.Connection` object from the -``contextual_connect()`` method, which returns a -:class:`~sqlalchemy.engine.base.Connection` that is **inside** the scope of -the transaction: - -.. sourcecode:: python+sql +To access the :class:`.Connection` that is bound to the threadlocal scope, +call :meth:`.Engine.contextual_connect`:: conn = db.contextual_connect() call_operation3(conn) conn.close() -Calling ``close()`` on the "contextual" connection does not release the -connection resources to the pool if other resources are making use of it. A -resource-counting mechanism is employed so that the connection is released -back to the pool only when all users of that connection, including the -transaction established by ``engine.begin()``, have been completed. - -So remember - if you're not sure if you need to use ``strategy="threadlocal"`` -or not, the answer is **no** ! It's driven by a specific programming pattern -that is generally not the norm. - +Calling :meth:`~.Connection.close` on the "contextual" connection does not release +its resources until all other usages of that resource are closed as well, including +that any ongoing transactions are rolled back or committed. \ No newline at end of file diff --git a/doc/build/core/engines.rst b/doc/build/core/engines.rst index 52ef3036ae..c6364b2fd5 100644 --- a/doc/build/core/engines.rst +++ b/doc/build/core/engines.rst @@ -1,8 +1,9 @@ .. _engines_toplevel: -================ -Database Engines -================ +==================== +Engine Configuration +==================== + The **Engine** is the starting point for any SQLAlchemy application. It's "home base" for the actual database and its DBAPI, delivered to the SQLAlchemy application through a connection pool and a **Dialect**, which describes how @@ -31,70 +32,12 @@ Creating an engine is just a matter of issuing a single call, The above engine invokes the ``postgresql`` dialect and a connection pool which references ``localhost:5432``. -Note that the appropriate usage of :func:`create_engine()` is once per -particular configuration, held globally for the lifetime of a single -application process (not including child processes via ``fork()`` - these -would require a new engine). A single :class:`~sqlalchemy.engine.base.Engine` -manages connections on behalf of the process and is intended to be called upon -in a concurrent fashion. Creating engines for each particular operation is not -the intended usage. - -The engine can be used directly to issue SQL to the database. The most generic -way is to use connections, which you get via the ``connect()`` method:: - - connection = engine.connect() - result = connection.execute("select username from users") - for row in result: - print "username:", row['username'] - connection.close() - -The connection is an instance of :class:`~sqlalchemy.engine.base.Connection`, -which is a **proxy** object for an actual DBAPI connection. The returned -result is an instance of :class:`~sqlalchemy.engine.ResultProxy`, which acts -very much like a DBAPI cursor. - -When you say ``engine.connect()``, a new -:class:`~sqlalchemy.engine.base.Connection` object is created, and a DBAPI -connection is retrieved from the connection pool. Later, when you call -``connection.close()``, the DBAPI connection is returned to the pool; nothing -is actually "closed" from the perspective of the database. - -To execute some SQL more quickly, you can skip the -:class:`~sqlalchemy.engine.base.Connection` part and just say:: - - result = engine.execute("select username from users") - for row in result: - print "username:", row['username'] - result.close() - -Where above, the ``execute()`` method on the -:class:`~sqlalchemy.engine.base.Engine` does the ``connect()`` part for you, -and returns the :class:`~sqlalchemy.engine.base.ResultProxy` directly. The -actual :class:`~sqlalchemy.engine.base.Connection` is *inside* the -:class:`~sqlalchemy.engine.base.ResultProxy`, waiting for you to finish -reading the result. In this case, when you ``close()`` the -:class:`~sqlalchemy.engine.base.ResultProxy`, the underlying -:class:`~sqlalchemy.engine.base.Connection` is closed, which returns the DBAPI -connection to the pool. - -To summarize the above two examples, when you use a -:class:`~sqlalchemy.engine.base.Connection` object, it's known as **explicit -execution**. When you don't see the -:class:`~sqlalchemy.engine.base.Connection` object, but you still use the -``execute()`` method on the :class:`~sqlalchemy.engine.base.Engine`, it's -called **explicit, connectionless execution**. A third variant of execution -also exists called **implicit execution**; this will be described later. - -The :class:`~sqlalchemy.engine.base.Engine` and -:class:`~sqlalchemy.engine.base.Connection` can do a lot more than what we -illustrated above; SQL strings are only its most rudimentary function. Later -chapters will describe how "constructed SQL" expressions can be used with -engines; in many cases, you don't have to deal with the -:class:`~sqlalchemy.engine.base.Engine` at all after it's created. The Object -Relational Mapper (ORM), an optional feature of SQLAlchemy, also uses the -:class:`~sqlalchemy.engine.base.Engine` in order to get at connections; that's -also a case where you can often create the engine once, and then forget about -it. +The :class:`.Engine`, once created, can either be used directly to interact with the database, +or can be passed to a :class:`.Session` object to work with the ORM. This section +covers the details of configuring an :class:`.Engine`. The next section, :ref:`connections_toplevel`, +will detail the usage API of the :class:`.Engine` and similar, typically for non-ORM +applications. + .. _supported_dbapis: diff --git a/doc/build/core/pooling.rst b/doc/build/core/pooling.rst index d37425e3a6..7af56eab8f 100644 --- a/doc/build/core/pooling.rst +++ b/doc/build/core/pooling.rst @@ -77,31 +77,37 @@ Builtin Pool Implementations ---------------------------- .. autoclass:: AssertionPool - :members: :show-inheritance: + .. automethod:: __init__ + .. autoclass:: NullPool - :members: :show-inheritance: + .. automethod:: __init__ + .. autoclass:: sqlalchemy.pool.Pool - :members: - :show-inheritance: - :undoc-members: - :inherited-members: + + .. automethod:: __init__ + .. automethod:: connect + .. automethod:: dispose + .. automethod:: recreate .. autoclass:: sqlalchemy.pool.QueuePool - :members: :show-inheritance: + .. automethod:: __init__ + .. autoclass:: SingletonThreadPool - :members: :show-inheritance: + .. automethod:: __init__ + .. autoclass:: StaticPool - :members: :show-inheritance: + .. automethod:: __init__ + Pooling Plain DB-API Connections -------------------------------- diff --git a/doc/build/core/schema.rst b/doc/build/core/schema.rst index 9f6e9be387..13b70af8b4 100644 --- a/doc/build/core/schema.rst +++ b/doc/build/core/schema.rst @@ -361,10 +361,10 @@ Backend-Specific Options mysql_engine='InnoDB' ) -Other backends may support table-level options as well. +Other backends may support table-level options as well - these would be described in the individual documentation sections for each dialect. -API Constructs --------------- +Schema API Constructs +--------------------- .. autoclass:: Column :members: @@ -797,8 +797,8 @@ executed standalone like a SQL expression, which has the effect of calling its seq = Sequence('some_sequence') nextid = connection.execute(seq) -API Constructs --------------- +Default Geneation API Constructs +-------------------------------- .. autoclass:: ColumnDefault :show-inheritance: @@ -976,8 +976,8 @@ Note that these clauses are not supported on SQLite, and require ``InnoDB`` tables when used with MySQL. They may also not be supported on other databases. -API Constructs -~~~~~~~~~~~~~~~ +Foreign Key API Constructs +~~~~~~~~~~~~~~~~~~~~~~~~~~ .. autoclass:: ForeignKey :members: diff --git a/lib/sqlalchemy/dialects/mssql/base.py b/lib/sqlalchemy/dialects/mssql/base.py index 88ca36dbdf..95a5bf4c44 100644 --- a/lib/sqlalchemy/dialects/mssql/base.py +++ b/lib/sqlalchemy/dialects/mssql/base.py @@ -104,7 +104,7 @@ Compatibility Levels MSSQL supports the notion of setting compatibility levels at the database level. This allows, for instance, to run a database that is compatibile with SQL2000 while running on a SQL2005 database -server. ``server_version_info`` will always retrun the database +server. ``server_version_info`` will always return the database server version information (in this case SQL2005) and not the compatibiility level information. Because of this, if running under a backwards compatibility mode SQAlchemy may attempt to use T-SQL diff --git a/lib/sqlalchemy/engine/base.py b/lib/sqlalchemy/engine/base.py index 20e7e23388..7098e695df 100644 --- a/lib/sqlalchemy/engine/base.py +++ b/lib/sqlalchemy/engine/base.py @@ -797,9 +797,10 @@ class Connectable(object): class Connection(Connectable): """Provides high-level functionality for a wrapped DB-API connection. - Provides execution support for string-based SQL statements as well - as ClauseElement, Compiled and DefaultGenerator objects. Provides - a :meth:`begin` method to return Transaction objects. + Provides execution support for string-based SQL statements as well as + :class:`.ClauseElement`, :class:`.Compiled` and :class:`.DefaultGenerator` + objects. Provides a :meth:`begin` method to return :class:`.Transaction` + objects. The Connection object is **not** thread-safe. While a Connection can be shared among threads using properly synchronized access, it is still @@ -825,9 +826,9 @@ class Connection(Connectable): _branch=False, _execution_options=None): """Construct a new Connection. - Connection objects are typically constructed by an - :class:`~sqlalchemy.engine.Engine`, see the ``connect()`` and - ``contextual_connect()`` methods of Engine. + The constructor here is not public and is only called only by an + :class:`.Engine`. See :meth:`.Engine.connect` and + :meth:`.Engine.contextual_connect` methods. """ self.engine = engine @@ -1167,7 +1168,22 @@ class Connection(Connectable): return self.execute(object, *multiparams, **params).scalar() def execute(self, object, *multiparams, **params): - """Executes and returns a ResultProxy.""" + """Executes the given construct and returns a :class:`.ResultProxy`. + + The construct can be one of: + + * a textual SQL string + * any :class:`.ClauseElement` construct that is also + a subclass of :class:`.Executable`, such as a + :func:`.select` construct + * a :class:`.FunctionElement`, such as that generated + by :attr:`.func`, will be automatically wrapped in + a SELECT statement, which is then executed. + * a :class:`.DDLElement` object + * a :class:`.DefaultGenerator` object + * a :class:`.Compiled` object + + """ for c in type(object).__mro__: if c in Connection.executors: @@ -1739,7 +1755,20 @@ class Engine(Connectable, log.Identified): conn.close() def execute(self, statement, *multiparams, **params): - """Executes and returns a ResultProxy.""" + """Executes the given construct and returns a :class:`.ResultProxy`. + + The arguments are the same as those used by + :meth:`.Connection.execute`. + + Here, a :class:`.Connection` is acquired using the + :meth:`~.Engine.contextual_connect` method, and the statement executed + with that connection. The returned :class:`.ResultProxy` is flagged + such that when the :class:`.ResultProxy` is exhausted and its + underlying cursor is closed, the :class:`.Connection` created here + will also be closed, which allows its associated DBAPI connection + resource to be returned to the connection pool. + + """ connection = self.contextual_connect(close_with_result=True) return connection.execute(statement, *multiparams, **params) @@ -1756,16 +1785,30 @@ class Engine(Connectable, log.Identified): return connection._execute_compiled(compiled, multiparams, params) def connect(self, **kwargs): - """Return a newly allocated Connection object.""" + """Return a new :class:`.Connection` object. + + The :class:`.Connection`, upon construction, will procure a DBAPI connection + from the :class:`.Pool` referenced by this :class:`.Engine`, + returning it back to the :class:`.Pool` after the :meth:`.Connection.close` + method is called. + + """ return self.Connection(self, **kwargs) def contextual_connect(self, close_with_result=False, **kwargs): - """Return a Connection object which may be newly allocated, - or may be part of some ongoing context. - - This Connection is meant to be used by the various - "auto-connecting" operations. + """Return a :class:`.Connection` object which may be part of some ongoing context. + + By default, this method does the same thing as :meth:`.Engine.connect`. + Subclasses of :class:`.Engine` may override this method + to provide contextual behavior. + + :param close_with_result: When True, the first :class:`.ResultProxy` created + by the :class:`.Connection` will call the :meth:`.Connection.close` method + of that connection as soon as any pending result rows are exhausted. + This is used to supply the "connectionless execution" behavior provided + by the :meth:`.Engine.execute` method. + """ return self.Connection(self, diff --git a/lib/sqlalchemy/pool.py b/lib/sqlalchemy/pool.py index 9d37b18384..c70a41069f 100644 --- a/lib/sqlalchemy/pool.py +++ b/lib/sqlalchemy/pool.py @@ -132,13 +132,30 @@ class Pool(log.Identified): self.add_listener(l) def unique_connection(self): + """Produce a DBAPI connection that is not referenced by any + thread-local context. + + This method is different from :meth:`.Pool.connect` only if the + ``use_threadlocal`` flag has been set to ``True``. + + """ + return _ConnectionFairy(self).checkout() def create_connection(self): + """Called by subclasses to create a new ConnectionRecord.""" + return _ConnectionRecord(self) def recreate(self): - """Return a new instance with identical creation arguments.""" + """Return a new :class:`.Pool`, of the same class as this one + and configured with identical creation arguments. + + This method is used in conjunection with :meth:`dispose` + to close out an entire :class:`.Pool` and create a new one in + its place. + + """ raise NotImplementedError() @@ -149,11 +166,19 @@ class Pool(log.Identified): remaining open, It is advised to not reuse the pool once dispose() is called, and to instead use a new pool constructed by the recreate() method. + """ raise NotImplementedError() def connect(self): + """Return a DBAPI connection from the pool. + + The connection is instrumented such that when its + ``close()`` method is called, the connection will be returned to + the pool. + + """ if not self._use_threadlocal: return _ConnectionFairy(self).checkout() @@ -169,17 +194,33 @@ class Pool(log.Identified): return agent.checkout() def return_conn(self, record): + """Given a _ConnectionRecord, return it to the :class:`.Pool`. + + This method is called when an instrumented DBAPI connection + has its ``close()`` method called. + + """ if self._use_threadlocal and hasattr(self._threadconns, "current"): del self._threadconns.current self.do_return_conn(record) def get(self): + """Return a non-instrumented DBAPI connection from this :class:`.Pool`. + + This is called by ConnectionRecord in order to get its DBAPI + resource. + + """ return self.do_get() def do_get(self): + """Implementation for :meth:`get`, supplied by subclasses.""" + raise NotImplementedError() def do_return_conn(self, conn): + """Implementation for :meth:`return_conn`, supplied by subclasses.""" + raise NotImplementedError() def status(self): diff --git a/lib/sqlalchemy/sql/expression.py b/lib/sqlalchemy/sql/expression.py index d184816ab2..5235a696bb 100644 --- a/lib/sqlalchemy/sql/expression.py +++ b/lib/sqlalchemy/sql/expression.py @@ -1269,10 +1269,13 @@ class ClauseElement(Visitable): return engine else: return None - + + @util.deprecated("0.7", "Only SQL expressions which subclass :class:`.Executable` " + "may provide the :func:`.execute` method.") def execute(self, *multiparams, **params): - """Compile and execute this :class:`ClauseElement`.""" - + """Compile and execute this :class:`ClauseElement`. + + """ e = self.bind if e is None: label = getattr(self, 'description', self.__class__.__name__) @@ -1284,6 +1287,8 @@ class ClauseElement(Visitable): raise exc.UnboundExecutionError(msg) return e._execute_clauseelement(self, multiparams, params) + @util.deprecated("0.7", "Only SQL expressions which subclass :class:`.Executable` " + "may provide the :func:`.scalar` method.") def scalar(self, *multiparams, **params): """Compile and execute this :class:`ClauseElement`, returning the result's scalar representation. @@ -2401,7 +2406,7 @@ class Executable(_Generative): COMMIT will be invoked in order to provide its "autocommit" feature. Typically, all INSERT/UPDATE/DELETE statements as well as CREATE/DROP statements have autocommit behavior enabled; SELECT - constructs do not. Use this option when invokving a SELECT or other + constructs do not. Use this option when invoking a SELECT or other specific SQL construct where COMMIT is desired (typically when calling stored procedures and such). @@ -2436,6 +2441,27 @@ class Executable(_Generative): """ self._execution_options = self._execution_options.union(kw) + def execute(self, *multiparams, **params): + """Compile and execute this :class:`.Executable`.""" + + e = self.bind + if e is None: + label = getattr(self, 'description', self.__class__.__name__) + msg = ('This %s is not bound and does not support direct ' + 'execution. Supply this statement to a Connection or ' + 'Engine for execution. Or, assign a bind to the statement ' + 'or the Metadata of its underlying tables to enable ' + 'implicit execution via this method.' % label) + raise exc.UnboundExecutionError(msg) + return e._execute_clauseelement(self, multiparams, params) + + def scalar(self, *multiparams, **params): + """Compile and execute this :class:`.Executable`, returning the + result's scalar representation. + + """ + return self.execute(*multiparams, **params).scalar() + # legacy, some outside users may be calling this _Executable = Executable diff --git a/test/orm/test_assorted_eager.py b/test/orm/test_assorted_eager.py index 20736b8fe9..0e389b74ba 100644 --- a/test/orm/test_assorted_eager.py +++ b/test/orm/test_assorted_eager.py @@ -324,7 +324,7 @@ class EagerTest3(_base.MappedTest): arb_data = sa.select( [stats.c.data_id, sa.func.max(stats.c.somedata).label('max')], stats.c.data_id <= 5, - group_by=[stats.c.data_id]).alias('arb') + group_by=[stats.c.data_id]) arb_result = arb_data.execute().fetchall() @@ -334,6 +334,8 @@ class EagerTest3(_base.MappedTest): # extract just the "data_id" from it arb_result = [row['data_id'] for row in arb_result] + arb_data = arb_data.alias('arb') + # now query for Data objects using that above select, adding the # "order by max desc" separately q = (session.query(Data). diff --git a/test/sql/test_case_statement.py b/test/sql/test_case_statement.py index 645822fa70..1a106ee5e1 100644 --- a/test/sql/test_case_statement.py +++ b/test/sql/test_case_statement.py @@ -32,14 +32,14 @@ class CaseTest(TestBase, AssertsCompiledSQL): @testing.fails_on('firebird', 'FIXME: unknown') @testing.fails_on('maxdb', 'FIXME: unknown') @testing.requires.subqueries - def testcase(self): + def test_case(self): inner = select([case([ [info_table.c.pk < 3, 'lessthan3'], [and_(info_table.c.pk >= 3, info_table.c.pk < 7), 'gt3']]).label('x'), info_table.c.pk, info_table.c.info], - from_obj=[info_table]).alias('q_inner') + from_obj=[info_table]) inner_result = inner.execute().fetchall() @@ -59,7 +59,7 @@ class CaseTest(TestBase, AssertsCompiledSQL): ('gt3', 6, 'pk_6_data') ] - outer = select([inner]) + outer = select([inner.alias('q_inner')]) outer_result = outer.execute().fetchall() @@ -79,7 +79,7 @@ class CaseTest(TestBase, AssertsCompiledSQL): 6]], else_ = 0).label('x'), info_table.c.pk, info_table.c.info], - from_obj=[info_table]).alias('q_inner') + from_obj=[info_table]) else_result = w_else.execute().fetchall() diff --git a/test/sql/test_query.py b/test/sql/test_query.py index 0a496906d6..d78ddbb572 100644 --- a/test/sql/test_query.py +++ b/test/sql/test_query.py @@ -743,6 +743,7 @@ class QueryTest(TestBase): r = testing.db.execute('select user_name from query_users').first() eq_(len(r), 1) + @testing.uses_deprecated(r'.*which subclass Executable') def test_cant_execute_join(self): try: users.join(addresses).execute()