text.py 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369
  1. from django.db import NotSupportedError
  2. from django.db.models.expressions import Func, Value
  3. from django.db.models.fields import CharField, IntegerField, TextField
  4. from django.db.models.functions import Cast, Coalesce
  5. from django.db.models.lookups import Transform
  6. class MySQLSHA2Mixin:
  7. def as_mysql(self, compiler, connection, **extra_context):
  8. return super().as_sql(
  9. compiler,
  10. connection,
  11. template="SHA2(%%(expressions)s, %s)" % self.function[3:],
  12. **extra_context,
  13. )
  14. class OracleHashMixin:
  15. def as_oracle(self, compiler, connection, **extra_context):
  16. return super().as_sql(
  17. compiler,
  18. connection,
  19. template=(
  20. "LOWER(RAWTOHEX(STANDARD_HASH(UTL_I18N.STRING_TO_RAW("
  21. "%(expressions)s, 'AL32UTF8'), '%(function)s')))"
  22. ),
  23. **extra_context,
  24. )
  25. class PostgreSQLSHAMixin:
  26. def as_postgresql(self, compiler, connection, **extra_context):
  27. return super().as_sql(
  28. compiler,
  29. connection,
  30. template="ENCODE(DIGEST(%(expressions)s, '%(function)s'), 'hex')",
  31. function=self.function.lower(),
  32. **extra_context,
  33. )
  34. class Chr(Transform):
  35. function = "CHR"
  36. lookup_name = "chr"
  37. output_field = CharField()
  38. def as_mysql(self, compiler, connection, **extra_context):
  39. return super().as_sql(
  40. compiler,
  41. connection,
  42. function="CHAR",
  43. template="%(function)s(%(expressions)s USING utf16)",
  44. **extra_context,
  45. )
  46. def as_oracle(self, compiler, connection, **extra_context):
  47. return super().as_sql(
  48. compiler,
  49. connection,
  50. template="%(function)s(%(expressions)s USING NCHAR_CS)",
  51. **extra_context,
  52. )
  53. def as_sqlite(self, compiler, connection, **extra_context):
  54. return super().as_sql(compiler, connection, function="CHAR", **extra_context)
  55. class ConcatPair(Func):
  56. """
  57. Concatenate two arguments together. This is used by `Concat` because not
  58. all backend databases support more than two arguments.
  59. """
  60. function = "CONCAT"
  61. def as_sqlite(self, compiler, connection, **extra_context):
  62. coalesced = self.coalesce()
  63. return super(ConcatPair, coalesced).as_sql(
  64. compiler,
  65. connection,
  66. template="%(expressions)s",
  67. arg_joiner=" || ",
  68. **extra_context,
  69. )
  70. def as_postgresql(self, compiler, connection, **extra_context):
  71. copy = self.copy()
  72. copy.set_source_expressions(
  73. [
  74. Cast(expression, TextField())
  75. for expression in copy.get_source_expressions()
  76. ]
  77. )
  78. return super(ConcatPair, copy).as_sql(
  79. compiler,
  80. connection,
  81. **extra_context,
  82. )
  83. def as_mysql(self, compiler, connection, **extra_context):
  84. # Use CONCAT_WS with an empty separator so that NULLs are ignored.
  85. return super().as_sql(
  86. compiler,
  87. connection,
  88. function="CONCAT_WS",
  89. template="%(function)s('', %(expressions)s)",
  90. **extra_context,
  91. )
  92. def coalesce(self):
  93. # null on either side results in null for expression, wrap with coalesce
  94. c = self.copy()
  95. c.set_source_expressions(
  96. [
  97. Coalesce(expression, Value(""))
  98. for expression in c.get_source_expressions()
  99. ]
  100. )
  101. return c
  102. class Concat(Func):
  103. """
  104. Concatenate text fields together. Backends that result in an entire
  105. null expression when any arguments are null will wrap each argument in
  106. coalesce functions to ensure a non-null result.
  107. """
  108. function = None
  109. template = "%(expressions)s"
  110. def __init__(self, *expressions, **extra):
  111. if len(expressions) < 2:
  112. raise ValueError("Concat must take at least two expressions")
  113. paired = self._paired(expressions)
  114. super().__init__(paired, **extra)
  115. def _paired(self, expressions):
  116. # wrap pairs of expressions in successive concat functions
  117. # exp = [a, b, c, d]
  118. # -> ConcatPair(a, ConcatPair(b, ConcatPair(c, d))))
  119. if len(expressions) == 2:
  120. return ConcatPair(*expressions)
  121. return ConcatPair(expressions[0], self._paired(expressions[1:]))
  122. class Left(Func):
  123. function = "LEFT"
  124. arity = 2
  125. output_field = CharField()
  126. def __init__(self, expression, length, **extra):
  127. """
  128. expression: the name of a field, or an expression returning a string
  129. length: the number of characters to return from the start of the string
  130. """
  131. if not hasattr(length, "resolve_expression"):
  132. if length < 1:
  133. raise ValueError("'length' must be greater than 0.")
  134. super().__init__(expression, length, **extra)
  135. def get_substr(self):
  136. return Substr(self.source_expressions[0], Value(1), self.source_expressions[1])
  137. def as_oracle(self, compiler, connection, **extra_context):
  138. return self.get_substr().as_oracle(compiler, connection, **extra_context)
  139. def as_sqlite(self, compiler, connection, **extra_context):
  140. return self.get_substr().as_sqlite(compiler, connection, **extra_context)
  141. class Length(Transform):
  142. """Return the number of characters in the expression."""
  143. function = "LENGTH"
  144. lookup_name = "length"
  145. output_field = IntegerField()
  146. def as_mysql(self, compiler, connection, **extra_context):
  147. return super().as_sql(
  148. compiler, connection, function="CHAR_LENGTH", **extra_context
  149. )
  150. class Lower(Transform):
  151. function = "LOWER"
  152. lookup_name = "lower"
  153. class LPad(Func):
  154. function = "LPAD"
  155. output_field = CharField()
  156. def __init__(self, expression, length, fill_text=Value(" "), **extra):
  157. if (
  158. not hasattr(length, "resolve_expression")
  159. and length is not None
  160. and length < 0
  161. ):
  162. raise ValueError("'length' must be greater or equal to 0.")
  163. super().__init__(expression, length, fill_text, **extra)
  164. class LTrim(Transform):
  165. function = "LTRIM"
  166. lookup_name = "ltrim"
  167. class MD5(OracleHashMixin, Transform):
  168. function = "MD5"
  169. lookup_name = "md5"
  170. class Ord(Transform):
  171. function = "ASCII"
  172. lookup_name = "ord"
  173. output_field = IntegerField()
  174. def as_mysql(self, compiler, connection, **extra_context):
  175. return super().as_sql(compiler, connection, function="ORD", **extra_context)
  176. def as_sqlite(self, compiler, connection, **extra_context):
  177. return super().as_sql(compiler, connection, function="UNICODE", **extra_context)
  178. class Repeat(Func):
  179. function = "REPEAT"
  180. output_field = CharField()
  181. def __init__(self, expression, number, **extra):
  182. if (
  183. not hasattr(number, "resolve_expression")
  184. and number is not None
  185. and number < 0
  186. ):
  187. raise ValueError("'number' must be greater or equal to 0.")
  188. super().__init__(expression, number, **extra)
  189. def as_oracle(self, compiler, connection, **extra_context):
  190. expression, number = self.source_expressions
  191. length = None if number is None else Length(expression) * number
  192. rpad = RPad(expression, length, expression)
  193. return rpad.as_sql(compiler, connection, **extra_context)
  194. class Replace(Func):
  195. function = "REPLACE"
  196. def __init__(self, expression, text, replacement=Value(""), **extra):
  197. super().__init__(expression, text, replacement, **extra)
  198. class Reverse(Transform):
  199. function = "REVERSE"
  200. lookup_name = "reverse"
  201. def as_oracle(self, compiler, connection, **extra_context):
  202. # REVERSE in Oracle is undocumented and doesn't support multi-byte
  203. # strings. Use a special subquery instead.
  204. sql, params = super().as_sql(
  205. compiler,
  206. connection,
  207. template=(
  208. "(SELECT LISTAGG(s) WITHIN GROUP (ORDER BY n DESC) FROM "
  209. "(SELECT LEVEL n, SUBSTR(%(expressions)s, LEVEL, 1) s "
  210. "FROM DUAL CONNECT BY LEVEL <= LENGTH(%(expressions)s)) "
  211. "GROUP BY %(expressions)s)"
  212. ),
  213. **extra_context,
  214. )
  215. return sql, params * 3
  216. class Right(Left):
  217. function = "RIGHT"
  218. def get_substr(self):
  219. return Substr(
  220. self.source_expressions[0],
  221. self.source_expressions[1] * Value(-1),
  222. self.source_expressions[1],
  223. )
  224. class RPad(LPad):
  225. function = "RPAD"
  226. class RTrim(Transform):
  227. function = "RTRIM"
  228. lookup_name = "rtrim"
  229. class SHA1(OracleHashMixin, PostgreSQLSHAMixin, Transform):
  230. function = "SHA1"
  231. lookup_name = "sha1"
  232. class SHA224(MySQLSHA2Mixin, PostgreSQLSHAMixin, Transform):
  233. function = "SHA224"
  234. lookup_name = "sha224"
  235. def as_oracle(self, compiler, connection, **extra_context):
  236. raise NotSupportedError("SHA224 is not supported on Oracle.")
  237. class SHA256(MySQLSHA2Mixin, OracleHashMixin, PostgreSQLSHAMixin, Transform):
  238. function = "SHA256"
  239. lookup_name = "sha256"
  240. class SHA384(MySQLSHA2Mixin, OracleHashMixin, PostgreSQLSHAMixin, Transform):
  241. function = "SHA384"
  242. lookup_name = "sha384"
  243. class SHA512(MySQLSHA2Mixin, OracleHashMixin, PostgreSQLSHAMixin, Transform):
  244. function = "SHA512"
  245. lookup_name = "sha512"
  246. class StrIndex(Func):
  247. """
  248. Return a positive integer corresponding to the 1-indexed position of the
  249. first occurrence of a substring inside another string, or 0 if the
  250. substring is not found.
  251. """
  252. function = "INSTR"
  253. arity = 2
  254. output_field = IntegerField()
  255. def as_postgresql(self, compiler, connection, **extra_context):
  256. return super().as_sql(compiler, connection, function="STRPOS", **extra_context)
  257. class Substr(Func):
  258. function = "SUBSTRING"
  259. output_field = CharField()
  260. def __init__(self, expression, pos, length=None, **extra):
  261. """
  262. expression: the name of a field, or an expression returning a string
  263. pos: an integer > 0, or an expression returning an integer
  264. length: an optional number of characters to return
  265. """
  266. if not hasattr(pos, "resolve_expression"):
  267. if pos < 1:
  268. raise ValueError("'pos' must be greater than 0")
  269. expressions = [expression, pos]
  270. if length is not None:
  271. expressions.append(length)
  272. super().__init__(*expressions, **extra)
  273. def as_sqlite(self, compiler, connection, **extra_context):
  274. return super().as_sql(compiler, connection, function="SUBSTR", **extra_context)
  275. def as_oracle(self, compiler, connection, **extra_context):
  276. return super().as_sql(compiler, connection, function="SUBSTR", **extra_context)
  277. class Trim(Transform):
  278. function = "TRIM"
  279. lookup_name = "trim"
  280. class Upper(Transform):
  281. function = "UPPER"
  282. lookup_name = "upper"