ormlambda 3.12.2__py3-none-any.whl → 3.34.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.
- ormlambda/__init__.py +2 -0
- ormlambda/caster/__init__.py +1 -1
- ormlambda/caster/caster.py +29 -12
- ormlambda/common/abstract_classes/clause_info_converter.py +4 -12
- ormlambda/common/abstract_classes/decomposition_query.py +17 -2
- ormlambda/common/abstract_classes/non_query_base.py +9 -7
- ormlambda/common/abstract_classes/query_base.py +3 -1
- ormlambda/common/errors/__init__.py +29 -0
- ormlambda/common/interfaces/IQueryCommand.py +6 -2
- ormlambda/databases/__init__.py +0 -1
- ormlambda/databases/my_sql/__init__.py +0 -1
- ormlambda/databases/my_sql/caster/caster.py +23 -19
- ormlambda/databases/my_sql/caster/types/__init__.py +3 -0
- ormlambda/databases/my_sql/caster/types/boolean.py +35 -0
- ormlambda/databases/my_sql/caster/types/bytes.py +7 -7
- ormlambda/databases/my_sql/caster/types/date.py +34 -0
- ormlambda/databases/my_sql/caster/types/datetime.py +7 -7
- ormlambda/databases/my_sql/caster/types/decimal.py +32 -0
- ormlambda/databases/my_sql/caster/types/float.py +7 -7
- ormlambda/databases/my_sql/caster/types/int.py +7 -7
- ormlambda/databases/my_sql/caster/types/iterable.py +7 -7
- ormlambda/databases/my_sql/caster/types/none.py +8 -7
- ormlambda/databases/my_sql/caster/types/point.py +4 -4
- ormlambda/databases/my_sql/caster/types/string.py +7 -7
- ormlambda/databases/my_sql/clauses/ST_AsText.py +8 -7
- ormlambda/databases/my_sql/clauses/ST_Contains.py +10 -5
- ormlambda/databases/my_sql/clauses/__init__.py +4 -10
- ormlambda/databases/my_sql/clauses/count.py +5 -15
- ormlambda/databases/my_sql/clauses/delete.py +3 -50
- ormlambda/databases/my_sql/clauses/group_by.py +3 -16
- ormlambda/databases/my_sql/clauses/having.py +2 -6
- ormlambda/databases/my_sql/clauses/insert.py +4 -92
- ormlambda/databases/my_sql/clauses/joins.py +5 -140
- ormlambda/databases/my_sql/clauses/limit.py +4 -15
- ormlambda/databases/my_sql/clauses/offset.py +4 -15
- ormlambda/databases/my_sql/clauses/order.py +4 -61
- ormlambda/databases/my_sql/clauses/update.py +4 -67
- ormlambda/databases/my_sql/clauses/upsert.py +3 -66
- ormlambda/databases/my_sql/clauses/where.py +4 -42
- ormlambda/databases/my_sql/repository.py +217 -0
- ormlambda/dialects/__init__.py +39 -0
- ormlambda/dialects/default/__init__.py +1 -0
- ormlambda/dialects/default/base.py +39 -0
- ormlambda/dialects/interface/__init__.py +1 -0
- ormlambda/dialects/interface/dialect.py +78 -0
- ormlambda/dialects/mysql/__init__.py +8 -0
- ormlambda/dialects/mysql/base.py +387 -0
- ormlambda/dialects/mysql/mysqlconnector.py +46 -0
- ormlambda/dialects/mysql/types.py +732 -0
- ormlambda/dialects/sqlite/__init__.py +5 -0
- ormlambda/dialects/sqlite/base.py +47 -0
- ormlambda/dialects/sqlite/pysqlite.py +32 -0
- ormlambda/engine/__init__.py +1 -0
- ormlambda/engine/base.py +58 -0
- ormlambda/engine/create.py +9 -23
- ormlambda/engine/url.py +31 -19
- ormlambda/env.py +30 -0
- ormlambda/errors.py +17 -0
- ormlambda/model/base_model.py +7 -9
- ormlambda/repository/base_repository.py +36 -5
- ormlambda/repository/interfaces/IRepositoryBase.py +121 -7
- ormlambda/repository/response.py +134 -0
- ormlambda/sql/clause_info/aggregate_function_base.py +19 -9
- ormlambda/sql/clause_info/clause_info.py +34 -17
- ormlambda/sql/clauses/__init__.py +14 -0
- ormlambda/{databases/my_sql → sql}/clauses/alias.py +23 -6
- ormlambda/sql/clauses/count.py +57 -0
- ormlambda/sql/clauses/delete.py +71 -0
- ormlambda/sql/clauses/group_by.py +30 -0
- ormlambda/sql/clauses/having.py +21 -0
- ormlambda/sql/clauses/insert.py +104 -0
- ormlambda/sql/clauses/interfaces/__init__.py +5 -0
- ormlambda/{components → sql/clauses}/join/join_context.py +15 -7
- ormlambda/sql/clauses/joins.py +159 -0
- ormlambda/sql/clauses/limit.py +15 -0
- ormlambda/sql/clauses/offset.py +15 -0
- ormlambda/sql/clauses/order.py +55 -0
- ormlambda/{databases/my_sql → sql}/clauses/select.py +12 -13
- ormlambda/sql/clauses/update.py +84 -0
- ormlambda/sql/clauses/upsert.py +77 -0
- ormlambda/sql/clauses/where.py +65 -0
- ormlambda/sql/column/__init__.py +1 -0
- ormlambda/sql/{column.py → column/column.py} +82 -22
- ormlambda/sql/comparer.py +51 -37
- ormlambda/sql/compiler.py +427 -0
- ormlambda/sql/ddl.py +68 -0
- ormlambda/sql/elements.py +36 -0
- ormlambda/sql/foreign_key.py +43 -39
- ormlambda/{databases/my_sql → sql}/functions/concat.py +13 -5
- ormlambda/{databases/my_sql → sql}/functions/max.py +9 -4
- ormlambda/{databases/my_sql → sql}/functions/min.py +9 -13
- ormlambda/{databases/my_sql → sql}/functions/sum.py +8 -10
- ormlambda/sql/sqltypes.py +647 -0
- ormlambda/sql/table/__init__.py +1 -1
- ormlambda/sql/table/table.py +179 -0
- ormlambda/sql/table/table_constructor.py +1 -208
- ormlambda/sql/type_api.py +35 -0
- ormlambda/sql/types.py +3 -1
- ormlambda/sql/visitors.py +74 -0
- ormlambda/statements/__init__.py +1 -0
- ormlambda/statements/base_statement.py +28 -38
- ormlambda/statements/interfaces/IStatements.py +5 -4
- ormlambda/{databases/my_sql → statements}/query_builder.py +35 -30
- ormlambda/{databases/my_sql → statements}/statements.py +50 -60
- ormlambda/statements/types.py +2 -2
- ormlambda/types/__init__.py +24 -0
- ormlambda/types/metadata.py +42 -0
- ormlambda/util/__init__.py +88 -0
- ormlambda/util/load_module.py +21 -0
- ormlambda/util/plugin_loader.py +32 -0
- ormlambda/util/typing.py +6 -0
- ormlambda-3.34.1.dist-info/AUTHORS +32 -0
- {ormlambda-3.12.2.dist-info → ormlambda-3.34.1.dist-info}/METADATA +2 -3
- ormlambda-3.34.1.dist-info/RECORD +157 -0
- {ormlambda-3.12.2.dist-info → ormlambda-3.34.1.dist-info}/WHEEL +1 -1
- ormlambda/components/__init__.py +0 -4
- ormlambda/components/delete/__init__.py +0 -2
- ormlambda/components/delete/abstract_delete.py +0 -17
- ormlambda/components/insert/__init__.py +0 -2
- ormlambda/components/insert/abstract_insert.py +0 -25
- ormlambda/components/select/__init__.py +0 -1
- ormlambda/components/update/__init__.py +0 -2
- ormlambda/components/update/abstract_update.py +0 -29
- ormlambda/components/upsert/__init__.py +0 -2
- ormlambda/components/upsert/abstract_upsert.py +0 -25
- ormlambda/databases/my_sql/clauses/create_database.py +0 -35
- ormlambda/databases/my_sql/clauses/drop_database.py +0 -17
- ormlambda/databases/my_sql/repository/__init__.py +0 -1
- ormlambda/databases/my_sql/repository/repository.py +0 -351
- ormlambda/engine/template.py +0 -47
- ormlambda/sql/dtypes.py +0 -94
- ormlambda/utils/__init__.py +0 -1
- ormlambda-3.12.2.dist-info/RECORD +0 -125
- /ormlambda/databases/my_sql/{types.py → pool_types.py} +0 -0
- /ormlambda/{components/delete → sql/clauses/interfaces}/IDelete.py +0 -0
- /ormlambda/{components/insert → sql/clauses/interfaces}/IInsert.py +0 -0
- /ormlambda/{components/select → sql/clauses/interfaces}/ISelect.py +0 -0
- /ormlambda/{components/update → sql/clauses/interfaces}/IUpdate.py +0 -0
- /ormlambda/{components/upsert → sql/clauses/interfaces}/IUpsert.py +0 -0
- /ormlambda/{components → sql/clauses}/join/__init__.py +0 -0
- /ormlambda/{databases/my_sql → sql}/functions/__init__.py +0 -0
- /ormlambda/{utils → util}/module_tree/__init__.py +0 -0
- /ormlambda/{utils → util}/module_tree/dfs_traversal.py +0 -0
- /ormlambda/{utils → util}/module_tree/dynamic_module.py +0 -0
- {ormlambda-3.12.2.dist-info → ormlambda-3.34.1.dist-info}/LICENSE +0 -0
@@ -0,0 +1,427 @@
|
|
1
|
+
from __future__ import annotations
|
2
|
+
import abc
|
3
|
+
from typing import Any, ClassVar, Optional, TYPE_CHECKING
|
4
|
+
|
5
|
+
from ormlambda.sql.ddl import CreateColumn
|
6
|
+
from ormlambda.sql.foreign_key import ForeignKey
|
7
|
+
from ormlambda.sql.sqltypes import resolve_primitive_types
|
8
|
+
|
9
|
+
from .visitors import Visitor
|
10
|
+
from ormlambda import util
|
11
|
+
from ormlambda.sql.type_api import TypeEngine
|
12
|
+
|
13
|
+
if TYPE_CHECKING:
|
14
|
+
from ormlambda import Column
|
15
|
+
from .visitors import Element
|
16
|
+
from .elements import ClauseElement
|
17
|
+
from ormlambda.dialects import Dialect
|
18
|
+
from ormlambda.sql.ddl import CreateTable, CreateSchema, DropSchema
|
19
|
+
from .sqltypes import (
|
20
|
+
INTEGER,
|
21
|
+
SMALLINTEGER,
|
22
|
+
BIGINTEGER,
|
23
|
+
NUMERIC,
|
24
|
+
FLOAT,
|
25
|
+
REAL,
|
26
|
+
DOUBLE,
|
27
|
+
STRING,
|
28
|
+
TEXT,
|
29
|
+
UNICODE,
|
30
|
+
UNICODETEXT,
|
31
|
+
NCHAR,
|
32
|
+
VARCHAR,
|
33
|
+
NVARCHAR,
|
34
|
+
CHAR,
|
35
|
+
DATE,
|
36
|
+
TIME,
|
37
|
+
DATETIME,
|
38
|
+
TIMESTAMP,
|
39
|
+
BOOLEAN,
|
40
|
+
LARGEBINARY,
|
41
|
+
VARBINARY,
|
42
|
+
ENUM,
|
43
|
+
POINT,
|
44
|
+
)
|
45
|
+
|
46
|
+
from ormlambda.sql.clauses import (
|
47
|
+
Insert,
|
48
|
+
Delete,
|
49
|
+
Upsert,
|
50
|
+
Update,
|
51
|
+
Limit,
|
52
|
+
Offset,
|
53
|
+
Count,
|
54
|
+
Where,
|
55
|
+
Having,
|
56
|
+
Order,
|
57
|
+
Concat,
|
58
|
+
Max,
|
59
|
+
Min,
|
60
|
+
Sum,
|
61
|
+
Groupby,
|
62
|
+
)
|
63
|
+
|
64
|
+
|
65
|
+
class Compiled:
|
66
|
+
"""Represent a compiled SQL or DDL expression.
|
67
|
+
|
68
|
+
The ``__str__`` method of the ``Compiled`` object should produce
|
69
|
+
the actual text of the statement. ``Compiled`` objects are
|
70
|
+
specific to their underlying database dialect, and also may
|
71
|
+
or may not be specific to the columns referenced within a
|
72
|
+
particular set of bind parameters. In no case should the
|
73
|
+
``Compiled`` object be dependent on the actual values of those
|
74
|
+
bind parameters, even though it may reference those values as
|
75
|
+
defaults.
|
76
|
+
"""
|
77
|
+
|
78
|
+
dialect: Dialect
|
79
|
+
"The dialect to compile against."
|
80
|
+
|
81
|
+
statement: Optional[ClauseElement] = None
|
82
|
+
"The statement to compile."
|
83
|
+
|
84
|
+
string: str = ""
|
85
|
+
"The string representation of the ``statement``"
|
86
|
+
|
87
|
+
_gen_time: float
|
88
|
+
"The time when the statement was generated."
|
89
|
+
|
90
|
+
is_sql: ClassVar[bool] = False
|
91
|
+
is_ddl: ClassVar[bool] = False
|
92
|
+
|
93
|
+
def __init__(
|
94
|
+
self,
|
95
|
+
dialect: Dialect,
|
96
|
+
statement: Optional[ClauseElement] = None,
|
97
|
+
**kw: Any,
|
98
|
+
) -> None:
|
99
|
+
"""Construct a new :class:`.Compiled` object.
|
100
|
+
|
101
|
+
:param dialect: :class:`.Dialect` to compile against.
|
102
|
+
|
103
|
+
:param statement: :class:`_expression.ClauseElement` to be compiled.
|
104
|
+
|
105
|
+
"""
|
106
|
+
self.dialect = dialect
|
107
|
+
|
108
|
+
if statement is not None:
|
109
|
+
self.statement = statement
|
110
|
+
self.string = self.process(self.statement, **kw)
|
111
|
+
|
112
|
+
@property
|
113
|
+
def sql_compiler(self):
|
114
|
+
"""Return a Compiled that is capable of processing SQL expressions.
|
115
|
+
|
116
|
+
If this compiler is one, it would likely just return 'self'.
|
117
|
+
|
118
|
+
"""
|
119
|
+
|
120
|
+
raise NotImplementedError()
|
121
|
+
|
122
|
+
def process(self, obj: Element, **kwargs: Any) -> str:
|
123
|
+
return obj._compiler_dispatch(self, **kwargs)
|
124
|
+
|
125
|
+
|
126
|
+
class TypeCompiler(Visitor):
|
127
|
+
"""Base class for all type compilers."""
|
128
|
+
|
129
|
+
def __init__(self, dialect: Dialect):
|
130
|
+
self.dialect = dialect
|
131
|
+
|
132
|
+
def process(self, type_: TypeEngine[Any], **kw: Any) -> str:
|
133
|
+
"""Process a type object into a string representation.
|
134
|
+
|
135
|
+
:param type_: The type object to process.
|
136
|
+
:param kw: Additional keyword arguments.
|
137
|
+
:return: The string representation of the type object.
|
138
|
+
"""
|
139
|
+
if not isinstance(type_, TypeEngine):
|
140
|
+
type_ = resolve_primitive_types(type_)
|
141
|
+
return type_._compiler_dispatch(self, **kw)
|
142
|
+
|
143
|
+
|
144
|
+
class SQLCompiler(Compiled, abc.ABC):
|
145
|
+
is_sql = True
|
146
|
+
|
147
|
+
@abc.abstractmethod
|
148
|
+
def visit_insert(self, insert: Insert, **kw) -> Insert: ...
|
149
|
+
@abc.abstractmethod
|
150
|
+
def visit_delete(self, delete: Delete, **kw) -> Delete: ...
|
151
|
+
@abc.abstractmethod
|
152
|
+
def visit_upsert(self, upsert: Upsert, **kw) -> Upsert: ...
|
153
|
+
@abc.abstractmethod
|
154
|
+
def visit_update(self, update: Update, **kw) -> Update: ...
|
155
|
+
@abc.abstractmethod
|
156
|
+
def visit_limit(self, limit: Limit, **kw) -> Limit: ...
|
157
|
+
@abc.abstractmethod
|
158
|
+
def visit_offset(self, offset: Offset, **kw) -> Offset: ...
|
159
|
+
@abc.abstractmethod
|
160
|
+
def visit_count(self, count: Count, **kw) -> Count: ...
|
161
|
+
@abc.abstractmethod
|
162
|
+
def visit_where(self, where: Where, **kw) -> Where: ...
|
163
|
+
@abc.abstractmethod
|
164
|
+
def visit_having(self, having: Having, **kw) -> Having: ...
|
165
|
+
@abc.abstractmethod
|
166
|
+
def visit_order(self, order: Order, **kw) -> Order: ...
|
167
|
+
@abc.abstractmethod
|
168
|
+
def visit_concat(self, concat: Concat, **kw) -> Concat: ...
|
169
|
+
@abc.abstractmethod
|
170
|
+
def visit_max(self, max: Max, **kw) -> Max: ...
|
171
|
+
@abc.abstractmethod
|
172
|
+
def visit_min(self, min: Min, **kw) -> Min: ...
|
173
|
+
@abc.abstractmethod
|
174
|
+
def visit_sum(self, sum: Sum, **kw) -> Sum: ...
|
175
|
+
@abc.abstractmethod
|
176
|
+
def visit_group_by(self, groupby: Groupby, **kw) -> Groupby: ...
|
177
|
+
|
178
|
+
|
179
|
+
class DDLCompiler(Compiled):
|
180
|
+
is_ddl = True
|
181
|
+
|
182
|
+
if TYPE_CHECKING:
|
183
|
+
|
184
|
+
def __init__(
|
185
|
+
self,
|
186
|
+
dialect: Dialect,
|
187
|
+
statement: Optional[ClauseElement] = None,
|
188
|
+
**kw: Any,
|
189
|
+
) -> None: ...
|
190
|
+
|
191
|
+
@property
|
192
|
+
def sql_compiler(self):
|
193
|
+
"""Return a SQL compiler that is capable of processing SQL expressions.
|
194
|
+
|
195
|
+
This method returns the SQL compiler for the dialect, which is
|
196
|
+
used to process SQL expressions.
|
197
|
+
|
198
|
+
"""
|
199
|
+
return self.dialect.statement_compiler(self.dialect, None)
|
200
|
+
|
201
|
+
def visit_create_schema(self, create: CreateSchema, **kw) -> str:
|
202
|
+
"""
|
203
|
+
Generate a CREATE SCHEMA SQL statement for MySQL.
|
204
|
+
|
205
|
+
Args:
|
206
|
+
schema_name (str): Name of the schema/database to create
|
207
|
+
if_not_exists (bool): Whether to include IF NOT EXISTS clause
|
208
|
+
|
209
|
+
Returns:
|
210
|
+
str: The SQL CREATE SCHEMA statement
|
211
|
+
|
212
|
+
Raises:
|
213
|
+
ValueError: If schema_name is empty or contains invalid characters
|
214
|
+
"""
|
215
|
+
schema_name = create.schema
|
216
|
+
|
217
|
+
util.avoid_sql_injection(schema_name)
|
218
|
+
|
219
|
+
if_not_exists_clause = "IF NOT EXISTS " if create.if_not_exists else ""
|
220
|
+
return f"CREATE SCHEMA {if_not_exists_clause}{schema_name};"
|
221
|
+
|
222
|
+
def visit_drop_schema(self, drop: DropSchema, **kw):
|
223
|
+
if_exists_clause = "IF EXISTS " if drop.if_exists else ""
|
224
|
+
return f"DROP SCHEMA {if_exists_clause}{drop.schema};"
|
225
|
+
|
226
|
+
def visit_schema_exists(self, schema: str) -> bool:
|
227
|
+
return f"SHOW DATABASES LIKE {schema};"
|
228
|
+
# return f"SHOW DATABASES LIKE {self.dialect.caster.PLACEHOLDER};", (schema,)
|
229
|
+
|
230
|
+
def visit_create_table(self, create: CreateTable, **kw) -> str:
|
231
|
+
tablecls = create.element
|
232
|
+
column_sql: list[str] = []
|
233
|
+
for create_col in create.columns:
|
234
|
+
try:
|
235
|
+
processed = self.process(create_col)
|
236
|
+
if processed is not None:
|
237
|
+
column_sql.append(processed)
|
238
|
+
|
239
|
+
except Exception:
|
240
|
+
raise
|
241
|
+
|
242
|
+
foreign_keys = ForeignKey.create_query(tablecls, self.dialect)
|
243
|
+
table_options = " ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci"
|
244
|
+
|
245
|
+
sql = f"CREATE TABLE {tablecls.__table_name__} (\n\t"
|
246
|
+
sql += ",\n\t".join(column_sql)
|
247
|
+
sql += "\n\t" if not foreign_keys else ",\n\t"
|
248
|
+
sql += ",\n\t".join(foreign_keys)
|
249
|
+
sql += f"\n){table_options};"
|
250
|
+
return sql
|
251
|
+
|
252
|
+
def visit_create_column(self, create: CreateColumn, first_pk=False, **kw): # noqa: F821
|
253
|
+
column = create.element
|
254
|
+
return self.get_column_specification(column)
|
255
|
+
|
256
|
+
def get_column_specification(self, column: Column, **kwargs):
|
257
|
+
colspec = column.column_name + " " + self.dialect.type_compiler_instance.process(column.dtype)
|
258
|
+
default = self.get_column_default_string(column)
|
259
|
+
if default is not None:
|
260
|
+
colspec += " DEFAULT " + default
|
261
|
+
|
262
|
+
if column.is_not_null:
|
263
|
+
colspec += " NOT NULL"
|
264
|
+
|
265
|
+
if column.is_primary_key:
|
266
|
+
colspec += " PRIMARY KEY"
|
267
|
+
return colspec
|
268
|
+
|
269
|
+
def get_column_default_string(self, column: Column) -> Optional[str]:
|
270
|
+
if isinstance(column.default_value, str):
|
271
|
+
return column.default_value
|
272
|
+
if not column.default_value:
|
273
|
+
return None
|
274
|
+
return None
|
275
|
+
|
276
|
+
|
277
|
+
class GenericTypeCompiler(TypeCompiler):
|
278
|
+
"""Generic type compiler
|
279
|
+
|
280
|
+
This class is used to compile ormlambda types into their
|
281
|
+
string representations for the given dialect.
|
282
|
+
"""
|
283
|
+
|
284
|
+
def _render_string_type(self, type_: STRING, name: str, length_override: Optional[int] = None):
|
285
|
+
text = name
|
286
|
+
if length_override:
|
287
|
+
text += "(%d)" % length_override
|
288
|
+
elif type_.length:
|
289
|
+
text += "(%d)" % type_.length
|
290
|
+
if type_.collation:
|
291
|
+
text += ' COLLATE "%s"' % type_.collation
|
292
|
+
return text
|
293
|
+
|
294
|
+
def visit_INTEGER(self, type_: INTEGER, **kw):
|
295
|
+
return "INTEGER"
|
296
|
+
|
297
|
+
def visit_SMALLINTEGER(self, type_: SMALLINTEGER, **kw):
|
298
|
+
return "SMALLINTEGER"
|
299
|
+
|
300
|
+
def visit_BIGINTEGER(self, type_: BIGINTEGER, **kw):
|
301
|
+
return "BIGINTEGER"
|
302
|
+
|
303
|
+
def visit_NUMERIC(self, type_: NUMERIC, **kw):
|
304
|
+
return "NUMERIC"
|
305
|
+
|
306
|
+
def visit_FLOAT(self, type_: FLOAT, **kw):
|
307
|
+
return "FLOAT"
|
308
|
+
|
309
|
+
def visit_REAL(self, type_: REAL, **kw):
|
310
|
+
return "REAL"
|
311
|
+
|
312
|
+
def visit_DOUBLE(self, type_: DOUBLE, **kw):
|
313
|
+
return "DOUBLE"
|
314
|
+
|
315
|
+
def visit_TEXT(self, type_: TEXT, **kw):
|
316
|
+
return self._render_string_type(type_, "TEXT", **kw)
|
317
|
+
|
318
|
+
def visit_UNICODE(self, type_: UNICODE, **kw):
|
319
|
+
return self._render_string_type(type_, "UNICODE", **kw)
|
320
|
+
|
321
|
+
def visit_UNICODETEXT(self, type_: UNICODETEXT, **kw):
|
322
|
+
return self._render_string_type(type_, "UNICODETEXT", **kw)
|
323
|
+
|
324
|
+
def visit_CHAR(self, type_: CHAR, **kw):
|
325
|
+
return self._render_string_type(type_, "CHAR", **kw)
|
326
|
+
|
327
|
+
def visit_NCHAR(self, type_: NCHAR, **kw):
|
328
|
+
return self._render_string_type(type_, "NCHAR", **kw)
|
329
|
+
|
330
|
+
def visit_VARCHAR(self, type_: VARCHAR, **kw):
|
331
|
+
return self._render_string_type(type_, "VARCHAR", **kw)
|
332
|
+
|
333
|
+
def visit_NVARCHAR(self, type_: NVARCHAR, **kw):
|
334
|
+
return self._render_string_type(type_, "NVARCHAR", **kw)
|
335
|
+
|
336
|
+
def visit_DATE(self, type_: DATE, **kw):
|
337
|
+
return "DATE"
|
338
|
+
|
339
|
+
def visit_TIME(self, type_: TIME, **kw):
|
340
|
+
return "TIME"
|
341
|
+
|
342
|
+
def visit_DATETIME(self, type_: DATETIME, **kw):
|
343
|
+
return "DATETIME"
|
344
|
+
|
345
|
+
def visit_TIMESTAMP(self, type_: TIMESTAMP, **kw):
|
346
|
+
return "TIMESTAMP"
|
347
|
+
|
348
|
+
def visit_BOOLEAN(self, type_: BOOLEAN, **kw):
|
349
|
+
return "BOOLEAN"
|
350
|
+
|
351
|
+
def visit_LARGEBINARY(self, type_: LARGEBINARY, **kw):
|
352
|
+
return "LARGEBINARY"
|
353
|
+
|
354
|
+
def visit_VARBINARY(self, type_: VARBINARY, **kw):
|
355
|
+
return "VARBINARY"
|
356
|
+
|
357
|
+
def visit_ENUM(self, type_: ENUM, **kw):
|
358
|
+
return "ENUM"
|
359
|
+
|
360
|
+
def visit_BLOB(self, type_: LARGEBINARY, **kw):
|
361
|
+
return "BLOB"
|
362
|
+
|
363
|
+
def visit_null(self, type_: POINT, **kw):
|
364
|
+
return "NULL"
|
365
|
+
|
366
|
+
def visit_POINT(self, _type: POINT, **kw):
|
367
|
+
return "POINT"
|
368
|
+
|
369
|
+
def visit_uuid(self, type_, **kw):
|
370
|
+
if not type_.native_uuid or not self.dialect.supports_native_uuid:
|
371
|
+
return self._render_string_type(type_, "CHAR", length_override=32)
|
372
|
+
else:
|
373
|
+
return self.visit_UUID(type_, **kw)
|
374
|
+
|
375
|
+
def visit_large_binary(self, type_, **kw):
|
376
|
+
return self.visit_BLOB(type_, **kw)
|
377
|
+
|
378
|
+
def visit_boolean(self, type_, **kw):
|
379
|
+
return self.visit_BOOLEAN(type_, **kw)
|
380
|
+
|
381
|
+
def visit_time(self, type_, **kw):
|
382
|
+
return self.visit_TIME(type_, **kw)
|
383
|
+
|
384
|
+
def visit_datetime(self, type_, **kw):
|
385
|
+
return self.visit_DATETIME(type_, **kw)
|
386
|
+
|
387
|
+
def visit_date(self, type_, **kw):
|
388
|
+
return self.visit_DATE(type_, **kw)
|
389
|
+
|
390
|
+
def visit_big_integer(self, type_, **kw):
|
391
|
+
return self.visit_BIGINT(type_, **kw)
|
392
|
+
|
393
|
+
def visit_small_integer(self, type_, **kw):
|
394
|
+
return self.visit_SMALLINT(type_, **kw)
|
395
|
+
|
396
|
+
def visit_integer(self, type_, **kw):
|
397
|
+
return self.visit_INTEGER(type_, **kw)
|
398
|
+
|
399
|
+
def visit_real(self, type_, **kw):
|
400
|
+
return self.visit_REAL(type_, **kw)
|
401
|
+
|
402
|
+
def visit_float(self, type_, **kw):
|
403
|
+
return self.visit_FLOAT(type_, **kw)
|
404
|
+
|
405
|
+
def visit_double(self, type_, **kw):
|
406
|
+
return self.visit_DOUBLE(type_, **kw)
|
407
|
+
|
408
|
+
def visit_numeric(self, type_, **kw):
|
409
|
+
return self.visit_NUMERIC(type_, **kw)
|
410
|
+
|
411
|
+
def visit_string(self, type_, **kw):
|
412
|
+
return self.visit_VARCHAR(type_, **kw)
|
413
|
+
|
414
|
+
def visit_unicode(self, type_, **kw):
|
415
|
+
return self.visit_VARCHAR(type_, **kw)
|
416
|
+
|
417
|
+
def visit_text(self, type_, **kw):
|
418
|
+
return self.visit_TEXT(type_, **kw)
|
419
|
+
|
420
|
+
def visit_unicode_text(self, type_, **kw):
|
421
|
+
return self.visit_TEXT(type_, **kw)
|
422
|
+
|
423
|
+
def visit_enum(self, type_, **kw):
|
424
|
+
return self.visit_VARCHAR(type_, **kw)
|
425
|
+
|
426
|
+
def visit_point(self, type_: POINT, **kw):
|
427
|
+
return self.visit_POINT(type_, **kw)
|
ormlambda/sql/ddl.py
ADDED
@@ -0,0 +1,68 @@
|
|
1
|
+
from __future__ import annotations
|
2
|
+
from typing import TYPE_CHECKING
|
3
|
+
from .elements import ClauseElement
|
4
|
+
|
5
|
+
if TYPE_CHECKING:
|
6
|
+
from ormlambda.dialects.interface.dialect import Dialect
|
7
|
+
from ormlambda import Column
|
8
|
+
from ormlambda import Table
|
9
|
+
|
10
|
+
|
11
|
+
class BaseDDLElement(ClauseElement):
|
12
|
+
"""
|
13
|
+
Base class for all DDL elements.
|
14
|
+
"""
|
15
|
+
|
16
|
+
__visit_name__ = "ddl_element"
|
17
|
+
|
18
|
+
def _compiler(self, dialect: Dialect, **kw) -> str:
|
19
|
+
"""
|
20
|
+
Compile the DDL element into a SQL string.
|
21
|
+
"""
|
22
|
+
return dialect.ddl_compiler(dialect, self, **kw)
|
23
|
+
|
24
|
+
|
25
|
+
class CreateTable(BaseDDLElement):
|
26
|
+
"""
|
27
|
+
Class representing a CREATE TABLE statement.
|
28
|
+
"""
|
29
|
+
|
30
|
+
__visit_name__ = "create_table"
|
31
|
+
|
32
|
+
def __init__(self, element: Table):
|
33
|
+
self.element = element
|
34
|
+
self.columns = [CreateColumn(c) for c in element.get_columns()]
|
35
|
+
|
36
|
+
|
37
|
+
class CreateColumn[T](BaseDDLElement):
|
38
|
+
"""
|
39
|
+
Class representing a column in a CREATE TABLE statement.
|
40
|
+
"""
|
41
|
+
|
42
|
+
__visit_name__ = "create_column"
|
43
|
+
|
44
|
+
def __init__(self, element: Column[T]):
|
45
|
+
self.element = element
|
46
|
+
|
47
|
+
|
48
|
+
class CreateSchema(BaseDDLElement):
|
49
|
+
__visit_name__ = "create_schema"
|
50
|
+
|
51
|
+
def __init__(self, schema: str, if_not_exists: bool = True):
|
52
|
+
self.schema: str = schema
|
53
|
+
self.if_not_exists: bool = if_not_exists
|
54
|
+
|
55
|
+
|
56
|
+
class DropSchema(BaseDDLElement):
|
57
|
+
__visit_name__ = "drop_schema"
|
58
|
+
|
59
|
+
def __init__(self, schema: str, if_exists: bool = True):
|
60
|
+
self.schema = schema
|
61
|
+
self.if_exists = if_exists
|
62
|
+
|
63
|
+
|
64
|
+
class SchemaExists(BaseDDLElement):
|
65
|
+
__visit_name__ = "schema_exists"
|
66
|
+
|
67
|
+
def __init__(self, schema: str):
|
68
|
+
self.schema = schema
|
@@ -0,0 +1,36 @@
|
|
1
|
+
from __future__ import annotations
|
2
|
+
from typing import Optional, Any, TYPE_CHECKING
|
3
|
+
|
4
|
+
from .visitors import Element
|
5
|
+
|
6
|
+
if TYPE_CHECKING:
|
7
|
+
from ormlambda.dialects import Dialect
|
8
|
+
from .compiler import Compiled
|
9
|
+
|
10
|
+
|
11
|
+
class CompilerElement(Element):
|
12
|
+
"""
|
13
|
+
Base class for all compiler elements
|
14
|
+
"""
|
15
|
+
|
16
|
+
__slots__ = ()
|
17
|
+
__visit_name__ = "compiler_element"
|
18
|
+
|
19
|
+
def compile(self, dialect: Optional[Dialect] = None, **kw: Any) -> Compiled:
|
20
|
+
if dialect is None:
|
21
|
+
raise TypeError("Either bind or dialect must be provided.")
|
22
|
+
return self._compiler(dialect, **kw)
|
23
|
+
|
24
|
+
def _compiler(self, dialect: Dialect, **kw: Any) -> Compiled:
|
25
|
+
"""
|
26
|
+
Compile the element into a SQL string.
|
27
|
+
"""
|
28
|
+
return dialect.statement_compiler(dialect, self, **kw)
|
29
|
+
|
30
|
+
|
31
|
+
class ClauseElement(CompilerElement):
|
32
|
+
"""
|
33
|
+
Base class for all clause elements.
|
34
|
+
"""
|
35
|
+
|
36
|
+
__visit_name__ = "clause_element"
|
ormlambda/sql/foreign_key.py
CHANGED
@@ -1,13 +1,14 @@
|
|
1
1
|
from __future__ import annotations
|
2
|
-
from typing import Callable, TYPE_CHECKING, Optional, Any, Type, cast, overload
|
2
|
+
from typing import Callable, TYPE_CHECKING, Optional, Any, Type, cast, overload, ClassVar
|
3
3
|
|
4
4
|
from ormlambda.common.interfaces.IQueryCommand import IQuery
|
5
|
-
|
5
|
+
from ormlambda.sql.elements import Element
|
6
6
|
|
7
7
|
if TYPE_CHECKING:
|
8
8
|
from ormlambda.sql.comparer import Comparer
|
9
9
|
from ormlambda import Table
|
10
10
|
from ormlambda.sql.clause_info.clause_info_context import ClauseContextType
|
11
|
+
from ormlambda.dialects import Dialect
|
11
12
|
|
12
13
|
|
13
14
|
class ForeignKeyContext(set["ForeignKey"]):
|
@@ -32,49 +33,51 @@ class ForeignKeyContext(set["ForeignKey"]):
|
|
32
33
|
return super().add(element)
|
33
34
|
|
34
35
|
|
35
|
-
class ForeignKey[TLeft: Table, TRight: Table](IQuery):
|
36
|
-
|
36
|
+
class ForeignKey[TLeft: Table, TRight: Table](Element, IQuery):
|
37
|
+
__visit_name__ = "foreign_key"
|
38
|
+
|
39
|
+
stored_calls: ClassVar[ForeignKeyContext] = ForeignKeyContext()
|
37
40
|
|
38
41
|
@overload
|
39
|
-
def __new__
|
42
|
+
def __new__(self, comparer: Comparer, clause_name: str) -> None: ...
|
40
43
|
@overload
|
41
|
-
def __new__[
|
42
|
-
|
43
|
-
def __new__[LProp, TRight, RProp](
|
44
|
+
def __new__[TRight](
|
44
45
|
cls,
|
45
|
-
tright:
|
46
|
-
relationship:
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
) -> TRight:
|
46
|
+
tright: Type[TRight],
|
47
|
+
relationship: Callable[[TLeft, TRight], Any | Comparer],
|
48
|
+
keep_alive: bool = ...,
|
49
|
+
dialect: Dialect = ...,
|
50
|
+
) -> TRight: ...
|
51
|
+
|
52
|
+
def __new__[TRight](cls, *args, **kwargs) -> TRight:
|
52
53
|
return super().__new__(cls)
|
53
54
|
|
54
|
-
def __init__
|
55
|
+
def __init__(
|
55
56
|
self,
|
56
57
|
tright: Optional[TRight] = None,
|
57
|
-
relationship: Optional[Callable[[TLeft, TRight], Any | Comparer
|
58
|
+
relationship: Optional[Callable[[TLeft, TRight], Any | Comparer]] = None,
|
58
59
|
*,
|
59
60
|
comparer: Optional[Comparer] = None,
|
60
61
|
clause_name: Optional[str] = None,
|
61
62
|
keep_alive: bool = False,
|
63
|
+
**kwargs: Any,
|
62
64
|
) -> None:
|
65
|
+
self.kwargs = kwargs
|
63
66
|
self._keep_alive = keep_alive
|
64
67
|
if comparer is not None and clause_name is not None:
|
65
|
-
self.__init__with_comparer(comparer, clause_name)
|
68
|
+
self.__init__with_comparer(comparer, clause_name, **kwargs)
|
66
69
|
else:
|
67
70
|
self.__init_with_callable(tright, relationship)
|
68
71
|
|
69
|
-
def __init__with_comparer
|
72
|
+
def __init__with_comparer(self, comparer: Comparer, clause_name: str, **kwargs) -> None:
|
70
73
|
self._relationship = None
|
71
|
-
self._tleft: TLeft = comparer.left_condition.table
|
72
|
-
self._tright: TRight = comparer.right_condition.table
|
74
|
+
self._tleft: TLeft = comparer.left_condition(**kwargs).table
|
75
|
+
self._tright: TRight = comparer.right_condition(**kwargs).table
|
73
76
|
self._clause_name: str = clause_name
|
74
|
-
self._comparer: Comparer
|
77
|
+
self._comparer: Comparer = comparer
|
75
78
|
|
76
|
-
def __init_with_callable
|
77
|
-
self._relationship: Callable[[TLeft, TRight], Comparer
|
79
|
+
def __init_with_callable(self, tright: Optional[TRight], relationship: Optional[Callable[[TLeft, TRight], Comparer]]) -> None:
|
80
|
+
self._relationship: Callable[[TLeft, TRight], Comparer] = relationship
|
78
81
|
self._tleft: TLeft = None
|
79
82
|
self._tright: TRight = tright
|
80
83
|
self._clause_name: str = None
|
@@ -113,29 +116,29 @@ class ForeignKey[TLeft: Table, TRight: Table](IQuery):
|
|
113
116
|
def clause_name(self) -> str:
|
114
117
|
return self._clause_name
|
115
118
|
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
rcon = alias if (alias := compare.right_condition.alias_table) else compare.right_condition.table.__table_name__
|
120
|
-
return f"FOREIGN KEY ({
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
self._comparer =
|
125
|
-
lcol = self._comparer.left_condition._column.column_name
|
126
|
-
rcol = self._comparer.right_condition._column.column_name
|
119
|
+
def query(self, dialect: Dialect, **kwargs) -> str:
|
120
|
+
compare = self.resolved_function(dialect)
|
121
|
+
left_col = compare.left_condition(dialect).column
|
122
|
+
rcon = alias if (alias := compare.right_condition(dialect).alias_table) else compare.right_condition(dialect).table.__table_name__
|
123
|
+
return f"FOREIGN KEY ({left_col}) REFERENCES {rcon}({compare.right_condition(dialect).column})"
|
124
|
+
|
125
|
+
def get_alias(self, dialect: Dialect) -> str:
|
126
|
+
self._comparer = self.resolved_function(dialect)
|
127
|
+
self._comparer._dialect = dialect
|
128
|
+
lcol = self._comparer.left_condition(dialect)._column.column_name
|
129
|
+
rcol = self._comparer.right_condition(dialect)._column.column_name
|
127
130
|
return f"{self.tleft.__table_name__}_{lcol}_{rcol}"
|
128
131
|
|
129
132
|
@classmethod
|
130
|
-
def create_query(cls, orig_table: Table) -> list[str]:
|
133
|
+
def create_query(cls, orig_table: Table, dialect: Dialect) -> list[str]:
|
131
134
|
clauses: list[str] = []
|
132
135
|
|
133
|
-
for attr in
|
136
|
+
for attr in orig_table.__dict__.values():
|
134
137
|
if isinstance(attr, ForeignKey):
|
135
|
-
clauses.append(attr.query)
|
138
|
+
clauses.append(attr.query(dialect))
|
136
139
|
return clauses
|
137
140
|
|
138
|
-
def resolved_function[LProp: Any, RProp: Any](self, context: ClauseContextType = None) -> Comparer
|
141
|
+
def resolved_function[LProp: Any, RProp: Any](self, dialect: Dialect, context: Optional[ClauseContextType] = None) -> Comparer:
|
139
142
|
""" """
|
140
143
|
if self._comparer is not None:
|
141
144
|
return self._comparer
|
@@ -144,4 +147,5 @@ class ForeignKey[TLeft: Table, TRight: Table](IQuery):
|
|
144
147
|
right = self._tright
|
145
148
|
comparer = self._relationship(left, right)
|
146
149
|
comparer.set_context(context)
|
150
|
+
comparer._dialect = dialect
|
147
151
|
return comparer
|