1"""Database functions that do comparisons or type conversions."""
2
3from django.db.models.expressions import Func, Value
4from django.utils.regex_helper import _lazy_re_compile
5
6
7class Cast(Func):
8 """Coerce an expression to a new field type."""
9
10 function = "CAST"
11 template = "%(function)s(%(expressions)s AS %(db_type)s)"
12
13 def __init__(self, expression, output_field):
14 super().__init__(expression, output_field=output_field)
15
16 def as_sql(self, compiler, connection, **extra_context):
17 extra_context["db_type"] = self.output_field.cast_db_type(connection)
18 return super().as_sql(compiler, connection, **extra_context)
19
20 def as_sqlite(self, compiler, connection, **extra_context):
21 db_type = self.output_field.db_type(connection)
22 if db_type in {"datetime", "time"}:
23 # Use strftime as datetime/time don't keep fractional seconds.
24 template = "strftime(%%s, %(expressions)s)"
25 sql, params = super().as_sql(
26 compiler, connection, template=template, **extra_context
27 )
28 format_string = "%H:%M:%f" if db_type == "time" else "%Y-%m-%d %H:%M:%f"
29 params.insert(0, format_string)
30 return sql, params
31 elif db_type == "date":
32 template = "date(%(expressions)s)"
33 return super().as_sql(
34 compiler, connection, template=template, **extra_context
35 )
36 return self.as_sql(compiler, connection, **extra_context)
37
38 def as_mysql(self, compiler, connection, **extra_context):
39 template = None
40 output_type = self.output_field.get_internal_type()
41 # MySQL doesn't support explicit cast to float.
42 if output_type == "FloatField":
43 template = "(%(expressions)s + 0.0)"
44 # MariaDB doesn't support explicit cast to JSON.
45 elif output_type == "JSONField" and connection.mysql_is_mariadb:
46 template = "JSON_EXTRACT(%(expressions)s, '$')"
47 return self.as_sql(compiler, connection, template=template, **extra_context)
48
49 def as_postgresql(self, compiler, connection, **extra_context):
50 # CAST would be valid too, but the :: shortcut syntax is more readable.
51 # 'expressions' is wrapped in parentheses in case it's a complex
52 # expression.
53 return self.as_sql(
54 compiler,
55 connection,
56 template="(%(expressions)s)::%(db_type)s",
57 **extra_context,
58 )
59
60 def as_oracle(self, compiler, connection, **extra_context):
61 if self.output_field.get_internal_type() == "JSONField":
62 # Oracle doesn't support explicit cast to JSON.
63 template = "JSON_QUERY(%(expressions)s, '$')"
64 return super().as_sql(
65 compiler, connection, template=template, **extra_context
66 )
67 return self.as_sql(compiler, connection, **extra_context)
68
69
70class Coalesce(Func):
71 """Return, from left to right, the first non-null expression."""
72
73 function = "COALESCE"
74
75 def __init__(self, *expressions, **extra):
76 if len(expressions) < 2:
77 raise ValueError("Coalesce must take at least two expressions")
78 super().__init__(*expressions, **extra)
79
80 @property
81 def empty_result_set_value(self):
82 for expression in self.get_source_expressions():
83 result = expression.empty_result_set_value
84 if result is NotImplemented or result is not None:
85 return result
86 return None
87
88 def as_oracle(self, compiler, connection, **extra_context):
89 # Oracle prohibits mixing TextField (NCLOB) and CharField (NVARCHAR2),
90 # so convert all fields to NCLOB when that type is expected.
91 if self.output_field.get_internal_type() == "TextField":
92 clone = self.copy()
93 clone.set_source_expressions(
94 [
95 Func(expression, function="TO_NCLOB")
96 for expression in self.get_source_expressions()
97 ]
98 )
99 return super(Coalesce, clone).as_sql(compiler, connection, **extra_context)
100 return self.as_sql(compiler, connection, **extra_context)
101
102
103class Collate(Func):
104 function = "COLLATE"
105 template = "%(expressions)s %(function)s %(collation)s"
106 allowed_default = False
107 # Inspired from
108 # https://www.postgresql.org/docs/current/sql-syntax-lexical.html#SQL-SYNTAX-IDENTIFIERS
109 collation_re = _lazy_re_compile(r"^[\w-]+$")
110
111 def __init__(self, expression, collation):
112 if not (collation and self.collation_re.match(collation)):
113 raise ValueError("Invalid collation name: %r." % collation)
114 self.collation = collation
115 super().__init__(expression)
116
117 def as_sql(self, compiler, connection, **extra_context):
118 extra_context.setdefault("collation", connection.ops.quote_name(self.collation))
119 return super().as_sql(compiler, connection, **extra_context)
120
121
122class Greatest(Func):
123 """
124 Return the maximum expression.
125
126 If any expression is null the return value is database-specific:
127 On PostgreSQL, the maximum not-null expression is returned.
128 On MySQL, Oracle, and SQLite, if any expression is null, null is returned.
129 """
130
131 function = "GREATEST"
132
133 def __init__(self, *expressions, **extra):
134 if len(expressions) < 2:
135 raise ValueError("Greatest must take at least two expressions")
136 super().__init__(*expressions, **extra)
137
138 def as_sqlite(self, compiler, connection, **extra_context):
139 """Use the MAX function on SQLite."""
140 return super().as_sqlite(compiler, connection, function="MAX", **extra_context)
141
142
143class Least(Func):
144 """
145 Return the minimum expression.
146
147 If any expression is null the return value is database-specific:
148 On PostgreSQL, return the minimum not-null expression.
149 On MySQL, Oracle, and SQLite, if any expression is null, return null.
150 """
151
152 function = "LEAST"
153
154 def __init__(self, *expressions, **extra):
155 if len(expressions) < 2:
156 raise ValueError("Least must take at least two expressions")
157 super().__init__(*expressions, **extra)
158
159 def as_sqlite(self, compiler, connection, **extra_context):
160 """Use the MIN function on SQLite."""
161 return super().as_sqlite(compiler, connection, function="MIN", **extra_context)
162
163
164class NullIf(Func):
165 function = "NULLIF"
166 arity = 2
167
168 def as_oracle(self, compiler, connection, **extra_context):
169 expression1 = self.get_source_expressions()[0]
170 if isinstance(expression1, Value) and expression1.value is None:
171 raise ValueError("Oracle does not allow Value(None) for expression1.")
172 return super().as_sql(compiler, connection, **extra_context)