sqlalchemy-cubrid 0.7.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.
- sqlalchemy_cubrid/__init__.py +85 -0
- sqlalchemy_cubrid/alembic_impl.py +145 -0
- sqlalchemy_cubrid/base.py +428 -0
- sqlalchemy_cubrid/compiler.py +515 -0
- sqlalchemy_cubrid/dialect.py +700 -0
- sqlalchemy_cubrid/dml.py +318 -0
- sqlalchemy_cubrid/py.typed +0 -0
- sqlalchemy_cubrid/pycubrid_dialect.py +124 -0
- sqlalchemy_cubrid/requirements.py +270 -0
- sqlalchemy_cubrid/trace.py +92 -0
- sqlalchemy_cubrid/types.py +349 -0
- sqlalchemy_cubrid-0.7.1.dist-info/METADATA +281 -0
- sqlalchemy_cubrid-0.7.1.dist-info/RECORD +18 -0
- sqlalchemy_cubrid-0.7.1.dist-info/WHEEL +5 -0
- sqlalchemy_cubrid-0.7.1.dist-info/entry_points.txt +7 -0
- sqlalchemy_cubrid-0.7.1.dist-info/licenses/AUTHORS +2 -0
- sqlalchemy_cubrid-0.7.1.dist-info/licenses/LICENSE +21 -0
- sqlalchemy_cubrid-0.7.1.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,515 @@
|
|
|
1
|
+
# sqlalchemy_cubrid/compiler.py
|
|
2
|
+
# Copyright (C) 2021-2026 by sqlalchemy-cubrid authors and contributors
|
|
3
|
+
# <see AUTHORS file>
|
|
4
|
+
#
|
|
5
|
+
# This module is part of sqlalchemy-cubrid and is released under
|
|
6
|
+
# the MIT License: http://www.opensource.org/licenses/mit-license.php
|
|
7
|
+
|
|
8
|
+
"""CUBRID SQL, DDL, and type compilers for SQLAlchemy 2.0."""
|
|
9
|
+
|
|
10
|
+
from __future__ import annotations
|
|
11
|
+
|
|
12
|
+
from sqlalchemy.sql import compiler
|
|
13
|
+
from sqlalchemy.sql import sqltypes
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
class CubridCompiler(compiler.SQLCompiler):
|
|
17
|
+
"""SQLCompiler subclass for CUBRID."""
|
|
18
|
+
|
|
19
|
+
def visit_sysdate_func(self, fn, **kw):
|
|
20
|
+
return "SYSDATE"
|
|
21
|
+
|
|
22
|
+
def visit_utc_timestamp_func(self, fn, **kw):
|
|
23
|
+
return "UTC_TIMESTAMP()"
|
|
24
|
+
|
|
25
|
+
def visit_group_concat_func(self, fn, **kw):
|
|
26
|
+
"""Render GROUP_CONCAT aggregate function.
|
|
27
|
+
|
|
28
|
+
CUBRID supports GROUP_CONCAT([DISTINCT] expr [ORDER BY ...] [SEPARATOR '...'])
|
|
29
|
+
"""
|
|
30
|
+
return "GROUP_CONCAT(%s)" % self.function_argspec(fn, **kw)
|
|
31
|
+
|
|
32
|
+
def visit_cast(self, cast, **kw):
|
|
33
|
+
# https://www.cubrid.org/manual/en/11.0/sql/function/typecast_fn.html#cast
|
|
34
|
+
type_ = self.process(cast.typeclause)
|
|
35
|
+
if type_ is None:
|
|
36
|
+
return self.process(cast.clause.self_group())
|
|
37
|
+
return f"CAST({self.process(cast.clause)} AS {type_})"
|
|
38
|
+
|
|
39
|
+
def render_literal_value(self, value, type_):
|
|
40
|
+
value = super().render_literal_value(value, type_)
|
|
41
|
+
value = value.replace("\\", "\\\\")
|
|
42
|
+
return value
|
|
43
|
+
|
|
44
|
+
def get_select_precolumns(self, select, **kw):
|
|
45
|
+
if bool(select._distinct):
|
|
46
|
+
return "DISTINCT "
|
|
47
|
+
return ""
|
|
48
|
+
|
|
49
|
+
def visit_join(self, join, asfrom=False, **kwargs):
|
|
50
|
+
# https://www.cubrid.org/manual/en/11.0/sql/query/select.html#join-query
|
|
51
|
+
return "".join(
|
|
52
|
+
(
|
|
53
|
+
self.process(join.left, asfrom=True, **kwargs),
|
|
54
|
+
(join.isouter and " LEFT OUTER JOIN " or " INNER JOIN "),
|
|
55
|
+
self.process(join.right, asfrom=True, **kwargs),
|
|
56
|
+
" ON ",
|
|
57
|
+
self.process(join.onclause, **kwargs),
|
|
58
|
+
)
|
|
59
|
+
)
|
|
60
|
+
|
|
61
|
+
def for_update_clause(self, select, **kw):
|
|
62
|
+
"""Render FOR UPDATE clause.
|
|
63
|
+
|
|
64
|
+
CUBRID supports::
|
|
65
|
+
|
|
66
|
+
SELECT ... FOR UPDATE
|
|
67
|
+
SELECT ... FOR UPDATE OF col1, col2
|
|
68
|
+
|
|
69
|
+
CUBRID does NOT support NOWAIT or SKIP LOCKED.
|
|
70
|
+
"""
|
|
71
|
+
if select._for_update_arg is None:
|
|
72
|
+
return ""
|
|
73
|
+
text = " FOR UPDATE"
|
|
74
|
+
if select._for_update_arg.of:
|
|
75
|
+
text += " OF " + ", ".join(self.process(col, **kw) for col in select._for_update_arg.of)
|
|
76
|
+
return text
|
|
77
|
+
|
|
78
|
+
def limit_clause(self, select, **kw):
|
|
79
|
+
# https://www.cubrid.org/manual/en/11.0/sql/query/select.html#limit-clause
|
|
80
|
+
# SA 2.0: _limit_clause / _offset_clause are ClauseElements, not raw ints.
|
|
81
|
+
limit_clause = select._limit_clause
|
|
82
|
+
offset_clause = select._offset_clause
|
|
83
|
+
if limit_clause is None and offset_clause is None:
|
|
84
|
+
return ""
|
|
85
|
+
elif limit_clause is None and offset_clause is not None:
|
|
86
|
+
return " \n LIMIT %s, 1073741823" % (self.process(offset_clause, **kw),)
|
|
87
|
+
elif offset_clause is not None:
|
|
88
|
+
return " \n LIMIT %s, %s" % (
|
|
89
|
+
self.process(offset_clause, **kw),
|
|
90
|
+
self.process(limit_clause, **kw),
|
|
91
|
+
)
|
|
92
|
+
else:
|
|
93
|
+
return " \n LIMIT %s" % (self.process(limit_clause, **kw),)
|
|
94
|
+
|
|
95
|
+
def update_limit_clause(self, update_stmt):
|
|
96
|
+
# https://www.cubrid.org/manual/en/11.0/sql/query/update.html
|
|
97
|
+
limit = update_stmt.kwargs.get(f"{self.dialect.name}_limit", None)
|
|
98
|
+
if limit:
|
|
99
|
+
return f"LIMIT {limit}"
|
|
100
|
+
return None
|
|
101
|
+
|
|
102
|
+
def update_tables_clause(self, update_stmt, from_table, extra_froms, **kw):
|
|
103
|
+
return ", ".join(
|
|
104
|
+
t._compiler_dispatch(self, asfrom=True, **kw) for t in [from_table] + list(extra_froms)
|
|
105
|
+
)
|
|
106
|
+
|
|
107
|
+
def update_from_clause(self, update_stmt, from_table, extra_froms, from_hints, **kw):
|
|
108
|
+
return None
|
|
109
|
+
|
|
110
|
+
def visit_on_duplicate_key_update(self, on_duplicate, **kw):
|
|
111
|
+
"""Render ON DUPLICATE KEY UPDATE clause.
|
|
112
|
+
|
|
113
|
+
CUBRID uses VALUES() function to reference inserted values,
|
|
114
|
+
identical to MySQL's pre-8.0 syntax.
|
|
115
|
+
"""
|
|
116
|
+
from sqlalchemy.sql import coercions, elements, roles, visitors
|
|
117
|
+
from sqlalchemy.sql.expression import literal_column
|
|
118
|
+
|
|
119
|
+
statement = self.current_executable
|
|
120
|
+
table = getattr(statement, "table", None)
|
|
121
|
+
if table is None:
|
|
122
|
+
return "ON DUPLICATE KEY UPDATE"
|
|
123
|
+
|
|
124
|
+
if on_duplicate._parameter_ordering:
|
|
125
|
+
parameter_ordering = [
|
|
126
|
+
coercions.expect(roles.DMLColumnRole, key)
|
|
127
|
+
for key in on_duplicate._parameter_ordering
|
|
128
|
+
]
|
|
129
|
+
ordered_keys = set(parameter_ordering)
|
|
130
|
+
cols = [table.c[key] for key in parameter_ordering if key in table.c] + [
|
|
131
|
+
c for c in table.c if c.key not in ordered_keys
|
|
132
|
+
]
|
|
133
|
+
else:
|
|
134
|
+
cols = list(table.c)
|
|
135
|
+
|
|
136
|
+
clauses = []
|
|
137
|
+
on_duplicate_update = {
|
|
138
|
+
coercions.expect_as_key(roles.DMLColumnRole, key): value
|
|
139
|
+
for key, value in on_duplicate.update.items()
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
for column in (col for col in cols if col.key in on_duplicate_update):
|
|
143
|
+
val = on_duplicate_update[column.key]
|
|
144
|
+
if coercions._is_literal(val):
|
|
145
|
+
val = elements.BindParameter(None, val, type_=column.type)
|
|
146
|
+
value_text = self.process(val.self_group(), use_schema=False)
|
|
147
|
+
else:
|
|
148
|
+
|
|
149
|
+
def replace(element, captured_column=column, **kw):
|
|
150
|
+
if isinstance(element, elements.BindParameter) and element.type._isnull:
|
|
151
|
+
return element._with_binary_element_type(captured_column.type)
|
|
152
|
+
elif (
|
|
153
|
+
isinstance(element, elements.ColumnClause)
|
|
154
|
+
and element.table is on_duplicate.inserted_alias
|
|
155
|
+
):
|
|
156
|
+
return literal_column(f"VALUES({self.preparer.quote(element.name)})")
|
|
157
|
+
else:
|
|
158
|
+
return None
|
|
159
|
+
|
|
160
|
+
val = visitors.replacement_traverse(val, {}, replace)
|
|
161
|
+
value_text = self.process(val.self_group(), use_schema=False)
|
|
162
|
+
|
|
163
|
+
name_text = self.preparer.quote(column.name)
|
|
164
|
+
clauses.append(f"{name_text} = {value_text}")
|
|
165
|
+
|
|
166
|
+
non_matching = set(on_duplicate_update) - {c.key for c in cols}
|
|
167
|
+
if non_matching:
|
|
168
|
+
from sqlalchemy import util
|
|
169
|
+
|
|
170
|
+
table_name = getattr(table, "name", "<unknown>")
|
|
171
|
+
util.warn(
|
|
172
|
+
"Additional column names not matching "
|
|
173
|
+
"any column keys in table '%s': %s"
|
|
174
|
+
% (
|
|
175
|
+
table_name,
|
|
176
|
+
(", ".join("'%s'" % c for c in non_matching)),
|
|
177
|
+
)
|
|
178
|
+
)
|
|
179
|
+
|
|
180
|
+
return f"ON DUPLICATE KEY UPDATE {', '.join(clauses)}"
|
|
181
|
+
|
|
182
|
+
def visit_merge(self, merge_stmt, **kw):
|
|
183
|
+
from sqlalchemy import exc
|
|
184
|
+
from sqlalchemy.sql import coercions, elements
|
|
185
|
+
|
|
186
|
+
target = merge_stmt._target
|
|
187
|
+
source = merge_stmt._using_source
|
|
188
|
+
on_condition = merge_stmt._on_condition
|
|
189
|
+
when_matched = merge_stmt._when_matched
|
|
190
|
+
when_not_matched = merge_stmt._when_not_matched
|
|
191
|
+
|
|
192
|
+
if target is None:
|
|
193
|
+
raise exc.CompileError("MERGE statement requires a target table")
|
|
194
|
+
if source is None:
|
|
195
|
+
raise exc.CompileError("MERGE statement requires a USING source")
|
|
196
|
+
if on_condition is None:
|
|
197
|
+
raise exc.CompileError("MERGE statement requires an ON condition")
|
|
198
|
+
if when_matched is None and when_not_matched is None:
|
|
199
|
+
raise exc.CompileError(
|
|
200
|
+
"MERGE statement must include WHEN MATCHED and/or WHEN NOT MATCHED"
|
|
201
|
+
)
|
|
202
|
+
|
|
203
|
+
target_columns = getattr(target, "c", None)
|
|
204
|
+
|
|
205
|
+
def _resolve_target_column(column_key):
|
|
206
|
+
if isinstance(column_key, str):
|
|
207
|
+
if target_columns is not None and column_key in target_columns:
|
|
208
|
+
return target_columns[column_key]
|
|
209
|
+
return None
|
|
210
|
+
if hasattr(column_key, "name"):
|
|
211
|
+
return column_key
|
|
212
|
+
return None
|
|
213
|
+
|
|
214
|
+
def _render_column_name(column_key):
|
|
215
|
+
if isinstance(column_key, str):
|
|
216
|
+
return self.preparer.quote(column_key)
|
|
217
|
+
if hasattr(column_key, "name"):
|
|
218
|
+
return self.preparer.quote(column_key.name)
|
|
219
|
+
return self.process(column_key, **kw)
|
|
220
|
+
|
|
221
|
+
def _render_value(value, target_column):
|
|
222
|
+
if coercions._is_literal(value):
|
|
223
|
+
value = elements.BindParameter(
|
|
224
|
+
None,
|
|
225
|
+
value,
|
|
226
|
+
type_=getattr(target_column, "type", None),
|
|
227
|
+
)
|
|
228
|
+
return self.process(value.self_group(), use_schema=False, **kw)
|
|
229
|
+
|
|
230
|
+
lines = [
|
|
231
|
+
f"MERGE INTO {self.process(target, asfrom=True, **kw)}",
|
|
232
|
+
f"USING {self.process(source, asfrom=True, **kw)}",
|
|
233
|
+
f"ON ({self.process(on_condition, **kw)})",
|
|
234
|
+
]
|
|
235
|
+
|
|
236
|
+
if when_matched is not None:
|
|
237
|
+
matched_values = when_matched.get("values") or {}
|
|
238
|
+
if not matched_values:
|
|
239
|
+
raise exc.CompileError(
|
|
240
|
+
"MERGE WHEN MATCHED clause requires at least one UPDATE value"
|
|
241
|
+
)
|
|
242
|
+
|
|
243
|
+
set_clauses = []
|
|
244
|
+
for column_key, value in matched_values.items():
|
|
245
|
+
target_column = _resolve_target_column(column_key)
|
|
246
|
+
set_clauses.append(
|
|
247
|
+
f"{_render_column_name(column_key)} = {_render_value(value, target_column)}"
|
|
248
|
+
)
|
|
249
|
+
|
|
250
|
+
matched_clause = f"WHEN MATCHED THEN UPDATE SET {', '.join(set_clauses)}" # nosec B608
|
|
251
|
+
matched_where = when_matched.get("where")
|
|
252
|
+
if matched_where is not None:
|
|
253
|
+
matched_clause += f" WHERE {self.process(matched_where, **kw)}"
|
|
254
|
+
|
|
255
|
+
delete_where = when_matched.get("delete_where")
|
|
256
|
+
if delete_where is not None:
|
|
257
|
+
matched_clause += f" DELETE WHERE {self.process(delete_where, **kw)}"
|
|
258
|
+
|
|
259
|
+
lines.append(matched_clause)
|
|
260
|
+
|
|
261
|
+
if when_not_matched is not None:
|
|
262
|
+
insert_columns = when_not_matched.get("columns") or []
|
|
263
|
+
insert_values = when_not_matched.get("values") or []
|
|
264
|
+
|
|
265
|
+
if not insert_columns or not insert_values:
|
|
266
|
+
raise exc.CompileError(
|
|
267
|
+
"MERGE WHEN NOT MATCHED clause requires INSERT columns and values"
|
|
268
|
+
)
|
|
269
|
+
if len(insert_columns) != len(insert_values):
|
|
270
|
+
raise exc.CompileError(
|
|
271
|
+
"MERGE WHEN NOT MATCHED INSERT columns and values must match"
|
|
272
|
+
)
|
|
273
|
+
|
|
274
|
+
rendered_columns = []
|
|
275
|
+
rendered_values = []
|
|
276
|
+
for column_key, value in zip(insert_columns, insert_values):
|
|
277
|
+
target_column = _resolve_target_column(column_key)
|
|
278
|
+
rendered_columns.append(_render_column_name(column_key))
|
|
279
|
+
rendered_values.append(_render_value(value, target_column))
|
|
280
|
+
|
|
281
|
+
not_matched_clause = (
|
|
282
|
+
"WHEN NOT MATCHED THEN INSERT "
|
|
283
|
+
f"({', '.join(rendered_columns)}) "
|
|
284
|
+
f"VALUES ({', '.join(rendered_values)})"
|
|
285
|
+
)
|
|
286
|
+
insert_where = when_not_matched.get("where")
|
|
287
|
+
if insert_where is not None:
|
|
288
|
+
not_matched_clause += f" WHERE {self.process(insert_where, **kw)}"
|
|
289
|
+
|
|
290
|
+
lines.append(not_matched_clause)
|
|
291
|
+
|
|
292
|
+
return "\n".join(lines)
|
|
293
|
+
|
|
294
|
+
def visit_replace(self, replace_stmt, **kw):
|
|
295
|
+
text = super().visit_insert(replace_stmt, **kw)
|
|
296
|
+
if "INSERT INTO" in text:
|
|
297
|
+
return text.replace("INSERT INTO", "REPLACE INTO", 1)
|
|
298
|
+
if text.startswith("INSERT"):
|
|
299
|
+
return "REPLACE" + text[len("INSERT") :]
|
|
300
|
+
return text
|
|
301
|
+
|
|
302
|
+
|
|
303
|
+
class CubridDDLCompiler(compiler.DDLCompiler):
|
|
304
|
+
"""DDLCompiler subclass for CUBRID.
|
|
305
|
+
|
|
306
|
+
Handles AUTO_INCREMENT for autoincrement columns and column defaults.
|
|
307
|
+
"""
|
|
308
|
+
|
|
309
|
+
def get_column_specification(self, column, **kw):
|
|
310
|
+
"""Build column DDL specification.
|
|
311
|
+
|
|
312
|
+
CUBRID syntax::
|
|
313
|
+
|
|
314
|
+
column_name TYPE [NOT NULL] [AUTO_INCREMENT] [DEFAULT value]
|
|
315
|
+
"""
|
|
316
|
+
colspec = [
|
|
317
|
+
self.preparer.format_column(column),
|
|
318
|
+
self.dialect.type_compiler_instance.process(column.type, type_expression=column),
|
|
319
|
+
]
|
|
320
|
+
|
|
321
|
+
if not column.nullable:
|
|
322
|
+
colspec.append("NOT NULL")
|
|
323
|
+
|
|
324
|
+
if (
|
|
325
|
+
column.table is not None
|
|
326
|
+
and column is column.table._autoincrement_column
|
|
327
|
+
and (column.server_default is None)
|
|
328
|
+
):
|
|
329
|
+
colspec.append("AUTO_INCREMENT")
|
|
330
|
+
else:
|
|
331
|
+
default = self.get_column_default_string(column)
|
|
332
|
+
if default is not None:
|
|
333
|
+
colspec.append("DEFAULT " + default)
|
|
334
|
+
|
|
335
|
+
if column.comment is not None:
|
|
336
|
+
literal = self.sql_compiler.render_literal_value(
|
|
337
|
+
column.comment,
|
|
338
|
+
sqltypes.String(),
|
|
339
|
+
)
|
|
340
|
+
colspec.append("COMMENT " + literal)
|
|
341
|
+
|
|
342
|
+
return " ".join(colspec)
|
|
343
|
+
|
|
344
|
+
def post_create_table(self, table):
|
|
345
|
+
table_opts = []
|
|
346
|
+
if table.comment is not None:
|
|
347
|
+
literal = self.sql_compiler.render_literal_value(
|
|
348
|
+
table.comment,
|
|
349
|
+
sqltypes.String(),
|
|
350
|
+
)
|
|
351
|
+
table_opts.append(f"\n COMMENT = {literal}")
|
|
352
|
+
return "".join(table_opts)
|
|
353
|
+
|
|
354
|
+
def visit_set_table_comment(self, create, **kw):
|
|
355
|
+
return "ALTER TABLE %s COMMENT = %s" % (
|
|
356
|
+
self.preparer.format_table(create.element),
|
|
357
|
+
self.sql_compiler.render_literal_value(
|
|
358
|
+
create.element.comment,
|
|
359
|
+
sqltypes.String(),
|
|
360
|
+
),
|
|
361
|
+
)
|
|
362
|
+
|
|
363
|
+
def visit_drop_table_comment(self, drop, **kw):
|
|
364
|
+
return "ALTER TABLE %s COMMENT = ''" % (self.preparer.format_table(drop.element),)
|
|
365
|
+
|
|
366
|
+
def visit_set_column_comment(self, create, **kw):
|
|
367
|
+
return "ALTER TABLE %s MODIFY %s %s COMMENT %s" % (
|
|
368
|
+
self.preparer.format_table(create.element.table),
|
|
369
|
+
self.preparer.format_column(create.element),
|
|
370
|
+
self.dialect.type_compiler_instance.process(
|
|
371
|
+
create.element.type,
|
|
372
|
+
type_expression=create.element,
|
|
373
|
+
),
|
|
374
|
+
self.sql_compiler.render_literal_value(
|
|
375
|
+
create.element.comment,
|
|
376
|
+
sqltypes.String(),
|
|
377
|
+
),
|
|
378
|
+
)
|
|
379
|
+
|
|
380
|
+
|
|
381
|
+
class CubridTypeCompiler(compiler.GenericTypeCompiler):
|
|
382
|
+
"""TypeCompiler for CUBRID data types."""
|
|
383
|
+
|
|
384
|
+
def _get(self, key, type_, kw):
|
|
385
|
+
return kw.get(key, getattr(type_, key, None))
|
|
386
|
+
|
|
387
|
+
def visit_BOOLEAN(self, type_, **kw):
|
|
388
|
+
# CUBRID has no native BOOLEAN; map to SMALLINT.
|
|
389
|
+
return self.visit_SMALLINT(type_)
|
|
390
|
+
|
|
391
|
+
def visit_NUMERIC(self, type_, **kw):
|
|
392
|
+
if type_.precision is None:
|
|
393
|
+
return "NUMERIC"
|
|
394
|
+
elif type_.scale is None:
|
|
395
|
+
return f"NUMERIC({type_.precision})"
|
|
396
|
+
else:
|
|
397
|
+
return f"NUMERIC({type_.precision}, {type_.scale})"
|
|
398
|
+
|
|
399
|
+
def visit_DECIMAL(self, type_, **kw):
|
|
400
|
+
if type_.precision is None:
|
|
401
|
+
return "DECIMAL"
|
|
402
|
+
elif type_.scale is None:
|
|
403
|
+
return f"DECIMAL({type_.precision})"
|
|
404
|
+
else:
|
|
405
|
+
return f"DECIMAL({type_.precision}, {type_.scale})"
|
|
406
|
+
|
|
407
|
+
def visit_FLOAT(self, type_, **kw):
|
|
408
|
+
if type_.precision is None:
|
|
409
|
+
return "FLOAT"
|
|
410
|
+
else:
|
|
411
|
+
return f"FLOAT({type_.precision})"
|
|
412
|
+
|
|
413
|
+
def visit_DOUBLE(self, type_, **kw):
|
|
414
|
+
return "DOUBLE"
|
|
415
|
+
|
|
416
|
+
def visit_MONETARY(self, type_, **kw):
|
|
417
|
+
return "MONETARY"
|
|
418
|
+
|
|
419
|
+
def visit_SMALLINT(self, type_, **kw):
|
|
420
|
+
return "SMALLINT"
|
|
421
|
+
|
|
422
|
+
def visit_BIGINT(self, type_, **kw):
|
|
423
|
+
return "BIGINT"
|
|
424
|
+
|
|
425
|
+
def visit_BIT(self, type_, **kw):
|
|
426
|
+
if type_.varying:
|
|
427
|
+
compiled = "BIT VARYING"
|
|
428
|
+
if type_.length is not None:
|
|
429
|
+
compiled += f"({type_.length})"
|
|
430
|
+
else:
|
|
431
|
+
compiled = f"BIT({type_.length})"
|
|
432
|
+
return compiled
|
|
433
|
+
|
|
434
|
+
def visit_datetime(self, type_, **kw):
|
|
435
|
+
return "DATETIME"
|
|
436
|
+
|
|
437
|
+
def visit_DATETIME(self, type_, **kw):
|
|
438
|
+
return "DATETIME"
|
|
439
|
+
|
|
440
|
+
def visit_DATE(self, type_, **kw):
|
|
441
|
+
return "DATE"
|
|
442
|
+
|
|
443
|
+
def visit_TIME(self, type_, **kw):
|
|
444
|
+
return "TIME"
|
|
445
|
+
|
|
446
|
+
def visit_TIMESTAMP(self, type_, **kw):
|
|
447
|
+
return "TIMESTAMP"
|
|
448
|
+
|
|
449
|
+
def visit_VARCHAR(self, type_, **kw):
|
|
450
|
+
if hasattr(type_, "national") and type_.national:
|
|
451
|
+
return self.visit_NVARCHAR(type_)
|
|
452
|
+
elif type_.length:
|
|
453
|
+
return "VARCHAR(%d)" % type_.length
|
|
454
|
+
else:
|
|
455
|
+
return "VARCHAR(4096)"
|
|
456
|
+
|
|
457
|
+
def visit_CHAR(self, type_, **kw):
|
|
458
|
+
if hasattr(type_, "national") and type_.national:
|
|
459
|
+
return self.visit_NCHAR(type_)
|
|
460
|
+
elif type_.length:
|
|
461
|
+
return f"CHAR({type_.length})"
|
|
462
|
+
else:
|
|
463
|
+
return "CHAR"
|
|
464
|
+
|
|
465
|
+
def visit_NVARCHAR(self, type_, **kw):
|
|
466
|
+
if type_.length:
|
|
467
|
+
return f"NCHAR VARYING({type_.length})"
|
|
468
|
+
else:
|
|
469
|
+
return "NCHAR VARYING(4096)"
|
|
470
|
+
|
|
471
|
+
def visit_NCHAR(self, type_, **kw):
|
|
472
|
+
if type_.length:
|
|
473
|
+
return f"NCHAR({type_.length})"
|
|
474
|
+
else:
|
|
475
|
+
return "NCHAR"
|
|
476
|
+
|
|
477
|
+
def visit_OBJECT(self, type_, **kw):
|
|
478
|
+
return "OBJECT"
|
|
479
|
+
|
|
480
|
+
def visit_large_binary(self, type_, **kw):
|
|
481
|
+
return self.visit_BLOB(type_)
|
|
482
|
+
|
|
483
|
+
def visit_text(self, type_, **kw):
|
|
484
|
+
return self.visit_STRING(type_)
|
|
485
|
+
|
|
486
|
+
def visit_BLOB(self, type_, **kw):
|
|
487
|
+
return "BLOB"
|
|
488
|
+
|
|
489
|
+
def visit_CLOB(self, type_, **kw):
|
|
490
|
+
return "CLOB"
|
|
491
|
+
|
|
492
|
+
def visit_STRING(self, type_, **kw):
|
|
493
|
+
return "STRING"
|
|
494
|
+
|
|
495
|
+
def visit_SET(self, type_, **kw):
|
|
496
|
+
return self._visit_collection(type_, "SET")
|
|
497
|
+
|
|
498
|
+
def visit_MULTISET(self, type_, **kw):
|
|
499
|
+
return self._visit_collection(type_, "MULTISET")
|
|
500
|
+
|
|
501
|
+
def visit_SEQUENCE(self, type_, **kw):
|
|
502
|
+
return self._visit_collection(type_, "SEQUENCE")
|
|
503
|
+
|
|
504
|
+
def _visit_collection(self, type_, collection_type, **kw):
|
|
505
|
+
"""Compile CUBRID collection types (SET, MULTISET, LIST/SEQUENCE).
|
|
506
|
+
|
|
507
|
+
See: https://www.cubrid.org/manual/en/11.0/sql/datatype.html#collection-types
|
|
508
|
+
"""
|
|
509
|
+
parts = []
|
|
510
|
+
for value in type_._ddl_values:
|
|
511
|
+
if isinstance(value, str):
|
|
512
|
+
parts.append(value)
|
|
513
|
+
else:
|
|
514
|
+
parts.append(value.__visit_name__)
|
|
515
|
+
return f"{collection_type}({','.join(parts)})"
|