main commit
All checks were successful
continuous-integration/drone/push Build is passing

This commit is contained in:
2025-10-16 16:30:25 +09:00
parent 91c7e04474
commit 537e7b363f
1146 changed files with 45926 additions and 77196 deletions

View File

@@ -1,9 +1,10 @@
# ext/compiler.py
# Copyright (C) 2005-2025 the SQLAlchemy authors and contributors
# Copyright (C) 2005-2023 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
# mypy: ignore-errors
r"""Provides an API for creation of custom ClauseElements and compilers.
@@ -17,11 +18,9 @@ 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
@@ -33,12 +32,10 @@ when the object is compiled to a string::
from sqlalchemy import select
s = select(MyColumn("x"), MyColumn("y"))
s = select(MyColumn('x'), MyColumn('y'))
print(str(s))
Produces:
.. sourcecode:: sql
Produces::
SELECT [x], [y]
@@ -50,7 +47,6 @@ invoked for the dialect in use::
from sqlalchemy.schema import DDLElement
class AlterColumn(DDLElement):
inherit_cache = False
@@ -58,18 +54,14 @@ invoked for the dialect in use::
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")
@compiles(AlterColumn, 'postgresql')
def visit_alter_column(element, compiler, **kw):
return "ALTER TABLE %s ALTER COLUMN %s ..." % (
element.table.name,
element.column.name,
)
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.
@@ -89,7 +81,6 @@ method which can be used for compilation of embedded attributes::
from sqlalchemy.sql.expression import Executable, ClauseElement
class InsertFromSelect(Executable, ClauseElement):
inherit_cache = False
@@ -97,27 +88,20 @@ method which can be used for compilation of embedded attributes::
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),
compiler.process(element.select, **kw)
)
insert = InsertFromSelect(t1, select(t1).where(t1.c.x > 5))
insert = InsertFromSelect(t1, select(t1).where(t1.c.x>5))
print(insert)
Produces (formatted for readability):
Produces::
.. sourcecode:: sql
INSERT INTO mytable (
SELECT mytable.x, mytable.y, mytable.z
FROM mytable
WHERE mytable.x > :x_1
)
"INSERT INTO mytable (SELECT mytable.x, mytable.y, mytable.z
FROM mytable WHERE mytable.x > :x_1)"
.. note::
@@ -137,10 +121,11 @@ 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
kw['literal_binds'] = True
return "CONSTRAINT %s CHECK (%s)" % (
constraint.name,
ddlcompiler.sql_compiler.process(constraint.expression, **kw),
ddlcompiler.sql_compiler.process(
constraint.expression, **kw)
)
Above, we add an additional flag to the process step as called by
@@ -168,7 +153,6 @@ 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)
@@ -184,16 +168,17 @@ 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")
@compiles(String, 'mssql')
@compiles(VARCHAR, 'mssql')
def compile_varchar(element, compiler, **kw):
if element.length == "max":
if element.length == 'max':
return "VARCHAR('max')"
else:
return compiler.visit_VARCHAR(element, **kw)
foo = Table("foo", metadata, Column("data", VARCHAR("max")))
foo = Table('foo', metadata,
Column('data', VARCHAR('max'))
)
Subclassing Guidelines
======================
@@ -231,23 +216,18 @@ A synopsis is as follows:
from sqlalchemy.sql.expression import FunctionElement
class coalesce(FunctionElement):
name = "coalesce"
name = 'coalesce'
inherit_cache = True
@compiles(coalesce)
def compile(element, compiler, **kw):
return "coalesce(%s)" % compiler.process(element.clauses, **kw)
@compiles(coalesce, "oracle")
@compiles(coalesce, 'oracle')
def compile(element, compiler, **kw):
if len(element.clauses) > 2:
raise TypeError(
"coalesce only supports two arguments on " "Oracle Database"
)
raise TypeError("coalesce only supports two arguments on Oracle")
return "nvl(%s)" % compiler.process(element.clauses, **kw)
* :class:`.ExecutableDDLElement` - The root of all DDL expressions,
@@ -301,7 +281,6 @@ 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
@@ -340,12 +319,11 @@ caching::
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),
compiler.process(element.select, **kw)
)
While it is also possible that the above ``InsertFromSelect`` could be made to
@@ -381,32 +359,28 @@ For PostgreSQL and Microsoft SQL Server::
from sqlalchemy.ext.compiler import compiles
from sqlalchemy.types import DateTime
class utcnow(expression.FunctionElement):
type = DateTime()
inherit_cache = True
@compiles(utcnow, "postgresql")
@compiles(utcnow, 'postgresql')
def pg_utcnow(element, compiler, **kw):
return "TIMEZONE('utc', CURRENT_TIMESTAMP)"
@compiles(utcnow, "mssql")
@compiles(utcnow, 'mssql')
def ms_utcnow(element, compiler, **kw):
return "GETUTCDATE()"
Example usage::
from sqlalchemy import Table, Column, Integer, String, DateTime, MetaData
from sqlalchemy import (
Table, Column, Integer, String, DateTime, MetaData
)
metadata = MetaData()
event = Table(
"event",
metadata,
event = Table("event", metadata,
Column("id", Integer, primary_key=True),
Column("description", String(50), nullable=False),
Column("timestamp", DateTime, server_default=utcnow()),
Column("timestamp", DateTime, server_default=utcnow())
)
"GREATEST" function
@@ -421,30 +395,30 @@ accommodates two arguments::
from sqlalchemy.ext.compiler import compiles
from sqlalchemy.types import Numeric
class greatest(expression.FunctionElement):
type = Numeric()
name = "greatest"
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")
@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
)
Session.query(Account).\
filter(
greatest(
Account.checking_balance,
Account.savings_balance) > 10000
)
"false" expression
------------------
@@ -455,19 +429,16 @@ 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")
@compiles(sql_false, 'mssql')
@compiles(sql_false, 'mysql')
@compiles(sql_false, 'oracle')
def int_false(element, compiler, **kw):
return "0"
@@ -477,33 +448,19 @@ Example usage::
exp = union_all(
select(users.c.name, sql_false().label("enrolled")),
select(customers.c.name, customers.c.enrolled),
select(customers.c.name, customers.c.enrolled)
)
"""
from __future__ import annotations
from typing import Any
from typing import Callable
from typing import Dict
from typing import Type
from typing import TYPE_CHECKING
from typing import TypeVar
from .. import exc
from ..sql import sqltypes
if TYPE_CHECKING:
from ..sql.compiler import SQLCompiler
_F = TypeVar("_F", bound=Callable[..., Any])
def compiles(class_: Type[Any], *specs: str) -> Callable[[_F], _F]:
def compiles(class_, *specs):
"""Register a function as a compiler for a
given :class:`_expression.ClauseElement` type."""
def decorate(fn: _F) -> _F:
def decorate(fn):
# get an existing @compiles handler
existing = class_.__dict__.get("_compiler_dispatcher", None)
@@ -516,9 +473,7 @@ def compiles(class_: Type[Any], *specs: str) -> Callable[[_F], _F]:
if existing_dispatch:
def _wrap_existing_dispatch(
element: Any, compiler: SQLCompiler, **kw: Any
) -> Any:
def _wrap_existing_dispatch(element, compiler, **kw):
try:
return existing_dispatch(element, compiler, **kw)
except exc.UnsupportedCompilationError as uce:
@@ -550,7 +505,7 @@ def compiles(class_: Type[Any], *specs: str) -> Callable[[_F], _F]:
return decorate
def deregister(class_: Type[Any]) -> None:
def deregister(class_):
"""Remove all custom compilers associated with a given
:class:`_expression.ClauseElement` type.
@@ -562,10 +517,10 @@ def deregister(class_: Type[Any]) -> None:
class _dispatcher:
def __init__(self) -> None:
self.specs: Dict[str, Callable[..., Any]] = {}
def __init__(self):
self.specs = {}
def __call__(self, element: Any, compiler: SQLCompiler, **kw: Any) -> Any:
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: