Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.10/site-packages/django/db/models/functions/comparison.py: 39%

Shortcuts on this page

r m x   toggle line displays

j k   next/prev highlighted chunk

0   (zero) top of page

1   (one) first highlighted chunk

93 statements  

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)