A function created using :class:`.GenericFunction` can now specify that the
name of the function should be rendered with or without quotes by assigning
the :class:`.quoted_name` construct to the .name element of the object.
Prior to 1.3.4, quoting was never applied to function names, and some
quoting was introduced in :ticket:`4467` but no means to force quoting for
a mixed case name was available. Additionally, the :class:`.quoted_name`
construct when used as the name will properly register its lowercase name
in the function registry so that the name continues to be available via the
``func.`` registry.
Fixes: #5079
Change-Id: I0653ab8b16e75e628ce82dbbc3d0f77f8336c407
(cherry picked from commit
a697fcc1cb87b5a4e4f0c70361bd598086f4210f)
--- /dev/null
+.. change::
+ :tags: usecase, sql
+ :tickets: 5079
+
+ A function created using :class:`.GenericFunction` can now specify that the
+ name of the function should be rendered with or without quotes by assigning
+ the :class:`.quoted_name` construct to the .name element of the object.
+ Prior to 1.3.4, quoting was never applied to function names, and some
+ quoting was introduced in :ticket:`4467` but no means to force quoting for
+ a mixed case name was available. Additionally, the :class:`.quoted_name`
+ construct when used as the name will properly register its lowercase name
+ in the function registry so that the name continues to be available via the
+ ``func.`` registry.
+
+ .. seealso::
+
+ :class:`.GenericFunction`
+
name = (
self.preparer.quote(name)
if self.preparer._requires_quotes_illegal_chars(name)
+ or isinstance(name, elements.quoted_name)
else name
)
name = name + "%(expr)s"
(
self.preparer.quote(tok)
if self.preparer._requires_quotes_illegal_chars(tok)
+ or isinstance(name, elements.quoted_name)
else tok
)
for tok in func.packagenames
reg = _registry[package]
case_sensitive_reg = _case_sensitive_registry[package]
raw_identifier = identifier
- identifier = identifier.lower()
+ identifier = util.text_type(identifier).lower()
# Check if a function with the same lowercase identifier is registered.
if identifier in reg and reg[identifier] is not _CASE_SENSITIVE:
The above function will render as follows::
- >>> print func.geo.buffer()
+ >>> print(func.geo.buffer())
ST_Buffer()
+ The name will be rendered as is, however without quoting unless the name
+ contains special characters that require quoting. To force quoting
+ on or off for the name, use the :class:`.sqlalchemy.sql.quoted_name`
+ construct::
+
+ from sqlalchemy.sql import quoted_name
+
+ class GeoBuffer(GenericFunction):
+ type = Geometry
+ package = "geo"
+ name = quoted_name("ST_Buffer", True)
+ identifier = "buffer"
+
+ The above function will render as::
+
+ >>> print(func.geo.buffer())
+ "ST_Buffer"()
+
+ .. versionadded:: 1.3.13 The :class:`.quoted_name` construct is now
+ recognized for quoting when used with the "name" attribute of the
+ object, so that quoting can be forced on or off for the function
+ name.
+
+
"""
coerce_arguments = True
from sqlalchemy.dialects import sqlite
from sqlalchemy.sql import column
from sqlalchemy.sql import functions
+from sqlalchemy.sql import quoted_name
from sqlalchemy.sql import table
from sqlalchemy.sql.compiler import BIND_TEMPLATES
from sqlalchemy.sql.functions import FunctionElement
assert isinstance(func.notmyfunc(), myfunc)
assert not isinstance(func.myfunc(), myfunc)
+ def test_custom_w_quoted_name(self):
+ class myfunc(GenericFunction):
+ name = quoted_name("NotMyFunc", quote=True)
+ identifier = "myfunc"
+
+ self.assert_compile(func.myfunc(), '"NotMyFunc"()')
+
+ def test_custom_w_quoted_name_no_identifier(self):
+ class myfunc(GenericFunction):
+ name = quoted_name("NotMyFunc", quote=True)
+
+ # note this requires that the quoted name be lower cased for
+ # correct lookup
+ self.assert_compile(func.notmyfunc(), '"NotMyFunc"()')
+
def test_custom_package_namespace(self):
def cls1(pk_name):
class myfunc(GenericFunction):