Packages update
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
# ext/compiler.py
|
||||
# Copyright (C) 2005-2011 the SQLAlchemy authors and contributors <see AUTHORS file>
|
||||
# Copyright (C) 2005-2012 the SQLAlchemy authors and contributors <see AUTHORS file>
|
||||
#
|
||||
# This module is part of SQLAlchemy and is released under
|
||||
# the MIT License: http://www.opensource.org/licenses/mit-license.php
|
||||
@@ -91,6 +91,11 @@ Produces::
|
||||
|
||||
"INSERT INTO mytable (SELECT mytable.x, mytable.y, mytable.z FROM mytable WHERE mytable.x > :x_1)"
|
||||
|
||||
.. note::
|
||||
|
||||
The above ``InsertFromSelect`` construct probably wants to have "autocommit"
|
||||
enabled. See :ref:`enabling_compiled_autocommit` for this step.
|
||||
|
||||
Cross Compiling between SQL and DDL compilers
|
||||
---------------------------------------------
|
||||
|
||||
@@ -106,6 +111,50 @@ constraint that embeds a SQL expression::
|
||||
ddlcompiler.sql_compiler.process(constraint.expression)
|
||||
)
|
||||
|
||||
.. _enabling_compiled_autocommit:
|
||||
|
||||
Enabling Autocommit on a Construct
|
||||
==================================
|
||||
|
||||
Recall from the section :ref:`autocommit` that the :class:`.Engine`, when asked to execute
|
||||
a construct in the absence of a user-defined transaction, detects if the given
|
||||
construct represents DML or DDL, that is, a data modification or data definition statement, which
|
||||
requires (or may require, in the case of DDL) that the transaction generated by the DBAPI be committed
|
||||
(recall that DBAPI always has a transaction going on regardless of what SQLAlchemy does). Checking
|
||||
for this is actually accomplished
|
||||
by checking for the "autocommit" execution option on the construct. When building a construct like
|
||||
an INSERT derivation, a new DDL type, or perhaps a stored procedure that alters data, the "autocommit"
|
||||
option needs to be set in order for the statement to function with "connectionless" execution
|
||||
(as described in :ref:`dbengine_implicit`).
|
||||
|
||||
Currently a quick way to do this is to subclass :class:`.Executable`, then add the "autocommit" flag
|
||||
to the ``_execution_options`` dictionary (note this is a "frozen" dictionary which supplies a generative
|
||||
``union()`` method)::
|
||||
|
||||
from sqlalchemy.sql.expression import Executable, ClauseElement
|
||||
|
||||
class MyInsertThing(Executable, ClauseElement):
|
||||
_execution_options = \\
|
||||
Executable._execution_options.union({'autocommit': True})
|
||||
|
||||
More succinctly, if the construct is truly similar to an INSERT, UPDATE, or DELETE, :class:`.UpdateBase`
|
||||
can be used, which already is a subclass of :class:`.Executable`, :class:`.ClauseElement` and includes the
|
||||
``autocommit`` flag::
|
||||
|
||||
from sqlalchemy.sql.expression import UpdateBase
|
||||
|
||||
class MyInsertThing(UpdateBase):
|
||||
def __init__(self, ...):
|
||||
...
|
||||
|
||||
|
||||
|
||||
|
||||
DDL elements that subclass :class:`.DDLElement` already have the "autocommit" flag turned on.
|
||||
|
||||
|
||||
|
||||
|
||||
Changing the default compilation of existing constructs
|
||||
=======================================================
|
||||
|
||||
@@ -147,7 +196,10 @@ Changing Compilation of Types
|
||||
Subclassing Guidelines
|
||||
======================
|
||||
|
||||
A big part of using the compiler extension is subclassing SQLAlchemy expression constructs. To make this easier, the expression and schema packages feature a set of "bases" intended for common tasks. A synopsis is as follows:
|
||||
A big part of using the compiler extension is subclassing SQLAlchemy
|
||||
expression constructs. To make this easier, the expression and
|
||||
schema packages feature a set of "bases" intended for common tasks.
|
||||
A synopsis is as follows:
|
||||
|
||||
* :class:`~sqlalchemy.sql.expression.ClauseElement` - This is the root
|
||||
expression class. Any SQL expression can be derived from this base, and is
|
||||
@@ -201,7 +253,121 @@ A big part of using the compiler extension is subclassing SQLAlchemy expression
|
||||
can be passed directly to an ``execute()`` method. It is already implicit
|
||||
within ``DDLElement`` and ``FunctionElement``.
|
||||
|
||||
Further Examples
|
||||
================
|
||||
|
||||
"UTC timestamp" function
|
||||
-------------------------
|
||||
|
||||
A function that works like "CURRENT_TIMESTAMP" except applies the appropriate conversions
|
||||
so that the time is in UTC time. Timestamps are best stored in relational databases
|
||||
as UTC, without time zones. UTC so that your database doesn't think time has gone
|
||||
backwards in the hour when daylight savings ends, without timezones because timezones
|
||||
are like character encodings - they're best applied only at the endpoints of an
|
||||
application (i.e. convert to UTC upon user input, re-apply desired timezone upon display).
|
||||
|
||||
For Postgresql and Microsoft SQL Server::
|
||||
|
||||
from sqlalchemy.sql import expression
|
||||
from sqlalchemy.ext.compiler import compiles
|
||||
from sqlalchemy.types import DateTime
|
||||
|
||||
class utcnow(expression.FunctionElement):
|
||||
type = DateTime()
|
||||
|
||||
@compiles(utcnow, 'postgresql')
|
||||
def pg_utcnow(element, compiler, **kw):
|
||||
return "TIMEZONE('utc', CURRENT_TIMESTAMP)"
|
||||
|
||||
@compiles(utcnow, 'mssql')
|
||||
def ms_utcnow(element, compiler, **kw):
|
||||
return "GETUTCDATE()"
|
||||
|
||||
Example usage::
|
||||
|
||||
from sqlalchemy import (
|
||||
Table, Column, Integer, String, DateTime, MetaData
|
||||
)
|
||||
metadata = MetaData()
|
||||
event = Table("event", metadata,
|
||||
Column("id", Integer, primary_key=True),
|
||||
Column("description", String(50), nullable=False),
|
||||
Column("timestamp", DateTime, server_default=utcnow())
|
||||
)
|
||||
|
||||
"GREATEST" function
|
||||
-------------------
|
||||
|
||||
The "GREATEST" function is given any number of arguments and returns the one that is
|
||||
of the highest value - it's equivalent to Python's ``max`` function. A SQL
|
||||
standard version versus a CASE based version which only accommodates two
|
||||
arguments::
|
||||
|
||||
from sqlalchemy.sql import expression
|
||||
from sqlalchemy.ext.compiler import compiles
|
||||
from sqlalchemy.types import Numeric
|
||||
|
||||
class greatest(expression.FunctionElement):
|
||||
type = Numeric()
|
||||
name = 'greatest'
|
||||
|
||||
@compiles(greatest)
|
||||
def default_greatest(element, compiler, **kw):
|
||||
return compiler.visit_function(element)
|
||||
|
||||
@compiles(greatest, 'sqlite')
|
||||
@compiles(greatest, 'mssql')
|
||||
@compiles(greatest, 'oracle')
|
||||
def case_greatest(element, compiler, **kw):
|
||||
arg1, arg2 = list(element.clauses)
|
||||
return "CASE WHEN %s > %s THEN %s ELSE %s END" % (
|
||||
compiler.process(arg1),
|
||||
compiler.process(arg2),
|
||||
compiler.process(arg1),
|
||||
compiler.process(arg2),
|
||||
)
|
||||
|
||||
Example usage::
|
||||
|
||||
Session.query(Account).\\
|
||||
filter(
|
||||
greatest(
|
||||
Account.checking_balance,
|
||||
Account.savings_balance) > 10000
|
||||
)
|
||||
|
||||
"false" expression
|
||||
------------------
|
||||
|
||||
Render a "false" constant expression, rendering as "0" on platforms that don't have a "false" constant::
|
||||
|
||||
from sqlalchemy.sql import expression
|
||||
from sqlalchemy.ext.compiler import compiles
|
||||
|
||||
class sql_false(expression.ColumnElement):
|
||||
pass
|
||||
|
||||
@compiles(sql_false)
|
||||
def default_false(element, compiler, **kw):
|
||||
return "false"
|
||||
|
||||
@compiles(sql_false, 'mssql')
|
||||
@compiles(sql_false, 'mysql')
|
||||
@compiles(sql_false, 'oracle')
|
||||
def int_false(element, compiler, **kw):
|
||||
return "0"
|
||||
|
||||
Example usage::
|
||||
|
||||
from sqlalchemy import select, union_all
|
||||
|
||||
exp = union_all(
|
||||
select([users.c.name, sql_false().label("enrolled")]),
|
||||
select([customers.c.name, customers.c.enrolled])
|
||||
)
|
||||
|
||||
"""
|
||||
from sqlalchemy import exc
|
||||
|
||||
def compiles(class_, *specs):
|
||||
def decorate(fn):
|
||||
@@ -234,6 +400,11 @@ class _dispatcher(object):
|
||||
# TODO: yes, this could also switch off of DBAPI in use.
|
||||
fn = self.specs.get(compiler.dialect.name, None)
|
||||
if not fn:
|
||||
fn = self.specs['default']
|
||||
try:
|
||||
fn = self.specs['default']
|
||||
except KeyError:
|
||||
raise exc.CompileError(
|
||||
"%s construct has no default "
|
||||
"compilation handler." % type(element))
|
||||
return fn(element, compiler, **kw)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user