masonite-framework-orm 3.0.1__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 (116) hide show
  1. masonite_framework_orm-3.0.1.dist-info/METADATA +87 -0
  2. masonite_framework_orm-3.0.1.dist-info/RECORD +116 -0
  3. masonite_framework_orm-3.0.1.dist-info/WHEEL +5 -0
  4. masonite_framework_orm-3.0.1.dist-info/entry_points.txt +3 -0
  5. masonite_framework_orm-3.0.1.dist-info/licenses/LICENSE +21 -0
  6. masonite_framework_orm-3.0.1.dist-info/top_level.txt +1 -0
  7. masoniteorm/__init__.py +1 -0
  8. masoniteorm/collection/Collection.py +605 -0
  9. masoniteorm/collection/__init__.py +1 -0
  10. masoniteorm/commands/CanOverrideConfig.py +16 -0
  11. masoniteorm/commands/CanOverrideOptionsDefault.py +22 -0
  12. masoniteorm/commands/Command.py +6 -0
  13. masoniteorm/commands/Entry.py +43 -0
  14. masoniteorm/commands/MakeMigrationCommand.py +57 -0
  15. masoniteorm/commands/MakeModelCommand.py +78 -0
  16. masoniteorm/commands/MakeModelDocstringCommand.py +37 -0
  17. masoniteorm/commands/MakeObserverCommand.py +54 -0
  18. masoniteorm/commands/MakeSeedCommand.py +54 -0
  19. masoniteorm/commands/MigrateCommand.py +46 -0
  20. masoniteorm/commands/MigrateFreshCommand.py +41 -0
  21. masoniteorm/commands/MigrateRefreshCommand.py +41 -0
  22. masoniteorm/commands/MigrateResetCommand.py +25 -0
  23. masoniteorm/commands/MigrateRollbackCommand.py +26 -0
  24. masoniteorm/commands/MigrateStatusCommand.py +51 -0
  25. masoniteorm/commands/SeedRunCommand.py +35 -0
  26. masoniteorm/commands/ShellCommand.py +205 -0
  27. masoniteorm/commands/__init__.py +18 -0
  28. masoniteorm/commands/stubs/create_migration.stub +20 -0
  29. masoniteorm/commands/stubs/create_seed.stub +9 -0
  30. masoniteorm/commands/stubs/model.stub +9 -0
  31. masoniteorm/commands/stubs/observer.stub +101 -0
  32. masoniteorm/commands/stubs/table_migration.stub +19 -0
  33. masoniteorm/config.py +123 -0
  34. masoniteorm/connections/BaseConnection.py +101 -0
  35. masoniteorm/connections/ConnectionFactory.py +59 -0
  36. masoniteorm/connections/ConnectionResolver.py +132 -0
  37. masoniteorm/connections/MSSQLConnection.py +176 -0
  38. masoniteorm/connections/MySQLConnection.py +232 -0
  39. masoniteorm/connections/PostgresConnection.py +225 -0
  40. masoniteorm/connections/SQLiteConnection.py +179 -0
  41. masoniteorm/connections/__init__.py +6 -0
  42. masoniteorm/exceptions.py +38 -0
  43. masoniteorm/expressions/__init__.py +1 -0
  44. masoniteorm/expressions/expressions.py +288 -0
  45. masoniteorm/factories/Factory.py +112 -0
  46. masoniteorm/factories/__init__.py +1 -0
  47. masoniteorm/helpers/__init__.py +0 -0
  48. masoniteorm/helpers/misc.py +22 -0
  49. masoniteorm/migrations/Migration.py +330 -0
  50. masoniteorm/migrations/__init__.py +1 -0
  51. masoniteorm/models/MigrationModel.py +9 -0
  52. masoniteorm/models/Model.py +1209 -0
  53. masoniteorm/models/Model.pyi +1366 -0
  54. masoniteorm/models/Pivot.py +5 -0
  55. masoniteorm/models/__init__.py +1 -0
  56. masoniteorm/observers/ObservesEvents.py +27 -0
  57. masoniteorm/observers/__init__.py +1 -0
  58. masoniteorm/pagination/BasePaginator.py +10 -0
  59. masoniteorm/pagination/LengthAwarePaginator.py +34 -0
  60. masoniteorm/pagination/SimplePaginator.py +28 -0
  61. masoniteorm/pagination/__init__.py +2 -0
  62. masoniteorm/providers/ORMProvider.py +39 -0
  63. masoniteorm/providers/__init__.py +1 -0
  64. masoniteorm/query/EagerRelation.py +42 -0
  65. masoniteorm/query/QueryBuilder.py +2486 -0
  66. masoniteorm/query/__init__.py +1 -0
  67. masoniteorm/query/grammars/BaseGrammar.py +1027 -0
  68. masoniteorm/query/grammars/MSSQLGrammar.py +194 -0
  69. masoniteorm/query/grammars/MySQLGrammar.py +238 -0
  70. masoniteorm/query/grammars/PostgresGrammar.py +213 -0
  71. masoniteorm/query/grammars/SQLiteGrammar.py +228 -0
  72. masoniteorm/query/grammars/__init__.py +4 -0
  73. masoniteorm/query/processors/MSSQLPostProcessor.py +58 -0
  74. masoniteorm/query/processors/MySQLPostProcessor.py +48 -0
  75. masoniteorm/query/processors/PostgresPostProcessor.py +49 -0
  76. masoniteorm/query/processors/SQLitePostProcessor.py +49 -0
  77. masoniteorm/query/processors/__init__.py +4 -0
  78. masoniteorm/relationships/BaseRelationship.py +161 -0
  79. masoniteorm/relationships/BelongsTo.py +124 -0
  80. masoniteorm/relationships/BelongsToMany.py +604 -0
  81. masoniteorm/relationships/HasMany.py +66 -0
  82. masoniteorm/relationships/HasManyThrough.py +269 -0
  83. masoniteorm/relationships/HasOne.py +111 -0
  84. masoniteorm/relationships/HasOneThrough.py +275 -0
  85. masoniteorm/relationships/MorphMany.py +152 -0
  86. masoniteorm/relationships/MorphOne.py +156 -0
  87. masoniteorm/relationships/MorphTo.py +111 -0
  88. masoniteorm/relationships/MorphToMany.py +108 -0
  89. masoniteorm/relationships/__init__.py +10 -0
  90. masoniteorm/schema/Blueprint.py +1161 -0
  91. masoniteorm/schema/Column.py +144 -0
  92. masoniteorm/schema/ColumnDiff.py +0 -0
  93. masoniteorm/schema/Constraint.py +5 -0
  94. masoniteorm/schema/ForeignKeyConstraint.py +28 -0
  95. masoniteorm/schema/Index.py +5 -0
  96. masoniteorm/schema/Schema.py +359 -0
  97. masoniteorm/schema/Table.py +94 -0
  98. masoniteorm/schema/TableDiff.py +86 -0
  99. masoniteorm/schema/__init__.py +3 -0
  100. masoniteorm/schema/platforms/MSSQLPlatform.py +367 -0
  101. masoniteorm/schema/platforms/MySQLPlatform.py +513 -0
  102. masoniteorm/schema/platforms/Platform.py +97 -0
  103. masoniteorm/schema/platforms/PostgresPlatform.py +551 -0
  104. masoniteorm/schema/platforms/SQLitePlatform.py +481 -0
  105. masoniteorm/schema/platforms/__init__.py +4 -0
  106. masoniteorm/scopes/BaseScope.py +6 -0
  107. masoniteorm/scopes/SoftDeleteScope.py +56 -0
  108. masoniteorm/scopes/SoftDeletesMixin.py +13 -0
  109. masoniteorm/scopes/TimeStampsMixin.py +12 -0
  110. masoniteorm/scopes/TimeStampsScope.py +47 -0
  111. masoniteorm/scopes/UUIDPrimaryKeyMixin.py +8 -0
  112. masoniteorm/scopes/UUIDPrimaryKeyScope.py +51 -0
  113. masoniteorm/scopes/__init__.py +8 -0
  114. masoniteorm/scopes/scope.py +15 -0
  115. masoniteorm/seeds/Seeder.py +42 -0
  116. masoniteorm/seeds/__init__.py +1 -0
@@ -0,0 +1,1027 @@
1
+ import re
2
+
3
+ from ...expressions.expressions import (
4
+ JoinClause,
5
+ OnClause,
6
+ SelectExpression,
7
+ SubGroupExpression,
8
+ SubSelectExpression,
9
+ )
10
+
11
+
12
+ class BaseGrammar:
13
+ """The keys in this dictionary is how the ORM will reference these aggregates
14
+
15
+ The values on the right are the matching functions for the grammar
16
+
17
+ Returns:
18
+ [type] -- [description]
19
+ """
20
+
21
+ table = "users"
22
+
23
+ def __init__(
24
+ self,
25
+ columns=(),
26
+ table="users",
27
+ database=None,
28
+ wheres=(),
29
+ limit=False,
30
+ offset=False,
31
+ updates=None,
32
+ aggregates=(),
33
+ order_by=(),
34
+ distinct=False,
35
+ group_by=(),
36
+ joins=(),
37
+ lock=False,
38
+ having=(),
39
+ connection_details=None,
40
+ ):
41
+ self._columns = columns
42
+ self.table = table
43
+ self.database = database
44
+ self._wheres = wheres
45
+ self._limit = limit
46
+ self._offset = offset
47
+ self._updates = updates or {}
48
+ self._aggregates = aggregates
49
+ self._order_by = order_by
50
+ self._group_by = group_by
51
+ self._distinct = distinct
52
+ self._joins = joins
53
+ self._having = having
54
+ self.lock = lock
55
+ self._connection_details = connection_details or {}
56
+ self._column = None
57
+
58
+ self._bindings = []
59
+
60
+ self._sql = ""
61
+
62
+ self._sql_qmark = ""
63
+ self._action = "select"
64
+ self.queries = []
65
+
66
+ def compile(self, action, qmark=False):
67
+ self._action = action
68
+ return getattr(self, "_compile_" + action)(qmark=qmark)
69
+
70
+ def _compile_select(self, qmark=False):
71
+ """Compile a select query statement.
72
+
73
+ Keyword Arguments:
74
+ qmark {bool} -- [description] (default: {False})
75
+
76
+ Returns:
77
+ [type] -- [description]
78
+ """
79
+ if not self.table:
80
+ self._sql = (
81
+ self.select_no_table()
82
+ .format(
83
+ columns=self.process_columns(separator=", ", qmark=qmark),
84
+ table=self.process_table(self.table),
85
+ joins=self.process_joins(qmark=qmark),
86
+ wheres=self.process_wheres(qmark=qmark),
87
+ limit=self.process_limit(),
88
+ offset=self.process_offset(),
89
+ aggregates=self.process_aggregates(),
90
+ order_by=self.process_order_by(),
91
+ group_by=self.process_group_by(),
92
+ having=self.process_having(),
93
+ lock=self.process_locks(),
94
+ )
95
+ .strip()
96
+ )
97
+ else:
98
+ self._sql = (
99
+ self.select_format()
100
+ .format(
101
+ columns=self.process_columns(separator=", ", qmark=qmark),
102
+ keyword="DISTINCT" if self._distinct else "",
103
+ table=self.process_table(self.table),
104
+ joins=self.process_joins(qmark=qmark),
105
+ wheres=self.process_wheres(qmark=qmark),
106
+ limit=self.process_limit(),
107
+ offset=self.process_offset(),
108
+ aggregates=self.process_aggregates(),
109
+ order_by=self.process_order_by(),
110
+ group_by=self.process_group_by(),
111
+ having=self.process_having(),
112
+ lock=self.process_locks(),
113
+ )
114
+ .strip()
115
+ )
116
+
117
+ return self
118
+
119
+ def _compile_update(self, qmark=False):
120
+ """Compiles an update query statement.
121
+
122
+ Keyword Arguments:
123
+ qmark {bool} -- Whether the query should use qmark. (default: {False})
124
+
125
+ Returns:
126
+ self
127
+ """
128
+ self._sql = self.update_format().format(
129
+ key_equals=self._compile_key_value_equals(qmark=qmark),
130
+ table=self.process_table(self.table),
131
+ wheres=self.process_wheres(qmark=qmark),
132
+ )
133
+
134
+ return self
135
+
136
+ def _compile_insert(self, qmark=False):
137
+ """Compiles an insert expression.
138
+
139
+ Returns:
140
+ self
141
+ """
142
+ self._sql = self.insert_format().format(
143
+ key_equals=self._compile_key_value_equals(qmark=qmark),
144
+ table=self.process_table(self.table),
145
+ columns=self.process_columns(
146
+ separator=", ", action="insert", qmark=qmark
147
+ ),
148
+ values=self.process_values(separator=", ", qmark=qmark),
149
+ )
150
+
151
+ return self
152
+
153
+ def _compile_bulk_create(self, qmark=False):
154
+ """Compiles an insert expression.
155
+
156
+ Returns:
157
+ self
158
+ """
159
+ all_values = [list(x.values()) for x in self._columns]
160
+
161
+ self._sql = self.bulk_insert_format().format(
162
+ key_equals=self._compile_key_value_equals(qmark=qmark),
163
+ table=self.process_table(self.table),
164
+ columns=self.columnize_bulk_columns(list(self._columns[0].keys())),
165
+ values=self.columnize_bulk_values(all_values, qmark=qmark),
166
+ )
167
+ return self
168
+
169
+ def columnize_bulk_columns(self, columns=[]):
170
+ return ", ".join(
171
+ self.column_string().format(column=x, separator="")
172
+ for x in columns
173
+ ).rstrip(",")
174
+
175
+ def columnize_bulk_values(self, columns=[], qmark=False):
176
+ sql = ""
177
+ for x in columns:
178
+ inner = ""
179
+ if isinstance(x, list):
180
+ for y in x:
181
+ if qmark:
182
+ self.add_binding(y)
183
+ inner += (
184
+ "?, "
185
+ if qmark
186
+ else self.value_string().format(
187
+ value=y, separator=", "
188
+ )
189
+ )
190
+
191
+ inner = inner.rstrip(", ")
192
+ sql += self.process_value_string().format(
193
+ value=inner, separator=", "
194
+ )
195
+ else:
196
+ if qmark:
197
+ self.add_binding(x)
198
+ sql += (
199
+ "?, "
200
+ if qmark
201
+ else self.process_value_string().format(
202
+ value="?" if qmark else x, separator=", "
203
+ )
204
+ )
205
+
206
+ return sql.rstrip(", ")
207
+
208
+ def process_value_string(self):
209
+ return "({value}){separator}"
210
+
211
+ def _compile_delete(self, qmark=False):
212
+ """Compiles a delete expression.
213
+
214
+ Returns:
215
+ self
216
+ """
217
+ self._sql = self.delete_format().format(
218
+ key_equals=self._compile_key_value_equals(qmark=qmark),
219
+ table=self.process_table(self.table),
220
+ wheres=self.process_wheres(qmark=qmark),
221
+ )
222
+
223
+ return self
224
+
225
+ # TODO: Columnize?
226
+ def _get_multiple_columns(self, columns):
227
+ """Compiles a string or a list of strings into the grammars column syntax.
228
+
229
+ Arguments:
230
+ columns {string|list} -- A column or list of columns
231
+
232
+ Returns:
233
+ self
234
+ """
235
+ if isinstance(columns, list):
236
+ column_string = ""
237
+ for col in columns:
238
+ column_string += self.process_column(col) + ", "
239
+ return column_string.rstrip(", ")
240
+
241
+ return self.process_column(columns)
242
+
243
+ def process_joins(self, qmark=False):
244
+ """Compiles a join expression.
245
+
246
+ Returns:
247
+ self
248
+ """
249
+ sql = ""
250
+ for join in self._joins:
251
+ if isinstance(join, JoinClause):
252
+ on_string = ""
253
+ for clause_idx, clause in enumerate(join.get_on_clauses()):
254
+ keyword = clause.operator.upper() if clause_idx else "ON"
255
+
256
+ if isinstance(clause, OnClause):
257
+ on_string += f"{keyword} {self._table_column_string(clause.column1)} {clause.equality} {self._table_column_string(clause.column2)} "
258
+ else:
259
+ if clause.value_type == "NULL":
260
+ sql_string = f"{self.where_null_string()} "
261
+ on_string += sql_string.format(
262
+ keyword=keyword,
263
+ column=self.process_column(clause.column),
264
+ )
265
+ elif clause.value_type == "NOT NULL":
266
+ sql_string = f"{self.where_not_null_string()} "
267
+ on_string += sql_string.format(
268
+ keyword=keyword,
269
+ column=self.process_column(clause.column),
270
+ )
271
+ else:
272
+ if qmark:
273
+ value = "?"
274
+ self.add_binding(clause.value)
275
+ else:
276
+ value = self._compile_value(clause.value)
277
+ on_string += f"{keyword} {self._table_column_string(clause.column)} {clause.equality} {value} "
278
+
279
+ sql += self.join_string().format(
280
+ foreign_table=self.process_table(join.table),
281
+ alias=(
282
+ f" AS {self.process_table(join.alias)}"
283
+ if join.alias
284
+ else ""
285
+ ),
286
+ on=on_string,
287
+ keyword=self.join_keywords[join.clause],
288
+ )
289
+ sql += " "
290
+
291
+ return sql
292
+
293
+ # TODO: Clean
294
+ def _compile_key_value_equals(self, qmark=False):
295
+ """Compiles key value pairs.
296
+
297
+ Keyword Arguments:
298
+ qmark {bool} -- Whether the query should use qmark. (default: {False})
299
+
300
+ Returns:
301
+ self
302
+ """
303
+ sql = ""
304
+ for update in self._updates:
305
+ if update.update_type == "increment":
306
+ sql_string = self.increment_string()
307
+ elif update.update_type == "decrement":
308
+ sql_string = self.decrement_string()
309
+ else:
310
+ sql_string = self.key_value_string()
311
+
312
+ column = update.column
313
+ value = update.value
314
+ if isinstance(column, dict):
315
+ for key, value in column.items():
316
+ if hasattr(value, "expression"):
317
+ sql += self.column_value_string().format(
318
+ column=self._table_column_string(key),
319
+ value=value.expression,
320
+ separator=", ",
321
+ )
322
+ else:
323
+ sql += sql_string.format(
324
+ column=self._table_column_string(key),
325
+ value=(
326
+ self.value_string().format(
327
+ value=value, separator=""
328
+ )
329
+ if not qmark
330
+ else "?"
331
+ ),
332
+ separator=", ",
333
+ )
334
+
335
+ if qmark:
336
+ self._bindings += (value,)
337
+ else:
338
+ sql += sql_string.format(
339
+ column=self._table_column_string(column),
340
+ value=(
341
+ self.value_string().format(value=value, separator=", ")
342
+ if not qmark
343
+ else "?"
344
+ ),
345
+ separator=", ",
346
+ )
347
+ if qmark:
348
+ self._bindings += (value,)
349
+
350
+ sql = sql.rstrip(", ")
351
+ return sql
352
+
353
+ def process_aggregates(self):
354
+ """Compiles aggregates to be used in a query expression.
355
+
356
+ Returns:
357
+ self
358
+ """
359
+ sql = ""
360
+ for aggregates in self._aggregates:
361
+ aggregate = aggregates.aggregate
362
+ column = aggregates.column
363
+ aggregate_function = self.aggregate_options.get(aggregate, "")
364
+ if not aggregates.alias and column == "*":
365
+ aggregate_string = self.aggregate_string_without_alias()
366
+ else:
367
+ aggregate_string = self.aggregate_string_with_alias()
368
+
369
+ sql += (
370
+ aggregate_string.format(
371
+ aggregate_function=aggregate_function,
372
+ column=(
373
+ "*"
374
+ if column == "*"
375
+ else self._table_column_string(column)
376
+ ),
377
+ alias=self.process_alias(aggregates.alias or column),
378
+ )
379
+ + ", "
380
+ )
381
+
382
+ return sql
383
+
384
+ def process_order_by(self):
385
+ """Compiles an order by for a query expression.
386
+
387
+ Returns:
388
+ self
389
+ """
390
+ sql = ""
391
+ if self._order_by:
392
+ order_crit = ""
393
+ for order_bys in self._order_by:
394
+ if order_bys.raw:
395
+ order_crit += order_bys.column
396
+ if not isinstance(order_bys.bindings, (list, tuple)):
397
+ raise ValueError(
398
+ f"Bindings must be tuple or list. Received {type(order_bys.bindings)}"
399
+ )
400
+
401
+ if order_bys.bindings:
402
+ self.add_binding(*order_bys.bindings)
403
+
404
+ continue
405
+
406
+ if len(order_crit):
407
+ order_crit += ", "
408
+ column = order_bys.column
409
+ direction = order_bys.direction
410
+ if "." in column:
411
+ column_string = self._table_column_string(column)
412
+ else:
413
+ column_string = self.column_string().format(
414
+ column=column, separator=""
415
+ )
416
+ order_crit += self.order_by_format().format(
417
+ column=column_string, direction=direction.upper()
418
+ )
419
+
420
+ sql += self.order_by_string().format(order_columns=order_crit)
421
+ return sql
422
+
423
+ def process_group_by(self):
424
+ """Compiles a group by for a query expression.
425
+
426
+ Returns:
427
+ self
428
+ """
429
+ sql = ""
430
+ columns = []
431
+ for group_by in self._group_by:
432
+ if group_by.raw:
433
+ if group_by.bindings:
434
+ self.add_binding(*group_by.bindings)
435
+
436
+ sql += "GROUP BY " + group_by.column
437
+ return sql
438
+
439
+ else:
440
+ columns.append(self._table_column_string(group_by.column))
441
+
442
+ if columns:
443
+ sql += " GROUP BY {column}".format(column=", ".join(columns))
444
+
445
+ return sql
446
+
447
+ def process_alias(self, column):
448
+ """Compiles an alias for a column.
449
+
450
+ Arguments:
451
+ column {string} -- The name of the column.
452
+
453
+ Returns:
454
+ self
455
+ """
456
+ return column
457
+
458
+ def process_table(self, table):
459
+ """Compiles a given table name.
460
+
461
+ Arguments:
462
+ table {string} -- The table name to compile.
463
+
464
+ Returns:
465
+ self
466
+ """
467
+ if not table:
468
+ return ""
469
+
470
+ if isinstance(table, str):
471
+ return ".".join(
472
+ self.table_string().format(
473
+ table=t,
474
+ database=self._connection_details.get("database", ""),
475
+ prefix=self._connection_details.get("prefix", ""),
476
+ )
477
+ for t in table.split(".")
478
+ )
479
+
480
+ if table.raw:
481
+ return table.name
482
+
483
+ return ".".join(
484
+ self.table_string().format(
485
+ table=t,
486
+ database=self._connection_details.get("database", ""),
487
+ prefix=self._connection_details.get("prefix", ""),
488
+ )
489
+ for t in table.name.split(".")
490
+ )
491
+
492
+ def process_limit(self):
493
+ """Compiles the limit expression.
494
+
495
+ Returns:
496
+ self
497
+ """
498
+ if not self._limit:
499
+ return ""
500
+
501
+ return self.limit_string(offset=self._offset).format(limit=self._limit)
502
+
503
+ def process_offset(self):
504
+ """Compiles the offset expression.
505
+
506
+ Returns:
507
+ self
508
+ """
509
+ if not self._offset:
510
+ return ""
511
+
512
+ return self.offset_string().format(
513
+ offset=self._offset, limit=self._limit or 1
514
+ )
515
+
516
+ def process_locks(self):
517
+ return self.locks.get(self.lock, "")
518
+
519
+ def process_having(self, qmark=False):
520
+ """Compiles having expression.
521
+
522
+ Keyword Arguments:
523
+ qmark {bool} -- Whether or not to use Qmark (default: {False})
524
+
525
+ Returns:
526
+ self
527
+ """
528
+ sql = ""
529
+ for having in self._having:
530
+ column = having.column
531
+ equality = having.equality
532
+ value = having.value
533
+ raw = having.raw
534
+
535
+ if not equality and not value:
536
+ sql_string = self.having_string()
537
+ else:
538
+ sql_string = self.having_equality_string()
539
+
540
+ sql += sql_string.format(
541
+ column=(
542
+ self._table_column_string(column)
543
+ if raw is False
544
+ else column
545
+ ),
546
+ equality=equality,
547
+ value=self._compile_value(value),
548
+ )
549
+
550
+ return sql
551
+
552
+ def process_wheres(self, qmark=False, strip_first_where=False):
553
+ """Compiles the where expression.
554
+
555
+ Keyword Arguments:
556
+ qmark {bool} -- Whether or not to use Qmark. (default: {False})
557
+ strip_first_where {bool} -- Whether or not to strip out the first where keyword.
558
+ This is useful when using subselects (default: {False})
559
+
560
+ Returns:
561
+ self
562
+ """
563
+ sql = ""
564
+ loop_count = 0
565
+ for where in self._wheres:
566
+ column = where.column
567
+ equality = where.equality
568
+ value = where.value
569
+ value_type = where.value_type
570
+
571
+ """Need to get a specific keyword here. This keyword either needs to be
572
+ something like WHERE, AND, OR.
573
+
574
+ Depending on the loop depends on the placement of the AND
575
+ """
576
+ if loop_count == 0:
577
+ if strip_first_where:
578
+ keyword = ""
579
+ else:
580
+ keyword = " " + self.first_where_string()
581
+ elif hasattr(where, "keyword") and where.keyword == "or":
582
+ keyword = " " + self.or_where_string()
583
+ else:
584
+ keyword = " " + self.additional_where_string()
585
+
586
+ if where.raw:
587
+ """If we have a raw query we just want to use the query supplied
588
+ and don't need to compile anything.
589
+ """
590
+ sql += self.raw_query_string().format(
591
+ keyword=keyword, query=where.column
592
+ )
593
+
594
+ if not isinstance(where.bindings, (list, tuple)):
595
+ raise ValueError(
596
+ f"Bindings must be tuple or list. Received {type(where.bindings)}"
597
+ )
598
+
599
+ if where.bindings:
600
+ self.add_binding(*where.bindings)
601
+
602
+ loop_count += 1
603
+
604
+ continue
605
+
606
+ """The column is an easy compile
607
+ """
608
+ column = self._table_column_string(column)
609
+
610
+ """Need to find which type of where string it is.
611
+ If it is a WHERE NULL, WHERE EXISTS, WHERE `col` = 'val' etc
612
+ """
613
+ equality = equality.upper()
614
+
615
+ if equality == "BETWEEN":
616
+ low = where.low
617
+ high = where.high
618
+ if qmark:
619
+ self.add_binding(low)
620
+ self.add_binding(high)
621
+
622
+ sql_string = self.between_string().format(
623
+ low=self._compile_value(low) if not qmark else "?",
624
+ high=self._compile_value(high) if not qmark else "?",
625
+ column=self._table_column_string(where.column),
626
+ keyword=keyword,
627
+ )
628
+ elif equality == "NOT BETWEEN":
629
+ low = where.low
630
+ high = where.high
631
+ if qmark:
632
+ self.add_binding(low)
633
+ self.add_binding(high)
634
+
635
+ sql_string = self.not_between_string().format(
636
+ low=self._compile_value(low) if not qmark else "?",
637
+ high=self._compile_value(high) if not qmark else "?",
638
+ column=self._table_column_string(where.column),
639
+ keyword=keyword,
640
+ )
641
+ elif value_type == "value_equals":
642
+ sql_string = self.value_equal_string().format(
643
+ value1=where.column, value2=where.value, keyword=keyword
644
+ )
645
+ elif value_type == "NULL":
646
+ sql_string = self.where_null_string()
647
+ elif value_type == "DATE":
648
+ sql_string = self.where_date_string()
649
+ elif value_type == "NOT NULL":
650
+ sql_string = self.where_not_null_string()
651
+ elif equality == "EXISTS":
652
+ sql_string = self.where_exists_string()
653
+ elif equality == "NOT EXISTS":
654
+ sql_string = self.where_not_exists_string()
655
+ elif equality == "LIKE":
656
+ sql_string = self.where_like_string()
657
+ elif equality == "REGEXP":
658
+ sql_string = self.where_regexp_string()
659
+ elif equality == "NOT REGEXP":
660
+ sql_string = self.where_not_regexp_string()
661
+ elif equality == "NOT LIKE":
662
+ sql_string = self.where_not_like_string()
663
+ else:
664
+ sql_string = self.where_string()
665
+
666
+ """If the value should actually be a sub query then we need to wrap it in a query here
667
+ """
668
+ if isinstance(value, SubGroupExpression):
669
+ grammar = value.builder.get_grammar()
670
+ query_value = (
671
+ self.subquery_string()
672
+ .format(
673
+ query=grammar.process_wheres(
674
+ qmark=qmark, strip_first_where=True
675
+ )
676
+ )
677
+ .replace("( ", "(")
678
+ )
679
+ if grammar._bindings:
680
+ self.add_binding(*grammar._bindings)
681
+ sql_string = self.where_group_string()
682
+ elif isinstance(value, SubSelectExpression):
683
+ if qmark:
684
+ query_from_builder = value.builder.to_qmark()
685
+ if value.builder._bindings:
686
+ self.add_binding(*value.builder._bindings)
687
+ else:
688
+ query_from_builder = value.builder.to_sql()
689
+ query_value = self.subquery_string().format(
690
+ query=query_from_builder
691
+ )
692
+ elif isinstance(value, list):
693
+ query_value = "("
694
+ for val in value:
695
+ if qmark:
696
+ query_value += "?, "
697
+ self.add_binding(val)
698
+ else:
699
+ query_value += self.value_string().format(
700
+ value=val, separator=","
701
+ )
702
+ query_value = query_value.rstrip(",").rstrip(", ") + ")"
703
+ elif value is True and value_type != "NOT NULL":
704
+ sql_string = self.get_true_column_string()
705
+ query_value = 1
706
+ elif value is False and value_type != "NOT NULL":
707
+ sql_string = self.get_false_column_string()
708
+ query_value = 0
709
+ elif qmark and value_type != "column":
710
+ query_value = "?"
711
+ if (
712
+ value is not True
713
+ and value_type != "value_equals"
714
+ and value_type != "NULL"
715
+ and value_type != "BETWEEN"
716
+ ):
717
+ self.add_binding(value)
718
+ elif value_type == "value":
719
+ if qmark:
720
+ query_value = "?"
721
+ else:
722
+ query_value = self.value_string().format(
723
+ value=value, separator=""
724
+ )
725
+
726
+ self.add_binding(value)
727
+ elif value_type == "column":
728
+ query_value = self._table_column_string(
729
+ column=value, separator=""
730
+ )
731
+ elif value_type == "DATE":
732
+ query_value = self.value_string().format(
733
+ value=value, separator=""
734
+ )
735
+ elif value_type == "having":
736
+ query_value = self._table_column_string(
737
+ column=value, separator=""
738
+ )
739
+ else:
740
+ query_value = ""
741
+
742
+ sql += sql_string.format(
743
+ keyword=keyword,
744
+ column=column,
745
+ equality=equality,
746
+ value=query_value,
747
+ )
748
+
749
+ loop_count += 1
750
+
751
+ return sql
752
+
753
+ def get_true_column_string(self):
754
+ return "{keyword} {column} = '1'"
755
+
756
+ def get_false_column_string(self):
757
+ return "{keyword} {column} = '0'"
758
+
759
+ def add_binding(self, *bindings):
760
+ """Adds one or more bindings to the bindings tuple.
761
+
762
+ Arguments:
763
+ binding {string} -- A value to bind.
764
+ """
765
+ self._bindings += bindings
766
+
767
+ def column_exists(self, column):
768
+ """Check if a column exists
769
+
770
+ Arguments:
771
+ column {string} -- The name of the column to check for existence.
772
+
773
+ Returns:
774
+ self
775
+ """
776
+ self._column = column
777
+ self._sql = self.process_exists()
778
+ return self
779
+
780
+ def table_exists(self):
781
+ """Checks if a table exists.
782
+
783
+ Returns:
784
+ self
785
+ """
786
+ self._sql = self.table_exists_string().format(
787
+ table=self.process_table(self.table),
788
+ database=self.database,
789
+ clean_table=self.table,
790
+ )
791
+ return self
792
+
793
+ def wrap_table(self, table_name):
794
+ return self.table_string().format(table=table_name)
795
+
796
+ def process_exists(self):
797
+ """Specifies the column exists expression.
798
+
799
+ Returns:
800
+ self
801
+ """
802
+ return self.column_exists_string().format(
803
+ table=self.process_table(self.table),
804
+ clean_table=self.table,
805
+ value=self._compile_value(self._column),
806
+ )
807
+
808
+ def to_sql(self):
809
+ """Cleans up the SQL string and returns the SQL
810
+
811
+ Returns:
812
+ string
813
+ """
814
+ return re.sub(" +", " ", self._sql.strip())
815
+
816
+ def to_qmark(self):
817
+ """Cleans up the SQL string and returns the SQL
818
+
819
+ Returns:
820
+ string
821
+ """
822
+ return re.sub(" +", " ", self._sql.strip())
823
+
824
+ # TODO: Inspect this can't just be used by another method. seems duplicative
825
+ def process_columns(self, separator="", action="select", qmark=False):
826
+ """Specifies the columns in a selection expression.
827
+
828
+ Keyword Arguments:
829
+ separator {str} -- The separator used between columns (default: {""})
830
+
831
+ Returns:
832
+ self
833
+ """
834
+ sql = ""
835
+ for column in self._columns:
836
+ alias = None
837
+ if isinstance(column, SelectExpression):
838
+ alias = column.alias
839
+ if column.raw:
840
+ sql += column.column + ", "
841
+ continue
842
+
843
+ column = column.column
844
+
845
+ if isinstance(column, SubGroupExpression):
846
+ if qmark:
847
+ builder_sql = column.builder.to_qmark()
848
+ if column.builder._bindings:
849
+ self.add_binding(*column.builder._bindings)
850
+ else:
851
+ builder_sql = column.builder.to_sql()
852
+ sql += f"({builder_sql}) AS {column.alias}, "
853
+ continue
854
+
855
+ sql += self._table_column_string(
856
+ column, alias=alias, separator=separator
857
+ )
858
+
859
+ if self._aggregates:
860
+ sql += self.process_aggregates()
861
+
862
+ if sql == "":
863
+ return "*"
864
+
865
+ return sql.rstrip(",").rstrip(", ")
866
+
867
+ # TODO: Duplicative?
868
+ def process_values(self, separator="", qmark=False):
869
+ """Compiles column values for insert expressions.
870
+
871
+ Keyword Arguments:
872
+ separator {str} -- The separator used between columns (default: {""})
873
+
874
+ Returns:
875
+ self
876
+ """
877
+ sql = ""
878
+ if self._columns == "*":
879
+ return self._columns
880
+ elif isinstance(self._columns, list):
881
+ for c in self._columns:
882
+ for column, value in dict(c).items():
883
+ if qmark:
884
+ self.add_binding(value)
885
+ sql += f"?{separator}".strip()
886
+ else:
887
+ sql += self._compile_value(value, separator=separator)
888
+ else:
889
+ for column, value in dict(self._columns).items():
890
+ if qmark:
891
+ self.add_binding(value)
892
+ sql += f"?{separator}".strip()
893
+ else:
894
+ sql += self._compile_value(value, separator=separator)
895
+
896
+ if not qmark:
897
+ return sql[:-2]
898
+
899
+ return sql.rstrip(separator.strip())
900
+
901
+ def process_column(self, column, separator=""):
902
+ """Compiles a column into the column syntax.
903
+
904
+ Arguments:
905
+ column {string} -- The name of the column.
906
+
907
+ Keyword Arguments:
908
+ separator {string} -- The separator used between columns (default: {""})
909
+
910
+ Returns:
911
+ self
912
+ """
913
+ table = None
914
+ if column and "." in column:
915
+ table, column = column.split(".")
916
+ return self.column_string().format(
917
+ column=column, separator=separator, table=table or self.table
918
+ )
919
+
920
+ def _table_column_string(self, column, alias=None, separator=""):
921
+ """Compiles a column into the column syntax.
922
+
923
+ Arguments:
924
+ column {string} -- The name of the column.
925
+
926
+ Keyword Arguments:
927
+ separator {string} -- The separator used between columns (default: {""})
928
+
929
+ Returns:
930
+ self
931
+ """
932
+ table = None
933
+ if column and "." in column:
934
+ table, column = column.split(".")
935
+
936
+ if column == "*":
937
+ return self.column_strings.get("select_all").format(
938
+ column=column,
939
+ separator=separator,
940
+ table=self.process_table(table or self.table),
941
+ )
942
+
943
+ if alias:
944
+ alias_string = self.subquery_alias_string().format(alias=alias)
945
+ return self.column_strings.get(self._action).format(
946
+ column=column,
947
+ separator=separator,
948
+ alias=" " + alias_string if alias else "",
949
+ table=self.process_table(table or self.table),
950
+ )
951
+
952
+ def _compile_value(self, value, separator=""):
953
+ """Compiles a value using the value syntax.
954
+
955
+ Arguments:
956
+ value {string} -- The value to compile.
957
+
958
+ Keyword Arguments:
959
+ separator {string} -- The separator used between columns (default: {""})
960
+
961
+ Returns:
962
+ self
963
+ """
964
+ return self.value_string().format(value=value, separator=separator)
965
+
966
+ def drop_table(self, table):
967
+ """Specifies a drop table expression.
968
+
969
+ Arguments:
970
+ table {string} -- The table to drop.
971
+
972
+ Returns:
973
+ self
974
+ """
975
+ self._sql = self.drop_table_string().format(
976
+ table=self.process_column(table)
977
+ )
978
+ return self
979
+
980
+ def drop_table_if_exists(self, table):
981
+ """Specifies a drop table if exists expression.
982
+
983
+ Arguments:
984
+ table {string} -- The name of the table to drop.
985
+
986
+ Returns:
987
+ self
988
+ """
989
+ self._sql = self.drop_table_if_exists_string().format(
990
+ table=self.process_column(table)
991
+ )
992
+ return self
993
+
994
+ def rename_table(self, current_table_name, new_table_name):
995
+ """Specifies a rename table expression.
996
+
997
+ Arguments:
998
+ current_table_name {string} -- The name of the table currently.
999
+ new_table_name {string} -- The name you want to use now for the table.
1000
+
1001
+ Returns:
1002
+ self
1003
+ """
1004
+ self._sql = self.rename_table_string().format(
1005
+ current_table_name=self.process_column(current_table_name),
1006
+ new_table_name=self.process_column(new_table_name),
1007
+ )
1008
+ return self
1009
+
1010
+ def truncate_table(self, table, foreign_keys=False):
1011
+ """Specifies a truncate table expression.
1012
+
1013
+ Arguments;
1014
+ table {string} -- The name of the table to truncate.
1015
+
1016
+ Returns:
1017
+ self
1018
+ """
1019
+ raise NotImplementedError(
1020
+ f"'{self.__class__.__name__}' does not support truncating"
1021
+ )
1022
+
1023
+ def where_regexp_string(self):
1024
+ return "{keyword} {column} REGEXP {value}"
1025
+
1026
+ def where_not_regexp_string(self):
1027
+ return "{keyword} {column} NOT REGEXP {value}"