diff options
author | Adrien Berchet <adrien.berchet@gmail.com> | 2019-04-15 13:59:18 -0400 |
---|---|---|
committer | Mike Bayer <mike_mp@zzzcomputing.com> | 2019-04-29 17:24:32 -0400 |
commit | a10a4ea1248902349d789de7f5470bb8e437a584 (patch) | |
tree | 536acae15894ae8f60ac2aedd784e37575ccba39 /lib/sqlalchemy/sql/functions.py | |
parent | 64865304051b2af1ee4f90c6bf5e93378d4f302c (diff) | |
download | sqlalchemy-a10a4ea1248902349d789de7f5470bb8e437a584.tar.gz |
Add case insensitivity feature to GenericFunction.
The :class:`.GenericFunction` namespace is being migrated so that function
names are looked up in a case-insensitive manner, as SQL functions do not
collide on case sensitive differences nor is this something which would
occur with user-defined functions or stored procedures. Lookups for
functions declared with :class:`.GenericFunction` now use a case
insensitive scheme, however a deprecation case is supported which allows
two or more :class:`.GenericFunction` objects with the same name of
different cases to exist, which will cause case sensitive lookups to occur
for that particular name, while emitting a warning at function registration
time. Thanks to Adrien Berchet for a lot of work on this complicated
feature.
Fixes: #4569
Closes: #4570
Pull-request: https://github.com/sqlalchemy/sqlalchemy/pull/4570
Pull-request-sha: 37d4f3322b6bace88c99b959cb1916dbbc57610e
Change-Id: Ief07c6eb55bf398f6aad85b60ef13ee6d1173109
Diffstat (limited to 'lib/sqlalchemy/sql/functions.py')
-rw-r--r-- | lib/sqlalchemy/sql/functions.py | 65 |
1 files changed, 63 insertions, 2 deletions
diff --git a/lib/sqlalchemy/sql/functions.py b/lib/sqlalchemy/sql/functions.py index 0e92d5e50..d3775ea6b 100644 --- a/lib/sqlalchemy/sql/functions.py +++ b/lib/sqlalchemy/sql/functions.py @@ -37,6 +37,13 @@ from .. import util _registry = util.defaultdict(dict) +_case_sensitive_registry = util.defaultdict( + lambda: util.defaultdict(dict) +) +_CASE_SENSITIVE = util.symbol( + name="case_sensitive_function", + doc="Symbol to mark the functions that are switched into case-sensitive " + "mode.") def register_function(identifier, fn, package="_default"): @@ -49,7 +56,57 @@ def register_function(identifier, fn, package="_default"): """ reg = _registry[package] - reg[identifier] = fn + case_sensitive_reg = _case_sensitive_registry[package] + raw_identifier = identifier + identifier = identifier.lower() + + # Check if a function with the same lowercase identifier is registered. + if identifier in reg and reg[identifier] is not _CASE_SENSITIVE: + if raw_identifier in case_sensitive_reg[identifier]: + util.warn( + "The GenericFunction '{}' is already registered and " + "is going to be overriden.".format(identifier)) + reg[identifier] = fn + else: + # If a function with the same lowercase identifier is registered, + # then these 2 functions are considered as case-sensitive. + # Note: This case should raise an error in a later release. + util.warn_deprecated( + "GenericFunction '{}' is already registered with " + "different letter case, so the previously registered function " + "'{}' is switched into case-sensitive mode. " + "GenericFunction objects will be fully case-insensitive in a " + "future release.".format( + raw_identifier, + list(case_sensitive_reg[identifier].keys())[0], + )) + reg[identifier] = _CASE_SENSITIVE + + # Check if a function with different letter case identifier is registered. + elif identifier in case_sensitive_reg: + # Note: This case will be removed in a later release. + if ( + raw_identifier not in case_sensitive_reg[identifier] + ): + util.warn_deprecated( + "GenericFunction(s) '{}' are already registered with " + "different letter cases and might interact with '{}'. " + "GenericFunction objects will be fully case-insensitive in a " + "future release.".format( + sorted(case_sensitive_reg[identifier].keys()), + raw_identifier)) + + else: + util.warn( + "The GenericFunction '{}' is already registered and " + "is going to be overriden.".format(raw_identifier)) + + # Register by default + else: + reg[identifier] = fn + + # Always register in case-sensitive registry + case_sensitive_reg[identifier][raw_identifier] = fn class FunctionElement(Executable, ColumnElement, FromClause): @@ -453,7 +510,11 @@ class _FunctionGenerator(object): package = None if package is not None: - func = _registry[package].get(fname) + func = _registry[package].get(fname.lower()) + if func is _CASE_SENSITIVE: + case_sensitive_reg = _case_sensitive_registry[package] + func = case_sensitive_reg.get(fname.lower()).get(fname) + if func is not None: return func(*c, **o) |