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.
Files changed (149) hide show
  1. sqlobject/__init__.py +15 -0
  2. sqlobject/__version__.py +8 -0
  3. sqlobject/boundattributes.py +124 -0
  4. sqlobject/cache.py +376 -0
  5. sqlobject/classregistry.py +138 -0
  6. sqlobject/col.py +2187 -0
  7. sqlobject/compat.py +59 -0
  8. sqlobject/conftest.py +64 -0
  9. sqlobject/constraints.py +77 -0
  10. sqlobject/converters.py +278 -0
  11. sqlobject/dbconnection.py +1137 -0
  12. sqlobject/dberrors.py +54 -0
  13. sqlobject/declarative.py +211 -0
  14. sqlobject/events.py +365 -0
  15. sqlobject/firebird/__init__.py +8 -0
  16. sqlobject/firebird/firebirdconnection.py +463 -0
  17. sqlobject/include/__init__.py +1 -0
  18. sqlobject/include/hashcol.py +109 -0
  19. sqlobject/include/tests/__init__.py +1 -0
  20. sqlobject/include/tests/test_hashcol.py +84 -0
  21. sqlobject/index.py +189 -0
  22. sqlobject/inheritance/__init__.py +551 -0
  23. sqlobject/inheritance/iteration.py +106 -0
  24. sqlobject/inheritance/tests/__init__.py +1 -0
  25. sqlobject/inheritance/tests/test_aggregates.py +23 -0
  26. sqlobject/inheritance/tests/test_asdict.py +48 -0
  27. sqlobject/inheritance/tests/test_deep_inheritance.py +114 -0
  28. sqlobject/inheritance/tests/test_destroy_cascade.py +23 -0
  29. sqlobject/inheritance/tests/test_foreignKey.py +94 -0
  30. sqlobject/inheritance/tests/test_indexes.py +42 -0
  31. sqlobject/inheritance/tests/test_inheritance.py +164 -0
  32. sqlobject/inheritance/tests/test_inheritance_tree.py +50 -0
  33. sqlobject/joins.py +656 -0
  34. sqlobject/main.py +1880 -0
  35. sqlobject/manager/__init__.py +1 -0
  36. sqlobject/manager/command.py +1309 -0
  37. sqlobject/maxdb/__init__.py +8 -0
  38. sqlobject/maxdb/maxdbconnection.py +320 -0
  39. sqlobject/maxdb/readme.txt +27 -0
  40. sqlobject/mssql/__init__.py +8 -0
  41. sqlobject/mssql/mssqlconnection.py +417 -0
  42. sqlobject/mysql/__init__.py +8 -0
  43. sqlobject/mysql/mysqlconnection.py +562 -0
  44. sqlobject/postgres/__init__.py +8 -0
  45. sqlobject/postgres/pgconnection.py +619 -0
  46. sqlobject/sqlbuilder.py +1655 -0
  47. sqlobject/sqlite/__init__.py +8 -0
  48. sqlobject/sqlite/sqliteconnection.py +418 -0
  49. sqlobject/sresults.py +377 -0
  50. sqlobject/styles.py +174 -0
  51. sqlobject/sybase/__init__.py +8 -0
  52. sqlobject/sybase/sybaseconnection.py +183 -0
  53. sqlobject/tests/__init__.py +1 -0
  54. sqlobject/tests/dbtest.py +363 -0
  55. sqlobject/tests/test_ForeignKey.py +133 -0
  56. sqlobject/tests/test_ForeignKey_cascade.py +150 -0
  57. sqlobject/tests/test_NoneValuedResultItem.py +33 -0
  58. sqlobject/tests/test_SQLMultipleJoin.py +65 -0
  59. sqlobject/tests/test_SQLRelatedJoin.py +114 -0
  60. sqlobject/tests/test_SingleJoin.py +30 -0
  61. sqlobject/tests/test_aggregates.py +75 -0
  62. sqlobject/tests/test_aliases.py +49 -0
  63. sqlobject/tests/test_asdict.py +18 -0
  64. sqlobject/tests/test_auto.py +218 -0
  65. sqlobject/tests/test_basic.py +359 -0
  66. sqlobject/tests/test_blob.py +34 -0
  67. sqlobject/tests/test_boundattributes.py +67 -0
  68. sqlobject/tests/test_cache.py +57 -0
  69. sqlobject/tests/test_class_hash.py +22 -0
  70. sqlobject/tests/test_columns_order.py +18 -0
  71. sqlobject/tests/test_combining_joins.py +47 -0
  72. sqlobject/tests/test_comparison.py +24 -0
  73. sqlobject/tests/test_compat.py +10 -0
  74. sqlobject/tests/test_complex_sorting.py +77 -0
  75. sqlobject/tests/test_constraints.py +43 -0
  76. sqlobject/tests/test_converters.py +276 -0
  77. sqlobject/tests/test_create_drop.py +29 -0
  78. sqlobject/tests/test_csvexport.py +101 -0
  79. sqlobject/tests/test_csvimport.py +21 -0
  80. sqlobject/tests/test_cyclic_reference.py +55 -0
  81. sqlobject/tests/test_datetime.py +171 -0
  82. sqlobject/tests/test_dbconnection.py +17 -0
  83. sqlobject/tests/test_decimal.py +63 -0
  84. sqlobject/tests/test_declarative.py +83 -0
  85. sqlobject/tests/test_default_style.py +65 -0
  86. sqlobject/tests/test_delete.py +74 -0
  87. sqlobject/tests/test_distinct.py +36 -0
  88. sqlobject/tests/test_empty.py +22 -0
  89. sqlobject/tests/test_enum.py +75 -0
  90. sqlobject/tests/test_events.py +142 -0
  91. sqlobject/tests/test_exceptions.py +40 -0
  92. sqlobject/tests/test_expire.py +29 -0
  93. sqlobject/tests/test_groupBy.py +45 -0
  94. sqlobject/tests/test_identity.py +29 -0
  95. sqlobject/tests/test_indexes.py +100 -0
  96. sqlobject/tests/test_inheritance.py +35 -0
  97. sqlobject/tests/test_joins.py +160 -0
  98. sqlobject/tests/test_joins_conditional.py +143 -0
  99. sqlobject/tests/test_jsonbcol.py +53 -0
  100. sqlobject/tests/test_jsoncol.py +56 -0
  101. sqlobject/tests/test_lazy.py +165 -0
  102. sqlobject/tests/test_md5.py +13 -0
  103. sqlobject/tests/test_mysql.py +50 -0
  104. sqlobject/tests/test_new_joins.py +165 -0
  105. sqlobject/tests/test_parse_uri.py +147 -0
  106. sqlobject/tests/test_paste.py +92 -0
  107. sqlobject/tests/test_perConnection.py +20 -0
  108. sqlobject/tests/test_pickle.py +42 -0
  109. sqlobject/tests/test_picklecol.py +44 -0
  110. sqlobject/tests/test_postgres.py +82 -0
  111. sqlobject/tests/test_reparent_sqlmeta.py +33 -0
  112. sqlobject/tests/test_schema.py +30 -0
  113. sqlobject/tests/test_select.py +218 -0
  114. sqlobject/tests/test_select_through.py +63 -0
  115. sqlobject/tests/test_setters.py +23 -0
  116. sqlobject/tests/test_slice.py +59 -0
  117. sqlobject/tests/test_sorting.py +80 -0
  118. sqlobject/tests/test_sqlbuilder.py +134 -0
  119. sqlobject/tests/test_sqlbuilder_dbspecific.py +66 -0
  120. sqlobject/tests/test_sqlbuilder_importproxy.py +55 -0
  121. sqlobject/tests/test_sqlbuilder_joins_instances.py +90 -0
  122. sqlobject/tests/test_sqlite.py +160 -0
  123. sqlobject/tests/test_sqlmeta_idName.py +52 -0
  124. sqlobject/tests/test_sqlobject_admin.py +38 -0
  125. sqlobject/tests/test_string_id.py +79 -0
  126. sqlobject/tests/test_style.py +36 -0
  127. sqlobject/tests/test_subqueries.py +142 -0
  128. sqlobject/tests/test_transactions.py +122 -0
  129. sqlobject/tests/test_unicode.py +84 -0
  130. sqlobject/tests/test_uuidcol.py +40 -0
  131. sqlobject/tests/test_validation.py +125 -0
  132. sqlobject/tests/test_views.py +159 -0
  133. sqlobject/util/__init__.py +1 -0
  134. sqlobject/util/csvexport.py +210 -0
  135. sqlobject/util/csvimport.py +366 -0
  136. sqlobject/util/moduleloader.py +35 -0
  137. sqlobject/util/threadinglocal.py +6 -0
  138. sqlobject/versioning/__init__.py +120 -0
  139. sqlobject/versioning/test/__init__.py +0 -0
  140. sqlobject/versioning/test/test_version.py +183 -0
  141. sqlobject/views.py +151 -0
  142. sqlobject/wsgi_middleware.py +109 -0
  143. sqlobject-3.13.1.data/scripts/sqlobject-admin +36 -0
  144. sqlobject-3.13.1.data/scripts/sqlobject-convertOldURI +19 -0
  145. sqlobject-3.13.1.dist-info/METADATA +177 -0
  146. sqlobject-3.13.1.dist-info/RECORD +149 -0
  147. sqlobject-3.13.1.dist-info/WHEEL +6 -0
  148. sqlobject-3.13.1.dist-info/entry_points.txt +4 -0
  149. sqlobject-3.13.1.dist-info/top_level.txt +1 -0
@@ -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:]