sqlspec 0.12.1__py3-none-any.whl → 0.13.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of sqlspec might be problematic. Click here for more details.
- sqlspec/_sql.py +21 -180
- sqlspec/adapters/adbc/config.py +10 -12
- sqlspec/adapters/adbc/driver.py +120 -118
- sqlspec/adapters/aiosqlite/config.py +3 -3
- sqlspec/adapters/aiosqlite/driver.py +116 -141
- sqlspec/adapters/asyncmy/config.py +3 -4
- sqlspec/adapters/asyncmy/driver.py +123 -135
- sqlspec/adapters/asyncpg/config.py +3 -7
- sqlspec/adapters/asyncpg/driver.py +98 -140
- sqlspec/adapters/bigquery/config.py +4 -5
- sqlspec/adapters/bigquery/driver.py +231 -181
- sqlspec/adapters/duckdb/config.py +3 -6
- sqlspec/adapters/duckdb/driver.py +132 -124
- sqlspec/adapters/oracledb/config.py +6 -5
- sqlspec/adapters/oracledb/driver.py +242 -259
- sqlspec/adapters/psqlpy/config.py +3 -7
- sqlspec/adapters/psqlpy/driver.py +118 -93
- sqlspec/adapters/psycopg/config.py +34 -30
- sqlspec/adapters/psycopg/driver.py +342 -214
- sqlspec/adapters/sqlite/config.py +3 -3
- sqlspec/adapters/sqlite/driver.py +150 -104
- sqlspec/config.py +0 -4
- sqlspec/driver/_async.py +89 -98
- sqlspec/driver/_common.py +52 -17
- sqlspec/driver/_sync.py +81 -105
- sqlspec/driver/connection.py +207 -0
- sqlspec/driver/mixins/_csv_writer.py +91 -0
- sqlspec/driver/mixins/_pipeline.py +38 -49
- sqlspec/driver/mixins/_result_utils.py +27 -9
- sqlspec/driver/mixins/_storage.py +149 -216
- sqlspec/driver/mixins/_type_coercion.py +3 -4
- sqlspec/driver/parameters.py +138 -0
- sqlspec/exceptions.py +10 -2
- sqlspec/extensions/aiosql/adapter.py +0 -10
- sqlspec/extensions/litestar/handlers.py +0 -1
- sqlspec/extensions/litestar/plugin.py +0 -3
- sqlspec/extensions/litestar/providers.py +0 -14
- sqlspec/loader.py +31 -118
- sqlspec/protocols.py +542 -0
- sqlspec/service/__init__.py +3 -2
- sqlspec/service/_util.py +147 -0
- sqlspec/service/base.py +1116 -9
- sqlspec/statement/builder/__init__.py +42 -32
- sqlspec/statement/builder/_ddl_utils.py +0 -10
- sqlspec/statement/builder/_parsing_utils.py +10 -4
- sqlspec/statement/builder/base.py +70 -23
- sqlspec/statement/builder/column.py +283 -0
- sqlspec/statement/builder/ddl.py +102 -65
- sqlspec/statement/builder/delete.py +23 -7
- sqlspec/statement/builder/insert.py +29 -15
- sqlspec/statement/builder/merge.py +4 -4
- sqlspec/statement/builder/mixins/_aggregate_functions.py +113 -14
- sqlspec/statement/builder/mixins/_common_table_expr.py +0 -1
- sqlspec/statement/builder/mixins/_delete_from.py +1 -1
- sqlspec/statement/builder/mixins/_from.py +10 -8
- sqlspec/statement/builder/mixins/_group_by.py +0 -1
- sqlspec/statement/builder/mixins/_insert_from_select.py +0 -1
- sqlspec/statement/builder/mixins/_insert_values.py +0 -2
- sqlspec/statement/builder/mixins/_join.py +20 -13
- sqlspec/statement/builder/mixins/_limit_offset.py +3 -3
- sqlspec/statement/builder/mixins/_merge_clauses.py +3 -4
- sqlspec/statement/builder/mixins/_order_by.py +2 -2
- sqlspec/statement/builder/mixins/_pivot.py +4 -7
- sqlspec/statement/builder/mixins/_select_columns.py +6 -5
- sqlspec/statement/builder/mixins/_unpivot.py +6 -9
- sqlspec/statement/builder/mixins/_update_from.py +2 -1
- sqlspec/statement/builder/mixins/_update_set.py +11 -8
- sqlspec/statement/builder/mixins/_where.py +61 -34
- sqlspec/statement/builder/select.py +32 -17
- sqlspec/statement/builder/update.py +25 -11
- sqlspec/statement/filters.py +39 -14
- sqlspec/statement/parameter_manager.py +220 -0
- sqlspec/statement/parameters.py +210 -79
- sqlspec/statement/pipelines/__init__.py +166 -23
- sqlspec/statement/pipelines/analyzers/_analyzer.py +22 -25
- sqlspec/statement/pipelines/context.py +35 -39
- sqlspec/statement/pipelines/transformers/__init__.py +2 -3
- sqlspec/statement/pipelines/transformers/_expression_simplifier.py +19 -187
- sqlspec/statement/pipelines/transformers/_literal_parameterizer.py +667 -43
- sqlspec/statement/pipelines/transformers/_remove_comments_and_hints.py +76 -0
- sqlspec/statement/pipelines/validators/_dml_safety.py +33 -18
- sqlspec/statement/pipelines/validators/_parameter_style.py +87 -14
- sqlspec/statement/pipelines/validators/_performance.py +38 -23
- sqlspec/statement/pipelines/validators/_security.py +39 -62
- sqlspec/statement/result.py +37 -129
- sqlspec/statement/splitter.py +0 -12
- sqlspec/statement/sql.py +885 -379
- sqlspec/statement/sql_compiler.py +140 -0
- sqlspec/storage/__init__.py +10 -2
- sqlspec/storage/backends/fsspec.py +82 -35
- sqlspec/storage/backends/obstore.py +66 -49
- sqlspec/storage/capabilities.py +101 -0
- sqlspec/storage/registry.py +56 -83
- sqlspec/typing.py +6 -434
- sqlspec/utils/cached_property.py +25 -0
- sqlspec/utils/correlation.py +0 -2
- sqlspec/utils/logging.py +0 -6
- sqlspec/utils/sync_tools.py +0 -4
- sqlspec/utils/text.py +0 -5
- sqlspec/utils/type_guards.py +892 -0
- {sqlspec-0.12.1.dist-info → sqlspec-0.13.0.dist-info}/METADATA +1 -1
- sqlspec-0.13.0.dist-info/RECORD +150 -0
- sqlspec/statement/builder/protocols.py +0 -20
- sqlspec/statement/pipelines/base.py +0 -315
- sqlspec/statement/pipelines/result_types.py +0 -41
- sqlspec/statement/pipelines/transformers/_remove_comments.py +0 -66
- sqlspec/statement/pipelines/transformers/_remove_hints.py +0 -81
- sqlspec/statement/pipelines/validators/base.py +0 -67
- sqlspec/storage/protocol.py +0 -170
- sqlspec-0.12.1.dist-info/RECORD +0 -145
- {sqlspec-0.12.1.dist-info → sqlspec-0.13.0.dist-info}/WHEEL +0 -0
- {sqlspec-0.12.1.dist-info → sqlspec-0.13.0.dist-info}/licenses/LICENSE +0 -0
- {sqlspec-0.12.1.dist-info → sqlspec-0.13.0.dist-info}/licenses/NOTICE +0 -0
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
sqlspec/__init__.py,sha256=hyVFQsYsgDWOZ2EPnW1LnATQVxGcYu3liBvSOvk4EQk,705
|
|
2
|
+
sqlspec/__metadata__.py,sha256=hNP3wXvtk8fQVPKGjRLpZ9mP-gaPJqzrmgm3UqpDIXQ,460
|
|
3
|
+
sqlspec/_serialization.py,sha256=7zox4G9zIps-DCdIEwYs4gwALfEOy1g_sWS4r5kpzO8,2604
|
|
4
|
+
sqlspec/_sql.py,sha256=zqMtVWBZzbnNErB8VyJkjDCPq0RNkhGUPA2ZCOtzmGo,35759
|
|
5
|
+
sqlspec/_typing.py,sha256=Vn1CTCfedAHZV3pKZP-l_mPw9pTxesCzRKVRypzNY_k,17903
|
|
6
|
+
sqlspec/base.py,sha256=a7adbCUzohf1MU-iP0TxazGsk9fsJhJmxuFKNWkgC6o,18355
|
|
7
|
+
sqlspec/config.py,sha256=NJg5cjITI3GBCm3ru-m9K3y_XLa0Ru8Z4suG3TjXRmw,12896
|
|
8
|
+
sqlspec/exceptions.py,sha256=T2h-tCN05sRKSpXDjPtYXvNgwInNVzTpha1PIkSUROQ,14168
|
|
9
|
+
sqlspec/loader.py,sha256=J0PaBiY8kC_gCI-3i8Vj3IBZIgKJ4TvRAJzMQerLZMQ,15354
|
|
10
|
+
sqlspec/protocols.py,sha256=ce9L1nqxpmDSQqUR6UoKB8f5eL0hFnp2ProrsMwMsKk,17218
|
|
11
|
+
sqlspec/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
12
|
+
sqlspec/typing.py,sha256=qkUcvruNzRUUfpv4AkbQezpy9SokhVHXlMbfFNwQlnU,8721
|
|
13
|
+
sqlspec/adapters/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
14
|
+
sqlspec/adapters/adbc/__init__.py,sha256=v9bs7501PgEyzR1XIsEpE2Wdrj9DOYkZ4grysw72UXk,218
|
|
15
|
+
sqlspec/adapters/adbc/config.py,sha256=v_gk-C-StoWiSjmB9Ehl6ce2eGqUgb1-JUxSq3lS8l8,20022
|
|
16
|
+
sqlspec/adapters/adbc/driver.py,sha256=WP3YVS-9Y1aup9IQ7y4L8x9e414epcWCyhPHuBVp66w,16644
|
|
17
|
+
sqlspec/adapters/aiosqlite/__init__.py,sha256=7wPmhXQeu4jRi-LZzPxAPTdgRmgmyqCn9U-4wnCWoLM,258
|
|
18
|
+
sqlspec/adapters/aiosqlite/config.py,sha256=2YBdTPaWLeJeZwvqGwfNa0UM6ErVWaKulPk6vCB6LU0,7168
|
|
19
|
+
sqlspec/adapters/aiosqlite/driver.py,sha256=cfxuob0EmnLB3iCgpuqjw40-9ZWSmVLZ_UL3BNznXt0,11282
|
|
20
|
+
sqlspec/adapters/asyncmy/__init__.py,sha256=zYpebEt_PrNCifLcqXiCcWVm0Zl-LvWbFDromWwSTw8,270
|
|
21
|
+
sqlspec/adapters/asyncmy/config.py,sha256=4d_R3sCYi3JB2QcGsPhJ5NTfBsjFrqLhJnHVQlFf2E4,10330
|
|
22
|
+
sqlspec/adapters/asyncmy/driver.py,sha256=7l9rsAxTiJhiTiBMQc0e1QwBDWopvfii5jgFwREITog,10085
|
|
23
|
+
sqlspec/adapters/asyncpg/__init__.py,sha256=svnbKlOin8jRL88AdAqflBEU-WEAzp0Thc2y50QsxIo,310
|
|
24
|
+
sqlspec/adapters/asyncpg/config.py,sha256=J0dzHDwSRdlz3gr6tuTztwhZg0pUcbYIJg8ylmb4BKU,12804
|
|
25
|
+
sqlspec/adapters/asyncpg/driver.py,sha256=X5IF5_NEiQ86aLspuqjY9Fh5OIluCCNa7TAzLEFENjQ,17572
|
|
26
|
+
sqlspec/adapters/bigquery/__init__.py,sha256=fWRH-BoCNX4rYwhU2DK64cXWpfkYpWIExddJAti0bxM,250
|
|
27
|
+
sqlspec/adapters/bigquery/config.py,sha256=iwQ3BPYtywjD17TaYh0YAO4X_ry5DkWIRKWPAeYrkzg,16929
|
|
28
|
+
sqlspec/adapters/bigquery/driver.py,sha256=2aVWjjVAWci92wyXKo5dVV9PGBb6uDkSvDBEEs1eFPw,30385
|
|
29
|
+
sqlspec/adapters/duckdb/__init__.py,sha256=I1f6szfpKKrq6WhyDoUXD3i3NN4yjsh94_fhP1URI5M,351
|
|
30
|
+
sqlspec/adapters/duckdb/config.py,sha256=DBfDLaS_y3qfdYgxCnfnWml7lj1OFOMeUrGxkt6kFj0,20304
|
|
31
|
+
sqlspec/adapters/duckdb/driver.py,sha256=JZipJMy1yUfedtlNZxd8pGTFVylve8ia4XeKfd7dd0c,18095
|
|
32
|
+
sqlspec/adapters/oracledb/__init__.py,sha256=nn3whn0UyBThoXnE1-5_byVuc9PJjvB2P896p7LpNZI,474
|
|
33
|
+
sqlspec/adapters/oracledb/config.py,sha256=Qgls9PF6AVim_E7gfgi_sCDQGwGeZ5VWTyqKM43uIr4,22757
|
|
34
|
+
sqlspec/adapters/oracledb/driver.py,sha256=ZFOHGQwajIkG7PSWi_ER9QQRNdf18DyYho3L36m7CVU,24473
|
|
35
|
+
sqlspec/adapters/psqlpy/__init__.py,sha256=dp0-96V4SAbNEvOqlJ8PWEyJMYzZGElVoyneZqJ-fbQ,297
|
|
36
|
+
sqlspec/adapters/psqlpy/config.py,sha256=6jxBkNLpoe0c4ehjM0RKhvMlWnWfqgxc5NTRfhF3O6Q,16177
|
|
37
|
+
sqlspec/adapters/psqlpy/driver.py,sha256=liB3pGbDKuz2PyTd3QeDk3rRLKE57leviIdN_hqJHvg,9961
|
|
38
|
+
sqlspec/adapters/psycopg/__init__.py,sha256=ukkCUPrJPyAG78v4rOqcK4WZDs26PeB9Ra9qkFrGJ3E,484
|
|
39
|
+
sqlspec/adapters/psycopg/config.py,sha256=p2UP5nICbHoNZD84E31A4hrl483VB5JpbeyFpHG_tfQ,26777
|
|
40
|
+
sqlspec/adapters/psycopg/driver.py,sha256=UnnVJg5SwjReYWYX4fQ05JmL5hoJ1XeUby2m9IBrOgM,40064
|
|
41
|
+
sqlspec/adapters/sqlite/__init__.py,sha256=1lYrJ-DojUAOvXMoZRUJNEVyMmYhO41hMJnDWCEeXlw,234
|
|
42
|
+
sqlspec/adapters/sqlite/config.py,sha256=ph3FjdrbVkDaasGDEoZ-estP1NJdj8PqBJqpV-2IJxI,6191
|
|
43
|
+
sqlspec/adapters/sqlite/driver.py,sha256=0xBl7J5bPuv07fte5zPQj-bLhuiIC1_QPHu64i8Il80,12423
|
|
44
|
+
sqlspec/driver/__init__.py,sha256=0udRS5IlJ17HzOCvzapG8c_88yAwTQri1XLD_3fZxZU,671
|
|
45
|
+
sqlspec/driver/_async.py,sha256=ZSUNvVBU22KcpvKrDPJnlc6lnD49XLovS4vCQnr_OPA,9992
|
|
46
|
+
sqlspec/driver/_common.py,sha256=ALJyI6NS_pa0BOad0xiCMRwFLh4ykneaDW_xg9h8Z48,15811
|
|
47
|
+
sqlspec/driver/_sync.py,sha256=KzETeE-NHc1cOcX3LQ_88PejM-TJHD24UFQrB_yLDfQ,9832
|
|
48
|
+
sqlspec/driver/connection.py,sha256=zKP2p-VLxN07IPfOIBKMRELHN7ZMjmRVUKBgSW-6RQg,6610
|
|
49
|
+
sqlspec/driver/parameters.py,sha256=srU_7z3sB2vBu_lz9rAiDW2J2Ce47l12bqhxw6jEePE,4002
|
|
50
|
+
sqlspec/driver/mixins/__init__.py,sha256=-FSWLYq644NftRsmjmXMA9Q7_l7tIFAIh1ZyK0yrba8,652
|
|
51
|
+
sqlspec/driver/mixins/_csv_writer.py,sha256=-uRe7QZRWtdZTFG3Fiej4Ia8EyQcjYr7oPT6wb0qTOc,2716
|
|
52
|
+
sqlspec/driver/mixins/_pipeline.py,sha256=ESQyQo1ow_yerUvdFYS_CBy-6XS9tIow41j80yjCz4w,19588
|
|
53
|
+
sqlspec/driver/mixins/_result_utils.py,sha256=RvdE1BKwnTPx3aXDwnJEonJzp5PdT1Wgn63DGZyglvg,5692
|
|
54
|
+
sqlspec/driver/mixins/_sql_translator.py,sha256=p_PR4KBg9NKNpRpiEqA0hcUIttpdillHpnLUVJEcxuE,1519
|
|
55
|
+
sqlspec/driver/mixins/_storage.py,sha256=P6eIvCMCM6BMH4G0rbWZTb4Q54mmvtnTxk2pMp3JW7o,38558
|
|
56
|
+
sqlspec/driver/mixins/_type_coercion.py,sha256=ix-_p1-Yp9LhQdGFhFN1H_O8_VbVih-nzLPbsQiNFnE,4578
|
|
57
|
+
sqlspec/extensions/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
58
|
+
sqlspec/extensions/aiosql/__init__.py,sha256=-9cefc9pYPf9vCgALoB-y1DtmcgRjKe2azfl6RIarAA,414
|
|
59
|
+
sqlspec/extensions/aiosql/adapter.py,sha256=BcJI0y0Hj5t5aH6_Kx_06EkKy3aBLsh7xIJH-LMMoGg,16544
|
|
60
|
+
sqlspec/extensions/litestar/__init__.py,sha256=aXF2g4-U86pdJHoOA6RCvZgnyHVBbjRhlWqcSuRX2gg,243
|
|
61
|
+
sqlspec/extensions/litestar/_utils.py,sha256=o-FuUj1_WkDrLxQxiP6hXDak66XfyRP3QLyEVKrIRjI,1954
|
|
62
|
+
sqlspec/extensions/litestar/config.py,sha256=vLXM425tCV3IbJiNO1ZG90ctjBTb9oQCuLsqCaUYmo8,4685
|
|
63
|
+
sqlspec/extensions/litestar/handlers.py,sha256=bVoWmHhCsHZkXZXYHE23yDaJlRoydfvU-boJjA3BSLE,10478
|
|
64
|
+
sqlspec/extensions/litestar/plugin.py,sha256=ysuy4dmF6VAhUkIcVOSPfv5HcBGmwSZ9_PjttBs81g4,5431
|
|
65
|
+
sqlspec/extensions/litestar/providers.py,sha256=wUGqPeNV58rPqL1iwDQg439C4y-Zg-eXEogjeFsv0iQ,21021
|
|
66
|
+
sqlspec/service/__init__.py,sha256=DayFSxYU4AMbcN0JWBuB1rtrOAs-amKIAIyTJXXNKH0,206
|
|
67
|
+
sqlspec/service/_util.py,sha256=K31tzlOsTXGhbXe4u5ltZYLExuaFtpiZoIHLOk8yNyM,6096
|
|
68
|
+
sqlspec/service/base.py,sha256=iW4LbrIxjrSt0mDPPSmuUGQgdQTFM-xgmZdThCLa5u4,40592
|
|
69
|
+
sqlspec/service/pagination.py,sha256=ndLQwsvswoISWEE_3IG9nmdHt42OZkVMJvr-2HTWsLw,619
|
|
70
|
+
sqlspec/statement/__init__.py,sha256=NDvdrpr1EqAZXp4HmhTVaRnWVscahPSHpmSO8DHSoMY,536
|
|
71
|
+
sqlspec/statement/filters.py,sha256=qX5KOe-rLmFwyD8xaUcTTkh45Dg1ViKx_hTAqjKtWh4,22565
|
|
72
|
+
sqlspec/statement/parameter_manager.py,sha256=nbeLzO2Hnm4Su8id-fKEELvWEI8dbbC5ZYiqo4jjbvc,9557
|
|
73
|
+
sqlspec/statement/parameters.py,sha256=8V9mNr5Lb4iZmHfucZB9HMYOI2OmhaNzr2ZnMSAYnKk,35791
|
|
74
|
+
sqlspec/statement/result.py,sha256=-yARFxg7Wsm8nAeHMpnJ58OLd2VujFiZx29wfo7eFVs,14514
|
|
75
|
+
sqlspec/statement/splitter.py,sha256=mCzJsNEtJFk4A0FdTELkG_qOggRFCQC_ZEJisVo9orY,24111
|
|
76
|
+
sqlspec/statement/sql.py,sha256=l_KrA91wlIAXIQiaOsvEqZbY-Hg6QWtF8Va2qr7mfCE,71916
|
|
77
|
+
sqlspec/statement/sql_compiler.py,sha256=f1UoGWjZTCLwOMBiG1R45_tq8oQlR8Qmm9fHKy_uXYA,5882
|
|
78
|
+
sqlspec/statement/builder/__init__.py,sha256=FMTnEKOIHCywjnqGXfOfzZzYKmciue0h37DG2KZAUZI,1610
|
|
79
|
+
sqlspec/statement/builder/_ddl_utils.py,sha256=P0INvHoOA9Mk7rmrga0xRvxF6YgNXchsg1Q3hfvlCqQ,4326
|
|
80
|
+
sqlspec/statement/builder/_parsing_utils.py,sha256=xAxWfa-mjjLPyweM9396lQy91G5MsxYyaJzTAoEiuAU,5753
|
|
81
|
+
sqlspec/statement/builder/base.py,sha256=DArCuNwDXslt73Jk5Io3WqUP1ZXA0e4cUXtzFjvQ5g4,14738
|
|
82
|
+
sqlspec/statement/builder/column.py,sha256=JWeZ5hnawCWm00jeojwm8i79p8oYNkg30SIdOOolNjM,11521
|
|
83
|
+
sqlspec/statement/builder/ddl.py,sha256=vqAebkN6JoX6n8aLVwZdyW3Xfyj3Ws6frWu-_7wy8uE,51516
|
|
84
|
+
sqlspec/statement/builder/delete.py,sha256=5BVwxcNY5nSgeZrZ4tSfdA2di3X8M3y7I5t9D35d0mk,2881
|
|
85
|
+
sqlspec/statement/builder/insert.py,sha256=Q2ArKq4BPP69G-OTxcUVgixlhsKjdOm248RJxtJFyps,10129
|
|
86
|
+
sqlspec/statement/builder/merge.py,sha256=SUrumqMrUBO1vMFWll45vvYj4DmrL3WuCZLdHap8ej8,2783
|
|
87
|
+
sqlspec/statement/builder/select.py,sha256=4UXNo9_13XQIqmzzovsRQBAwooFtBdzsolUoTzxgNE4,8642
|
|
88
|
+
sqlspec/statement/builder/update.py,sha256=qV5frucca42xuzSeAdBAqNnYTRyEZJrhkZ5uEI1eGKs,6245
|
|
89
|
+
sqlspec/statement/builder/mixins/__init__.py,sha256=B5GhsdySb_-DN3SvBlA2oimmymAJX3Rf4A7Xnz3uNN4,2887
|
|
90
|
+
sqlspec/statement/builder/mixins/_aggregate_functions.py,sha256=yeXm_mp0JDbzSe2N2vpJHbGVYmdnrCMqwdHXz2rJAVs,10551
|
|
91
|
+
sqlspec/statement/builder/mixins/_case_builder.py,sha256=fGdevoEZxmoJjsDV9Wwfom8vu3lmgF9B_o5HXCf25Kg,3047
|
|
92
|
+
sqlspec/statement/builder/mixins/_common_table_expr.py,sha256=R7BUdWEHpuPGumgBsUnUPaVLH0sF3oRiJRw-xXEkTj8,3694
|
|
93
|
+
sqlspec/statement/builder/mixins/_delete_from.py,sha256=HsJseeYY-HUoiTYRdPpCDG0kIHrhXOg_GA52RT7dEGY,1019
|
|
94
|
+
sqlspec/statement/builder/mixins/_from.py,sha256=bGcI7TuY3Z3Xx8oLR9jXx_d25s0db0uJBx4SSw0kKh8,2850
|
|
95
|
+
sqlspec/statement/builder/mixins/_group_by.py,sha256=gLbveNJuvk_FdZxYKBzx4cCQQvxqzecc_vmb5d8_Du0,4142
|
|
96
|
+
sqlspec/statement/builder/mixins/_having.py,sha256=X8-hdwEeJg4RYxyjhaYLvlkpzE0AwIPl6t8SPGz5gi0,1112
|
|
97
|
+
sqlspec/statement/builder/mixins/_insert_from_select.py,sha256=RMvdNXU1W7zqceOGwo_G_n0bbd-S63bBwOCs44H0j4Y,1801
|
|
98
|
+
sqlspec/statement/builder/mixins/_insert_into.py,sha256=7979JCYqKM_NRTHwkrJJY6l9lS2AQ-evZYRU8fs3yw0,1049
|
|
99
|
+
sqlspec/statement/builder/mixins/_insert_values.py,sha256=5kheKKtdpOVHSIPox88bYwtD4Rr5v1_J_K9Wmam7-Vk,2820
|
|
100
|
+
sqlspec/statement/builder/mixins/_join.py,sha256=lSGFtq_KJ985SCmUKmIx-4NwniIwQIVmLJYryF5mtc4,5658
|
|
101
|
+
sqlspec/statement/builder/mixins/_limit_offset.py,sha256=qLxBek2CpJz-k717ZTpJ5eVlQJ5McZPAQEwFbRM-XuI,1753
|
|
102
|
+
sqlspec/statement/builder/mixins/_merge_clauses.py,sha256=Tmi9Yb_7BPtFtpxj3splUTNQfjS8TdcN8TY9qJQqNNU,16798
|
|
103
|
+
sqlspec/statement/builder/mixins/_order_by.py,sha256=1yMXkl9MYd5uk3RRMWxhR4VCmvHGvwY6bojofFt6jc0,1717
|
|
104
|
+
sqlspec/statement/builder/mixins/_pivot.py,sha256=mnGNCZYbl00JAny6h9p-EQkhGNiNN8w--ZNd7X0Fs8I,3257
|
|
105
|
+
sqlspec/statement/builder/mixins/_returning.py,sha256=O39j2MEIcqiiUhFFGnyGwIXhLS7KrGigfZ9kPxFjjtI,1349
|
|
106
|
+
sqlspec/statement/builder/mixins/_select_columns.py,sha256=wVUg9ECJH6r1QEh7VWV6DGpb86oo4vuFHPpOx_G8Hcw,2421
|
|
107
|
+
sqlspec/statement/builder/mixins/_set_ops.py,sha256=uWV32ZAi2tojbS9b6Q0ZDIn1Rhbx4cCE42jhbVdm90I,5291
|
|
108
|
+
sqlspec/statement/builder/mixins/_unpivot.py,sha256=99tN2skDD3M77v7MyqisugxHR-6BB984FLX7iY4RpG0,3086
|
|
109
|
+
sqlspec/statement/builder/mixins/_update_from.py,sha256=NAHiL08NBoh8jFxZG_ZiSjbEDcIR9ni7-d--nVIXf6g,2540
|
|
110
|
+
sqlspec/statement/builder/mixins/_update_set.py,sha256=gLcGeBMSHqCGPrJz-pb5951g-4xCLJDltOzbMM1OQRM,4306
|
|
111
|
+
sqlspec/statement/builder/mixins/_update_table.py,sha256=FX3KezuHdu7YLQI4i5iniHWk1Lbzph8MzLcOM6iTd70,936
|
|
112
|
+
sqlspec/statement/builder/mixins/_where.py,sha256=kfttMYubr4zzOJkbxTh-U3lTt5uNFgkmjCEixs6n9LM,21227
|
|
113
|
+
sqlspec/statement/builder/mixins/_window_functions.py,sha256=8ldy06gK6jOlGg31oa1dCrG7IkfCVYvVr_f-lx0YcgU,3745
|
|
114
|
+
sqlspec/statement/pipelines/__init__.py,sha256=eFCKEmMqQAyDwfg9_kgE5e719OSC2nyhTme4IGmxb70,7326
|
|
115
|
+
sqlspec/statement/pipelines/context.py,sha256=mE7twvOydqyWQffp6KyMvOUl9FlTY9ypGL7M6SFYFK8,4210
|
|
116
|
+
sqlspec/statement/pipelines/analyzers/__init__.py,sha256=RY7W0AiWG-8qdrTmRSGlEofjrPPJCJUnNK-LRukKt5Q,330
|
|
117
|
+
sqlspec/statement/pipelines/analyzers/_analyzer.py,sha256=0_FDty6H_4JvQwee_NrzmptX9-VdhyAePXUstuq4MU4,27474
|
|
118
|
+
sqlspec/statement/pipelines/transformers/__init__.py,sha256=Kh4CE_izdwEsSF7Is6_6NXqtNmbLm5x5OWdnlUGp9ok,480
|
|
119
|
+
sqlspec/statement/pipelines/transformers/_expression_simplifier.py,sha256=0R2j046n12DDk1wGEmwX3f3KnaMUcSDWC0-lRmforjw,3385
|
|
120
|
+
sqlspec/statement/pipelines/transformers/_literal_parameterizer.py,sha256=N6a2lMGBHXcoHuSySY_9JhObNkPWabUBpR_25ZCnv0w,57183
|
|
121
|
+
sqlspec/statement/pipelines/transformers/_remove_comments_and_hints.py,sha256=-gM6VnlhNtQzSbQq44hQ5mhaPqNsjzeCaTgXTycgkOk,3008
|
|
122
|
+
sqlspec/statement/pipelines/validators/__init__.py,sha256=cdlwdLhOT2OwTbueIsA7bfRG2b6y-j7dw9pMzl5AP0M,747
|
|
123
|
+
sqlspec/statement/pipelines/validators/_dml_safety.py,sha256=s7Eee60u6w0K35vz4GVAwsB5Xy8zUqWhiIx_pRVfERo,10585
|
|
124
|
+
sqlspec/statement/pipelines/validators/_parameter_style.py,sha256=m8gNydG0KKyhmQyOEGZk8mhV_jLjT0vxIJy22AY8xkY,17840
|
|
125
|
+
sqlspec/statement/pipelines/validators/_performance.py,sha256=mTL5UB9Yj2XNjv31_CjqWHUAdj77iFtE7XDXQosG_20,26357
|
|
126
|
+
sqlspec/statement/pipelines/validators/_security.py,sha256=3WuH9n4j-VKOgmQnZvFIoI4MieRBVShtOZvXl2SJsyk,42103
|
|
127
|
+
sqlspec/storage/__init__.py,sha256=ZS8rmhN8e_h_df-Oab64Jh4EbddElBbmrtHkuO-KqOE,703
|
|
128
|
+
sqlspec/storage/capabilities.py,sha256=fsnQ9jbjHBFvd3P9yWAzfjPpVYUGcsTtR6fm5iOCvXY,3054
|
|
129
|
+
sqlspec/storage/registry.py,sha256=lv9pGt6vt0tDNAlao9qA7rSsFsCZtaOr48aG15IoMFA,10976
|
|
130
|
+
sqlspec/storage/backends/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
131
|
+
sqlspec/storage/backends/base.py,sha256=34XYQuz1tQ-q_ZZwOz-Kimgvl088SR3VUYJoA-STslc,6350
|
|
132
|
+
sqlspec/storage/backends/fsspec.py,sha256=wo40MwtEjNTc73ZNTCmNC2-Img0zldUCaFGh9H3BZuE,14715
|
|
133
|
+
sqlspec/storage/backends/obstore.py,sha256=FnoVzTzzNn5NRXR8GgETqD3Rq9C5EWAmfWmY4JDFVJE,21214
|
|
134
|
+
sqlspec/utils/__init__.py,sha256=_Ya8IZuc2cZIstXr_xjgnSfxICXHXvu5mfWsi2USDrw,183
|
|
135
|
+
sqlspec/utils/cached_property.py,sha256=Sw_JZxfChSJu72gvYBJPF-89FyyIJAPf-1ETlDq-F2E,648
|
|
136
|
+
sqlspec/utils/correlation.py,sha256=4jqpjMivxI1tQ13Ed-P0hLdFHUKUQ9GlvIM69_HOlS4,4370
|
|
137
|
+
sqlspec/utils/deprecation.py,sha256=zrmb_eKRlLWVA6dWrjUbN3Vz6D3_-Z_15Ixk4H-sDRk,3850
|
|
138
|
+
sqlspec/utils/fixtures.py,sha256=q_Pghpmw2VgJ9P0TfkyjSF5PvdaD5Y2Laha0Bj4IDrA,1838
|
|
139
|
+
sqlspec/utils/logging.py,sha256=56a5tqx4jfTqm20WDyj5c7Dy-h_O0KambIKOMr1-Oms,3780
|
|
140
|
+
sqlspec/utils/module_loader.py,sha256=9LcmEhy4T0jgkCaDVkxX47PSgJOMeJ8IV67yXEWBp-U,3074
|
|
141
|
+
sqlspec/utils/serializers.py,sha256=TKsRryRcYMnb8Z8MGkYGClIxcYvC8CW7MsrPQTJqEcY,154
|
|
142
|
+
sqlspec/utils/singleton.py,sha256=KZ7481tlDAxq6gcAlpULVqPLNc9P0XkHOEp7hfWIHcI,1096
|
|
143
|
+
sqlspec/utils/sync_tools.py,sha256=ckP1_uLh40C2pDvdc4FsR4NkmZVa1zEB8jhUxPLIuAI,8726
|
|
144
|
+
sqlspec/utils/text.py,sha256=DpEnRuSDv3acp4VQQGEOQixlJnLGZsN5YBws4rkI6t0,4756
|
|
145
|
+
sqlspec/utils/type_guards.py,sha256=uEtfznkkumiy2lnPOde35Ylzq2RrcPB-MBiWseMCADg,25722
|
|
146
|
+
sqlspec-0.13.0.dist-info/METADATA,sha256=f0OluI8DRpMFrFoTmpLoHZIdlKaarOiYqHNLdTydQpE,16663
|
|
147
|
+
sqlspec-0.13.0.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
|
|
148
|
+
sqlspec-0.13.0.dist-info/licenses/LICENSE,sha256=MdujfZ6l5HuLz4mElxlu049itenOR3gnhN1_Nd3nVcM,1078
|
|
149
|
+
sqlspec-0.13.0.dist-info/licenses/NOTICE,sha256=Lyir8ozXWov7CyYS4huVaOCNrtgL17P-bNV-5daLntQ,1634
|
|
150
|
+
sqlspec-0.13.0.dist-info/RECORD,,
|
|
@@ -1,20 +0,0 @@
|
|
|
1
|
-
from typing import Any, Optional, Protocol, Union
|
|
2
|
-
|
|
3
|
-
from sqlglot import exp
|
|
4
|
-
from typing_extensions import Self
|
|
5
|
-
|
|
6
|
-
__all__ = ("BuilderProtocol", "SelectBuilderProtocol")
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
class BuilderProtocol(Protocol):
|
|
10
|
-
_expression: Optional[exp.Expression]
|
|
11
|
-
_parameters: dict[str, Any]
|
|
12
|
-
_parameter_counter: int
|
|
13
|
-
dialect: Any
|
|
14
|
-
dialect_name: Optional[str]
|
|
15
|
-
|
|
16
|
-
def add_parameter(self, value: Any, name: Optional[str] = None) -> tuple[Any, str]: ...
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
class SelectBuilderProtocol(BuilderProtocol, Protocol):
|
|
20
|
-
def select(self, *columns: Union[str, exp.Expression]) -> Self: ...
|
|
@@ -1,315 +0,0 @@
|
|
|
1
|
-
"""SQL Processing Pipeline Base.
|
|
2
|
-
|
|
3
|
-
This module defines the core framework for constructing and executing a series of
|
|
4
|
-
SQL processing steps, such as transformations and validations.
|
|
5
|
-
"""
|
|
6
|
-
|
|
7
|
-
import contextlib
|
|
8
|
-
from abc import ABC, abstractmethod
|
|
9
|
-
from typing import TYPE_CHECKING, Optional
|
|
10
|
-
|
|
11
|
-
import sqlglot # Added
|
|
12
|
-
from sqlglot import exp
|
|
13
|
-
from sqlglot.errors import ParseError as SQLGlotParseError # Added
|
|
14
|
-
from typing_extensions import TypeVar
|
|
15
|
-
|
|
16
|
-
from sqlspec.exceptions import RiskLevel, SQLValidationError
|
|
17
|
-
from sqlspec.statement.pipelines.context import PipelineResult
|
|
18
|
-
from sqlspec.statement.pipelines.result_types import ValidationError
|
|
19
|
-
from sqlspec.utils.correlation import CorrelationContext
|
|
20
|
-
from sqlspec.utils.logging import get_logger
|
|
21
|
-
|
|
22
|
-
if TYPE_CHECKING:
|
|
23
|
-
from collections.abc import Sequence
|
|
24
|
-
|
|
25
|
-
from sqlglot.dialects.dialect import DialectType
|
|
26
|
-
|
|
27
|
-
from sqlspec.statement.pipelines.context import SQLProcessingContext
|
|
28
|
-
from sqlspec.statement.sql import SQLConfig, Statement
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
__all__ = ("ProcessorProtocol", "SQLValidator", "StatementPipeline", "UsesExpression")
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
logger = get_logger("pipelines")
|
|
35
|
-
|
|
36
|
-
ExpressionT = TypeVar("ExpressionT", bound="exp.Expression")
|
|
37
|
-
ResultT = TypeVar("ResultT")
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
# Copied UsesExpression class here
|
|
41
|
-
class UsesExpression:
|
|
42
|
-
"""Utility mixin class to get a sqlglot expression from various inputs."""
|
|
43
|
-
|
|
44
|
-
@staticmethod
|
|
45
|
-
def get_expression(statement: "Statement", dialect: "DialectType" = None) -> "exp.Expression":
|
|
46
|
-
"""Convert SQL input to expression.
|
|
47
|
-
|
|
48
|
-
Args:
|
|
49
|
-
statement: The SQL statement to convert to an expression.
|
|
50
|
-
dialect: The SQL dialect.
|
|
51
|
-
|
|
52
|
-
Raises:
|
|
53
|
-
SQLValidationError: If the SQL parsing fails.
|
|
54
|
-
|
|
55
|
-
Returns:
|
|
56
|
-
An exp.Expression.
|
|
57
|
-
"""
|
|
58
|
-
if isinstance(statement, exp.Expression):
|
|
59
|
-
return statement
|
|
60
|
-
|
|
61
|
-
# Local import to avoid circular dependency at module level
|
|
62
|
-
from sqlspec.statement.sql import SQL
|
|
63
|
-
|
|
64
|
-
if isinstance(statement, SQL):
|
|
65
|
-
expr = statement.expression
|
|
66
|
-
if expr is not None:
|
|
67
|
-
return expr
|
|
68
|
-
return sqlglot.parse_one(statement.sql, read=dialect)
|
|
69
|
-
|
|
70
|
-
# Assuming statement is str hereafter
|
|
71
|
-
sql_str = str(statement)
|
|
72
|
-
if not sql_str or not sql_str.strip():
|
|
73
|
-
return exp.Select()
|
|
74
|
-
|
|
75
|
-
try:
|
|
76
|
-
return sqlglot.parse_one(sql_str, read=dialect)
|
|
77
|
-
except SQLGlotParseError as e:
|
|
78
|
-
msg = f"SQL parsing failed: {e}"
|
|
79
|
-
raise SQLValidationError(msg, sql_str, RiskLevel.HIGH) from e
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
class ProcessorProtocol(ABC):
|
|
83
|
-
"""Defines the interface for a single processing step in the SQL pipeline."""
|
|
84
|
-
|
|
85
|
-
@abstractmethod
|
|
86
|
-
def process(
|
|
87
|
-
self, expression: "Optional[exp.Expression]", context: "SQLProcessingContext"
|
|
88
|
-
) -> "Optional[exp.Expression]":
|
|
89
|
-
"""Processes an SQL expression.
|
|
90
|
-
|
|
91
|
-
Args:
|
|
92
|
-
expression: The SQL expression to process.
|
|
93
|
-
context: The SQLProcessingContext holding the current state and config.
|
|
94
|
-
|
|
95
|
-
Returns:
|
|
96
|
-
The (possibly modified) SQL expression for transformers, or None for validators/analyzers.
|
|
97
|
-
"""
|
|
98
|
-
raise NotImplementedError
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
class StatementPipeline:
|
|
102
|
-
"""Orchestrates the processing of an SQL expression through transformers, validators, and analyzers."""
|
|
103
|
-
|
|
104
|
-
def __init__(
|
|
105
|
-
self,
|
|
106
|
-
transformers: Optional[list[ProcessorProtocol]] = None,
|
|
107
|
-
validators: Optional[list[ProcessorProtocol]] = None,
|
|
108
|
-
analyzers: Optional[list[ProcessorProtocol]] = None,
|
|
109
|
-
) -> None:
|
|
110
|
-
self.transformers = transformers or []
|
|
111
|
-
self.validators = validators or []
|
|
112
|
-
self.analyzers = analyzers or []
|
|
113
|
-
|
|
114
|
-
def execute_pipeline(self, context: "SQLProcessingContext") -> "PipelineResult":
|
|
115
|
-
"""Executes the full pipeline (transform, validate, analyze) using the SQLProcessingContext."""
|
|
116
|
-
CorrelationContext.get()
|
|
117
|
-
if context.current_expression is None:
|
|
118
|
-
if context.config.enable_parsing:
|
|
119
|
-
try:
|
|
120
|
-
context.current_expression = sqlglot.parse_one(context.initial_sql_string, dialect=context.dialect)
|
|
121
|
-
except Exception as e:
|
|
122
|
-
error = ValidationError(
|
|
123
|
-
message=f"SQL Parsing Error: {e}",
|
|
124
|
-
code="parsing-error",
|
|
125
|
-
risk_level=RiskLevel.CRITICAL,
|
|
126
|
-
processor="StatementPipeline",
|
|
127
|
-
expression=None,
|
|
128
|
-
)
|
|
129
|
-
context.validation_errors.append(error)
|
|
130
|
-
|
|
131
|
-
return PipelineResult(expression=exp.Select(), context=context)
|
|
132
|
-
else:
|
|
133
|
-
# If parsing is disabled and no expression given, it's a config error for the pipeline.
|
|
134
|
-
# However, SQL._initialize_statement should have handled this by not calling the pipeline
|
|
135
|
-
# or by ensuring current_expression is set if enable_parsing is false.
|
|
136
|
-
# For safety, we can raise or create an error result.
|
|
137
|
-
|
|
138
|
-
error = ValidationError(
|
|
139
|
-
message="Pipeline executed without an initial expression and parsing disabled.",
|
|
140
|
-
code="no-expression",
|
|
141
|
-
risk_level=RiskLevel.CRITICAL,
|
|
142
|
-
processor="StatementPipeline",
|
|
143
|
-
expression=None,
|
|
144
|
-
)
|
|
145
|
-
context.validation_errors.append(error)
|
|
146
|
-
|
|
147
|
-
return PipelineResult(
|
|
148
|
-
expression=exp.Select(), # Default empty expression
|
|
149
|
-
context=context,
|
|
150
|
-
)
|
|
151
|
-
|
|
152
|
-
# 1. Transformation Stage
|
|
153
|
-
if context.config.enable_transformations:
|
|
154
|
-
for transformer in self.transformers:
|
|
155
|
-
transformer_name = transformer.__class__.__name__
|
|
156
|
-
try:
|
|
157
|
-
if context.current_expression is not None:
|
|
158
|
-
context.current_expression = transformer.process(context.current_expression, context)
|
|
159
|
-
except Exception as e:
|
|
160
|
-
# Log transformation failure as a validation error
|
|
161
|
-
|
|
162
|
-
error = ValidationError(
|
|
163
|
-
message=f"Transformer {transformer_name} failed: {e}",
|
|
164
|
-
code="transformer-failure",
|
|
165
|
-
risk_level=RiskLevel.CRITICAL,
|
|
166
|
-
processor=transformer_name,
|
|
167
|
-
expression=context.current_expression,
|
|
168
|
-
)
|
|
169
|
-
context.validation_errors.append(error)
|
|
170
|
-
logger.exception("Transformer %s failed", transformer_name)
|
|
171
|
-
break
|
|
172
|
-
|
|
173
|
-
# 2. Validation Stage
|
|
174
|
-
if context.config.enable_validation:
|
|
175
|
-
for validator_component in self.validators:
|
|
176
|
-
validator_name = validator_component.__class__.__name__
|
|
177
|
-
try:
|
|
178
|
-
# Validators process and add errors to context
|
|
179
|
-
if context.current_expression is not None:
|
|
180
|
-
validator_component.process(context.current_expression, context)
|
|
181
|
-
except Exception as e:
|
|
182
|
-
# Log validator failure
|
|
183
|
-
|
|
184
|
-
error = ValidationError(
|
|
185
|
-
message=f"Validator {validator_name} failed: {e}",
|
|
186
|
-
code="validator-failure",
|
|
187
|
-
risk_level=RiskLevel.CRITICAL,
|
|
188
|
-
processor=validator_name,
|
|
189
|
-
expression=context.current_expression,
|
|
190
|
-
)
|
|
191
|
-
context.validation_errors.append(error)
|
|
192
|
-
logger.exception("Validator %s failed", validator_name)
|
|
193
|
-
|
|
194
|
-
# 3. Analysis Stage
|
|
195
|
-
if context.config.enable_analysis and context.current_expression is not None:
|
|
196
|
-
for analyzer_component in self.analyzers:
|
|
197
|
-
analyzer_name = analyzer_component.__class__.__name__
|
|
198
|
-
try:
|
|
199
|
-
analyzer_component.process(context.current_expression, context)
|
|
200
|
-
except Exception as e:
|
|
201
|
-
error = ValidationError(
|
|
202
|
-
message=f"Analyzer {analyzer_name} failed: {e}",
|
|
203
|
-
code="analyzer-failure",
|
|
204
|
-
risk_level=RiskLevel.MEDIUM,
|
|
205
|
-
processor=analyzer_name,
|
|
206
|
-
expression=context.current_expression,
|
|
207
|
-
)
|
|
208
|
-
context.validation_errors.append(error)
|
|
209
|
-
logger.exception("Analyzer %s failed", analyzer_name)
|
|
210
|
-
|
|
211
|
-
return PipelineResult(expression=context.current_expression or exp.Select(), context=context)
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
class SQLValidator(ProcessorProtocol, UsesExpression):
|
|
215
|
-
"""Main SQL validator that orchestrates multiple validation checks.
|
|
216
|
-
This class functions as a validation pipeline runner.
|
|
217
|
-
"""
|
|
218
|
-
|
|
219
|
-
def __init__(
|
|
220
|
-
self,
|
|
221
|
-
validators: "Optional[Sequence[ProcessorProtocol]]" = None,
|
|
222
|
-
min_risk_to_raise: "Optional[RiskLevel]" = RiskLevel.HIGH,
|
|
223
|
-
) -> None:
|
|
224
|
-
self.validators: list[ProcessorProtocol] = list(validators) if validators is not None else []
|
|
225
|
-
self.min_risk_to_raise = min_risk_to_raise
|
|
226
|
-
|
|
227
|
-
def add_validator(self, validator: "ProcessorProtocol") -> None:
|
|
228
|
-
"""Add a validator to the pipeline."""
|
|
229
|
-
self.validators.append(validator)
|
|
230
|
-
|
|
231
|
-
def process(
|
|
232
|
-
self, expression: "Optional[exp.Expression]", context: "SQLProcessingContext"
|
|
233
|
-
) -> "Optional[exp.Expression]":
|
|
234
|
-
"""Process the expression through all configured validators.
|
|
235
|
-
|
|
236
|
-
Args:
|
|
237
|
-
expression: The SQL expression to validate.
|
|
238
|
-
context: The SQLProcessingContext holding the current state and config.
|
|
239
|
-
|
|
240
|
-
Returns:
|
|
241
|
-
The expression unchanged (validators don't transform).
|
|
242
|
-
"""
|
|
243
|
-
if expression is None:
|
|
244
|
-
return None
|
|
245
|
-
|
|
246
|
-
if not context.config.enable_validation:
|
|
247
|
-
# Skip validation - add a skip marker to context
|
|
248
|
-
return expression
|
|
249
|
-
|
|
250
|
-
self._run_validators(expression, context)
|
|
251
|
-
return expression
|
|
252
|
-
|
|
253
|
-
@staticmethod
|
|
254
|
-
def _validate_safely(
|
|
255
|
-
validator_instance: "ProcessorProtocol", expression: "exp.Expression", context: "SQLProcessingContext"
|
|
256
|
-
) -> None:
|
|
257
|
-
try:
|
|
258
|
-
validator_instance.process(expression, context)
|
|
259
|
-
except Exception as e:
|
|
260
|
-
# Add error to context
|
|
261
|
-
|
|
262
|
-
error = ValidationError(
|
|
263
|
-
message=f"Validator {validator_instance.__class__.__name__} error: {e}",
|
|
264
|
-
code="validator-error",
|
|
265
|
-
risk_level=RiskLevel.CRITICAL,
|
|
266
|
-
processor=validator_instance.__class__.__name__,
|
|
267
|
-
expression=expression,
|
|
268
|
-
)
|
|
269
|
-
context.validation_errors.append(error)
|
|
270
|
-
logger.warning("Individual validator %s failed: %s", validator_instance.__class__.__name__, e)
|
|
271
|
-
|
|
272
|
-
def _run_validators(self, expression: "Optional[exp.Expression]", context: "SQLProcessingContext") -> None:
|
|
273
|
-
"""Run all validators and handle exceptions."""
|
|
274
|
-
if not expression:
|
|
275
|
-
# If no expression, nothing to validate
|
|
276
|
-
return
|
|
277
|
-
for validator_instance in self.validators:
|
|
278
|
-
self._validate_safely(validator_instance, expression, context)
|
|
279
|
-
|
|
280
|
-
def validate(
|
|
281
|
-
self, sql: "Statement", dialect: "DialectType", config: "Optional[SQLConfig]" = None
|
|
282
|
-
) -> "list[ValidationError]":
|
|
283
|
-
"""Convenience method to validate a raw SQL string or expression.
|
|
284
|
-
|
|
285
|
-
Returns:
|
|
286
|
-
List of ValidationError objects found during validation.
|
|
287
|
-
"""
|
|
288
|
-
from sqlspec.statement.pipelines.context import SQLProcessingContext # Local import for context
|
|
289
|
-
from sqlspec.statement.sql import SQLConfig # Local import for SQL.to_expression
|
|
290
|
-
|
|
291
|
-
current_config = config or SQLConfig()
|
|
292
|
-
expression_to_validate = self.get_expression(sql, dialect=dialect)
|
|
293
|
-
|
|
294
|
-
# Create a context for this validation run
|
|
295
|
-
validation_context = SQLProcessingContext(
|
|
296
|
-
initial_sql_string=str(sql),
|
|
297
|
-
dialect=dialect,
|
|
298
|
-
config=current_config,
|
|
299
|
-
current_expression=expression_to_validate,
|
|
300
|
-
initial_expression=expression_to_validate,
|
|
301
|
-
# Other context fields like parameters might not be strictly necessary for all validators
|
|
302
|
-
# but good to pass if available or if validators might need them.
|
|
303
|
-
# For a standalone validate() call, parameter context might be minimal.
|
|
304
|
-
input_sql_had_placeholders=False, # Assume false for raw validation, or detect
|
|
305
|
-
)
|
|
306
|
-
if isinstance(sql, str):
|
|
307
|
-
with contextlib.suppress(Exception):
|
|
308
|
-
param_val = current_config.parameter_validator
|
|
309
|
-
if param_val.extract_parameters(sql):
|
|
310
|
-
validation_context.input_sql_had_placeholders = True
|
|
311
|
-
|
|
312
|
-
self.process(expression_to_validate, validation_context)
|
|
313
|
-
|
|
314
|
-
# Return the list of validation errors
|
|
315
|
-
return list(validation_context.validation_errors)
|
|
@@ -1,41 +0,0 @@
|
|
|
1
|
-
"""Specific result types for the SQL processing pipeline."""
|
|
2
|
-
|
|
3
|
-
from dataclasses import dataclass
|
|
4
|
-
from typing import TYPE_CHECKING, Any, Optional
|
|
5
|
-
|
|
6
|
-
if TYPE_CHECKING:
|
|
7
|
-
from sqlglot import exp
|
|
8
|
-
|
|
9
|
-
from sqlspec.exceptions import RiskLevel
|
|
10
|
-
|
|
11
|
-
__all__ = ("AnalysisFinding", "TransformationLog", "ValidationError")
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
@dataclass
|
|
15
|
-
class ValidationError:
|
|
16
|
-
"""A specific validation issue found during processing."""
|
|
17
|
-
|
|
18
|
-
message: str
|
|
19
|
-
code: str # e.g., "risky-delete", "missing-where"
|
|
20
|
-
risk_level: "RiskLevel"
|
|
21
|
-
processor: str # Which processor found it
|
|
22
|
-
expression: "Optional[exp.Expression]" = None # Problematic sub-expression
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
@dataclass
|
|
26
|
-
class AnalysisFinding:
|
|
27
|
-
"""Metadata discovered during analysis."""
|
|
28
|
-
|
|
29
|
-
key: str # e.g., "complexity_score", "table_count"
|
|
30
|
-
value: Any
|
|
31
|
-
processor: str
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
@dataclass
|
|
35
|
-
class TransformationLog:
|
|
36
|
-
"""Record of a transformation applied."""
|
|
37
|
-
|
|
38
|
-
description: str
|
|
39
|
-
processor: str
|
|
40
|
-
before: Optional[str] = None # SQL before transform
|
|
41
|
-
after: Optional[str] = None # SQL after transform
|
|
@@ -1,66 +0,0 @@
|
|
|
1
|
-
from typing import Optional
|
|
2
|
-
|
|
3
|
-
from sqlglot import exp
|
|
4
|
-
|
|
5
|
-
from sqlspec.statement.pipelines.base import ProcessorProtocol
|
|
6
|
-
from sqlspec.statement.pipelines.context import SQLProcessingContext
|
|
7
|
-
|
|
8
|
-
__all__ = ("CommentRemover",)
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
class CommentRemover(ProcessorProtocol):
|
|
12
|
-
"""Removes standard SQL comments from expressions using SQLGlot's AST traversal.
|
|
13
|
-
|
|
14
|
-
This transformer removes SQL comments while preserving functionality:
|
|
15
|
-
- Removes line comments (-- comment)
|
|
16
|
-
- Removes block comments (/* comment */)
|
|
17
|
-
- Preserves string literals that contain comment-like patterns
|
|
18
|
-
- Always preserves SQL hints and MySQL version comments (use HintRemover separately)
|
|
19
|
-
- Uses SQLGlot's AST for reliable, context-aware comment detection
|
|
20
|
-
|
|
21
|
-
Note: This transformer now focuses only on standard comments. Use HintRemover
|
|
22
|
-
separately if you need to remove Oracle hints (/*+ hint */) or MySQL version
|
|
23
|
-
comments (/*!50000 */).
|
|
24
|
-
|
|
25
|
-
Args:
|
|
26
|
-
enabled: Whether comment removal is enabled.
|
|
27
|
-
"""
|
|
28
|
-
|
|
29
|
-
def __init__(self, enabled: bool = True) -> None:
|
|
30
|
-
self.enabled = enabled
|
|
31
|
-
|
|
32
|
-
def process(self, expression: Optional[exp.Expression], context: SQLProcessingContext) -> Optional[exp.Expression]:
|
|
33
|
-
"""Process the expression to remove comments using SQLGlot AST traversal."""
|
|
34
|
-
if not self.enabled or expression is None or context.current_expression is None:
|
|
35
|
-
return expression
|
|
36
|
-
|
|
37
|
-
comments_removed_count = 0
|
|
38
|
-
|
|
39
|
-
def _remove_comments(node: exp.Expression) -> "Optional[exp.Expression]":
|
|
40
|
-
nonlocal comments_removed_count
|
|
41
|
-
if hasattr(node, "comments") and node.comments:
|
|
42
|
-
original_comment_count = len(node.comments)
|
|
43
|
-
comments_to_keep = []
|
|
44
|
-
|
|
45
|
-
for comment in node.comments:
|
|
46
|
-
comment_text = str(comment).strip()
|
|
47
|
-
hint_keywords = ["INDEX", "USE_NL", "USE_HASH", "PARALLEL", "FULL", "FIRST_ROWS", "ALL_ROWS"]
|
|
48
|
-
is_hint = any(keyword in comment_text.upper() for keyword in hint_keywords)
|
|
49
|
-
|
|
50
|
-
if is_hint or (comment_text.startswith("!") and comment_text.endswith("")):
|
|
51
|
-
comments_to_keep.append(comment)
|
|
52
|
-
|
|
53
|
-
if len(comments_to_keep) < original_comment_count:
|
|
54
|
-
comments_removed_count += original_comment_count - len(comments_to_keep)
|
|
55
|
-
node.pop_comments()
|
|
56
|
-
if comments_to_keep:
|
|
57
|
-
node.add_comments(comments_to_keep)
|
|
58
|
-
|
|
59
|
-
return node
|
|
60
|
-
|
|
61
|
-
cleaned_expression = context.current_expression.transform(_remove_comments, copy=True)
|
|
62
|
-
context.current_expression = cleaned_expression
|
|
63
|
-
|
|
64
|
-
context.metadata["comments_removed"] = comments_removed_count
|
|
65
|
-
|
|
66
|
-
return cleaned_expression
|