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,461 @@
|
|
|
1
|
+
"""Table-backed queue implementation for EventChannel."""
|
|
2
|
+
|
|
3
|
+
import asyncio
|
|
4
|
+
import time
|
|
5
|
+
from datetime import datetime, timedelta, timezone
|
|
6
|
+
from typing import TYPE_CHECKING, Any, cast
|
|
7
|
+
|
|
8
|
+
from sqlspec.core import SQL, StatementConfig
|
|
9
|
+
from sqlspec.extensions.events._hints import EventRuntimeHints, get_runtime_hints, resolve_adapter_name
|
|
10
|
+
from sqlspec.extensions.events._models import EventMessage
|
|
11
|
+
from sqlspec.extensions.events._payload import parse_event_timestamp
|
|
12
|
+
from sqlspec.extensions.events._store import normalize_queue_table_name
|
|
13
|
+
from sqlspec.utils.logging import get_logger
|
|
14
|
+
from sqlspec.utils.serializers import from_json
|
|
15
|
+
from sqlspec.utils.uuids import uuid4
|
|
16
|
+
|
|
17
|
+
if TYPE_CHECKING:
|
|
18
|
+
from contextlib import AbstractAsyncContextManager, AbstractContextManager
|
|
19
|
+
|
|
20
|
+
from sqlspec.config import DatabaseConfigProtocol
|
|
21
|
+
from sqlspec.driver import AsyncDriverAdapterBase, SyncDriverAdapterBase
|
|
22
|
+
|
|
23
|
+
logger = get_logger("sqlspec.events.queue")
|
|
24
|
+
|
|
25
|
+
__all__ = ("AsyncTableEventQueue", "SyncTableEventQueue", "build_queue_backend")
|
|
26
|
+
|
|
27
|
+
_PENDING_STATUS = "pending"
|
|
28
|
+
_LEASED_STATUS = "leased"
|
|
29
|
+
_ACKED_STATUS = "acked"
|
|
30
|
+
_DEFAULT_TABLE = "sqlspec_event_queue"
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
class _BaseTableEventQueue:
|
|
34
|
+
"""Base class with shared SQL generation and hydration logic."""
|
|
35
|
+
|
|
36
|
+
__slots__ = (
|
|
37
|
+
"_ack_sql",
|
|
38
|
+
"_acked_cleanup_sql",
|
|
39
|
+
"_claim_sql",
|
|
40
|
+
"_config",
|
|
41
|
+
"_dialect",
|
|
42
|
+
"_lease_seconds",
|
|
43
|
+
"_max_claim_attempts",
|
|
44
|
+
"_nack_sql",
|
|
45
|
+
"_retention_seconds",
|
|
46
|
+
"_runtime",
|
|
47
|
+
"_select_by_id_sql",
|
|
48
|
+
"_select_sql",
|
|
49
|
+
"_statement_config",
|
|
50
|
+
"_table_name",
|
|
51
|
+
"_upsert_sql",
|
|
52
|
+
)
|
|
53
|
+
|
|
54
|
+
def __init__(
|
|
55
|
+
self,
|
|
56
|
+
config: "DatabaseConfigProtocol[Any, Any, Any]",
|
|
57
|
+
*,
|
|
58
|
+
queue_table: str | None = None,
|
|
59
|
+
lease_seconds: int | None = None,
|
|
60
|
+
retention_seconds: int | None = None,
|
|
61
|
+
select_for_update: bool | None = None,
|
|
62
|
+
skip_locked: bool | None = None,
|
|
63
|
+
) -> None:
|
|
64
|
+
self._config = config
|
|
65
|
+
self._statement_config = config.statement_config
|
|
66
|
+
self._runtime = config.get_observability_runtime()
|
|
67
|
+
self._dialect = str(self._statement_config.dialect or "").lower() if self._statement_config else ""
|
|
68
|
+
self._table_name = normalize_queue_table_name(queue_table or _DEFAULT_TABLE)
|
|
69
|
+
self._lease_seconds = lease_seconds or 30
|
|
70
|
+
self._retention_seconds = retention_seconds or 86_400
|
|
71
|
+
self._max_claim_attempts = 5
|
|
72
|
+
self._upsert_sql = self._build_insert_sql()
|
|
73
|
+
self._select_sql = self._build_select_sql(bool(select_for_update), bool(skip_locked))
|
|
74
|
+
self._select_by_id_sql = self._build_select_by_id_sql()
|
|
75
|
+
self._claim_sql = self._build_claim_sql()
|
|
76
|
+
self._ack_sql = self._build_ack_sql()
|
|
77
|
+
self._nack_sql = self._build_nack_sql()
|
|
78
|
+
self._acked_cleanup_sql = self._build_cleanup_sql()
|
|
79
|
+
|
|
80
|
+
@property
|
|
81
|
+
def statement_config(self) -> "StatementConfig":
|
|
82
|
+
return self._statement_config
|
|
83
|
+
|
|
84
|
+
def _build_insert_sql(self) -> str:
|
|
85
|
+
columns = "event_id, channel, payload_json, metadata_json, status, available_at, lease_expires_at, attempts, created_at"
|
|
86
|
+
values = ":event_id, :channel, :payload_json, :metadata_json, :status, :available_at, :lease_expires_at, :attempts, :created_at"
|
|
87
|
+
return f"INSERT INTO {self._table_name} ({columns}) VALUES ({values})"
|
|
88
|
+
|
|
89
|
+
def _build_select_sql(self, select_for_update: bool, skip_locked: bool) -> str:
|
|
90
|
+
limit_clause = " FETCH FIRST 1 ROWS ONLY" if "oracle" in self._dialect else " LIMIT 1"
|
|
91
|
+
base = (
|
|
92
|
+
f"SELECT event_id, channel, payload_json, metadata_json, attempts, available_at, lease_expires_at, created_at "
|
|
93
|
+
f"FROM {self._table_name} "
|
|
94
|
+
"WHERE channel = :channel AND available_at <= :available_cutoff AND ("
|
|
95
|
+
"status = :pending_status OR (status = :leased_status AND (lease_expires_at IS NULL OR lease_expires_at <= :lease_cutoff))"
|
|
96
|
+
") ORDER BY created_at ASC"
|
|
97
|
+
)
|
|
98
|
+
locking_clause = ""
|
|
99
|
+
if select_for_update:
|
|
100
|
+
locking_clause = " FOR UPDATE"
|
|
101
|
+
if skip_locked:
|
|
102
|
+
locking_clause += " SKIP LOCKED"
|
|
103
|
+
return base + limit_clause + locking_clause
|
|
104
|
+
|
|
105
|
+
def _build_select_by_id_sql(self) -> str:
|
|
106
|
+
limit_clause = " FETCH FIRST 1 ROWS ONLY" if "oracle" in self._dialect else " LIMIT 1"
|
|
107
|
+
return (
|
|
108
|
+
f"SELECT event_id, channel, payload_json, metadata_json, attempts, available_at, lease_expires_at, created_at "
|
|
109
|
+
f"FROM {self._table_name} WHERE event_id = :event_id" + limit_clause
|
|
110
|
+
)
|
|
111
|
+
|
|
112
|
+
def _build_claim_sql(self) -> str:
|
|
113
|
+
return (
|
|
114
|
+
f"UPDATE {self._table_name} SET status = :claimed_status, lease_expires_at = :lease_expires_at, attempts = attempts + 1 "
|
|
115
|
+
"WHERE event_id = :event_id AND ("
|
|
116
|
+
"status = :pending_status OR (status = :leased_status AND (lease_expires_at IS NULL OR lease_expires_at <= :lease_reentry_cutoff))"
|
|
117
|
+
")"
|
|
118
|
+
)
|
|
119
|
+
|
|
120
|
+
def _build_ack_sql(self) -> str:
|
|
121
|
+
return f"UPDATE {self._table_name} SET status = :acked, acknowledged_at = :acked_at WHERE event_id = :event_id"
|
|
122
|
+
|
|
123
|
+
def _build_nack_sql(self) -> str:
|
|
124
|
+
return f"UPDATE {self._table_name} SET status = :pending, lease_expires_at = NULL, attempts = attempts + 1 WHERE event_id = :event_id"
|
|
125
|
+
|
|
126
|
+
def _build_cleanup_sql(self) -> str:
|
|
127
|
+
return f"DELETE FROM {self._table_name} WHERE status = :acked AND acknowledged_at IS NOT NULL AND acknowledged_at <= :cutoff"
|
|
128
|
+
|
|
129
|
+
@staticmethod
|
|
130
|
+
def _utcnow() -> "datetime":
|
|
131
|
+
return datetime.now(timezone.utc)
|
|
132
|
+
|
|
133
|
+
@staticmethod
|
|
134
|
+
def _hydrate_event(row: "dict[str, Any]", lease_expires_at: "datetime | None") -> EventMessage:
|
|
135
|
+
payload_raw = row.get("payload_json")
|
|
136
|
+
metadata_raw = row.get("metadata_json")
|
|
137
|
+
if isinstance(payload_raw, dict):
|
|
138
|
+
payload_obj = payload_raw
|
|
139
|
+
elif payload_raw is not None:
|
|
140
|
+
payload_obj = from_json(payload_raw)
|
|
141
|
+
else:
|
|
142
|
+
payload_obj = {}
|
|
143
|
+
if isinstance(metadata_raw, dict):
|
|
144
|
+
metadata_obj = metadata_raw
|
|
145
|
+
elif metadata_raw is not None:
|
|
146
|
+
metadata_obj = from_json(metadata_raw)
|
|
147
|
+
else:
|
|
148
|
+
metadata_obj = None
|
|
149
|
+
payload_value = payload_obj if isinstance(payload_obj, dict) else {"value": payload_obj}
|
|
150
|
+
metadata_value = (
|
|
151
|
+
metadata_obj if isinstance(metadata_obj, dict) or metadata_obj is None else {"value": metadata_obj}
|
|
152
|
+
)
|
|
153
|
+
available_at = parse_event_timestamp(row.get("available_at"))
|
|
154
|
+
created_at = parse_event_timestamp(row.get("created_at"))
|
|
155
|
+
lease_value = lease_expires_at or row.get("lease_expires_at")
|
|
156
|
+
lease_at = parse_event_timestamp(lease_value) if lease_value is not None else None
|
|
157
|
+
return EventMessage(
|
|
158
|
+
event_id=row["event_id"],
|
|
159
|
+
channel=row["channel"],
|
|
160
|
+
payload=payload_value,
|
|
161
|
+
metadata=metadata_value,
|
|
162
|
+
attempts=int(row.get("attempts", 0)),
|
|
163
|
+
available_at=available_at,
|
|
164
|
+
lease_expires_at=lease_at,
|
|
165
|
+
created_at=created_at,
|
|
166
|
+
)
|
|
167
|
+
|
|
168
|
+
|
|
169
|
+
class SyncTableEventQueue(_BaseTableEventQueue):
|
|
170
|
+
"""Sync table queue implementation."""
|
|
171
|
+
|
|
172
|
+
__slots__ = ()
|
|
173
|
+
|
|
174
|
+
supports_sync = True
|
|
175
|
+
supports_async = False
|
|
176
|
+
backend_name = "table_queue"
|
|
177
|
+
|
|
178
|
+
def publish(self, channel: str, payload: "dict[str, Any]", metadata: "dict[str, Any] | None" = None) -> str:
|
|
179
|
+
event_id = uuid4().hex
|
|
180
|
+
now = self._utcnow()
|
|
181
|
+
self._execute(
|
|
182
|
+
self._upsert_sql,
|
|
183
|
+
{
|
|
184
|
+
"event_id": event_id,
|
|
185
|
+
"channel": channel,
|
|
186
|
+
"payload_json": payload,
|
|
187
|
+
"metadata_json": metadata,
|
|
188
|
+
"status": _PENDING_STATUS,
|
|
189
|
+
"available_at": now,
|
|
190
|
+
"lease_expires_at": None,
|
|
191
|
+
"attempts": 0,
|
|
192
|
+
"created_at": now,
|
|
193
|
+
},
|
|
194
|
+
)
|
|
195
|
+
self._runtime.increment_metric("events.publish")
|
|
196
|
+
return event_id
|
|
197
|
+
|
|
198
|
+
def dequeue(self, channel: str, poll_interval: float | None = None) -> "EventMessage | None":
|
|
199
|
+
attempt = 0
|
|
200
|
+
while attempt < self._max_claim_attempts:
|
|
201
|
+
attempt += 1
|
|
202
|
+
row = self._fetch_candidate(channel)
|
|
203
|
+
if row is None:
|
|
204
|
+
if poll_interval is not None and poll_interval > 0:
|
|
205
|
+
time.sleep(poll_interval)
|
|
206
|
+
return None
|
|
207
|
+
now = self._utcnow()
|
|
208
|
+
leased_until = now + timedelta(seconds=self._lease_seconds)
|
|
209
|
+
claimed = self._execute(
|
|
210
|
+
self._claim_sql,
|
|
211
|
+
{
|
|
212
|
+
"claimed_status": _LEASED_STATUS,
|
|
213
|
+
"lease_expires_at": leased_until,
|
|
214
|
+
"event_id": row["event_id"],
|
|
215
|
+
"pending_status": _PENDING_STATUS,
|
|
216
|
+
"leased_status": _LEASED_STATUS,
|
|
217
|
+
"lease_reentry_cutoff": now,
|
|
218
|
+
},
|
|
219
|
+
)
|
|
220
|
+
if claimed:
|
|
221
|
+
return self._hydrate_event(row, leased_until)
|
|
222
|
+
return None
|
|
223
|
+
|
|
224
|
+
def dequeue_by_event_id(self, event_id: str) -> "EventMessage | None":
|
|
225
|
+
row = self._fetch_by_event_id(event_id)
|
|
226
|
+
if row is None:
|
|
227
|
+
return None
|
|
228
|
+
now = self._utcnow()
|
|
229
|
+
leased_until = now + timedelta(seconds=self._lease_seconds)
|
|
230
|
+
claimed = self._execute(
|
|
231
|
+
self._claim_sql,
|
|
232
|
+
{
|
|
233
|
+
"claimed_status": _LEASED_STATUS,
|
|
234
|
+
"lease_expires_at": leased_until,
|
|
235
|
+
"event_id": row["event_id"],
|
|
236
|
+
"pending_status": _PENDING_STATUS,
|
|
237
|
+
"leased_status": _LEASED_STATUS,
|
|
238
|
+
"lease_reentry_cutoff": now,
|
|
239
|
+
},
|
|
240
|
+
)
|
|
241
|
+
if claimed:
|
|
242
|
+
return self._hydrate_event(row, leased_until)
|
|
243
|
+
return None
|
|
244
|
+
|
|
245
|
+
def ack(self, event_id: str) -> None:
|
|
246
|
+
now = self._utcnow()
|
|
247
|
+
self._execute(self._ack_sql, {"acked": _ACKED_STATUS, "acked_at": now, "event_id": event_id})
|
|
248
|
+
self._cleanup(now)
|
|
249
|
+
self._runtime.increment_metric("events.ack")
|
|
250
|
+
|
|
251
|
+
def nack(self, event_id: str) -> None:
|
|
252
|
+
self._execute(self._nack_sql, {"pending": _PENDING_STATUS, "event_id": event_id})
|
|
253
|
+
self._runtime.increment_metric("events.nack")
|
|
254
|
+
|
|
255
|
+
def shutdown(self) -> None:
|
|
256
|
+
"""Shutdown the backend (no-op for table queue)."""
|
|
257
|
+
|
|
258
|
+
def _cleanup(self, reference: "datetime") -> None:
|
|
259
|
+
cutoff = reference - timedelta(seconds=self._retention_seconds)
|
|
260
|
+
self._execute(self._acked_cleanup_sql, {"acked": _ACKED_STATUS, "cutoff": cutoff})
|
|
261
|
+
|
|
262
|
+
def _fetch_candidate(self, channel: str) -> "dict[str, Any] | None":
|
|
263
|
+
current_time = self._utcnow()
|
|
264
|
+
with cast("AbstractContextManager[SyncDriverAdapterBase]", self._config.provide_session()) as driver:
|
|
265
|
+
return driver.select_one_or_none(
|
|
266
|
+
SQL(
|
|
267
|
+
self._select_sql,
|
|
268
|
+
{
|
|
269
|
+
"channel": channel,
|
|
270
|
+
"available_cutoff": current_time,
|
|
271
|
+
"pending_status": _PENDING_STATUS,
|
|
272
|
+
"leased_status": _LEASED_STATUS,
|
|
273
|
+
"lease_cutoff": current_time,
|
|
274
|
+
},
|
|
275
|
+
statement_config=self._statement_config,
|
|
276
|
+
)
|
|
277
|
+
)
|
|
278
|
+
|
|
279
|
+
def _fetch_by_event_id(self, event_id: str) -> "dict[str, Any] | None":
|
|
280
|
+
with cast("AbstractContextManager[SyncDriverAdapterBase]", self._config.provide_session()) as driver:
|
|
281
|
+
return driver.select_one_or_none(
|
|
282
|
+
SQL(self._select_by_id_sql, {"event_id": event_id}, statement_config=self._statement_config)
|
|
283
|
+
)
|
|
284
|
+
|
|
285
|
+
def _execute(self, sql: str, parameters: "dict[str, Any]") -> int:
|
|
286
|
+
with cast(
|
|
287
|
+
"AbstractContextManager[SyncDriverAdapterBase]", self._config.provide_session(transaction=True)
|
|
288
|
+
) as driver:
|
|
289
|
+
result = driver.execute(SQL(sql, parameters, statement_config=self._statement_config))
|
|
290
|
+
driver.commit()
|
|
291
|
+
return result.rows_affected
|
|
292
|
+
|
|
293
|
+
|
|
294
|
+
class AsyncTableEventQueue(_BaseTableEventQueue):
|
|
295
|
+
"""Async table queue implementation."""
|
|
296
|
+
|
|
297
|
+
__slots__ = ()
|
|
298
|
+
|
|
299
|
+
supports_sync = False
|
|
300
|
+
supports_async = True
|
|
301
|
+
backend_name = "table_queue"
|
|
302
|
+
|
|
303
|
+
async def publish(self, channel: str, payload: "dict[str, Any]", metadata: "dict[str, Any] | None" = None) -> str:
|
|
304
|
+
event_id = uuid4().hex
|
|
305
|
+
now = self._utcnow()
|
|
306
|
+
await self._execute(
|
|
307
|
+
self._upsert_sql,
|
|
308
|
+
{
|
|
309
|
+
"event_id": event_id,
|
|
310
|
+
"channel": channel,
|
|
311
|
+
"payload_json": payload,
|
|
312
|
+
"metadata_json": metadata,
|
|
313
|
+
"status": _PENDING_STATUS,
|
|
314
|
+
"available_at": now,
|
|
315
|
+
"lease_expires_at": None,
|
|
316
|
+
"attempts": 0,
|
|
317
|
+
"created_at": now,
|
|
318
|
+
},
|
|
319
|
+
)
|
|
320
|
+
self._runtime.increment_metric("events.publish")
|
|
321
|
+
return event_id
|
|
322
|
+
|
|
323
|
+
async def dequeue(self, channel: str, poll_interval: float | None = None) -> "EventMessage | None":
|
|
324
|
+
attempt = 0
|
|
325
|
+
while attempt < self._max_claim_attempts:
|
|
326
|
+
attempt += 1
|
|
327
|
+
row = await self._fetch_candidate(channel)
|
|
328
|
+
if row is None:
|
|
329
|
+
if poll_interval is not None and poll_interval > 0:
|
|
330
|
+
await asyncio.sleep(poll_interval)
|
|
331
|
+
return None
|
|
332
|
+
now = self._utcnow()
|
|
333
|
+
leased_until = now + timedelta(seconds=self._lease_seconds)
|
|
334
|
+
claimed = await self._execute(
|
|
335
|
+
self._claim_sql,
|
|
336
|
+
{
|
|
337
|
+
"claimed_status": _LEASED_STATUS,
|
|
338
|
+
"lease_expires_at": leased_until,
|
|
339
|
+
"event_id": row["event_id"],
|
|
340
|
+
"pending_status": _PENDING_STATUS,
|
|
341
|
+
"leased_status": _LEASED_STATUS,
|
|
342
|
+
"lease_reentry_cutoff": now,
|
|
343
|
+
},
|
|
344
|
+
)
|
|
345
|
+
if claimed:
|
|
346
|
+
return self._hydrate_event(row, leased_until)
|
|
347
|
+
return None
|
|
348
|
+
|
|
349
|
+
async def dequeue_by_event_id(self, event_id: str) -> "EventMessage | None":
|
|
350
|
+
row = await self._fetch_by_event_id(event_id)
|
|
351
|
+
if row is None:
|
|
352
|
+
return None
|
|
353
|
+
now = self._utcnow()
|
|
354
|
+
leased_until = now + timedelta(seconds=self._lease_seconds)
|
|
355
|
+
claimed = await self._execute(
|
|
356
|
+
self._claim_sql,
|
|
357
|
+
{
|
|
358
|
+
"claimed_status": _LEASED_STATUS,
|
|
359
|
+
"lease_expires_at": leased_until,
|
|
360
|
+
"event_id": row["event_id"],
|
|
361
|
+
"pending_status": _PENDING_STATUS,
|
|
362
|
+
"leased_status": _LEASED_STATUS,
|
|
363
|
+
"lease_reentry_cutoff": now,
|
|
364
|
+
},
|
|
365
|
+
)
|
|
366
|
+
if claimed:
|
|
367
|
+
return self._hydrate_event(row, leased_until)
|
|
368
|
+
return None
|
|
369
|
+
|
|
370
|
+
async def ack(self, event_id: str) -> None:
|
|
371
|
+
now = self._utcnow()
|
|
372
|
+
await self._execute(self._ack_sql, {"acked": _ACKED_STATUS, "acked_at": now, "event_id": event_id})
|
|
373
|
+
await self._cleanup(now)
|
|
374
|
+
self._runtime.increment_metric("events.ack")
|
|
375
|
+
|
|
376
|
+
async def nack(self, event_id: str) -> None:
|
|
377
|
+
await self._execute(self._nack_sql, {"pending": _PENDING_STATUS, "event_id": event_id})
|
|
378
|
+
self._runtime.increment_metric("events.nack")
|
|
379
|
+
|
|
380
|
+
async def shutdown(self) -> None:
|
|
381
|
+
"""Shutdown the backend (no-op for table queue)."""
|
|
382
|
+
|
|
383
|
+
async def _cleanup(self, reference: "datetime") -> None:
|
|
384
|
+
cutoff = reference - timedelta(seconds=self._retention_seconds)
|
|
385
|
+
await self._execute(self._acked_cleanup_sql, {"acked": _ACKED_STATUS, "cutoff": cutoff})
|
|
386
|
+
|
|
387
|
+
async def _fetch_candidate(self, channel: str) -> "dict[str, Any] | None":
|
|
388
|
+
current_time = self._utcnow()
|
|
389
|
+
async with cast(
|
|
390
|
+
"AbstractAsyncContextManager[AsyncDriverAdapterBase]", self._config.provide_session()
|
|
391
|
+
) as driver:
|
|
392
|
+
return await driver.select_one_or_none(
|
|
393
|
+
SQL(
|
|
394
|
+
self._select_sql,
|
|
395
|
+
{
|
|
396
|
+
"channel": channel,
|
|
397
|
+
"available_cutoff": current_time,
|
|
398
|
+
"pending_status": _PENDING_STATUS,
|
|
399
|
+
"leased_status": _LEASED_STATUS,
|
|
400
|
+
"lease_cutoff": current_time,
|
|
401
|
+
},
|
|
402
|
+
statement_config=self._statement_config,
|
|
403
|
+
)
|
|
404
|
+
)
|
|
405
|
+
|
|
406
|
+
async def _fetch_by_event_id(self, event_id: str) -> "dict[str, Any] | None":
|
|
407
|
+
async with cast(
|
|
408
|
+
"AbstractAsyncContextManager[AsyncDriverAdapterBase]", self._config.provide_session()
|
|
409
|
+
) as driver:
|
|
410
|
+
return await driver.select_one_or_none(
|
|
411
|
+
SQL(self._select_by_id_sql, {"event_id": event_id}, statement_config=self._statement_config)
|
|
412
|
+
)
|
|
413
|
+
|
|
414
|
+
async def _execute(self, sql: str, parameters: "dict[str, Any]") -> int:
|
|
415
|
+
async with cast(
|
|
416
|
+
"AbstractAsyncContextManager[AsyncDriverAdapterBase]", self._config.provide_session(transaction=True)
|
|
417
|
+
) as driver:
|
|
418
|
+
result = await driver.execute(SQL(sql, parameters, statement_config=self._statement_config))
|
|
419
|
+
await driver.commit()
|
|
420
|
+
return result.rows_affected
|
|
421
|
+
|
|
422
|
+
|
|
423
|
+
def build_queue_backend(
|
|
424
|
+
config: "DatabaseConfigProtocol[Any, Any, Any]",
|
|
425
|
+
extension_settings: "dict[str, Any] | None" = None,
|
|
426
|
+
*,
|
|
427
|
+
adapter_name: "str | None" = None,
|
|
428
|
+
hints: "EventRuntimeHints | None" = None,
|
|
429
|
+
) -> "SyncTableEventQueue | AsyncTableEventQueue":
|
|
430
|
+
"""Build a table queue backend using adapter hints and extension overrides."""
|
|
431
|
+
settings = dict(extension_settings or {})
|
|
432
|
+
resolved_adapter = adapter_name or resolve_adapter_name(config)
|
|
433
|
+
runtime_hints = hints or get_runtime_hints(resolved_adapter, config)
|
|
434
|
+
kwargs: dict[str, Any] = {
|
|
435
|
+
"queue_table": settings.get("queue_table"),
|
|
436
|
+
"lease_seconds": _resolve_int_setting(settings, "lease_seconds", runtime_hints.lease_seconds),
|
|
437
|
+
"retention_seconds": _resolve_int_setting(settings, "retention_seconds", runtime_hints.retention_seconds),
|
|
438
|
+
"select_for_update": _resolve_bool_setting(settings, "select_for_update", runtime_hints.select_for_update),
|
|
439
|
+
"skip_locked": _resolve_bool_setting(settings, "skip_locked", runtime_hints.skip_locked),
|
|
440
|
+
}
|
|
441
|
+
if config.is_async:
|
|
442
|
+
return AsyncTableEventQueue(config, **kwargs)
|
|
443
|
+
return SyncTableEventQueue(config, **kwargs)
|
|
444
|
+
|
|
445
|
+
|
|
446
|
+
def _resolve_bool_setting(settings: "dict[str, Any]", key: str, default: bool) -> bool:
|
|
447
|
+
if key not in settings:
|
|
448
|
+
return bool(default)
|
|
449
|
+
value = settings.get(key)
|
|
450
|
+
if value is None:
|
|
451
|
+
return bool(default)
|
|
452
|
+
return bool(value)
|
|
453
|
+
|
|
454
|
+
|
|
455
|
+
def _resolve_int_setting(settings: "dict[str, Any]", key: str, default: int) -> int:
|
|
456
|
+
if key not in settings:
|
|
457
|
+
return int(default)
|
|
458
|
+
value = settings.get(key)
|
|
459
|
+
if value is None:
|
|
460
|
+
return int(default)
|
|
461
|
+
return int(value)
|
|
@@ -0,0 +1,209 @@
|
|
|
1
|
+
"""Base classes for adapter-specific event queue stores."""
|
|
2
|
+
|
|
3
|
+
import re
|
|
4
|
+
from abc import ABC, abstractmethod
|
|
5
|
+
from typing import TYPE_CHECKING, Any, Generic, TypeVar, cast
|
|
6
|
+
|
|
7
|
+
from sqlspec.exceptions import EventChannelError
|
|
8
|
+
|
|
9
|
+
if TYPE_CHECKING:
|
|
10
|
+
from sqlspec.config import DatabaseConfigProtocol
|
|
11
|
+
|
|
12
|
+
ConfigT = TypeVar("ConfigT", bound="DatabaseConfigProtocol[Any, Any, Any]")
|
|
13
|
+
|
|
14
|
+
__all__ = ("BaseEventQueueStore", "normalize_event_channel_name", "normalize_queue_table_name")
|
|
15
|
+
|
|
16
|
+
_IDENTIFIER_PATTERN = re.compile(r"^[A-Za-z_][A-Za-z0-9_]*$")
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
def normalize_queue_table_name(name: str) -> str:
|
|
20
|
+
"""Validate schema-qualified identifiers and return normalized name."""
|
|
21
|
+
segments = name.split(".")
|
|
22
|
+
for segment in segments:
|
|
23
|
+
if not _IDENTIFIER_PATTERN.match(segment):
|
|
24
|
+
msg = f"Invalid events table name: {name}"
|
|
25
|
+
raise EventChannelError(msg)
|
|
26
|
+
return name
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
def normalize_event_channel_name(name: str) -> str:
|
|
30
|
+
"""Validate event channel identifiers and return normalized name."""
|
|
31
|
+
if not _IDENTIFIER_PATTERN.match(name):
|
|
32
|
+
msg = f"Invalid events channel name: {name}"
|
|
33
|
+
raise EventChannelError(msg)
|
|
34
|
+
return name
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
class BaseEventQueueStore(ABC, Generic[ConfigT]):
|
|
38
|
+
"""Base class for adapter-specific event queue DDL generators.
|
|
39
|
+
|
|
40
|
+
This class provides a hook-based pattern for DDL generation. Adapters only
|
|
41
|
+
need to override `_column_types()` and optionally any hook methods for
|
|
42
|
+
dialect-specific variations:
|
|
43
|
+
|
|
44
|
+
- `_string_type(length)`: String type syntax (default: VARCHAR(N))
|
|
45
|
+
- `_integer_type()`: Integer type syntax (default: INTEGER)
|
|
46
|
+
- `_timestamp_default()`: Timestamp default expression (default: CURRENT_TIMESTAMP)
|
|
47
|
+
- `_primary_key_syntax()`: Inline PRIMARY KEY clause (default: empty, PK on column)
|
|
48
|
+
- `_table_clause()`: Additional table options (default: empty)
|
|
49
|
+
|
|
50
|
+
For complex dialects (Oracle PL/SQL, BigQuery CLUSTER BY), adapters may
|
|
51
|
+
override `_build_create_table_sql()` directly.
|
|
52
|
+
"""
|
|
53
|
+
|
|
54
|
+
__slots__ = ("_config", "_extension_settings", "_table_name")
|
|
55
|
+
|
|
56
|
+
def __init__(self, config: ConfigT) -> None:
|
|
57
|
+
self._config = config
|
|
58
|
+
extension_config = cast("dict[str, Any]", config.extension_config)
|
|
59
|
+
self._extension_settings = cast("dict[str, Any]", extension_config.get("events", {}))
|
|
60
|
+
table_name = self._extension_settings.get("queue_table", "sqlspec_event_queue")
|
|
61
|
+
self._table_name = normalize_queue_table_name(str(table_name))
|
|
62
|
+
|
|
63
|
+
@property
|
|
64
|
+
def table_name(self) -> str:
|
|
65
|
+
"""Return the configured queue table name."""
|
|
66
|
+
return self._table_name
|
|
67
|
+
|
|
68
|
+
@property
|
|
69
|
+
def settings(self) -> "dict[str, Any]":
|
|
70
|
+
"""Return extension settings for adapters to inspect."""
|
|
71
|
+
return self._extension_settings
|
|
72
|
+
|
|
73
|
+
def create_statements(self) -> "list[str]":
|
|
74
|
+
"""Return statements required to create the queue table and indexes."""
|
|
75
|
+
statements = [self._wrap_create_statement(self._build_create_table_sql(), "table")]
|
|
76
|
+
index_statement = self._build_index_sql()
|
|
77
|
+
if index_statement:
|
|
78
|
+
statements.append(self._wrap_create_statement(index_statement, "index"))
|
|
79
|
+
return statements
|
|
80
|
+
|
|
81
|
+
def drop_statements(self) -> "list[str]":
|
|
82
|
+
"""Return statements required to drop queue artifacts."""
|
|
83
|
+
return [self._wrap_drop_statement(f"DROP TABLE {self.table_name}")]
|
|
84
|
+
|
|
85
|
+
def _string_type(self, length: int) -> str:
|
|
86
|
+
"""Return string type syntax for the given length.
|
|
87
|
+
|
|
88
|
+
Override for dialects with different string type syntax.
|
|
89
|
+
|
|
90
|
+
Args:
|
|
91
|
+
length: Maximum string length.
|
|
92
|
+
|
|
93
|
+
Returns:
|
|
94
|
+
String type declaration (e.g., VARCHAR(64), STRING(64)).
|
|
95
|
+
"""
|
|
96
|
+
return f"VARCHAR({length})"
|
|
97
|
+
|
|
98
|
+
def _integer_type(self) -> str:
|
|
99
|
+
"""Return integer type syntax.
|
|
100
|
+
|
|
101
|
+
Override for dialects with different integer type syntax.
|
|
102
|
+
|
|
103
|
+
Returns:
|
|
104
|
+
Integer type declaration (e.g., INTEGER, INT64).
|
|
105
|
+
"""
|
|
106
|
+
return "INTEGER"
|
|
107
|
+
|
|
108
|
+
def _timestamp_default(self) -> str:
|
|
109
|
+
"""Return timestamp default expression.
|
|
110
|
+
|
|
111
|
+
Override for dialects requiring different default syntax.
|
|
112
|
+
|
|
113
|
+
Returns:
|
|
114
|
+
Default timestamp expression (e.g., CURRENT_TIMESTAMP, CURRENT_TIMESTAMP(6)).
|
|
115
|
+
"""
|
|
116
|
+
return "CURRENT_TIMESTAMP"
|
|
117
|
+
|
|
118
|
+
def _primary_key_syntax(self) -> str:
|
|
119
|
+
"""Return inline PRIMARY KEY clause for table definition.
|
|
120
|
+
|
|
121
|
+
Override for dialects that require PRIMARY KEY at the end of CREATE TABLE
|
|
122
|
+
instead of on the column definition (e.g., Spanner).
|
|
123
|
+
|
|
124
|
+
Returns:
|
|
125
|
+
Empty string for column-level PK, or " PRIMARY KEY (event_id)" for table-level.
|
|
126
|
+
"""
|
|
127
|
+
return ""
|
|
128
|
+
|
|
129
|
+
def _build_create_table_sql(self) -> str:
|
|
130
|
+
"""Build CREATE TABLE SQL using hook methods.
|
|
131
|
+
|
|
132
|
+
Most adapters should NOT override this method. Instead, override the
|
|
133
|
+
hook methods (_string_type, _integer_type, _timestamp_default, etc.)
|
|
134
|
+
for dialect-specific variations.
|
|
135
|
+
|
|
136
|
+
Only override this method for complex dialects that require entirely
|
|
137
|
+
different DDL structure (e.g., Oracle PL/SQL blocks, BigQuery CLUSTER BY).
|
|
138
|
+
"""
|
|
139
|
+
payload_type, metadata_type, timestamp_type = self._column_types()
|
|
140
|
+
string_64 = self._string_type(64)
|
|
141
|
+
string_128 = self._string_type(128)
|
|
142
|
+
string_32 = self._string_type(32)
|
|
143
|
+
integer_type = self._integer_type()
|
|
144
|
+
ts_default = self._timestamp_default()
|
|
145
|
+
pk_inline = self._primary_key_syntax()
|
|
146
|
+
table_clause = self._table_clause()
|
|
147
|
+
|
|
148
|
+
pk_column = " PRIMARY KEY" if not pk_inline else ""
|
|
149
|
+
|
|
150
|
+
return (
|
|
151
|
+
f"CREATE TABLE {self.table_name} ("
|
|
152
|
+
f"event_id {string_64}{pk_column},"
|
|
153
|
+
f" channel {string_128} NOT NULL,"
|
|
154
|
+
f" payload_json {payload_type} NOT NULL,"
|
|
155
|
+
f" metadata_json {metadata_type},"
|
|
156
|
+
f" status {string_32} NOT NULL DEFAULT 'pending',"
|
|
157
|
+
f" available_at {timestamp_type} NOT NULL DEFAULT {ts_default},"
|
|
158
|
+
f" lease_expires_at {timestamp_type},"
|
|
159
|
+
f" attempts {integer_type} NOT NULL DEFAULT 0,"
|
|
160
|
+
f" created_at {timestamp_type} NOT NULL DEFAULT {ts_default},"
|
|
161
|
+
f" acknowledged_at {timestamp_type}"
|
|
162
|
+
f"){pk_inline}{table_clause}"
|
|
163
|
+
)
|
|
164
|
+
|
|
165
|
+
def _build_index_sql(self) -> str | None:
|
|
166
|
+
"""Build CREATE INDEX SQL for queue operations."""
|
|
167
|
+
index_name = self._index_name()
|
|
168
|
+
return f"CREATE INDEX {index_name} ON {self.table_name}(channel, status, available_at)"
|
|
169
|
+
|
|
170
|
+
def _table_clause(self) -> str:
|
|
171
|
+
"""Return additional table options clause.
|
|
172
|
+
|
|
173
|
+
Override for dialects that need options after the column definitions
|
|
174
|
+
(e.g., BigQuery CLUSTER BY, Oracle INMEMORY).
|
|
175
|
+
"""
|
|
176
|
+
return ""
|
|
177
|
+
|
|
178
|
+
def _index_name(self) -> str:
|
|
179
|
+
"""Return the index name for the queue table."""
|
|
180
|
+
return f"idx_{self.table_name.replace('.', '_')}_channel_status"
|
|
181
|
+
|
|
182
|
+
def _wrap_create_statement(self, statement: str, object_type: str) -> str:
|
|
183
|
+
"""Wrap CREATE statement with IF NOT EXISTS.
|
|
184
|
+
|
|
185
|
+
Override for dialects that don't support IF NOT EXISTS (e.g., Spanner).
|
|
186
|
+
"""
|
|
187
|
+
if object_type == "table":
|
|
188
|
+
return statement.replace("CREATE TABLE", "CREATE TABLE IF NOT EXISTS", 1)
|
|
189
|
+
if object_type == "index":
|
|
190
|
+
return statement.replace("CREATE INDEX", "CREATE INDEX IF NOT EXISTS", 1)
|
|
191
|
+
return statement
|
|
192
|
+
|
|
193
|
+
def _wrap_drop_statement(self, statement: str) -> str:
|
|
194
|
+
"""Wrap DROP statement with IF EXISTS.
|
|
195
|
+
|
|
196
|
+
Override for dialects that don't support IF EXISTS (e.g., Spanner).
|
|
197
|
+
"""
|
|
198
|
+
return statement.replace("DROP TABLE", "DROP TABLE IF EXISTS", 1)
|
|
199
|
+
|
|
200
|
+
@abstractmethod
|
|
201
|
+
def _column_types(self) -> "tuple[str, str, str]":
|
|
202
|
+
"""Return payload, metadata, and timestamp column types for the adapter.
|
|
203
|
+
|
|
204
|
+
Args:
|
|
205
|
+
None
|
|
206
|
+
|
|
207
|
+
Returns:
|
|
208
|
+
Tuple of (payload_type, metadata_type, timestamp_type).
|
|
209
|
+
"""
|