pyobvector 0.2.21__py3-none-any.whl → 0.2.23__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.
- pyobvector/__init__.py +6 -5
- pyobvector/client/__init__.py +5 -4
- pyobvector/client/collection_schema.py +5 -1
- pyobvector/client/enum.py +1 -1
- pyobvector/client/exceptions.py +9 -7
- pyobvector/client/fts_index_param.py +8 -4
- pyobvector/client/hybrid_search.py +14 -4
- pyobvector/client/index_param.py +56 -41
- pyobvector/client/milvus_like_client.py +71 -54
- pyobvector/client/ob_client.py +20 -16
- pyobvector/client/ob_vec_client.py +45 -41
- pyobvector/client/ob_vec_json_table_client.py +366 -274
- pyobvector/client/partitions.py +81 -39
- pyobvector/client/schema_type.py +3 -1
- pyobvector/json_table/__init__.py +4 -3
- pyobvector/json_table/json_value_returning_func.py +12 -10
- pyobvector/json_table/oceanbase_dialect.py +15 -8
- pyobvector/json_table/virtual_data_type.py +47 -28
- pyobvector/schema/__init__.py +7 -1
- pyobvector/schema/array.py +6 -2
- pyobvector/schema/dialect.py +4 -0
- pyobvector/schema/full_text_index.py +8 -3
- pyobvector/schema/geo_srid_point.py +5 -2
- pyobvector/schema/gis_func.py +23 -11
- pyobvector/schema/match_against_func.py +10 -5
- pyobvector/schema/ob_table.py +2 -0
- pyobvector/schema/reflection.py +25 -8
- pyobvector/schema/replace_stmt.py +4 -0
- pyobvector/schema/sparse_vector.py +7 -4
- pyobvector/schema/vec_dist_func.py +22 -9
- pyobvector/schema/vector.py +3 -1
- pyobvector/schema/vector_index.py +7 -3
- pyobvector/util/__init__.py +1 -0
- pyobvector/util/ob_version.py +2 -0
- pyobvector/util/sparse_vector.py +9 -6
- pyobvector/util/vector.py +2 -0
- {pyobvector-0.2.21.dist-info → pyobvector-0.2.23.dist-info}/METADATA +13 -14
- pyobvector-0.2.23.dist-info/RECORD +40 -0
- {pyobvector-0.2.21.dist-info → pyobvector-0.2.23.dist-info}/licenses/LICENSE +1 -1
- pyobvector-0.2.21.dist-info/RECORD +0 -40
- {pyobvector-0.2.21.dist-info → pyobvector-0.2.23.dist-info}/WHEEL +0 -0
|
@@ -3,7 +3,17 @@ import logging
|
|
|
3
3
|
import re
|
|
4
4
|
from typing import Optional, Union
|
|
5
5
|
|
|
6
|
-
from sqlalchemy import
|
|
6
|
+
from sqlalchemy import (
|
|
7
|
+
Column,
|
|
8
|
+
Integer,
|
|
9
|
+
String,
|
|
10
|
+
JSON,
|
|
11
|
+
Engine,
|
|
12
|
+
select,
|
|
13
|
+
text,
|
|
14
|
+
func,
|
|
15
|
+
CursorResult,
|
|
16
|
+
)
|
|
7
17
|
from sqlalchemy.dialects.mysql import TINYINT
|
|
8
18
|
from sqlalchemy.orm import declarative_base, sessionmaker, Session
|
|
9
19
|
from sqlglot import parse_one, exp, Expression, to_identifier
|
|
@@ -18,7 +28,7 @@ from ..json_table import (
|
|
|
18
28
|
JsonTableDecimalFactory,
|
|
19
29
|
JsonTableInt,
|
|
20
30
|
val2json,
|
|
21
|
-
json_value
|
|
31
|
+
json_value,
|
|
22
32
|
)
|
|
23
33
|
|
|
24
34
|
logger = logging.getLogger(__name__)
|
|
@@ -27,6 +37,7 @@ logger.setLevel(logging.DEBUG)
|
|
|
27
37
|
JSON_TABLE_META_TABLE_NAME = "meta_json_t"
|
|
28
38
|
JSON_TABLE_DATA_TABLE_NAME = "data_json_t"
|
|
29
39
|
|
|
40
|
+
|
|
30
41
|
class ObVecJsonTableClient(ObVecClient):
|
|
31
42
|
"""OceanBase Vector Store Client with JSON Table."""
|
|
32
43
|
|
|
@@ -34,7 +45,7 @@ class ObVecJsonTableClient(ObVecClient):
|
|
|
34
45
|
|
|
35
46
|
class JsonTableMetaTBL(Base):
|
|
36
47
|
__tablename__ = JSON_TABLE_META_TABLE_NAME
|
|
37
|
-
|
|
48
|
+
|
|
38
49
|
user_id = Column(String(128), primary_key=True, autoincrement=False)
|
|
39
50
|
jtable_name = Column(String(512), primary_key=True)
|
|
40
51
|
jcol_id = Column(Integer, primary_key=True)
|
|
@@ -53,33 +64,33 @@ class ObVecJsonTableClient(ObVecClient):
|
|
|
53
64
|
jdata_id = Column(Integer, primary_key=True, autoincrement=True, nullable=False)
|
|
54
65
|
jdata = Column(JSON)
|
|
55
66
|
|
|
56
|
-
class JsonTableMetadata:
|
|
67
|
+
class JsonTableMetadata:
|
|
57
68
|
def __init__(self, user_id: str):
|
|
58
69
|
self.user_id = user_id
|
|
59
70
|
self.meta_cache: dict[str, list] = {}
|
|
60
71
|
|
|
61
72
|
@classmethod
|
|
62
73
|
def _parse_col_type(cls, col_type: str):
|
|
63
|
-
if col_type.startswith(
|
|
74
|
+
if col_type.startswith("TINYINT"):
|
|
64
75
|
return JsonTableBool
|
|
65
|
-
elif col_type.startswith(
|
|
76
|
+
elif col_type.startswith("TIMESTAMP"):
|
|
66
77
|
return JsonTableTimestamp
|
|
67
|
-
elif col_type.startswith(
|
|
78
|
+
elif col_type.startswith("INT"):
|
|
68
79
|
return JsonTableInt
|
|
69
|
-
elif col_type.startswith(
|
|
70
|
-
if col_type ==
|
|
80
|
+
elif col_type.startswith("VARCHAR"):
|
|
81
|
+
if col_type == "VARCHAR":
|
|
71
82
|
factory = JsonTableVarcharFactory(255)
|
|
72
83
|
else:
|
|
73
|
-
varchar_pattern = r
|
|
84
|
+
varchar_pattern = r"VARCHAR\s*\((\d+)\)"
|
|
74
85
|
varchar_matches = re.findall(varchar_pattern, col_type)
|
|
75
86
|
factory = JsonTableVarcharFactory(int(varchar_matches[0]))
|
|
76
87
|
model = factory.get_json_table_varchar_type()
|
|
77
88
|
return model
|
|
78
|
-
elif col_type.startswith(
|
|
79
|
-
if col_type ==
|
|
89
|
+
elif col_type.startswith("DECIMAL"):
|
|
90
|
+
if col_type == "DECIMAL":
|
|
80
91
|
factory = JsonTableDecimalFactory(10, 0)
|
|
81
92
|
else:
|
|
82
|
-
decimal_pattern = r
|
|
93
|
+
decimal_pattern = r"DECIMAL\s*\((\d+),\s*(\d+)\)"
|
|
83
94
|
decimal_matches = re.findall(decimal_pattern, col_type)
|
|
84
95
|
x, y = decimal_matches[0]
|
|
85
96
|
factory = JsonTableDecimalFactory(int(x), int(y))
|
|
@@ -98,26 +109,29 @@ class ObVecJsonTableClient(ObVecClient):
|
|
|
98
109
|
for r in res:
|
|
99
110
|
if r[1] not in self.meta_cache:
|
|
100
111
|
self.meta_cache[r[1]] = []
|
|
101
|
-
self.meta_cache[r[1]].append(
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
112
|
+
self.meta_cache[r[1]].append(
|
|
113
|
+
{
|
|
114
|
+
"jcol_id": r[2],
|
|
115
|
+
"jcol_name": r[3],
|
|
116
|
+
"jcol_type": r[4],
|
|
117
|
+
"jcol_nullable": bool(r[5]),
|
|
118
|
+
"jcol_has_default": bool(r[6]),
|
|
119
|
+
"jcol_default": (
|
|
120
|
+
r[7]["default"]
|
|
121
|
+
if isinstance(r[7], dict)
|
|
122
|
+
else json.loads(r[7])["default"]
|
|
123
|
+
),
|
|
124
|
+
"jcol_model": ObVecJsonTableClient.JsonTableMetadata._parse_col_type(
|
|
125
|
+
r[4]
|
|
126
|
+
),
|
|
127
|
+
}
|
|
128
|
+
)
|
|
114
129
|
for k, _ in self.meta_cache.items():
|
|
115
|
-
self.meta_cache[k].sort(key=lambda x: x[
|
|
130
|
+
self.meta_cache[k].sort(key=lambda x: x["jcol_id"])
|
|
116
131
|
|
|
117
132
|
for k, v in self.meta_cache.items():
|
|
118
133
|
logger.debug(f"LOAD TABLE --- {k}: {v}")
|
|
119
134
|
|
|
120
|
-
|
|
121
135
|
def __init__(
|
|
122
136
|
self,
|
|
123
137
|
user_id: Optional[str],
|
|
@@ -137,28 +151,30 @@ class ObVecJsonTableClient(ObVecClient):
|
|
|
137
151
|
raise ValueError(f"Invalid admin_id: {admin_id}")
|
|
138
152
|
self.jmetadata = ObVecJsonTableClient.JsonTableMetadata(self.admin_id)
|
|
139
153
|
self.jmetadata.reflect(self.engine)
|
|
140
|
-
|
|
154
|
+
|
|
141
155
|
def check_admin_id_ok(self):
|
|
142
156
|
if self.user_id is None:
|
|
143
157
|
return True
|
|
144
|
-
|
|
158
|
+
|
|
145
159
|
with self.session() as session:
|
|
146
160
|
with session.begin():
|
|
147
161
|
distinct_admin_ids = (
|
|
148
162
|
session.query(ObVecJsonTableClient.JsonTableDataTBL.admin_id)
|
|
149
|
-
.filter_by(user_id
|
|
163
|
+
.filter_by(user_id=self.user_id)
|
|
150
164
|
.distinct()
|
|
151
165
|
.all()
|
|
152
166
|
)
|
|
153
167
|
admin_ids = [admin_id[0] for admin_id in distinct_admin_ids]
|
|
154
|
-
return (len(admin_ids) == 0) or (
|
|
168
|
+
return (len(admin_ids) == 0) or (
|
|
169
|
+
len(admin_ids) == 1 and admin_ids[0] == self.admin_id
|
|
170
|
+
)
|
|
155
171
|
|
|
156
172
|
def _reset(self):
|
|
157
173
|
# Only for test
|
|
158
174
|
self.perform_raw_text_sql(f"TRUNCATE TABLE {JSON_TABLE_DATA_TABLE_NAME}")
|
|
159
175
|
self.perform_raw_text_sql(f"TRUNCATE TABLE {JSON_TABLE_META_TABLE_NAME}")
|
|
160
176
|
self.jmetadata = ObVecJsonTableClient.JsonTableMetadata(self.admin_id)
|
|
161
|
-
|
|
177
|
+
|
|
162
178
|
def refresh_metadata(self) -> None:
|
|
163
179
|
self.jmetadata.reflect(self.engine)
|
|
164
180
|
|
|
@@ -171,7 +187,7 @@ class ObVecJsonTableClient(ObVecClient):
|
|
|
171
187
|
"""Perform common SQL that operates on JSON Table."""
|
|
172
188
|
ast = parse_one(sql, dialect="oceanbase")
|
|
173
189
|
if isinstance(ast, exp.Create):
|
|
174
|
-
if ast.kind and ast.kind ==
|
|
190
|
+
if ast.kind and ast.kind == "TABLE":
|
|
175
191
|
self._handle_create_json_table(ast)
|
|
176
192
|
else:
|
|
177
193
|
raise ValueError(f"Create {ast.kind} is not supported")
|
|
@@ -192,7 +208,7 @@ class ObVecJsonTableClient(ObVecClient):
|
|
|
192
208
|
return self._handle_jtable_dml_select(ast, select_with_data_id, opt_user_id)
|
|
193
209
|
else:
|
|
194
210
|
raise ValueError(f"{type(ast)} not supported")
|
|
195
|
-
|
|
211
|
+
|
|
196
212
|
def _parse_datatype_to_str(self, datatype):
|
|
197
213
|
if datatype == exp.DataType.Type.INT:
|
|
198
214
|
return "INT"
|
|
@@ -205,7 +221,7 @@ class ObVecJsonTableClient(ObVecClient):
|
|
|
205
221
|
if datatype == exp.DataType.Type.DECIMAL:
|
|
206
222
|
return "DECIMAL"
|
|
207
223
|
raise ValueError(f"{datatype} not supported")
|
|
208
|
-
|
|
224
|
+
|
|
209
225
|
def _calc_default_value(self, default_val):
|
|
210
226
|
if default_val is None:
|
|
211
227
|
return None
|
|
@@ -214,7 +230,7 @@ class ObVecJsonTableClient(ObVecClient):
|
|
|
214
230
|
for r in res:
|
|
215
231
|
logger.debug(f"============== Calculate default value: {r[0]}")
|
|
216
232
|
return r[0]
|
|
217
|
-
|
|
233
|
+
|
|
218
234
|
def _handle_create_json_table(self, ast: Expression):
|
|
219
235
|
logger.debug("HANDLE CREATE JSON TABLE")
|
|
220
236
|
|
|
@@ -228,11 +244,14 @@ class ObVecJsonTableClient(ObVecClient):
|
|
|
228
244
|
raise ValueError("Invalid create table statement")
|
|
229
245
|
jtable_name = jtable.this.this
|
|
230
246
|
|
|
231
|
-
if
|
|
247
|
+
if (
|
|
248
|
+
jtable_name == JSON_TABLE_META_TABLE_NAME
|
|
249
|
+
or jtable_name == JSON_TABLE_DATA_TABLE_NAME
|
|
250
|
+
):
|
|
232
251
|
raise ValueError(f"Invalid table name: {jtable_name}")
|
|
233
252
|
if jtable_name in self.jmetadata.meta_cache:
|
|
234
253
|
raise ValueError("Table name duplicated")
|
|
235
|
-
|
|
254
|
+
|
|
236
255
|
session = self.session()
|
|
237
256
|
session.execute(text("SET @@session.autocommit=0"))
|
|
238
257
|
new_meta_cache_items = []
|
|
@@ -251,13 +270,17 @@ class ObVecJsonTableClient(ObVecClient):
|
|
|
251
270
|
else:
|
|
252
271
|
col_type_params_list.append(f"{param.this}")
|
|
253
272
|
if len(col_type_params_list) > 0:
|
|
254
|
-
col_type_str +=
|
|
255
|
-
col_type_model = ObVecJsonTableClient.JsonTableMetadata._parse_col_type(
|
|
256
|
-
|
|
273
|
+
col_type_str += "(" + ",".join(col_type_params_list) + ")"
|
|
274
|
+
col_type_model = ObVecJsonTableClient.JsonTableMetadata._parse_col_type(
|
|
275
|
+
col_type_str
|
|
276
|
+
)
|
|
277
|
+
|
|
257
278
|
for cons in col_def.constraints:
|
|
258
279
|
if isinstance(cons.kind, exp.DefaultColumnConstraint):
|
|
259
280
|
col_has_default = True
|
|
260
|
-
logger.debug(
|
|
281
|
+
logger.debug(
|
|
282
|
+
f"############ create jtable ########### {str(cons.kind.this)}"
|
|
283
|
+
)
|
|
261
284
|
col_default_val = str(cons.kind.this)
|
|
262
285
|
if col_default_val.upper() == "NULL":
|
|
263
286
|
col_default_val = None
|
|
@@ -267,7 +290,7 @@ class ObVecJsonTableClient(ObVecClient):
|
|
|
267
290
|
pass
|
|
268
291
|
# raise ValueError(f"{cons.kind} constriaint is not supported.")
|
|
269
292
|
# TODO support json index
|
|
270
|
-
|
|
293
|
+
|
|
271
294
|
if col_has_default and (col_default_val is not None):
|
|
272
295
|
# check default value is valid
|
|
273
296
|
col_type_model(val=self._calc_default_value(col_default_val))
|
|
@@ -280,34 +303,40 @@ class ObVecJsonTableClient(ObVecClient):
|
|
|
280
303
|
f"col_type_str={col_type_str}, col_nullable={col_nullable}, "
|
|
281
304
|
f"col_has_default={col_has_default}, col_default_val={col_default_val}"
|
|
282
305
|
)
|
|
283
|
-
new_meta_cache_items.append(
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
session.add(ObVecJsonTableClient.JsonTableMetaTBL(
|
|
293
|
-
user_id = self.admin_id,
|
|
294
|
-
jtable_name = jtable_name,
|
|
295
|
-
jcol_id = col_id,
|
|
296
|
-
jcol_name = col_name,
|
|
297
|
-
jcol_type = col_type_str,
|
|
298
|
-
jcol_nullable = col_nullable,
|
|
299
|
-
jcol_has_default = col_has_default,
|
|
300
|
-
jcol_default = {
|
|
301
|
-
'default': col_default_val,
|
|
306
|
+
new_meta_cache_items.append(
|
|
307
|
+
{
|
|
308
|
+
"jcol_id": col_id,
|
|
309
|
+
"jcol_name": col_name,
|
|
310
|
+
"jcol_type": col_type_str,
|
|
311
|
+
"jcol_nullable": col_nullable,
|
|
312
|
+
"jcol_has_default": col_has_default,
|
|
313
|
+
"jcol_default": col_default_val,
|
|
314
|
+
"jcol_model": col_type_model,
|
|
302
315
|
}
|
|
303
|
-
)
|
|
304
|
-
|
|
316
|
+
)
|
|
317
|
+
session.add(
|
|
318
|
+
ObVecJsonTableClient.JsonTableMetaTBL(
|
|
319
|
+
user_id=self.admin_id,
|
|
320
|
+
jtable_name=jtable_name,
|
|
321
|
+
jcol_id=col_id,
|
|
322
|
+
jcol_name=col_name,
|
|
323
|
+
jcol_type=col_type_str,
|
|
324
|
+
jcol_nullable=col_nullable,
|
|
325
|
+
jcol_has_default=col_has_default,
|
|
326
|
+
jcol_default={
|
|
327
|
+
"default": col_default_val,
|
|
328
|
+
},
|
|
329
|
+
)
|
|
330
|
+
)
|
|
331
|
+
|
|
305
332
|
col_id += 1
|
|
306
|
-
|
|
333
|
+
|
|
307
334
|
try:
|
|
308
335
|
session.commit()
|
|
309
336
|
self.jmetadata.meta_cache[jtable_name] = new_meta_cache_items
|
|
310
|
-
logger.debug(
|
|
337
|
+
logger.debug(
|
|
338
|
+
f"ADD METADATA CACHE ---- {jtable_name}: {new_meta_cache_items}"
|
|
339
|
+
)
|
|
311
340
|
except Exception as e:
|
|
312
341
|
session.rollback()
|
|
313
342
|
logger.error(f"Error occurred: {e}")
|
|
@@ -316,15 +345,15 @@ class ObVecJsonTableClient(ObVecClient):
|
|
|
316
345
|
|
|
317
346
|
def _check_table_exists(self, jtable_name: str) -> bool:
|
|
318
347
|
return jtable_name in self.jmetadata.meta_cache
|
|
319
|
-
|
|
348
|
+
|
|
320
349
|
def _check_col_exists(self, jtable_name: str, col_name: str) -> Optional[dict]:
|
|
321
350
|
if not self._check_table_exists(jtable_name):
|
|
322
351
|
return None
|
|
323
352
|
for col_meta in self.jmetadata.meta_cache[jtable_name]:
|
|
324
|
-
if col_meta[
|
|
353
|
+
if col_meta["jcol_name"] == col_name:
|
|
325
354
|
return col_meta
|
|
326
355
|
return None
|
|
327
|
-
|
|
356
|
+
|
|
328
357
|
def _parse_col_datatype(self, expr: Expression) -> str:
|
|
329
358
|
col_type_str = self._parse_datatype_to_str(expr.this)
|
|
330
359
|
col_type_params_list = []
|
|
@@ -334,16 +363,18 @@ class ObVecJsonTableClient(ObVecClient):
|
|
|
334
363
|
else:
|
|
335
364
|
col_type_params_list.append(f"{param.this}")
|
|
336
365
|
if len(col_type_params_list) > 0:
|
|
337
|
-
col_type_str +=
|
|
366
|
+
col_type_str += "(" + ",".join(col_type_params_list) + ")"
|
|
338
367
|
return col_type_str
|
|
339
|
-
|
|
368
|
+
|
|
340
369
|
def _parse_col_constraints(self, expr: Expression) -> dict:
|
|
341
370
|
col_has_default = False
|
|
342
371
|
col_nullable = True
|
|
343
372
|
for cons in expr:
|
|
344
373
|
if isinstance(cons.kind, exp.DefaultColumnConstraint):
|
|
345
374
|
col_has_default = True
|
|
346
|
-
logger.debug(
|
|
375
|
+
logger.debug(
|
|
376
|
+
f"############ column constraints ########### {str(cons.kind.this)}"
|
|
377
|
+
)
|
|
347
378
|
col_default_val = str(cons.kind.this)
|
|
348
379
|
if col_default_val.upper() == "NULL":
|
|
349
380
|
col_default_val = None
|
|
@@ -352,9 +383,9 @@ class ObVecJsonTableClient(ObVecClient):
|
|
|
352
383
|
else:
|
|
353
384
|
raise ValueError(f"{cons.kind} constriaint is not supported.")
|
|
354
385
|
return {
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
386
|
+
"jcol_nullable": col_nullable,
|
|
387
|
+
"jcol_has_default": col_has_default,
|
|
388
|
+
"jcol_default": col_default_val,
|
|
358
389
|
}
|
|
359
390
|
|
|
360
391
|
def _handle_alter_jtable_change_column(
|
|
@@ -367,56 +398,60 @@ class ObVecJsonTableClient(ObVecClient):
|
|
|
367
398
|
origin_col_name = change_col.origin_col_name.this
|
|
368
399
|
if not self._check_col_exists(jtable_name, origin_col_name):
|
|
369
400
|
raise ValueError(f"{origin_col_name} not exists in {jtable_name}")
|
|
370
|
-
|
|
401
|
+
|
|
371
402
|
new_col_name = change_col.this
|
|
372
403
|
if self._check_col_exists(jtable_name, new_col_name):
|
|
373
404
|
raise ValueError(f"Column {new_col_name} exists!")
|
|
374
|
-
|
|
405
|
+
|
|
375
406
|
col_type_str = self._parse_col_datatype(change_col.dtype)
|
|
376
407
|
|
|
377
408
|
session.query(ObVecJsonTableClient.JsonTableMetaTBL).filter_by(
|
|
378
|
-
user_id=self.admin_id,
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
},
|
|
389
|
-
})
|
|
409
|
+
user_id=self.admin_id, jtable_name=jtable_name, jcol_name=origin_col_name
|
|
410
|
+
).update(
|
|
411
|
+
{
|
|
412
|
+
ObVecJsonTableClient.JsonTableMetaTBL.jcol_name: new_col_name,
|
|
413
|
+
ObVecJsonTableClient.JsonTableMetaTBL.jcol_type: col_type_str,
|
|
414
|
+
ObVecJsonTableClient.JsonTableMetaTBL.jcol_nullable: True,
|
|
415
|
+
ObVecJsonTableClient.JsonTableMetaTBL.jcol_has_default: True,
|
|
416
|
+
ObVecJsonTableClient.JsonTableMetaTBL.jcol_default: {"default": None},
|
|
417
|
+
}
|
|
418
|
+
)
|
|
390
419
|
|
|
391
420
|
session.query(ObVecJsonTableClient.JsonTableDataTBL).filter_by(
|
|
392
421
|
admin_id=self.admin_id,
|
|
393
422
|
jtable_name=jtable_name,
|
|
394
|
-
).update(
|
|
395
|
-
|
|
396
|
-
func.
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
423
|
+
).update(
|
|
424
|
+
{
|
|
425
|
+
ObVecJsonTableClient.JsonTableDataTBL.jdata: func.json_insert(
|
|
426
|
+
func.json_remove(
|
|
427
|
+
ObVecJsonTableClient.JsonTableDataTBL.jdata,
|
|
428
|
+
f"$.{origin_col_name}",
|
|
429
|
+
),
|
|
430
|
+
f"$.{new_col_name}",
|
|
431
|
+
func.json_value(
|
|
432
|
+
ObVecJsonTableClient.JsonTableDataTBL.jdata,
|
|
433
|
+
f"$.{origin_col_name}",
|
|
434
|
+
),
|
|
435
|
+
)
|
|
436
|
+
}
|
|
437
|
+
)
|
|
405
438
|
|
|
406
439
|
session.query(ObVecJsonTableClient.JsonTableDataTBL).filter_by(
|
|
407
440
|
admin_id=self.admin_id,
|
|
408
441
|
jtable_name=jtable_name,
|
|
409
|
-
).update(
|
|
410
|
-
|
|
411
|
-
ObVecJsonTableClient.JsonTableDataTBL.jdata
|
|
412
|
-
f'$.{new_col_name}',
|
|
413
|
-
json_value(
|
|
442
|
+
).update(
|
|
443
|
+
{
|
|
444
|
+
ObVecJsonTableClient.JsonTableDataTBL.jdata: func.json_replace(
|
|
414
445
|
ObVecJsonTableClient.JsonTableDataTBL.jdata,
|
|
415
|
-
f
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
446
|
+
f"$.{new_col_name}",
|
|
447
|
+
json_value(
|
|
448
|
+
ObVecJsonTableClient.JsonTableDataTBL.jdata,
|
|
449
|
+
f"$.{new_col_name}",
|
|
450
|
+
col_type_str,
|
|
451
|
+
),
|
|
452
|
+
)
|
|
453
|
+
}
|
|
454
|
+
)
|
|
420
455
|
|
|
421
456
|
def _handle_alter_jtable_drop_column(
|
|
422
457
|
self,
|
|
@@ -432,19 +467,19 @@ class ObVecJsonTableClient(ObVecClient):
|
|
|
432
467
|
raise ValueError(f"{col_name} not exists in {jtable_name}")
|
|
433
468
|
|
|
434
469
|
session.query(ObVecJsonTableClient.JsonTableMetaTBL).filter_by(
|
|
435
|
-
user_id=self.admin_id,
|
|
436
|
-
jtable_name=jtable_name,
|
|
437
|
-
jcol_name=col_name
|
|
470
|
+
user_id=self.admin_id, jtable_name=jtable_name, jcol_name=col_name
|
|
438
471
|
).delete()
|
|
439
472
|
|
|
440
473
|
session.query(ObVecJsonTableClient.JsonTableDataTBL).filter_by(
|
|
441
474
|
admin_id=self.admin_id,
|
|
442
475
|
jtable_name=jtable_name,
|
|
443
|
-
).update(
|
|
444
|
-
|
|
445
|
-
ObVecJsonTableClient.JsonTableDataTBL.jdata
|
|
446
|
-
|
|
447
|
-
|
|
476
|
+
).update(
|
|
477
|
+
{
|
|
478
|
+
ObVecJsonTableClient.JsonTableDataTBL.jdata: func.json_remove(
|
|
479
|
+
ObVecJsonTableClient.JsonTableDataTBL.jdata, f"$.{col_name}"
|
|
480
|
+
)
|
|
481
|
+
}
|
|
482
|
+
)
|
|
448
483
|
|
|
449
484
|
def _handle_alter_jtable_add_column(
|
|
450
485
|
self,
|
|
@@ -456,54 +491,69 @@ class ObVecJsonTableClient(ObVecClient):
|
|
|
456
491
|
new_col_name = add_col.this.this
|
|
457
492
|
if self._check_col_exists(jtable_name, new_col_name):
|
|
458
493
|
raise ValueError(f"{new_col_name} exists!")
|
|
459
|
-
|
|
494
|
+
|
|
460
495
|
col_type_str = self._parse_col_datatype(add_col.kind)
|
|
461
496
|
model = ObVecJsonTableClient.JsonTableMetadata._parse_col_type(col_type_str)
|
|
462
497
|
constraints = self._parse_col_constraints(add_col.constraints)
|
|
463
|
-
if (
|
|
498
|
+
if (
|
|
499
|
+
(not constraints["jcol_nullable"])
|
|
500
|
+
and constraints["jcol_has_default"]
|
|
501
|
+
and (constraints["jcol_default"] is None)
|
|
502
|
+
):
|
|
464
503
|
raise ValueError(f"Invalid default value for '{new_col_name}'")
|
|
465
|
-
if constraints[
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
504
|
+
if constraints["jcol_has_default"] and (
|
|
505
|
+
constraints["jcol_default"] is not None
|
|
506
|
+
):
|
|
507
|
+
model(val=self._calc_default_value(constraints["jcol_default"]))
|
|
508
|
+
cur_col_id = (
|
|
509
|
+
max([meta["jcol_id"] for meta in self.jmetadata.meta_cache[jtable_name]])
|
|
510
|
+
+ 1
|
|
511
|
+
)
|
|
512
|
+
|
|
513
|
+
session.add(
|
|
514
|
+
ObVecJsonTableClient.JsonTableMetaTBL(
|
|
515
|
+
user_id=self.admin_id,
|
|
516
|
+
jtable_name=jtable_name,
|
|
517
|
+
jcol_id=cur_col_id,
|
|
518
|
+
jcol_name=new_col_name,
|
|
519
|
+
jcol_type=col_type_str,
|
|
520
|
+
jcol_nullable=constraints["jcol_nullable"],
|
|
521
|
+
jcol_has_default=constraints["jcol_has_default"],
|
|
522
|
+
jcol_default={
|
|
523
|
+
"default": constraints["jcol_default"],
|
|
524
|
+
},
|
|
525
|
+
)
|
|
526
|
+
)
|
|
481
527
|
|
|
482
|
-
if constraints[
|
|
528
|
+
if constraints["jcol_default"] is None:
|
|
483
529
|
session.query(ObVecJsonTableClient.JsonTableDataTBL).filter_by(
|
|
484
530
|
admin_id=self.admin_id,
|
|
485
531
|
jtable_name=jtable_name,
|
|
486
|
-
).update(
|
|
487
|
-
|
|
488
|
-
ObVecJsonTableClient.JsonTableDataTBL.jdata
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
532
|
+
).update(
|
|
533
|
+
{
|
|
534
|
+
ObVecJsonTableClient.JsonTableDataTBL.jdata: func.json_insert(
|
|
535
|
+
ObVecJsonTableClient.JsonTableDataTBL.jdata,
|
|
536
|
+
f"$.{new_col_name}",
|
|
537
|
+
None,
|
|
538
|
+
)
|
|
539
|
+
}
|
|
540
|
+
)
|
|
493
541
|
else:
|
|
494
542
|
model = ObVecJsonTableClient.JsonTableMetadata._parse_col_type(col_type_str)
|
|
495
|
-
datum = model(val=self._calc_default_value(constraints[
|
|
543
|
+
datum = model(val=self._calc_default_value(constraints["jcol_default"]))
|
|
496
544
|
json_val = val2json(datum.val)
|
|
497
545
|
session.query(ObVecJsonTableClient.JsonTableDataTBL).filter_by(
|
|
498
546
|
admin_id=self.admin_id,
|
|
499
547
|
jtable_name=jtable_name,
|
|
500
|
-
).update(
|
|
501
|
-
|
|
502
|
-
ObVecJsonTableClient.JsonTableDataTBL.jdata
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
548
|
+
).update(
|
|
549
|
+
{
|
|
550
|
+
ObVecJsonTableClient.JsonTableDataTBL.jdata: func.json_insert(
|
|
551
|
+
ObVecJsonTableClient.JsonTableDataTBL.jdata,
|
|
552
|
+
f"$.{new_col_name}",
|
|
553
|
+
json_val,
|
|
554
|
+
)
|
|
555
|
+
}
|
|
556
|
+
)
|
|
507
557
|
|
|
508
558
|
def _handle_alter_jtable_modify_column(
|
|
509
559
|
self,
|
|
@@ -516,65 +566,79 @@ class ObVecJsonTableClient(ObVecClient):
|
|
|
516
566
|
col_name = col_def.this.this
|
|
517
567
|
if not self._check_col_exists(jtable_name, col_name):
|
|
518
568
|
raise ValueError(f"{col_name} not exists in {jtable_name}")
|
|
519
|
-
|
|
569
|
+
|
|
520
570
|
col_type_str = self._parse_col_datatype(col_def.kind)
|
|
521
571
|
model = ObVecJsonTableClient.JsonTableMetadata._parse_col_type(col_type_str)
|
|
522
572
|
constraints = self._parse_col_constraints(col_def.constraints)
|
|
523
|
-
if (
|
|
573
|
+
if (
|
|
574
|
+
(not constraints["jcol_nullable"])
|
|
575
|
+
and constraints["jcol_has_default"]
|
|
576
|
+
and (constraints["jcol_default"] is None)
|
|
577
|
+
):
|
|
524
578
|
raise ValueError(f"Invalid default value for '{col_name}'")
|
|
525
|
-
if constraints[
|
|
526
|
-
|
|
579
|
+
if constraints["jcol_has_default"] and (
|
|
580
|
+
constraints["jcol_default"] is not None
|
|
581
|
+
):
|
|
582
|
+
model(val=self._calc_default_value(constraints["jcol_default"]))
|
|
527
583
|
|
|
528
584
|
session.query(ObVecJsonTableClient.JsonTableMetaTBL).filter_by(
|
|
529
|
-
user_id=self.admin_id,
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
585
|
+
user_id=self.admin_id, jtable_name=jtable_name, jcol_name=col_name
|
|
586
|
+
).update(
|
|
587
|
+
{
|
|
588
|
+
ObVecJsonTableClient.JsonTableMetaTBL.jcol_name: col_name,
|
|
589
|
+
ObVecJsonTableClient.JsonTableMetaTBL.jcol_type: col_type_str,
|
|
590
|
+
ObVecJsonTableClient.JsonTableMetaTBL.jcol_nullable: constraints[
|
|
591
|
+
"jcol_nullable"
|
|
592
|
+
],
|
|
593
|
+
ObVecJsonTableClient.JsonTableMetaTBL.jcol_has_default: constraints[
|
|
594
|
+
"jcol_has_default"
|
|
595
|
+
],
|
|
596
|
+
ObVecJsonTableClient.JsonTableMetaTBL.jcol_default: {
|
|
597
|
+
"default": constraints["jcol_default"]
|
|
598
|
+
},
|
|
599
|
+
}
|
|
600
|
+
)
|
|
601
|
+
|
|
602
|
+
if constraints["jcol_default"] is None:
|
|
543
603
|
session.query(ObVecJsonTableClient.JsonTableDataTBL).filter_by(
|
|
544
604
|
admin_id=self.admin_id,
|
|
545
605
|
jtable_name=jtable_name,
|
|
546
|
-
).update(
|
|
547
|
-
|
|
548
|
-
ObVecJsonTableClient.JsonTableDataTBL.jdata
|
|
549
|
-
f'$.{col_name}',
|
|
550
|
-
json_value(
|
|
606
|
+
).update(
|
|
607
|
+
{
|
|
608
|
+
ObVecJsonTableClient.JsonTableDataTBL.jdata: func.json_replace(
|
|
551
609
|
ObVecJsonTableClient.JsonTableDataTBL.jdata,
|
|
552
|
-
f
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
610
|
+
f"$.{col_name}",
|
|
611
|
+
json_value(
|
|
612
|
+
ObVecJsonTableClient.JsonTableDataTBL.jdata,
|
|
613
|
+
f"$.{col_name}",
|
|
614
|
+
col_type_str,
|
|
615
|
+
),
|
|
616
|
+
)
|
|
617
|
+
}
|
|
618
|
+
)
|
|
557
619
|
else:
|
|
558
620
|
model = ObVecJsonTableClient.JsonTableMetadata._parse_col_type(col_type_str)
|
|
559
|
-
datum = model(val=self._calc_default_value(constraints[
|
|
621
|
+
datum = model(val=self._calc_default_value(constraints["jcol_default"]))
|
|
560
622
|
json_val = val2json(datum.val)
|
|
561
623
|
session.query(ObVecJsonTableClient.JsonTableDataTBL).filter_by(
|
|
562
624
|
admin_id=self.admin_id,
|
|
563
625
|
jtable_name=jtable_name,
|
|
564
|
-
).update(
|
|
565
|
-
|
|
566
|
-
ObVecJsonTableClient.JsonTableDataTBL.jdata
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
626
|
+
).update(
|
|
627
|
+
{
|
|
628
|
+
ObVecJsonTableClient.JsonTableDataTBL.jdata: func.json_replace(
|
|
629
|
+
ObVecJsonTableClient.JsonTableDataTBL.jdata,
|
|
630
|
+
f"$.{col_name}",
|
|
631
|
+
func.ifnull(
|
|
632
|
+
json_value(
|
|
633
|
+
ObVecJsonTableClient.JsonTableDataTBL.jdata,
|
|
634
|
+
f"$.{col_name}",
|
|
635
|
+
col_type_str,
|
|
636
|
+
),
|
|
637
|
+
json_val,
|
|
573
638
|
),
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
})
|
|
639
|
+
)
|
|
640
|
+
}
|
|
641
|
+
)
|
|
578
642
|
|
|
579
643
|
def _handle_alter_jtable_rename_table(
|
|
580
644
|
self,
|
|
@@ -584,17 +648,19 @@ class ObVecJsonTableClient(ObVecClient):
|
|
|
584
648
|
):
|
|
585
649
|
if not self._check_table_exists(jtable_name):
|
|
586
650
|
raise ValueError(f"Table {jtable_name} does not exists")
|
|
587
|
-
|
|
651
|
+
|
|
588
652
|
new_table_name = rename.this.this.this
|
|
589
653
|
if self.check_table_exists(new_table_name):
|
|
590
654
|
raise ValueError(f"Table {new_table_name} exists!")
|
|
591
|
-
|
|
655
|
+
|
|
592
656
|
session.query(ObVecJsonTableClient.JsonTableMetaTBL).filter_by(
|
|
593
657
|
user_id=self.admin_id,
|
|
594
658
|
jtable_name=jtable_name,
|
|
595
|
-
).update(
|
|
596
|
-
|
|
597
|
-
|
|
659
|
+
).update(
|
|
660
|
+
{
|
|
661
|
+
ObVecJsonTableClient.JsonTableMetaTBL.jtable_name: new_table_name,
|
|
662
|
+
}
|
|
663
|
+
)
|
|
598
664
|
|
|
599
665
|
def _handle_alter_json_table(self, ast: Expression):
|
|
600
666
|
if not isinstance(ast.this, exp.Table):
|
|
@@ -604,7 +670,7 @@ class ObVecJsonTableClient(ObVecClient):
|
|
|
604
670
|
jtable_name = ast.this.this.this
|
|
605
671
|
if not self._check_table_exists(jtable_name):
|
|
606
672
|
raise ValueError(f"Table {jtable_name} does not exists")
|
|
607
|
-
|
|
673
|
+
|
|
608
674
|
session = self.session()
|
|
609
675
|
session.execute(text("SET @@session.autocommit=0"))
|
|
610
676
|
for action in ast.actions:
|
|
@@ -638,7 +704,7 @@ class ObVecJsonTableClient(ObVecClient):
|
|
|
638
704
|
jtable_name,
|
|
639
705
|
action,
|
|
640
706
|
)
|
|
641
|
-
|
|
707
|
+
|
|
642
708
|
try:
|
|
643
709
|
session.commit()
|
|
644
710
|
self.jmetadata.reflect(self.engine)
|
|
@@ -648,7 +714,9 @@ class ObVecJsonTableClient(ObVecClient):
|
|
|
648
714
|
finally:
|
|
649
715
|
session.close()
|
|
650
716
|
|
|
651
|
-
def _handle_jtable_dml_insert(
|
|
717
|
+
def _handle_jtable_dml_insert(
|
|
718
|
+
self, ast: Expression, opt_user_id: Optional[str] = None
|
|
719
|
+
):
|
|
652
720
|
real_user_id = opt_user_id or self.user_id
|
|
653
721
|
|
|
654
722
|
if real_user_id is None:
|
|
@@ -660,11 +728,12 @@ class ObVecJsonTableClient(ObVecClient):
|
|
|
660
728
|
table_name = ast.this.this.this
|
|
661
729
|
if not self._check_table_exists(table_name):
|
|
662
730
|
raise ValueError(f"Table {table_name} does not exists")
|
|
663
|
-
|
|
664
|
-
table_col_names = [
|
|
731
|
+
|
|
732
|
+
table_col_names = [
|
|
733
|
+
meta["jcol_name"] for meta in self.jmetadata.meta_cache[table_name]
|
|
734
|
+
]
|
|
665
735
|
cols = {
|
|
666
|
-
meta[
|
|
667
|
-
for meta in self.jmetadata.meta_cache[table_name]
|
|
736
|
+
meta["jcol_name"]: meta for meta in self.jmetadata.meta_cache[table_name]
|
|
668
737
|
}
|
|
669
738
|
if isinstance(ast.this, exp.Schema):
|
|
670
739
|
insert_col_names = [expr.this for expr in ast.this.expressions]
|
|
@@ -672,9 +741,14 @@ class ObVecJsonTableClient(ObVecClient):
|
|
|
672
741
|
if col_name not in table_col_names:
|
|
673
742
|
raise ValueError(f"Unknown column {col_name} in field list")
|
|
674
743
|
for meta in self.jmetadata.meta_cache[table_name]:
|
|
675
|
-
if (
|
|
676
|
-
(
|
|
677
|
-
|
|
744
|
+
if (
|
|
745
|
+
(meta["jcol_name"] not in insert_col_names)
|
|
746
|
+
and (not meta["jcol_nullable"])
|
|
747
|
+
and (not meta["jcol_has_default"])
|
|
748
|
+
):
|
|
749
|
+
raise ValueError(
|
|
750
|
+
f"Field {meta['jcol_name']} does not have a default value"
|
|
751
|
+
)
|
|
678
752
|
elif isinstance(ast.this, exp.Table):
|
|
679
753
|
insert_col_names = table_col_names
|
|
680
754
|
else:
|
|
@@ -686,28 +760,34 @@ class ObVecJsonTableClient(ObVecClient):
|
|
|
686
760
|
for tuple in ast.expression.expressions:
|
|
687
761
|
expr_list = tuple.expressions
|
|
688
762
|
if len(expr_list) != len(insert_col_names):
|
|
689
|
-
raise ValueError(
|
|
763
|
+
raise ValueError(
|
|
764
|
+
f"Values Tuple length does not match with the length of insert columns"
|
|
765
|
+
)
|
|
690
766
|
kv = {}
|
|
691
767
|
for col_name, expr in zip(insert_col_names, expr_list):
|
|
692
|
-
model = cols[col_name][
|
|
768
|
+
model = cols[col_name]["jcol_model"]
|
|
693
769
|
datum = model(val=self._calc_default_value(str(expr)))
|
|
694
770
|
kv[col_name] = val2json(datum.val)
|
|
695
771
|
for col_name in table_col_names:
|
|
696
772
|
if col_name not in insert_col_names:
|
|
697
|
-
model = cols[col_name][
|
|
698
|
-
datum = model(
|
|
773
|
+
model = cols[col_name]["jcol_model"]
|
|
774
|
+
datum = model(
|
|
775
|
+
val=self._calc_default_value(cols[col_name]["jcol_default"])
|
|
776
|
+
)
|
|
699
777
|
kv[col_name] = val2json(datum.val)
|
|
700
778
|
|
|
701
779
|
logger.debug(f"================= [INSERT] =============== {kv}")
|
|
702
780
|
|
|
703
|
-
session.add(
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
781
|
+
session.add(
|
|
782
|
+
ObVecJsonTableClient.JsonTableDataTBL(
|
|
783
|
+
user_id=real_user_id,
|
|
784
|
+
admin_id=self.admin_id,
|
|
785
|
+
jtable_name=table_name,
|
|
786
|
+
jdata=kv,
|
|
787
|
+
)
|
|
788
|
+
)
|
|
709
789
|
n_new_records += 1
|
|
710
|
-
|
|
790
|
+
|
|
711
791
|
try:
|
|
712
792
|
session.commit()
|
|
713
793
|
except Exception as e:
|
|
@@ -718,20 +798,24 @@ class ObVecJsonTableClient(ObVecClient):
|
|
|
718
798
|
session.close()
|
|
719
799
|
return n_new_records
|
|
720
800
|
|
|
721
|
-
def _handle_jtable_dml_update(
|
|
801
|
+
def _handle_jtable_dml_update(
|
|
802
|
+
self, ast: Expression, opt_user_id: Optional[str] = None
|
|
803
|
+
):
|
|
722
804
|
real_user_id = opt_user_id or self.user_id
|
|
723
805
|
|
|
724
806
|
table_name = ast.this.this.this
|
|
725
807
|
if not self._check_table_exists(table_name):
|
|
726
808
|
raise ValueError(f"Table {table_name} does not exists")
|
|
727
|
-
|
|
809
|
+
|
|
728
810
|
path_settings = []
|
|
729
811
|
for expr in ast.expressions:
|
|
730
812
|
col_name = expr.this.this.this
|
|
731
813
|
if not self._check_col_exists(table_name, col_name):
|
|
732
814
|
raise ValueError(f"Column {col_name} does not exists")
|
|
733
815
|
col_expr = expr.expression
|
|
734
|
-
new_node = parse_one(
|
|
816
|
+
new_node = parse_one(
|
|
817
|
+
f"JSON_VALUE({JSON_TABLE_DATA_TABLE_NAME}.jdata, '$.{col_name}')"
|
|
818
|
+
)
|
|
735
819
|
for column in col_expr.find_all(exp.Column):
|
|
736
820
|
parent_node = column.parent
|
|
737
821
|
if isinstance(parent_node.args[column.arg_key], list):
|
|
@@ -747,20 +831,20 @@ class ObVecJsonTableClient(ObVecClient):
|
|
|
747
831
|
logger.info(str(col_expr))
|
|
748
832
|
path_settings.append(f"'$.{col_name}', {str(col_expr)}")
|
|
749
833
|
|
|
750
|
-
where_clause = None
|
|
751
|
-
if
|
|
752
|
-
for column in ast.args[
|
|
834
|
+
where_clause = None
|
|
835
|
+
if "where" in ast.args.keys() and ast.args["where"]:
|
|
836
|
+
for column in ast.args["where"].find_all(exp.Column):
|
|
753
837
|
where_col_name = column.this.this
|
|
754
838
|
if not self._check_col_exists(table_name, where_col_name):
|
|
755
839
|
raise ValueError(f"Column {where_col_name} does not exists")
|
|
756
|
-
column.parent.args[
|
|
840
|
+
column.parent.args["this"] = parse_one(
|
|
757
841
|
f"JSON_VALUE({JSON_TABLE_DATA_TABLE_NAME}.jdata, '$.{where_col_name}')"
|
|
758
842
|
)
|
|
759
843
|
if real_user_id:
|
|
760
844
|
where_clause = f"{JSON_TABLE_DATA_TABLE_NAME}.user_id = '{real_user_id}' AND {JSON_TABLE_DATA_TABLE_NAME}.jtable_name = '{table_name}' AND ({str(ast.args['where'].this)})"
|
|
761
845
|
else:
|
|
762
846
|
where_clause = f"{JSON_TABLE_DATA_TABLE_NAME}.jtable_name = '{table_name}' AND ({str(ast.args['where'].this)})"
|
|
763
|
-
|
|
847
|
+
|
|
764
848
|
if where_clause:
|
|
765
849
|
update_sql = f"UPDATE {JSON_TABLE_DATA_TABLE_NAME} SET jdata = JSON_REPLACE({JSON_TABLE_DATA_TABLE_NAME}.jdata, {', '.join(path_settings)}) WHERE {where_clause}"
|
|
766
850
|
else:
|
|
@@ -770,29 +854,33 @@ class ObVecJsonTableClient(ObVecClient):
|
|
|
770
854
|
res = self.perform_raw_text_sql(update_sql)
|
|
771
855
|
return res.rowcount
|
|
772
856
|
|
|
773
|
-
def _handle_jtable_dml_delete(
|
|
857
|
+
def _handle_jtable_dml_delete(
|
|
858
|
+
self, ast: Expression, opt_user_id: Optional[str] = None
|
|
859
|
+
):
|
|
774
860
|
real_user_id = opt_user_id or self.user_id
|
|
775
861
|
|
|
776
862
|
table_name = ast.this.this.this
|
|
777
863
|
if not self._check_table_exists(table_name):
|
|
778
864
|
raise ValueError(f"Table {table_name} does not exists")
|
|
779
|
-
|
|
865
|
+
|
|
780
866
|
where_clause = None
|
|
781
|
-
if
|
|
782
|
-
for column in ast.args[
|
|
867
|
+
if "where" in ast.args.keys() and ast.args["where"]:
|
|
868
|
+
for column in ast.args["where"].find_all(exp.Column):
|
|
783
869
|
where_col_name = column.this.this
|
|
784
870
|
if not self._check_col_exists(table_name, where_col_name):
|
|
785
871
|
raise ValueError(f"Column {where_col_name} does not exists")
|
|
786
|
-
column.parent.args[
|
|
872
|
+
column.parent.args["this"] = parse_one(
|
|
787
873
|
f"JSON_VALUE({JSON_TABLE_DATA_TABLE_NAME}.jdata, '$.{where_col_name}')"
|
|
788
874
|
)
|
|
789
875
|
if real_user_id:
|
|
790
876
|
where_clause = f"{JSON_TABLE_DATA_TABLE_NAME}.user_id = '{real_user_id}' AND {JSON_TABLE_DATA_TABLE_NAME}.jtable_name = '{table_name}' AND ({str(ast.args['where'].this)})"
|
|
791
877
|
else:
|
|
792
878
|
where_clause = f"{JSON_TABLE_DATA_TABLE_NAME}.jtable_name = '{table_name}' AND ({str(ast.args['where'].this)})"
|
|
793
|
-
|
|
879
|
+
|
|
794
880
|
if where_clause:
|
|
795
|
-
delete_sql =
|
|
881
|
+
delete_sql = (
|
|
882
|
+
f"DELETE FROM {JSON_TABLE_DATA_TABLE_NAME} WHERE {where_clause}"
|
|
883
|
+
)
|
|
796
884
|
else:
|
|
797
885
|
delete_sql = f"DELETE FROM {JSON_TABLE_DATA_TABLE_NAME}"
|
|
798
886
|
|
|
@@ -811,16 +899,18 @@ class ObVecJsonTableClient(ObVecClient):
|
|
|
811
899
|
self,
|
|
812
900
|
ast: Expression,
|
|
813
901
|
select_with_data_id: bool = False,
|
|
814
|
-
opt_user_id: Optional[str] = None
|
|
902
|
+
opt_user_id: Optional[str] = None,
|
|
815
903
|
):
|
|
816
904
|
real_user_id = opt_user_id or self.user_id
|
|
817
905
|
|
|
818
|
-
from_key =
|
|
906
|
+
from_key = "from_" if "from_" in ast.args else "from"
|
|
819
907
|
table_name = ast.args[from_key].this.this.this
|
|
820
908
|
if not self._check_table_exists(table_name):
|
|
821
909
|
raise ValueError(f"Table {table_name} does not exists")
|
|
822
|
-
|
|
823
|
-
ast.args[from_key].args[
|
|
910
|
+
|
|
911
|
+
ast.args[from_key].args["this"].args["this"] = to_identifier(
|
|
912
|
+
name=JSON_TABLE_DATA_TABLE_NAME, quoted=False
|
|
913
|
+
)
|
|
824
914
|
|
|
825
915
|
col_meta = self.jmetadata.meta_cache[table_name]
|
|
826
916
|
json_table_meta_str = []
|
|
@@ -830,42 +920,42 @@ class ObVecJsonTableClient(ObVecClient):
|
|
|
830
920
|
f"{meta['jcol_name']} {self._get_full_datatype(meta['jcol_type'])} "
|
|
831
921
|
f"PATH '$.{meta['jcol_name']}'"
|
|
832
922
|
)
|
|
833
|
-
all_jcol_names.append(meta[
|
|
834
|
-
|
|
923
|
+
all_jcol_names.append(meta["jcol_name"])
|
|
924
|
+
|
|
835
925
|
need_replace_select_exprs = False
|
|
836
926
|
new_select_exprs = []
|
|
837
927
|
|
|
838
928
|
if select_with_data_id:
|
|
839
929
|
data_id_col_expr = exp.Column()
|
|
840
930
|
data_id_identifier = exp.Identifier()
|
|
841
|
-
data_id_identifier.args[
|
|
842
|
-
data_id_identifier.args[
|
|
931
|
+
data_id_identifier.args["this"] = "jdata_id"
|
|
932
|
+
data_id_identifier.args["quoted"] = False
|
|
843
933
|
data_json_table_identifier = exp.Identifier()
|
|
844
|
-
data_json_table_identifier.args[
|
|
845
|
-
data_json_table_identifier.args[
|
|
846
|
-
data_id_col_expr.args[
|
|
847
|
-
data_id_col_expr.args[
|
|
934
|
+
data_json_table_identifier.args["this"] = JSON_TABLE_DATA_TABLE_NAME
|
|
935
|
+
data_json_table_identifier.args["quoted"] = False
|
|
936
|
+
data_id_col_expr.args["this"] = data_id_identifier
|
|
937
|
+
data_id_col_expr.args["table"] = data_json_table_identifier
|
|
848
938
|
new_select_exprs.append(data_id_col_expr)
|
|
849
939
|
need_replace_select_exprs = True
|
|
850
940
|
|
|
851
941
|
alias_names = set()
|
|
852
|
-
for select_expr in ast.args[
|
|
942
|
+
for select_expr in ast.args["expressions"]:
|
|
853
943
|
if isinstance(select_expr, exp.Star):
|
|
854
944
|
need_replace_select_exprs = True
|
|
855
945
|
for jcol_name in all_jcol_names:
|
|
856
946
|
col_expr = exp.Column()
|
|
857
947
|
identifier = exp.Identifier()
|
|
858
|
-
identifier.args[
|
|
859
|
-
identifier.args[
|
|
860
|
-
col_expr.args[
|
|
948
|
+
identifier.args["this"] = jcol_name
|
|
949
|
+
identifier.args["quoted"] = False
|
|
950
|
+
col_expr.args["this"] = identifier
|
|
861
951
|
new_select_exprs.append(col_expr)
|
|
862
952
|
else:
|
|
863
953
|
if isinstance(select_expr, exp.Alias):
|
|
864
|
-
alias_names.add(select_expr.args[
|
|
954
|
+
alias_names.add(select_expr.args["alias"].args["this"])
|
|
865
955
|
new_select_exprs.append(select_expr)
|
|
866
956
|
if need_replace_select_exprs:
|
|
867
|
-
ast.args[
|
|
868
|
-
|
|
957
|
+
ast.args["expressions"] = new_select_exprs
|
|
958
|
+
|
|
869
959
|
tmp_table_name = "__tmp"
|
|
870
960
|
json_table_str = f"json_table({JSON_TABLE_DATA_TABLE_NAME}.jdata, '$' COLUMNS ({', '.join(json_table_meta_str)})) {tmp_table_name}"
|
|
871
961
|
|
|
@@ -873,41 +963,43 @@ class ObVecJsonTableClient(ObVecClient):
|
|
|
873
963
|
col_name = col.args["this"].args["this"]
|
|
874
964
|
if col_name in alias_names:
|
|
875
965
|
continue
|
|
876
|
-
if
|
|
877
|
-
if col.args[
|
|
878
|
-
col.args[
|
|
966
|
+
if "table" in col.args.keys():
|
|
967
|
+
if col.args["table"].args["this"] != JSON_TABLE_DATA_TABLE_NAME:
|
|
968
|
+
col.args["table"].args["this"] = tmp_table_name
|
|
879
969
|
else:
|
|
880
970
|
identifier = exp.Identifier()
|
|
881
|
-
identifier.args[
|
|
882
|
-
identifier.args[
|
|
883
|
-
col.args[
|
|
971
|
+
identifier.args["this"] = tmp_table_name
|
|
972
|
+
identifier.args["quoted"] = False
|
|
973
|
+
col.args["table"] = identifier
|
|
884
974
|
|
|
885
975
|
# Manually create the JOIN node for json_table
|
|
886
976
|
# In some versions of sqlglot, comma-separated tables may not be parsed as
|
|
887
977
|
# explicit JOINS, so we directly parse the json_table expression and create a JOIN node
|
|
888
978
|
# explicitly
|
|
889
979
|
json_table_expr = parse_one(json_table_str, dialect="oceanbase")
|
|
890
|
-
|
|
980
|
+
|
|
891
981
|
join_node = exp.Join()
|
|
892
|
-
join_node.args[
|
|
893
|
-
join_node.args[
|
|
894
|
-
|
|
895
|
-
if
|
|
896
|
-
ast.args[
|
|
897
|
-
ast.args[
|
|
982
|
+
join_node.args["this"] = json_table_expr
|
|
983
|
+
join_node.args["kind"] = None # CROSS JOIN (implicit join with comma)
|
|
984
|
+
|
|
985
|
+
if "joins" not in ast.args:
|
|
986
|
+
ast.args["joins"] = []
|
|
987
|
+
ast.args["joins"].append(join_node)
|
|
898
988
|
|
|
899
989
|
if real_user_id:
|
|
900
990
|
extra_filter_str = f"{JSON_TABLE_DATA_TABLE_NAME}.user_id = '{real_user_id}' AND {JSON_TABLE_DATA_TABLE_NAME}.jtable_name = '{table_name}'"
|
|
901
991
|
else:
|
|
902
|
-
extra_filter_str =
|
|
903
|
-
|
|
904
|
-
|
|
992
|
+
extra_filter_str = (
|
|
993
|
+
f"{JSON_TABLE_DATA_TABLE_NAME}.jtable_name = '{table_name}'"
|
|
994
|
+
)
|
|
995
|
+
if "where" in ast.args.keys():
|
|
996
|
+
filter_str = str(ast.args["where"].args["this"])
|
|
905
997
|
new_filter_str = f"{extra_filter_str} AND ({filter_str})"
|
|
906
|
-
ast.args[
|
|
998
|
+
ast.args["where"].args["this"] = parse_one(new_filter_str)
|
|
907
999
|
else:
|
|
908
1000
|
where_clause = exp.Where()
|
|
909
|
-
where_clause.args[
|
|
910
|
-
ast.args[
|
|
1001
|
+
where_clause.args["this"] = parse_one(extra_filter_str)
|
|
1002
|
+
ast.args["where"] = where_clause
|
|
911
1003
|
|
|
912
1004
|
select_sql = ast.sql(dialect="mysql", identify=True)
|
|
913
1005
|
logger.debug(f"===================== do select: {select_sql}")
|