You can not select more than 25 topics
			Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
		
		
		
		
		
			
		
			
				
					614 lines
				
				22 KiB
			
		
		
			
		
	
	
					614 lines
				
				22 KiB
			| 
								 
											3 years ago
										 
									 | 
							
								# ext/compiler.py
							 | 
						||
| 
								 | 
							
								# Copyright (C) 2005-2022 the SQLAlchemy authors and contributors
							 | 
						||
| 
								 | 
							
								# <see AUTHORS file>
							 | 
						||
| 
								 | 
							
								#
							 | 
						||
| 
								 | 
							
								# This module is part of SQLAlchemy and is released under
							 | 
						||
| 
								 | 
							
								# the MIT License: https://www.opensource.org/licenses/mit-license.php
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								r"""Provides an API for creation of custom ClauseElements and compilers.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								Synopsis
							 | 
						||
| 
								 | 
							
								========
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								Usage involves the creation of one or more
							 | 
						||
| 
								 | 
							
								:class:`~sqlalchemy.sql.expression.ClauseElement` subclasses and one or
							 | 
						||
| 
								 | 
							
								more callables defining its compilation::
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    from sqlalchemy.ext.compiler import compiles
							 | 
						||
| 
								 | 
							
								    from sqlalchemy.sql.expression import ColumnClause
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    class MyColumn(ColumnClause):
							 | 
						||
| 
								 | 
							
								        inherit_cache = True
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    @compiles(MyColumn)
							 | 
						||
| 
								 | 
							
								    def compile_mycolumn(element, compiler, **kw):
							 | 
						||
| 
								 | 
							
								        return "[%s]" % element.name
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								Above, ``MyColumn`` extends :class:`~sqlalchemy.sql.expression.ColumnClause`,
							 | 
						||
| 
								 | 
							
								the base expression element for named column objects. The ``compiles``
							 | 
						||
| 
								 | 
							
								decorator registers itself with the ``MyColumn`` class so that it is invoked
							 | 
						||
| 
								 | 
							
								when the object is compiled to a string::
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    from sqlalchemy import select
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    s = select(MyColumn('x'), MyColumn('y'))
							 | 
						||
| 
								 | 
							
								    print(str(s))
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								Produces::
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    SELECT [x], [y]
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								Dialect-specific compilation rules
							 | 
						||
| 
								 | 
							
								==================================
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								Compilers can also be made dialect-specific. The appropriate compiler will be
							 | 
						||
| 
								 | 
							
								invoked for the dialect in use::
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    from sqlalchemy.schema import DDLElement
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    class AlterColumn(DDLElement):
							 | 
						||
| 
								 | 
							
								        inherit_cache = False
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        def __init__(self, column, cmd):
							 | 
						||
| 
								 | 
							
								            self.column = column
							 | 
						||
| 
								 | 
							
								            self.cmd = cmd
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    @compiles(AlterColumn)
							 | 
						||
| 
								 | 
							
								    def visit_alter_column(element, compiler, **kw):
							 | 
						||
| 
								 | 
							
								        return "ALTER COLUMN %s ..." % element.column.name
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    @compiles(AlterColumn, 'postgresql')
							 | 
						||
| 
								 | 
							
								    def visit_alter_column(element, compiler, **kw):
							 | 
						||
| 
								 | 
							
								        return "ALTER TABLE %s ALTER COLUMN %s ..." % (element.table.name,
							 | 
						||
| 
								 | 
							
								                                                       element.column.name)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								The second ``visit_alter_table`` will be invoked when any ``postgresql``
							 | 
						||
| 
								 | 
							
								dialect is used.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								.. _compilerext_compiling_subelements:
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								Compiling sub-elements of a custom expression construct
							 | 
						||
| 
								 | 
							
								=======================================================
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								The ``compiler`` argument is the
							 | 
						||
| 
								 | 
							
								:class:`~sqlalchemy.engine.interfaces.Compiled` object in use. This object
							 | 
						||
| 
								 | 
							
								can be inspected for any information about the in-progress compilation,
							 | 
						||
| 
								 | 
							
								including ``compiler.dialect``, ``compiler.statement`` etc. The
							 | 
						||
| 
								 | 
							
								:class:`~sqlalchemy.sql.compiler.SQLCompiler` and
							 | 
						||
| 
								 | 
							
								:class:`~sqlalchemy.sql.compiler.DDLCompiler` both include a ``process()``
							 | 
						||
| 
								 | 
							
								method which can be used for compilation of embedded attributes::
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    from sqlalchemy.sql.expression import Executable, ClauseElement
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    class InsertFromSelect(Executable, ClauseElement):
							 | 
						||
| 
								 | 
							
								        inherit_cache = False
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        def __init__(self, table, select):
							 | 
						||
| 
								 | 
							
								            self.table = table
							 | 
						||
| 
								 | 
							
								            self.select = select
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    @compiles(InsertFromSelect)
							 | 
						||
| 
								 | 
							
								    def visit_insert_from_select(element, compiler, **kw):
							 | 
						||
| 
								 | 
							
								        return "INSERT INTO %s (%s)" % (
							 | 
						||
| 
								 | 
							
								            compiler.process(element.table, asfrom=True, **kw),
							 | 
						||
| 
								 | 
							
								            compiler.process(element.select, **kw)
							 | 
						||
| 
								 | 
							
								        )
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    insert = InsertFromSelect(t1, select(t1).where(t1.c.x>5))
							 | 
						||
| 
								 | 
							
								    print(insert)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								Produces::
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    "INSERT INTO mytable (SELECT mytable.x, mytable.y, mytable.z
							 | 
						||
| 
								 | 
							
								                          FROM mytable WHERE mytable.x > :x_1)"
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								.. note::
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    The above ``InsertFromSelect`` construct is only an example, this actual
							 | 
						||
| 
								 | 
							
								    functionality is already available using the
							 | 
						||
| 
								 | 
							
								    :meth:`_expression.Insert.from_select` method.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								.. 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
							 | 
						||
| 
								 | 
							
								---------------------------------------------
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								SQL and DDL constructs are each compiled using different base compilers -
							 | 
						||
| 
								 | 
							
								``SQLCompiler`` and ``DDLCompiler``.   A common need is to access the
							 | 
						||
| 
								 | 
							
								compilation rules of SQL expressions from within a DDL expression. The
							 | 
						||
| 
								 | 
							
								``DDLCompiler`` includes an accessor ``sql_compiler`` for this reason, such as
							 | 
						||
| 
								 | 
							
								below where we generate a CHECK constraint that embeds a SQL expression::
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    @compiles(MyConstraint)
							 | 
						||
| 
								 | 
							
								    def compile_my_constraint(constraint, ddlcompiler, **kw):
							 | 
						||
| 
								 | 
							
								        kw['literal_binds'] = True
							 | 
						||
| 
								 | 
							
								        return "CONSTRAINT %s CHECK (%s)" % (
							 | 
						||
| 
								 | 
							
								            constraint.name,
							 | 
						||
| 
								 | 
							
								            ddlcompiler.sql_compiler.process(
							 | 
						||
| 
								 | 
							
								                constraint.expression, **kw)
							 | 
						||
| 
								 | 
							
								        )
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								Above, we add an additional flag to the process step as called by
							 | 
						||
| 
								 | 
							
								:meth:`.SQLCompiler.process`, which is the ``literal_binds`` flag.  This
							 | 
						||
| 
								 | 
							
								indicates that any SQL expression which refers to a :class:`.BindParameter`
							 | 
						||
| 
								 | 
							
								object or other "literal" object such as those which refer to strings or
							 | 
						||
| 
								 | 
							
								integers should be rendered **in-place**, rather than being referred to as
							 | 
						||
| 
								 | 
							
								a bound parameter;  when emitting DDL, bound parameters are typically not
							 | 
						||
| 
								 | 
							
								supported.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								.. _enabling_compiled_autocommit:
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								Enabling Autocommit on a Construct
							 | 
						||
| 
								 | 
							
								==================================
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								Recall from the section :ref:`autocommit` that the :class:`_engine.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:`_expression.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
							 | 
						||
| 
								 | 
							
								=======================================================
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								The compiler extension applies just as well to the existing constructs.  When
							 | 
						||
| 
								 | 
							
								overriding the compilation of a built in SQL construct, the @compiles
							 | 
						||
| 
								 | 
							
								decorator is invoked upon the appropriate class (be sure to use the class,
							 | 
						||
| 
								 | 
							
								i.e. ``Insert`` or ``Select``, instead of the creation function such
							 | 
						||
| 
								 | 
							
								as ``insert()`` or ``select()``).
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								Within the new compilation function, to get at the "original" compilation
							 | 
						||
| 
								 | 
							
								routine, use the appropriate visit_XXX method - this
							 | 
						||
| 
								 | 
							
								because compiler.process() will call upon the overriding routine and cause
							 | 
						||
| 
								 | 
							
								an endless loop.   Such as, to add "prefix" to all insert statements::
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    from sqlalchemy.sql.expression import Insert
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    @compiles(Insert)
							 | 
						||
| 
								 | 
							
								    def prefix_inserts(insert, compiler, **kw):
							 | 
						||
| 
								 | 
							
								        return compiler.visit_insert(insert.prefix_with("some prefix"), **kw)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								The above compiler will prefix all INSERT statements with "some prefix" when
							 | 
						||
| 
								 | 
							
								compiled.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								.. _type_compilation_extension:
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								Changing Compilation of Types
							 | 
						||
| 
								 | 
							
								=============================
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								``compiler`` works for types, too, such as below where we implement the
							 | 
						||
| 
								 | 
							
								MS-SQL specific 'max' keyword for ``String``/``VARCHAR``::
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    @compiles(String, 'mssql')
							 | 
						||
| 
								 | 
							
								    @compiles(VARCHAR, 'mssql')
							 | 
						||
| 
								 | 
							
								    def compile_varchar(element, compiler, **kw):
							 | 
						||
| 
								 | 
							
								        if element.length == 'max':
							 | 
						||
| 
								 | 
							
								            return "VARCHAR('max')"
							 | 
						||
| 
								 | 
							
								        else:
							 | 
						||
| 
								 | 
							
								            return compiler.visit_VARCHAR(element, **kw)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    foo = Table('foo', metadata,
							 | 
						||
| 
								 | 
							
								        Column('data', VARCHAR('max'))
							 | 
						||
| 
								 | 
							
								    )
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								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:
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								* :class:`~sqlalchemy.sql.expression.ClauseElement` - This is the root
							 | 
						||
| 
								 | 
							
								  expression class. Any SQL expression can be derived from this base, and is
							 | 
						||
| 
								 | 
							
								  probably the best choice for longer constructs such as specialized INSERT
							 | 
						||
| 
								 | 
							
								  statements.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								* :class:`~sqlalchemy.sql.expression.ColumnElement` - The root of all
							 | 
						||
| 
								 | 
							
								  "column-like" elements. Anything that you'd place in the "columns" clause of
							 | 
						||
| 
								 | 
							
								  a SELECT statement (as well as order by and group by) can derive from this -
							 | 
						||
| 
								 | 
							
								  the object will automatically have Python "comparison" behavior.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  :class:`~sqlalchemy.sql.expression.ColumnElement` classes want to have a
							 | 
						||
| 
								 | 
							
								  ``type`` member which is expression's return type.  This can be established
							 | 
						||
| 
								 | 
							
								  at the instance level in the constructor, or at the class level if its
							 | 
						||
| 
								 | 
							
								  generally constant::
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								      class timestamp(ColumnElement):
							 | 
						||
| 
								 | 
							
								          type = TIMESTAMP()
							 | 
						||
| 
								 | 
							
								          inherit_cache = True
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								* :class:`~sqlalchemy.sql.functions.FunctionElement` - This is a hybrid of a
							 | 
						||
| 
								 | 
							
								  ``ColumnElement`` and a "from clause" like object, and represents a SQL
							 | 
						||
| 
								 | 
							
								  function or stored procedure type of call. Since most databases support
							 | 
						||
| 
								 | 
							
								  statements along the line of "SELECT FROM <some function>"
							 | 
						||
| 
								 | 
							
								  ``FunctionElement`` adds in the ability to be used in the FROM clause of a
							 | 
						||
| 
								 | 
							
								  ``select()`` construct::
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								      from sqlalchemy.sql.expression import FunctionElement
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								      class coalesce(FunctionElement):
							 | 
						||
| 
								 | 
							
								          name = 'coalesce'
							 | 
						||
| 
								 | 
							
								          inherit_cache = True
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								      @compiles(coalesce)
							 | 
						||
| 
								 | 
							
								      def compile(element, compiler, **kw):
							 | 
						||
| 
								 | 
							
								          return "coalesce(%s)" % compiler.process(element.clauses, **kw)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								      @compiles(coalesce, 'oracle')
							 | 
						||
| 
								 | 
							
								      def compile(element, compiler, **kw):
							 | 
						||
| 
								 | 
							
								          if len(element.clauses) > 2:
							 | 
						||
| 
								 | 
							
								              raise TypeError("coalesce only supports two arguments on Oracle")
							 | 
						||
| 
								 | 
							
								          return "nvl(%s)" % compiler.process(element.clauses, **kw)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								* :class:`.DDLElement` - The root of all DDL expressions,
							 | 
						||
| 
								 | 
							
								  like CREATE TABLE, ALTER TABLE, etc. Compilation of :class:`.DDLElement`
							 | 
						||
| 
								 | 
							
								  subclasses is issued by a :class:`.DDLCompiler` instead of a
							 | 
						||
| 
								 | 
							
								  :class:`.SQLCompiler`. :class:`.DDLElement` can also be used as an event hook
							 | 
						||
| 
								 | 
							
								  in conjunction with event hooks like :meth:`.DDLEvents.before_create` and
							 | 
						||
| 
								 | 
							
								  :meth:`.DDLEvents.after_create`, allowing the construct to be invoked
							 | 
						||
| 
								 | 
							
								  automatically during CREATE TABLE and DROP TABLE sequences.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  .. seealso::
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    :ref:`metadata_ddl_toplevel` - contains examples of associating
							 | 
						||
| 
								 | 
							
								    :class:`.DDL` objects (which are themselves :class:`.DDLElement`
							 | 
						||
| 
								 | 
							
								    instances) with :class:`.DDLEvents` event hooks.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								* :class:`~sqlalchemy.sql.expression.Executable` - This is a mixin which
							 | 
						||
| 
								 | 
							
								  should be used with any expression class that represents a "standalone"
							 | 
						||
| 
								 | 
							
								  SQL statement that can be passed directly to an ``execute()`` method.  It
							 | 
						||
| 
								 | 
							
								  is already implicit within ``DDLElement`` and ``FunctionElement``.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								Most of the above constructs also respond to SQL statement caching.   A
							 | 
						||
| 
								 | 
							
								subclassed construct will want to define the caching behavior for the object,
							 | 
						||
| 
								 | 
							
								which usually means setting the flag ``inherit_cache`` to the value of
							 | 
						||
| 
								 | 
							
								``False`` or ``True``.  See the next section :ref:`compilerext_caching`
							 | 
						||
| 
								 | 
							
								for background.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								.. _compilerext_caching:
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								Enabling Caching Support for Custom Constructs
							 | 
						||
| 
								 | 
							
								==============================================
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								SQLAlchemy as of version 1.4 includes a
							 | 
						||
| 
								 | 
							
								:ref:`SQL compilation caching facility <sql_caching>` which will allow
							 | 
						||
| 
								 | 
							
								equivalent SQL constructs to cache their stringified form, along with other
							 | 
						||
| 
								 | 
							
								structural information used to fetch results from the statement.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								For reasons discussed at :ref:`caching_caveats`, the implementation of this
							 | 
						||
| 
								 | 
							
								caching system takes a conservative approach towards including custom SQL
							 | 
						||
| 
								 | 
							
								constructs and/or subclasses within the caching system.   This includes that
							 | 
						||
| 
								 | 
							
								any user-defined SQL constructs, including all the examples for this
							 | 
						||
| 
								 | 
							
								extension, will not participate in caching by default unless they positively
							 | 
						||
| 
								 | 
							
								assert that they are able to do so.  The :attr:`.HasCacheKey.inherit_cache`
							 | 
						||
| 
								 | 
							
								attribute when set to ``True`` at the class level of a specific subclass
							 | 
						||
| 
								 | 
							
								will indicate that instances of this class may be safely cached, using the
							 | 
						||
| 
								 | 
							
								cache key generation scheme of the immediate superclass.  This applies
							 | 
						||
| 
								 | 
							
								for example to the "synopsis" example indicated previously::
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    class MyColumn(ColumnClause):
							 | 
						||
| 
								 | 
							
								        inherit_cache = True
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    @compiles(MyColumn)
							 | 
						||
| 
								 | 
							
								    def compile_mycolumn(element, compiler, **kw):
							 | 
						||
| 
								 | 
							
								        return "[%s]" % element.name
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								Above, the ``MyColumn`` class does not include any new state that
							 | 
						||
| 
								 | 
							
								affects its SQL compilation; the cache key of ``MyColumn`` instances will
							 | 
						||
| 
								 | 
							
								make use of that of the ``ColumnClause`` superclass, meaning it will take
							 | 
						||
| 
								 | 
							
								into account the class of the object (``MyColumn``), the string name and
							 | 
						||
| 
								 | 
							
								datatype of the object::
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    >>> MyColumn("some_name", String())._generate_cache_key()
							 | 
						||
| 
								 | 
							
								    CacheKey(
							 | 
						||
| 
								 | 
							
								        key=('0', <class '__main__.MyColumn'>,
							 | 
						||
| 
								 | 
							
								        'name', 'some_name',
							 | 
						||
| 
								 | 
							
								        'type', (<class 'sqlalchemy.sql.sqltypes.String'>,
							 | 
						||
| 
								 | 
							
								                 ('length', None), ('collation', None))
							 | 
						||
| 
								 | 
							
								    ), bindparams=[])
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								For objects that are likely to be **used liberally as components within many
							 | 
						||
| 
								 | 
							
								larger statements**, such as :class:`_schema.Column` subclasses and custom SQL
							 | 
						||
| 
								 | 
							
								datatypes, it's important that **caching be enabled as much as possible**, as
							 | 
						||
| 
								 | 
							
								this may otherwise negatively affect performance.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								An example of an object that **does** contain state which affects its SQL
							 | 
						||
| 
								 | 
							
								compilation is the one illustrated at :ref:`compilerext_compiling_subelements`;
							 | 
						||
| 
								 | 
							
								this is an "INSERT FROM SELECT" construct that combines together a
							 | 
						||
| 
								 | 
							
								:class:`_schema.Table` as well as a :class:`_sql.Select` construct, each of
							 | 
						||
| 
								 | 
							
								which independently affect the SQL string generation of the construct.  For
							 | 
						||
| 
								 | 
							
								this class, the example illustrates that it simply does not participate in
							 | 
						||
| 
								 | 
							
								caching::
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    class InsertFromSelect(Executable, ClauseElement):
							 | 
						||
| 
								 | 
							
								        inherit_cache = False
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        def __init__(self, table, select):
							 | 
						||
| 
								 | 
							
								            self.table = table
							 | 
						||
| 
								 | 
							
								            self.select = select
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    @compiles(InsertFromSelect)
							 | 
						||
| 
								 | 
							
								    def visit_insert_from_select(element, compiler, **kw):
							 | 
						||
| 
								 | 
							
								        return "INSERT INTO %s (%s)" % (
							 | 
						||
| 
								 | 
							
								            compiler.process(element.table, asfrom=True, **kw),
							 | 
						||
| 
								 | 
							
								            compiler.process(element.select, **kw)
							 | 
						||
| 
								 | 
							
								        )
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								While it is also possible that the above ``InsertFromSelect`` could be made to
							 | 
						||
| 
								 | 
							
								produce a cache key that is composed of that of the :class:`_schema.Table` and
							 | 
						||
| 
								 | 
							
								:class:`_sql.Select` components together, the API for this is not at the moment
							 | 
						||
| 
								 | 
							
								fully public. However, for an "INSERT FROM SELECT" construct, which is only
							 | 
						||
| 
								 | 
							
								used by itself for specific operations, caching is not as critical as in the
							 | 
						||
| 
								 | 
							
								previous example.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								For objects that are **used in relative isolation and are generally
							 | 
						||
| 
								 | 
							
								standalone**, such as custom :term:`DML` constructs like an "INSERT FROM
							 | 
						||
| 
								 | 
							
								SELECT", **caching is generally less critical** as the lack of caching for such
							 | 
						||
| 
								 | 
							
								a construct will have only localized implications for that specific operation.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								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()
							 | 
						||
| 
								 | 
							
								        inherit_cache = True
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    @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 - its 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, case
							 | 
						||
| 
								 | 
							
								    from sqlalchemy.ext.compiler import compiles
							 | 
						||
| 
								 | 
							
								    from sqlalchemy.types import Numeric
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    class greatest(expression.FunctionElement):
							 | 
						||
| 
								 | 
							
								        type = Numeric()
							 | 
						||
| 
								 | 
							
								        name = 'greatest'
							 | 
						||
| 
								 | 
							
								        inherit_cache = True
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    @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 compiler.process(case([(arg1 > arg2, arg1)], else_=arg2), **kw)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								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):
							 | 
						||
| 
								 | 
							
								        inherit_cache = True
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    @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 .. import exc
							 | 
						||
| 
								 | 
							
								from .. import util
							 | 
						||
| 
								 | 
							
								from ..sql import sqltypes
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								def compiles(class_, *specs):
							 | 
						||
| 
								 | 
							
								    """Register a function as a compiler for a
							 | 
						||
| 
								 | 
							
								    given :class:`_expression.ClauseElement` type."""
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    def decorate(fn):
							 | 
						||
| 
								 | 
							
								        # get an existing @compiles handler
							 | 
						||
| 
								 | 
							
								        existing = class_.__dict__.get("_compiler_dispatcher", None)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        # get the original handler.  All ClauseElement classes have one
							 | 
						||
| 
								 | 
							
								        # of these, but some TypeEngine classes will not.
							 | 
						||
| 
								 | 
							
								        existing_dispatch = getattr(class_, "_compiler_dispatch", None)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        if not existing:
							 | 
						||
| 
								 | 
							
								            existing = _dispatcher()
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								            if existing_dispatch:
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								                def _wrap_existing_dispatch(element, compiler, **kw):
							 | 
						||
| 
								 | 
							
								                    try:
							 | 
						||
| 
								 | 
							
								                        return existing_dispatch(element, compiler, **kw)
							 | 
						||
| 
								 | 
							
								                    except exc.UnsupportedCompilationError as uce:
							 | 
						||
| 
								 | 
							
								                        util.raise_(
							 | 
						||
| 
								 | 
							
								                            exc.UnsupportedCompilationError(
							 | 
						||
| 
								 | 
							
								                                compiler,
							 | 
						||
| 
								 | 
							
								                                type(element),
							 | 
						||
| 
								 | 
							
								                                message="%s construct has no default "
							 | 
						||
| 
								 | 
							
								                                "compilation handler." % type(element),
							 | 
						||
| 
								 | 
							
								                            ),
							 | 
						||
| 
								 | 
							
								                            from_=uce,
							 | 
						||
| 
								 | 
							
								                        )
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								                existing.specs["default"] = _wrap_existing_dispatch
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								            # TODO: why is the lambda needed ?
							 | 
						||
| 
								 | 
							
								            setattr(
							 | 
						||
| 
								 | 
							
								                class_,
							 | 
						||
| 
								 | 
							
								                "_compiler_dispatch",
							 | 
						||
| 
								 | 
							
								                lambda *arg, **kw: existing(*arg, **kw),
							 | 
						||
| 
								 | 
							
								            )
							 | 
						||
| 
								 | 
							
								            setattr(class_, "_compiler_dispatcher", existing)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        if specs:
							 | 
						||
| 
								 | 
							
								            for s in specs:
							 | 
						||
| 
								 | 
							
								                existing.specs[s] = fn
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        else:
							 | 
						||
| 
								 | 
							
								            existing.specs["default"] = fn
							 | 
						||
| 
								 | 
							
								        return fn
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    return decorate
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								def deregister(class_):
							 | 
						||
| 
								 | 
							
								    """Remove all custom compilers associated with a given
							 | 
						||
| 
								 | 
							
								    :class:`_expression.ClauseElement` type.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    """
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    if hasattr(class_, "_compiler_dispatcher"):
							 | 
						||
| 
								 | 
							
								        class_._compiler_dispatch = class_._original_compiler_dispatch
							 | 
						||
| 
								 | 
							
								        del class_._compiler_dispatcher
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								class _dispatcher(object):
							 | 
						||
| 
								 | 
							
								    def __init__(self):
							 | 
						||
| 
								 | 
							
								        self.specs = {}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    def __call__(self, element, compiler, **kw):
							 | 
						||
| 
								 | 
							
								        # TODO: yes, this could also switch off of DBAPI in use.
							 | 
						||
| 
								 | 
							
								        fn = self.specs.get(compiler.dialect.name, None)
							 | 
						||
| 
								 | 
							
								        if not fn:
							 | 
						||
| 
								 | 
							
								            try:
							 | 
						||
| 
								 | 
							
								                fn = self.specs["default"]
							 | 
						||
| 
								 | 
							
								            except KeyError as ke:
							 | 
						||
| 
								 | 
							
								                util.raise_(
							 | 
						||
| 
								 | 
							
								                    exc.UnsupportedCompilationError(
							 | 
						||
| 
								 | 
							
								                        compiler,
							 | 
						||
| 
								 | 
							
								                        type(element),
							 | 
						||
| 
								 | 
							
								                        message="%s construct has no default "
							 | 
						||
| 
								 | 
							
								                        "compilation handler." % type(element),
							 | 
						||
| 
								 | 
							
								                    ),
							 | 
						||
| 
								 | 
							
								                    replace_context=ke,
							 | 
						||
| 
								 | 
							
								                )
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        # if compilation includes add_to_result_map, collect add_to_result_map
							 | 
						||
| 
								 | 
							
								        # arguments from the user-defined callable, which are probably none
							 | 
						||
| 
								 | 
							
								        # because this is not public API.  if it wasn't called, then call it
							 | 
						||
| 
								 | 
							
								        # ourselves.
							 | 
						||
| 
								 | 
							
								        arm = kw.get("add_to_result_map", None)
							 | 
						||
| 
								 | 
							
								        if arm:
							 | 
						||
| 
								 | 
							
								            arm_collection = []
							 | 
						||
| 
								 | 
							
								            kw["add_to_result_map"] = lambda *args: arm_collection.append(args)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        expr = fn(element, compiler, **kw)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        if arm:
							 | 
						||
| 
								 | 
							
								            if not arm_collection:
							 | 
						||
| 
								 | 
							
								                arm_collection.append(
							 | 
						||
| 
								 | 
							
								                    (None, None, (element,), sqltypes.NULLTYPE)
							 | 
						||
| 
								 | 
							
								                )
							 | 
						||
| 
								 | 
							
								            for tup in arm_collection:
							 | 
						||
| 
								 | 
							
								                arm(*tup)
							 | 
						||
| 
								 | 
							
								        return expr
							 |