sqlspec 0.27.0__py3-none-any.whl → 0.28.0__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.
Potentially problematic release.
This version of sqlspec might be problematic. Click here for more details.
- sqlspec/_typing.py +93 -0
- sqlspec/adapters/adbc/adk/store.py +21 -11
- sqlspec/adapters/adbc/data_dictionary.py +27 -5
- sqlspec/adapters/adbc/driver.py +83 -14
- sqlspec/adapters/aiosqlite/adk/store.py +27 -18
- sqlspec/adapters/asyncmy/adk/store.py +26 -16
- sqlspec/adapters/asyncpg/adk/store.py +26 -16
- sqlspec/adapters/asyncpg/data_dictionary.py +24 -17
- sqlspec/adapters/bigquery/adk/store.py +30 -21
- sqlspec/adapters/bigquery/config.py +11 -0
- sqlspec/adapters/bigquery/driver.py +138 -1
- sqlspec/adapters/duckdb/adk/store.py +21 -11
- sqlspec/adapters/duckdb/driver.py +87 -1
- sqlspec/adapters/oracledb/adk/store.py +89 -206
- sqlspec/adapters/oracledb/driver.py +183 -2
- sqlspec/adapters/oracledb/litestar/store.py +22 -24
- sqlspec/adapters/psqlpy/adk/store.py +28 -27
- sqlspec/adapters/psqlpy/data_dictionary.py +24 -17
- sqlspec/adapters/psqlpy/driver.py +7 -10
- sqlspec/adapters/psycopg/adk/store.py +51 -33
- sqlspec/adapters/psycopg/data_dictionary.py +48 -34
- sqlspec/adapters/sqlite/adk/store.py +29 -19
- sqlspec/config.py +100 -2
- sqlspec/core/filters.py +18 -10
- sqlspec/core/result.py +133 -2
- sqlspec/driver/_async.py +89 -0
- sqlspec/driver/_common.py +64 -29
- sqlspec/driver/_sync.py +95 -0
- sqlspec/extensions/adk/migrations/0001_create_adk_tables.py +2 -2
- sqlspec/extensions/adk/service.py +3 -3
- sqlspec/extensions/adk/store.py +8 -8
- sqlspec/extensions/aiosql/adapter.py +3 -15
- sqlspec/extensions/fastapi/__init__.py +21 -0
- sqlspec/extensions/fastapi/extension.py +331 -0
- sqlspec/extensions/fastapi/providers.py +543 -0
- sqlspec/extensions/flask/__init__.py +36 -0
- sqlspec/extensions/flask/_state.py +71 -0
- sqlspec/extensions/flask/_utils.py +40 -0
- sqlspec/extensions/flask/extension.py +389 -0
- sqlspec/extensions/litestar/config.py +3 -6
- sqlspec/extensions/litestar/plugin.py +26 -2
- sqlspec/extensions/starlette/__init__.py +10 -0
- sqlspec/extensions/starlette/_state.py +25 -0
- sqlspec/extensions/starlette/_utils.py +52 -0
- sqlspec/extensions/starlette/extension.py +254 -0
- sqlspec/extensions/starlette/middleware.py +154 -0
- sqlspec/protocols.py +40 -0
- sqlspec/storage/_utils.py +1 -14
- sqlspec/storage/backends/fsspec.py +3 -5
- sqlspec/storage/backends/local.py +1 -1
- sqlspec/storage/backends/obstore.py +10 -18
- sqlspec/typing.py +16 -0
- sqlspec/utils/__init__.py +25 -4
- sqlspec/utils/arrow_helpers.py +81 -0
- sqlspec/utils/module_loader.py +203 -3
- sqlspec/utils/portal.py +311 -0
- sqlspec/utils/serializers.py +110 -1
- sqlspec/utils/sync_tools.py +15 -5
- sqlspec/utils/type_guards.py +25 -0
- {sqlspec-0.27.0.dist-info → sqlspec-0.28.0.dist-info}/METADATA +2 -2
- {sqlspec-0.27.0.dist-info → sqlspec-0.28.0.dist-info}/RECORD +64 -50
- {sqlspec-0.27.0.dist-info → sqlspec-0.28.0.dist-info}/WHEEL +0 -0
- {sqlspec-0.27.0.dist-info → sqlspec-0.28.0.dist-info}/entry_points.txt +0 -0
- {sqlspec-0.27.0.dist-info → sqlspec-0.28.0.dist-info}/licenses/LICENSE +0 -0
|
@@ -116,16 +116,31 @@ class OracleAsyncADKStore(BaseAsyncADKStore["OracleAsyncConfig"]):
|
|
|
116
116
|
- session_table: Sessions table name (default: "adk_sessions")
|
|
117
117
|
- events_table: Events table name (default: "adk_events")
|
|
118
118
|
- owner_id_column: Optional owner FK column DDL (default: None)
|
|
119
|
-
- in_memory: Enable INMEMORY clause (default: False)
|
|
119
|
+
- in_memory: Enable INMEMORY PRIORITY HIGH clause (default: False)
|
|
120
120
|
"""
|
|
121
121
|
super().__init__(config)
|
|
122
122
|
self._json_storage_type: JSONStorageType | None = None
|
|
123
123
|
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
124
|
+
adk_config = config.extension_config.get("adk", {})
|
|
125
|
+
self._in_memory: bool = bool(adk_config.get("in_memory", False))
|
|
126
|
+
|
|
127
|
+
async def _get_create_sessions_table_sql(self) -> str:
|
|
128
|
+
"""Get Oracle CREATE TABLE SQL for sessions table.
|
|
129
|
+
|
|
130
|
+
Auto-detects optimal JSON storage type based on Oracle version.
|
|
131
|
+
Result is cached to minimize database queries.
|
|
132
|
+
"""
|
|
133
|
+
storage_type = await self._detect_json_storage_type()
|
|
134
|
+
return self._get_create_sessions_table_sql_for_type(storage_type)
|
|
135
|
+
|
|
136
|
+
async def _get_create_events_table_sql(self) -> str:
|
|
137
|
+
"""Get Oracle CREATE TABLE SQL for events table.
|
|
138
|
+
|
|
139
|
+
Auto-detects optimal JSON storage type based on Oracle version.
|
|
140
|
+
Result is cached to minimize database queries.
|
|
141
|
+
"""
|
|
142
|
+
storage_type = await self._detect_json_storage_type()
|
|
143
|
+
return self._get_create_events_table_sql_for_type(storage_type)
|
|
129
144
|
|
|
130
145
|
async def _detect_json_storage_type(self) -> JSONStorageType:
|
|
131
146
|
"""Detect the appropriate JSON storage type based on Oracle version.
|
|
@@ -292,7 +307,7 @@ class OracleAsyncADKStore(BaseAsyncADKStore["OracleAsyncConfig"]):
|
|
|
292
307
|
state_column = "state BLOB NOT NULL"
|
|
293
308
|
|
|
294
309
|
owner_id_column_sql = f", {self._owner_id_column_ddl}" if self._owner_id_column_ddl else ""
|
|
295
|
-
inmemory_clause = " INMEMORY" if self._in_memory else ""
|
|
310
|
+
inmemory_clause = " INMEMORY PRIORITY HIGH" if self._in_memory else ""
|
|
296
311
|
|
|
297
312
|
return f"""
|
|
298
313
|
BEGIN
|
|
@@ -363,7 +378,7 @@ class OracleAsyncADKStore(BaseAsyncADKStore["OracleAsyncConfig"]):
|
|
|
363
378
|
long_running_tool_ids_json BLOB
|
|
364
379
|
"""
|
|
365
380
|
|
|
366
|
-
inmemory_clause = " INMEMORY" if self._in_memory else ""
|
|
381
|
+
inmemory_clause = " INMEMORY PRIORITY HIGH" if self._in_memory else ""
|
|
367
382
|
|
|
368
383
|
return f"""
|
|
369
384
|
BEGIN
|
|
@@ -404,89 +419,6 @@ class OracleAsyncADKStore(BaseAsyncADKStore["OracleAsyncConfig"]):
|
|
|
404
419
|
END;
|
|
405
420
|
"""
|
|
406
421
|
|
|
407
|
-
def _get_create_sessions_table_sql(self) -> str:
|
|
408
|
-
"""Get Oracle CREATE TABLE SQL for sessions.
|
|
409
|
-
|
|
410
|
-
Returns:
|
|
411
|
-
SQL statement to create adk_sessions table with indexes.
|
|
412
|
-
|
|
413
|
-
Notes:
|
|
414
|
-
- VARCHAR2(128) for IDs and names
|
|
415
|
-
- CLOB with IS JSON constraint for state storage
|
|
416
|
-
- TIMESTAMP WITH TIME ZONE for timezone-aware timestamps
|
|
417
|
-
- SYSTIMESTAMP for default current timestamp
|
|
418
|
-
- Composite index on (app_name, user_id) for listing
|
|
419
|
-
- Index on update_time DESC for recent session queries
|
|
420
|
-
"""
|
|
421
|
-
return f"""
|
|
422
|
-
BEGIN
|
|
423
|
-
EXECUTE IMMEDIATE 'CREATE TABLE {self._session_table} (
|
|
424
|
-
id VARCHAR2(128) PRIMARY KEY,
|
|
425
|
-
app_name VARCHAR2(128) NOT NULL,
|
|
426
|
-
user_id VARCHAR2(128) NOT NULL,
|
|
427
|
-
state CLOB CHECK (state IS JSON),
|
|
428
|
-
create_time TIMESTAMP WITH TIME ZONE DEFAULT SYSTIMESTAMP NOT NULL,
|
|
429
|
-
update_time TIMESTAMP WITH TIME ZONE DEFAULT SYSTIMESTAMP NOT NULL
|
|
430
|
-
)';
|
|
431
|
-
EXCEPTION
|
|
432
|
-
WHEN OTHERS THEN
|
|
433
|
-
IF SQLCODE != -955 THEN
|
|
434
|
-
RAISE;
|
|
435
|
-
END IF;
|
|
436
|
-
END;
|
|
437
|
-
"""
|
|
438
|
-
|
|
439
|
-
def _get_create_events_table_sql(self) -> str:
|
|
440
|
-
"""Get Oracle CREATE TABLE SQL for events (legacy method).
|
|
441
|
-
|
|
442
|
-
Returns:
|
|
443
|
-
SQL statement to create adk_events table with indexes.
|
|
444
|
-
|
|
445
|
-
Notes:
|
|
446
|
-
DEPRECATED: Use _get_create_events_table_sql_for_type() instead.
|
|
447
|
-
This method uses BLOB with IS JSON constraints (12c+ compatible).
|
|
448
|
-
|
|
449
|
-
- VARCHAR2 sizes: id(128), session_id(128), invocation_id(256), author(256),
|
|
450
|
-
branch(256), error_code(256), error_message(1024)
|
|
451
|
-
- BLOB for pickled actions
|
|
452
|
-
- BLOB with IS JSON for all JSON fields (content, grounding_metadata,
|
|
453
|
-
custom_metadata, long_running_tool_ids_json)
|
|
454
|
-
- NUMBER(1) for partial, turn_complete, interrupted
|
|
455
|
-
- Foreign key to sessions with CASCADE delete
|
|
456
|
-
- Index on (session_id, timestamp ASC) for ordered event retrieval
|
|
457
|
-
"""
|
|
458
|
-
return f"""
|
|
459
|
-
BEGIN
|
|
460
|
-
EXECUTE IMMEDIATE 'CREATE TABLE {self._events_table} (
|
|
461
|
-
id VARCHAR2(128) PRIMARY KEY,
|
|
462
|
-
session_id VARCHAR2(128) NOT NULL,
|
|
463
|
-
app_name VARCHAR2(128) NOT NULL,
|
|
464
|
-
user_id VARCHAR2(128) NOT NULL,
|
|
465
|
-
invocation_id VARCHAR2(256),
|
|
466
|
-
author VARCHAR2(256),
|
|
467
|
-
actions BLOB,
|
|
468
|
-
branch VARCHAR2(256),
|
|
469
|
-
timestamp TIMESTAMP WITH TIME ZONE DEFAULT SYSTIMESTAMP NOT NULL,
|
|
470
|
-
content BLOB CHECK (content IS JSON),
|
|
471
|
-
grounding_metadata BLOB CHECK (grounding_metadata IS JSON),
|
|
472
|
-
custom_metadata BLOB CHECK (custom_metadata IS JSON),
|
|
473
|
-
long_running_tool_ids_json BLOB CHECK (long_running_tool_ids_json IS JSON),
|
|
474
|
-
partial NUMBER(1),
|
|
475
|
-
turn_complete NUMBER(1),
|
|
476
|
-
interrupted NUMBER(1),
|
|
477
|
-
error_code VARCHAR2(256),
|
|
478
|
-
error_message VARCHAR2(1024),
|
|
479
|
-
CONSTRAINT fk_{self._events_table}_session FOREIGN KEY (session_id)
|
|
480
|
-
REFERENCES {self._session_table}(id) ON DELETE CASCADE
|
|
481
|
-
)';
|
|
482
|
-
EXCEPTION
|
|
483
|
-
WHEN OTHERS THEN
|
|
484
|
-
IF SQLCODE != -955 THEN
|
|
485
|
-
RAISE;
|
|
486
|
-
END IF;
|
|
487
|
-
END;
|
|
488
|
-
"""
|
|
489
|
-
|
|
490
422
|
def _get_drop_tables_sql(self) -> "list[str]":
|
|
491
423
|
"""Get Oracle DROP TABLE SQL statements.
|
|
492
424
|
|
|
@@ -561,8 +493,7 @@ class OracleAsyncADKStore(BaseAsyncADKStore["OracleAsyncConfig"]):
|
|
|
561
493
|
logger.info("Creating ADK tables with storage type: %s", storage_type)
|
|
562
494
|
|
|
563
495
|
async with self._config.provide_session() as driver:
|
|
564
|
-
|
|
565
|
-
await driver.execute_script(sessions_sql)
|
|
496
|
+
await driver.execute_script(self._get_create_sessions_table_sql_for_type(storage_type))
|
|
566
497
|
|
|
567
498
|
await driver.execute_script(self._get_create_events_table_sql_for_type(storage_type))
|
|
568
499
|
|
|
@@ -704,32 +635,42 @@ class OracleAsyncADKStore(BaseAsyncADKStore["OracleAsyncConfig"]):
|
|
|
704
635
|
await cursor.execute(sql, {"id": session_id})
|
|
705
636
|
await conn.commit()
|
|
706
637
|
|
|
707
|
-
async def list_sessions(self, app_name: str, user_id: str) -> "list[SessionRecord]":
|
|
708
|
-
"""List
|
|
638
|
+
async def list_sessions(self, app_name: str, user_id: str | None = None) -> "list[SessionRecord]":
|
|
639
|
+
"""List sessions for an app, optionally filtered by user.
|
|
709
640
|
|
|
710
641
|
Args:
|
|
711
642
|
app_name: Application name.
|
|
712
|
-
user_id: User identifier.
|
|
643
|
+
user_id: User identifier. If None, lists all sessions for the app.
|
|
713
644
|
|
|
714
645
|
Returns:
|
|
715
646
|
List of session records ordered by update_time DESC.
|
|
716
647
|
|
|
717
648
|
Notes:
|
|
718
|
-
Uses composite index on (app_name, user_id).
|
|
649
|
+
Uses composite index on (app_name, user_id) when user_id is provided.
|
|
719
650
|
State is deserialized using version-appropriate format.
|
|
720
651
|
"""
|
|
721
652
|
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
653
|
+
if user_id is None:
|
|
654
|
+
sql = f"""
|
|
655
|
+
SELECT id, app_name, user_id, state, create_time, update_time
|
|
656
|
+
FROM {self._session_table}
|
|
657
|
+
WHERE app_name = :app_name
|
|
658
|
+
ORDER BY update_time DESC
|
|
659
|
+
"""
|
|
660
|
+
params = {"app_name": app_name}
|
|
661
|
+
else:
|
|
662
|
+
sql = f"""
|
|
663
|
+
SELECT id, app_name, user_id, state, create_time, update_time
|
|
664
|
+
FROM {self._session_table}
|
|
665
|
+
WHERE app_name = :app_name AND user_id = :user_id
|
|
666
|
+
ORDER BY update_time DESC
|
|
667
|
+
"""
|
|
668
|
+
params = {"app_name": app_name, "user_id": user_id}
|
|
728
669
|
|
|
729
670
|
try:
|
|
730
671
|
async with self._config.provide_connection() as conn:
|
|
731
672
|
cursor = conn.cursor()
|
|
732
|
-
await cursor.execute(sql,
|
|
673
|
+
await cursor.execute(sql, params)
|
|
733
674
|
rows = await cursor.fetchall()
|
|
734
675
|
|
|
735
676
|
results = []
|
|
@@ -953,16 +894,31 @@ class OracleSyncADKStore(BaseSyncADKStore["OracleSyncConfig"]):
|
|
|
953
894
|
- session_table: Sessions table name (default: "adk_sessions")
|
|
954
895
|
- events_table: Events table name (default: "adk_events")
|
|
955
896
|
- owner_id_column: Optional owner FK column DDL (default: None)
|
|
956
|
-
- in_memory: Enable INMEMORY clause (default: False)
|
|
897
|
+
- in_memory: Enable INMEMORY PRIORITY HIGH clause (default: False)
|
|
957
898
|
"""
|
|
958
899
|
super().__init__(config)
|
|
959
900
|
self._json_storage_type: JSONStorageType | None = None
|
|
960
901
|
|
|
961
|
-
|
|
962
|
-
|
|
963
|
-
|
|
964
|
-
|
|
965
|
-
|
|
902
|
+
adk_config = config.extension_config.get("adk", {})
|
|
903
|
+
self._in_memory: bool = bool(adk_config.get("in_memory", False))
|
|
904
|
+
|
|
905
|
+
def _get_create_sessions_table_sql(self) -> str:
|
|
906
|
+
"""Get Oracle CREATE TABLE SQL for sessions table.
|
|
907
|
+
|
|
908
|
+
Auto-detects optimal JSON storage type based on Oracle version.
|
|
909
|
+
Result is cached to minimize database queries.
|
|
910
|
+
"""
|
|
911
|
+
storage_type = self._detect_json_storage_type()
|
|
912
|
+
return self._get_create_sessions_table_sql_for_type(storage_type)
|
|
913
|
+
|
|
914
|
+
def _get_create_events_table_sql(self) -> str:
|
|
915
|
+
"""Get Oracle CREATE TABLE SQL for events table.
|
|
916
|
+
|
|
917
|
+
Auto-detects optimal JSON storage type based on Oracle version.
|
|
918
|
+
Result is cached to minimize database queries.
|
|
919
|
+
"""
|
|
920
|
+
storage_type = self._detect_json_storage_type()
|
|
921
|
+
return self._get_create_events_table_sql_for_type(storage_type)
|
|
966
922
|
|
|
967
923
|
def _detect_json_storage_type(self) -> JSONStorageType:
|
|
968
924
|
"""Detect the appropriate JSON storage type based on Oracle version.
|
|
@@ -1129,7 +1085,7 @@ class OracleSyncADKStore(BaseSyncADKStore["OracleSyncConfig"]):
|
|
|
1129
1085
|
state_column = "state BLOB NOT NULL"
|
|
1130
1086
|
|
|
1131
1087
|
owner_id_column_sql = f", {self._owner_id_column_ddl}" if self._owner_id_column_ddl else ""
|
|
1132
|
-
inmemory_clause = " INMEMORY" if self._in_memory else ""
|
|
1088
|
+
inmemory_clause = " INMEMORY PRIORITY HIGH" if self._in_memory else ""
|
|
1133
1089
|
|
|
1134
1090
|
return f"""
|
|
1135
1091
|
BEGIN
|
|
@@ -1200,7 +1156,7 @@ class OracleSyncADKStore(BaseSyncADKStore["OracleSyncConfig"]):
|
|
|
1200
1156
|
long_running_tool_ids_json BLOB
|
|
1201
1157
|
"""
|
|
1202
1158
|
|
|
1203
|
-
inmemory_clause = " INMEMORY" if self._in_memory else ""
|
|
1159
|
+
inmemory_clause = " INMEMORY PRIORITY HIGH" if self._in_memory else ""
|
|
1204
1160
|
|
|
1205
1161
|
return f"""
|
|
1206
1162
|
BEGIN
|
|
@@ -1241,89 +1197,6 @@ class OracleSyncADKStore(BaseSyncADKStore["OracleSyncConfig"]):
|
|
|
1241
1197
|
END;
|
|
1242
1198
|
"""
|
|
1243
1199
|
|
|
1244
|
-
def _get_create_sessions_table_sql(self) -> str:
|
|
1245
|
-
"""Get Oracle CREATE TABLE SQL for sessions.
|
|
1246
|
-
|
|
1247
|
-
Returns:
|
|
1248
|
-
SQL statement to create adk_sessions table with indexes.
|
|
1249
|
-
|
|
1250
|
-
Notes:
|
|
1251
|
-
- VARCHAR2(128) for IDs and names
|
|
1252
|
-
- CLOB with IS JSON constraint for state storage
|
|
1253
|
-
- TIMESTAMP WITH TIME ZONE for timezone-aware timestamps
|
|
1254
|
-
- SYSTIMESTAMP for default current timestamp
|
|
1255
|
-
- Composite index on (app_name, user_id) for listing
|
|
1256
|
-
- Index on update_time DESC for recent session queries
|
|
1257
|
-
"""
|
|
1258
|
-
return f"""
|
|
1259
|
-
BEGIN
|
|
1260
|
-
EXECUTE IMMEDIATE 'CREATE TABLE {self._session_table} (
|
|
1261
|
-
id VARCHAR2(128) PRIMARY KEY,
|
|
1262
|
-
app_name VARCHAR2(128) NOT NULL,
|
|
1263
|
-
user_id VARCHAR2(128) NOT NULL,
|
|
1264
|
-
state CLOB CHECK (state IS JSON),
|
|
1265
|
-
create_time TIMESTAMP WITH TIME ZONE DEFAULT SYSTIMESTAMP NOT NULL,
|
|
1266
|
-
update_time TIMESTAMP WITH TIME ZONE DEFAULT SYSTIMESTAMP NOT NULL
|
|
1267
|
-
)';
|
|
1268
|
-
EXCEPTION
|
|
1269
|
-
WHEN OTHERS THEN
|
|
1270
|
-
IF SQLCODE != -955 THEN
|
|
1271
|
-
RAISE;
|
|
1272
|
-
END IF;
|
|
1273
|
-
END;
|
|
1274
|
-
"""
|
|
1275
|
-
|
|
1276
|
-
def _get_create_events_table_sql(self) -> str:
|
|
1277
|
-
"""Get Oracle CREATE TABLE SQL for events (legacy method).
|
|
1278
|
-
|
|
1279
|
-
Returns:
|
|
1280
|
-
SQL statement to create adk_events table with indexes.
|
|
1281
|
-
|
|
1282
|
-
Notes:
|
|
1283
|
-
DEPRECATED: Use _get_create_events_table_sql_for_type() instead.
|
|
1284
|
-
This method uses BLOB with IS JSON constraints (12c+ compatible).
|
|
1285
|
-
|
|
1286
|
-
- VARCHAR2 sizes: id(128), session_id(128), invocation_id(256), author(256),
|
|
1287
|
-
branch(256), error_code(256), error_message(1024)
|
|
1288
|
-
- BLOB for pickled actions
|
|
1289
|
-
- BLOB with IS JSON for all JSON fields (content, grounding_metadata,
|
|
1290
|
-
custom_metadata, long_running_tool_ids_json)
|
|
1291
|
-
- NUMBER(1) for partial, turn_complete, interrupted
|
|
1292
|
-
- Foreign key to sessions with CASCADE delete
|
|
1293
|
-
- Index on (session_id, timestamp ASC) for ordered event retrieval
|
|
1294
|
-
"""
|
|
1295
|
-
return f"""
|
|
1296
|
-
BEGIN
|
|
1297
|
-
EXECUTE IMMEDIATE 'CREATE TABLE {self._events_table} (
|
|
1298
|
-
id VARCHAR2(128) PRIMARY KEY,
|
|
1299
|
-
session_id VARCHAR2(128) NOT NULL,
|
|
1300
|
-
app_name VARCHAR2(128) NOT NULL,
|
|
1301
|
-
user_id VARCHAR2(128) NOT NULL,
|
|
1302
|
-
invocation_id VARCHAR2(256),
|
|
1303
|
-
author VARCHAR2(256),
|
|
1304
|
-
actions BLOB,
|
|
1305
|
-
branch VARCHAR2(256),
|
|
1306
|
-
timestamp TIMESTAMP WITH TIME ZONE DEFAULT SYSTIMESTAMP NOT NULL,
|
|
1307
|
-
content BLOB CHECK (content IS JSON),
|
|
1308
|
-
grounding_metadata BLOB CHECK (grounding_metadata IS JSON),
|
|
1309
|
-
custom_metadata BLOB CHECK (custom_metadata IS JSON),
|
|
1310
|
-
long_running_tool_ids_json BLOB CHECK (long_running_tool_ids_json IS JSON),
|
|
1311
|
-
partial NUMBER(1),
|
|
1312
|
-
turn_complete NUMBER(1),
|
|
1313
|
-
interrupted NUMBER(1),
|
|
1314
|
-
error_code VARCHAR2(256),
|
|
1315
|
-
error_message VARCHAR2(1024),
|
|
1316
|
-
CONSTRAINT fk_{self._events_table}_session FOREIGN KEY (session_id)
|
|
1317
|
-
REFERENCES {self._session_table}(id) ON DELETE CASCADE
|
|
1318
|
-
)';
|
|
1319
|
-
EXCEPTION
|
|
1320
|
-
WHEN OTHERS THEN
|
|
1321
|
-
IF SQLCODE != -955 THEN
|
|
1322
|
-
RAISE;
|
|
1323
|
-
END IF;
|
|
1324
|
-
END;
|
|
1325
|
-
"""
|
|
1326
|
-
|
|
1327
1200
|
def _get_drop_tables_sql(self) -> "list[str]":
|
|
1328
1201
|
"""Get Oracle DROP TABLE SQL statements.
|
|
1329
1202
|
|
|
@@ -1542,32 +1415,42 @@ class OracleSyncADKStore(BaseSyncADKStore["OracleSyncConfig"]):
|
|
|
1542
1415
|
cursor.execute(sql, {"id": session_id})
|
|
1543
1416
|
conn.commit()
|
|
1544
1417
|
|
|
1545
|
-
def list_sessions(self, app_name: str, user_id: str) -> "list[SessionRecord]":
|
|
1546
|
-
"""List
|
|
1418
|
+
def list_sessions(self, app_name: str, user_id: str | None = None) -> "list[SessionRecord]":
|
|
1419
|
+
"""List sessions for an app, optionally filtered by user.
|
|
1547
1420
|
|
|
1548
1421
|
Args:
|
|
1549
1422
|
app_name: Application name.
|
|
1550
|
-
user_id: User identifier.
|
|
1423
|
+
user_id: User identifier. If None, lists all sessions for the app.
|
|
1551
1424
|
|
|
1552
1425
|
Returns:
|
|
1553
1426
|
List of session records ordered by update_time DESC.
|
|
1554
1427
|
|
|
1555
1428
|
Notes:
|
|
1556
|
-
Uses composite index on (app_name, user_id).
|
|
1429
|
+
Uses composite index on (app_name, user_id) when user_id is provided.
|
|
1557
1430
|
State is deserialized using version-appropriate format.
|
|
1558
1431
|
"""
|
|
1559
1432
|
|
|
1560
|
-
|
|
1561
|
-
|
|
1562
|
-
|
|
1563
|
-
|
|
1564
|
-
|
|
1565
|
-
|
|
1433
|
+
if user_id is None:
|
|
1434
|
+
sql = f"""
|
|
1435
|
+
SELECT id, app_name, user_id, state, create_time, update_time
|
|
1436
|
+
FROM {self._session_table}
|
|
1437
|
+
WHERE app_name = :app_name
|
|
1438
|
+
ORDER BY update_time DESC
|
|
1439
|
+
"""
|
|
1440
|
+
params = {"app_name": app_name}
|
|
1441
|
+
else:
|
|
1442
|
+
sql = f"""
|
|
1443
|
+
SELECT id, app_name, user_id, state, create_time, update_time
|
|
1444
|
+
FROM {self._session_table}
|
|
1445
|
+
WHERE app_name = :app_name AND user_id = :user_id
|
|
1446
|
+
ORDER BY update_time DESC
|
|
1447
|
+
"""
|
|
1448
|
+
params = {"app_name": app_name, "user_id": user_id}
|
|
1566
1449
|
|
|
1567
1450
|
try:
|
|
1568
1451
|
with self._config.provide_connection() as conn:
|
|
1569
1452
|
cursor = conn.cursor()
|
|
1570
|
-
cursor.execute(sql,
|
|
1453
|
+
cursor.execute(sql, params)
|
|
1571
1454
|
rows = cursor.fetchall()
|
|
1572
1455
|
|
|
1573
1456
|
results = []
|
|
@@ -13,7 +13,8 @@ from sqlspec.adapters.oracledb.data_dictionary import OracleAsyncDataDictionary,
|
|
|
13
13
|
from sqlspec.adapters.oracledb.type_converter import OracleTypeConverter
|
|
14
14
|
from sqlspec.core.cache import get_cache_config
|
|
15
15
|
from sqlspec.core.parameters import ParameterStyle, ParameterStyleConfig
|
|
16
|
-
from sqlspec.core.
|
|
16
|
+
from sqlspec.core.result import create_arrow_result
|
|
17
|
+
from sqlspec.core.statement import SQL, StatementConfig
|
|
17
18
|
from sqlspec.driver import (
|
|
18
19
|
AsyncDataDictionaryBase,
|
|
19
20
|
AsyncDriverAdapterBase,
|
|
@@ -33,14 +34,18 @@ from sqlspec.exceptions import (
|
|
|
33
34
|
TransactionError,
|
|
34
35
|
UniqueViolationError,
|
|
35
36
|
)
|
|
37
|
+
from sqlspec.utils.module_loader import ensure_pyarrow
|
|
36
38
|
from sqlspec.utils.serializers import to_json
|
|
37
39
|
|
|
38
40
|
if TYPE_CHECKING:
|
|
39
41
|
from contextlib import AbstractAsyncContextManager, AbstractContextManager
|
|
40
42
|
|
|
43
|
+
from sqlspec.builder import QueryBuilder
|
|
44
|
+
from sqlspec.core import StatementFilter
|
|
41
45
|
from sqlspec.core.result import SQLResult
|
|
42
|
-
from sqlspec.core.statement import
|
|
46
|
+
from sqlspec.core.statement import Statement
|
|
43
47
|
from sqlspec.driver._common import ExecutionResult
|
|
48
|
+
from sqlspec.typing import StatementParameters
|
|
44
49
|
|
|
45
50
|
logger = logging.getLogger(__name__)
|
|
46
51
|
|
|
@@ -587,6 +592,94 @@ class OracleSyncDriver(SyncDriverAdapterBase):
|
|
|
587
592
|
msg = f"Failed to commit Oracle transaction: {e}"
|
|
588
593
|
raise SQLSpecError(msg) from e
|
|
589
594
|
|
|
595
|
+
def select_to_arrow(
|
|
596
|
+
self,
|
|
597
|
+
statement: "Statement | QueryBuilder",
|
|
598
|
+
/,
|
|
599
|
+
*parameters: "StatementParameters | StatementFilter",
|
|
600
|
+
statement_config: "StatementConfig | None" = None,
|
|
601
|
+
return_format: str = "table",
|
|
602
|
+
native_only: bool = False,
|
|
603
|
+
batch_size: int | None = None,
|
|
604
|
+
arrow_schema: Any = None,
|
|
605
|
+
**kwargs: Any,
|
|
606
|
+
) -> "Any":
|
|
607
|
+
"""Execute query and return results as Apache Arrow format using Oracle native support.
|
|
608
|
+
|
|
609
|
+
This implementation uses Oracle's native fetch_df_all() method which returns
|
|
610
|
+
an OracleDataFrame with Arrow PyCapsule interface, providing zero-copy data
|
|
611
|
+
transfer and 5-10x performance improvement over dict conversion.
|
|
612
|
+
|
|
613
|
+
Args:
|
|
614
|
+
statement: SQL query string, Statement, or QueryBuilder
|
|
615
|
+
*parameters: Query parameters (same format as execute()/select())
|
|
616
|
+
statement_config: Optional statement configuration override
|
|
617
|
+
return_format: "table" for pyarrow.Table (default), "batches" for RecordBatch
|
|
618
|
+
native_only: If False, use base conversion path instead of native (default: False uses native)
|
|
619
|
+
batch_size: Rows per batch when using "batches" format
|
|
620
|
+
arrow_schema: Optional pyarrow.Schema for type casting
|
|
621
|
+
**kwargs: Additional keyword arguments
|
|
622
|
+
|
|
623
|
+
Returns:
|
|
624
|
+
ArrowResult containing pyarrow.Table or RecordBatch
|
|
625
|
+
|
|
626
|
+
Examples:
|
|
627
|
+
>>> result = driver.select_to_arrow(
|
|
628
|
+
... "SELECT * FROM users WHERE age > :1", (18,)
|
|
629
|
+
... )
|
|
630
|
+
>>> df = result.to_pandas()
|
|
631
|
+
>>> print(df.head())
|
|
632
|
+
"""
|
|
633
|
+
# Check pyarrow is available
|
|
634
|
+
ensure_pyarrow()
|
|
635
|
+
|
|
636
|
+
# If native_only=False explicitly passed, use base conversion path
|
|
637
|
+
if native_only is False:
|
|
638
|
+
return super().select_to_arrow(
|
|
639
|
+
statement,
|
|
640
|
+
*parameters,
|
|
641
|
+
statement_config=statement_config,
|
|
642
|
+
return_format=return_format,
|
|
643
|
+
native_only=native_only,
|
|
644
|
+
batch_size=batch_size,
|
|
645
|
+
arrow_schema=arrow_schema,
|
|
646
|
+
**kwargs,
|
|
647
|
+
)
|
|
648
|
+
|
|
649
|
+
import pyarrow as pa
|
|
650
|
+
|
|
651
|
+
# Prepare statement with parameters
|
|
652
|
+
config = statement_config or self.statement_config
|
|
653
|
+
prepared_statement = self.prepare_statement(statement, parameters, statement_config=config, kwargs=kwargs)
|
|
654
|
+
sql, prepared_parameters = self._get_compiled_sql(prepared_statement, config)
|
|
655
|
+
|
|
656
|
+
# Use Oracle's native fetch_df_all() for zero-copy Arrow transfer
|
|
657
|
+
oracle_df = self.connection.fetch_df_all(
|
|
658
|
+
statement=sql, parameters=prepared_parameters or [], arraysize=batch_size or 1000
|
|
659
|
+
)
|
|
660
|
+
|
|
661
|
+
# Convert OracleDataFrame to PyArrow Table using PyCapsule interface
|
|
662
|
+
arrow_table = pa.table(oracle_df)
|
|
663
|
+
|
|
664
|
+
# Apply schema casting if provided
|
|
665
|
+
if arrow_schema is not None:
|
|
666
|
+
if not isinstance(arrow_schema, pa.Schema):
|
|
667
|
+
msg = f"arrow_schema must be a pyarrow.Schema, got {type(arrow_schema).__name__}"
|
|
668
|
+
raise TypeError(msg)
|
|
669
|
+
arrow_table = arrow_table.cast(arrow_schema)
|
|
670
|
+
|
|
671
|
+
# Convert to batches if requested
|
|
672
|
+
if return_format == "batches":
|
|
673
|
+
batches = arrow_table.to_batches()
|
|
674
|
+
arrow_data: Any = batches[0] if batches else pa.RecordBatch.from_pydict({})
|
|
675
|
+
else:
|
|
676
|
+
arrow_data = arrow_table
|
|
677
|
+
|
|
678
|
+
# Get row count
|
|
679
|
+
rows_affected = len(arrow_table)
|
|
680
|
+
|
|
681
|
+
return create_arrow_result(statement=prepared_statement, data=arrow_data, rows_affected=rows_affected)
|
|
682
|
+
|
|
590
683
|
@property
|
|
591
684
|
def data_dictionary(self) -> "SyncDataDictionaryBase":
|
|
592
685
|
"""Get the data dictionary for this driver.
|
|
@@ -783,6 +876,94 @@ class OracleAsyncDriver(AsyncDriverAdapterBase):
|
|
|
783
876
|
msg = f"Failed to commit Oracle transaction: {e}"
|
|
784
877
|
raise SQLSpecError(msg) from e
|
|
785
878
|
|
|
879
|
+
async def select_to_arrow(
|
|
880
|
+
self,
|
|
881
|
+
statement: "Statement | QueryBuilder",
|
|
882
|
+
/,
|
|
883
|
+
*parameters: "StatementParameters | StatementFilter",
|
|
884
|
+
statement_config: "StatementConfig | None" = None,
|
|
885
|
+
return_format: str = "table",
|
|
886
|
+
native_only: bool = False,
|
|
887
|
+
batch_size: int | None = None,
|
|
888
|
+
arrow_schema: Any = None,
|
|
889
|
+
**kwargs: Any,
|
|
890
|
+
) -> "Any":
|
|
891
|
+
"""Execute query and return results as Apache Arrow format using Oracle native support.
|
|
892
|
+
|
|
893
|
+
This implementation uses Oracle's native fetch_df_all() method which returns
|
|
894
|
+
an OracleDataFrame with Arrow PyCapsule interface, providing zero-copy data
|
|
895
|
+
transfer and 5-10x performance improvement over dict conversion.
|
|
896
|
+
|
|
897
|
+
Args:
|
|
898
|
+
statement: SQL query string, Statement, or QueryBuilder
|
|
899
|
+
*parameters: Query parameters (same format as execute()/select())
|
|
900
|
+
statement_config: Optional statement configuration override
|
|
901
|
+
return_format: "table" for pyarrow.Table (default), "batches" for RecordBatch
|
|
902
|
+
native_only: If False, use base conversion path instead of native (default: False uses native)
|
|
903
|
+
batch_size: Rows per batch when using "batches" format
|
|
904
|
+
arrow_schema: Optional pyarrow.Schema for type casting
|
|
905
|
+
**kwargs: Additional keyword arguments
|
|
906
|
+
|
|
907
|
+
Returns:
|
|
908
|
+
ArrowResult containing pyarrow.Table or RecordBatch
|
|
909
|
+
|
|
910
|
+
Examples:
|
|
911
|
+
>>> result = await driver.select_to_arrow(
|
|
912
|
+
... "SELECT * FROM users WHERE age > :1", (18,)
|
|
913
|
+
... )
|
|
914
|
+
>>> df = result.to_pandas()
|
|
915
|
+
>>> print(df.head())
|
|
916
|
+
"""
|
|
917
|
+
# Check pyarrow is available
|
|
918
|
+
ensure_pyarrow()
|
|
919
|
+
|
|
920
|
+
# If native_only=False explicitly passed, use base conversion path
|
|
921
|
+
if native_only is False:
|
|
922
|
+
return await super().select_to_arrow(
|
|
923
|
+
statement,
|
|
924
|
+
*parameters,
|
|
925
|
+
statement_config=statement_config,
|
|
926
|
+
return_format=return_format,
|
|
927
|
+
native_only=native_only,
|
|
928
|
+
batch_size=batch_size,
|
|
929
|
+
arrow_schema=arrow_schema,
|
|
930
|
+
**kwargs,
|
|
931
|
+
)
|
|
932
|
+
|
|
933
|
+
import pyarrow as pa
|
|
934
|
+
|
|
935
|
+
# Prepare statement with parameters
|
|
936
|
+
config = statement_config or self.statement_config
|
|
937
|
+
prepared_statement = self.prepare_statement(statement, parameters, statement_config=config, kwargs=kwargs)
|
|
938
|
+
sql, prepared_parameters = self._get_compiled_sql(prepared_statement, config)
|
|
939
|
+
|
|
940
|
+
# Use Oracle's native fetch_df_all() for zero-copy Arrow transfer
|
|
941
|
+
oracle_df = await self.connection.fetch_df_all(
|
|
942
|
+
statement=sql, parameters=prepared_parameters or [], arraysize=batch_size or 1000
|
|
943
|
+
)
|
|
944
|
+
|
|
945
|
+
# Convert OracleDataFrame to PyArrow Table using PyCapsule interface
|
|
946
|
+
arrow_table = pa.table(oracle_df)
|
|
947
|
+
|
|
948
|
+
# Apply schema casting if provided
|
|
949
|
+
if arrow_schema is not None:
|
|
950
|
+
if not isinstance(arrow_schema, pa.Schema):
|
|
951
|
+
msg = f"arrow_schema must be a pyarrow.Schema, got {type(arrow_schema).__name__}"
|
|
952
|
+
raise TypeError(msg)
|
|
953
|
+
arrow_table = arrow_table.cast(arrow_schema)
|
|
954
|
+
|
|
955
|
+
# Convert to batches if requested
|
|
956
|
+
if return_format == "batches":
|
|
957
|
+
batches = arrow_table.to_batches()
|
|
958
|
+
arrow_data: Any = batches[0] if batches else pa.RecordBatch.from_pydict({})
|
|
959
|
+
else:
|
|
960
|
+
arrow_data = arrow_table
|
|
961
|
+
|
|
962
|
+
# Get row count
|
|
963
|
+
rows_affected = len(arrow_table)
|
|
964
|
+
|
|
965
|
+
return create_arrow_result(statement=prepared_statement, data=arrow_data, rows_affected=rows_affected)
|
|
966
|
+
|
|
786
967
|
@property
|
|
787
968
|
def data_dictionary(self) -> "AsyncDataDictionaryBase":
|
|
788
969
|
"""Get the data dictionary for this driver.
|