1from plain.exceptions import FieldError, FullResultSet
2from plain.models.expressions import Col
3from plain.models.sql import compiler
4
5
6class SQLCompiler(compiler.SQLCompiler):
7 def as_subquery_condition(self, alias, columns, compiler):
8 qn = compiler.quote_name_unless_alias
9 qn2 = self.connection.ops.quote_name
10 sql, params = self.as_sql()
11 return (
12 "({}) IN ({})".format(
13 ", ".join(f"{qn(alias)}.{qn2(column)}" for column in columns),
14 sql,
15 ),
16 params,
17 )
18
19
20class SQLInsertCompiler(compiler.SQLInsertCompiler, SQLCompiler):
21 pass
22
23
24class SQLDeleteCompiler(compiler.SQLDeleteCompiler, SQLCompiler):
25 def as_sql(self):
26 # Prefer the non-standard DELETE FROM syntax over the SQL generated by
27 # the SQLDeleteCompiler's default implementation when multiple tables
28 # are involved since MySQL/MariaDB will generate a more efficient query
29 # plan than when using a subquery.
30 where, having, qualify = self.query.where.split_having_qualify(
31 must_group_by=self.query.group_by is not None
32 )
33 if self.single_alias or having or qualify:
34 # DELETE FROM cannot be used when filtering against aggregates or
35 # window functions as it doesn't allow for GROUP BY/HAVING clauses
36 # and the subquery wrapping (necessary to emulate QUALIFY).
37 return super().as_sql()
38 result = [
39 f"DELETE {self.quote_name_unless_alias(self.query.get_initial_alias())} FROM"
40 ]
41 from_sql, params = self.get_from_clause()
42 result.extend(from_sql)
43 try:
44 where_sql, where_params = self.compile(where)
45 except FullResultSet:
46 pass
47 else:
48 result.append(f"WHERE {where_sql}")
49 params.extend(where_params)
50 return " ".join(result), tuple(params)
51
52
53class SQLUpdateCompiler(compiler.SQLUpdateCompiler, SQLCompiler):
54 def as_sql(self):
55 update_query, update_params = super().as_sql()
56 # MySQL and MariaDB support UPDATE ... ORDER BY syntax.
57 if self.query.order_by:
58 order_by_sql = []
59 order_by_params = []
60 db_table = self.query.get_meta().db_table
61 try:
62 for resolved, (sql, params, _) in self.get_order_by():
63 if (
64 isinstance(resolved.expression, Col)
65 and resolved.expression.alias != db_table
66 ):
67 # Ignore ordering if it contains joined fields, because
68 # they cannot be used in the ORDER BY clause.
69 raise FieldError
70 order_by_sql.append(sql)
71 order_by_params.extend(params)
72 update_query += " ORDER BY " + ", ".join(order_by_sql)
73 update_params += tuple(order_by_params)
74 except FieldError:
75 # Ignore ordering if it contains annotations, because they're
76 # removed in .update() and cannot be resolved.
77 pass
78 return update_query, update_params
79
80
81class SQLAggregateCompiler(compiler.SQLAggregateCompiler, SQLCompiler):
82 pass