sqlspec 0.36.0__cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.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.
- ac8f31065839703b4e70__mypyc.cpython-310-aarch64-linux-gnu.so +0 -0
- sqlspec/__init__.py +140 -0
- sqlspec/__main__.py +12 -0
- sqlspec/__metadata__.py +14 -0
- sqlspec/_serialization.py +315 -0
- sqlspec/_typing.py +700 -0
- sqlspec/adapters/__init__.py +0 -0
- sqlspec/adapters/adbc/__init__.py +5 -0
- sqlspec/adapters/adbc/_typing.py +82 -0
- sqlspec/adapters/adbc/adk/__init__.py +5 -0
- sqlspec/adapters/adbc/adk/store.py +1273 -0
- sqlspec/adapters/adbc/config.py +295 -0
- sqlspec/adapters/adbc/core.cpython-310-aarch64-linux-gnu.so +0 -0
- sqlspec/adapters/adbc/core.py +735 -0
- sqlspec/adapters/adbc/data_dictionary.py +334 -0
- sqlspec/adapters/adbc/driver.py +529 -0
- sqlspec/adapters/adbc/events/__init__.py +5 -0
- sqlspec/adapters/adbc/events/store.py +285 -0
- sqlspec/adapters/adbc/litestar/__init__.py +5 -0
- sqlspec/adapters/adbc/litestar/store.py +502 -0
- sqlspec/adapters/adbc/type_converter.cpython-310-aarch64-linux-gnu.so +0 -0
- sqlspec/adapters/adbc/type_converter.py +140 -0
- sqlspec/adapters/aiosqlite/__init__.py +25 -0
- sqlspec/adapters/aiosqlite/_typing.py +82 -0
- sqlspec/adapters/aiosqlite/adk/__init__.py +5 -0
- sqlspec/adapters/aiosqlite/adk/store.py +818 -0
- sqlspec/adapters/aiosqlite/config.py +334 -0
- sqlspec/adapters/aiosqlite/core.cpython-310-aarch64-linux-gnu.so +0 -0
- sqlspec/adapters/aiosqlite/core.py +315 -0
- sqlspec/adapters/aiosqlite/data_dictionary.py +208 -0
- sqlspec/adapters/aiosqlite/driver.py +313 -0
- sqlspec/adapters/aiosqlite/events/__init__.py +5 -0
- sqlspec/adapters/aiosqlite/events/store.py +20 -0
- sqlspec/adapters/aiosqlite/litestar/__init__.py +5 -0
- sqlspec/adapters/aiosqlite/litestar/store.py +279 -0
- sqlspec/adapters/aiosqlite/pool.py +533 -0
- sqlspec/adapters/asyncmy/__init__.py +21 -0
- sqlspec/adapters/asyncmy/_typing.py +87 -0
- sqlspec/adapters/asyncmy/adk/__init__.py +5 -0
- sqlspec/adapters/asyncmy/adk/store.py +703 -0
- sqlspec/adapters/asyncmy/config.py +302 -0
- sqlspec/adapters/asyncmy/core.cpython-310-aarch64-linux-gnu.so +0 -0
- sqlspec/adapters/asyncmy/core.py +360 -0
- sqlspec/adapters/asyncmy/data_dictionary.py +124 -0
- sqlspec/adapters/asyncmy/driver.py +383 -0
- sqlspec/adapters/asyncmy/events/__init__.py +5 -0
- sqlspec/adapters/asyncmy/events/store.py +104 -0
- sqlspec/adapters/asyncmy/litestar/__init__.py +5 -0
- sqlspec/adapters/asyncmy/litestar/store.py +296 -0
- sqlspec/adapters/asyncpg/__init__.py +19 -0
- sqlspec/adapters/asyncpg/_typing.py +88 -0
- sqlspec/adapters/asyncpg/adk/__init__.py +5 -0
- sqlspec/adapters/asyncpg/adk/store.py +748 -0
- sqlspec/adapters/asyncpg/config.py +569 -0
- sqlspec/adapters/asyncpg/core.cpython-310-aarch64-linux-gnu.so +0 -0
- sqlspec/adapters/asyncpg/core.py +367 -0
- sqlspec/adapters/asyncpg/data_dictionary.py +162 -0
- sqlspec/adapters/asyncpg/driver.py +487 -0
- sqlspec/adapters/asyncpg/events/__init__.py +6 -0
- sqlspec/adapters/asyncpg/events/backend.py +286 -0
- sqlspec/adapters/asyncpg/events/store.py +40 -0
- sqlspec/adapters/asyncpg/litestar/__init__.py +5 -0
- sqlspec/adapters/asyncpg/litestar/store.py +251 -0
- sqlspec/adapters/bigquery/__init__.py +14 -0
- sqlspec/adapters/bigquery/_typing.py +86 -0
- sqlspec/adapters/bigquery/adk/__init__.py +5 -0
- sqlspec/adapters/bigquery/adk/store.py +827 -0
- sqlspec/adapters/bigquery/config.py +353 -0
- sqlspec/adapters/bigquery/core.cpython-310-aarch64-linux-gnu.so +0 -0
- sqlspec/adapters/bigquery/core.py +715 -0
- sqlspec/adapters/bigquery/data_dictionary.py +128 -0
- sqlspec/adapters/bigquery/driver.py +548 -0
- sqlspec/adapters/bigquery/events/__init__.py +5 -0
- sqlspec/adapters/bigquery/events/store.py +139 -0
- sqlspec/adapters/bigquery/litestar/__init__.py +5 -0
- sqlspec/adapters/bigquery/litestar/store.py +325 -0
- sqlspec/adapters/bigquery/type_converter.cpython-310-aarch64-linux-gnu.so +0 -0
- sqlspec/adapters/bigquery/type_converter.py +107 -0
- sqlspec/adapters/cockroach_asyncpg/__init__.py +24 -0
- sqlspec/adapters/cockroach_asyncpg/_typing.py +72 -0
- sqlspec/adapters/cockroach_asyncpg/adk/__init__.py +3 -0
- sqlspec/adapters/cockroach_asyncpg/adk/store.py +410 -0
- sqlspec/adapters/cockroach_asyncpg/config.py +238 -0
- sqlspec/adapters/cockroach_asyncpg/core.cpython-310-aarch64-linux-gnu.so +0 -0
- sqlspec/adapters/cockroach_asyncpg/core.py +55 -0
- sqlspec/adapters/cockroach_asyncpg/data_dictionary.py +107 -0
- sqlspec/adapters/cockroach_asyncpg/driver.py +144 -0
- sqlspec/adapters/cockroach_asyncpg/events/__init__.py +3 -0
- sqlspec/adapters/cockroach_asyncpg/events/store.py +20 -0
- sqlspec/adapters/cockroach_asyncpg/litestar/__init__.py +3 -0
- sqlspec/adapters/cockroach_asyncpg/litestar/store.py +142 -0
- sqlspec/adapters/cockroach_psycopg/__init__.py +38 -0
- sqlspec/adapters/cockroach_psycopg/_typing.py +129 -0
- sqlspec/adapters/cockroach_psycopg/adk/__init__.py +13 -0
- sqlspec/adapters/cockroach_psycopg/adk/store.py +868 -0
- sqlspec/adapters/cockroach_psycopg/config.py +484 -0
- sqlspec/adapters/cockroach_psycopg/core.cpython-310-aarch64-linux-gnu.so +0 -0
- sqlspec/adapters/cockroach_psycopg/core.py +63 -0
- sqlspec/adapters/cockroach_psycopg/data_dictionary.py +215 -0
- sqlspec/adapters/cockroach_psycopg/driver.py +284 -0
- sqlspec/adapters/cockroach_psycopg/events/__init__.py +6 -0
- sqlspec/adapters/cockroach_psycopg/events/store.py +34 -0
- sqlspec/adapters/cockroach_psycopg/litestar/__init__.py +3 -0
- sqlspec/adapters/cockroach_psycopg/litestar/store.py +325 -0
- sqlspec/adapters/duckdb/__init__.py +25 -0
- sqlspec/adapters/duckdb/_typing.py +81 -0
- sqlspec/adapters/duckdb/adk/__init__.py +14 -0
- sqlspec/adapters/duckdb/adk/store.py +850 -0
- sqlspec/adapters/duckdb/config.py +463 -0
- sqlspec/adapters/duckdb/core.cpython-310-aarch64-linux-gnu.so +0 -0
- sqlspec/adapters/duckdb/core.py +257 -0
- sqlspec/adapters/duckdb/data_dictionary.py +140 -0
- sqlspec/adapters/duckdb/driver.py +430 -0
- sqlspec/adapters/duckdb/events/__init__.py +5 -0
- sqlspec/adapters/duckdb/events/store.py +57 -0
- sqlspec/adapters/duckdb/litestar/__init__.py +5 -0
- sqlspec/adapters/duckdb/litestar/store.py +330 -0
- sqlspec/adapters/duckdb/pool.py +293 -0
- sqlspec/adapters/duckdb/type_converter.cpython-310-aarch64-linux-gnu.so +0 -0
- sqlspec/adapters/duckdb/type_converter.py +118 -0
- sqlspec/adapters/mock/__init__.py +72 -0
- sqlspec/adapters/mock/_typing.py +147 -0
- sqlspec/adapters/mock/config.py +483 -0
- sqlspec/adapters/mock/core.py +319 -0
- sqlspec/adapters/mock/data_dictionary.py +366 -0
- sqlspec/adapters/mock/driver.py +721 -0
- sqlspec/adapters/mysqlconnector/__init__.py +36 -0
- sqlspec/adapters/mysqlconnector/_typing.py +141 -0
- sqlspec/adapters/mysqlconnector/adk/__init__.py +15 -0
- sqlspec/adapters/mysqlconnector/adk/store.py +1060 -0
- sqlspec/adapters/mysqlconnector/config.py +394 -0
- sqlspec/adapters/mysqlconnector/core.cpython-310-aarch64-linux-gnu.so +0 -0
- sqlspec/adapters/mysqlconnector/core.py +303 -0
- sqlspec/adapters/mysqlconnector/data_dictionary.py +235 -0
- sqlspec/adapters/mysqlconnector/driver.py +483 -0
- sqlspec/adapters/mysqlconnector/events/__init__.py +8 -0
- sqlspec/adapters/mysqlconnector/events/store.py +98 -0
- sqlspec/adapters/mysqlconnector/litestar/__init__.py +5 -0
- sqlspec/adapters/mysqlconnector/litestar/store.py +426 -0
- sqlspec/adapters/oracledb/__init__.py +60 -0
- sqlspec/adapters/oracledb/_numpy_handlers.py +141 -0
- sqlspec/adapters/oracledb/_typing.py +182 -0
- sqlspec/adapters/oracledb/_uuid_handlers.py +166 -0
- sqlspec/adapters/oracledb/adk/__init__.py +10 -0
- sqlspec/adapters/oracledb/adk/store.py +2369 -0
- sqlspec/adapters/oracledb/config.py +550 -0
- sqlspec/adapters/oracledb/core.cpython-310-aarch64-linux-gnu.so +0 -0
- sqlspec/adapters/oracledb/core.py +543 -0
- sqlspec/adapters/oracledb/data_dictionary.py +536 -0
- sqlspec/adapters/oracledb/driver.py +1229 -0
- sqlspec/adapters/oracledb/events/__init__.py +16 -0
- sqlspec/adapters/oracledb/events/backend.py +347 -0
- sqlspec/adapters/oracledb/events/store.py +420 -0
- sqlspec/adapters/oracledb/litestar/__init__.py +5 -0
- sqlspec/adapters/oracledb/litestar/store.py +781 -0
- sqlspec/adapters/oracledb/migrations.py +535 -0
- sqlspec/adapters/oracledb/type_converter.cpython-310-aarch64-linux-gnu.so +0 -0
- sqlspec/adapters/oracledb/type_converter.py +211 -0
- sqlspec/adapters/psqlpy/__init__.py +17 -0
- sqlspec/adapters/psqlpy/_typing.py +79 -0
- sqlspec/adapters/psqlpy/adk/__init__.py +5 -0
- sqlspec/adapters/psqlpy/adk/store.py +766 -0
- sqlspec/adapters/psqlpy/config.py +304 -0
- sqlspec/adapters/psqlpy/core.cpython-310-aarch64-linux-gnu.so +0 -0
- sqlspec/adapters/psqlpy/core.py +480 -0
- sqlspec/adapters/psqlpy/data_dictionary.py +126 -0
- sqlspec/adapters/psqlpy/driver.py +438 -0
- sqlspec/adapters/psqlpy/events/__init__.py +6 -0
- sqlspec/adapters/psqlpy/events/backend.py +310 -0
- sqlspec/adapters/psqlpy/events/store.py +20 -0
- sqlspec/adapters/psqlpy/litestar/__init__.py +5 -0
- sqlspec/adapters/psqlpy/litestar/store.py +270 -0
- sqlspec/adapters/psqlpy/type_converter.cpython-310-aarch64-linux-gnu.so +0 -0
- sqlspec/adapters/psqlpy/type_converter.py +113 -0
- sqlspec/adapters/psycopg/__init__.py +32 -0
- sqlspec/adapters/psycopg/_typing.py +164 -0
- sqlspec/adapters/psycopg/adk/__init__.py +10 -0
- sqlspec/adapters/psycopg/adk/store.py +1387 -0
- sqlspec/adapters/psycopg/config.py +576 -0
- sqlspec/adapters/psycopg/core.cpython-310-aarch64-linux-gnu.so +0 -0
- sqlspec/adapters/psycopg/core.py +450 -0
- sqlspec/adapters/psycopg/data_dictionary.py +289 -0
- sqlspec/adapters/psycopg/driver.py +975 -0
- sqlspec/adapters/psycopg/events/__init__.py +20 -0
- sqlspec/adapters/psycopg/events/backend.py +458 -0
- sqlspec/adapters/psycopg/events/store.py +42 -0
- sqlspec/adapters/psycopg/litestar/__init__.py +5 -0
- sqlspec/adapters/psycopg/litestar/store.py +552 -0
- sqlspec/adapters/psycopg/type_converter.cpython-310-aarch64-linux-gnu.so +0 -0
- sqlspec/adapters/psycopg/type_converter.py +93 -0
- sqlspec/adapters/pymysql/__init__.py +21 -0
- sqlspec/adapters/pymysql/_typing.py +71 -0
- sqlspec/adapters/pymysql/adk/__init__.py +5 -0
- sqlspec/adapters/pymysql/adk/store.py +540 -0
- sqlspec/adapters/pymysql/config.py +195 -0
- sqlspec/adapters/pymysql/core.cpython-310-aarch64-linux-gnu.so +0 -0
- sqlspec/adapters/pymysql/core.py +299 -0
- sqlspec/adapters/pymysql/data_dictionary.py +122 -0
- sqlspec/adapters/pymysql/driver.py +259 -0
- sqlspec/adapters/pymysql/events/__init__.py +5 -0
- sqlspec/adapters/pymysql/events/store.py +50 -0
- sqlspec/adapters/pymysql/litestar/__init__.py +5 -0
- sqlspec/adapters/pymysql/litestar/store.py +232 -0
- sqlspec/adapters/pymysql/pool.py +137 -0
- sqlspec/adapters/spanner/__init__.py +40 -0
- sqlspec/adapters/spanner/_typing.py +86 -0
- sqlspec/adapters/spanner/adk/__init__.py +5 -0
- sqlspec/adapters/spanner/adk/store.py +732 -0
- sqlspec/adapters/spanner/config.py +352 -0
- sqlspec/adapters/spanner/core.cpython-310-aarch64-linux-gnu.so +0 -0
- sqlspec/adapters/spanner/core.py +188 -0
- sqlspec/adapters/spanner/data_dictionary.py +120 -0
- sqlspec/adapters/spanner/dialect/__init__.py +6 -0
- sqlspec/adapters/spanner/dialect/_spangres.py +57 -0
- sqlspec/adapters/spanner/dialect/_spanner.py +130 -0
- sqlspec/adapters/spanner/driver.py +373 -0
- sqlspec/adapters/spanner/events/__init__.py +5 -0
- sqlspec/adapters/spanner/events/store.py +187 -0
- sqlspec/adapters/spanner/litestar/__init__.py +5 -0
- sqlspec/adapters/spanner/litestar/store.py +291 -0
- sqlspec/adapters/spanner/type_converter.cpython-310-aarch64-linux-gnu.so +0 -0
- sqlspec/adapters/spanner/type_converter.py +331 -0
- sqlspec/adapters/sqlite/__init__.py +19 -0
- sqlspec/adapters/sqlite/_typing.py +80 -0
- sqlspec/adapters/sqlite/adk/__init__.py +5 -0
- sqlspec/adapters/sqlite/adk/store.py +958 -0
- sqlspec/adapters/sqlite/config.py +280 -0
- sqlspec/adapters/sqlite/core.cpython-310-aarch64-linux-gnu.so +0 -0
- sqlspec/adapters/sqlite/core.py +312 -0
- sqlspec/adapters/sqlite/data_dictionary.py +202 -0
- sqlspec/adapters/sqlite/driver.py +359 -0
- sqlspec/adapters/sqlite/events/__init__.py +5 -0
- sqlspec/adapters/sqlite/events/store.py +20 -0
- sqlspec/adapters/sqlite/litestar/__init__.py +5 -0
- sqlspec/adapters/sqlite/litestar/store.py +316 -0
- sqlspec/adapters/sqlite/pool.py +198 -0
- sqlspec/adapters/sqlite/type_converter.cpython-310-aarch64-linux-gnu.so +0 -0
- sqlspec/adapters/sqlite/type_converter.py +114 -0
- sqlspec/base.py +747 -0
- sqlspec/builder/__init__.py +179 -0
- sqlspec/builder/_base.cpython-310-aarch64-linux-gnu.so +0 -0
- sqlspec/builder/_base.py +1022 -0
- sqlspec/builder/_column.cpython-310-aarch64-linux-gnu.so +0 -0
- sqlspec/builder/_column.py +521 -0
- sqlspec/builder/_ddl.cpython-310-aarch64-linux-gnu.so +0 -0
- sqlspec/builder/_ddl.py +1642 -0
- sqlspec/builder/_delete.cpython-310-aarch64-linux-gnu.so +0 -0
- sqlspec/builder/_delete.py +95 -0
- sqlspec/builder/_dml.cpython-310-aarch64-linux-gnu.so +0 -0
- sqlspec/builder/_dml.py +365 -0
- sqlspec/builder/_explain.cpython-310-aarch64-linux-gnu.so +0 -0
- sqlspec/builder/_explain.py +579 -0
- sqlspec/builder/_expression_wrappers.cpython-310-aarch64-linux-gnu.so +0 -0
- sqlspec/builder/_expression_wrappers.py +46 -0
- sqlspec/builder/_factory.cpython-310-aarch64-linux-gnu.so +0 -0
- sqlspec/builder/_factory.py +1697 -0
- sqlspec/builder/_insert.cpython-310-aarch64-linux-gnu.so +0 -0
- sqlspec/builder/_insert.py +328 -0
- sqlspec/builder/_join.cpython-310-aarch64-linux-gnu.so +0 -0
- sqlspec/builder/_join.py +499 -0
- sqlspec/builder/_merge.cpython-310-aarch64-linux-gnu.so +0 -0
- sqlspec/builder/_merge.py +821 -0
- sqlspec/builder/_parsing_utils.cpython-310-aarch64-linux-gnu.so +0 -0
- sqlspec/builder/_parsing_utils.py +297 -0
- sqlspec/builder/_select.cpython-310-aarch64-linux-gnu.so +0 -0
- sqlspec/builder/_select.py +1660 -0
- sqlspec/builder/_temporal.cpython-310-aarch64-linux-gnu.so +0 -0
- sqlspec/builder/_temporal.py +139 -0
- sqlspec/builder/_update.cpython-310-aarch64-linux-gnu.so +0 -0
- sqlspec/builder/_update.py +173 -0
- sqlspec/builder/_vector_expressions.py +267 -0
- sqlspec/cli.py +911 -0
- sqlspec/config.py +1755 -0
- sqlspec/core/__init__.py +374 -0
- sqlspec/core/_correlation.cpython-310-aarch64-linux-gnu.so +0 -0
- sqlspec/core/_correlation.py +176 -0
- sqlspec/core/cache.cpython-310-aarch64-linux-gnu.so +0 -0
- sqlspec/core/cache.py +1069 -0
- sqlspec/core/compiler.cpython-310-aarch64-linux-gnu.so +0 -0
- sqlspec/core/compiler.py +954 -0
- sqlspec/core/explain.cpython-310-aarch64-linux-gnu.so +0 -0
- sqlspec/core/explain.py +275 -0
- sqlspec/core/filters.cpython-310-aarch64-linux-gnu.so +0 -0
- sqlspec/core/filters.py +952 -0
- sqlspec/core/hashing.cpython-310-aarch64-linux-gnu.so +0 -0
- sqlspec/core/hashing.py +262 -0
- sqlspec/core/metrics.cpython-310-aarch64-linux-gnu.so +0 -0
- sqlspec/core/metrics.py +83 -0
- sqlspec/core/parameters/__init__.py +71 -0
- sqlspec/core/parameters/_alignment.cpython-310-aarch64-linux-gnu.so +0 -0
- sqlspec/core/parameters/_alignment.py +270 -0
- sqlspec/core/parameters/_converter.cpython-310-aarch64-linux-gnu.so +0 -0
- sqlspec/core/parameters/_converter.py +543 -0
- sqlspec/core/parameters/_processor.cpython-310-aarch64-linux-gnu.so +0 -0
- sqlspec/core/parameters/_processor.py +505 -0
- sqlspec/core/parameters/_registry.cpython-310-aarch64-linux-gnu.so +0 -0
- sqlspec/core/parameters/_registry.py +206 -0
- sqlspec/core/parameters/_transformers.cpython-310-aarch64-linux-gnu.so +0 -0
- sqlspec/core/parameters/_transformers.py +292 -0
- sqlspec/core/parameters/_types.cpython-310-aarch64-linux-gnu.so +0 -0
- sqlspec/core/parameters/_types.py +499 -0
- sqlspec/core/parameters/_validator.cpython-310-aarch64-linux-gnu.so +0 -0
- sqlspec/core/parameters/_validator.py +180 -0
- sqlspec/core/pipeline.cpython-310-aarch64-linux-gnu.so +0 -0
- sqlspec/core/pipeline.py +319 -0
- sqlspec/core/query_modifiers.cpython-310-aarch64-linux-gnu.so +0 -0
- sqlspec/core/query_modifiers.py +437 -0
- sqlspec/core/result/__init__.py +23 -0
- sqlspec/core/result/_base.cpython-310-aarch64-linux-gnu.so +0 -0
- sqlspec/core/result/_base.py +1121 -0
- sqlspec/core/result/_io.cpython-310-aarch64-linux-gnu.so +0 -0
- sqlspec/core/result/_io.py +28 -0
- sqlspec/core/splitter.cpython-310-aarch64-linux-gnu.so +0 -0
- sqlspec/core/splitter.py +966 -0
- sqlspec/core/stack.cpython-310-aarch64-linux-gnu.so +0 -0
- sqlspec/core/stack.py +163 -0
- sqlspec/core/statement.cpython-310-aarch64-linux-gnu.so +0 -0
- sqlspec/core/statement.py +1503 -0
- sqlspec/core/type_converter.cpython-310-aarch64-linux-gnu.so +0 -0
- sqlspec/core/type_converter.py +339 -0
- sqlspec/data_dictionary/__init__.py +22 -0
- sqlspec/data_dictionary/_loader.py +123 -0
- sqlspec/data_dictionary/_registry.cpython-310-aarch64-linux-gnu.so +0 -0
- sqlspec/data_dictionary/_registry.py +74 -0
- sqlspec/data_dictionary/_types.cpython-310-aarch64-linux-gnu.so +0 -0
- sqlspec/data_dictionary/_types.py +121 -0
- sqlspec/data_dictionary/dialects/__init__.py +21 -0
- sqlspec/data_dictionary/dialects/bigquery.cpython-310-aarch64-linux-gnu.so +0 -0
- sqlspec/data_dictionary/dialects/bigquery.py +49 -0
- sqlspec/data_dictionary/dialects/cockroachdb.cpython-310-aarch64-linux-gnu.so +0 -0
- sqlspec/data_dictionary/dialects/cockroachdb.py +43 -0
- sqlspec/data_dictionary/dialects/duckdb.cpython-310-aarch64-linux-gnu.so +0 -0
- sqlspec/data_dictionary/dialects/duckdb.py +47 -0
- sqlspec/data_dictionary/dialects/mysql.cpython-310-aarch64-linux-gnu.so +0 -0
- sqlspec/data_dictionary/dialects/mysql.py +42 -0
- sqlspec/data_dictionary/dialects/oracle.cpython-310-aarch64-linux-gnu.so +0 -0
- sqlspec/data_dictionary/dialects/oracle.py +34 -0
- sqlspec/data_dictionary/dialects/postgres.cpython-310-aarch64-linux-gnu.so +0 -0
- sqlspec/data_dictionary/dialects/postgres.py +46 -0
- sqlspec/data_dictionary/dialects/spanner.cpython-310-aarch64-linux-gnu.so +0 -0
- sqlspec/data_dictionary/dialects/spanner.py +37 -0
- sqlspec/data_dictionary/dialects/sqlite.cpython-310-aarch64-linux-gnu.so +0 -0
- sqlspec/data_dictionary/dialects/sqlite.py +42 -0
- sqlspec/data_dictionary/sql/.gitkeep +0 -0
- sqlspec/data_dictionary/sql/bigquery/columns.sql +23 -0
- sqlspec/data_dictionary/sql/bigquery/foreign_keys.sql +34 -0
- sqlspec/data_dictionary/sql/bigquery/indexes.sql +19 -0
- sqlspec/data_dictionary/sql/bigquery/tables.sql +33 -0
- sqlspec/data_dictionary/sql/bigquery/version.sql +3 -0
- sqlspec/data_dictionary/sql/cockroachdb/columns.sql +34 -0
- sqlspec/data_dictionary/sql/cockroachdb/foreign_keys.sql +40 -0
- sqlspec/data_dictionary/sql/cockroachdb/indexes.sql +32 -0
- sqlspec/data_dictionary/sql/cockroachdb/tables.sql +44 -0
- sqlspec/data_dictionary/sql/cockroachdb/version.sql +3 -0
- sqlspec/data_dictionary/sql/duckdb/columns.sql +23 -0
- sqlspec/data_dictionary/sql/duckdb/foreign_keys.sql +36 -0
- sqlspec/data_dictionary/sql/duckdb/indexes.sql +19 -0
- sqlspec/data_dictionary/sql/duckdb/tables.sql +38 -0
- sqlspec/data_dictionary/sql/duckdb/version.sql +3 -0
- sqlspec/data_dictionary/sql/mysql/columns.sql +23 -0
- sqlspec/data_dictionary/sql/mysql/foreign_keys.sql +28 -0
- sqlspec/data_dictionary/sql/mysql/indexes.sql +26 -0
- sqlspec/data_dictionary/sql/mysql/tables.sql +33 -0
- sqlspec/data_dictionary/sql/mysql/version.sql +3 -0
- sqlspec/data_dictionary/sql/oracle/columns.sql +23 -0
- sqlspec/data_dictionary/sql/oracle/foreign_keys.sql +48 -0
- sqlspec/data_dictionary/sql/oracle/indexes.sql +44 -0
- sqlspec/data_dictionary/sql/oracle/tables.sql +25 -0
- sqlspec/data_dictionary/sql/oracle/version.sql +20 -0
- sqlspec/data_dictionary/sql/postgres/columns.sql +34 -0
- sqlspec/data_dictionary/sql/postgres/foreign_keys.sql +40 -0
- sqlspec/data_dictionary/sql/postgres/indexes.sql +56 -0
- sqlspec/data_dictionary/sql/postgres/tables.sql +44 -0
- sqlspec/data_dictionary/sql/postgres/version.sql +3 -0
- sqlspec/data_dictionary/sql/spanner/columns.sql +23 -0
- sqlspec/data_dictionary/sql/spanner/foreign_keys.sql +70 -0
- sqlspec/data_dictionary/sql/spanner/indexes.sql +30 -0
- sqlspec/data_dictionary/sql/spanner/tables.sql +9 -0
- sqlspec/data_dictionary/sql/spanner/version.sql +3 -0
- sqlspec/data_dictionary/sql/sqlite/columns.sql +23 -0
- sqlspec/data_dictionary/sql/sqlite/foreign_keys.sql +22 -0
- sqlspec/data_dictionary/sql/sqlite/indexes.sql +7 -0
- sqlspec/data_dictionary/sql/sqlite/tables.sql +28 -0
- sqlspec/data_dictionary/sql/sqlite/version.sql +3 -0
- sqlspec/driver/__init__.py +32 -0
- sqlspec/driver/_async.cpython-310-aarch64-linux-gnu.so +0 -0
- sqlspec/driver/_async.py +1737 -0
- sqlspec/driver/_common.cpython-310-aarch64-linux-gnu.so +0 -0
- sqlspec/driver/_common.py +1478 -0
- sqlspec/driver/_sql_helpers.cpython-310-aarch64-linux-gnu.so +0 -0
- sqlspec/driver/_sql_helpers.py +148 -0
- sqlspec/driver/_storage_helpers.cpython-310-aarch64-linux-gnu.so +0 -0
- sqlspec/driver/_storage_helpers.py +144 -0
- sqlspec/driver/_sync.cpython-310-aarch64-linux-gnu.so +0 -0
- sqlspec/driver/_sync.py +1710 -0
- sqlspec/exceptions.py +338 -0
- sqlspec/extensions/__init__.py +0 -0
- sqlspec/extensions/adk/__init__.py +70 -0
- sqlspec/extensions/adk/_types.py +51 -0
- sqlspec/extensions/adk/converters.py +172 -0
- sqlspec/extensions/adk/memory/__init__.py +69 -0
- sqlspec/extensions/adk/memory/_types.py +30 -0
- sqlspec/extensions/adk/memory/converters.py +149 -0
- sqlspec/extensions/adk/memory/service.py +217 -0
- sqlspec/extensions/adk/memory/store.py +569 -0
- sqlspec/extensions/adk/migrations/0001_create_adk_tables.py +246 -0
- sqlspec/extensions/adk/migrations/__init__.py +0 -0
- sqlspec/extensions/adk/service.py +225 -0
- sqlspec/extensions/adk/store.py +567 -0
- sqlspec/extensions/events/__init__.py +51 -0
- sqlspec/extensions/events/_channel.py +703 -0
- sqlspec/extensions/events/_hints.py +45 -0
- sqlspec/extensions/events/_models.py +23 -0
- sqlspec/extensions/events/_payload.py +69 -0
- sqlspec/extensions/events/_protocols.py +134 -0
- sqlspec/extensions/events/_queue.py +461 -0
- sqlspec/extensions/events/_store.py +209 -0
- sqlspec/extensions/events/migrations/0001_create_event_queue.py +59 -0
- sqlspec/extensions/events/migrations/__init__.py +3 -0
- sqlspec/extensions/fastapi/__init__.py +19 -0
- sqlspec/extensions/fastapi/extension.py +351 -0
- sqlspec/extensions/fastapi/providers.py +607 -0
- sqlspec/extensions/flask/__init__.py +37 -0
- sqlspec/extensions/flask/_state.py +76 -0
- sqlspec/extensions/flask/_utils.py +71 -0
- sqlspec/extensions/flask/extension.py +519 -0
- sqlspec/extensions/litestar/__init__.py +28 -0
- sqlspec/extensions/litestar/_utils.py +52 -0
- sqlspec/extensions/litestar/channels.py +165 -0
- sqlspec/extensions/litestar/cli.py +102 -0
- sqlspec/extensions/litestar/config.py +90 -0
- sqlspec/extensions/litestar/handlers.py +316 -0
- sqlspec/extensions/litestar/migrations/0001_create_session_table.py +137 -0
- sqlspec/extensions/litestar/migrations/__init__.py +3 -0
- sqlspec/extensions/litestar/plugin.py +671 -0
- sqlspec/extensions/litestar/providers.py +526 -0
- sqlspec/extensions/litestar/store.py +296 -0
- sqlspec/extensions/otel/__init__.py +58 -0
- sqlspec/extensions/prometheus/__init__.py +113 -0
- sqlspec/extensions/starlette/__init__.py +19 -0
- sqlspec/extensions/starlette/_state.py +30 -0
- sqlspec/extensions/starlette/_utils.py +96 -0
- sqlspec/extensions/starlette/extension.py +346 -0
- sqlspec/extensions/starlette/middleware.py +235 -0
- sqlspec/loader.cpython-310-aarch64-linux-gnu.so +0 -0
- sqlspec/loader.py +702 -0
- sqlspec/migrations/__init__.py +36 -0
- sqlspec/migrations/base.py +731 -0
- sqlspec/migrations/commands.py +1232 -0
- sqlspec/migrations/context.py +157 -0
- sqlspec/migrations/fix.py +204 -0
- sqlspec/migrations/loaders.py +443 -0
- sqlspec/migrations/runner.py +1172 -0
- sqlspec/migrations/templates.py +234 -0
- sqlspec/migrations/tracker.py +611 -0
- sqlspec/migrations/utils.py +256 -0
- sqlspec/migrations/validation.py +207 -0
- sqlspec/migrations/version.py +446 -0
- sqlspec/observability/__init__.py +55 -0
- sqlspec/observability/_common.cpython-310-aarch64-linux-gnu.so +0 -0
- sqlspec/observability/_common.py +77 -0
- sqlspec/observability/_config.cpython-310-aarch64-linux-gnu.so +0 -0
- sqlspec/observability/_config.py +348 -0
- sqlspec/observability/_diagnostics.cpython-310-aarch64-linux-gnu.so +0 -0
- sqlspec/observability/_diagnostics.py +74 -0
- sqlspec/observability/_dispatcher.cpython-310-aarch64-linux-gnu.so +0 -0
- sqlspec/observability/_dispatcher.py +152 -0
- sqlspec/observability/_formatters/__init__.py +13 -0
- sqlspec/observability/_formatters/_aws.cpython-310-aarch64-linux-gnu.so +0 -0
- sqlspec/observability/_formatters/_aws.py +102 -0
- sqlspec/observability/_formatters/_azure.cpython-310-aarch64-linux-gnu.so +0 -0
- sqlspec/observability/_formatters/_azure.py +96 -0
- sqlspec/observability/_formatters/_base.cpython-310-aarch64-linux-gnu.so +0 -0
- sqlspec/observability/_formatters/_base.py +57 -0
- sqlspec/observability/_formatters/_gcp.cpython-310-aarch64-linux-gnu.so +0 -0
- sqlspec/observability/_formatters/_gcp.py +131 -0
- sqlspec/observability/_formatting.py +58 -0
- sqlspec/observability/_observer.cpython-310-aarch64-linux-gnu.so +0 -0
- sqlspec/observability/_observer.py +357 -0
- sqlspec/observability/_runtime.cpython-310-aarch64-linux-gnu.so +0 -0
- sqlspec/observability/_runtime.py +420 -0
- sqlspec/observability/_sampling.cpython-310-aarch64-linux-gnu.so +0 -0
- sqlspec/observability/_sampling.py +188 -0
- sqlspec/observability/_spans.cpython-310-aarch64-linux-gnu.so +0 -0
- sqlspec/observability/_spans.py +161 -0
- sqlspec/protocols.py +916 -0
- sqlspec/py.typed +0 -0
- sqlspec/storage/__init__.py +48 -0
- sqlspec/storage/_utils.py +104 -0
- sqlspec/storage/backends/__init__.py +1 -0
- sqlspec/storage/backends/base.py +253 -0
- sqlspec/storage/backends/fsspec.py +529 -0
- sqlspec/storage/backends/local.py +441 -0
- sqlspec/storage/backends/obstore.py +916 -0
- sqlspec/storage/errors.py +104 -0
- sqlspec/storage/pipeline.py +582 -0
- sqlspec/storage/registry.py +301 -0
- sqlspec/typing.py +395 -0
- sqlspec/utils/__init__.py +7 -0
- sqlspec/utils/arrow_helpers.py +318 -0
- sqlspec/utils/config_tools.py +332 -0
- sqlspec/utils/correlation.cpython-310-aarch64-linux-gnu.so +0 -0
- sqlspec/utils/correlation.py +134 -0
- sqlspec/utils/deprecation.py +190 -0
- sqlspec/utils/fixtures.cpython-310-aarch64-linux-gnu.so +0 -0
- sqlspec/utils/fixtures.py +258 -0
- sqlspec/utils/logging.py +222 -0
- sqlspec/utils/module_loader.py +306 -0
- sqlspec/utils/portal.cpython-310-aarch64-linux-gnu.so +0 -0
- sqlspec/utils/portal.py +375 -0
- sqlspec/utils/schema.cpython-310-aarch64-linux-gnu.so +0 -0
- sqlspec/utils/schema.py +485 -0
- sqlspec/utils/serializers.cpython-310-aarch64-linux-gnu.so +0 -0
- sqlspec/utils/serializers.py +408 -0
- sqlspec/utils/singleton.cpython-310-aarch64-linux-gnu.so +0 -0
- sqlspec/utils/singleton.py +41 -0
- sqlspec/utils/sync_tools.cpython-310-aarch64-linux-gnu.so +0 -0
- sqlspec/utils/sync_tools.py +311 -0
- sqlspec/utils/text.cpython-310-aarch64-linux-gnu.so +0 -0
- sqlspec/utils/text.py +108 -0
- sqlspec/utils/type_converters.cpython-310-aarch64-linux-gnu.so +0 -0
- sqlspec/utils/type_converters.py +128 -0
- sqlspec/utils/type_guards.cpython-310-aarch64-linux-gnu.so +0 -0
- sqlspec/utils/type_guards.py +1360 -0
- sqlspec/utils/uuids.cpython-310-aarch64-linux-gnu.so +0 -0
- sqlspec/utils/uuids.py +225 -0
- sqlspec-0.36.0.dist-info/METADATA +205 -0
- sqlspec-0.36.0.dist-info/RECORD +531 -0
- sqlspec-0.36.0.dist-info/WHEEL +7 -0
- sqlspec-0.36.0.dist-info/entry_points.txt +2 -0
- sqlspec-0.36.0.dist-info/licenses/LICENSE +21 -0
|
@@ -0,0 +1,1060 @@
|
|
|
1
|
+
"""MysqlConnector ADK store for Google Agent Development Kit session/event storage."""
|
|
2
|
+
|
|
3
|
+
import re
|
|
4
|
+
from typing import TYPE_CHECKING, Any, Final, cast
|
|
5
|
+
|
|
6
|
+
import mysql.connector
|
|
7
|
+
|
|
8
|
+
from sqlspec.extensions.adk import BaseAsyncADKStore, BaseSyncADKStore, EventRecord, SessionRecord
|
|
9
|
+
from sqlspec.extensions.adk.memory.store import BaseAsyncADKMemoryStore, BaseSyncADKMemoryStore
|
|
10
|
+
from sqlspec.utils.serializers import from_json, to_json
|
|
11
|
+
|
|
12
|
+
if TYPE_CHECKING:
|
|
13
|
+
from datetime import datetime
|
|
14
|
+
|
|
15
|
+
from sqlspec.adapters.mysqlconnector.config import MysqlConnectorAsyncConfig, MysqlConnectorSyncConfig
|
|
16
|
+
from sqlspec.extensions.adk import MemoryRecord
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
__all__ = (
|
|
20
|
+
"MysqlConnectorAsyncADKMemoryStore",
|
|
21
|
+
"MysqlConnectorAsyncADKStore",
|
|
22
|
+
"MysqlConnectorSyncADKMemoryStore",
|
|
23
|
+
"MysqlConnectorSyncADKStore",
|
|
24
|
+
)
|
|
25
|
+
|
|
26
|
+
MYSQL_TABLE_NOT_FOUND_ERROR: Final = 1146
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
def _parse_owner_id_column_for_mysql(column_ddl: str) -> "tuple[str, str]":
|
|
30
|
+
references_match = re.search(r"\s+REFERENCES\s+(.+)", column_ddl, re.IGNORECASE)
|
|
31
|
+
if not references_match:
|
|
32
|
+
return (column_ddl.strip(), "")
|
|
33
|
+
|
|
34
|
+
col_def = column_ddl[: references_match.start()].strip()
|
|
35
|
+
fk_clause = references_match.group(1).strip()
|
|
36
|
+
col_name = col_def.split()[0]
|
|
37
|
+
fk_constraint = f"FOREIGN KEY ({col_name}) REFERENCES {fk_clause}"
|
|
38
|
+
return (col_def, fk_constraint)
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
class MysqlConnectorAsyncADKStore(BaseAsyncADKStore["MysqlConnectorAsyncConfig"]):
|
|
42
|
+
"""MySQL/MariaDB ADK store using mysql-connector async driver."""
|
|
43
|
+
|
|
44
|
+
__slots__ = ()
|
|
45
|
+
|
|
46
|
+
def __init__(self, config: "MysqlConnectorAsyncConfig") -> None:
|
|
47
|
+
super().__init__(config)
|
|
48
|
+
|
|
49
|
+
def _parse_owner_id_column_for_mysql(self, column_ddl: str) -> "tuple[str, str]":
|
|
50
|
+
return _parse_owner_id_column_for_mysql(column_ddl)
|
|
51
|
+
|
|
52
|
+
async def _get_create_sessions_table_sql(self) -> str:
|
|
53
|
+
owner_id_col = ""
|
|
54
|
+
fk_constraint = ""
|
|
55
|
+
|
|
56
|
+
if self._owner_id_column_ddl:
|
|
57
|
+
col_def, fk_def = self._parse_owner_id_column_for_mysql(self._owner_id_column_ddl)
|
|
58
|
+
owner_id_col = f"{col_def},"
|
|
59
|
+
if fk_def:
|
|
60
|
+
fk_constraint = f",\n {fk_def}"
|
|
61
|
+
|
|
62
|
+
return f"""
|
|
63
|
+
CREATE TABLE IF NOT EXISTS {self._session_table} (
|
|
64
|
+
id VARCHAR(128) PRIMARY KEY,
|
|
65
|
+
app_name VARCHAR(128) NOT NULL,
|
|
66
|
+
user_id VARCHAR(128) NOT NULL,
|
|
67
|
+
{owner_id_col}
|
|
68
|
+
state JSON NOT NULL,
|
|
69
|
+
create_time TIMESTAMP(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6),
|
|
70
|
+
update_time TIMESTAMP(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6) ON UPDATE CURRENT_TIMESTAMP(6),
|
|
71
|
+
INDEX idx_{self._session_table}_app_user (app_name, user_id),
|
|
72
|
+
INDEX idx_{self._session_table}_update_time (update_time DESC){fk_constraint}
|
|
73
|
+
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci
|
|
74
|
+
"""
|
|
75
|
+
|
|
76
|
+
async def _get_create_events_table_sql(self) -> str:
|
|
77
|
+
return f"""
|
|
78
|
+
CREATE TABLE IF NOT EXISTS {self._events_table} (
|
|
79
|
+
id VARCHAR(128) PRIMARY KEY,
|
|
80
|
+
session_id VARCHAR(128) NOT NULL,
|
|
81
|
+
app_name VARCHAR(128) NOT NULL,
|
|
82
|
+
user_id VARCHAR(128) NOT NULL,
|
|
83
|
+
invocation_id VARCHAR(256) NOT NULL,
|
|
84
|
+
author VARCHAR(256) NOT NULL,
|
|
85
|
+
actions BLOB NOT NULL,
|
|
86
|
+
long_running_tool_ids_json JSON,
|
|
87
|
+
branch VARCHAR(256),
|
|
88
|
+
timestamp TIMESTAMP(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6),
|
|
89
|
+
content JSON,
|
|
90
|
+
grounding_metadata JSON,
|
|
91
|
+
custom_metadata JSON,
|
|
92
|
+
partial BOOLEAN,
|
|
93
|
+
turn_complete BOOLEAN,
|
|
94
|
+
interrupted BOOLEAN,
|
|
95
|
+
error_code VARCHAR(256),
|
|
96
|
+
error_message VARCHAR(1024),
|
|
97
|
+
FOREIGN KEY (session_id) REFERENCES {self._session_table}(id) ON DELETE CASCADE,
|
|
98
|
+
INDEX idx_{self._events_table}_session (session_id, timestamp ASC)
|
|
99
|
+
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci
|
|
100
|
+
"""
|
|
101
|
+
|
|
102
|
+
def _get_drop_tables_sql(self) -> "list[str]":
|
|
103
|
+
return [f"DROP TABLE IF EXISTS {self._events_table}", f"DROP TABLE IF EXISTS {self._session_table}"]
|
|
104
|
+
|
|
105
|
+
async def create_tables(self) -> None:
|
|
106
|
+
async with self._config.provide_session() as driver:
|
|
107
|
+
await driver.execute_script(await self._get_create_sessions_table_sql())
|
|
108
|
+
await driver.execute_script(await self._get_create_events_table_sql())
|
|
109
|
+
|
|
110
|
+
async def create_session(
|
|
111
|
+
self, session_id: str, app_name: str, user_id: str, state: "dict[str, Any]", owner_id: "Any | None" = None
|
|
112
|
+
) -> SessionRecord:
|
|
113
|
+
state_json = to_json(state)
|
|
114
|
+
|
|
115
|
+
params: tuple[Any, ...]
|
|
116
|
+
if self._owner_id_column_name:
|
|
117
|
+
sql = f"""
|
|
118
|
+
INSERT INTO {self._session_table} (id, app_name, user_id, {self._owner_id_column_name}, state, create_time, update_time)
|
|
119
|
+
VALUES (%s, %s, %s, %s, %s, UTC_TIMESTAMP(6), UTC_TIMESTAMP(6))
|
|
120
|
+
"""
|
|
121
|
+
params = (session_id, app_name, user_id, owner_id, state_json)
|
|
122
|
+
else:
|
|
123
|
+
sql = f"""
|
|
124
|
+
INSERT INTO {self._session_table} (id, app_name, user_id, state, create_time, update_time)
|
|
125
|
+
VALUES (%s, %s, %s, %s, UTC_TIMESTAMP(6), UTC_TIMESTAMP(6))
|
|
126
|
+
"""
|
|
127
|
+
params = (session_id, app_name, user_id, state_json)
|
|
128
|
+
|
|
129
|
+
async with self._config.provide_connection() as conn:
|
|
130
|
+
cursor = await conn.cursor()
|
|
131
|
+
try:
|
|
132
|
+
await cursor.execute(sql, params)
|
|
133
|
+
finally:
|
|
134
|
+
await cursor.close()
|
|
135
|
+
await conn.commit()
|
|
136
|
+
|
|
137
|
+
return await self.get_session(session_id) # type: ignore[return-value]
|
|
138
|
+
|
|
139
|
+
async def get_session(self, session_id: str) -> "SessionRecord | None":
|
|
140
|
+
sql = f"""
|
|
141
|
+
SELECT id, app_name, user_id, state, create_time, update_time
|
|
142
|
+
FROM {self._session_table}
|
|
143
|
+
WHERE id = %s
|
|
144
|
+
"""
|
|
145
|
+
|
|
146
|
+
try:
|
|
147
|
+
async with self._config.provide_connection() as conn:
|
|
148
|
+
cursor = await conn.cursor()
|
|
149
|
+
try:
|
|
150
|
+
await cursor.execute(sql, (session_id,))
|
|
151
|
+
row = await cursor.fetchone()
|
|
152
|
+
finally:
|
|
153
|
+
await cursor.close()
|
|
154
|
+
|
|
155
|
+
if row is None:
|
|
156
|
+
return None
|
|
157
|
+
|
|
158
|
+
session_id_val, app_name_val, user_id_val, state_json, create_time_val, update_time_val = row
|
|
159
|
+
|
|
160
|
+
return SessionRecord(
|
|
161
|
+
id=cast("str", session_id_val),
|
|
162
|
+
app_name=cast("str", app_name_val),
|
|
163
|
+
user_id=cast("str", user_id_val),
|
|
164
|
+
state=from_json(state_json) if isinstance(state_json, str) else cast("dict[str, Any]", state_json),
|
|
165
|
+
create_time=cast("datetime", create_time_val),
|
|
166
|
+
update_time=cast("datetime", update_time_val),
|
|
167
|
+
)
|
|
168
|
+
except mysql.connector.Error as exc:
|
|
169
|
+
if "doesn't exist" in str(exc) or getattr(exc, "errno", None) == MYSQL_TABLE_NOT_FOUND_ERROR:
|
|
170
|
+
return None
|
|
171
|
+
raise
|
|
172
|
+
|
|
173
|
+
async def update_session_state(self, session_id: str, state: "dict[str, Any]") -> None:
|
|
174
|
+
state_json = to_json(state)
|
|
175
|
+
|
|
176
|
+
sql = f"""
|
|
177
|
+
UPDATE {self._session_table}
|
|
178
|
+
SET state = %s
|
|
179
|
+
WHERE id = %s
|
|
180
|
+
"""
|
|
181
|
+
|
|
182
|
+
async with self._config.provide_connection() as conn:
|
|
183
|
+
cursor = await conn.cursor()
|
|
184
|
+
try:
|
|
185
|
+
await cursor.execute(sql, (state_json, session_id))
|
|
186
|
+
finally:
|
|
187
|
+
await cursor.close()
|
|
188
|
+
await conn.commit()
|
|
189
|
+
|
|
190
|
+
async def delete_session(self, session_id: str) -> None:
|
|
191
|
+
sql = f"DELETE FROM {self._session_table} WHERE id = %s"
|
|
192
|
+
|
|
193
|
+
async with self._config.provide_connection() as conn:
|
|
194
|
+
cursor = await conn.cursor()
|
|
195
|
+
try:
|
|
196
|
+
await cursor.execute(sql, (session_id,))
|
|
197
|
+
finally:
|
|
198
|
+
await cursor.close()
|
|
199
|
+
await conn.commit()
|
|
200
|
+
|
|
201
|
+
async def list_sessions(self, app_name: str, user_id: str | None = None) -> "list[SessionRecord]":
|
|
202
|
+
if user_id is None:
|
|
203
|
+
sql = f"""
|
|
204
|
+
SELECT id, app_name, user_id, state, create_time, update_time
|
|
205
|
+
FROM {self._session_table}
|
|
206
|
+
WHERE app_name = %s
|
|
207
|
+
ORDER BY update_time DESC
|
|
208
|
+
"""
|
|
209
|
+
params: tuple[str, ...] = (app_name,)
|
|
210
|
+
else:
|
|
211
|
+
sql = f"""
|
|
212
|
+
SELECT id, app_name, user_id, state, create_time, update_time
|
|
213
|
+
FROM {self._session_table}
|
|
214
|
+
WHERE app_name = %s AND user_id = %s
|
|
215
|
+
ORDER BY update_time DESC
|
|
216
|
+
"""
|
|
217
|
+
params = (app_name, user_id)
|
|
218
|
+
|
|
219
|
+
try:
|
|
220
|
+
async with self._config.provide_connection() as conn:
|
|
221
|
+
cursor = await conn.cursor()
|
|
222
|
+
try:
|
|
223
|
+
await cursor.execute(sql, params)
|
|
224
|
+
rows = await cursor.fetchall()
|
|
225
|
+
finally:
|
|
226
|
+
await cursor.close()
|
|
227
|
+
|
|
228
|
+
return [
|
|
229
|
+
SessionRecord(
|
|
230
|
+
id=cast("str", row[0]),
|
|
231
|
+
app_name=cast("str", row[1]),
|
|
232
|
+
user_id=cast("str", row[2]),
|
|
233
|
+
state=from_json(row[3]) if isinstance(row[3], str) else cast("dict[str, Any]", row[3]),
|
|
234
|
+
create_time=cast("datetime", row[4]),
|
|
235
|
+
update_time=cast("datetime", row[5]),
|
|
236
|
+
)
|
|
237
|
+
for row in rows
|
|
238
|
+
]
|
|
239
|
+
except mysql.connector.Error as exc:
|
|
240
|
+
if "doesn't exist" in str(exc) or getattr(exc, "errno", None) == MYSQL_TABLE_NOT_FOUND_ERROR:
|
|
241
|
+
return []
|
|
242
|
+
raise
|
|
243
|
+
|
|
244
|
+
async def append_event(self, event_record: EventRecord) -> None:
|
|
245
|
+
content_json = to_json(event_record.get("content")) if event_record.get("content") else None
|
|
246
|
+
grounding_metadata_json = (
|
|
247
|
+
to_json(event_record.get("grounding_metadata")) if event_record.get("grounding_metadata") else None
|
|
248
|
+
)
|
|
249
|
+
custom_metadata_json = (
|
|
250
|
+
to_json(event_record.get("custom_metadata")) if event_record.get("custom_metadata") else None
|
|
251
|
+
)
|
|
252
|
+
|
|
253
|
+
sql = f"""
|
|
254
|
+
INSERT INTO {self._events_table} (
|
|
255
|
+
id, session_id, app_name, user_id, invocation_id, author, actions,
|
|
256
|
+
long_running_tool_ids_json, branch, timestamp, content,
|
|
257
|
+
grounding_metadata, custom_metadata, partial, turn_complete,
|
|
258
|
+
interrupted, error_code, error_message
|
|
259
|
+
) VALUES (
|
|
260
|
+
%s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s
|
|
261
|
+
)
|
|
262
|
+
"""
|
|
263
|
+
|
|
264
|
+
async with self._config.provide_connection() as conn:
|
|
265
|
+
cursor = await conn.cursor()
|
|
266
|
+
try:
|
|
267
|
+
await cursor.execute(
|
|
268
|
+
sql,
|
|
269
|
+
(
|
|
270
|
+
event_record["id"],
|
|
271
|
+
event_record["session_id"],
|
|
272
|
+
event_record["app_name"],
|
|
273
|
+
event_record["user_id"],
|
|
274
|
+
event_record["invocation_id"],
|
|
275
|
+
event_record["author"],
|
|
276
|
+
event_record["actions"],
|
|
277
|
+
event_record.get("long_running_tool_ids_json"),
|
|
278
|
+
event_record.get("branch"),
|
|
279
|
+
event_record["timestamp"],
|
|
280
|
+
content_json,
|
|
281
|
+
grounding_metadata_json,
|
|
282
|
+
custom_metadata_json,
|
|
283
|
+
event_record.get("partial"),
|
|
284
|
+
event_record.get("turn_complete"),
|
|
285
|
+
event_record.get("interrupted"),
|
|
286
|
+
event_record.get("error_code"),
|
|
287
|
+
event_record.get("error_message"),
|
|
288
|
+
),
|
|
289
|
+
)
|
|
290
|
+
finally:
|
|
291
|
+
await cursor.close()
|
|
292
|
+
await conn.commit()
|
|
293
|
+
|
|
294
|
+
async def get_events(
|
|
295
|
+
self, session_id: str, after_timestamp: "datetime | None" = None, limit: "int | None" = None
|
|
296
|
+
) -> "list[EventRecord]":
|
|
297
|
+
where_clauses = ["session_id = %s"]
|
|
298
|
+
params: list[Any] = [session_id]
|
|
299
|
+
|
|
300
|
+
if after_timestamp is not None:
|
|
301
|
+
where_clauses.append("timestamp > %s")
|
|
302
|
+
params.append(after_timestamp)
|
|
303
|
+
|
|
304
|
+
where_clause = " AND ".join(where_clauses)
|
|
305
|
+
limit_clause = f" LIMIT {limit}" if limit else ""
|
|
306
|
+
|
|
307
|
+
sql = f"""
|
|
308
|
+
SELECT id, session_id, app_name, user_id, invocation_id, author, actions,
|
|
309
|
+
long_running_tool_ids_json, branch, timestamp, content,
|
|
310
|
+
grounding_metadata, custom_metadata, partial, turn_complete,
|
|
311
|
+
interrupted, error_code, error_message
|
|
312
|
+
FROM {self._events_table}
|
|
313
|
+
WHERE {where_clause}
|
|
314
|
+
ORDER BY timestamp ASC{limit_clause}
|
|
315
|
+
"""
|
|
316
|
+
|
|
317
|
+
try:
|
|
318
|
+
async with self._config.provide_connection() as conn:
|
|
319
|
+
cursor = await conn.cursor()
|
|
320
|
+
try:
|
|
321
|
+
await cursor.execute(sql, params)
|
|
322
|
+
rows = await cursor.fetchall()
|
|
323
|
+
finally:
|
|
324
|
+
await cursor.close()
|
|
325
|
+
|
|
326
|
+
return [
|
|
327
|
+
EventRecord(
|
|
328
|
+
id=cast("str", row[0]),
|
|
329
|
+
session_id=cast("str", row[1]),
|
|
330
|
+
app_name=cast("str", row[2]),
|
|
331
|
+
user_id=cast("str", row[3]),
|
|
332
|
+
invocation_id=cast("str", row[4]),
|
|
333
|
+
author=cast("str", row[5]),
|
|
334
|
+
actions=bytes(cast("bytes", row[6])),
|
|
335
|
+
long_running_tool_ids_json=cast("str | None", row[7]),
|
|
336
|
+
branch=cast("str | None", row[8]),
|
|
337
|
+
timestamp=cast("datetime", row[9]),
|
|
338
|
+
content=from_json(row[10])
|
|
339
|
+
if row[10] and isinstance(row[10], str)
|
|
340
|
+
else cast("dict[str, Any] | None", row[10]),
|
|
341
|
+
grounding_metadata=from_json(row[11])
|
|
342
|
+
if row[11] and isinstance(row[11], str)
|
|
343
|
+
else cast("dict[str, Any] | None", row[11]),
|
|
344
|
+
custom_metadata=from_json(row[12])
|
|
345
|
+
if row[12] and isinstance(row[12], str)
|
|
346
|
+
else cast("dict[str, Any] | None", row[12]),
|
|
347
|
+
partial=cast("bool | None", row[13]),
|
|
348
|
+
turn_complete=cast("bool | None", row[14]),
|
|
349
|
+
interrupted=cast("bool | None", row[15]),
|
|
350
|
+
error_code=cast("str | None", row[16]),
|
|
351
|
+
error_message=cast("str | None", row[17]),
|
|
352
|
+
)
|
|
353
|
+
for row in rows
|
|
354
|
+
]
|
|
355
|
+
except mysql.connector.Error as exc:
|
|
356
|
+
if "doesn't exist" in str(exc) or getattr(exc, "errno", None) == MYSQL_TABLE_NOT_FOUND_ERROR:
|
|
357
|
+
return []
|
|
358
|
+
raise
|
|
359
|
+
|
|
360
|
+
|
|
361
|
+
class MysqlConnectorSyncADKStore(BaseSyncADKStore["MysqlConnectorSyncConfig"]):
|
|
362
|
+
"""MySQL/MariaDB ADK store using mysql-connector sync driver."""
|
|
363
|
+
|
|
364
|
+
__slots__ = ()
|
|
365
|
+
|
|
366
|
+
def __init__(self, config: "MysqlConnectorSyncConfig") -> None:
|
|
367
|
+
super().__init__(config)
|
|
368
|
+
|
|
369
|
+
def _parse_owner_id_column_for_mysql(self, column_ddl: str) -> "tuple[str, str]":
|
|
370
|
+
return _parse_owner_id_column_for_mysql(column_ddl)
|
|
371
|
+
|
|
372
|
+
def _get_create_sessions_table_sql(self) -> str:
|
|
373
|
+
owner_id_col = ""
|
|
374
|
+
fk_constraint = ""
|
|
375
|
+
|
|
376
|
+
if self._owner_id_column_ddl:
|
|
377
|
+
col_def, fk_def = self._parse_owner_id_column_for_mysql(self._owner_id_column_ddl)
|
|
378
|
+
owner_id_col = f"{col_def},"
|
|
379
|
+
if fk_def:
|
|
380
|
+
fk_constraint = f",\n {fk_def}"
|
|
381
|
+
|
|
382
|
+
return f"""
|
|
383
|
+
CREATE TABLE IF NOT EXISTS {self._session_table} (
|
|
384
|
+
id VARCHAR(128) PRIMARY KEY,
|
|
385
|
+
app_name VARCHAR(128) NOT NULL,
|
|
386
|
+
user_id VARCHAR(128) NOT NULL,
|
|
387
|
+
{owner_id_col}
|
|
388
|
+
state JSON NOT NULL,
|
|
389
|
+
create_time TIMESTAMP(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6),
|
|
390
|
+
update_time TIMESTAMP(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6) ON UPDATE CURRENT_TIMESTAMP(6),
|
|
391
|
+
INDEX idx_{self._session_table}_app_user (app_name, user_id),
|
|
392
|
+
INDEX idx_{self._session_table}_update_time (update_time DESC){fk_constraint}
|
|
393
|
+
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci
|
|
394
|
+
"""
|
|
395
|
+
|
|
396
|
+
def _get_create_events_table_sql(self) -> str:
|
|
397
|
+
return f"""
|
|
398
|
+
CREATE TABLE IF NOT EXISTS {self._events_table} (
|
|
399
|
+
id VARCHAR(128) PRIMARY KEY,
|
|
400
|
+
session_id VARCHAR(128) NOT NULL,
|
|
401
|
+
app_name VARCHAR(128) NOT NULL,
|
|
402
|
+
user_id VARCHAR(128) NOT NULL,
|
|
403
|
+
invocation_id VARCHAR(256) NOT NULL,
|
|
404
|
+
author VARCHAR(256) NOT NULL,
|
|
405
|
+
actions BLOB NOT NULL,
|
|
406
|
+
long_running_tool_ids_json JSON,
|
|
407
|
+
branch VARCHAR(256),
|
|
408
|
+
timestamp TIMESTAMP(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6),
|
|
409
|
+
content JSON,
|
|
410
|
+
grounding_metadata JSON,
|
|
411
|
+
custom_metadata JSON,
|
|
412
|
+
partial BOOLEAN,
|
|
413
|
+
turn_complete BOOLEAN,
|
|
414
|
+
interrupted BOOLEAN,
|
|
415
|
+
error_code VARCHAR(256),
|
|
416
|
+
error_message VARCHAR(1024),
|
|
417
|
+
FOREIGN KEY (session_id) REFERENCES {self._session_table}(id) ON DELETE CASCADE,
|
|
418
|
+
INDEX idx_{self._events_table}_session (session_id, timestamp ASC)
|
|
419
|
+
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci
|
|
420
|
+
"""
|
|
421
|
+
|
|
422
|
+
def _get_drop_tables_sql(self) -> "list[str]":
|
|
423
|
+
return [f"DROP TABLE IF EXISTS {self._events_table}", f"DROP TABLE IF EXISTS {self._session_table}"]
|
|
424
|
+
|
|
425
|
+
def create_tables(self) -> None:
|
|
426
|
+
with self._config.provide_session() as driver:
|
|
427
|
+
driver.execute_script(self._get_create_sessions_table_sql())
|
|
428
|
+
driver.execute_script(self._get_create_events_table_sql())
|
|
429
|
+
|
|
430
|
+
def create_session(
|
|
431
|
+
self, session_id: str, app_name: str, user_id: str, state: "dict[str, Any]", owner_id: "Any | None" = None
|
|
432
|
+
) -> SessionRecord:
|
|
433
|
+
state_json = to_json(state)
|
|
434
|
+
|
|
435
|
+
params: tuple[Any, ...]
|
|
436
|
+
if self._owner_id_column_name:
|
|
437
|
+
sql = f"""
|
|
438
|
+
INSERT INTO {self._session_table} (id, app_name, user_id, {self._owner_id_column_name}, state, create_time, update_time)
|
|
439
|
+
VALUES (%s, %s, %s, %s, %s, UTC_TIMESTAMP(6), UTC_TIMESTAMP(6))
|
|
440
|
+
"""
|
|
441
|
+
params = (session_id, app_name, user_id, owner_id, state_json)
|
|
442
|
+
else:
|
|
443
|
+
sql = f"""
|
|
444
|
+
INSERT INTO {self._session_table} (id, app_name, user_id, state, create_time, update_time)
|
|
445
|
+
VALUES (%s, %s, %s, %s, UTC_TIMESTAMP(6), UTC_TIMESTAMP(6))
|
|
446
|
+
"""
|
|
447
|
+
params = (session_id, app_name, user_id, state_json)
|
|
448
|
+
|
|
449
|
+
with self._config.provide_connection() as conn:
|
|
450
|
+
cursor = conn.cursor()
|
|
451
|
+
try:
|
|
452
|
+
cursor.execute(sql, params)
|
|
453
|
+
finally:
|
|
454
|
+
cursor.close()
|
|
455
|
+
conn.commit()
|
|
456
|
+
|
|
457
|
+
result = self.get_session(session_id)
|
|
458
|
+
if result is None:
|
|
459
|
+
msg = "Failed to fetch created session"
|
|
460
|
+
raise RuntimeError(msg)
|
|
461
|
+
return result
|
|
462
|
+
|
|
463
|
+
def get_session(self, session_id: str) -> "SessionRecord | None":
|
|
464
|
+
sql = f"""
|
|
465
|
+
SELECT id, app_name, user_id, state, create_time, update_time
|
|
466
|
+
FROM {self._session_table}
|
|
467
|
+
WHERE id = %s
|
|
468
|
+
"""
|
|
469
|
+
|
|
470
|
+
try:
|
|
471
|
+
with self._config.provide_connection() as conn:
|
|
472
|
+
cursor = conn.cursor()
|
|
473
|
+
try:
|
|
474
|
+
cursor.execute(sql, (session_id,))
|
|
475
|
+
row = cursor.fetchone()
|
|
476
|
+
finally:
|
|
477
|
+
cursor.close()
|
|
478
|
+
|
|
479
|
+
if row is None:
|
|
480
|
+
return None
|
|
481
|
+
|
|
482
|
+
session_id_val, app_name_val, user_id_val, state_json, create_time_val, update_time_val = row
|
|
483
|
+
|
|
484
|
+
return SessionRecord(
|
|
485
|
+
id=cast("str", session_id_val),
|
|
486
|
+
app_name=cast("str", app_name_val),
|
|
487
|
+
user_id=cast("str", user_id_val),
|
|
488
|
+
state=from_json(state_json) if isinstance(state_json, str) else cast("dict[str, Any]", state_json),
|
|
489
|
+
create_time=cast("datetime", create_time_val),
|
|
490
|
+
update_time=cast("datetime", update_time_val),
|
|
491
|
+
)
|
|
492
|
+
except mysql.connector.Error as exc:
|
|
493
|
+
if "doesn't exist" in str(exc) or getattr(exc, "errno", None) == MYSQL_TABLE_NOT_FOUND_ERROR:
|
|
494
|
+
return None
|
|
495
|
+
raise
|
|
496
|
+
|
|
497
|
+
def update_session_state(self, session_id: str, state: "dict[str, Any]") -> None:
|
|
498
|
+
state_json = to_json(state)
|
|
499
|
+
|
|
500
|
+
sql = f"""
|
|
501
|
+
UPDATE {self._session_table}
|
|
502
|
+
SET state = %s
|
|
503
|
+
WHERE id = %s
|
|
504
|
+
"""
|
|
505
|
+
|
|
506
|
+
with self._config.provide_connection() as conn:
|
|
507
|
+
cursor = conn.cursor()
|
|
508
|
+
try:
|
|
509
|
+
cursor.execute(sql, (state_json, session_id))
|
|
510
|
+
finally:
|
|
511
|
+
cursor.close()
|
|
512
|
+
conn.commit()
|
|
513
|
+
|
|
514
|
+
def delete_session(self, session_id: str) -> None:
|
|
515
|
+
sql = f"DELETE FROM {self._session_table} WHERE id = %s"
|
|
516
|
+
|
|
517
|
+
with self._config.provide_connection() as conn:
|
|
518
|
+
cursor = conn.cursor()
|
|
519
|
+
try:
|
|
520
|
+
cursor.execute(sql, (session_id,))
|
|
521
|
+
finally:
|
|
522
|
+
cursor.close()
|
|
523
|
+
conn.commit()
|
|
524
|
+
|
|
525
|
+
def list_sessions(self, app_name: str, user_id: str | None = None) -> "list[SessionRecord]":
|
|
526
|
+
if user_id is None:
|
|
527
|
+
sql = f"""
|
|
528
|
+
SELECT id, app_name, user_id, state, create_time, update_time
|
|
529
|
+
FROM {self._session_table}
|
|
530
|
+
WHERE app_name = %s
|
|
531
|
+
ORDER BY update_time DESC
|
|
532
|
+
"""
|
|
533
|
+
params: tuple[str, ...] = (app_name,)
|
|
534
|
+
else:
|
|
535
|
+
sql = f"""
|
|
536
|
+
SELECT id, app_name, user_id, state, create_time, update_time
|
|
537
|
+
FROM {self._session_table}
|
|
538
|
+
WHERE app_name = %s AND user_id = %s
|
|
539
|
+
ORDER BY update_time DESC
|
|
540
|
+
"""
|
|
541
|
+
params = (app_name, user_id)
|
|
542
|
+
|
|
543
|
+
try:
|
|
544
|
+
with self._config.provide_connection() as conn:
|
|
545
|
+
cursor = conn.cursor()
|
|
546
|
+
try:
|
|
547
|
+
cursor.execute(sql, params)
|
|
548
|
+
rows = cursor.fetchall()
|
|
549
|
+
finally:
|
|
550
|
+
cursor.close()
|
|
551
|
+
|
|
552
|
+
return [
|
|
553
|
+
SessionRecord(
|
|
554
|
+
id=cast("str", row[0]),
|
|
555
|
+
app_name=cast("str", row[1]),
|
|
556
|
+
user_id=cast("str", row[2]),
|
|
557
|
+
state=from_json(row[3]) if isinstance(row[3], str) else cast("dict[str, Any]", row[3]),
|
|
558
|
+
create_time=cast("datetime", row[4]),
|
|
559
|
+
update_time=cast("datetime", row[5]),
|
|
560
|
+
)
|
|
561
|
+
for row in rows
|
|
562
|
+
]
|
|
563
|
+
except mysql.connector.Error as exc:
|
|
564
|
+
if "doesn't exist" in str(exc) or getattr(exc, "errno", None) == MYSQL_TABLE_NOT_FOUND_ERROR:
|
|
565
|
+
return []
|
|
566
|
+
raise
|
|
567
|
+
|
|
568
|
+
def append_event(self, event_record: EventRecord) -> None:
|
|
569
|
+
content_json = to_json(event_record.get("content")) if event_record.get("content") else None
|
|
570
|
+
grounding_metadata_json = (
|
|
571
|
+
to_json(event_record.get("grounding_metadata")) if event_record.get("grounding_metadata") else None
|
|
572
|
+
)
|
|
573
|
+
custom_metadata_json = (
|
|
574
|
+
to_json(event_record.get("custom_metadata")) if event_record.get("custom_metadata") else None
|
|
575
|
+
)
|
|
576
|
+
|
|
577
|
+
sql = f"""
|
|
578
|
+
INSERT INTO {self._events_table} (
|
|
579
|
+
id, session_id, app_name, user_id, invocation_id, author, actions,
|
|
580
|
+
long_running_tool_ids_json, branch, timestamp, content,
|
|
581
|
+
grounding_metadata, custom_metadata, partial, turn_complete,
|
|
582
|
+
interrupted, error_code, error_message
|
|
583
|
+
) VALUES (
|
|
584
|
+
%s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s
|
|
585
|
+
)
|
|
586
|
+
"""
|
|
587
|
+
|
|
588
|
+
with self._config.provide_connection() as conn:
|
|
589
|
+
cursor = conn.cursor()
|
|
590
|
+
try:
|
|
591
|
+
cursor.execute(
|
|
592
|
+
sql,
|
|
593
|
+
(
|
|
594
|
+
event_record["id"],
|
|
595
|
+
event_record["session_id"],
|
|
596
|
+
event_record["app_name"],
|
|
597
|
+
event_record["user_id"],
|
|
598
|
+
event_record["invocation_id"],
|
|
599
|
+
event_record["author"],
|
|
600
|
+
event_record["actions"],
|
|
601
|
+
event_record.get("long_running_tool_ids_json"),
|
|
602
|
+
event_record.get("branch"),
|
|
603
|
+
event_record["timestamp"],
|
|
604
|
+
content_json,
|
|
605
|
+
grounding_metadata_json,
|
|
606
|
+
custom_metadata_json,
|
|
607
|
+
event_record.get("partial"),
|
|
608
|
+
event_record.get("turn_complete"),
|
|
609
|
+
event_record.get("interrupted"),
|
|
610
|
+
event_record.get("error_code"),
|
|
611
|
+
event_record.get("error_message"),
|
|
612
|
+
),
|
|
613
|
+
)
|
|
614
|
+
finally:
|
|
615
|
+
cursor.close()
|
|
616
|
+
conn.commit()
|
|
617
|
+
|
|
618
|
+
def get_events(
|
|
619
|
+
self, session_id: str, after_timestamp: "datetime | None" = None, limit: "int | None" = None
|
|
620
|
+
) -> "list[EventRecord]":
|
|
621
|
+
where_clauses = ["session_id = %s"]
|
|
622
|
+
params: list[Any] = [session_id]
|
|
623
|
+
|
|
624
|
+
if after_timestamp is not None:
|
|
625
|
+
where_clauses.append("timestamp > %s")
|
|
626
|
+
params.append(after_timestamp)
|
|
627
|
+
|
|
628
|
+
where_clause = " AND ".join(where_clauses)
|
|
629
|
+
limit_clause = f" LIMIT {limit}" if limit else ""
|
|
630
|
+
|
|
631
|
+
sql = f"""
|
|
632
|
+
SELECT id, session_id, app_name, user_id, invocation_id, author, actions,
|
|
633
|
+
long_running_tool_ids_json, branch, timestamp, content,
|
|
634
|
+
grounding_metadata, custom_metadata, partial, turn_complete,
|
|
635
|
+
interrupted, error_code, error_message
|
|
636
|
+
FROM {self._events_table}
|
|
637
|
+
WHERE {where_clause}
|
|
638
|
+
ORDER BY timestamp ASC{limit_clause}
|
|
639
|
+
"""
|
|
640
|
+
|
|
641
|
+
try:
|
|
642
|
+
with self._config.provide_connection() as conn:
|
|
643
|
+
cursor = conn.cursor()
|
|
644
|
+
try:
|
|
645
|
+
cursor.execute(sql, params)
|
|
646
|
+
rows = cursor.fetchall()
|
|
647
|
+
finally:
|
|
648
|
+
cursor.close()
|
|
649
|
+
|
|
650
|
+
return [
|
|
651
|
+
EventRecord(
|
|
652
|
+
id=cast("str", row[0]),
|
|
653
|
+
session_id=cast("str", row[1]),
|
|
654
|
+
app_name=cast("str", row[2]),
|
|
655
|
+
user_id=cast("str", row[3]),
|
|
656
|
+
invocation_id=cast("str", row[4]),
|
|
657
|
+
author=cast("str", row[5]),
|
|
658
|
+
actions=bytes(cast("bytes", row[6])),
|
|
659
|
+
long_running_tool_ids_json=cast("str | None", row[7]),
|
|
660
|
+
branch=cast("str | None", row[8]),
|
|
661
|
+
timestamp=cast("datetime", row[9]),
|
|
662
|
+
content=from_json(row[10])
|
|
663
|
+
if row[10] and isinstance(row[10], str)
|
|
664
|
+
else cast("dict[str, Any] | None", row[10]),
|
|
665
|
+
grounding_metadata=from_json(row[11])
|
|
666
|
+
if row[11] and isinstance(row[11], str)
|
|
667
|
+
else cast("dict[str, Any] | None", row[11]),
|
|
668
|
+
custom_metadata=from_json(row[12])
|
|
669
|
+
if row[12] and isinstance(row[12], str)
|
|
670
|
+
else cast("dict[str, Any] | None", row[12]),
|
|
671
|
+
partial=cast("bool | None", row[13]),
|
|
672
|
+
turn_complete=cast("bool | None", row[14]),
|
|
673
|
+
interrupted=cast("bool | None", row[15]),
|
|
674
|
+
error_code=cast("str | None", row[16]),
|
|
675
|
+
error_message=cast("str | None", row[17]),
|
|
676
|
+
)
|
|
677
|
+
for row in rows
|
|
678
|
+
]
|
|
679
|
+
except mysql.connector.Error as exc:
|
|
680
|
+
if "doesn't exist" in str(exc) or getattr(exc, "errno", None) == MYSQL_TABLE_NOT_FOUND_ERROR:
|
|
681
|
+
return []
|
|
682
|
+
raise
|
|
683
|
+
|
|
684
|
+
|
|
685
|
+
class MysqlConnectorAsyncADKMemoryStore(BaseAsyncADKMemoryStore["MysqlConnectorAsyncConfig"]):
|
|
686
|
+
"""MySQL/MariaDB ADK memory store using mysql-connector async driver."""
|
|
687
|
+
|
|
688
|
+
__slots__ = ()
|
|
689
|
+
|
|
690
|
+
def __init__(self, config: "MysqlConnectorAsyncConfig") -> None:
|
|
691
|
+
super().__init__(config)
|
|
692
|
+
|
|
693
|
+
async def _get_create_memory_table_sql(self) -> str:
|
|
694
|
+
owner_id_line = ""
|
|
695
|
+
fk_constraint = ""
|
|
696
|
+
if self._owner_id_column_ddl:
|
|
697
|
+
col_def, fk_def = _parse_owner_id_column_for_mysql(self._owner_id_column_ddl)
|
|
698
|
+
owner_id_line = f",\n {col_def}"
|
|
699
|
+
if fk_def:
|
|
700
|
+
fk_constraint = f",\n {fk_def}"
|
|
701
|
+
|
|
702
|
+
fts_index = ""
|
|
703
|
+
if self._use_fts:
|
|
704
|
+
fts_index = f",\n FULLTEXT INDEX idx_{self._memory_table}_fts (content_text)"
|
|
705
|
+
|
|
706
|
+
return f"""
|
|
707
|
+
CREATE TABLE IF NOT EXISTS {self._memory_table} (
|
|
708
|
+
id VARCHAR(128) PRIMARY KEY,
|
|
709
|
+
session_id VARCHAR(128) NOT NULL,
|
|
710
|
+
app_name VARCHAR(128) NOT NULL,
|
|
711
|
+
user_id VARCHAR(128) NOT NULL,
|
|
712
|
+
event_id VARCHAR(128) NOT NULL UNIQUE,
|
|
713
|
+
author VARCHAR(256){owner_id_line},
|
|
714
|
+
timestamp TIMESTAMP(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6),
|
|
715
|
+
content_json JSON NOT NULL,
|
|
716
|
+
content_text TEXT NOT NULL,
|
|
717
|
+
metadata_json JSON,
|
|
718
|
+
inserted_at TIMESTAMP(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6),
|
|
719
|
+
INDEX idx_{self._memory_table}_app_user_time (app_name, user_id, timestamp),
|
|
720
|
+
INDEX idx_{self._memory_table}_session (session_id){fts_index}{fk_constraint}
|
|
721
|
+
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci
|
|
722
|
+
"""
|
|
723
|
+
|
|
724
|
+
def _get_drop_memory_table_sql(self) -> "list[str]":
|
|
725
|
+
return [f"DROP TABLE IF EXISTS {self._memory_table}"]
|
|
726
|
+
|
|
727
|
+
async def create_tables(self) -> None:
|
|
728
|
+
if not self._enabled:
|
|
729
|
+
return
|
|
730
|
+
|
|
731
|
+
async with self._config.provide_session() as driver:
|
|
732
|
+
await driver.execute_script(await self._get_create_memory_table_sql())
|
|
733
|
+
|
|
734
|
+
async def insert_memory_entries(self, entries: "list[MemoryRecord]", owner_id: "object | None" = None) -> int:
|
|
735
|
+
if not self._enabled:
|
|
736
|
+
msg = "Memory store is disabled"
|
|
737
|
+
raise RuntimeError(msg)
|
|
738
|
+
|
|
739
|
+
if not entries:
|
|
740
|
+
return 0
|
|
741
|
+
|
|
742
|
+
inserted_count = 0
|
|
743
|
+
if self._owner_id_column_name:
|
|
744
|
+
sql = f"""
|
|
745
|
+
INSERT IGNORE INTO {self._memory_table} (
|
|
746
|
+
id, session_id, app_name, user_id, event_id, author,
|
|
747
|
+
{self._owner_id_column_name}, timestamp, content_json,
|
|
748
|
+
content_text, metadata_json, inserted_at
|
|
749
|
+
) VALUES (%s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s)
|
|
750
|
+
"""
|
|
751
|
+
else:
|
|
752
|
+
sql = f"""
|
|
753
|
+
INSERT IGNORE INTO {self._memory_table} (
|
|
754
|
+
id, session_id, app_name, user_id, event_id, author,
|
|
755
|
+
timestamp, content_json, content_text, metadata_json, inserted_at
|
|
756
|
+
) VALUES (%s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s)
|
|
757
|
+
"""
|
|
758
|
+
|
|
759
|
+
async with self._config.provide_connection() as conn:
|
|
760
|
+
cursor = await conn.cursor()
|
|
761
|
+
try:
|
|
762
|
+
for entry in entries:
|
|
763
|
+
params: tuple[Any, ...]
|
|
764
|
+
if self._owner_id_column_name:
|
|
765
|
+
params = (
|
|
766
|
+
entry["id"],
|
|
767
|
+
entry["session_id"],
|
|
768
|
+
entry["app_name"],
|
|
769
|
+
entry["user_id"],
|
|
770
|
+
entry["event_id"],
|
|
771
|
+
entry["author"],
|
|
772
|
+
owner_id,
|
|
773
|
+
entry["timestamp"],
|
|
774
|
+
to_json(entry["content_json"]),
|
|
775
|
+
entry["content_text"],
|
|
776
|
+
to_json(entry["metadata_json"]),
|
|
777
|
+
entry["inserted_at"],
|
|
778
|
+
)
|
|
779
|
+
else:
|
|
780
|
+
params = (
|
|
781
|
+
entry["id"],
|
|
782
|
+
entry["session_id"],
|
|
783
|
+
entry["app_name"],
|
|
784
|
+
entry["user_id"],
|
|
785
|
+
entry["event_id"],
|
|
786
|
+
entry["author"],
|
|
787
|
+
entry["timestamp"],
|
|
788
|
+
to_json(entry["content_json"]),
|
|
789
|
+
entry["content_text"],
|
|
790
|
+
to_json(entry["metadata_json"]),
|
|
791
|
+
entry["inserted_at"],
|
|
792
|
+
)
|
|
793
|
+
await cursor.execute(sql, params)
|
|
794
|
+
inserted_count += cursor.rowcount
|
|
795
|
+
finally:
|
|
796
|
+
await cursor.close()
|
|
797
|
+
await conn.commit()
|
|
798
|
+
return inserted_count
|
|
799
|
+
|
|
800
|
+
async def search_entries(
|
|
801
|
+
self, query: str, app_name: str, user_id: str, limit: "int | None" = None
|
|
802
|
+
) -> "list[MemoryRecord]":
|
|
803
|
+
if not self._enabled:
|
|
804
|
+
msg = "Memory store is disabled"
|
|
805
|
+
raise RuntimeError(msg)
|
|
806
|
+
|
|
807
|
+
if not query:
|
|
808
|
+
return []
|
|
809
|
+
|
|
810
|
+
limit_value = limit or self._max_results
|
|
811
|
+
if self._use_fts:
|
|
812
|
+
sql = f"""
|
|
813
|
+
SELECT * FROM {self._memory_table}
|
|
814
|
+
WHERE app_name = %s AND user_id = %s
|
|
815
|
+
AND MATCH(content_text) AGAINST (%s IN NATURAL LANGUAGE MODE)
|
|
816
|
+
ORDER BY timestamp DESC
|
|
817
|
+
LIMIT %s
|
|
818
|
+
"""
|
|
819
|
+
params = (app_name, user_id, query, limit_value)
|
|
820
|
+
else:
|
|
821
|
+
sql = f"""
|
|
822
|
+
SELECT * FROM {self._memory_table}
|
|
823
|
+
WHERE app_name = %s AND user_id = %s AND content_text LIKE %s
|
|
824
|
+
ORDER BY timestamp DESC
|
|
825
|
+
LIMIT %s
|
|
826
|
+
"""
|
|
827
|
+
params = (app_name, user_id, f"%{query}%", limit_value)
|
|
828
|
+
|
|
829
|
+
async with self._config.provide_connection() as conn:
|
|
830
|
+
cursor = await conn.cursor()
|
|
831
|
+
try:
|
|
832
|
+
await cursor.execute(sql, params)
|
|
833
|
+
rows = await cursor.fetchall()
|
|
834
|
+
columns = [col[0] for col in cursor.description or []]
|
|
835
|
+
finally:
|
|
836
|
+
await cursor.close()
|
|
837
|
+
|
|
838
|
+
return [cast("MemoryRecord", dict(zip(columns, row, strict=False))) for row in rows]
|
|
839
|
+
|
|
840
|
+
async def delete_entries_by_session(self, session_id: str) -> int:
|
|
841
|
+
if not self._enabled:
|
|
842
|
+
msg = "Memory store is disabled"
|
|
843
|
+
raise RuntimeError(msg)
|
|
844
|
+
|
|
845
|
+
sql = f"DELETE FROM {self._memory_table} WHERE session_id = %s"
|
|
846
|
+
async with self._config.provide_connection() as conn:
|
|
847
|
+
cursor = await conn.cursor()
|
|
848
|
+
try:
|
|
849
|
+
await cursor.execute(sql, (session_id,))
|
|
850
|
+
await conn.commit()
|
|
851
|
+
return cursor.rowcount if cursor.rowcount and cursor.rowcount > 0 else 0
|
|
852
|
+
finally:
|
|
853
|
+
await cursor.close()
|
|
854
|
+
|
|
855
|
+
async def delete_entries_older_than(self, days: int) -> int:
|
|
856
|
+
if not self._enabled:
|
|
857
|
+
msg = "Memory store is disabled"
|
|
858
|
+
raise RuntimeError(msg)
|
|
859
|
+
|
|
860
|
+
sql = f"""
|
|
861
|
+
DELETE FROM {self._memory_table}
|
|
862
|
+
WHERE inserted_at < (UTC_TIMESTAMP(6) - INTERVAL %s DAY)
|
|
863
|
+
"""
|
|
864
|
+
async with self._config.provide_connection() as conn:
|
|
865
|
+
cursor = await conn.cursor()
|
|
866
|
+
try:
|
|
867
|
+
await cursor.execute(sql, (days,))
|
|
868
|
+
await conn.commit()
|
|
869
|
+
return cursor.rowcount if cursor.rowcount and cursor.rowcount > 0 else 0
|
|
870
|
+
finally:
|
|
871
|
+
await cursor.close()
|
|
872
|
+
|
|
873
|
+
|
|
874
|
+
class MysqlConnectorSyncADKMemoryStore(BaseSyncADKMemoryStore["MysqlConnectorSyncConfig"]):
|
|
875
|
+
"""MySQL/MariaDB ADK memory store using mysql-connector sync driver."""
|
|
876
|
+
|
|
877
|
+
__slots__ = ()
|
|
878
|
+
|
|
879
|
+
def __init__(self, config: "MysqlConnectorSyncConfig") -> None:
|
|
880
|
+
super().__init__(config)
|
|
881
|
+
|
|
882
|
+
def _get_create_memory_table_sql(self) -> str:
|
|
883
|
+
owner_id_line = ""
|
|
884
|
+
fk_constraint = ""
|
|
885
|
+
if self._owner_id_column_ddl:
|
|
886
|
+
col_def, fk_def = _parse_owner_id_column_for_mysql(self._owner_id_column_ddl)
|
|
887
|
+
owner_id_line = f",\n {col_def}"
|
|
888
|
+
if fk_def:
|
|
889
|
+
fk_constraint = f",\n {fk_def}"
|
|
890
|
+
|
|
891
|
+
fts_index = ""
|
|
892
|
+
if self._use_fts:
|
|
893
|
+
fts_index = f",\n FULLTEXT INDEX idx_{self._memory_table}_fts (content_text)"
|
|
894
|
+
|
|
895
|
+
return f"""
|
|
896
|
+
CREATE TABLE IF NOT EXISTS {self._memory_table} (
|
|
897
|
+
id VARCHAR(128) PRIMARY KEY,
|
|
898
|
+
session_id VARCHAR(128) NOT NULL,
|
|
899
|
+
app_name VARCHAR(128) NOT NULL,
|
|
900
|
+
user_id VARCHAR(128) NOT NULL,
|
|
901
|
+
event_id VARCHAR(128) NOT NULL UNIQUE,
|
|
902
|
+
author VARCHAR(256){owner_id_line},
|
|
903
|
+
timestamp TIMESTAMP(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6),
|
|
904
|
+
content_json JSON NOT NULL,
|
|
905
|
+
content_text TEXT NOT NULL,
|
|
906
|
+
metadata_json JSON,
|
|
907
|
+
inserted_at TIMESTAMP(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6),
|
|
908
|
+
INDEX idx_{self._memory_table}_app_user_time (app_name, user_id, timestamp),
|
|
909
|
+
INDEX idx_{self._memory_table}_session (session_id){fts_index}{fk_constraint}
|
|
910
|
+
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci
|
|
911
|
+
"""
|
|
912
|
+
|
|
913
|
+
def _get_drop_memory_table_sql(self) -> "list[str]":
|
|
914
|
+
return [f"DROP TABLE IF EXISTS {self._memory_table}"]
|
|
915
|
+
|
|
916
|
+
def create_tables(self) -> None:
|
|
917
|
+
if not self._enabled:
|
|
918
|
+
return
|
|
919
|
+
|
|
920
|
+
with self._config.provide_session() as driver:
|
|
921
|
+
driver.execute_script(self._get_create_memory_table_sql())
|
|
922
|
+
|
|
923
|
+
def insert_memory_entries(self, entries: "list[MemoryRecord]", owner_id: "object | None" = None) -> int:
|
|
924
|
+
if not self._enabled:
|
|
925
|
+
msg = "Memory store is disabled"
|
|
926
|
+
raise RuntimeError(msg)
|
|
927
|
+
|
|
928
|
+
if not entries:
|
|
929
|
+
return 0
|
|
930
|
+
|
|
931
|
+
inserted_count = 0
|
|
932
|
+
if self._owner_id_column_name:
|
|
933
|
+
sql = f"""
|
|
934
|
+
INSERT IGNORE INTO {self._memory_table} (
|
|
935
|
+
id, session_id, app_name, user_id, event_id, author,
|
|
936
|
+
{self._owner_id_column_name}, timestamp, content_json,
|
|
937
|
+
content_text, metadata_json, inserted_at
|
|
938
|
+
) VALUES (%s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s)
|
|
939
|
+
"""
|
|
940
|
+
else:
|
|
941
|
+
sql = f"""
|
|
942
|
+
INSERT IGNORE INTO {self._memory_table} (
|
|
943
|
+
id, session_id, app_name, user_id, event_id, author,
|
|
944
|
+
timestamp, content_json, content_text, metadata_json, inserted_at
|
|
945
|
+
) VALUES (%s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s)
|
|
946
|
+
"""
|
|
947
|
+
|
|
948
|
+
with self._config.provide_connection() as conn:
|
|
949
|
+
cursor = conn.cursor()
|
|
950
|
+
try:
|
|
951
|
+
for entry in entries:
|
|
952
|
+
params: tuple[Any, ...]
|
|
953
|
+
if self._owner_id_column_name:
|
|
954
|
+
params = (
|
|
955
|
+
entry["id"],
|
|
956
|
+
entry["session_id"],
|
|
957
|
+
entry["app_name"],
|
|
958
|
+
entry["user_id"],
|
|
959
|
+
entry["event_id"],
|
|
960
|
+
entry["author"],
|
|
961
|
+
owner_id,
|
|
962
|
+
entry["timestamp"],
|
|
963
|
+
to_json(entry["content_json"]),
|
|
964
|
+
entry["content_text"],
|
|
965
|
+
to_json(entry["metadata_json"]),
|
|
966
|
+
entry["inserted_at"],
|
|
967
|
+
)
|
|
968
|
+
else:
|
|
969
|
+
params = (
|
|
970
|
+
entry["id"],
|
|
971
|
+
entry["session_id"],
|
|
972
|
+
entry["app_name"],
|
|
973
|
+
entry["user_id"],
|
|
974
|
+
entry["event_id"],
|
|
975
|
+
entry["author"],
|
|
976
|
+
entry["timestamp"],
|
|
977
|
+
to_json(entry["content_json"]),
|
|
978
|
+
entry["content_text"],
|
|
979
|
+
to_json(entry["metadata_json"]),
|
|
980
|
+
entry["inserted_at"],
|
|
981
|
+
)
|
|
982
|
+
cursor.execute(sql, cast("tuple[Any, ...]", params))
|
|
983
|
+
inserted_count += cursor.rowcount
|
|
984
|
+
finally:
|
|
985
|
+
cursor.close()
|
|
986
|
+
conn.commit()
|
|
987
|
+
return inserted_count
|
|
988
|
+
|
|
989
|
+
def search_entries(
|
|
990
|
+
self, query: str, app_name: str, user_id: str, limit: "int | None" = None
|
|
991
|
+
) -> "list[MemoryRecord]":
|
|
992
|
+
if not self._enabled:
|
|
993
|
+
msg = "Memory store is disabled"
|
|
994
|
+
raise RuntimeError(msg)
|
|
995
|
+
|
|
996
|
+
if not query:
|
|
997
|
+
return []
|
|
998
|
+
|
|
999
|
+
limit_value = limit or self._max_results
|
|
1000
|
+
if self._use_fts:
|
|
1001
|
+
sql = f"""
|
|
1002
|
+
SELECT * FROM {self._memory_table}
|
|
1003
|
+
WHERE app_name = %s AND user_id = %s
|
|
1004
|
+
AND MATCH(content_text) AGAINST (%s IN NATURAL LANGUAGE MODE)
|
|
1005
|
+
ORDER BY timestamp DESC
|
|
1006
|
+
LIMIT %s
|
|
1007
|
+
"""
|
|
1008
|
+
params = (app_name, user_id, query, limit_value)
|
|
1009
|
+
else:
|
|
1010
|
+
sql = f"""
|
|
1011
|
+
SELECT * FROM {self._memory_table}
|
|
1012
|
+
WHERE app_name = %s AND user_id = %s AND content_text LIKE %s
|
|
1013
|
+
ORDER BY timestamp DESC
|
|
1014
|
+
LIMIT %s
|
|
1015
|
+
"""
|
|
1016
|
+
params = (app_name, user_id, f"%{query}%", limit_value)
|
|
1017
|
+
|
|
1018
|
+
with self._config.provide_connection() as conn:
|
|
1019
|
+
cursor = conn.cursor()
|
|
1020
|
+
try:
|
|
1021
|
+
cursor.execute(sql, params)
|
|
1022
|
+
rows = cursor.fetchall()
|
|
1023
|
+
columns = [col[0] for col in cursor.description or []]
|
|
1024
|
+
finally:
|
|
1025
|
+
cursor.close()
|
|
1026
|
+
|
|
1027
|
+
return [cast("MemoryRecord", dict(zip(columns, row, strict=False))) for row in rows]
|
|
1028
|
+
|
|
1029
|
+
def delete_entries_by_session(self, session_id: str) -> int:
|
|
1030
|
+
if not self._enabled:
|
|
1031
|
+
msg = "Memory store is disabled"
|
|
1032
|
+
raise RuntimeError(msg)
|
|
1033
|
+
|
|
1034
|
+
sql = f"DELETE FROM {self._memory_table} WHERE session_id = %s"
|
|
1035
|
+
with self._config.provide_connection() as conn:
|
|
1036
|
+
cursor = conn.cursor()
|
|
1037
|
+
try:
|
|
1038
|
+
cursor.execute(sql, (session_id,))
|
|
1039
|
+
conn.commit()
|
|
1040
|
+
return cursor.rowcount if cursor.rowcount and cursor.rowcount > 0 else 0
|
|
1041
|
+
finally:
|
|
1042
|
+
cursor.close()
|
|
1043
|
+
|
|
1044
|
+
def delete_entries_older_than(self, days: int) -> int:
|
|
1045
|
+
if not self._enabled:
|
|
1046
|
+
msg = "Memory store is disabled"
|
|
1047
|
+
raise RuntimeError(msg)
|
|
1048
|
+
|
|
1049
|
+
sql = f"""
|
|
1050
|
+
DELETE FROM {self._memory_table}
|
|
1051
|
+
WHERE inserted_at < (UTC_TIMESTAMP(6) - INTERVAL %s DAY)
|
|
1052
|
+
"""
|
|
1053
|
+
with self._config.provide_connection() as conn:
|
|
1054
|
+
cursor = conn.cursor()
|
|
1055
|
+
try:
|
|
1056
|
+
cursor.execute(sql, (days,))
|
|
1057
|
+
conn.commit()
|
|
1058
|
+
return cursor.rowcount if cursor.rowcount and cursor.rowcount > 0 else 0
|
|
1059
|
+
finally:
|
|
1060
|
+
cursor.close()
|