forked from googleapis/python-spanner-django
-
Notifications
You must be signed in to change notification settings - Fork 1
Expand file tree
/
Copy pathcompiler.py
More file actions
131 lines (114 loc) · 4.99 KB
/
compiler.py
File metadata and controls
131 lines (114 loc) · 4.99 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
# Copyright 2020 Google LLC
#
# Use of this source code is governed by a BSD-style
# license that can be found in the LICENSE file or at
# https://developers.google.com/open-source/licenses/bsd
from django.core.exceptions import EmptyResultSet
from django.db.models.sql.compiler import (
SQLAggregateCompiler as BaseSQLAggregateCompiler,
SQLCompiler as BaseSQLCompiler,
SQLDeleteCompiler as BaseSQLDeleteCompiler,
SQLInsertCompiler as BaseSQLInsertCompiler,
SQLUpdateCompiler as BaseSQLUpdateCompiler,
)
from django.db.utils import DatabaseError
from django_spanner.utils import add_dummy_where
class SQLCompiler(BaseSQLCompiler):
"""A variation of the Django SQL compiler, adjusted for Spanner-specific
functionality.
"""
def get_combinator_sql(self, combinator, all):
"""Overrides the native Django method.
Copied from the base class except for:
combinator_sql += ' ALL' if all else ' DISTINCT'
Cloud Spanner requires ALL or DISTINCT.
:type combinator: str
:param combinator: A type of the combinator for the operation.
:type all: bool
:param all: Bool option for the SQL statement.
:rtype: tuple
:returns: A tuple containing SQL statement(s) with some additional
parameters.
"""
features = self.connection.features
compilers = [
query.get_compiler(self.using, self.connection)
for query in self.query.combined_queries
if not query.is_empty()
]
if not features.supports_slicing_ordering_in_compound:
for query, compiler in zip(self.query.combined_queries, compilers):
if query.low_mark or query.high_mark:
raise DatabaseError(
"LIMIT/OFFSET not allowed in subqueries of compound "
"statements."
)
if compiler.get_order_by():
raise DatabaseError(
"ORDER BY not allowed in subqueries of compound "
"statements."
)
parts = ()
for compiler in compilers:
try:
# If the columns list is limited, then all combined queries
# must have the same columns list. Set the selects defined on
# the query on all combined queries, if not already set.
if (
not compiler.query.values_select
and self.query.values_select
):
compiler.query.set_values(
(
*self.query.extra_select,
*self.query.values_select,
*self.query.annotation_select,
)
)
part_sql, part_args = compiler.as_sql()
if compiler.query.combinator:
# Wrap in a subquery if wrapping in parentheses isn't
# supported.
if not features.supports_parentheses_in_compound:
part_sql = "SELECT * FROM ({})".format(part_sql)
# Add parentheses when combining with compound query if not
# already added for all compound queries.
elif not features.supports_slicing_ordering_in_compound:
part_sql = "({})".format(part_sql)
parts += ((part_sql, part_args),)
except EmptyResultSet:
# Omit the empty queryset with UNION and with DIFFERENCE if the
# first queryset is nonempty.
if combinator == "union" or (
combinator == "difference" and parts
):
continue
raise
if not parts:
raise EmptyResultSet
combinator_sql = self.connection.ops.set_operators[combinator]
combinator_sql += " ALL" if all else " DISTINCT"
braces = (
"({})" if features.supports_slicing_ordering_in_compound else "{}"
)
sql_parts, args_parts = zip(
*((braces.format(sql), args) for sql, args in parts)
)
result = [" {} ".format(combinator_sql).join(sql_parts)]
params = []
for part in args_parts:
params.extend(part)
result = add_dummy_where(result)
return result, params
class SQLInsertCompiler(BaseSQLInsertCompiler, SQLCompiler):
"""A wrapper class for compatibility with Django specifications."""
pass
class SQLDeleteCompiler(BaseSQLDeleteCompiler, SQLCompiler):
"""A wrapper class for compatibility with Django specifications."""
pass
class SQLUpdateCompiler(BaseSQLUpdateCompiler, SQLCompiler):
"""A wrapper class for compatibility with Django specifications."""
pass
class SQLAggregateCompiler(BaseSQLAggregateCompiler, SQLCompiler):
"""A wrapper class for compatibility with Django specifications."""
pass