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
sqlspec/core/splitter.py
ADDED
|
@@ -0,0 +1,966 @@
|
|
|
1
|
+
"""SQL statement splitter with caching and dialect support.
|
|
2
|
+
|
|
3
|
+
This module provides SQL script statement splitting functionality
|
|
4
|
+
with support for multiple SQL dialects and caching for performance.
|
|
5
|
+
|
|
6
|
+
Components:
|
|
7
|
+
StatementSplitter: Main SQL script splitter with caching
|
|
8
|
+
DialectConfig: Base class for dialect-specific configurations
|
|
9
|
+
Token/TokenType: Token representation and classification
|
|
10
|
+
Caching: Pattern and result caching for performance
|
|
11
|
+
|
|
12
|
+
Supported dialects include Oracle PL/SQL, T-SQL, PostgreSQL,
|
|
13
|
+
MySQL, SQLite, DuckDB, and BigQuery.
|
|
14
|
+
"""
|
|
15
|
+
|
|
16
|
+
import logging
|
|
17
|
+
import re
|
|
18
|
+
import threading
|
|
19
|
+
from abc import ABC, abstractmethod
|
|
20
|
+
from collections.abc import Callable, Generator
|
|
21
|
+
from enum import Enum
|
|
22
|
+
from re import Pattern
|
|
23
|
+
from typing import Any, Final, TypeAlias, cast
|
|
24
|
+
|
|
25
|
+
from mypy_extensions import mypyc_attr
|
|
26
|
+
|
|
27
|
+
from sqlspec.core.cache import CacheKey, LRUCache
|
|
28
|
+
from sqlspec.utils.logging import get_logger
|
|
29
|
+
|
|
30
|
+
__all__ = (
|
|
31
|
+
"DialectConfig",
|
|
32
|
+
"OracleDialectConfig",
|
|
33
|
+
"PostgreSQLDialectConfig",
|
|
34
|
+
"StatementSplitter",
|
|
35
|
+
"TSQLDialectConfig",
|
|
36
|
+
"Token",
|
|
37
|
+
"TokenType",
|
|
38
|
+
"split_sql_script",
|
|
39
|
+
)
|
|
40
|
+
|
|
41
|
+
logger = get_logger("sqlspec.core.splitter")
|
|
42
|
+
|
|
43
|
+
_TOKENIZE_DEBUG_SAMPLE_LIMIT: Final[int] = 3
|
|
44
|
+
_TOKENIZE_SNIPPET_LENGTH: Final[int] = 20
|
|
45
|
+
|
|
46
|
+
DEFAULT_PATTERN_CACHE_SIZE: Final = 1000
|
|
47
|
+
DEFAULT_RESULT_CACHE_SIZE: Final = 5000
|
|
48
|
+
DEFAULT_CACHE_TTL: Final = 3600
|
|
49
|
+
|
|
50
|
+
DIALECT_CONFIG_SLOTS: Final = (
|
|
51
|
+
"_block_starters",
|
|
52
|
+
"_block_enders",
|
|
53
|
+
"_statement_terminators",
|
|
54
|
+
"_batch_separators",
|
|
55
|
+
"_special_terminators",
|
|
56
|
+
"_max_nesting_depth",
|
|
57
|
+
"_name",
|
|
58
|
+
)
|
|
59
|
+
|
|
60
|
+
TOKEN_SLOTS: Final = ("type", "value", "line", "column", "position")
|
|
61
|
+
|
|
62
|
+
SPLITTER_SLOTS: Final = (
|
|
63
|
+
"_dialect",
|
|
64
|
+
"_strip_trailing_semicolon",
|
|
65
|
+
"_token_patterns",
|
|
66
|
+
"_compiled_patterns",
|
|
67
|
+
"_pattern_cache_key",
|
|
68
|
+
"_result_cache",
|
|
69
|
+
"_pattern_cache",
|
|
70
|
+
)
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
def _special_terminator_true(_tokens: "list[Token]", _pos: int) -> bool:
|
|
74
|
+
return True
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
class TokenType(Enum):
|
|
78
|
+
"""Types of tokens recognized by the SQL lexer."""
|
|
79
|
+
|
|
80
|
+
COMMENT_LINE = "COMMENT_LINE"
|
|
81
|
+
COMMENT_BLOCK = "COMMENT_BLOCK"
|
|
82
|
+
STRING_LITERAL = "STRING_LITERAL"
|
|
83
|
+
QUOTED_IDENTIFIER = "QUOTED_IDENTIFIER"
|
|
84
|
+
KEYWORD = "KEYWORD"
|
|
85
|
+
TERMINATOR = "TERMINATOR"
|
|
86
|
+
BATCH_SEPARATOR = "BATCH_SEPARATOR"
|
|
87
|
+
WHITESPACE = "WHITESPACE"
|
|
88
|
+
OTHER = "OTHER"
|
|
89
|
+
|
|
90
|
+
|
|
91
|
+
@mypyc_attr(allow_interpreted_subclasses=False)
|
|
92
|
+
class Token:
|
|
93
|
+
"""SQL token with metadata."""
|
|
94
|
+
|
|
95
|
+
__slots__ = TOKEN_SLOTS
|
|
96
|
+
|
|
97
|
+
def __init__(self, type: TokenType, value: str, line: int, column: int, position: int) -> None:
|
|
98
|
+
self.type = type
|
|
99
|
+
self.value = value
|
|
100
|
+
self.line = line
|
|
101
|
+
self.column = column
|
|
102
|
+
self.position = position
|
|
103
|
+
|
|
104
|
+
def __repr__(self) -> str:
|
|
105
|
+
return f"Token({self.type.value}, {self.value!r}, {self.line}:{self.column})"
|
|
106
|
+
|
|
107
|
+
|
|
108
|
+
TokenHandler: TypeAlias = Callable[[str, int, int, int], Token | None]
|
|
109
|
+
TokenPattern: TypeAlias = str | TokenHandler
|
|
110
|
+
CompiledTokenPattern: TypeAlias = Pattern[str] | TokenHandler
|
|
111
|
+
|
|
112
|
+
|
|
113
|
+
@mypyc_attr(allow_interpreted_subclasses=False)
|
|
114
|
+
class DialectConfig(ABC):
|
|
115
|
+
"""Abstract base class for SQL dialect configurations."""
|
|
116
|
+
|
|
117
|
+
__slots__ = DIALECT_CONFIG_SLOTS
|
|
118
|
+
|
|
119
|
+
def __init__(self) -> None:
|
|
120
|
+
"""Initialize dialect configuration."""
|
|
121
|
+
self._name: str | None = None
|
|
122
|
+
self._block_starters: set[str] | None = None
|
|
123
|
+
self._block_enders: set[str] | None = None
|
|
124
|
+
self._statement_terminators: set[str] | None = None
|
|
125
|
+
self._batch_separators: set[str] | None = None
|
|
126
|
+
self._special_terminators: dict[str, Callable[[list[Token], int], bool]] | None = None
|
|
127
|
+
self._max_nesting_depth: int | None = None
|
|
128
|
+
|
|
129
|
+
@property
|
|
130
|
+
@abstractmethod
|
|
131
|
+
def name(self) -> str:
|
|
132
|
+
"""Name of the dialect (e.g., 'oracle', 'tsql')."""
|
|
133
|
+
|
|
134
|
+
@property
|
|
135
|
+
@abstractmethod
|
|
136
|
+
def block_starters(self) -> "set[str]":
|
|
137
|
+
"""Keywords that start a block (e.g., BEGIN, DECLARE)."""
|
|
138
|
+
|
|
139
|
+
@property
|
|
140
|
+
@abstractmethod
|
|
141
|
+
def block_enders(self) -> "set[str]":
|
|
142
|
+
"""Keywords that end a block (e.g., END)."""
|
|
143
|
+
|
|
144
|
+
@property
|
|
145
|
+
@abstractmethod
|
|
146
|
+
def statement_terminators(self) -> "set[str]":
|
|
147
|
+
"""Characters that terminate statements (e.g., ;)."""
|
|
148
|
+
|
|
149
|
+
@property
|
|
150
|
+
def batch_separators(self) -> "set[str]":
|
|
151
|
+
"""Keywords that separate batches (e.g., GO for T-SQL)."""
|
|
152
|
+
if self._batch_separators is None:
|
|
153
|
+
self._batch_separators = set()
|
|
154
|
+
return self._batch_separators
|
|
155
|
+
|
|
156
|
+
@property
|
|
157
|
+
def special_terminators(self) -> "dict[str, Callable[[list[Token], int], bool]]":
|
|
158
|
+
"""Special terminators that need custom handling."""
|
|
159
|
+
if self._special_terminators is None:
|
|
160
|
+
self._special_terminators = {}
|
|
161
|
+
return self._special_terminators
|
|
162
|
+
|
|
163
|
+
@property
|
|
164
|
+
def max_nesting_depth(self) -> int:
|
|
165
|
+
"""Maximum allowed nesting depth for blocks."""
|
|
166
|
+
if self._max_nesting_depth is None:
|
|
167
|
+
self._max_nesting_depth = 256
|
|
168
|
+
return self._max_nesting_depth
|
|
169
|
+
|
|
170
|
+
def get_all_token_patterns(self) -> "list[tuple[TokenType, TokenPattern]]":
|
|
171
|
+
"""Get the complete ordered list of token patterns for this dialect.
|
|
172
|
+
|
|
173
|
+
Returns:
|
|
174
|
+
List of tuples containing token types and their regex patterns
|
|
175
|
+
"""
|
|
176
|
+
patterns: list[tuple[TokenType, TokenPattern]] = [
|
|
177
|
+
(TokenType.COMMENT_LINE, r"--[^\n]*"),
|
|
178
|
+
(TokenType.COMMENT_BLOCK, r"/\*[\s\S]*?\*/"),
|
|
179
|
+
(TokenType.STRING_LITERAL, r"'(?:[^']|'')*'"),
|
|
180
|
+
(TokenType.QUOTED_IDENTIFIER, r'"[^"]*"|\[[^\]]*\]'),
|
|
181
|
+
]
|
|
182
|
+
|
|
183
|
+
patterns.extend(self._get_dialect_specific_patterns())
|
|
184
|
+
|
|
185
|
+
all_keywords = self.block_starters | self.block_enders | self.batch_separators
|
|
186
|
+
if all_keywords:
|
|
187
|
+
sorted_keywords = sorted(all_keywords, key=len, reverse=True)
|
|
188
|
+
patterns.append((TokenType.KEYWORD, r"\b(" + "|".join(re.escape(kw) for kw in sorted_keywords) + r")\b"))
|
|
189
|
+
|
|
190
|
+
all_terminators = self.statement_terminators | set(self.special_terminators.keys())
|
|
191
|
+
if all_terminators:
|
|
192
|
+
patterns.append((TokenType.TERMINATOR, "|".join(re.escape(t) for t in all_terminators)))
|
|
193
|
+
|
|
194
|
+
patterns.extend([(TokenType.WHITESPACE, r"\s+"), (TokenType.OTHER, r".")])
|
|
195
|
+
|
|
196
|
+
return patterns
|
|
197
|
+
|
|
198
|
+
def _get_dialect_specific_patterns(self) -> "list[tuple[TokenType, TokenPattern]]":
|
|
199
|
+
"""Get dialect-specific token patterns.
|
|
200
|
+
|
|
201
|
+
Returns:
|
|
202
|
+
List of dialect-specific token patterns
|
|
203
|
+
"""
|
|
204
|
+
return []
|
|
205
|
+
|
|
206
|
+
@staticmethod
|
|
207
|
+
def is_real_block_ender(tokens: "list[Token]", current_pos: int) -> bool: # noqa: ARG004
|
|
208
|
+
"""Check if END keyword represents an actual block terminator.
|
|
209
|
+
|
|
210
|
+
Args:
|
|
211
|
+
tokens: List of all tokens
|
|
212
|
+
current_pos: Position of END token
|
|
213
|
+
|
|
214
|
+
Returns:
|
|
215
|
+
True if END represents a block terminator, False otherwise
|
|
216
|
+
"""
|
|
217
|
+
return True
|
|
218
|
+
|
|
219
|
+
def should_delay_semicolon_termination(self, tokens: "list[Token]", current_pos: int) -> bool:
|
|
220
|
+
"""Check if semicolon termination should be delayed.
|
|
221
|
+
|
|
222
|
+
Args:
|
|
223
|
+
tokens: List of all tokens
|
|
224
|
+
current_pos: Current position in token list
|
|
225
|
+
|
|
226
|
+
Returns:
|
|
227
|
+
True if termination should be delayed, False otherwise
|
|
228
|
+
"""
|
|
229
|
+
return False
|
|
230
|
+
|
|
231
|
+
|
|
232
|
+
class OracleDialectConfig(DialectConfig):
|
|
233
|
+
"""Configuration for Oracle PL/SQL dialect."""
|
|
234
|
+
|
|
235
|
+
@property
|
|
236
|
+
def name(self) -> str:
|
|
237
|
+
if self._name is None:
|
|
238
|
+
self._name = "oracle"
|
|
239
|
+
return self._name
|
|
240
|
+
|
|
241
|
+
@property
|
|
242
|
+
def block_starters(self) -> "set[str]":
|
|
243
|
+
if self._block_starters is None:
|
|
244
|
+
self._block_starters = {"BEGIN", "DECLARE", "CASE"}
|
|
245
|
+
return self._block_starters
|
|
246
|
+
|
|
247
|
+
@property
|
|
248
|
+
def block_enders(self) -> "set[str]":
|
|
249
|
+
if self._block_enders is None:
|
|
250
|
+
self._block_enders = {"END"}
|
|
251
|
+
return self._block_enders
|
|
252
|
+
|
|
253
|
+
@property
|
|
254
|
+
def statement_terminators(self) -> "set[str]":
|
|
255
|
+
if self._statement_terminators is None:
|
|
256
|
+
self._statement_terminators = {";"}
|
|
257
|
+
return self._statement_terminators
|
|
258
|
+
|
|
259
|
+
@property
|
|
260
|
+
def special_terminators(self) -> "dict[str, Callable[[list[Token], int], bool]]":
|
|
261
|
+
if self._special_terminators is None:
|
|
262
|
+
self._special_terminators = {"/": self._handle_slash_terminator}
|
|
263
|
+
return self._special_terminators
|
|
264
|
+
|
|
265
|
+
def should_delay_semicolon_termination(self, tokens: "list[Token]", current_pos: int) -> bool:
|
|
266
|
+
"""Check if semicolon termination should be delayed for Oracle slash terminators.
|
|
267
|
+
|
|
268
|
+
Args:
|
|
269
|
+
tokens: List of all tokens
|
|
270
|
+
current_pos: Current position in token list
|
|
271
|
+
|
|
272
|
+
Returns:
|
|
273
|
+
True if termination should be delayed, False otherwise
|
|
274
|
+
"""
|
|
275
|
+
pos = current_pos - 1
|
|
276
|
+
while pos >= 0:
|
|
277
|
+
token = tokens[pos]
|
|
278
|
+
if token.type == TokenType.WHITESPACE:
|
|
279
|
+
pos -= 1
|
|
280
|
+
continue
|
|
281
|
+
if token.type == TokenType.KEYWORD and token.value.upper() == "END":
|
|
282
|
+
return self._has_upcoming_slash(tokens, current_pos)
|
|
283
|
+
break
|
|
284
|
+
|
|
285
|
+
return False
|
|
286
|
+
|
|
287
|
+
def _has_upcoming_slash(self, tokens: "list[Token]", current_pos: int) -> bool:
|
|
288
|
+
"""Check if there's a slash terminator on its own line ahead.
|
|
289
|
+
|
|
290
|
+
Args:
|
|
291
|
+
tokens: List of all tokens
|
|
292
|
+
current_pos: Current position in token list
|
|
293
|
+
|
|
294
|
+
Returns:
|
|
295
|
+
True if slash terminator found on its own line, False otherwise
|
|
296
|
+
"""
|
|
297
|
+
pos = current_pos + 1
|
|
298
|
+
found_newline = False
|
|
299
|
+
|
|
300
|
+
while pos < len(tokens):
|
|
301
|
+
token = tokens[pos]
|
|
302
|
+
if token.type == TokenType.WHITESPACE:
|
|
303
|
+
if "\n" in token.value:
|
|
304
|
+
found_newline = True
|
|
305
|
+
pos += 1
|
|
306
|
+
continue
|
|
307
|
+
if token.type == TokenType.TERMINATOR and token.value == "/":
|
|
308
|
+
return found_newline and self._handle_slash_terminator(tokens, pos)
|
|
309
|
+
if token.type in {TokenType.COMMENT_LINE, TokenType.COMMENT_BLOCK}:
|
|
310
|
+
pos += 1
|
|
311
|
+
continue
|
|
312
|
+
break
|
|
313
|
+
|
|
314
|
+
return False
|
|
315
|
+
|
|
316
|
+
@staticmethod
|
|
317
|
+
def is_real_block_ender(tokens: "list[Token]", current_pos: int) -> bool:
|
|
318
|
+
"""Check if END keyword represents a block terminator in Oracle PL/SQL.
|
|
319
|
+
|
|
320
|
+
Args:
|
|
321
|
+
tokens: List of all tokens
|
|
322
|
+
current_pos: Position of END token
|
|
323
|
+
|
|
324
|
+
Returns:
|
|
325
|
+
True if END represents a block terminator, False otherwise
|
|
326
|
+
"""
|
|
327
|
+
pos = current_pos + 1
|
|
328
|
+
while pos < len(tokens):
|
|
329
|
+
next_token = tokens[pos]
|
|
330
|
+
|
|
331
|
+
if next_token.type == TokenType.WHITESPACE:
|
|
332
|
+
pos += 1
|
|
333
|
+
continue
|
|
334
|
+
if next_token.type == TokenType.OTHER:
|
|
335
|
+
word_chars = []
|
|
336
|
+
word_pos = pos
|
|
337
|
+
while word_pos < len(tokens) and tokens[word_pos].type == TokenType.OTHER:
|
|
338
|
+
word_chars.append(tokens[word_pos].value)
|
|
339
|
+
word_pos += 1
|
|
340
|
+
|
|
341
|
+
word = "".join(word_chars).upper()
|
|
342
|
+
if word in {"IF", "LOOP", "CASE", "WHILE"}:
|
|
343
|
+
return False
|
|
344
|
+
break
|
|
345
|
+
return True
|
|
346
|
+
|
|
347
|
+
@staticmethod
|
|
348
|
+
def _handle_slash_terminator(tokens: "list[Token]", current_pos: int) -> bool:
|
|
349
|
+
"""Check if Oracle slash terminator is properly positioned.
|
|
350
|
+
|
|
351
|
+
Oracle slash terminators must be on their own line with only
|
|
352
|
+
whitespace or comments preceding them on the same line.
|
|
353
|
+
|
|
354
|
+
Args:
|
|
355
|
+
tokens: List of all tokens
|
|
356
|
+
current_pos: Position of slash token
|
|
357
|
+
|
|
358
|
+
Returns:
|
|
359
|
+
True if slash is properly positioned, False otherwise
|
|
360
|
+
"""
|
|
361
|
+
if current_pos == 0:
|
|
362
|
+
return True
|
|
363
|
+
|
|
364
|
+
pos = current_pos - 1
|
|
365
|
+
while pos >= 0:
|
|
366
|
+
token = tokens[pos]
|
|
367
|
+
if "\n" in token.value:
|
|
368
|
+
break
|
|
369
|
+
if token.type not in {TokenType.WHITESPACE, TokenType.COMMENT_LINE}:
|
|
370
|
+
return False
|
|
371
|
+
pos -= 1
|
|
372
|
+
|
|
373
|
+
return True
|
|
374
|
+
|
|
375
|
+
|
|
376
|
+
class TSQLDialectConfig(DialectConfig):
|
|
377
|
+
"""Configuration for T-SQL (SQL Server) dialect."""
|
|
378
|
+
|
|
379
|
+
@property
|
|
380
|
+
def name(self) -> str:
|
|
381
|
+
if self._name is None:
|
|
382
|
+
self._name = "tsql"
|
|
383
|
+
return self._name
|
|
384
|
+
|
|
385
|
+
@property
|
|
386
|
+
def block_starters(self) -> "set[str]":
|
|
387
|
+
if self._block_starters is None:
|
|
388
|
+
self._block_starters = {"BEGIN", "TRY"}
|
|
389
|
+
return self._block_starters
|
|
390
|
+
|
|
391
|
+
@property
|
|
392
|
+
def block_enders(self) -> "set[str]":
|
|
393
|
+
if self._block_enders is None:
|
|
394
|
+
self._block_enders = {"END", "CATCH"}
|
|
395
|
+
return self._block_enders
|
|
396
|
+
|
|
397
|
+
@property
|
|
398
|
+
def statement_terminators(self) -> "set[str]":
|
|
399
|
+
if self._statement_terminators is None:
|
|
400
|
+
self._statement_terminators = {";"}
|
|
401
|
+
return self._statement_terminators
|
|
402
|
+
|
|
403
|
+
@property
|
|
404
|
+
def batch_separators(self) -> "set[str]":
|
|
405
|
+
if self._batch_separators is None:
|
|
406
|
+
self._batch_separators = {"GO"}
|
|
407
|
+
return self._batch_separators
|
|
408
|
+
|
|
409
|
+
|
|
410
|
+
class PostgreSQLDialectConfig(DialectConfig):
|
|
411
|
+
"""Configuration for PostgreSQL dialect with dollar-quoted strings."""
|
|
412
|
+
|
|
413
|
+
@property
|
|
414
|
+
def name(self) -> str:
|
|
415
|
+
if self._name is None:
|
|
416
|
+
self._name = "postgresql"
|
|
417
|
+
return self._name
|
|
418
|
+
|
|
419
|
+
@property
|
|
420
|
+
def block_starters(self) -> "set[str]":
|
|
421
|
+
if self._block_starters is None:
|
|
422
|
+
self._block_starters = {"DECLARE", "CASE", "DO"}
|
|
423
|
+
return self._block_starters
|
|
424
|
+
|
|
425
|
+
@property
|
|
426
|
+
def block_enders(self) -> "set[str]":
|
|
427
|
+
if self._block_enders is None:
|
|
428
|
+
self._block_enders = {"END"}
|
|
429
|
+
return self._block_enders
|
|
430
|
+
|
|
431
|
+
@property
|
|
432
|
+
def statement_terminators(self) -> "set[str]":
|
|
433
|
+
if self._statement_terminators is None:
|
|
434
|
+
self._statement_terminators = {";"}
|
|
435
|
+
return self._statement_terminators
|
|
436
|
+
|
|
437
|
+
def _get_dialect_specific_patterns(self) -> "list[tuple[TokenType, TokenPattern]]":
|
|
438
|
+
"""Get PostgreSQL-specific token patterns.
|
|
439
|
+
|
|
440
|
+
Returns:
|
|
441
|
+
List of dialect-specific token patterns
|
|
442
|
+
"""
|
|
443
|
+
return [(TokenType.STRING_LITERAL, self._handle_dollar_quoted_string)]
|
|
444
|
+
|
|
445
|
+
@staticmethod
|
|
446
|
+
def _handle_dollar_quoted_string(text: str, position: int, line: int, column: int) -> Token | None:
|
|
447
|
+
"""Handle PostgreSQL dollar-quoted string literals.
|
|
448
|
+
|
|
449
|
+
Parses dollar-quoted strings in the format $tag$content$tag$
|
|
450
|
+
where tag is optional.
|
|
451
|
+
|
|
452
|
+
Args:
|
|
453
|
+
text: The full SQL text being tokenized
|
|
454
|
+
position: Current position in the text
|
|
455
|
+
line: Current line number
|
|
456
|
+
column: Current column number
|
|
457
|
+
|
|
458
|
+
Returns:
|
|
459
|
+
Token representing the dollar-quoted string, or None if no match
|
|
460
|
+
"""
|
|
461
|
+
start_match = re.match(r"\$([a-zA-Z_][a-zA-Z0-9_]*)?\$", text[position:])
|
|
462
|
+
if not start_match:
|
|
463
|
+
return None
|
|
464
|
+
|
|
465
|
+
tag = start_match.group(0)
|
|
466
|
+
content_start = position + len(tag)
|
|
467
|
+
|
|
468
|
+
try:
|
|
469
|
+
content_end = text.index(tag, content_start)
|
|
470
|
+
full_value = text[position : content_end + len(tag)]
|
|
471
|
+
|
|
472
|
+
return Token(type=TokenType.STRING_LITERAL, value=full_value, line=line, column=column, position=position)
|
|
473
|
+
except ValueError:
|
|
474
|
+
return None
|
|
475
|
+
|
|
476
|
+
|
|
477
|
+
class GenericDialectConfig(DialectConfig):
|
|
478
|
+
"""Generic SQL dialect configuration for standard SQL."""
|
|
479
|
+
|
|
480
|
+
@property
|
|
481
|
+
def name(self) -> str:
|
|
482
|
+
if self._name is None:
|
|
483
|
+
self._name = "generic"
|
|
484
|
+
return self._name
|
|
485
|
+
|
|
486
|
+
@property
|
|
487
|
+
def block_starters(self) -> "set[str]":
|
|
488
|
+
if self._block_starters is None:
|
|
489
|
+
self._block_starters = {"BEGIN", "DECLARE", "CASE"}
|
|
490
|
+
return self._block_starters
|
|
491
|
+
|
|
492
|
+
@property
|
|
493
|
+
def block_enders(self) -> "set[str]":
|
|
494
|
+
if self._block_enders is None:
|
|
495
|
+
self._block_enders = {"END"}
|
|
496
|
+
return self._block_enders
|
|
497
|
+
|
|
498
|
+
@property
|
|
499
|
+
def statement_terminators(self) -> "set[str]":
|
|
500
|
+
if self._statement_terminators is None:
|
|
501
|
+
self._statement_terminators = {";"}
|
|
502
|
+
return self._statement_terminators
|
|
503
|
+
|
|
504
|
+
|
|
505
|
+
class MySQLDialectConfig(DialectConfig):
|
|
506
|
+
"""Configuration for MySQL dialect."""
|
|
507
|
+
|
|
508
|
+
@property
|
|
509
|
+
def name(self) -> str:
|
|
510
|
+
if self._name is None:
|
|
511
|
+
self._name = "mysql"
|
|
512
|
+
return self._name
|
|
513
|
+
|
|
514
|
+
@property
|
|
515
|
+
def block_starters(self) -> "set[str]":
|
|
516
|
+
if self._block_starters is None:
|
|
517
|
+
self._block_starters = {"BEGIN", "DECLARE", "CASE"}
|
|
518
|
+
return self._block_starters
|
|
519
|
+
|
|
520
|
+
@property
|
|
521
|
+
def block_enders(self) -> "set[str]":
|
|
522
|
+
if self._block_enders is None:
|
|
523
|
+
self._block_enders = {"END"}
|
|
524
|
+
return self._block_enders
|
|
525
|
+
|
|
526
|
+
@property
|
|
527
|
+
def statement_terminators(self) -> "set[str]":
|
|
528
|
+
if self._statement_terminators is None:
|
|
529
|
+
self._statement_terminators = {";"}
|
|
530
|
+
return self._statement_terminators
|
|
531
|
+
|
|
532
|
+
@property
|
|
533
|
+
def special_terminators(self) -> "dict[str, Callable[[list[Token], int], bool]]":
|
|
534
|
+
if self._special_terminators is None:
|
|
535
|
+
self._special_terminators = {"\\g": _special_terminator_true, "\\G": _special_terminator_true}
|
|
536
|
+
return self._special_terminators
|
|
537
|
+
|
|
538
|
+
|
|
539
|
+
class SQLiteDialectConfig(DialectConfig):
|
|
540
|
+
"""Configuration for SQLite dialect."""
|
|
541
|
+
|
|
542
|
+
@property
|
|
543
|
+
def name(self) -> str:
|
|
544
|
+
if self._name is None:
|
|
545
|
+
self._name = "sqlite"
|
|
546
|
+
return self._name
|
|
547
|
+
|
|
548
|
+
@property
|
|
549
|
+
def block_starters(self) -> "set[str]":
|
|
550
|
+
if self._block_starters is None:
|
|
551
|
+
self._block_starters = {"BEGIN", "CASE"}
|
|
552
|
+
return self._block_starters
|
|
553
|
+
|
|
554
|
+
@property
|
|
555
|
+
def block_enders(self) -> "set[str]":
|
|
556
|
+
if self._block_enders is None:
|
|
557
|
+
self._block_enders = {"END"}
|
|
558
|
+
return self._block_enders
|
|
559
|
+
|
|
560
|
+
@property
|
|
561
|
+
def statement_terminators(self) -> "set[str]":
|
|
562
|
+
if self._statement_terminators is None:
|
|
563
|
+
self._statement_terminators = {";"}
|
|
564
|
+
return self._statement_terminators
|
|
565
|
+
|
|
566
|
+
|
|
567
|
+
class DuckDBDialectConfig(DialectConfig):
|
|
568
|
+
"""Configuration for DuckDB dialect."""
|
|
569
|
+
|
|
570
|
+
@property
|
|
571
|
+
def name(self) -> str:
|
|
572
|
+
if self._name is None:
|
|
573
|
+
self._name = "duckdb"
|
|
574
|
+
return self._name
|
|
575
|
+
|
|
576
|
+
@property
|
|
577
|
+
def block_starters(self) -> "set[str]":
|
|
578
|
+
if self._block_starters is None:
|
|
579
|
+
self._block_starters = {"BEGIN", "CASE"}
|
|
580
|
+
return self._block_starters
|
|
581
|
+
|
|
582
|
+
@property
|
|
583
|
+
def block_enders(self) -> "set[str]":
|
|
584
|
+
if self._block_enders is None:
|
|
585
|
+
self._block_enders = {"END"}
|
|
586
|
+
return self._block_enders
|
|
587
|
+
|
|
588
|
+
@property
|
|
589
|
+
def statement_terminators(self) -> "set[str]":
|
|
590
|
+
if self._statement_terminators is None:
|
|
591
|
+
self._statement_terminators = {";"}
|
|
592
|
+
return self._statement_terminators
|
|
593
|
+
|
|
594
|
+
|
|
595
|
+
class BigQueryDialectConfig(DialectConfig):
|
|
596
|
+
"""Configuration for BigQuery dialect."""
|
|
597
|
+
|
|
598
|
+
@property
|
|
599
|
+
def name(self) -> str:
|
|
600
|
+
if self._name is None:
|
|
601
|
+
self._name = "bigquery"
|
|
602
|
+
return self._name
|
|
603
|
+
|
|
604
|
+
@property
|
|
605
|
+
def block_starters(self) -> "set[str]":
|
|
606
|
+
if self._block_starters is None:
|
|
607
|
+
self._block_starters = {"BEGIN", "CASE"}
|
|
608
|
+
return self._block_starters
|
|
609
|
+
|
|
610
|
+
@property
|
|
611
|
+
def block_enders(self) -> "set[str]":
|
|
612
|
+
if self._block_enders is None:
|
|
613
|
+
self._block_enders = {"END"}
|
|
614
|
+
return self._block_enders
|
|
615
|
+
|
|
616
|
+
@property
|
|
617
|
+
def statement_terminators(self) -> "set[str]":
|
|
618
|
+
if self._statement_terminators is None:
|
|
619
|
+
self._statement_terminators = {";"}
|
|
620
|
+
return self._statement_terminators
|
|
621
|
+
|
|
622
|
+
|
|
623
|
+
_pattern_cache: LRUCache | None = None
|
|
624
|
+
_result_cache: LRUCache | None = None
|
|
625
|
+
_cache_lock = threading.Lock()
|
|
626
|
+
|
|
627
|
+
|
|
628
|
+
def _get_pattern_cache() -> LRUCache:
|
|
629
|
+
"""Get or create the global pattern compilation cache.
|
|
630
|
+
|
|
631
|
+
Returns:
|
|
632
|
+
The pattern cache instance
|
|
633
|
+
"""
|
|
634
|
+
global _pattern_cache
|
|
635
|
+
if _pattern_cache is None:
|
|
636
|
+
with _cache_lock:
|
|
637
|
+
if _pattern_cache is None:
|
|
638
|
+
_pattern_cache = LRUCache(max_size=DEFAULT_PATTERN_CACHE_SIZE, ttl_seconds=DEFAULT_CACHE_TTL)
|
|
639
|
+
return _pattern_cache
|
|
640
|
+
|
|
641
|
+
|
|
642
|
+
def _get_result_cache() -> LRUCache:
|
|
643
|
+
"""Get or create the global result cache.
|
|
644
|
+
|
|
645
|
+
Returns:
|
|
646
|
+
The result cache instance
|
|
647
|
+
"""
|
|
648
|
+
global _result_cache
|
|
649
|
+
if _result_cache is None:
|
|
650
|
+
with _cache_lock:
|
|
651
|
+
if _result_cache is None:
|
|
652
|
+
_result_cache = LRUCache(max_size=DEFAULT_RESULT_CACHE_SIZE, ttl_seconds=DEFAULT_CACHE_TTL)
|
|
653
|
+
return _result_cache
|
|
654
|
+
|
|
655
|
+
|
|
656
|
+
@mypyc_attr(allow_interpreted_subclasses=False)
|
|
657
|
+
class StatementSplitter:
|
|
658
|
+
"""SQL script splitter with caching and dialect support."""
|
|
659
|
+
|
|
660
|
+
__slots__ = SPLITTER_SLOTS
|
|
661
|
+
|
|
662
|
+
def __init__(self, dialect: DialectConfig, strip_trailing_semicolon: bool = False) -> None:
|
|
663
|
+
"""Initialize the statement splitter.
|
|
664
|
+
|
|
665
|
+
Args:
|
|
666
|
+
dialect: The SQL dialect configuration to use
|
|
667
|
+
strip_trailing_semicolon: Whether to remove trailing semicolons from statements
|
|
668
|
+
"""
|
|
669
|
+
self._dialect = dialect
|
|
670
|
+
self._strip_trailing_semicolon = strip_trailing_semicolon
|
|
671
|
+
self._token_patterns = dialect.get_all_token_patterns()
|
|
672
|
+
|
|
673
|
+
self._pattern_cache_key = f"{dialect.name}:{hash(tuple(str(p) for _, p in self._token_patterns))}"
|
|
674
|
+
|
|
675
|
+
self._pattern_cache = _get_pattern_cache()
|
|
676
|
+
self._result_cache = _get_result_cache()
|
|
677
|
+
|
|
678
|
+
self._compiled_patterns = self._get_or_compile_patterns()
|
|
679
|
+
|
|
680
|
+
def _get_or_compile_patterns(self) -> "list[tuple[TokenType, CompiledTokenPattern]]":
|
|
681
|
+
"""Get compiled regex patterns from cache or compile and cache them.
|
|
682
|
+
|
|
683
|
+
Returns:
|
|
684
|
+
List of compiled token patterns with their types
|
|
685
|
+
"""
|
|
686
|
+
cache_key = CacheKey(("pattern", self._pattern_cache_key))
|
|
687
|
+
|
|
688
|
+
cached_patterns = self._pattern_cache.get(cache_key)
|
|
689
|
+
if cached_patterns is not None:
|
|
690
|
+
return cast("list[tuple[TokenType, CompiledTokenPattern]]", cached_patterns)
|
|
691
|
+
|
|
692
|
+
compiled: list[tuple[TokenType, CompiledTokenPattern]] = []
|
|
693
|
+
for token_type, pattern in self._token_patterns:
|
|
694
|
+
if isinstance(pattern, str):
|
|
695
|
+
compiled.append((token_type, re.compile(pattern, re.IGNORECASE | re.DOTALL)))
|
|
696
|
+
else:
|
|
697
|
+
compiled.append((token_type, pattern))
|
|
698
|
+
|
|
699
|
+
self._pattern_cache.put(cache_key, compiled)
|
|
700
|
+
return compiled
|
|
701
|
+
|
|
702
|
+
def _tokenize(self, sql: str) -> Generator[Token, None, None]:
|
|
703
|
+
"""Tokenize SQL string into Token objects.
|
|
704
|
+
|
|
705
|
+
Args:
|
|
706
|
+
sql: The SQL string to tokenize
|
|
707
|
+
|
|
708
|
+
Yields:
|
|
709
|
+
Token objects representing the lexical elements
|
|
710
|
+
"""
|
|
711
|
+
pos = 0
|
|
712
|
+
line = 1
|
|
713
|
+
line_start = 0
|
|
714
|
+
unmatched_count = 0
|
|
715
|
+
first_unmatched_pos: int | None = None
|
|
716
|
+
first_unmatched_snippet: str | None = None
|
|
717
|
+
|
|
718
|
+
while pos < len(sql):
|
|
719
|
+
matched = False
|
|
720
|
+
|
|
721
|
+
for token_type, pattern in self._compiled_patterns:
|
|
722
|
+
if callable(pattern):
|
|
723
|
+
column = pos - line_start + 1
|
|
724
|
+
token = pattern(sql, pos, line, column)
|
|
725
|
+
if token:
|
|
726
|
+
newlines = token.value.count("\n")
|
|
727
|
+
if newlines > 0:
|
|
728
|
+
line += newlines
|
|
729
|
+
last_newline = token.value.rfind("\n")
|
|
730
|
+
line_start = pos + last_newline + 1
|
|
731
|
+
|
|
732
|
+
yield token
|
|
733
|
+
pos += len(token.value)
|
|
734
|
+
matched = True
|
|
735
|
+
break
|
|
736
|
+
else:
|
|
737
|
+
match = pattern.match(sql, pos)
|
|
738
|
+
if match:
|
|
739
|
+
value = match.group(0)
|
|
740
|
+
column = pos - line_start + 1
|
|
741
|
+
|
|
742
|
+
newlines = value.count("\n")
|
|
743
|
+
if newlines > 0:
|
|
744
|
+
line += newlines
|
|
745
|
+
last_newline = value.rfind("\n")
|
|
746
|
+
line_start = pos + last_newline + 1
|
|
747
|
+
|
|
748
|
+
yield Token(type=token_type, value=value, line=line, column=column, position=pos)
|
|
749
|
+
pos = match.end()
|
|
750
|
+
matched = True
|
|
751
|
+
break
|
|
752
|
+
|
|
753
|
+
if not matched:
|
|
754
|
+
if unmatched_count == 0:
|
|
755
|
+
first_unmatched_pos = pos
|
|
756
|
+
first_unmatched_snippet = sql[pos : pos + _TOKENIZE_SNIPPET_LENGTH]
|
|
757
|
+
unmatched_count += 1
|
|
758
|
+
if unmatched_count <= _TOKENIZE_DEBUG_SAMPLE_LIMIT and logger.isEnabledFor(logging.DEBUG):
|
|
759
|
+
logger.debug(
|
|
760
|
+
"Failed to tokenize at position %d: %s", pos, sql[pos : pos + _TOKENIZE_SNIPPET_LENGTH]
|
|
761
|
+
)
|
|
762
|
+
pos += 1
|
|
763
|
+
|
|
764
|
+
if unmatched_count and logger.isEnabledFor(logging.DEBUG):
|
|
765
|
+
logger.debug(
|
|
766
|
+
"Tokenization skipped %d unmatched characters (first at %s: %s)",
|
|
767
|
+
unmatched_count,
|
|
768
|
+
first_unmatched_pos,
|
|
769
|
+
first_unmatched_snippet,
|
|
770
|
+
)
|
|
771
|
+
|
|
772
|
+
def split(self, sql: str) -> "list[str]":
|
|
773
|
+
"""Split SQL script into individual statements.
|
|
774
|
+
|
|
775
|
+
Args:
|
|
776
|
+
sql: The SQL script to split
|
|
777
|
+
|
|
778
|
+
Returns:
|
|
779
|
+
List of individual SQL statements
|
|
780
|
+
"""
|
|
781
|
+
script_hash = hash(sql)
|
|
782
|
+
cache_key = CacheKey(("split", self._dialect.name, script_hash, self._strip_trailing_semicolon))
|
|
783
|
+
|
|
784
|
+
cached_result = self._result_cache.get(cache_key)
|
|
785
|
+
if cached_result is not None:
|
|
786
|
+
return cast("list[str]", cached_result)
|
|
787
|
+
|
|
788
|
+
statements = self._do_split(sql)
|
|
789
|
+
|
|
790
|
+
self._result_cache.put(cache_key, statements)
|
|
791
|
+
return statements
|
|
792
|
+
|
|
793
|
+
def _do_split(self, sql: str) -> "list[str]":
|
|
794
|
+
"""Perform the actual SQL script splitting logic.
|
|
795
|
+
|
|
796
|
+
Args:
|
|
797
|
+
sql: The SQL script to split
|
|
798
|
+
|
|
799
|
+
Returns:
|
|
800
|
+
List of individual SQL statements
|
|
801
|
+
"""
|
|
802
|
+
statements = []
|
|
803
|
+
current_statement_tokens = []
|
|
804
|
+
current_statement_chars = []
|
|
805
|
+
block_stack = []
|
|
806
|
+
|
|
807
|
+
all_tokens = list(self._tokenize(sql))
|
|
808
|
+
|
|
809
|
+
for token_idx, token in enumerate(all_tokens):
|
|
810
|
+
current_statement_chars.append(token.value)
|
|
811
|
+
|
|
812
|
+
if token.type in {TokenType.WHITESPACE, TokenType.COMMENT_LINE, TokenType.COMMENT_BLOCK}:
|
|
813
|
+
current_statement_tokens.append(token)
|
|
814
|
+
continue
|
|
815
|
+
|
|
816
|
+
current_statement_tokens.append(token)
|
|
817
|
+
token_upper = token.value.upper()
|
|
818
|
+
|
|
819
|
+
if token.type == TokenType.KEYWORD:
|
|
820
|
+
if token_upper in self._dialect.block_starters:
|
|
821
|
+
block_stack.append(token_upper)
|
|
822
|
+
if len(block_stack) > self._dialect.max_nesting_depth:
|
|
823
|
+
msg = f"Maximum nesting depth ({self._dialect.max_nesting_depth}) exceeded"
|
|
824
|
+
raise ValueError(msg)
|
|
825
|
+
elif token_upper in self._dialect.block_enders:
|
|
826
|
+
if block_stack and self._dialect.is_real_block_ender(all_tokens, token_idx):
|
|
827
|
+
block_stack.pop()
|
|
828
|
+
|
|
829
|
+
is_terminator = False
|
|
830
|
+
if not block_stack:
|
|
831
|
+
if token.type == TokenType.TERMINATOR:
|
|
832
|
+
if token.value in self._dialect.statement_terminators:
|
|
833
|
+
should_delay = self._dialect.should_delay_semicolon_termination(all_tokens, token_idx)
|
|
834
|
+
|
|
835
|
+
if not should_delay and token.value == ";" and self._dialect.batch_separators:
|
|
836
|
+
should_delay = True
|
|
837
|
+
|
|
838
|
+
if not should_delay:
|
|
839
|
+
is_terminator = True
|
|
840
|
+
elif token.value in self._dialect.special_terminators:
|
|
841
|
+
handler = self._dialect.special_terminators[token.value]
|
|
842
|
+
if handler(all_tokens, token_idx):
|
|
843
|
+
is_terminator = True
|
|
844
|
+
|
|
845
|
+
elif token.type == TokenType.KEYWORD and token_upper in self._dialect.batch_separators:
|
|
846
|
+
is_terminator = True
|
|
847
|
+
|
|
848
|
+
if is_terminator:
|
|
849
|
+
statement = "".join(current_statement_chars).strip()
|
|
850
|
+
|
|
851
|
+
is_plsql_block = self._is_plsql_block(current_statement_tokens)
|
|
852
|
+
|
|
853
|
+
if (
|
|
854
|
+
self._strip_trailing_semicolon
|
|
855
|
+
and token.type == TokenType.TERMINATOR
|
|
856
|
+
and statement.endswith(token.value)
|
|
857
|
+
and not is_plsql_block
|
|
858
|
+
):
|
|
859
|
+
statement = statement[: -len(token.value)].rstrip()
|
|
860
|
+
|
|
861
|
+
if statement and self._contains_executable_content(statement):
|
|
862
|
+
statements.append(statement)
|
|
863
|
+
current_statement_tokens = []
|
|
864
|
+
current_statement_chars = []
|
|
865
|
+
|
|
866
|
+
if current_statement_chars:
|
|
867
|
+
statement = "".join(current_statement_chars).strip()
|
|
868
|
+
if statement and self._contains_executable_content(statement):
|
|
869
|
+
statements.append(statement)
|
|
870
|
+
|
|
871
|
+
return statements
|
|
872
|
+
|
|
873
|
+
@staticmethod
|
|
874
|
+
def _is_plsql_block(tokens: "list[Token]") -> bool:
|
|
875
|
+
"""Check if the token list represents a PL/SQL block.
|
|
876
|
+
|
|
877
|
+
Args:
|
|
878
|
+
tokens: List of tokens to examine
|
|
879
|
+
|
|
880
|
+
Returns:
|
|
881
|
+
True if tokens represent a PL/SQL block, False otherwise
|
|
882
|
+
"""
|
|
883
|
+
for token in tokens:
|
|
884
|
+
if token.type == TokenType.KEYWORD:
|
|
885
|
+
return token.value.upper() in {"BEGIN", "DECLARE"}
|
|
886
|
+
return False
|
|
887
|
+
|
|
888
|
+
def _contains_executable_content(self, statement: str) -> bool:
|
|
889
|
+
"""Check if a statement contains executable content.
|
|
890
|
+
|
|
891
|
+
Args:
|
|
892
|
+
statement: The SQL statement to check
|
|
893
|
+
|
|
894
|
+
Returns:
|
|
895
|
+
True if statement contains non-whitespace/non-comment content
|
|
896
|
+
"""
|
|
897
|
+
tokens = list(self._tokenize(statement))
|
|
898
|
+
|
|
899
|
+
for token in tokens:
|
|
900
|
+
if token.type not in {TokenType.WHITESPACE, TokenType.COMMENT_LINE, TokenType.COMMENT_BLOCK}:
|
|
901
|
+
return True
|
|
902
|
+
|
|
903
|
+
return False
|
|
904
|
+
|
|
905
|
+
|
|
906
|
+
def split_sql_script(script: str, dialect: str | None = None, strip_trailing_terminator: bool = False) -> "list[str]":
|
|
907
|
+
"""Split SQL script into individual statements.
|
|
908
|
+
|
|
909
|
+
Args:
|
|
910
|
+
script: The SQL script to split
|
|
911
|
+
dialect: The SQL dialect name
|
|
912
|
+
strip_trailing_terminator: If True, remove trailing terminators from statements
|
|
913
|
+
|
|
914
|
+
Returns:
|
|
915
|
+
List of individual SQL statements
|
|
916
|
+
"""
|
|
917
|
+
if dialect is None:
|
|
918
|
+
dialect = "generic"
|
|
919
|
+
|
|
920
|
+
dialect_configs = {
|
|
921
|
+
"generic": GenericDialectConfig(),
|
|
922
|
+
"oracle": OracleDialectConfig(),
|
|
923
|
+
"tsql": TSQLDialectConfig(),
|
|
924
|
+
"mssql": TSQLDialectConfig(),
|
|
925
|
+
"sqlserver": TSQLDialectConfig(),
|
|
926
|
+
"postgresql": PostgreSQLDialectConfig(),
|
|
927
|
+
"postgres": PostgreSQLDialectConfig(),
|
|
928
|
+
"mysql": MySQLDialectConfig(),
|
|
929
|
+
"sqlite": SQLiteDialectConfig(),
|
|
930
|
+
"duckdb": DuckDBDialectConfig(),
|
|
931
|
+
"bigquery": BigQueryDialectConfig(),
|
|
932
|
+
}
|
|
933
|
+
|
|
934
|
+
config = dialect_configs.get(dialect.lower())
|
|
935
|
+
if not config:
|
|
936
|
+
logger.warning("Unknown dialect '%s', using generic SQL splitter", dialect)
|
|
937
|
+
config = GenericDialectConfig()
|
|
938
|
+
|
|
939
|
+
splitter = StatementSplitter(config, strip_trailing_semicolon=strip_trailing_terminator)
|
|
940
|
+
return splitter.split(script)
|
|
941
|
+
|
|
942
|
+
|
|
943
|
+
def clear_splitter_caches() -> None:
|
|
944
|
+
"""Clear all splitter caches.
|
|
945
|
+
|
|
946
|
+
Clears both pattern and result caches to free memory.
|
|
947
|
+
"""
|
|
948
|
+
pattern_cache = _get_pattern_cache()
|
|
949
|
+
result_cache = _get_result_cache()
|
|
950
|
+
pattern_cache.clear()
|
|
951
|
+
result_cache.clear()
|
|
952
|
+
|
|
953
|
+
|
|
954
|
+
def get_splitter_cache_stats() -> "dict[str, Any]":
|
|
955
|
+
"""Get statistics from splitter caches.
|
|
956
|
+
|
|
957
|
+
Returns:
|
|
958
|
+
Dictionary containing cache statistics
|
|
959
|
+
"""
|
|
960
|
+
pattern_cache = _get_pattern_cache()
|
|
961
|
+
result_cache = _get_result_cache()
|
|
962
|
+
|
|
963
|
+
return {
|
|
964
|
+
"pattern_cache": {"size": pattern_cache.size(), "stats": pattern_cache.get_stats()},
|
|
965
|
+
"result_cache": {"size": result_cache.size(), "stats": result_cache.get_stats()},
|
|
966
|
+
}
|