sqlobject 3.13.1__py2.py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- sqlobject/__init__.py +15 -0
- sqlobject/__version__.py +8 -0
- sqlobject/boundattributes.py +124 -0
- sqlobject/cache.py +376 -0
- sqlobject/classregistry.py +138 -0
- sqlobject/col.py +2187 -0
- sqlobject/compat.py +59 -0
- sqlobject/conftest.py +64 -0
- sqlobject/constraints.py +77 -0
- sqlobject/converters.py +278 -0
- sqlobject/dbconnection.py +1137 -0
- sqlobject/dberrors.py +54 -0
- sqlobject/declarative.py +211 -0
- sqlobject/events.py +365 -0
- sqlobject/firebird/__init__.py +8 -0
- sqlobject/firebird/firebirdconnection.py +463 -0
- sqlobject/include/__init__.py +1 -0
- sqlobject/include/hashcol.py +109 -0
- sqlobject/include/tests/__init__.py +1 -0
- sqlobject/include/tests/test_hashcol.py +84 -0
- sqlobject/index.py +189 -0
- sqlobject/inheritance/__init__.py +551 -0
- sqlobject/inheritance/iteration.py +106 -0
- sqlobject/inheritance/tests/__init__.py +1 -0
- sqlobject/inheritance/tests/test_aggregates.py +23 -0
- sqlobject/inheritance/tests/test_asdict.py +48 -0
- sqlobject/inheritance/tests/test_deep_inheritance.py +114 -0
- sqlobject/inheritance/tests/test_destroy_cascade.py +23 -0
- sqlobject/inheritance/tests/test_foreignKey.py +94 -0
- sqlobject/inheritance/tests/test_indexes.py +42 -0
- sqlobject/inheritance/tests/test_inheritance.py +164 -0
- sqlobject/inheritance/tests/test_inheritance_tree.py +50 -0
- sqlobject/joins.py +656 -0
- sqlobject/main.py +1880 -0
- sqlobject/manager/__init__.py +1 -0
- sqlobject/manager/command.py +1309 -0
- sqlobject/maxdb/__init__.py +8 -0
- sqlobject/maxdb/maxdbconnection.py +320 -0
- sqlobject/maxdb/readme.txt +27 -0
- sqlobject/mssql/__init__.py +8 -0
- sqlobject/mssql/mssqlconnection.py +417 -0
- sqlobject/mysql/__init__.py +8 -0
- sqlobject/mysql/mysqlconnection.py +562 -0
- sqlobject/postgres/__init__.py +8 -0
- sqlobject/postgres/pgconnection.py +619 -0
- sqlobject/sqlbuilder.py +1655 -0
- sqlobject/sqlite/__init__.py +8 -0
- sqlobject/sqlite/sqliteconnection.py +418 -0
- sqlobject/sresults.py +377 -0
- sqlobject/styles.py +174 -0
- sqlobject/sybase/__init__.py +8 -0
- sqlobject/sybase/sybaseconnection.py +183 -0
- sqlobject/tests/__init__.py +1 -0
- sqlobject/tests/dbtest.py +363 -0
- sqlobject/tests/test_ForeignKey.py +133 -0
- sqlobject/tests/test_ForeignKey_cascade.py +150 -0
- sqlobject/tests/test_NoneValuedResultItem.py +33 -0
- sqlobject/tests/test_SQLMultipleJoin.py +65 -0
- sqlobject/tests/test_SQLRelatedJoin.py +114 -0
- sqlobject/tests/test_SingleJoin.py +30 -0
- sqlobject/tests/test_aggregates.py +75 -0
- sqlobject/tests/test_aliases.py +49 -0
- sqlobject/tests/test_asdict.py +18 -0
- sqlobject/tests/test_auto.py +218 -0
- sqlobject/tests/test_basic.py +359 -0
- sqlobject/tests/test_blob.py +34 -0
- sqlobject/tests/test_boundattributes.py +67 -0
- sqlobject/tests/test_cache.py +57 -0
- sqlobject/tests/test_class_hash.py +22 -0
- sqlobject/tests/test_columns_order.py +18 -0
- sqlobject/tests/test_combining_joins.py +47 -0
- sqlobject/tests/test_comparison.py +24 -0
- sqlobject/tests/test_compat.py +10 -0
- sqlobject/tests/test_complex_sorting.py +77 -0
- sqlobject/tests/test_constraints.py +43 -0
- sqlobject/tests/test_converters.py +276 -0
- sqlobject/tests/test_create_drop.py +29 -0
- sqlobject/tests/test_csvexport.py +101 -0
- sqlobject/tests/test_csvimport.py +21 -0
- sqlobject/tests/test_cyclic_reference.py +55 -0
- sqlobject/tests/test_datetime.py +171 -0
- sqlobject/tests/test_dbconnection.py +17 -0
- sqlobject/tests/test_decimal.py +63 -0
- sqlobject/tests/test_declarative.py +83 -0
- sqlobject/tests/test_default_style.py +65 -0
- sqlobject/tests/test_delete.py +74 -0
- sqlobject/tests/test_distinct.py +36 -0
- sqlobject/tests/test_empty.py +22 -0
- sqlobject/tests/test_enum.py +75 -0
- sqlobject/tests/test_events.py +142 -0
- sqlobject/tests/test_exceptions.py +40 -0
- sqlobject/tests/test_expire.py +29 -0
- sqlobject/tests/test_groupBy.py +45 -0
- sqlobject/tests/test_identity.py +29 -0
- sqlobject/tests/test_indexes.py +100 -0
- sqlobject/tests/test_inheritance.py +35 -0
- sqlobject/tests/test_joins.py +160 -0
- sqlobject/tests/test_joins_conditional.py +143 -0
- sqlobject/tests/test_jsonbcol.py +53 -0
- sqlobject/tests/test_jsoncol.py +56 -0
- sqlobject/tests/test_lazy.py +165 -0
- sqlobject/tests/test_md5.py +13 -0
- sqlobject/tests/test_mysql.py +50 -0
- sqlobject/tests/test_new_joins.py +165 -0
- sqlobject/tests/test_parse_uri.py +147 -0
- sqlobject/tests/test_paste.py +92 -0
- sqlobject/tests/test_perConnection.py +20 -0
- sqlobject/tests/test_pickle.py +42 -0
- sqlobject/tests/test_picklecol.py +44 -0
- sqlobject/tests/test_postgres.py +82 -0
- sqlobject/tests/test_reparent_sqlmeta.py +33 -0
- sqlobject/tests/test_schema.py +30 -0
- sqlobject/tests/test_select.py +218 -0
- sqlobject/tests/test_select_through.py +63 -0
- sqlobject/tests/test_setters.py +23 -0
- sqlobject/tests/test_slice.py +59 -0
- sqlobject/tests/test_sorting.py +80 -0
- sqlobject/tests/test_sqlbuilder.py +134 -0
- sqlobject/tests/test_sqlbuilder_dbspecific.py +66 -0
- sqlobject/tests/test_sqlbuilder_importproxy.py +55 -0
- sqlobject/tests/test_sqlbuilder_joins_instances.py +90 -0
- sqlobject/tests/test_sqlite.py +160 -0
- sqlobject/tests/test_sqlmeta_idName.py +52 -0
- sqlobject/tests/test_sqlobject_admin.py +38 -0
- sqlobject/tests/test_string_id.py +79 -0
- sqlobject/tests/test_style.py +36 -0
- sqlobject/tests/test_subqueries.py +142 -0
- sqlobject/tests/test_transactions.py +122 -0
- sqlobject/tests/test_unicode.py +84 -0
- sqlobject/tests/test_uuidcol.py +40 -0
- sqlobject/tests/test_validation.py +125 -0
- sqlobject/tests/test_views.py +159 -0
- sqlobject/util/__init__.py +1 -0
- sqlobject/util/csvexport.py +210 -0
- sqlobject/util/csvimport.py +366 -0
- sqlobject/util/moduleloader.py +35 -0
- sqlobject/util/threadinglocal.py +6 -0
- sqlobject/versioning/__init__.py +120 -0
- sqlobject/versioning/test/__init__.py +0 -0
- sqlobject/versioning/test/test_version.py +183 -0
- sqlobject/views.py +151 -0
- sqlobject/wsgi_middleware.py +109 -0
- sqlobject-3.13.1.data/scripts/sqlobject-admin +36 -0
- sqlobject-3.13.1.data/scripts/sqlobject-convertOldURI +19 -0
- sqlobject-3.13.1.dist-info/METADATA +177 -0
- sqlobject-3.13.1.dist-info/RECORD +149 -0
- sqlobject-3.13.1.dist-info/WHEEL +6 -0
- sqlobject-3.13.1.dist-info/entry_points.txt +4 -0
- sqlobject-3.13.1.dist-info/top_level.txt +1 -0
sqlobject/sqlbuilder.py
ADDED
|
@@ -0,0 +1,1655 @@
|
|
|
1
|
+
"""
|
|
2
|
+
sqlobject.sqlbuilder
|
|
3
|
+
--------------------
|
|
4
|
+
|
|
5
|
+
:author: Ian Bicking <ianb@colorstudy.com>
|
|
6
|
+
|
|
7
|
+
Builds SQL expressions from normal Python expressions.
|
|
8
|
+
|
|
9
|
+
Disclaimer
|
|
10
|
+
----------
|
|
11
|
+
|
|
12
|
+
This program is free software; you can redistribute it and/or modify
|
|
13
|
+
it under the terms of the GNU Lesser General Public License as
|
|
14
|
+
published by the Free Software Foundation; either version 2.1 of the
|
|
15
|
+
License, or (at your option any later version.
|
|
16
|
+
|
|
17
|
+
This program is distributed in the hope that it will be useful,
|
|
18
|
+
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
19
|
+
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
20
|
+
GNU General Public License for more details.
|
|
21
|
+
|
|
22
|
+
You should have received a copy of the GNU Lesser General Public
|
|
23
|
+
License along with this program; if not, write to the Free Software
|
|
24
|
+
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301,
|
|
25
|
+
USA.
|
|
26
|
+
|
|
27
|
+
Instructions
|
|
28
|
+
------------
|
|
29
|
+
|
|
30
|
+
To begin a SQL expression, you must use some sort of SQL object -- a
|
|
31
|
+
field, table, or SQL statement (``SELECT``, ``INSERT``, etc.) You can
|
|
32
|
+
then use normal operators, with the exception of: `and`, `or`, `not`,
|
|
33
|
+
and `in`. You can use the `AND`, `OR`, `NOT`, and `IN` functions
|
|
34
|
+
instead, or you can also use `&`, `|`, and `~` for `and`, `or`, and
|
|
35
|
+
`not` respectively (however -- the precidence for these operators
|
|
36
|
+
doesn't work as you would want, so you must use many parenthesis).
|
|
37
|
+
|
|
38
|
+
To create a sql field, table, or constant/function, use the namespaces
|
|
39
|
+
`table`, `const`, and `func`. For instance, ``table.address`` refers
|
|
40
|
+
to the ``address`` table, and ``table.address.state`` refers to the
|
|
41
|
+
``state`` field in the address table. ``const.NULL`` is the ``NULL``
|
|
42
|
+
SQL constant, and ``func.NOW()`` is the ``NOW()`` function call
|
|
43
|
+
(`const` and `func` are actually identicle, but the two names are
|
|
44
|
+
provided for clarity). Once you create this object, expressions
|
|
45
|
+
formed with it will produce SQL statements.
|
|
46
|
+
|
|
47
|
+
The ``sqlrepr(obj)`` function gets the SQL representation of these
|
|
48
|
+
objects, as well as the proper SQL representation of basic Python
|
|
49
|
+
types (None==NULL).
|
|
50
|
+
|
|
51
|
+
There are a number of DB-specific SQL features that this does not
|
|
52
|
+
implement. There are a bunch of normal ANSI features also not present.
|
|
53
|
+
|
|
54
|
+
See the bottom of this module for some examples, and run it (i.e.
|
|
55
|
+
``python sql.py``) to see the results of those examples.
|
|
56
|
+
|
|
57
|
+
"""
|
|
58
|
+
|
|
59
|
+
########################################
|
|
60
|
+
# Constants
|
|
61
|
+
########################################
|
|
62
|
+
|
|
63
|
+
import fnmatch
|
|
64
|
+
import operator
|
|
65
|
+
import re
|
|
66
|
+
import threading
|
|
67
|
+
import types
|
|
68
|
+
import weakref
|
|
69
|
+
|
|
70
|
+
from . import classregistry
|
|
71
|
+
from .converters import registerConverter, sqlrepr, quote_str, unquote_str
|
|
72
|
+
from .compat import PY2, string_type
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
class VersionError(Exception):
|
|
76
|
+
pass
|
|
77
|
+
|
|
78
|
+
|
|
79
|
+
class NoDefault:
|
|
80
|
+
pass
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
class SQLObjectState(object):
|
|
84
|
+
def __init__(self, soObject, connection=None):
|
|
85
|
+
self.soObject = weakref.proxy(soObject)
|
|
86
|
+
self.connection = connection
|
|
87
|
+
|
|
88
|
+
|
|
89
|
+
safeSQLRE = re.compile(r'^[a-zA-Z_][a-zA-Z0-9_\.]*$')
|
|
90
|
+
|
|
91
|
+
|
|
92
|
+
def sqlIdentifier(obj):
|
|
93
|
+
# some db drivers return unicode column names
|
|
94
|
+
return isinstance(obj, string_type) and bool(safeSQLRE.search(obj.strip()))
|
|
95
|
+
|
|
96
|
+
|
|
97
|
+
def execute(expr, executor):
|
|
98
|
+
if hasattr(expr, 'execute'):
|
|
99
|
+
return expr.execute(executor)
|
|
100
|
+
else:
|
|
101
|
+
return expr
|
|
102
|
+
|
|
103
|
+
|
|
104
|
+
def _str_or_sqlrepr(expr, db):
|
|
105
|
+
if isinstance(expr, string_type):
|
|
106
|
+
return expr
|
|
107
|
+
return sqlrepr(expr, db)
|
|
108
|
+
|
|
109
|
+
|
|
110
|
+
########################################
|
|
111
|
+
# Expression generation
|
|
112
|
+
########################################
|
|
113
|
+
|
|
114
|
+
|
|
115
|
+
class SQLExpression:
|
|
116
|
+
def __add__(self, other):
|
|
117
|
+
return SQLOp("+", self, other)
|
|
118
|
+
|
|
119
|
+
def __radd__(self, other):
|
|
120
|
+
return SQLOp("+", other, self)
|
|
121
|
+
|
|
122
|
+
def __sub__(self, other):
|
|
123
|
+
return SQLOp("-", self, other)
|
|
124
|
+
|
|
125
|
+
def __rsub__(self, other):
|
|
126
|
+
return SQLOp("-", other, self)
|
|
127
|
+
|
|
128
|
+
def __mul__(self, other):
|
|
129
|
+
return SQLOp("*", self, other)
|
|
130
|
+
|
|
131
|
+
def __rmul__(self, other):
|
|
132
|
+
return SQLOp("*", other, self)
|
|
133
|
+
|
|
134
|
+
def __div__(self, other):
|
|
135
|
+
return SQLOp("/", self, other)
|
|
136
|
+
|
|
137
|
+
def __rdiv__(self, other):
|
|
138
|
+
return SQLOp("/", other, self)
|
|
139
|
+
|
|
140
|
+
def __truediv__(self, other):
|
|
141
|
+
return SQLOp("/", self, other)
|
|
142
|
+
|
|
143
|
+
def __rtruediv__(self, other):
|
|
144
|
+
return SQLOp("/", other, self)
|
|
145
|
+
|
|
146
|
+
def __floordiv__(self, other):
|
|
147
|
+
return SQLConstant("FLOOR")(SQLOp("/", self, other))
|
|
148
|
+
|
|
149
|
+
def __rfloordiv__(self, other):
|
|
150
|
+
return SQLConstant("FLOOR")(SQLOp("/", other, self))
|
|
151
|
+
|
|
152
|
+
def __pos__(self):
|
|
153
|
+
return SQLPrefix("+", self)
|
|
154
|
+
|
|
155
|
+
def __neg__(self):
|
|
156
|
+
return SQLPrefix("-", self)
|
|
157
|
+
|
|
158
|
+
def __pow__(self, other):
|
|
159
|
+
return SQLConstant("POW")(self, other)
|
|
160
|
+
|
|
161
|
+
def __rpow__(self, other):
|
|
162
|
+
return SQLConstant("POW")(other, self)
|
|
163
|
+
|
|
164
|
+
def __abs__(self):
|
|
165
|
+
return SQLConstant("ABS")(self)
|
|
166
|
+
|
|
167
|
+
def __mod__(self, other):
|
|
168
|
+
return SQLModulo(self, other)
|
|
169
|
+
|
|
170
|
+
def __rmod__(self, other):
|
|
171
|
+
return SQLConstant("MOD")(other, self)
|
|
172
|
+
|
|
173
|
+
def __lt__(self, other):
|
|
174
|
+
return SQLOp("<", self, other)
|
|
175
|
+
|
|
176
|
+
def __le__(self, other):
|
|
177
|
+
return SQLOp("<=", self, other)
|
|
178
|
+
|
|
179
|
+
def __gt__(self, other):
|
|
180
|
+
return SQLOp(">", self, other)
|
|
181
|
+
|
|
182
|
+
def __ge__(self, other):
|
|
183
|
+
return SQLOp(">=", self, other)
|
|
184
|
+
|
|
185
|
+
def __eq__(self, other):
|
|
186
|
+
if other is None:
|
|
187
|
+
return ISNULL(self)
|
|
188
|
+
else:
|
|
189
|
+
return SQLOp("=", self, other)
|
|
190
|
+
|
|
191
|
+
def __ne__(self, other):
|
|
192
|
+
if other is None:
|
|
193
|
+
return ISNOTNULL(self)
|
|
194
|
+
else:
|
|
195
|
+
return SQLOp("<>", self, other)
|
|
196
|
+
|
|
197
|
+
def __and__(self, other):
|
|
198
|
+
return SQLOp("AND", self, other)
|
|
199
|
+
|
|
200
|
+
def __rand__(self, other):
|
|
201
|
+
return SQLOp("AND", other, self)
|
|
202
|
+
|
|
203
|
+
def __or__(self, other):
|
|
204
|
+
return SQLOp("OR", self, other)
|
|
205
|
+
|
|
206
|
+
def __ror__(self, other):
|
|
207
|
+
return SQLOp("OR", other, self)
|
|
208
|
+
|
|
209
|
+
def __invert__(self):
|
|
210
|
+
return SQLPrefix("NOT", self)
|
|
211
|
+
|
|
212
|
+
def __call__(self, *args):
|
|
213
|
+
return SQLCall(self, args)
|
|
214
|
+
|
|
215
|
+
def __repr__(self):
|
|
216
|
+
try:
|
|
217
|
+
return self.__sqlrepr__(None)
|
|
218
|
+
except AssertionError:
|
|
219
|
+
return '<%s %s>' % (
|
|
220
|
+
self.__class__.__name__, hex(id(self))[2:])
|
|
221
|
+
|
|
222
|
+
def __str__(self):
|
|
223
|
+
return repr(self)
|
|
224
|
+
|
|
225
|
+
def __cmp__(self, other):
|
|
226
|
+
raise VersionError("Python 2.1+ required")
|
|
227
|
+
|
|
228
|
+
def __rcmp__(self, other):
|
|
229
|
+
raise VersionError("Python 2.1+ required")
|
|
230
|
+
|
|
231
|
+
def startswith(self, s):
|
|
232
|
+
return STARTSWITH(self, s)
|
|
233
|
+
|
|
234
|
+
def endswith(self, s):
|
|
235
|
+
return ENDSWITH(self, s)
|
|
236
|
+
|
|
237
|
+
def contains(self, s):
|
|
238
|
+
return CONTAINSSTRING(self, s)
|
|
239
|
+
|
|
240
|
+
def components(self):
|
|
241
|
+
return []
|
|
242
|
+
|
|
243
|
+
def tablesUsed(self, db):
|
|
244
|
+
return self.tablesUsedSet(db)
|
|
245
|
+
|
|
246
|
+
def tablesUsedSet(self, db):
|
|
247
|
+
tables = set()
|
|
248
|
+
for table in self.tablesUsedImmediate():
|
|
249
|
+
if hasattr(table, '__sqlrepr__'):
|
|
250
|
+
table = sqlrepr(table, db)
|
|
251
|
+
tables.add(table)
|
|
252
|
+
for component in self.components():
|
|
253
|
+
tables.update(tablesUsedSet(component, db))
|
|
254
|
+
return tables
|
|
255
|
+
|
|
256
|
+
def tablesUsedImmediate(self):
|
|
257
|
+
return []
|
|
258
|
+
|
|
259
|
+
|
|
260
|
+
#######################################
|
|
261
|
+
# Converter for SQLExpression instances
|
|
262
|
+
#######################################
|
|
263
|
+
|
|
264
|
+
|
|
265
|
+
def SQLExprConverter(value, db):
|
|
266
|
+
return value.__sqlrepr__()
|
|
267
|
+
|
|
268
|
+
registerConverter(SQLExpression, SQLExprConverter)
|
|
269
|
+
|
|
270
|
+
|
|
271
|
+
def tablesUsedSet(obj, db):
|
|
272
|
+
if hasattr(obj, "tablesUsedSet"):
|
|
273
|
+
return obj.tablesUsedSet(db)
|
|
274
|
+
else:
|
|
275
|
+
return {}
|
|
276
|
+
|
|
277
|
+
|
|
278
|
+
if PY2:
|
|
279
|
+
div = operator.div
|
|
280
|
+
else:
|
|
281
|
+
div = operator.truediv
|
|
282
|
+
|
|
283
|
+
|
|
284
|
+
operatorMap = {
|
|
285
|
+
"+": operator.add,
|
|
286
|
+
"/": div,
|
|
287
|
+
"-": operator.sub,
|
|
288
|
+
"*": operator.mul,
|
|
289
|
+
"<": operator.lt,
|
|
290
|
+
"<=": operator.le,
|
|
291
|
+
"=": operator.eq,
|
|
292
|
+
"!=": operator.ne,
|
|
293
|
+
">=": operator.ge,
|
|
294
|
+
">": operator.gt,
|
|
295
|
+
"IN": operator.contains,
|
|
296
|
+
"IS": operator.eq,
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
|
|
300
|
+
class SQLOp(SQLExpression):
|
|
301
|
+
def __init__(self, op, expr1, expr2):
|
|
302
|
+
self.op = op.upper()
|
|
303
|
+
if isinstance(expr1, Subquery):
|
|
304
|
+
expr1, expr2 = expr2, expr1
|
|
305
|
+
self.expr1 = expr1
|
|
306
|
+
self.expr2 = expr2
|
|
307
|
+
|
|
308
|
+
def __sqlrepr__(self, db):
|
|
309
|
+
s1 = sqlrepr(self.expr1, db)
|
|
310
|
+
s2 = sqlrepr(self.expr2, db)
|
|
311
|
+
if s1[0] != '(' and s1 != 'NULL':
|
|
312
|
+
s1 = '(' + s1 + ')'
|
|
313
|
+
if s2[0] != '(' and s2 != 'NULL' and \
|
|
314
|
+
not isinstance(self.expr2, Subquery):
|
|
315
|
+
s2 = '(' + s2 + ')'
|
|
316
|
+
return "(%s %s %s)" % (s1, self.op, s2)
|
|
317
|
+
|
|
318
|
+
def components(self):
|
|
319
|
+
return [self.expr1, self.expr2]
|
|
320
|
+
|
|
321
|
+
def execute(self, executor):
|
|
322
|
+
if self.op == "AND":
|
|
323
|
+
return execute(self.expr1, executor) \
|
|
324
|
+
and execute(self.expr2, executor)
|
|
325
|
+
elif self.op == "OR":
|
|
326
|
+
return execute(self.expr1, executor) \
|
|
327
|
+
or execute(self.expr2, executor)
|
|
328
|
+
else:
|
|
329
|
+
return operatorMap[self.op.upper()](execute(self.expr1, executor),
|
|
330
|
+
execute(self.expr2, executor))
|
|
331
|
+
|
|
332
|
+
registerConverter(SQLOp, SQLExprConverter)
|
|
333
|
+
|
|
334
|
+
|
|
335
|
+
class SQLModulo(SQLOp):
|
|
336
|
+
def __init__(self, expr1, expr2):
|
|
337
|
+
SQLOp.__init__(self, '%', expr1, expr2)
|
|
338
|
+
|
|
339
|
+
def __sqlrepr__(self, db):
|
|
340
|
+
if db == 'sqlite':
|
|
341
|
+
return SQLOp.__sqlrepr__(self, db)
|
|
342
|
+
s1 = sqlrepr(self.expr1, db)
|
|
343
|
+
s2 = sqlrepr(self.expr2, db)
|
|
344
|
+
return "MOD(%s, %s)" % (s1, s2)
|
|
345
|
+
|
|
346
|
+
registerConverter(SQLModulo, SQLExprConverter)
|
|
347
|
+
|
|
348
|
+
|
|
349
|
+
class SQLCall(SQLExpression):
|
|
350
|
+
def __init__(self, expr, args):
|
|
351
|
+
self.expr = expr
|
|
352
|
+
self.args = args
|
|
353
|
+
|
|
354
|
+
def __sqlrepr__(self, db):
|
|
355
|
+
return "%s%s" % (sqlrepr(self.expr, db), sqlrepr(self.args, db))
|
|
356
|
+
|
|
357
|
+
def components(self):
|
|
358
|
+
return [self.expr] + list(self.args)
|
|
359
|
+
|
|
360
|
+
def execute(self, executor):
|
|
361
|
+
raise ValueError("I don't yet know how to locally execute functions")
|
|
362
|
+
|
|
363
|
+
registerConverter(SQLCall, SQLExprConverter)
|
|
364
|
+
|
|
365
|
+
|
|
366
|
+
class SQLPrefix(SQLExpression):
|
|
367
|
+
def __init__(self, prefix, expr):
|
|
368
|
+
self.prefix = prefix
|
|
369
|
+
self.expr = expr
|
|
370
|
+
|
|
371
|
+
def __sqlrepr__(self, db):
|
|
372
|
+
return "%s %s" % (self.prefix, sqlrepr(self.expr, db))
|
|
373
|
+
|
|
374
|
+
def components(self):
|
|
375
|
+
return [self.expr]
|
|
376
|
+
|
|
377
|
+
def execute(self, executor):
|
|
378
|
+
prefix = self.prefix
|
|
379
|
+
expr = execute(self.expr, executor)
|
|
380
|
+
if prefix == "+":
|
|
381
|
+
return expr
|
|
382
|
+
elif prefix == "-":
|
|
383
|
+
return -expr
|
|
384
|
+
elif prefix.upper() == "NOT":
|
|
385
|
+
return not expr
|
|
386
|
+
|
|
387
|
+
registerConverter(SQLPrefix, SQLExprConverter)
|
|
388
|
+
|
|
389
|
+
|
|
390
|
+
class SQLConstant(SQLExpression):
|
|
391
|
+
def __init__(self, const):
|
|
392
|
+
self.const = const
|
|
393
|
+
|
|
394
|
+
def __sqlrepr__(self, db):
|
|
395
|
+
return self.const
|
|
396
|
+
|
|
397
|
+
def execute(self, executor):
|
|
398
|
+
raise ValueError("I don't yet know how to execute SQL constants")
|
|
399
|
+
|
|
400
|
+
registerConverter(SQLConstant, SQLExprConverter)
|
|
401
|
+
|
|
402
|
+
|
|
403
|
+
class SQLTrueClauseClass(SQLExpression):
|
|
404
|
+
def __sqlrepr__(self, db):
|
|
405
|
+
return "1 = 1"
|
|
406
|
+
|
|
407
|
+
def execute(self, executor):
|
|
408
|
+
return 1
|
|
409
|
+
|
|
410
|
+
SQLTrueClause = SQLTrueClauseClass()
|
|
411
|
+
|
|
412
|
+
registerConverter(SQLTrueClauseClass, SQLExprConverter)
|
|
413
|
+
|
|
414
|
+
########################################
|
|
415
|
+
# Namespaces
|
|
416
|
+
########################################
|
|
417
|
+
|
|
418
|
+
|
|
419
|
+
class Field(SQLExpression):
|
|
420
|
+
def __init__(self, tableName, fieldName):
|
|
421
|
+
self.tableName = tableName
|
|
422
|
+
self.fieldName = fieldName
|
|
423
|
+
|
|
424
|
+
def __sqlrepr__(self, db):
|
|
425
|
+
return self.tableName + "." + self.fieldName
|
|
426
|
+
|
|
427
|
+
def tablesUsedImmediate(self):
|
|
428
|
+
return [self.tableName]
|
|
429
|
+
|
|
430
|
+
def execute(self, executor):
|
|
431
|
+
return executor.field(self.tableName, self.fieldName)
|
|
432
|
+
|
|
433
|
+
|
|
434
|
+
class SQLObjectField(Field):
|
|
435
|
+
def __init__(self, tableName, fieldName, original, soClass, column):
|
|
436
|
+
Field.__init__(self, tableName, fieldName)
|
|
437
|
+
self.original = original
|
|
438
|
+
self.soClass = soClass
|
|
439
|
+
self.column = column
|
|
440
|
+
|
|
441
|
+
def _from_python(self, value):
|
|
442
|
+
column = self.column
|
|
443
|
+
if not isinstance(value, SQLExpression) and \
|
|
444
|
+
column and column.from_python:
|
|
445
|
+
value = column.from_python(value, SQLObjectState(self.soClass))
|
|
446
|
+
return value
|
|
447
|
+
|
|
448
|
+
def __eq__(self, other):
|
|
449
|
+
if other is None:
|
|
450
|
+
return ISNULL(self)
|
|
451
|
+
other = self._from_python(other)
|
|
452
|
+
return SQLOp('=', self, other)
|
|
453
|
+
|
|
454
|
+
def __ne__(self, other):
|
|
455
|
+
if other is None:
|
|
456
|
+
return ISNOTNULL(self)
|
|
457
|
+
other = self._from_python(other)
|
|
458
|
+
return SQLOp('<>', self, other)
|
|
459
|
+
|
|
460
|
+
def startswith(self, s):
|
|
461
|
+
s = self._from_python(s)
|
|
462
|
+
return STARTSWITH(self, s)
|
|
463
|
+
|
|
464
|
+
def endswith(self, s):
|
|
465
|
+
s = self._from_python(s)
|
|
466
|
+
return ENDSWITH(self, s)
|
|
467
|
+
|
|
468
|
+
def contains(self, s):
|
|
469
|
+
s = self._from_python(s)
|
|
470
|
+
return CONTAINSSTRING(self, s)
|
|
471
|
+
|
|
472
|
+
registerConverter(SQLObjectField, SQLExprConverter)
|
|
473
|
+
|
|
474
|
+
|
|
475
|
+
class Table(SQLExpression):
|
|
476
|
+
FieldClass = Field
|
|
477
|
+
|
|
478
|
+
def __init__(self, tableName):
|
|
479
|
+
self.tableName = tableName
|
|
480
|
+
|
|
481
|
+
def __getattr__(self, attr):
|
|
482
|
+
if attr.startswith('__'):
|
|
483
|
+
raise AttributeError("Attribute '%s' is forbidden in '%s'" % (
|
|
484
|
+
attr, self.__class__.__name__))
|
|
485
|
+
return self.FieldClass(self.tableName, attr)
|
|
486
|
+
|
|
487
|
+
def __sqlrepr__(self, db):
|
|
488
|
+
return _str_or_sqlrepr(self.tableName, db)
|
|
489
|
+
|
|
490
|
+
def execute(self, executor):
|
|
491
|
+
raise ValueError("Tables don't have values")
|
|
492
|
+
|
|
493
|
+
|
|
494
|
+
class SQLObjectTable(Table):
|
|
495
|
+
FieldClass = SQLObjectField
|
|
496
|
+
|
|
497
|
+
def __init__(self, soClass):
|
|
498
|
+
self.soClass = soClass
|
|
499
|
+
assert soClass.sqlmeta.table, (
|
|
500
|
+
"Bad table name in class %r: %r"
|
|
501
|
+
% (soClass, soClass.sqlmeta.table))
|
|
502
|
+
Table.__init__(self, soClass.sqlmeta.table)
|
|
503
|
+
|
|
504
|
+
def __getattr__(self, attr):
|
|
505
|
+
if attr.startswith('__'):
|
|
506
|
+
raise AttributeError("Attribute '%s' is forbidden in '%s'" % (
|
|
507
|
+
attr, self.__class__.__name__))
|
|
508
|
+
if attr == 'id':
|
|
509
|
+
return self._getattrFromID(attr)
|
|
510
|
+
elif attr in self.soClass.sqlmeta.columns:
|
|
511
|
+
column = self.soClass.sqlmeta.columns[attr]
|
|
512
|
+
return self._getattrFromColumn(column, attr)
|
|
513
|
+
elif attr + 'ID' in \
|
|
514
|
+
[k for (k, v) in self.soClass.sqlmeta.columns.items()
|
|
515
|
+
if v.foreignKey]:
|
|
516
|
+
attr += 'ID'
|
|
517
|
+
column = self.soClass.sqlmeta.columns[attr]
|
|
518
|
+
return self._getattrFromColumn(column, attr)
|
|
519
|
+
else:
|
|
520
|
+
raise AttributeError(
|
|
521
|
+
"%s instance has no attribute '%s'" % (self.soClass.__name__,
|
|
522
|
+
attr))
|
|
523
|
+
|
|
524
|
+
def _getattrFromID(self, attr):
|
|
525
|
+
return self.FieldClass(self.tableName, self.soClass.sqlmeta.idName,
|
|
526
|
+
attr, self.soClass, None)
|
|
527
|
+
|
|
528
|
+
def _getattrFromColumn(self, column, attr):
|
|
529
|
+
return self.FieldClass(self.tableName, column.dbName, attr,
|
|
530
|
+
self.soClass, column)
|
|
531
|
+
|
|
532
|
+
|
|
533
|
+
class SQLObjectTableWithJoins(SQLObjectTable):
|
|
534
|
+
|
|
535
|
+
def __getattr__(self, attr):
|
|
536
|
+
if attr + 'ID' in \
|
|
537
|
+
[k for (k, v) in self.soClass.sqlmeta.columns.items()
|
|
538
|
+
if v.foreignKey]:
|
|
539
|
+
column = self.soClass.sqlmeta.columns[attr + 'ID']
|
|
540
|
+
return self._getattrFromForeignKey(column, attr)
|
|
541
|
+
elif attr in [x.joinMethodName for x in self.soClass.sqlmeta.joins]:
|
|
542
|
+
join = [x for x in self.soClass.sqlmeta.joins
|
|
543
|
+
if x.joinMethodName == attr][0]
|
|
544
|
+
return self._getattrFromJoin(join, attr)
|
|
545
|
+
else:
|
|
546
|
+
return SQLObjectTable.__getattr__(self, attr)
|
|
547
|
+
|
|
548
|
+
def _getattrFromForeignKey(self, column, attr):
|
|
549
|
+
ret = getattr(self, column.name) == \
|
|
550
|
+
getattr(self.soClass, '_SO_class_' + column.foreignKey).q.id
|
|
551
|
+
return ret
|
|
552
|
+
|
|
553
|
+
def _getattrFromJoin(self, join, attr):
|
|
554
|
+
if hasattr(join, 'otherColumn'):
|
|
555
|
+
return AND(
|
|
556
|
+
join.otherClass.q.id == Field(join.intermediateTable,
|
|
557
|
+
join.otherColumn),
|
|
558
|
+
Field(join.intermediateTable,
|
|
559
|
+
join.joinColumn) == self.soClass.q.id)
|
|
560
|
+
else:
|
|
561
|
+
return getattr(join.otherClass.q, join.joinColumn) == \
|
|
562
|
+
self.soClass.q.id
|
|
563
|
+
|
|
564
|
+
|
|
565
|
+
class TableSpace:
|
|
566
|
+
TableClass = Table
|
|
567
|
+
|
|
568
|
+
def __getattr__(self, attr):
|
|
569
|
+
if attr.startswith('__'):
|
|
570
|
+
raise AttributeError("Attribute '%s' is forbidden in '%s'" % (
|
|
571
|
+
attr, self.__class__.__name__))
|
|
572
|
+
return self.TableClass(attr)
|
|
573
|
+
|
|
574
|
+
|
|
575
|
+
class ConstantSpace:
|
|
576
|
+
def __getattr__(self, attr):
|
|
577
|
+
if attr.startswith('__'):
|
|
578
|
+
raise AttributeError("Attribute '%s' is forbidden in '%s'" % (
|
|
579
|
+
attr, self.__class__.__name__))
|
|
580
|
+
return SQLConstant(attr)
|
|
581
|
+
|
|
582
|
+
|
|
583
|
+
########################################
|
|
584
|
+
# Table aliases
|
|
585
|
+
########################################
|
|
586
|
+
|
|
587
|
+
class AliasField(Field):
|
|
588
|
+
def __init__(self, tableName, fieldName, alias, aliasTable):
|
|
589
|
+
Field.__init__(self, tableName, fieldName)
|
|
590
|
+
self.alias = alias
|
|
591
|
+
self.aliasTable = aliasTable
|
|
592
|
+
|
|
593
|
+
def __sqlrepr__(self, db):
|
|
594
|
+
fieldName = self.fieldName
|
|
595
|
+
if isinstance(fieldName, SQLExpression):
|
|
596
|
+
fieldName = sqlrepr(fieldName, db)
|
|
597
|
+
return self.alias + "." + fieldName
|
|
598
|
+
|
|
599
|
+
def tablesUsedImmediate(self):
|
|
600
|
+
return [self.aliasTable]
|
|
601
|
+
|
|
602
|
+
|
|
603
|
+
class AliasTable(Table):
|
|
604
|
+
as_string = '' # set it to "AS" if your database requires it
|
|
605
|
+
FieldClass = AliasField
|
|
606
|
+
|
|
607
|
+
_alias_lock = threading.Lock()
|
|
608
|
+
_alias_counter = 0
|
|
609
|
+
|
|
610
|
+
def __init__(self, table, alias=None):
|
|
611
|
+
if hasattr(table, "sqlmeta"):
|
|
612
|
+
tableName = SQLConstant(table.sqlmeta.table)
|
|
613
|
+
elif isinstance(table, (Select, Union)):
|
|
614
|
+
assert alias is not None, \
|
|
615
|
+
"Alias name cannot be constructed from Select instances, " \
|
|
616
|
+
"please provide an 'alias' keyword."
|
|
617
|
+
tableName = Subquery('', table)
|
|
618
|
+
table = None
|
|
619
|
+
else:
|
|
620
|
+
tableName = SQLConstant(table)
|
|
621
|
+
table = None
|
|
622
|
+
Table.__init__(self, tableName)
|
|
623
|
+
self.table = table
|
|
624
|
+
if alias is None:
|
|
625
|
+
self._alias_lock.acquire()
|
|
626
|
+
try:
|
|
627
|
+
AliasTable._alias_counter += 1
|
|
628
|
+
alias = "%s_alias%d" % (tableName, AliasTable._alias_counter)
|
|
629
|
+
finally:
|
|
630
|
+
self._alias_lock.release()
|
|
631
|
+
self.alias = alias
|
|
632
|
+
|
|
633
|
+
def __getattr__(self, attr):
|
|
634
|
+
if attr.startswith('__'):
|
|
635
|
+
raise AttributeError("Attribute '%s' is forbidden in '%s'" % (
|
|
636
|
+
attr, self.__class__.__name__))
|
|
637
|
+
if self.table:
|
|
638
|
+
attr = getattr(self.table.q, attr).fieldName
|
|
639
|
+
return self.FieldClass(self.tableName, attr, self.alias, self)
|
|
640
|
+
|
|
641
|
+
def __sqlrepr__(self, db):
|
|
642
|
+
return "%s %s %s" % (sqlrepr(self.tableName, db), self.as_string,
|
|
643
|
+
self.alias)
|
|
644
|
+
|
|
645
|
+
|
|
646
|
+
class AliasSQLMeta():
|
|
647
|
+
def __init__(self, table, alias):
|
|
648
|
+
self.__table = table
|
|
649
|
+
self.__alias = alias
|
|
650
|
+
|
|
651
|
+
def __getattr__(self, attr):
|
|
652
|
+
if attr.startswith('__'):
|
|
653
|
+
raise AttributeError("Attribute '%s' is forbidden in '%s'" % (
|
|
654
|
+
attr, self.__class__.__name__))
|
|
655
|
+
table = self.__table
|
|
656
|
+
if (attr == "table"):
|
|
657
|
+
return '%s %s' % (table.sqlmeta.table, self.__alias)
|
|
658
|
+
return getattr(table.sqlmeta, attr)
|
|
659
|
+
|
|
660
|
+
|
|
661
|
+
class Alias(SQLExpression):
|
|
662
|
+
def __init__(self, table, alias=None):
|
|
663
|
+
self.q = AliasTable(table, alias)
|
|
664
|
+
|
|
665
|
+
def __getattr__(self, attr):
|
|
666
|
+
table = self.q.table
|
|
667
|
+
if (attr == "sqlmeta") and hasattr(table, "sqlmeta"):
|
|
668
|
+
alias = self.q.alias
|
|
669
|
+
return AliasSQLMeta(table, alias)
|
|
670
|
+
return getattr(table, attr)
|
|
671
|
+
|
|
672
|
+
def __sqlrepr__(self, db):
|
|
673
|
+
return sqlrepr(self.q, db)
|
|
674
|
+
|
|
675
|
+
def components(self):
|
|
676
|
+
return [self.q]
|
|
677
|
+
|
|
678
|
+
def select(self, clause=None, clauseTables=None,
|
|
679
|
+
orderBy=NoDefault, limit=None,
|
|
680
|
+
lazyColumns=False, reversed=False,
|
|
681
|
+
distinct=False, connection=None,
|
|
682
|
+
join=None, forUpdate=False):
|
|
683
|
+
# Import here to avoid circular import
|
|
684
|
+
from .sresults import SelectResults
|
|
685
|
+
return SelectResults(self, clause,
|
|
686
|
+
clauseTables=clauseTables,
|
|
687
|
+
orderBy=orderBy,
|
|
688
|
+
limit=limit,
|
|
689
|
+
lazyColumns=lazyColumns,
|
|
690
|
+
reversed=reversed,
|
|
691
|
+
distinct=distinct,
|
|
692
|
+
connection=connection,
|
|
693
|
+
join=join, forUpdate=forUpdate)
|
|
694
|
+
|
|
695
|
+
|
|
696
|
+
class Union(SQLExpression):
|
|
697
|
+
def __init__(self, *tables):
|
|
698
|
+
tabs = []
|
|
699
|
+
for t in tables:
|
|
700
|
+
if not isinstance(t, SQLExpression) and hasattr(t, 'sqlmeta'):
|
|
701
|
+
t = t.sqlmeta.table
|
|
702
|
+
if isinstance(t, Alias):
|
|
703
|
+
t = t.q
|
|
704
|
+
if isinstance(t, Table):
|
|
705
|
+
t = t.tableName
|
|
706
|
+
if not isinstance(t, SQLExpression):
|
|
707
|
+
t = SQLConstant(t)
|
|
708
|
+
tabs.append(t)
|
|
709
|
+
self.tables = tabs
|
|
710
|
+
|
|
711
|
+
def __sqlrepr__(self, db):
|
|
712
|
+
return " UNION ".join([str(sqlrepr(t, db)) for t in self.tables])
|
|
713
|
+
|
|
714
|
+
########################################
|
|
715
|
+
# SQL Statements
|
|
716
|
+
########################################
|
|
717
|
+
|
|
718
|
+
|
|
719
|
+
class Select(SQLExpression):
|
|
720
|
+
def __init__(self, items=NoDefault, where=NoDefault, groupBy=NoDefault,
|
|
721
|
+
having=NoDefault, orderBy=NoDefault, limit=NoDefault,
|
|
722
|
+
join=NoDefault, lazyColumns=False, distinct=False,
|
|
723
|
+
start=0, end=None, reversed=False, forUpdate=False,
|
|
724
|
+
clause=NoDefault, staticTables=NoDefault,
|
|
725
|
+
distinctOn=NoDefault):
|
|
726
|
+
self.ops = {}
|
|
727
|
+
if not isinstance(items, (list, tuple, types.GeneratorType)):
|
|
728
|
+
items = [items]
|
|
729
|
+
if clause is NoDefault and where is not NoDefault:
|
|
730
|
+
clause = where
|
|
731
|
+
if staticTables is NoDefault:
|
|
732
|
+
staticTables = []
|
|
733
|
+
self.ops['items'] = items
|
|
734
|
+
self.ops['clause'] = clause
|
|
735
|
+
self.ops['groupBy'] = groupBy
|
|
736
|
+
self.ops['having'] = having
|
|
737
|
+
self.ops['orderBy'] = orderBy
|
|
738
|
+
self.ops['limit'] = limit
|
|
739
|
+
self.ops['join'] = join
|
|
740
|
+
self.ops['lazyColumns'] = lazyColumns
|
|
741
|
+
self.ops['distinct'] = distinct
|
|
742
|
+
self.ops['distinctOn'] = distinctOn
|
|
743
|
+
self.ops['start'] = start
|
|
744
|
+
self.ops['end'] = end
|
|
745
|
+
self.ops['reversed'] = reversed
|
|
746
|
+
self.ops['forUpdate'] = forUpdate
|
|
747
|
+
self.ops['staticTables'] = staticTables
|
|
748
|
+
|
|
749
|
+
def clone(self, **newOps):
|
|
750
|
+
ops = self.ops.copy()
|
|
751
|
+
ops.update(newOps)
|
|
752
|
+
return self.__class__(**ops)
|
|
753
|
+
|
|
754
|
+
def newItems(self, items):
|
|
755
|
+
return self.clone(items=items)
|
|
756
|
+
|
|
757
|
+
def newClause(self, new_clause):
|
|
758
|
+
return self.clone(clause=new_clause)
|
|
759
|
+
|
|
760
|
+
def orderBy(self, orderBy):
|
|
761
|
+
return self.clone(orderBy=orderBy)
|
|
762
|
+
|
|
763
|
+
def unlimited(self):
|
|
764
|
+
return self.clone(limit=NoDefault, start=0, end=None)
|
|
765
|
+
|
|
766
|
+
def limit(self, limit):
|
|
767
|
+
self.clone(limit=limit)
|
|
768
|
+
|
|
769
|
+
def lazyColumns(self, value):
|
|
770
|
+
return self.clone(lazyColumns=value)
|
|
771
|
+
|
|
772
|
+
def reversed(self):
|
|
773
|
+
return self.clone(reversed=not self.ops.get('reversed', False))
|
|
774
|
+
|
|
775
|
+
def distinct(self):
|
|
776
|
+
return self.clone(distinct=True)
|
|
777
|
+
|
|
778
|
+
def filter(self, filter_clause):
|
|
779
|
+
if filter_clause is None:
|
|
780
|
+
# None doesn't filter anything, it's just a no-op:
|
|
781
|
+
return self
|
|
782
|
+
clause = self.ops['clause']
|
|
783
|
+
if isinstance(clause, string_type):
|
|
784
|
+
clause = SQLConstant('(%s)' % clause)
|
|
785
|
+
return self.newClause(AND(clause, filter_clause))
|
|
786
|
+
|
|
787
|
+
def __sqlrepr__(self, db):
|
|
788
|
+
|
|
789
|
+
select = "SELECT"
|
|
790
|
+
if self.ops['distinct']:
|
|
791
|
+
select += " DISTINCT"
|
|
792
|
+
if self.ops['distinctOn'] is not NoDefault:
|
|
793
|
+
select += " ON(%s)" % _str_or_sqlrepr(
|
|
794
|
+
self.ops['distinctOn'], db)
|
|
795
|
+
if not self.ops['lazyColumns']:
|
|
796
|
+
select += " %s" % ", ".join(
|
|
797
|
+
[str(_str_or_sqlrepr(v, db)) for v in self.ops['items']])
|
|
798
|
+
else:
|
|
799
|
+
select += " %s" % _str_or_sqlrepr(self.ops['items'][0], db)
|
|
800
|
+
|
|
801
|
+
join = []
|
|
802
|
+
join_str = ''
|
|
803
|
+
if self.ops['join'] is not NoDefault and self.ops['join'] is not None:
|
|
804
|
+
_join = self.ops['join']
|
|
805
|
+
if isinstance(_join, str):
|
|
806
|
+
join_str = " " + _join
|
|
807
|
+
elif isinstance(_join, SQLJoin):
|
|
808
|
+
join.append(_join)
|
|
809
|
+
else:
|
|
810
|
+
join.extend(_join)
|
|
811
|
+
tables = set()
|
|
812
|
+
for x in self.ops['staticTables']:
|
|
813
|
+
if isinstance(x, SQLExpression):
|
|
814
|
+
x = sqlrepr(x, db)
|
|
815
|
+
tables.add(x)
|
|
816
|
+
things = list(self.ops['items']) + join
|
|
817
|
+
if self.ops['clause'] is not NoDefault:
|
|
818
|
+
things.append(self.ops['clause'])
|
|
819
|
+
for thing in things:
|
|
820
|
+
if isinstance(thing, SQLExpression):
|
|
821
|
+
tables.update(tablesUsedSet(thing, db))
|
|
822
|
+
for j in join:
|
|
823
|
+
t1 = _str_or_sqlrepr(j.table1, db)
|
|
824
|
+
if t1 in tables:
|
|
825
|
+
tables.remove(t1)
|
|
826
|
+
t2 = _str_or_sqlrepr(j.table2, db)
|
|
827
|
+
if t2 in tables:
|
|
828
|
+
tables.remove(t2)
|
|
829
|
+
if tables:
|
|
830
|
+
select += " FROM %s" % ", ".join(sorted(tables))
|
|
831
|
+
elif join:
|
|
832
|
+
select += " FROM"
|
|
833
|
+
tablesYet = tables
|
|
834
|
+
for j in join:
|
|
835
|
+
if tablesYet and j.table1:
|
|
836
|
+
sep = ", "
|
|
837
|
+
else:
|
|
838
|
+
sep = " "
|
|
839
|
+
select += sep + sqlrepr(j, db)
|
|
840
|
+
tablesYet = True
|
|
841
|
+
|
|
842
|
+
if join_str:
|
|
843
|
+
select += join_str
|
|
844
|
+
|
|
845
|
+
if self.ops['clause'] is not NoDefault:
|
|
846
|
+
select += " WHERE %s" % _str_or_sqlrepr(self.ops['clause'], db)
|
|
847
|
+
if self.ops['groupBy'] is not NoDefault:
|
|
848
|
+
groupBy = _str_or_sqlrepr(self.ops['groupBy'], db)
|
|
849
|
+
if isinstance(self.ops['groupBy'], (list, tuple)):
|
|
850
|
+
groupBy = groupBy[1:-1] # Remove parens
|
|
851
|
+
select += " GROUP BY %s" % groupBy
|
|
852
|
+
if self.ops['having'] is not NoDefault:
|
|
853
|
+
select += " HAVING %s" % _str_or_sqlrepr(self.ops['having'], db)
|
|
854
|
+
if self.ops['orderBy'] is not NoDefault and \
|
|
855
|
+
self.ops['orderBy'] is not None:
|
|
856
|
+
orderBy = self.ops['orderBy']
|
|
857
|
+
if self.ops['reversed']:
|
|
858
|
+
reverser = DESC
|
|
859
|
+
else:
|
|
860
|
+
def reverser(x):
|
|
861
|
+
return x
|
|
862
|
+
if isinstance(orderBy, (list, tuple)):
|
|
863
|
+
select += " ORDER BY %s" % ", ".join(
|
|
864
|
+
[_str_or_sqlrepr(reverser(_x), db) for _x in orderBy])
|
|
865
|
+
else:
|
|
866
|
+
select += " ORDER BY %s" % _str_or_sqlrepr(
|
|
867
|
+
reverser(orderBy), db)
|
|
868
|
+
start, end = self.ops['start'], self.ops['end']
|
|
869
|
+
if self.ops['limit'] is not NoDefault:
|
|
870
|
+
end = start + self.ops['limit']
|
|
871
|
+
if start or end:
|
|
872
|
+
from .dbconnection import dbConnectionForScheme
|
|
873
|
+
select = dbConnectionForScheme(db)._queryAddLimitOffset(select,
|
|
874
|
+
start, end)
|
|
875
|
+
if self.ops['forUpdate']:
|
|
876
|
+
select += " FOR UPDATE"
|
|
877
|
+
return select
|
|
878
|
+
|
|
879
|
+
registerConverter(Select, SQLExprConverter)
|
|
880
|
+
|
|
881
|
+
|
|
882
|
+
class Insert(SQLExpression):
|
|
883
|
+
def __init__(self, table, valueList=None, values=None, template=NoDefault):
|
|
884
|
+
self.template = template
|
|
885
|
+
self.table = table
|
|
886
|
+
if valueList:
|
|
887
|
+
if values:
|
|
888
|
+
raise TypeError("You may only give valueList *or* values")
|
|
889
|
+
self.valueList = valueList
|
|
890
|
+
else:
|
|
891
|
+
self.valueList = [values]
|
|
892
|
+
|
|
893
|
+
def __sqlrepr__(self, db):
|
|
894
|
+
if not self.valueList:
|
|
895
|
+
return ''
|
|
896
|
+
insert = "INSERT INTO %s" % self.table
|
|
897
|
+
allowNonDict = True
|
|
898
|
+
template = self.template
|
|
899
|
+
if (template is NoDefault) and isinstance(self.valueList[0], dict):
|
|
900
|
+
template = list(sorted(self.valueList[0].keys()))
|
|
901
|
+
allowNonDict = False
|
|
902
|
+
if template is not NoDefault:
|
|
903
|
+
insert += " (%s)" % ", ".join(template)
|
|
904
|
+
insert += " VALUES "
|
|
905
|
+
listToJoin = []
|
|
906
|
+
listToJoin_app = listToJoin.append
|
|
907
|
+
for value in self.valueList:
|
|
908
|
+
if isinstance(value, dict):
|
|
909
|
+
if template is NoDefault:
|
|
910
|
+
raise TypeError(
|
|
911
|
+
"You can't mix non-dictionaries with dictionaries "
|
|
912
|
+
"in an INSERT if you don't provide a template (%s)" %
|
|
913
|
+
repr(value))
|
|
914
|
+
value = dictToList(template, value)
|
|
915
|
+
elif not allowNonDict:
|
|
916
|
+
raise TypeError(
|
|
917
|
+
"You can't mix non-dictionaries with dictionaries "
|
|
918
|
+
"in an INSERT if you don't provide a template (%s)" %
|
|
919
|
+
repr(value))
|
|
920
|
+
listToJoin_app("(%s)" % ", ".join([sqlrepr(v, db) for v in value]))
|
|
921
|
+
insert = "%s%s" % (insert, ", ".join(listToJoin))
|
|
922
|
+
return insert
|
|
923
|
+
|
|
924
|
+
registerConverter(Insert, SQLExprConverter)
|
|
925
|
+
|
|
926
|
+
|
|
927
|
+
def dictToList(template, dict):
|
|
928
|
+
list = []
|
|
929
|
+
for key in template:
|
|
930
|
+
list.append(dict[key])
|
|
931
|
+
if len(dict.keys()) > len(template):
|
|
932
|
+
raise TypeError(
|
|
933
|
+
"Extra entries in dictionary that aren't asked for in template "
|
|
934
|
+
"(template=%s, dict=%s)" % (repr(template), repr(dict)))
|
|
935
|
+
return list
|
|
936
|
+
|
|
937
|
+
|
|
938
|
+
class Update(SQLExpression):
|
|
939
|
+
def __init__(self, table, values, template=NoDefault, where=NoDefault):
|
|
940
|
+
self.table = table
|
|
941
|
+
self.values = values
|
|
942
|
+
self.template = template
|
|
943
|
+
self.whereClause = where
|
|
944
|
+
|
|
945
|
+
def __sqlrepr__(self, db):
|
|
946
|
+
update = "%s %s" % (self.sqlName(), self.table)
|
|
947
|
+
update += " SET"
|
|
948
|
+
first = True
|
|
949
|
+
if self.template is not NoDefault:
|
|
950
|
+
for i in range(len(self.template)):
|
|
951
|
+
if first:
|
|
952
|
+
first = False
|
|
953
|
+
else:
|
|
954
|
+
update += ","
|
|
955
|
+
update += " %s=%s" % (self.template[i],
|
|
956
|
+
sqlrepr(self.values[i], db))
|
|
957
|
+
else:
|
|
958
|
+
for key, value in sorted(self.values.items()):
|
|
959
|
+
if first:
|
|
960
|
+
first = False
|
|
961
|
+
else:
|
|
962
|
+
update += ","
|
|
963
|
+
update += " %s=%s" % (key, sqlrepr(value, db))
|
|
964
|
+
if self.whereClause is not NoDefault:
|
|
965
|
+
update += " WHERE %s" % _str_or_sqlrepr(self.whereClause, db)
|
|
966
|
+
return update
|
|
967
|
+
|
|
968
|
+
def sqlName(self):
|
|
969
|
+
return "UPDATE"
|
|
970
|
+
|
|
971
|
+
registerConverter(Update, SQLExprConverter)
|
|
972
|
+
|
|
973
|
+
|
|
974
|
+
class Delete(SQLExpression):
|
|
975
|
+
"""To be safe, this will signal an error if there is no where clause,
|
|
976
|
+
unless you pass in where=None to the constructor."""
|
|
977
|
+
def __init__(self, table, where=NoDefault):
|
|
978
|
+
self.table = table
|
|
979
|
+
if where is NoDefault:
|
|
980
|
+
raise TypeError(
|
|
981
|
+
"You must give a where clause or pass in None "
|
|
982
|
+
"to indicate no where clause")
|
|
983
|
+
self.whereClause = where
|
|
984
|
+
|
|
985
|
+
def __sqlrepr__(self, db):
|
|
986
|
+
whereClause = self.whereClause
|
|
987
|
+
if whereClause is None:
|
|
988
|
+
return "DELETE FROM %s" % self.table
|
|
989
|
+
whereClause = _str_or_sqlrepr(whereClause, db)
|
|
990
|
+
return "DELETE FROM %s WHERE %s" % (self.table, whereClause)
|
|
991
|
+
|
|
992
|
+
registerConverter(Delete, SQLExprConverter)
|
|
993
|
+
|
|
994
|
+
|
|
995
|
+
class Replace(Update):
|
|
996
|
+
def sqlName(self):
|
|
997
|
+
return "REPLACE"
|
|
998
|
+
|
|
999
|
+
registerConverter(Replace, SQLExprConverter)
|
|
1000
|
+
|
|
1001
|
+
########################################
|
|
1002
|
+
# SQL Builtins
|
|
1003
|
+
########################################
|
|
1004
|
+
|
|
1005
|
+
|
|
1006
|
+
class DESC(SQLExpression):
|
|
1007
|
+
|
|
1008
|
+
def __init__(self, expr):
|
|
1009
|
+
self.expr = expr
|
|
1010
|
+
|
|
1011
|
+
def __sqlrepr__(self, db):
|
|
1012
|
+
if isinstance(self.expr, DESC):
|
|
1013
|
+
return sqlrepr(self.expr.expr, db)
|
|
1014
|
+
return '%s DESC' % sqlrepr(self.expr, db)
|
|
1015
|
+
|
|
1016
|
+
|
|
1017
|
+
def AND(*ops):
|
|
1018
|
+
if not ops:
|
|
1019
|
+
return None
|
|
1020
|
+
op1 = ops[0]
|
|
1021
|
+
ops = ops[1:]
|
|
1022
|
+
if ops:
|
|
1023
|
+
return SQLOp("AND", op1, AND(*ops))
|
|
1024
|
+
else:
|
|
1025
|
+
return op1
|
|
1026
|
+
|
|
1027
|
+
|
|
1028
|
+
def OR(*ops):
|
|
1029
|
+
if not ops:
|
|
1030
|
+
return None
|
|
1031
|
+
op1 = ops[0]
|
|
1032
|
+
ops = ops[1:]
|
|
1033
|
+
if ops:
|
|
1034
|
+
return SQLOp("OR", op1, OR(*ops))
|
|
1035
|
+
else:
|
|
1036
|
+
return op1
|
|
1037
|
+
|
|
1038
|
+
|
|
1039
|
+
def NOT(op):
|
|
1040
|
+
return SQLPrefix("NOT", op)
|
|
1041
|
+
|
|
1042
|
+
|
|
1043
|
+
def _IN(item, list):
|
|
1044
|
+
return SQLOp("IN", item, list)
|
|
1045
|
+
|
|
1046
|
+
|
|
1047
|
+
def IN(item, list):
|
|
1048
|
+
from .sresults import SelectResults # Import here to avoid circular import
|
|
1049
|
+
if isinstance(list, SelectResults):
|
|
1050
|
+
query = list.queryForSelect()
|
|
1051
|
+
query.ops['items'] = [list.sourceClass.q.id]
|
|
1052
|
+
list = query
|
|
1053
|
+
if isinstance(list, Select):
|
|
1054
|
+
return INSubquery(item, list)
|
|
1055
|
+
else:
|
|
1056
|
+
return _IN(item, list)
|
|
1057
|
+
|
|
1058
|
+
|
|
1059
|
+
def NOTIN(item, list):
|
|
1060
|
+
if isinstance(list, Select):
|
|
1061
|
+
return NOTINSubquery(item, list)
|
|
1062
|
+
else:
|
|
1063
|
+
return NOT(_IN(item, list))
|
|
1064
|
+
|
|
1065
|
+
|
|
1066
|
+
def STARTSWITH(expr, pattern):
|
|
1067
|
+
return LIKE(expr, _LikeQuoted(pattern) + '%', escape='\\')
|
|
1068
|
+
|
|
1069
|
+
|
|
1070
|
+
def ENDSWITH(expr, pattern):
|
|
1071
|
+
return LIKE(expr, '%' + _LikeQuoted(pattern), escape='\\')
|
|
1072
|
+
|
|
1073
|
+
|
|
1074
|
+
def CONTAINSSTRING(expr, pattern):
|
|
1075
|
+
return LIKE(expr, '%' + _LikeQuoted(pattern) + '%', escape='\\')
|
|
1076
|
+
|
|
1077
|
+
|
|
1078
|
+
def ISNULL(expr):
|
|
1079
|
+
return SQLOp("IS", expr, None)
|
|
1080
|
+
|
|
1081
|
+
|
|
1082
|
+
def ISNOTNULL(expr):
|
|
1083
|
+
return SQLOp("IS NOT", expr, None)
|
|
1084
|
+
|
|
1085
|
+
|
|
1086
|
+
class ColumnAS(SQLOp):
|
|
1087
|
+
''' Just like SQLOp('AS', expr, name) except without the parentheses '''
|
|
1088
|
+
def __init__(self, expr, name):
|
|
1089
|
+
if isinstance(name, string_type):
|
|
1090
|
+
name = SQLConstant(name)
|
|
1091
|
+
SQLOp.__init__(self, 'AS', expr, name)
|
|
1092
|
+
|
|
1093
|
+
def __sqlrepr__(self, db):
|
|
1094
|
+
return "%s %s %s" % (sqlrepr(self.expr1, db), self.op,
|
|
1095
|
+
sqlrepr(self.expr2, db))
|
|
1096
|
+
|
|
1097
|
+
|
|
1098
|
+
class _LikeQuoted:
|
|
1099
|
+
# It assumes prefix and postfix are strings; usually just a percent sign.
|
|
1100
|
+
|
|
1101
|
+
# @@: I'm not sure what the quoting rules really are for all the
|
|
1102
|
+
# databases
|
|
1103
|
+
|
|
1104
|
+
def __init__(self, expr):
|
|
1105
|
+
self.expr = expr
|
|
1106
|
+
self.prefix = ''
|
|
1107
|
+
self.postfix = ''
|
|
1108
|
+
|
|
1109
|
+
def __radd__(self, s):
|
|
1110
|
+
self.prefix = s + self.prefix
|
|
1111
|
+
return self
|
|
1112
|
+
|
|
1113
|
+
def __add__(self, s):
|
|
1114
|
+
self.postfix += s
|
|
1115
|
+
return self
|
|
1116
|
+
|
|
1117
|
+
def __sqlrepr__(self, db):
|
|
1118
|
+
s = self.expr
|
|
1119
|
+
if isinstance(s, SQLExpression):
|
|
1120
|
+
values = []
|
|
1121
|
+
if self.prefix:
|
|
1122
|
+
values.append(quote_str(self.prefix, db))
|
|
1123
|
+
s = _quote_like_special(sqlrepr(s, db), db)
|
|
1124
|
+
values.append(s)
|
|
1125
|
+
if self.postfix:
|
|
1126
|
+
values.append(quote_str(self.postfix, db))
|
|
1127
|
+
if db == "mysql":
|
|
1128
|
+
return "CONCAT(%s)" % ", ".join(values)
|
|
1129
|
+
elif db in ("mssql", "sybase"):
|
|
1130
|
+
return " + ".join(values)
|
|
1131
|
+
else:
|
|
1132
|
+
return " || ".join(values)
|
|
1133
|
+
elif isinstance(s, string_type):
|
|
1134
|
+
s = _quote_like_special(unquote_str(sqlrepr(s, db)), db)
|
|
1135
|
+
return quote_str("%s%s%s" % (self.prefix, s, self.postfix), db)
|
|
1136
|
+
else:
|
|
1137
|
+
raise TypeError(
|
|
1138
|
+
"expected str, unicode or SQLExpression, got %s" % type(s))
|
|
1139
|
+
|
|
1140
|
+
|
|
1141
|
+
def _quote_like_special(s, db):
|
|
1142
|
+
if db == 'postgres':
|
|
1143
|
+
escape = r'\\'
|
|
1144
|
+
else:
|
|
1145
|
+
escape = '\\'
|
|
1146
|
+
s = s.replace('\\', r'\\').\
|
|
1147
|
+
replace('%', escape + '%').\
|
|
1148
|
+
replace('_', escape + '_')
|
|
1149
|
+
return s
|
|
1150
|
+
|
|
1151
|
+
|
|
1152
|
+
class CONCAT(SQLExpression):
|
|
1153
|
+
def __init__(self, *expressions):
|
|
1154
|
+
self.expressions = expressions
|
|
1155
|
+
|
|
1156
|
+
def __sqlrepr__(self, db):
|
|
1157
|
+
values = [sqlrepr(expr, db) for expr in self.expressions]
|
|
1158
|
+
if db == "mysql":
|
|
1159
|
+
return "CONCAT(%s)" % ", ".join(values)
|
|
1160
|
+
elif db in ("mssql", "sybase"):
|
|
1161
|
+
return " + ".join(values)
|
|
1162
|
+
else:
|
|
1163
|
+
return " || ".join(values)
|
|
1164
|
+
|
|
1165
|
+
########################################
|
|
1166
|
+
# SQL JOINs
|
|
1167
|
+
########################################
|
|
1168
|
+
|
|
1169
|
+
|
|
1170
|
+
class SQLJoin(SQLExpression):
|
|
1171
|
+
def __init__(self, table1, table2, op=','):
|
|
1172
|
+
if hasattr(table1, 'sqlmeta'):
|
|
1173
|
+
table1 = table1.sqlmeta.table
|
|
1174
|
+
if hasattr(table2, 'sqlmeta'):
|
|
1175
|
+
table2 = table2.sqlmeta.table
|
|
1176
|
+
if isinstance(table1, str):
|
|
1177
|
+
table1 = SQLConstant(table1)
|
|
1178
|
+
if isinstance(table2, str):
|
|
1179
|
+
table2 = SQLConstant(table2)
|
|
1180
|
+
self.table1 = table1
|
|
1181
|
+
self.table2 = table2
|
|
1182
|
+
self.op = op
|
|
1183
|
+
|
|
1184
|
+
def __sqlrepr__(self, db):
|
|
1185
|
+
if self.table1:
|
|
1186
|
+
return "%s%s %s" % (sqlrepr(self.table1, db), self.op,
|
|
1187
|
+
sqlrepr(self.table2, db))
|
|
1188
|
+
else:
|
|
1189
|
+
return "%s %s" % (self.op, sqlrepr(self.table2, db))
|
|
1190
|
+
|
|
1191
|
+
registerConverter(SQLJoin, SQLExprConverter)
|
|
1192
|
+
|
|
1193
|
+
|
|
1194
|
+
def JOIN(table1, table2):
|
|
1195
|
+
return SQLJoin(table1, table2, " JOIN")
|
|
1196
|
+
|
|
1197
|
+
|
|
1198
|
+
def INNERJOIN(table1, table2):
|
|
1199
|
+
return SQLJoin(table1, table2, " INNER JOIN")
|
|
1200
|
+
|
|
1201
|
+
|
|
1202
|
+
def CROSSJOIN(table1, table2):
|
|
1203
|
+
return SQLJoin(table1, table2, " CROSS JOIN")
|
|
1204
|
+
|
|
1205
|
+
|
|
1206
|
+
def STRAIGHTJOIN(table1, table2):
|
|
1207
|
+
return SQLJoin(table1, table2, " STRAIGHT JOIN")
|
|
1208
|
+
|
|
1209
|
+
|
|
1210
|
+
def LEFTJOIN(table1, table2):
|
|
1211
|
+
return SQLJoin(table1, table2, " LEFT JOIN")
|
|
1212
|
+
|
|
1213
|
+
|
|
1214
|
+
def LEFTOUTERJOIN(table1, table2):
|
|
1215
|
+
return SQLJoin(table1, table2, " LEFT OUTER JOIN")
|
|
1216
|
+
|
|
1217
|
+
|
|
1218
|
+
def NATURALJOIN(table1, table2):
|
|
1219
|
+
return SQLJoin(table1, table2, " NATURAL JOIN")
|
|
1220
|
+
|
|
1221
|
+
|
|
1222
|
+
def NATURALLEFTJOIN(table1, table2):
|
|
1223
|
+
return SQLJoin(table1, table2, " NATURAL LEFT JOIN")
|
|
1224
|
+
|
|
1225
|
+
|
|
1226
|
+
def NATURALLEFTOUTERJOIN(table1, table2):
|
|
1227
|
+
return SQLJoin(table1, table2, " NATURAL LEFT OUTER JOIN")
|
|
1228
|
+
|
|
1229
|
+
|
|
1230
|
+
def RIGHTJOIN(table1, table2):
|
|
1231
|
+
return SQLJoin(table1, table2, " RIGHT JOIN")
|
|
1232
|
+
|
|
1233
|
+
|
|
1234
|
+
def RIGHTOUTERJOIN(table1, table2):
|
|
1235
|
+
return SQLJoin(table1, table2, " RIGHT OUTER JOIN")
|
|
1236
|
+
|
|
1237
|
+
|
|
1238
|
+
def NATURALRIGHTJOIN(table1, table2):
|
|
1239
|
+
return SQLJoin(table1, table2, " NATURAL RIGHT JOIN")
|
|
1240
|
+
|
|
1241
|
+
|
|
1242
|
+
def NATURALRIGHTOUTERJOIN(table1, table2):
|
|
1243
|
+
return SQLJoin(table1, table2, " NATURAL RIGHT OUTER JOIN")
|
|
1244
|
+
|
|
1245
|
+
|
|
1246
|
+
def FULLJOIN(table1, table2):
|
|
1247
|
+
return SQLJoin(table1, table2, " FULL JOIN")
|
|
1248
|
+
|
|
1249
|
+
|
|
1250
|
+
def FULLOUTERJOIN(table1, table2):
|
|
1251
|
+
return SQLJoin(table1, table2, " FULL OUTER JOIN")
|
|
1252
|
+
|
|
1253
|
+
|
|
1254
|
+
def NATURALFULLJOIN(table1, table2):
|
|
1255
|
+
return SQLJoin(table1, table2, " NATURAL FULL JOIN")
|
|
1256
|
+
|
|
1257
|
+
|
|
1258
|
+
def NATURALFULLOUTERJOIN(table1, table2):
|
|
1259
|
+
return SQLJoin(table1, table2, " NATURAL FULL OUTER JOIN")
|
|
1260
|
+
|
|
1261
|
+
|
|
1262
|
+
class SQLJoinConditional(SQLJoin):
|
|
1263
|
+
"""Conditional JOIN"""
|
|
1264
|
+
def __init__(self, table1, table2, op,
|
|
1265
|
+
on_condition=None, using_columns=None):
|
|
1266
|
+
"""For condition you must give on_condition or using_columns
|
|
1267
|
+
but not both
|
|
1268
|
+
|
|
1269
|
+
on_condition can be a string or SQLExpression, for example
|
|
1270
|
+
Table1.q.col1 == Table2.q.col2
|
|
1271
|
+
using_columns can be a string or a list of columns, e.g.
|
|
1272
|
+
(Table1.q.col1, Table2.q.col2)
|
|
1273
|
+
"""
|
|
1274
|
+
if not on_condition and not using_columns:
|
|
1275
|
+
raise TypeError("You must give ON condition or USING columns")
|
|
1276
|
+
if on_condition and using_columns:
|
|
1277
|
+
raise TypeError(
|
|
1278
|
+
"You must give ON condition or USING columns but not both")
|
|
1279
|
+
SQLJoin.__init__(self, table1, table2, op)
|
|
1280
|
+
self.on_condition = on_condition
|
|
1281
|
+
self.using_columns = using_columns
|
|
1282
|
+
|
|
1283
|
+
def __sqlrepr__(self, db):
|
|
1284
|
+
if self.on_condition:
|
|
1285
|
+
on_condition = self.on_condition
|
|
1286
|
+
if hasattr(on_condition, "__sqlrepr__"):
|
|
1287
|
+
on_condition = sqlrepr(on_condition, db)
|
|
1288
|
+
join = "%s %s ON %s" % (self.op, sqlrepr(self.table2, db),
|
|
1289
|
+
on_condition)
|
|
1290
|
+
if self.table1:
|
|
1291
|
+
join = "%s %s" % (sqlrepr(self.table1, db), join)
|
|
1292
|
+
return join
|
|
1293
|
+
elif self.using_columns:
|
|
1294
|
+
using_columns = []
|
|
1295
|
+
for col in self.using_columns:
|
|
1296
|
+
if hasattr(col, "__sqlrepr__"):
|
|
1297
|
+
col = sqlrepr(col, db)
|
|
1298
|
+
using_columns.append(col)
|
|
1299
|
+
using_columns = ", ".join(using_columns)
|
|
1300
|
+
join = "%s %s USING (%s)" % (self.op, sqlrepr(self.table2, db),
|
|
1301
|
+
using_columns)
|
|
1302
|
+
if self.table1:
|
|
1303
|
+
join = "%s %s" % (sqlrepr(self.table1, db), join)
|
|
1304
|
+
return join
|
|
1305
|
+
else:
|
|
1306
|
+
RuntimeError, "Impossible error"
|
|
1307
|
+
|
|
1308
|
+
registerConverter(SQLJoinConditional, SQLExprConverter)
|
|
1309
|
+
|
|
1310
|
+
|
|
1311
|
+
def INNERJOINConditional(table1, table2,
|
|
1312
|
+
on_condition=None, using_columns=None):
|
|
1313
|
+
return SQLJoinConditional(table1, table2, "INNER JOIN",
|
|
1314
|
+
on_condition, using_columns)
|
|
1315
|
+
|
|
1316
|
+
|
|
1317
|
+
def LEFTJOINConditional(table1, table2, on_condition=None, using_columns=None):
|
|
1318
|
+
return SQLJoinConditional(table1, table2, "LEFT JOIN",
|
|
1319
|
+
on_condition, using_columns)
|
|
1320
|
+
|
|
1321
|
+
|
|
1322
|
+
def LEFTOUTERJOINConditional(table1, table2,
|
|
1323
|
+
on_condition=None, using_columns=None):
|
|
1324
|
+
return SQLJoinConditional(table1, table2, "LEFT OUTER JOIN",
|
|
1325
|
+
on_condition, using_columns)
|
|
1326
|
+
|
|
1327
|
+
|
|
1328
|
+
def RIGHTJOINConditional(table1, table2,
|
|
1329
|
+
on_condition=None, using_columns=None):
|
|
1330
|
+
return SQLJoinConditional(table1, table2, "RIGHT JOIN",
|
|
1331
|
+
on_condition, using_columns)
|
|
1332
|
+
|
|
1333
|
+
|
|
1334
|
+
def RIGHTOUTERJOINConditional(table1, table2,
|
|
1335
|
+
on_condition=None, using_columns=None):
|
|
1336
|
+
return SQLJoinConditional(table1, table2, "RIGHT OUTER JOIN",
|
|
1337
|
+
on_condition, using_columns)
|
|
1338
|
+
|
|
1339
|
+
|
|
1340
|
+
def FULLJOINConditional(table1, table2, on_condition=None, using_columns=None):
|
|
1341
|
+
return SQLJoinConditional(table1, table2, "FULL JOIN",
|
|
1342
|
+
on_condition, using_columns)
|
|
1343
|
+
|
|
1344
|
+
|
|
1345
|
+
def FULLOUTERJOINConditional(table1, table2,
|
|
1346
|
+
on_condition=None, using_columns=None):
|
|
1347
|
+
return SQLJoinConditional(table1, table2, "FULL OUTER JOIN",
|
|
1348
|
+
on_condition, using_columns)
|
|
1349
|
+
|
|
1350
|
+
|
|
1351
|
+
class SQLJoinOn(SQLJoinConditional):
|
|
1352
|
+
"""Conditional JOIN ON"""
|
|
1353
|
+
def __init__(self, table1, table2, op, on_condition):
|
|
1354
|
+
SQLJoinConditional.__init__(self, table1, table2, op, on_condition)
|
|
1355
|
+
|
|
1356
|
+
registerConverter(SQLJoinOn, SQLExprConverter)
|
|
1357
|
+
|
|
1358
|
+
|
|
1359
|
+
class SQLJoinUsing(SQLJoinConditional):
|
|
1360
|
+
"""Conditional JOIN USING"""
|
|
1361
|
+
def __init__(self, table1, table2, op, using_columns):
|
|
1362
|
+
SQLJoinConditional.__init__(self, table1, table2,
|
|
1363
|
+
op, None, using_columns)
|
|
1364
|
+
|
|
1365
|
+
registerConverter(SQLJoinUsing, SQLExprConverter)
|
|
1366
|
+
|
|
1367
|
+
|
|
1368
|
+
def INNERJOINOn(table1, table2, on_condition):
|
|
1369
|
+
return SQLJoinOn(table1, table2, "INNER JOIN", on_condition)
|
|
1370
|
+
|
|
1371
|
+
|
|
1372
|
+
def LEFTJOINOn(table1, table2, on_condition):
|
|
1373
|
+
return SQLJoinOn(table1, table2, "LEFT JOIN", on_condition)
|
|
1374
|
+
|
|
1375
|
+
|
|
1376
|
+
def LEFTOUTERJOINOn(table1, table2, on_condition):
|
|
1377
|
+
return SQLJoinOn(table1, table2, "LEFT OUTER JOIN", on_condition)
|
|
1378
|
+
|
|
1379
|
+
|
|
1380
|
+
def RIGHTJOINOn(table1, table2, on_condition):
|
|
1381
|
+
return SQLJoinOn(table1, table2, "RIGHT JOIN", on_condition)
|
|
1382
|
+
|
|
1383
|
+
|
|
1384
|
+
def RIGHTOUTERJOINOn(table1, table2, on_condition):
|
|
1385
|
+
return SQLJoinOn(table1, table2, "RIGHT OUTER JOIN", on_condition)
|
|
1386
|
+
|
|
1387
|
+
|
|
1388
|
+
def FULLJOINOn(table1, table2, on_condition):
|
|
1389
|
+
return SQLJoinOn(table1, table2, "FULL JOIN", on_condition)
|
|
1390
|
+
|
|
1391
|
+
|
|
1392
|
+
def FULLOUTERJOINOn(table1, table2, on_condition):
|
|
1393
|
+
return SQLJoinOn(table1, table2, "FULL OUTER JOIN", on_condition)
|
|
1394
|
+
|
|
1395
|
+
|
|
1396
|
+
def INNERJOINUsing(table1, table2, using_columns):
|
|
1397
|
+
return SQLJoinUsing(table1, table2, "INNER JOIN", using_columns)
|
|
1398
|
+
|
|
1399
|
+
|
|
1400
|
+
def LEFTJOINUsing(table1, table2, using_columns):
|
|
1401
|
+
return SQLJoinUsing(table1, table2, "LEFT JOIN", using_columns)
|
|
1402
|
+
|
|
1403
|
+
|
|
1404
|
+
def LEFTOUTERJOINUsing(table1, table2, using_columns):
|
|
1405
|
+
return SQLJoinUsing(table1, table2, "LEFT OUTER JOIN", using_columns)
|
|
1406
|
+
|
|
1407
|
+
|
|
1408
|
+
def RIGHTJOINUsing(table1, table2, using_columns):
|
|
1409
|
+
return SQLJoinUsing(table1, table2, "RIGHT JOIN", using_columns)
|
|
1410
|
+
|
|
1411
|
+
|
|
1412
|
+
def RIGHTOUTERJOINUsing(table1, table2, using_columns):
|
|
1413
|
+
return SQLJoinUsing(table1, table2, "RIGHT OUTER JOIN", using_columns)
|
|
1414
|
+
|
|
1415
|
+
|
|
1416
|
+
def FULLJOINUsing(table1, table2, using_columns):
|
|
1417
|
+
return SQLJoinUsing(table1, table2, "FULL JOIN", using_columns)
|
|
1418
|
+
|
|
1419
|
+
|
|
1420
|
+
def FULLOUTERJOINUsing(table1, table2, using_columns):
|
|
1421
|
+
return SQLJoinUsing(table1, table2, "FULL OUTER JOIN", using_columns)
|
|
1422
|
+
|
|
1423
|
+
|
|
1424
|
+
########################################
|
|
1425
|
+
# Subqueries (subselects)
|
|
1426
|
+
########################################
|
|
1427
|
+
|
|
1428
|
+
class OuterField(SQLObjectField):
|
|
1429
|
+
def tablesUsedImmediate(self):
|
|
1430
|
+
return []
|
|
1431
|
+
|
|
1432
|
+
|
|
1433
|
+
class OuterTable(SQLObjectTable):
|
|
1434
|
+
FieldClass = OuterField
|
|
1435
|
+
|
|
1436
|
+
|
|
1437
|
+
class Outer:
|
|
1438
|
+
def __init__(self, table):
|
|
1439
|
+
self.q = OuterTable(table)
|
|
1440
|
+
|
|
1441
|
+
|
|
1442
|
+
class LIKE(SQLExpression):
|
|
1443
|
+
op = "LIKE"
|
|
1444
|
+
|
|
1445
|
+
def __init__(self, expr, string, escape=None):
|
|
1446
|
+
self.expr = expr
|
|
1447
|
+
self.string = string
|
|
1448
|
+
self.escape = escape
|
|
1449
|
+
|
|
1450
|
+
def __sqlrepr__(self, db):
|
|
1451
|
+
escape = self.escape
|
|
1452
|
+
like = "%s %s (%s)" % (sqlrepr(self.expr, db),
|
|
1453
|
+
self.op, sqlrepr(self.string, db))
|
|
1454
|
+
if escape is None:
|
|
1455
|
+
return "(%s)" % like
|
|
1456
|
+
else:
|
|
1457
|
+
return "(%s ESCAPE %s)" % (like, sqlrepr(escape, db))
|
|
1458
|
+
|
|
1459
|
+
def components(self):
|
|
1460
|
+
return [self.expr, self.string]
|
|
1461
|
+
|
|
1462
|
+
def execute(self, executor):
|
|
1463
|
+
if not hasattr(self, '_regex'):
|
|
1464
|
+
# @@: Crude, not entirely accurate
|
|
1465
|
+
dest = self.string
|
|
1466
|
+
dest = dest.replace("%%", "\001")
|
|
1467
|
+
dest = dest.replace("*", "\002")
|
|
1468
|
+
dest = dest.replace("%", "*")
|
|
1469
|
+
dest = dest.replace("\001", "%")
|
|
1470
|
+
dest = dest.replace("\002", "[*]")
|
|
1471
|
+
self._regex = re.compile(fnmatch.translate(dest), re.I)
|
|
1472
|
+
return self._regex.search(execute(self.expr, executor))
|
|
1473
|
+
|
|
1474
|
+
|
|
1475
|
+
class RLIKE(LIKE):
|
|
1476
|
+
op = "RLIKE"
|
|
1477
|
+
|
|
1478
|
+
op_db = {
|
|
1479
|
+
'firebird': 'RLIKE',
|
|
1480
|
+
'maxdb': 'RLIKE',
|
|
1481
|
+
'mysql': 'RLIKE',
|
|
1482
|
+
'postgres': '~',
|
|
1483
|
+
'sqlite': 'REGEXP'
|
|
1484
|
+
}
|
|
1485
|
+
|
|
1486
|
+
def _get_op(self, db):
|
|
1487
|
+
return self.op_db.get(db, 'LIKE')
|
|
1488
|
+
|
|
1489
|
+
def __sqlrepr__(self, db):
|
|
1490
|
+
return "(%s %s (%s))" % (
|
|
1491
|
+
sqlrepr(self.expr, db), self._get_op(db), sqlrepr(self.string, db)
|
|
1492
|
+
)
|
|
1493
|
+
|
|
1494
|
+
def execute(self, executor):
|
|
1495
|
+
self.op = self._get_op(self.db)
|
|
1496
|
+
return LIKE.execute(self, executor)
|
|
1497
|
+
|
|
1498
|
+
|
|
1499
|
+
class INSubquery(SQLExpression):
|
|
1500
|
+
op = "IN"
|
|
1501
|
+
|
|
1502
|
+
def __init__(self, item, subquery):
|
|
1503
|
+
self.item = item
|
|
1504
|
+
self.subquery = subquery
|
|
1505
|
+
|
|
1506
|
+
def components(self):
|
|
1507
|
+
return [self.item]
|
|
1508
|
+
|
|
1509
|
+
def __sqlrepr__(self, db):
|
|
1510
|
+
return "%s %s (%s)" % (sqlrepr(self.item, db),
|
|
1511
|
+
self.op, sqlrepr(self.subquery, db))
|
|
1512
|
+
|
|
1513
|
+
|
|
1514
|
+
class NOTINSubquery(INSubquery):
|
|
1515
|
+
op = "NOT IN"
|
|
1516
|
+
|
|
1517
|
+
|
|
1518
|
+
class Subquery(SQLExpression):
|
|
1519
|
+
def __init__(self, op, subquery):
|
|
1520
|
+
self.op = op
|
|
1521
|
+
self.subquery = subquery
|
|
1522
|
+
|
|
1523
|
+
def __sqlrepr__(self, db):
|
|
1524
|
+
return "%s (%s)" % (self.op, sqlrepr(self.subquery, db))
|
|
1525
|
+
|
|
1526
|
+
|
|
1527
|
+
def EXISTS(subquery):
|
|
1528
|
+
return Subquery("EXISTS", subquery)
|
|
1529
|
+
|
|
1530
|
+
|
|
1531
|
+
def NOTEXISTS(subquery):
|
|
1532
|
+
return Subquery("NOT EXISTS", subquery)
|
|
1533
|
+
|
|
1534
|
+
|
|
1535
|
+
def SOME(subquery):
|
|
1536
|
+
return Subquery("SOME", subquery)
|
|
1537
|
+
|
|
1538
|
+
|
|
1539
|
+
def ANY(subquery):
|
|
1540
|
+
return Subquery("ANY", subquery)
|
|
1541
|
+
|
|
1542
|
+
|
|
1543
|
+
def ALL(subquery):
|
|
1544
|
+
return Subquery("ALL", subquery)
|
|
1545
|
+
|
|
1546
|
+
|
|
1547
|
+
####
|
|
1548
|
+
|
|
1549
|
+
|
|
1550
|
+
class ImportProxyField(SQLObjectField):
|
|
1551
|
+
def tablesUsedImmediate(self):
|
|
1552
|
+
return [str(self.tableName)]
|
|
1553
|
+
|
|
1554
|
+
|
|
1555
|
+
class ImportProxy(SQLExpression):
|
|
1556
|
+
'''
|
|
1557
|
+
Class to be used in column definitions that rely on other tables that might
|
|
1558
|
+
not yet be in a classregistry
|
|
1559
|
+
'''
|
|
1560
|
+
FieldClass = ImportProxyField
|
|
1561
|
+
|
|
1562
|
+
def __init__(self, clsName, registry=None):
|
|
1563
|
+
self.tableName = _DelayClass(self, clsName)
|
|
1564
|
+
self.sqlmeta = _Delay_proxy(table=_DelayClass(self, clsName))
|
|
1565
|
+
self.q = self
|
|
1566
|
+
self.soClass = None
|
|
1567
|
+
classregistry.registry(registry).addClassCallback(
|
|
1568
|
+
clsName, lambda foreign, me: setattr(me, 'soClass', foreign), self)
|
|
1569
|
+
|
|
1570
|
+
def __nonzero__(self):
|
|
1571
|
+
return True
|
|
1572
|
+
__bool__ = __nonzero__
|
|
1573
|
+
|
|
1574
|
+
def __getattr__(self, attr):
|
|
1575
|
+
if self.soClass is None:
|
|
1576
|
+
return _Delay(self, attr)
|
|
1577
|
+
return getattr(self.soClass.q, attr)
|
|
1578
|
+
|
|
1579
|
+
|
|
1580
|
+
class _Delay(SQLExpression):
|
|
1581
|
+
def __init__(self, proxy, attr):
|
|
1582
|
+
self.attr = attr
|
|
1583
|
+
self.proxy = proxy
|
|
1584
|
+
|
|
1585
|
+
def __sqlrepr__(self, db):
|
|
1586
|
+
if self.proxy.soClass is None:
|
|
1587
|
+
return '_DELAYED_' + self.attr
|
|
1588
|
+
val = self._resolve()
|
|
1589
|
+
if isinstance(val, SQLExpression):
|
|
1590
|
+
val = sqlrepr(val, db)
|
|
1591
|
+
return val
|
|
1592
|
+
|
|
1593
|
+
def tablesUsedImmediate(self):
|
|
1594
|
+
return getattr(self._resolve(), 'tablesUsedImmediate', lambda: [])()
|
|
1595
|
+
|
|
1596
|
+
def components(self):
|
|
1597
|
+
return getattr(self._resolve(), 'components', lambda: [])()
|
|
1598
|
+
|
|
1599
|
+
def _resolve(self):
|
|
1600
|
+
return getattr(self.proxy, self.attr)
|
|
1601
|
+
|
|
1602
|
+
# For AliasTable etc
|
|
1603
|
+
def fieldName(self):
|
|
1604
|
+
class _aliasFieldName(SQLExpression):
|
|
1605
|
+
def __init__(self, proxy):
|
|
1606
|
+
self.proxy = proxy
|
|
1607
|
+
|
|
1608
|
+
def __sqlrepr__(self, db):
|
|
1609
|
+
return self.proxy._resolve().fieldName
|
|
1610
|
+
return _aliasFieldName(self)
|
|
1611
|
+
fieldName = property(fieldName)
|
|
1612
|
+
|
|
1613
|
+
|
|
1614
|
+
class _DelayClass(_Delay):
|
|
1615
|
+
def _resolve(self):
|
|
1616
|
+
return self.proxy.soClass.sqlmeta.table
|
|
1617
|
+
|
|
1618
|
+
|
|
1619
|
+
class _Delay_proxy(object):
|
|
1620
|
+
def __init__(self, **kw):
|
|
1621
|
+
self.__dict__.update(kw)
|
|
1622
|
+
|
|
1623
|
+
######
|
|
1624
|
+
|
|
1625
|
+
|
|
1626
|
+
########################################
|
|
1627
|
+
# Global initializations
|
|
1628
|
+
########################################
|
|
1629
|
+
|
|
1630
|
+
table = TableSpace()
|
|
1631
|
+
const = ConstantSpace()
|
|
1632
|
+
func = const
|
|
1633
|
+
|
|
1634
|
+
########################################
|
|
1635
|
+
# Testing
|
|
1636
|
+
########################################
|
|
1637
|
+
|
|
1638
|
+
if __name__ == "__main__":
|
|
1639
|
+
tests = """
|
|
1640
|
+
>>> AND(table.address.name == "Ian Bicking", table.address.zip > 30000)
|
|
1641
|
+
>>> table.address.name
|
|
1642
|
+
>>> AND(LIKE(table.address.name, "this"), IN(table.address.zip, [100, 200, 300]))
|
|
1643
|
+
>>> Select([table.address.name, table.address.state], where=LIKE(table.address.name, "%ian%"))
|
|
1644
|
+
>>> Select([table.user.name], where=AND(table.user.state == table.states.abbrev))
|
|
1645
|
+
>>> Insert(table.address, [{"name": "BOB", "address": "3049 N. 18th St."}, {"name": "TIM", "address": "409 S. 10th St."}])
|
|
1646
|
+
>>> Insert(table.address, [("BOB", "3049 N. 18th St."), ("TIM", "409 S. 10th St.")], template=('name', 'address'))
|
|
1647
|
+
>>> Delete(table.address, where="BOB"==table.address.name)
|
|
1648
|
+
>>> Update(table.address, {"lastModified": const.NOW()})
|
|
1649
|
+
>>> Replace(table.address, [("BOB", "3049 N. 18th St."), ("TIM", "409 S. 10th St.")], template=('name', 'address'))
|
|
1650
|
+
""" # noqa: allow long (> 79) lines
|
|
1651
|
+
for expr in tests.split('\n'):
|
|
1652
|
+
if not expr.strip():
|
|
1653
|
+
continue
|
|
1654
|
+
if expr.startswith('>>> '):
|
|
1655
|
+
expr = expr[4:]
|