ydb-sqlalchemy 0.0.1b4__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.
test/test_inspect.py ADDED
@@ -0,0 +1,34 @@
1
+ import sqlalchemy as sa
2
+ from sqlalchemy import Column, Integer, Numeric, Table, Unicode
3
+ from sqlalchemy.testing.fixtures import TablesTest
4
+
5
+
6
+ class TestInspection(TablesTest):
7
+ @classmethod
8
+ def define_tables(cls, metadata):
9
+ Table(
10
+ "test",
11
+ metadata,
12
+ Column("id", Integer, primary_key=True, nullable=False),
13
+ Column("value", Unicode),
14
+ Column("num", Numeric(22, 9)),
15
+ )
16
+
17
+ def test_get_columns(self, connection):
18
+ inspect = sa.inspect(connection)
19
+
20
+ columns = inspect.get_columns("test")
21
+ for c in columns:
22
+ c["type"] = type(c["type"])
23
+
24
+ assert columns == [
25
+ {"name": "id", "type": sa.INTEGER, "nullable": False, "default": None},
26
+ {"name": "value", "type": sa.TEXT, "nullable": True, "default": None},
27
+ {"name": "num", "type": sa.DECIMAL, "nullable": True, "default": None},
28
+ ]
29
+
30
+ def test_has_table(self, connection):
31
+ inspect = sa.inspect(connection)
32
+
33
+ assert inspect.has_table("test")
34
+ assert not inspect.has_table("foo")
test/test_suite.py ADDED
@@ -0,0 +1,553 @@
1
+ import ctypes
2
+
3
+ import pytest
4
+ import sqlalchemy as sa
5
+ import sqlalchemy.testing.suite.test_types
6
+ from sqlalchemy.testing import is_false, is_true
7
+ from sqlalchemy.testing.suite import * # noqa: F401, F403
8
+ from sqlalchemy.testing.suite import (
9
+ Column,
10
+ Integer,
11
+ MetaData,
12
+ String,
13
+ Table,
14
+ column,
15
+ config,
16
+ eq_,
17
+ exists,
18
+ fixtures,
19
+ func,
20
+ inspect,
21
+ literal_column,
22
+ provide_metadata,
23
+ requirements,
24
+ select,
25
+ testing,
26
+ union,
27
+ )
28
+ from sqlalchemy.testing.suite.test_ddl import (
29
+ LongNameBlowoutTest as _LongNameBlowoutTest,
30
+ )
31
+ from sqlalchemy.testing.suite.test_dialect import (
32
+ DifficultParametersTest as _DifficultParametersTest,
33
+ )
34
+ from sqlalchemy.testing.suite.test_dialect import EscapingTest as _EscapingTest
35
+ from sqlalchemy.testing.suite.test_insert import (
36
+ InsertBehaviorTest as _InsertBehaviorTest,
37
+ )
38
+ from sqlalchemy.testing.suite.test_reflection import (
39
+ ComponentReflectionTest as _ComponentReflectionTest,
40
+ )
41
+ from sqlalchemy.testing.suite.test_reflection import (
42
+ ComponentReflectionTestExtra as _ComponentReflectionTestExtra,
43
+ )
44
+ from sqlalchemy.testing.suite.test_reflection import (
45
+ CompositeKeyReflectionTest as _CompositeKeyReflectionTest,
46
+ )
47
+ from sqlalchemy.testing.suite.test_reflection import HasIndexTest as _HasIndexTest
48
+ from sqlalchemy.testing.suite.test_reflection import HasTableTest as _HasTableTest
49
+ from sqlalchemy.testing.suite.test_reflection import (
50
+ QuotedNameArgumentTest as _QuotedNameArgumentTest,
51
+ )
52
+ from sqlalchemy.testing.suite.test_results import RowFetchTest as _RowFetchTest
53
+ from sqlalchemy.testing.suite.test_select import ExistsTest as _ExistsTest
54
+ from sqlalchemy.testing.suite.test_select import (
55
+ FetchLimitOffsetTest as _FetchLimitOffsetTest,
56
+ )
57
+ from sqlalchemy.testing.suite.test_select import JoinTest as _JoinTest
58
+ from sqlalchemy.testing.suite.test_select import LikeFunctionsTest as _LikeFunctionsTest
59
+ from sqlalchemy.testing.suite.test_select import OrderByLabelTest as _OrderByLabelTest
60
+ from sqlalchemy.testing.suite.test_types import BinaryTest as _BinaryTest
61
+ from sqlalchemy.testing.suite.test_types import DateTest as _DateTest
62
+ from sqlalchemy.testing.suite.test_types import (
63
+ DateTimeCoercedToDateTimeTest as _DateTimeCoercedToDateTimeTest,
64
+ )
65
+ from sqlalchemy.testing.suite.test_types import (
66
+ DateTimeMicrosecondsTest as _DateTimeMicrosecondsTest,
67
+ )
68
+ from sqlalchemy.testing.suite.test_types import DateTimeTest as _DateTimeTest
69
+ from sqlalchemy.testing.suite.test_types import IntegerTest as _IntegerTest
70
+ from sqlalchemy.testing.suite.test_types import JSONTest as _JSONTest
71
+ from sqlalchemy.testing.suite.test_types import NativeUUIDTest as _NativeUUIDTest
72
+ from sqlalchemy.testing.suite.test_types import NumericTest as _NumericTest
73
+ from sqlalchemy.testing.suite.test_types import StringTest as _StringTest
74
+ from sqlalchemy.testing.suite.test_types import (
75
+ TimeMicrosecondsTest as _TimeMicrosecondsTest,
76
+ )
77
+ from sqlalchemy.testing.suite.test_types import (
78
+ TimestampMicrosecondsTest as _TimestampMicrosecondsTest,
79
+ )
80
+ from sqlalchemy.testing.suite.test_types import TimeTest as _TimeTest
81
+ from sqlalchemy.testing.suite.test_types import TrueDivTest as _TrueDivTest
82
+
83
+ from ydb_sqlalchemy.sqlalchemy import types as ydb_sa_types
84
+
85
+ test_types_suite = sqlalchemy.testing.suite.test_types
86
+ col_creator = test_types_suite.Column
87
+
88
+
89
+ def column_getter(*args, **kwargs):
90
+ col = col_creator(*args, **kwargs)
91
+ if col.name == "x":
92
+ col.primary_key = True
93
+ return col
94
+
95
+
96
+ test_types_suite.Column = column_getter
97
+
98
+
99
+ class ComponentReflectionTest(_ComponentReflectionTest):
100
+ def _check_list(self, result, exp, req_keys=None, msg=None):
101
+ try:
102
+ return super()._check_list(result, exp, req_keys, msg)
103
+ except AssertionError as err:
104
+ err_info = err.args[0]
105
+ if "nullable" in err_info:
106
+ return "We changed nullable in define_reflected_tables method so won't check it."
107
+ if "constrained_columns" in err_info and "contains one more item: 'data'" in err_info:
108
+ return "We changed primary_keys in define_reflected_tables method so this will fail"
109
+ raise
110
+
111
+ @classmethod
112
+ def define_reflected_tables(cls, metadata, schema):
113
+ Table(
114
+ "users",
115
+ metadata,
116
+ Column("user_id", sa.INT, primary_key=True),
117
+ Column("test1", sa.CHAR(5)),
118
+ Column("test2", sa.Float()),
119
+ Column("parent_user_id", sa.Integer),
120
+ schema=schema,
121
+ test_needs_fk=True,
122
+ )
123
+
124
+ Table(
125
+ "dingalings",
126
+ metadata,
127
+ Column("dingaling_id", sa.Integer, primary_key=True),
128
+ Column("address_id", sa.Integer),
129
+ Column("id_user", sa.Integer),
130
+ Column("data", sa.String(30)),
131
+ schema=schema,
132
+ test_needs_fk=True,
133
+ )
134
+
135
+ Table(
136
+ "email_addresses",
137
+ metadata,
138
+ Column("address_id", sa.Integer, primary_key=True),
139
+ Column("remote_user_id", sa.Integer),
140
+ Column("email_address", sa.String(20)),
141
+ schema=schema,
142
+ test_needs_fk=True,
143
+ )
144
+
145
+ Table(
146
+ "comment_test",
147
+ metadata,
148
+ Column("id", sa.Integer, primary_key=True, comment="id comment"),
149
+ Column("data", sa.String(20), comment="data % comment"),
150
+ Column("d2", sa.String(20), comment=r"""Comment types type speedily ' " \ '' Fun!"""),
151
+ schema=schema,
152
+ comment=r"""the test % ' " \ table comment""",
153
+ )
154
+
155
+ Table(
156
+ "no_constraints",
157
+ metadata,
158
+ Column("data", sa.String(20), primary_key=True, nullable=True),
159
+ schema=schema,
160
+ )
161
+
162
+ @pytest.mark.skip("views unsupported")
163
+ def test_get_view_names(self, connection, use_schema):
164
+ pass
165
+
166
+ def test_metadata(self, connection, **kwargs):
167
+ m = MetaData()
168
+ m.reflect(connection, resolve_fks=False)
169
+
170
+ insp = inspect(connection)
171
+ tables = insp.get_table_names()
172
+ eq_(sorted(m.tables), sorted(tables))
173
+
174
+
175
+ class CompositeKeyReflectionTest(_CompositeKeyReflectionTest):
176
+ @classmethod
177
+ def define_tables(cls, metadata):
178
+ Table(
179
+ "tb1",
180
+ metadata,
181
+ Column("id", Integer),
182
+ Column("attr", Integer),
183
+ Column("name", sa.VARCHAR(20)),
184
+ # named pk unsupported
185
+ sa.PrimaryKeyConstraint("name", "id", "attr"),
186
+ schema=None,
187
+ test_needs_fk=True,
188
+ )
189
+
190
+
191
+ class ComponentReflectionTestExtra(_ComponentReflectionTestExtra):
192
+ def _type_round_trip(self, connection, metadata, *types):
193
+ t = Table(
194
+ "t",
195
+ metadata,
196
+ # table without pk unsupported
197
+ *[Column("t%d" % i, type_, primary_key=True) for i, type_ in enumerate(types)],
198
+ )
199
+ t.create(connection)
200
+ return [c["type"] for c in inspect(connection).get_columns("t")]
201
+
202
+ @pytest.mark.skip("YDB: Only Decimal(22,9) is supported for table columns")
203
+ def test_numeric_reflection(self):
204
+ pass
205
+
206
+ @pytest.mark.skip("TODO: varchar with length unsupported")
207
+ def test_varchar_reflection(self):
208
+ pass
209
+
210
+ @testing.requires.table_reflection
211
+ def test_nullable_reflection(self, connection, metadata):
212
+ t = Table(
213
+ "t",
214
+ metadata,
215
+ # table without pk unsupported
216
+ Column("a", Integer, nullable=True, primary_key=True),
217
+ Column("b", Integer, nullable=False, primary_key=True),
218
+ )
219
+ t.create(connection)
220
+ eq_(
221
+ {col["name"]: col["nullable"] for col in inspect(connection).get_columns("t")},
222
+ {"a": True, "b": False},
223
+ )
224
+
225
+
226
+ class HasTableTest(_HasTableTest):
227
+ @classmethod
228
+ def define_tables(cls, metadata):
229
+ Table(
230
+ "test_table",
231
+ metadata,
232
+ Column("id", Integer, primary_key=True),
233
+ Column("data", String(50)),
234
+ )
235
+
236
+ def test_has_table_cache(self, metadata):
237
+ insp = inspect(config.db)
238
+ is_true(insp.has_table("test_table"))
239
+ # table without pk unsupported
240
+ nt = Table("new_table", metadata, Column("col", Integer, primary_key=True))
241
+ is_false(insp.has_table("new_table"))
242
+ nt.create(config.db)
243
+ try:
244
+ is_false(insp.has_table("new_table"))
245
+ insp.clear_cache()
246
+ is_true(insp.has_table("new_table"))
247
+ finally:
248
+ nt.drop(config.db)
249
+
250
+
251
+ @pytest.mark.skip("CREATE INDEX syntax unsupported")
252
+ class HasIndexTest(_HasIndexTest):
253
+ pass
254
+
255
+
256
+ @pytest.mark.skip("quotes unsupported in table names")
257
+ class QuotedNameArgumentTest(_QuotedNameArgumentTest):
258
+ pass
259
+
260
+
261
+ class IntegerTest(_IntegerTest):
262
+ @pytest.mark.skip("YQL doesn't support select with where without from")
263
+ def test_huge_int_auto_accommodation(self, connection, intvalue):
264
+ pass
265
+
266
+
267
+ @pytest.mark.skip("TODO: fix & skip those tests - add Double/Decimal support. see #12")
268
+ class NumericTest(_NumericTest):
269
+ # SqlAlchemy maybe eat Decimal and throw Double
270
+ pass
271
+
272
+
273
+ @pytest.mark.skip("TODO: see issue #13")
274
+ class BinaryTest(_BinaryTest):
275
+ pass
276
+
277
+
278
+ class TrueDivTest(_TrueDivTest):
279
+ @pytest.mark.skip("Unsupported builtin: FLOOR")
280
+ def test_floordiv_numeric(self, connection, left, right, expected):
281
+ pass
282
+
283
+ @pytest.mark.skip("Truediv unsupported for int")
284
+ def test_truediv_integer(self, connection, left, right, expected):
285
+ pass
286
+
287
+ @pytest.mark.skip("Truediv unsupported for int")
288
+ def test_truediv_integer_bound(self, connection):
289
+ pass
290
+
291
+ @pytest.mark.skip("Numeric is not Decimal")
292
+ def test_truediv_numeric(self):
293
+ # SqlAlchemy maybe eat Decimal and throw Double
294
+ pass
295
+
296
+ @testing.combinations(("6.25", "2.5", 2.5), argnames="left, right, expected")
297
+ def test_truediv_float(self, connection, left, right, expected):
298
+ eq_(
299
+ connection.scalar(select(literal_column(left, type_=sa.Float()) / literal_column(right, type_=sa.Float()))),
300
+ expected,
301
+ )
302
+
303
+
304
+ class ExistsTest(_ExistsTest):
305
+ """
306
+ YDB says: Filtering is not allowed without FROM so rewrite queries
307
+ """
308
+
309
+ def test_select_exists(self, connection):
310
+ stuff = self.tables.stuff
311
+ eq_(connection.execute(select(exists().where(stuff.c.data == "some data"))).fetchall(), [(True,)])
312
+
313
+ def test_select_exists_false(self, connection):
314
+ stuff = self.tables.stuff
315
+ eq_(connection.execute(select(exists().where(stuff.c.data == "no data"))).fetchall(), [(False,)])
316
+
317
+
318
+ class LikeFunctionsTest(_LikeFunctionsTest):
319
+ @testing.requires.regexp_match
320
+ def test_not_regexp_match(self):
321
+ col = self.tables.some_table.c.data
322
+ # YDB fetch NULL columns too
323
+ self._test(~col.regexp_match("a.cde"), {2, 3, 4, 7, 8, 10, 11})
324
+
325
+
326
+ class EscapingTest(_EscapingTest):
327
+ @provide_metadata
328
+ def test_percent_sign_round_trip(self):
329
+ """test that the DBAPI accommodates for escaped / nonescaped
330
+ percent signs in a way that matches the compiler
331
+
332
+ """
333
+ m = self.metadata
334
+ # table without pk unsupported
335
+ t = Table("t", m, Column("data", String(50), primary_key=True))
336
+ t.create(config.db)
337
+ with config.db.begin() as conn:
338
+ conn.execute(t.insert(), dict(data="some % value"))
339
+ conn.execute(t.insert(), dict(data="some %% other value"))
340
+
341
+ eq_(conn.scalar(select(t.c.data).where(t.c.data == literal_column("'some % value'"))), "some % value")
342
+
343
+ eq_(
344
+ conn.scalar(select(t.c.data).where(t.c.data == literal_column("'some %% other value'"))),
345
+ "some %% other value",
346
+ )
347
+
348
+
349
+ @pytest.mark.skip("unsupported tricky names for columns")
350
+ class DifficultParametersTest(_DifficultParametersTest):
351
+ pass
352
+
353
+
354
+ @pytest.mark.skip("JOIN ON expression must be a conjunction of equality predicates")
355
+ class JoinTest(_JoinTest):
356
+ pass
357
+
358
+
359
+ class OrderByLabelTest(_OrderByLabelTest):
360
+ def test_composed_multiple(self):
361
+ table = self.tables.some_table
362
+ lx = (table.c.x + table.c.y).label("lx")
363
+ ly = (table.c.q + table.c.p).label("ly") # unknown builtin: lower
364
+ self._assert_result(
365
+ select(lx, ly).order_by(lx, ly.desc()),
366
+ [(3, "q1p3"), (5, "q2p2"), (7, "q3p1")],
367
+ )
368
+
369
+ @testing.requires.group_by_complex_expression
370
+ def test_group_by_composed(self):
371
+ """
372
+ YDB says: column `some_table.x` must either be a key column in GROUP BY
373
+ or it should be used in aggregation function
374
+ """
375
+ table = self.tables.some_table
376
+ expr = (table.c.x + table.c.y).label("lx")
377
+ stmt = select(func.count(table.c.id), column("lx")).group_by(expr).order_by(column("lx"))
378
+ self._assert_result(stmt, [(1, 3), (1, 5), (1, 7)])
379
+
380
+
381
+ class FetchLimitOffsetTest(_FetchLimitOffsetTest):
382
+ def test_limit_render_multiple_times(self, connection):
383
+ """
384
+ YQL does not support scalar subquery, so test was refiled with simple subquery
385
+ """
386
+ table = self.tables.some_table
387
+ stmt = select(table.c.id).limit(1).subquery()
388
+
389
+ u = union(select(stmt), select(stmt)).subquery().select()
390
+
391
+ self._assert_result(
392
+ connection,
393
+ u,
394
+ [
395
+ (1,),
396
+ (1,),
397
+ ],
398
+ )
399
+
400
+
401
+ class InsertBehaviorTest(_InsertBehaviorTest):
402
+ @pytest.mark.skip("autoincrement unsupported")
403
+ def test_insert_from_select_autoinc(self, connection):
404
+ pass
405
+
406
+ @pytest.mark.skip("autoincrement unsupported")
407
+ def test_insert_from_select_autoinc_no_rows(self, connection):
408
+ pass
409
+
410
+ @pytest.mark.skip("implicit PK values unsupported")
411
+ def test_no_results_for_non_returning_insert(self, connection):
412
+ pass
413
+
414
+
415
+ class DateTest(_DateTest):
416
+ run_dispose_bind = "once"
417
+
418
+
419
+ class DateTimeMicrosecondsTest(_DateTimeMicrosecondsTest):
420
+ run_dispose_bind = "once"
421
+
422
+
423
+ class DateTimeTest(_DateTimeTest):
424
+ run_dispose_bind = "once"
425
+
426
+
427
+ class TimestampMicrosecondsTest(_TimestampMicrosecondsTest):
428
+ run_dispose_bind = "once"
429
+
430
+
431
+ @pytest.mark.skip("unsupported Time data type")
432
+ class TimeTest(_TimeTest):
433
+ pass
434
+
435
+
436
+ class JSONTest(_JSONTest):
437
+ @classmethod
438
+ def define_tables(cls, metadata):
439
+ Table(
440
+ "data_table",
441
+ metadata,
442
+ Column("id", Integer, primary_key=True, default=1),
443
+ Column("name", String(30), primary_key=True, nullable=False),
444
+ Column("data", cls.datatype, nullable=False),
445
+ Column("nulldata", cls.datatype(none_as_null=True)),
446
+ )
447
+
448
+ def _json_value_insert(self, connection, datatype, value, data_element):
449
+ if datatype == "float" and value is not None:
450
+ # As python's float is stored as C double, it needs to be shrank
451
+ value = ctypes.c_float(value).value
452
+ return super()._json_value_insert(connection, datatype, value, data_element)
453
+
454
+
455
+ class StringTest(_StringTest):
456
+ @requirements.unbounded_varchar
457
+ def test_nolength_string(self):
458
+ metadata = MetaData()
459
+ # table without pk unsupported
460
+ foo = Table("foo", metadata, Column("one", String, primary_key=True))
461
+ foo.create(config.db)
462
+ foo.drop(config.db)
463
+
464
+
465
+ class ContainerTypesTest(fixtures.TablesTest):
466
+ @classmethod
467
+ def define_tables(cls, metadata):
468
+ Table(
469
+ "container_types_test",
470
+ metadata,
471
+ Column("id", Integer),
472
+ sa.PrimaryKeyConstraint("id"),
473
+ schema=None,
474
+ test_needs_fk=True,
475
+ )
476
+
477
+ def test_ARRAY_bind_variable(self, connection):
478
+ table = self.tables.container_types_test
479
+
480
+ connection.execute(sa.insert(table).values([{"id": 1}, {"id": 2}, {"id": 3}]))
481
+
482
+ stmt = select(table.c.id).where(table.c.id.in_(sa.bindparam("id", type_=sa.ARRAY(sa.Integer))))
483
+
484
+ eq_(connection.execute(stmt, {"id": [1, 2]}).fetchall(), [(1,), (2,)])
485
+
486
+ def test_list_type_bind_variable(self, connection):
487
+ table = self.tables.container_types_test
488
+
489
+ connection.execute(sa.insert(table).values([{"id": 1}, {"id": 2}, {"id": 3}]))
490
+
491
+ stmt = select(table.c.id).where(table.c.id.in_(sa.bindparam("id", type_=ydb_sa_types.ListType(sa.Integer))))
492
+
493
+ eq_(connection.execute(stmt, {"id": [1, 2]}).fetchall(), [(1,), (2,)])
494
+
495
+ def test_struct_type_bind_variable(self, connection):
496
+ table = self.tables.container_types_test
497
+
498
+ connection.execute(sa.insert(table).values([{"id": 1}, {"id": 2}, {"id": 3}]))
499
+
500
+ stmt = select(table.c.id).where(
501
+ table.c.id
502
+ == sa.text(":struct.id").bindparams(
503
+ sa.bindparam("struct", type_=ydb_sa_types.StructType({"id": sa.Integer})),
504
+ )
505
+ )
506
+
507
+ eq_(connection.scalar(stmt, {"struct": {"id": 1}}), 1)
508
+
509
+ def test_from_as_table(self, connection):
510
+ table = self.tables.container_types_test
511
+
512
+ connection.execute(
513
+ sa.insert(table).from_select(
514
+ ["id"],
515
+ sa.select(sa.column("id")).select_from(
516
+ sa.func.as_table(
517
+ sa.bindparam(
518
+ "data",
519
+ value=[{"id": 1}, {"id": 2}, {"id": 3}],
520
+ type_=ydb_sa_types.ListType(ydb_sa_types.StructType({"id": sa.Integer})),
521
+ )
522
+ )
523
+ ),
524
+ )
525
+ )
526
+
527
+ eq_(connection.execute(sa.select(table)).fetchall(), [(1,), (2,), (3,)])
528
+
529
+
530
+ @pytest.mark.skip("uuid unsupported for columns")
531
+ class NativeUUIDTest(_NativeUUIDTest):
532
+ pass
533
+
534
+
535
+ @pytest.mark.skip("unsupported Time data type")
536
+ class TimeMicrosecondsTest(_TimeMicrosecondsTest):
537
+ pass
538
+
539
+
540
+ @pytest.mark.skip("unsupported coerce dates from datetime")
541
+ class DateTimeCoercedToDateTimeTest(_DateTimeCoercedToDateTimeTest):
542
+ pass
543
+
544
+
545
+ @pytest.mark.skip("named constraints unsupported")
546
+ class LongNameBlowoutTest(_LongNameBlowoutTest):
547
+ pass
548
+
549
+
550
+ class RowFetchTest(_RowFetchTest):
551
+ @pytest.mark.skip("scalar subquery unsupported")
552
+ def test_row_w_scalar_select(self, connection):
553
+ pass
test_dbapi/__init__.py ADDED
File without changes