Plain is headed towards 1.0! Subscribe for development updates →

 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            "DELETE %s FROM"
40            % self.quote_name_unless_alias(self.query.get_initial_alias())
41        ]
42        from_sql, params = self.get_from_clause()
43        result.extend(from_sql)
44        try:
45            where_sql, where_params = self.compile(where)
46        except FullResultSet:
47            pass
48        else:
49            result.append("WHERE %s" % where_sql)
50            params.extend(where_params)
51        return " ".join(result), tuple(params)
52
53
54class SQLUpdateCompiler(compiler.SQLUpdateCompiler, SQLCompiler):
55    def as_sql(self):
56        update_query, update_params = super().as_sql()
57        # MySQL and MariaDB support UPDATE ... ORDER BY syntax.
58        if self.query.order_by:
59            order_by_sql = []
60            order_by_params = []
61            db_table = self.query.get_meta().db_table
62            try:
63                for resolved, (sql, params, _) in self.get_order_by():
64                    if (
65                        isinstance(resolved.expression, Col)
66                        and resolved.expression.alias != db_table
67                    ):
68                        # Ignore ordering if it contains joined fields, because
69                        # they cannot be used in the ORDER BY clause.
70                        raise FieldError
71                    order_by_sql.append(sql)
72                    order_by_params.extend(params)
73                update_query += " ORDER BY " + ", ".join(order_by_sql)
74                update_params += tuple(order_by_params)
75            except FieldError:
76                # Ignore ordering if it contains annotations, because they're
77                # removed in .update() and cannot be resolved.
78                pass
79        return update_query, update_params
80
81
82class SQLAggregateCompiler(compiler.SQLAggregateCompiler, SQLCompiler):
83    pass