ydb-sqlalchemy 0.1.14__py2.py3-none-any.whl → 0.1.15__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_core.py CHANGED
@@ -228,6 +228,15 @@ class TestTypes(TablesTest):
228
228
  Column("float", sa.Float),
229
229
  Column("bool", sa.Boolean),
230
230
  )
231
+ Table(
232
+ "test_all_binary_types",
233
+ metadata,
234
+ Column("id", sa.Integer, primary_key=True),
235
+ Column("bin", sa.BINARY),
236
+ Column("large_bin", sa.LargeBinary),
237
+ Column("blob", sa.BLOB),
238
+ Column("custom_bin", types.Binary),
239
+ )
231
240
  Table(
232
241
  "test_datetime_types",
233
242
  metadata,
@@ -255,6 +264,21 @@ class TestTypes(TablesTest):
255
264
  row = connection.execute(sa.select(table)).fetchone()
256
265
  assert row == (42, "Hello World!", 3.5, True)
257
266
 
267
+ def test_all_binary_types(self, connection):
268
+ table = self.tables.test_all_binary_types
269
+ data = {
270
+ "id": 1,
271
+ "bin": b"binary",
272
+ "large_bin": b"large_binary",
273
+ "blob": b"blob",
274
+ "custom_bin": b"custom_binary",
275
+ }
276
+ statement = sa.insert(table).values(**data)
277
+ connection.execute(statement)
278
+
279
+ row = connection.execute(sa.select(table)).fetchone()
280
+ assert row == (1, b"binary", b"large_binary", b"blob", b"custom_binary")
281
+
258
282
  def test_integer_types(self, connection):
259
283
  stmt = sa.select(
260
284
  sa.func.FormatType(sa.func.TypeOf(sa.bindparam("p_uint8", 8, types.UInt8))),
@@ -1112,3 +1136,114 @@ class TestTablePathPrefix(TablesTest):
1112
1136
  metadata.reflect(reflection_engine)
1113
1137
 
1114
1138
  assert "nested_dir/table" in metadata.tables
1139
+
1140
+
1141
+ class TestAsTable(TablesTest):
1142
+ __backend__ = True
1143
+
1144
+ @classmethod
1145
+ def define_tables(cls, metadata):
1146
+ Table(
1147
+ "test_as_table",
1148
+ metadata,
1149
+ Column("id", Integer, primary_key=True),
1150
+ Column("val_int", Integer, nullable=True),
1151
+ Column("val_str", String, nullable=True),
1152
+ )
1153
+
1154
+ def test_upsert_as_table(self, connection):
1155
+ table = self.tables.test_as_table
1156
+
1157
+ input_data = [
1158
+ {"id": 1, "val_int": 10, "val_str": "a"},
1159
+ {"id": 2, "val_int": None, "val_str": "b"},
1160
+ {"id": 3, "val_int": 30, "val_str": None},
1161
+ ]
1162
+
1163
+ struct_type = types.StructType(
1164
+ {
1165
+ "id": Integer,
1166
+ "val_int": types.Optional(Integer),
1167
+ "val_str": types.Optional(String),
1168
+ }
1169
+ )
1170
+ list_type = types.ListType(struct_type)
1171
+
1172
+ bind_param = sa.bindparam("data", type_=list_type)
1173
+
1174
+ upsert_stm = ydb_sa.upsert(table).from_select(
1175
+ ["id", "val_int", "val_str"],
1176
+ sa.select(
1177
+ sa.column("id", type_=Integer), sa.column("val_int", type_=Integer), sa.column("val_str", type_=String)
1178
+ ).select_from(sa.func.AS_TABLE(bind_param)),
1179
+ )
1180
+
1181
+ connection.execute(upsert_stm, {"data": input_data})
1182
+
1183
+ rows = connection.execute(sa.select(table).order_by(table.c.id)).fetchall()
1184
+ assert rows == [
1185
+ (1, 10, "a"),
1186
+ (2, None, "b"),
1187
+ (3, 30, None),
1188
+ ]
1189
+
1190
+ def test_insert_as_table(self, connection):
1191
+ table = self.tables.test_as_table
1192
+
1193
+ input_data = [
1194
+ {"id": 4, "val_int": 40, "val_str": "d"},
1195
+ {"id": 5, "val_int": None, "val_str": "e"},
1196
+ ]
1197
+
1198
+ struct_type = types.StructType(
1199
+ {
1200
+ "id": Integer,
1201
+ "val_int": types.Optional(Integer),
1202
+ "val_str": types.Optional(String),
1203
+ }
1204
+ )
1205
+ list_type = types.ListType(struct_type)
1206
+
1207
+ bind_param = sa.bindparam("data", type_=list_type)
1208
+
1209
+ insert_stm = sa.insert(table).from_select(
1210
+ ["id", "val_int", "val_str"],
1211
+ sa.select(
1212
+ sa.column("id", type_=Integer), sa.column("val_int", type_=Integer), sa.column("val_str", type_=String)
1213
+ ).select_from(sa.func.AS_TABLE(bind_param)),
1214
+ )
1215
+
1216
+ connection.execute(insert_stm, {"data": input_data})
1217
+
1218
+ rows = connection.execute(sa.select(table).where(table.c.id >= 4).order_by(table.c.id)).fetchall()
1219
+ assert rows == [
1220
+ (4, 40, "d"),
1221
+ (5, None, "e"),
1222
+ ]
1223
+
1224
+ def test_upsert_from_table_reflection(self, connection):
1225
+ table = self.tables.test_as_table
1226
+
1227
+ input_data = [
1228
+ {"id": 1, "val_int": 10, "val_str": "a"},
1229
+ {"id": 2, "val_int": None, "val_str": "b"},
1230
+ ]
1231
+
1232
+ struct_type = types.StructType.from_table(table)
1233
+ list_type = types.ListType(struct_type)
1234
+
1235
+ bind_param = sa.bindparam("data", type_=list_type)
1236
+
1237
+ cols = [sa.column(c.name, type_=c.type) for c in table.columns]
1238
+ upsert_stm = ydb_sa.upsert(table).from_select(
1239
+ [c.name for c in table.columns],
1240
+ sa.select(*cols).select_from(sa.func.AS_TABLE(bind_param)),
1241
+ )
1242
+
1243
+ connection.execute(upsert_stm, {"data": input_data})
1244
+
1245
+ rows = connection.execute(sa.select(table).order_by(table.c.id)).fetchall()
1246
+ assert rows == [
1247
+ (1, 10, "a"),
1248
+ (2, None, "b"),
1249
+ ]
test/test_suite.py CHANGED
@@ -274,7 +274,6 @@ class NumericTest(_NumericTest):
274
274
  pass
275
275
 
276
276
 
277
- @pytest.mark.skip("TODO: see issue #13")
278
277
  class BinaryTest(_BinaryTest):
279
278
  pass
280
279
 
@@ -1 +1 @@
1
- VERSION = "0.1.14"
1
+ VERSION = "0.1.15"
@@ -157,6 +157,9 @@ class YqlDialect(StrCompileDialect):
157
157
  sa.types.DATETIME: types.YqlDateTime,
158
158
  sa.types.TIMESTAMP: types.YqlTimestamp,
159
159
  sa.types.DECIMAL: types.Decimal,
160
+ sa.types.BINARY: types.Binary,
161
+ sa.types.LargeBinary: types.Binary,
162
+ sa.types.BLOB: types.Binary,
160
163
  }
161
164
 
162
165
  connection_characteristics = util.immutabledict(
@@ -12,6 +12,7 @@ from sqlalchemy.sql.compiler import (
12
12
  StrSQLTypeCompiler,
13
13
  selectable,
14
14
  )
15
+ from sqlalchemy.sql.type_api import to_instance
15
16
  from typing import (
16
17
  Any,
17
18
  Dict,
@@ -24,6 +25,12 @@ from typing import (
24
25
  Union,
25
26
  )
26
27
 
28
+ try:
29
+ from sqlalchemy.types import _Binary as _BinaryType
30
+ except ImportError:
31
+ # For older sqlalchemy versions
32
+ from sqlalchemy.sql.sqltypes import _Binary as _BinaryType
33
+
27
34
 
28
35
  from .. import types
29
36
 
@@ -152,11 +159,17 @@ class BaseYqlTypeCompiler(StrSQLTypeCompiler):
152
159
  inner = self.process(type_.item_type, **kw)
153
160
  return f"List<{inner}>"
154
161
 
162
+ def visit_optional(self, type_: types.Optional, **kw):
163
+ el = to_instance(type_.element_type)
164
+ inner = self.process(el, **kw)
165
+ return f"Optional<{inner}>"
166
+
155
167
  def visit_struct_type(self, type_: types.StructType, **kw):
156
- text = "Struct<"
157
- for field, field_type in type_.fields_types:
158
- text += f"{field}:{self.process(field_type, **kw)}"
159
- return text + ">"
168
+ rendered_types = []
169
+ for field, field_type in type_.fields_types.items():
170
+ type_str = self.process(field_type, **kw)
171
+ rendered_types.append(f"{field}:{type_str}")
172
+ return f"Struct<{','.join(rendered_types)}>"
160
173
 
161
174
  def get_ydb_type(
162
175
  self, type_: sa.types.TypeEngine, is_optional: bool
@@ -167,6 +180,10 @@ class BaseYqlTypeCompiler(StrSQLTypeCompiler):
167
180
  if isinstance(type_, (sa.Text, sa.String)):
168
181
  ydb_type = ydb.PrimitiveType.Utf8
169
182
 
183
+ elif isinstance(type_, types.Optional):
184
+ inner = to_instance(type_.element_type)
185
+ return self.get_ydb_type(inner, is_optional=True)
186
+
170
187
  # Integers
171
188
  elif isinstance(type_, types.UInt64):
172
189
  ydb_type = ydb.PrimitiveType.Uint64
@@ -216,7 +233,7 @@ class BaseYqlTypeCompiler(StrSQLTypeCompiler):
216
233
  ydb_type = ydb.PrimitiveType.Timestamp
217
234
  elif isinstance(type_, sa.Date):
218
235
  ydb_type = ydb.PrimitiveType.Date
219
- elif isinstance(type_, sa.BINARY):
236
+ elif isinstance(type_, _BinaryType):
220
237
  ydb_type = ydb.PrimitiveType.String
221
238
  elif isinstance(type_, sa.Float):
222
239
  ydb_type = ydb.PrimitiveType.Float
@@ -235,7 +252,8 @@ class BaseYqlTypeCompiler(StrSQLTypeCompiler):
235
252
  elif isinstance(type_, types.StructType):
236
253
  ydb_type = ydb.StructType()
237
254
  for field, field_type in type_.fields_types.items():
238
- ydb_type.add_member(field, self.get_ydb_type(field_type(), is_optional=False))
255
+ inner_type = to_instance(field_type)
256
+ ydb_type.add_member(field, self.get_ydb_type(inner_type, is_optional=False))
239
257
  else:
240
258
  raise NotSupportedError(f"{type_} bind variables not supported")
241
259
 
@@ -35,3 +35,111 @@ def test_ydb_types():
35
35
  compiled = query.compile(dialect=dialect, compile_kwargs={"literal_binds": True})
36
36
 
37
37
  assert str(compiled) == "Date('1996-11-19')"
38
+
39
+
40
+ def test_binary_type():
41
+ dialect = YqlDialect()
42
+ expr = sa.literal(b"some bytes")
43
+ compiled = expr.compile(dialect=dialect, compile_kwargs={"literal_binds": True})
44
+ assert str(compiled) == "'some bytes'"
45
+
46
+ expr_binary = sa.cast(expr, sa.BINARY)
47
+ compiled_binary = expr_binary.compile(dialect=dialect, compile_kwargs={"literal_binds": True})
48
+ assert str(compiled_binary) == "CAST('some bytes' AS String)"
49
+
50
+
51
+ def test_all_binary_types():
52
+ dialect = YqlDialect()
53
+ expr = sa.literal(b"some bytes")
54
+
55
+ binary_types = [
56
+ sa.BINARY,
57
+ sa.LargeBinary,
58
+ sa.BLOB,
59
+ types.Binary,
60
+ ]
61
+
62
+ for type_ in binary_types:
63
+ expr_binary = sa.cast(expr, type_)
64
+ compiled_binary = expr_binary.compile(dialect=dialect, compile_kwargs={"literal_binds": True})
65
+ assert str(compiled_binary) == "CAST('some bytes' AS String)"
66
+
67
+
68
+ def test_struct_type_generation():
69
+ dialect = YqlDialect()
70
+ type_compiler = dialect.type_compiler
71
+
72
+ # Test default (non-optional)
73
+ struct_type = types.StructType(
74
+ {
75
+ "id": sa.Integer,
76
+ "val_int": sa.Integer,
77
+ }
78
+ )
79
+ ydb_type = type_compiler.get_ydb_type(struct_type, is_optional=False)
80
+ # Keys are sorted
81
+ assert str(ydb_type) == "Struct<id:Int64,val_int:Int64>"
82
+
83
+ # Test optional
84
+ struct_type_opt = types.StructType(
85
+ {
86
+ "id": sa.Integer,
87
+ "val_int": types.Optional(sa.Integer),
88
+ }
89
+ )
90
+ ydb_type_opt = type_compiler.get_ydb_type(struct_type_opt, is_optional=False)
91
+ assert str(ydb_type_opt) == "Struct<id:Int64,val_int:Int64?>"
92
+
93
+
94
+ def test_types_compilation():
95
+ dialect = YqlDialect()
96
+
97
+ def compile_type(type_):
98
+ return dialect.type_compiler.process(type_)
99
+
100
+ assert compile_type(types.UInt64()) == "UInt64"
101
+ assert compile_type(types.UInt32()) == "UInt32"
102
+ assert compile_type(types.UInt16()) == "UInt16"
103
+ assert compile_type(types.UInt8()) == "UInt8"
104
+
105
+ assert compile_type(types.Int64()) == "Int64"
106
+ assert compile_type(types.Int32()) == "Int32"
107
+ assert compile_type(types.Int16()) == "Int32"
108
+ assert compile_type(types.Int8()) == "Int8"
109
+
110
+ assert compile_type(types.ListType(types.Int64())) == "List<Int64>"
111
+
112
+ struct = types.StructType({"a": types.Int32(), "b": types.ListType(types.Int32())})
113
+ # Ordered by key: a, b
114
+ assert compile_type(struct) == "Struct<a:Int32,b:List<Int32>>"
115
+
116
+
117
+ def test_optional_type_compilation():
118
+ dialect = YqlDialect()
119
+ type_compiler = dialect.type_compiler
120
+
121
+ def compile_type(type_):
122
+ return type_compiler.process(type_)
123
+
124
+ # Test Optional(Integer)
125
+ opt_int = types.Optional(sa.Integer)
126
+ assert compile_type(opt_int) == "Optional<Int64>"
127
+
128
+ # Test Optional(String)
129
+ opt_str = types.Optional(sa.String)
130
+ assert compile_type(opt_str) == "Optional<UTF8>"
131
+
132
+ # Test Nested Optional
133
+ opt_opt_int = types.Optional(types.Optional(sa.Integer))
134
+ assert compile_type(opt_opt_int) == "Optional<Optional<Int64>>"
135
+
136
+ # Test get_ydb_type
137
+ ydb_type = type_compiler.get_ydb_type(opt_int, is_optional=False)
138
+ import ydb
139
+
140
+ assert isinstance(ydb_type, ydb.OptionalType)
141
+ # Int64 corresponds to PrimitiveType.Int64
142
+ # Note: ydb.PrimitiveType.Int64 is an enum member, but ydb_type.item is also an instance/enum?
143
+ # get_ydb_type returns ydb.PrimitiveType.Int64 (enum) wrapped in OptionalType.
144
+ # OptionalType.item is the inner type.
145
+ assert ydb_type.item == ydb.PrimitiveType.Int64
@@ -8,7 +8,7 @@ if sa_version.startswith("2."):
8
8
  else:
9
9
  from sqlalchemy.sql.expression import ColumnElement
10
10
 
11
- from sqlalchemy import ARRAY, exc, types
11
+ from sqlalchemy import ARRAY, exc, Table, types
12
12
  from sqlalchemy.sql import type_api
13
13
 
14
14
  from .datetime_types import YqlDate, YqlDateTime, YqlTimestamp, YqlDate32, YqlTimestamp64, YqlDateTime64 # noqa: F401
@@ -116,12 +116,56 @@ class HashableDict(dict):
116
116
  return hash(tuple(self.items()))
117
117
 
118
118
 
119
+ class Optional(types.TypeEngine):
120
+ """
121
+ Wrapper for YDB Optional type.
122
+
123
+ Used primarily within StructType to denote nullable fields.
124
+ """
125
+
126
+ __visit_name__ = "optional"
127
+
128
+ def __init__(self, element_type: Union[Type[types.TypeEngine], types.TypeEngine]):
129
+ self.element_type = element_type
130
+
131
+
119
132
  class StructType(types.TypeEngine[Mapping[str, Any]]):
133
+ """
134
+ YDB Struct type.
135
+
136
+ Represents a structured data type with named fields, mapped to a Python dictionary.
137
+ """
138
+
120
139
  __visit_name__ = "struct_type"
121
140
 
122
- def __init__(self, fields_types: Mapping[str, Union[Type[types.TypeEngine], Type[types.TypeDecorator]]]):
141
+ def __init__(
142
+ self,
143
+ fields_types: Mapping[
144
+ str,
145
+ Union[Type[types.TypeEngine], types.TypeEngine, Optional],
146
+ ],
147
+ ):
123
148
  self.fields_types = HashableDict(dict(sorted(fields_types.items())))
124
149
 
150
+ @classmethod
151
+ def from_table(cls, table: Table) -> "StructType":
152
+ """
153
+ Create a StructType definition from a SQLAlchemy Table.
154
+
155
+ Automatically wraps nullable columns in Optional.
156
+
157
+ :param table: SQLAlchemy Table object
158
+ :return: StructType instance
159
+ """
160
+ fields = {}
161
+ for col in table.columns:
162
+ t = col.type
163
+ if col.nullable:
164
+ fields[col.name] = Optional(t)
165
+ else:
166
+ fields[col.name] = t
167
+ return cls(fields)
168
+
125
169
  @property
126
170
  def python_type(self):
127
171
  return dict
@@ -139,3 +183,10 @@ class Lambda(ColumnElement):
139
183
 
140
184
  self.type = type_api.NULLTYPE
141
185
  self.func = func
186
+
187
+
188
+ class Binary(types.LargeBinary):
189
+ __visit_name__ = "BINARY"
190
+
191
+ def bind_processor(self, dialect):
192
+ return None
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: ydb-sqlalchemy
3
- Version: 0.1.14
3
+ Version: 0.1.15
4
4
  Summary: YDB Dialect for SQLAlchemy
5
5
  Home-page: http://github.com/ydb-platform/ydb-sqlalchemy
6
6
  Author: Yandex LLC
@@ -1,26 +1,26 @@
1
1
  test/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
2
2
  test/conftest.py,sha256=rhWa0EQB9EwO_wAwxPdK17Qi582DdbBE8p5Gv4180Ds,570
3
- test/test_core.py,sha256=XvPJ0MtWK2gqGytps4YMUpHtJWKlEqN1rQBUpeeelAg,42859
3
+ test/test_core.py,sha256=5ijngbM6b_8avees4CqCv0FgzAza2RRPDwgT2saU99k,47254
4
4
  test/test_inspect.py,sha256=c4kc3jc48MCOfllO-ciiYf1vO-HOfuv0xVoXYT1Jxro,1106
5
5
  test/test_orm.py,sha256=jQVVld50zbUwxwgW9ySIWGaNDEOLzHKXjTkdpsG9TpA,1825
6
- test/test_suite.py,sha256=3Ht_mEG5zzYUA_7YH3Hax5EZgnGhnPrN0PBuJZvdR9I,31104
6
+ test/test_suite.py,sha256=JYBGZjaRbg_ZiAqTHeCfL7DLnB6N6xkXN82gnooCyd8,31063
7
7
  ydb_sqlalchemy/__init__.py,sha256=hX7Gy-KOiHk7B5-0wj3ZmLjk4YDJnSMHIAqxVGn_PJY,181
8
- ydb_sqlalchemy/_version.py,sha256=7CBWvojWQb3zsrGuOA2awejgwAOfTSzzu4lhva5fpfY,19
9
- ydb_sqlalchemy/sqlalchemy/__init__.py,sha256=vAeQNsLCuzSzOlvyzMnw_hwOzDY5uqLscdX3flyllj8,17791
8
+ ydb_sqlalchemy/_version.py,sha256=f1tdrTNNxSCrK-kaNyZL5_MXChoOPzxvT7JLn3_KW6k,19
9
+ ydb_sqlalchemy/sqlalchemy/__init__.py,sha256=Z5aprCPByfUmH2DvybA0o7W0lQ8YDCR1XSyyfGmBdoE,17911
10
10
  ydb_sqlalchemy/sqlalchemy/datetime_types.py,sha256=wrI9kpsI_f7Jhbm7Fu0o_S1QoGCLIe6A9jfUwb41aMM,1929
11
11
  ydb_sqlalchemy/sqlalchemy/dbapi_adapter.py,sha256=7FDjganh9QStIkoXYPFfcRRhd07YCX63_8OmMnge1FI,3542
12
12
  ydb_sqlalchemy/sqlalchemy/dml.py,sha256=k_m6PLOAY7dVzG1gsyo2bB3Lp-o3rhzN0oSX_nfkbFU,310
13
13
  ydb_sqlalchemy/sqlalchemy/json.py,sha256=b4ydjlQjBhlhqGP_Sy2uZVKmt__D-9M7-YLGQMdYGME,1043
14
14
  ydb_sqlalchemy/sqlalchemy/requirements.py,sha256=zm6fcLormtk3KHnbtrBvxfkbG9ZyzNan38HrRB6vC3c,2505
15
- ydb_sqlalchemy/sqlalchemy/test_sqlalchemy.py,sha256=QPRgUPsTOqAf0gzWjidsIhTPVkfOILy4SCSgNb1GE7o,1039
16
- ydb_sqlalchemy/sqlalchemy/types.py,sha256=BVeC8RSa2nFnI3-Q7XZxCnAolHc_k_kMCfWhUmbi5wo,3823
15
+ ydb_sqlalchemy/sqlalchemy/test_sqlalchemy.py,sha256=4wyRHmE8YQaMElQPHX6ToEj7A9F8Mvv909aaz_0wRnA,4535
16
+ ydb_sqlalchemy/sqlalchemy/types.py,sha256=_PxK76x6BJZv-7gVdXL-XJtaOFjiYZY4f-_WBRWH8x0,4996
17
17
  ydb_sqlalchemy/sqlalchemy/compiler/__init__.py,sha256=QqA6r-_bw1R97nQZy5ZSJN724znXg88l4mi5PpqAOxI,492
18
- ydb_sqlalchemy/sqlalchemy/compiler/base.py,sha256=zbd9uhV0uxHfMkSTyi4eoZiaiS32yNid4IWfGUo82vA,19433
18
+ ydb_sqlalchemy/sqlalchemy/compiler/base.py,sha256=zKOf4SrcAjMKf1LjRvQA7IzMRic0EIznHuZTHySvc_k,20143
19
19
  ydb_sqlalchemy/sqlalchemy/compiler/sa14.py,sha256=LanxAnwOiMnsnrY05B0jpmvGn5NXuOKMcxi_6N3obVM,1186
20
20
  ydb_sqlalchemy/sqlalchemy/compiler/sa20.py,sha256=rvVhe-pq5bOyuW4KMMMAD7JIWMzy355eijymBvuPwKw,3421
21
- ydb_sqlalchemy-0.1.14.dist-info/LICENSE,sha256=xx0jnfkXJvxRnG63LTGOxlggYnIysveWIZ6H3PNdCrQ,11357
22
- ydb_sqlalchemy-0.1.14.dist-info/METADATA,sha256=Q1Mv9kMxkNqehnueGkrzrICV_HQ3Klgy30wA18YSqyM,5395
23
- ydb_sqlalchemy-0.1.14.dist-info/WHEEL,sha256=Ll72iyqtt6Rbxp-Q7FSafYA1LeRv98X15xcZWRsFEmY,109
24
- ydb_sqlalchemy-0.1.14.dist-info/entry_points.txt,sha256=iJxbKYuliWNBmL0iIiw8MxvOXrSEz5xe5fuEBqMRwCE,267
25
- ydb_sqlalchemy-0.1.14.dist-info/top_level.txt,sha256=iS69Y1GTAcTok0u0oQdxP-Q5iVgUGI71XBsaEUrWhMg,20
26
- ydb_sqlalchemy-0.1.14.dist-info/RECORD,,
21
+ ydb_sqlalchemy-0.1.15.dist-info/LICENSE,sha256=xx0jnfkXJvxRnG63LTGOxlggYnIysveWIZ6H3PNdCrQ,11357
22
+ ydb_sqlalchemy-0.1.15.dist-info/METADATA,sha256=iOoVXzaJ3tJ7ct7jssAXLSUwptb9C4e7Yx3dPp8VzdA,5395
23
+ ydb_sqlalchemy-0.1.15.dist-info/WHEEL,sha256=Ll72iyqtt6Rbxp-Q7FSafYA1LeRv98X15xcZWRsFEmY,109
24
+ ydb_sqlalchemy-0.1.15.dist-info/entry_points.txt,sha256=iJxbKYuliWNBmL0iIiw8MxvOXrSEz5xe5fuEBqMRwCE,267
25
+ ydb_sqlalchemy-0.1.15.dist-info/top_level.txt,sha256=iS69Y1GTAcTok0u0oQdxP-Q5iVgUGI71XBsaEUrWhMg,20
26
+ ydb_sqlalchemy-0.1.15.dist-info/RECORD,,