From 441fd35058b4b0e8ef2f01cdd54f726347617aba Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 2 Jan 2026 13:18:51 +0000 Subject: [PATCH 1/9] Bump sqlalchemy from 1.4.52 to 2.0.45 Bumps [sqlalchemy](https://github.com/sqlalchemy/sqlalchemy) from 1.4.52 to 2.0.45. - [Release notes](https://github.com/sqlalchemy/sqlalchemy/releases) - [Changelog](https://github.com/sqlalchemy/sqlalchemy/blob/main/CHANGES.rst) - [Commits](https://github.com/sqlalchemy/sqlalchemy/commits) --- updated-dependencies: - dependency-name: sqlalchemy dependency-version: 2.0.45 dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] --- poetry.lock | 141 ++++++++++++++++++++++++++----------------------- pyproject.toml | 2 +- 2 files changed, 77 insertions(+), 66 deletions(-) diff --git a/poetry.lock b/poetry.lock index 43a7ab2..69184ec 100644 --- a/poetry.lock +++ b/poetry.lock @@ -466,7 +466,7 @@ description = "Lightweight in-process concurrent programming" optional = true python-versions = ">=3.7" groups = ["main"] -markers = "(platform_machine == \"aarch64\" or platform_machine == \"ppc64le\" or platform_machine == \"x86_64\" or platform_machine == \"amd64\" or platform_machine == \"AMD64\" or platform_machine == \"win32\" or platform_machine == \"WIN32\") and extra == \"sqlalchemy\"" +markers = "extra == \"sqlalchemy\" and (platform_machine == \"aarch64\" or platform_machine == \"ppc64le\" or platform_machine == \"x86_64\" or platform_machine == \"amd64\" or platform_machine == \"AMD64\" or platform_machine == \"win32\" or platform_machine == \"WIN32\")" files = [ {file = "greenlet-3.0.3-cp310-cp310-macosx_11_0_universal2.whl", hash = "sha256:9da2bd29ed9e4f15955dd1595ad7bc9320308a3b766ef7f837e23ad4b4aac31a"}, {file = "greenlet-3.0.3-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d353cadd6083fdb056bb46ed07e4340b0869c305c8ca54ef9da3421acbdf6881"}, @@ -1102,84 +1102,95 @@ files = [ [[package]] name = "sqlalchemy" -version = "1.4.52" +version = "2.0.45" description = "Database Abstraction Library" optional = true -python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,>=2.7" +python-versions = ">=3.7" groups = ["main"] markers = "extra == \"sqlalchemy\"" files = [ - {file = "SQLAlchemy-1.4.52-cp310-cp310-macosx_11_0_x86_64.whl", hash = "sha256:f68016f9a5713684c1507cc37133c28035f29925c75c0df2f9d0f7571e23720a"}, - {file = "SQLAlchemy-1.4.52-cp310-cp310-manylinux1_x86_64.manylinux2010_x86_64.manylinux_2_12_x86_64.manylinux_2_5_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:24bb0f81fbbb13d737b7f76d1821ec0b117ce8cbb8ee5e8641ad2de41aa916d3"}, - {file = "SQLAlchemy-1.4.52-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e93983cc0d2edae253b3f2141b0a3fb07e41c76cd79c2ad743fc27eb79c3f6db"}, - {file = "SQLAlchemy-1.4.52-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:84e10772cfc333eb08d0b7ef808cd76e4a9a30a725fb62a0495877a57ee41d81"}, - {file = "SQLAlchemy-1.4.52-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:427988398d2902de042093d17f2b9619a5ebc605bf6372f7d70e29bde6736842"}, - {file = "SQLAlchemy-1.4.52-cp310-cp310-win32.whl", hash = "sha256:1296f2cdd6db09b98ceb3c93025f0da4835303b8ac46c15c2136e27ee4d18d94"}, - {file = "SQLAlchemy-1.4.52-cp310-cp310-win_amd64.whl", hash = "sha256:80e7f697bccc56ac6eac9e2df5c98b47de57e7006d2e46e1a3c17c546254f6ef"}, - {file = "SQLAlchemy-1.4.52-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:2f251af4c75a675ea42766880ff430ac33291c8d0057acca79710f9e5a77383d"}, - {file = "SQLAlchemy-1.4.52-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cb8f9e4c4718f111d7b530c4e6fb4d28f9f110eb82e7961412955b3875b66de0"}, - {file = "SQLAlchemy-1.4.52-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:afb1672b57f58c0318ad2cff80b384e816735ffc7e848d8aa51e0b0fc2f4b7bb"}, - {file = "SQLAlchemy-1.4.52-cp311-cp311-win32.whl", hash = "sha256:6e41cb5cda641f3754568d2ed8962f772a7f2b59403b95c60c89f3e0bd25f15e"}, - {file = "SQLAlchemy-1.4.52-cp311-cp311-win_amd64.whl", hash = "sha256:5bed4f8c3b69779de9d99eb03fd9ab67a850d74ab0243d1be9d4080e77b6af12"}, - {file = "SQLAlchemy-1.4.52-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:49e3772eb3380ac88d35495843daf3c03f094b713e66c7d017e322144a5c6b7c"}, - {file = "SQLAlchemy-1.4.52-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:618827c1a1c243d2540314c6e100aee7af09a709bd005bae971686fab6723554"}, - {file = "SQLAlchemy-1.4.52-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:de9acf369aaadb71a725b7e83a5ef40ca3de1cf4cdc93fa847df6b12d3cd924b"}, - {file = "SQLAlchemy-1.4.52-cp312-cp312-win32.whl", hash = "sha256:763bd97c4ebc74136ecf3526b34808c58945023a59927b416acebcd68d1fc126"}, - {file = "SQLAlchemy-1.4.52-cp312-cp312-win_amd64.whl", hash = "sha256:f12aaf94f4d9679ca475975578739e12cc5b461172e04d66f7a3c39dd14ffc64"}, - {file = "SQLAlchemy-1.4.52-cp36-cp36m-macosx_10_14_x86_64.whl", hash = "sha256:853fcfd1f54224ea7aabcf34b227d2b64a08cbac116ecf376907968b29b8e763"}, - {file = "SQLAlchemy-1.4.52-cp36-cp36m-manylinux1_x86_64.manylinux2010_x86_64.manylinux_2_12_x86_64.manylinux_2_5_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f98dbb8fcc6d1c03ae8ec735d3c62110949a3b8bc6e215053aa27096857afb45"}, - {file = "SQLAlchemy-1.4.52-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1e135fff2e84103bc15c07edd8569612ce317d64bdb391f49ce57124a73f45c5"}, - {file = "SQLAlchemy-1.4.52-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:5b5de6af8852500d01398f5047d62ca3431d1e29a331d0b56c3e14cb03f8094c"}, - {file = "SQLAlchemy-1.4.52-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3491c85df263a5c2157c594f54a1a9c72265b75d3777e61ee13c556d9e43ffc9"}, - {file = "SQLAlchemy-1.4.52-cp36-cp36m-win32.whl", hash = "sha256:427c282dd0deba1f07bcbf499cbcc9fe9a626743f5d4989bfdfd3ed3513003dd"}, - {file = "SQLAlchemy-1.4.52-cp36-cp36m-win_amd64.whl", hash = "sha256:ca5ce82b11731492204cff8845c5e8ca1a4bd1ade85e3b8fcf86e7601bfc6a39"}, - {file = "SQLAlchemy-1.4.52-cp37-cp37m-macosx_11_0_x86_64.whl", hash = "sha256:29d4247313abb2015f8979137fe65f4eaceead5247d39603cc4b4a610936cd2b"}, - {file = "SQLAlchemy-1.4.52-cp37-cp37m-manylinux1_x86_64.manylinux2010_x86_64.manylinux_2_12_x86_64.manylinux_2_5_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a752bff4796bf22803d052d4841ebc3c55c26fb65551f2c96e90ac7c62be763a"}, - {file = "SQLAlchemy-1.4.52-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f7ea11727feb2861deaa293c7971a4df57ef1c90e42cb53f0da40c3468388000"}, - {file = "SQLAlchemy-1.4.52-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:d913f8953e098ca931ad7f58797f91deed26b435ec3756478b75c608aa80d139"}, - {file = "SQLAlchemy-1.4.52-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a251146b921725547ea1735b060a11e1be705017b568c9f8067ca61e6ef85f20"}, - {file = "SQLAlchemy-1.4.52-cp37-cp37m-win32.whl", hash = "sha256:1f8e1c6a6b7f8e9407ad9afc0ea41c1f65225ce505b79bc0342159de9c890782"}, - {file = "SQLAlchemy-1.4.52-cp37-cp37m-win_amd64.whl", hash = "sha256:346ed50cb2c30f5d7a03d888e25744154ceac6f0e6e1ab3bc7b5b77138d37710"}, - {file = "SQLAlchemy-1.4.52-cp38-cp38-macosx_11_0_x86_64.whl", hash = "sha256:4dae6001457d4497736e3bc422165f107ecdd70b0d651fab7f731276e8b9e12d"}, - {file = "SQLAlchemy-1.4.52-cp38-cp38-manylinux1_x86_64.manylinux2010_x86_64.manylinux_2_12_x86_64.manylinux_2_5_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a5d2e08d79f5bf250afb4a61426b41026e448da446b55e4770c2afdc1e200fce"}, - {file = "SQLAlchemy-1.4.52-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5bbce5dd7c7735e01d24f5a60177f3e589078f83c8a29e124a6521b76d825b85"}, - {file = "SQLAlchemy-1.4.52-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:bdb7b4d889631a3b2a81a3347c4c3f031812eb4adeaa3ee4e6b0d028ad1852b5"}, - {file = "SQLAlchemy-1.4.52-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c294ae4e6bbd060dd79e2bd5bba8b6274d08ffd65b58d106394cb6abbf35cf45"}, - {file = "SQLAlchemy-1.4.52-cp38-cp38-win32.whl", hash = "sha256:bcdfb4b47fe04967669874fb1ce782a006756fdbebe7263f6a000e1db969120e"}, - {file = "SQLAlchemy-1.4.52-cp38-cp38-win_amd64.whl", hash = "sha256:7d0dbc56cb6af5088f3658982d3d8c1d6a82691f31f7b0da682c7b98fa914e91"}, - {file = "SQLAlchemy-1.4.52-cp39-cp39-macosx_11_0_x86_64.whl", hash = "sha256:a551d5f3dc63f096ed41775ceec72fdf91462bb95abdc179010dc95a93957800"}, - {file = "SQLAlchemy-1.4.52-cp39-cp39-manylinux1_x86_64.manylinux2010_x86_64.manylinux_2_12_x86_64.manylinux_2_5_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6ab773f9ad848118df7a9bbabca53e3f1002387cdbb6ee81693db808b82aaab0"}, - {file = "SQLAlchemy-1.4.52-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d2de46f5d5396d5331127cfa71f837cca945f9a2b04f7cb5a01949cf676db7d1"}, - {file = "SQLAlchemy-1.4.52-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:7027be7930a90d18a386b25ee8af30514c61f3852c7268899f23fdfbd3107181"}, - {file = "SQLAlchemy-1.4.52-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:99224d621affbb3c1a4f72b631f8393045f4ce647dd3262f12fe3576918f8bf3"}, - {file = "SQLAlchemy-1.4.52-cp39-cp39-win32.whl", hash = "sha256:c124912fd4e1bb9d1e7dc193ed482a9f812769cb1e69363ab68e01801e859821"}, - {file = "SQLAlchemy-1.4.52-cp39-cp39-win_amd64.whl", hash = "sha256:2c286fab42e49db23c46ab02479f328b8bdb837d3e281cae546cc4085c83b680"}, - {file = "SQLAlchemy-1.4.52.tar.gz", hash = "sha256:80e63bbdc5217dad3485059bdf6f65a7d43f33c8bde619df5c220edf03d87296"}, + {file = "sqlalchemy-2.0.45-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:c64772786d9eee72d4d3784c28f0a636af5b0a29f3fe26ff11f55efe90c0bd85"}, + {file = "sqlalchemy-2.0.45-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:7ae64ebf7657395824a19bca98ab10eb9a3ecb026bf09524014f1bb81cb598d4"}, + {file = "sqlalchemy-2.0.45-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0f02325709d1b1a1489f23a39b318e175a171497374149eae74d612634b234c0"}, + {file = "sqlalchemy-2.0.45-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:d2c3684fca8a05f0ac1d9a21c1f4a266983a7ea9180efb80ffeb03861ecd01a0"}, + {file = "sqlalchemy-2.0.45-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:040f6f0545b3b7da6b9317fc3e922c9a98fc7243b2a1b39f78390fc0942f7826"}, + {file = "sqlalchemy-2.0.45-cp310-cp310-win32.whl", hash = "sha256:830d434d609fe7bfa47c425c445a8b37929f140a7a44cdaf77f6d34df3a7296a"}, + {file = "sqlalchemy-2.0.45-cp310-cp310-win_amd64.whl", hash = "sha256:0209d9753671b0da74da2cfbb9ecf9c02f72a759e4b018b3ab35f244c91842c7"}, + {file = "sqlalchemy-2.0.45-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:2e90a344c644a4fa871eb01809c32096487928bd2038bf10f3e4515cb688cc56"}, + {file = "sqlalchemy-2.0.45-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b8c8b41b97fba5f62349aa285654230296829672fc9939cd7f35aab246d1c08b"}, + {file = "sqlalchemy-2.0.45-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:12c694ed6468333a090d2f60950e4250b928f457e4962389553d6ba5fe9951ac"}, + {file = "sqlalchemy-2.0.45-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:f7d27a1d977a1cfef38a0e2e1ca86f09c4212666ce34e6ae542f3ed0a33bc606"}, + {file = "sqlalchemy-2.0.45-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:d62e47f5d8a50099b17e2bfc1b0c7d7ecd8ba6b46b1507b58cc4f05eefc3bb1c"}, + {file = "sqlalchemy-2.0.45-cp311-cp311-win32.whl", hash = "sha256:3c5f76216e7b85770d5bb5130ddd11ee89f4d52b11783674a662c7dd57018177"}, + {file = "sqlalchemy-2.0.45-cp311-cp311-win_amd64.whl", hash = "sha256:a15b98adb7f277316f2c276c090259129ee4afca783495e212048daf846654b2"}, + {file = "sqlalchemy-2.0.45-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b3ee2aac15169fb0d45822983631466d60b762085bc4535cd39e66bea362df5f"}, + {file = "sqlalchemy-2.0.45-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:ba547ac0b361ab4f1608afbc8432db669bd0819b3e12e29fb5fa9529a8bba81d"}, + {file = "sqlalchemy-2.0.45-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:215f0528b914e5c75ef2559f69dca86878a3beeb0c1be7279d77f18e8d180ed4"}, + {file = "sqlalchemy-2.0.45-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:107029bf4f43d076d4011f1afb74f7c3e2ea029ec82eb23d8527d5e909e97aa6"}, + {file = "sqlalchemy-2.0.45-cp312-cp312-win32.whl", hash = "sha256:0c9f6ada57b58420a2c0277ff853abe40b9e9449f8d7d231763c6bc30f5c4953"}, + {file = "sqlalchemy-2.0.45-cp312-cp312-win_amd64.whl", hash = "sha256:8defe5737c6d2179c7997242d6473587c3beb52e557f5ef0187277009f73e5e1"}, + {file = "sqlalchemy-2.0.45-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:fe187fc31a54d7fd90352f34e8c008cf3ad5d064d08fedd3de2e8df83eb4a1cf"}, + {file = "sqlalchemy-2.0.45-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:672c45cae53ba88e0dad74b9027dddd09ef6f441e927786b05bec75d949fbb2e"}, + {file = "sqlalchemy-2.0.45-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:470daea2c1ce73910f08caf10575676a37159a6d16c4da33d0033546bddebc9b"}, + {file = "sqlalchemy-2.0.45-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:9c6378449e0940476577047150fd09e242529b761dc887c9808a9a937fe990c8"}, + {file = "sqlalchemy-2.0.45-cp313-cp313-win32.whl", hash = "sha256:4b6bec67ca45bc166c8729910bd2a87f1c0407ee955df110d78948f5b5827e8a"}, + {file = "sqlalchemy-2.0.45-cp313-cp313-win_amd64.whl", hash = "sha256:afbf47dc4de31fa38fd491f3705cac5307d21d4bb828a4f020ee59af412744ee"}, + {file = "sqlalchemy-2.0.45-cp313-cp313t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:83d7009f40ce619d483d26ac1b757dfe3167b39921379a8bd1b596cf02dab4a6"}, + {file = "sqlalchemy-2.0.45-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:d8a2ca754e5415cde2b656c27900b19d50ba076aa05ce66e2207623d3fe41f5a"}, + {file = "sqlalchemy-2.0.45-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:7f46ec744e7f51275582e6a24326e10c49fbdd3fc99103e01376841213028774"}, + {file = "sqlalchemy-2.0.45-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:883c600c345123c033c2f6caca18def08f1f7f4c3ebeb591a63b6fceffc95cce"}, + {file = "sqlalchemy-2.0.45-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:2c0b74aa79e2deade948fe8593654c8ef4228c44ba862bb7c9585c8e0db90f33"}, + {file = "sqlalchemy-2.0.45-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:8a420169cef179d4c9064365f42d779f1e5895ad26ca0c8b4c0233920973db74"}, + {file = "sqlalchemy-2.0.45-cp314-cp314-win32.whl", hash = "sha256:e50dcb81a5dfe4b7b4a4aa8f338116d127cb209559124f3694c70d6cd072b68f"}, + {file = "sqlalchemy-2.0.45-cp314-cp314-win_amd64.whl", hash = "sha256:4748601c8ea959e37e03d13dcda4a44837afcd1b21338e637f7c935b8da06177"}, + {file = "sqlalchemy-2.0.45-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:cd337d3526ec5298f67d6a30bbbe4ed7e5e68862f0bf6dd21d289f8d37b7d60b"}, + {file = "sqlalchemy-2.0.45-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:9a62b446b7d86a3909abbcd1cd3cc550a832f99c2bc37c5b22e1925438b9367b"}, + {file = "sqlalchemy-2.0.45-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:5964f832431b7cdfaaa22a660b4c7eb1dfcd6ed41375f67fd3e3440fd95cb3cc"}, + {file = "sqlalchemy-2.0.45-cp38-cp38-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ee580ab50e748208754ae8980cec79ec205983d8cf8b3f7c39067f3d9f2c8e22"}, + {file = "sqlalchemy-2.0.45-cp38-cp38-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:13e27397a7810163440c6bfed6b3fe46f1bfb2486eb540315a819abd2c004128"}, + {file = "sqlalchemy-2.0.45-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:ed3635353e55d28e7f4a95c8eda98a5cdc0a0b40b528433fbd41a9ae88f55b3d"}, + {file = "sqlalchemy-2.0.45-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:db6834900338fb13a9123307f0c2cbb1f890a8656fcd5e5448ae3ad5bbe8d312"}, + {file = "sqlalchemy-2.0.45-cp38-cp38-win32.whl", hash = "sha256:1d8b4a7a8c9b537509d56d5cd10ecdcfbb95912d72480c8861524efecc6a3fff"}, + {file = "sqlalchemy-2.0.45-cp38-cp38-win_amd64.whl", hash = "sha256:ebd300afd2b62679203435f596b2601adafe546cb7282d5a0cd3ed99e423720f"}, + {file = "sqlalchemy-2.0.45-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:d29b2b99d527dbc66dd87c3c3248a5dd789d974a507f4653c969999fc7c1191b"}, + {file = "sqlalchemy-2.0.45-cp39-cp39-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:59a8b8bd9c6bedf81ad07c8bd5543eedca55fe9b8780b2b628d495ba55f8db1e"}, + {file = "sqlalchemy-2.0.45-cp39-cp39-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:fd93c6f5d65f254ceabe97548c709e073d6da9883343adaa51bf1a913ce93f8e"}, + {file = "sqlalchemy-2.0.45-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:6d0beadc2535157070c9c17ecf25ecec31e13c229a8f69196d7590bde8082bf1"}, + {file = "sqlalchemy-2.0.45-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:e057f928ffe9c9b246a55b469c133b98a426297e1772ad24ce9f0c47d123bd5b"}, + {file = "sqlalchemy-2.0.45-cp39-cp39-win32.whl", hash = "sha256:c1c2091b1489435ff85728fafeb990f073e64f6f5e81d5cd53059773e8521eb6"}, + {file = "sqlalchemy-2.0.45-cp39-cp39-win_amd64.whl", hash = "sha256:56ead1f8dfb91a54a28cd1d072c74b3d635bcffbd25e50786533b822d4f2cde2"}, + {file = "sqlalchemy-2.0.45-py3-none-any.whl", hash = "sha256:5225a288e4c8cc2308dbdd874edad6e7d0fd38eac1e9e5f23503425c8eee20d0"}, + {file = "sqlalchemy-2.0.45.tar.gz", hash = "sha256:1632a4bda8d2d25703fdad6363058d882541bdaaee0e5e3ddfa0cd3229efce88"}, ] [package.dependencies] -greenlet = {version = "!=0.4.17", markers = "python_version >= \"3\" and (platform_machine == \"aarch64\" or platform_machine == \"ppc64le\" or platform_machine == \"x86_64\" or platform_machine == \"amd64\" or platform_machine == \"AMD64\" or platform_machine == \"win32\" or platform_machine == \"WIN32\")"} +greenlet = {version = ">=1", markers = "platform_machine == \"aarch64\" or platform_machine == \"ppc64le\" or platform_machine == \"x86_64\" or platform_machine == \"amd64\" or platform_machine == \"AMD64\" or platform_machine == \"win32\" or platform_machine == \"WIN32\""} +typing-extensions = ">=4.6.0" [package.extras] -aiomysql = ["aiomysql (>=0.2.0) ; python_version >= \"3\"", "greenlet (!=0.4.17) ; python_version >= \"3\""] -aiosqlite = ["aiosqlite ; python_version >= \"3\"", "greenlet (!=0.4.17) ; python_version >= \"3\"", "typing_extensions (!=3.10.0.1)"] -asyncio = ["greenlet (!=0.4.17) ; python_version >= \"3\""] -asyncmy = ["asyncmy (>=0.2.3,!=0.2.4) ; python_version >= \"3\"", "greenlet (!=0.4.17) ; python_version >= \"3\""] -mariadb-connector = ["mariadb (>=1.0.1,!=1.1.2) ; python_version >= \"3\""] +aiomysql = ["aiomysql (>=0.2.0)", "greenlet (>=1)"] +aioodbc = ["aioodbc", "greenlet (>=1)"] +aiosqlite = ["aiosqlite", "greenlet (>=1)", "typing_extensions (!=3.10.0.1)"] +asyncio = ["greenlet (>=1)"] +asyncmy = ["asyncmy (>=0.2.3,!=0.2.4,!=0.2.6)", "greenlet (>=1)"] +mariadb-connector = ["mariadb (>=1.0.1,!=1.1.2,!=1.1.5,!=1.1.10)"] mssql = ["pyodbc"] mssql-pymssql = ["pymssql"] mssql-pyodbc = ["pyodbc"] -mypy = ["mypy (>=0.910) ; python_version >= \"3\"", "sqlalchemy2-stubs"] -mysql = ["mysqlclient (>=1.4.0) ; python_version >= \"3\"", "mysqlclient (>=1.4.0,<2) ; python_version < \"3\""] +mypy = ["mypy (>=0.910)"] +mysql = ["mysqlclient (>=1.4.0)"] mysql-connector = ["mysql-connector-python"] -oracle = ["cx_oracle (>=7) ; python_version >= \"3\"", "cx_oracle (>=7,<8) ; python_version < \"3\""] +oracle = ["cx_oracle (>=8)"] +oracle-oracledb = ["oracledb (>=1.0.1)"] postgresql = ["psycopg2 (>=2.7)"] -postgresql-asyncpg = ["asyncpg ; python_version >= \"3\"", "greenlet (!=0.4.17) ; python_version >= \"3\""] -postgresql-pg8000 = ["pg8000 (>=1.16.6,!=1.29.0)"] +postgresql-asyncpg = ["asyncpg", "greenlet (>=1)"] +postgresql-pg8000 = ["pg8000 (>=1.29.1)"] +postgresql-psycopg = ["psycopg (>=3.0.7)"] postgresql-psycopg2binary = ["psycopg2-binary"] postgresql-psycopg2cffi = ["psycopg2cffi"] -pymysql = ["pymysql (<1) ; python_version < \"3\"", "pymysql ; python_version >= \"3\""] -sqlcipher = ["sqlcipher3_binary ; python_version >= \"3\""] +postgresql-psycopgbinary = ["psycopg[binary] (>=3.0.7)"] +pymysql = ["pymysql"] +sqlcipher = ["sqlcipher3_binary"] [[package]] name = "stack-data" @@ -1294,12 +1305,12 @@ version = "4.13.2" description = "Backported and Experimental Type Hints for Python 3.8+" optional = false python-versions = ">=3.8" -groups = ["dev"] -markers = "python_version == \"3.10\"" +groups = ["main", "dev"] files = [ {file = "typing_extensions-4.13.2-py3-none-any.whl", hash = "sha256:a439e7c04b49fec3e5d3e2beaa21755cadbbdc391694e28ccdd36ca4a1408f8c"}, {file = "typing_extensions-4.13.2.tar.gz", hash = "sha256:e6c81219bd689f51865d9e372991c540bda33a0379d5573cddb9a3a23f7caaef"}, ] +markers = {main = "extra == \"sqlalchemy\"", dev = "python_version == \"3.10\""} [[package]] name = "urllib3" @@ -1358,4 +1369,4 @@ sqlalchemy = ["requests", "sqlalchemy"] [metadata] lock-version = "2.1" python-versions = ">=3.10,<4" -content-hash = "6d9aaab357a1538dd4ddc37a60f2657f798b98677e3dcd4303f6453245efc228" +content-hash = "a634909525897a3809b651acc20cc65b76da09aa7ff3259c2936049f54652937" diff --git a/pyproject.toml b/pyproject.toml index 160e23d..f6e3b36 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -12,7 +12,7 @@ homepage = "https://github.com/python-pinot-dbapi/pinot-dbapi" python = ">=3.10,<4" ciso8601 = "^2.1.3" httpx = ">=0.28.1,<0.29" -sqlalchemy = {version = ">=1.4,<2", optional = true} +sqlalchemy = {version = ">=1.4,<3", optional = true} requests = "^2.25.0" # Pin urllib3 to a known-safe version (requests depends on urllib3 transitively). urllib3 = "==2.6.0" From 0e1d57bf2759c42021ca75d0bc8434e00336cb86 Mon Sep 17 00:00:00 2001 From: Xiang Fu Date: Fri, 2 Jan 2026 05:29:56 -0800 Subject: [PATCH 2/9] Fix examples for SQLAlchemy 2.0 compatibility --- examples/pinot_quickstart_auth_zk.py | 33 ++++++++++----- examples/pinot_quickstart_batch.py | 33 ++++++++++----- examples/pinot_quickstart_hybrid.py | 33 ++++++++++----- examples/pinot_quickstart_json_batch.py | 51 ++++++++++++++---------- examples/pinot_quickstart_multi_stage.py | 2 +- 5 files changed, 98 insertions(+), 54 deletions(-) diff --git a/examples/pinot_quickstart_auth_zk.py b/examples/pinot_quickstart_auth_zk.py index b074d12..d25032d 100644 --- a/examples/pinot_quickstart_auth_zk.py +++ b/examples/pinot_quickstart_auth_zk.py @@ -66,26 +66,37 @@ def run_pinot_quickstart_batch_sqlalchemy_example() -> None: # engine = create_engine('pinot+http://localhost:8000/query/sql?controller=http://localhost:9000/') # engine = create_engine('pinot+https://localhost:8000/query/sql?controller=http://localhost:9000/') - baseballStats = Table("baseballStats", MetaData(bind=engine), autoload=True, schema="default") + metadata = MetaData() + baseballStats = Table( + "baseballStats", + metadata, + autoload_with=engine, + schema="default", + ) print(f"\nSending Count(*) SQL to Pinot") - query = select([func.count("*")], from_obj=baseballStats) - print(engine.execute(query).scalar()) + query = select(func.count()).select_from(baseballStats) + with engine.connect() as connection: + print(connection.execute(query).scalar()) Session = sessionmaker(bind=engine) session = Session() - query = select( - [column("playerName"), func.sum(column("runs")).label("sum_runs")], - from_obj=baseballStats, - whereclause=text("yearID>=2000"), - group_by=[column("playerName")], - order_by=text("sum_runs DESC"), - limit="5", + query = ( + select( + column("playerName"), + func.sum(column("runs")).label("sum_runs"), + ) + .select_from(baseballStats) + .where(text("yearID>=2000")) + .group_by(column("playerName")) + .order_by(text("sum_runs DESC")) + .limit(5) ) print( f'\nSending SQL: "SELECT playerName, sum(runs) AS sum_runs FROM "baseballStats"' f' WHERE yearID>=2000 GROUP BY playerName ORDER BY sum_runs DESC LIMIT 5" to Pinot' ) - print(engine.execute(query).fetchall()) + with engine.connect() as connection: + print(connection.execute(query).fetchall()) def run_main(): diff --git a/examples/pinot_quickstart_batch.py b/examples/pinot_quickstart_batch.py index af6d5b8..40c5083 100644 --- a/examples/pinot_quickstart_batch.py +++ b/examples/pinot_quickstart_batch.py @@ -53,25 +53,36 @@ def run_pinot_quickstart_batch_sqlalchemy_example() -> None: # engine = create_engine('pinot+http://localhost:8000/query/sql?controller=http://localhost:9000/') # engine = create_engine('pinot+https://localhost:8000/query/sql?controller=http://localhost:9000/') - baseballStats = Table("baseballStats", MetaData(bind=engine), autoload=True, schema="default") + metadata = MetaData() + baseballStats = Table( + "baseballStats", + metadata, + autoload_with=engine, + schema="default", + ) print(f"\nSending Count(*) SQL to Pinot") - query = select([func.count("*")], from_obj=baseballStats) - print(engine.execute(query).scalar()) + query = select(func.count()).select_from(baseballStats) + with engine.connect() as connection: + print(connection.execute(query).scalar()) Session = sessionmaker(bind=engine) session = Session() - query = select( - [column("playerName"), func.sum(column("runs")).label("sum_runs")], - from_obj=baseballStats, - whereclause=text("yearID>=2000"), - group_by=[column("playerName")], - order_by=text("sum_runs DESC"), - limit="5", + query = ( + select( + column("playerName"), + func.sum(column("runs")).label("sum_runs"), + ) + .select_from(baseballStats) + .where(text("yearID>=2000")) + .group_by(column("playerName")) + .order_by(text("sum_runs DESC")) + .limit(5) ) print( f'\nSending SQL: "SELECT playerName, sum(runs) AS sum_runs FROM "baseballStats" WHERE yearID>=2000 GROUP BY playerName ORDER BY sum_runs DESC LIMIT 5" to Pinot' ) - print(engine.execute(query).fetchall()) + with engine.connect() as connection: + print(connection.execute(query).fetchall()) def run_main(): diff --git a/examples/pinot_quickstart_hybrid.py b/examples/pinot_quickstart_hybrid.py index d3d87c6..8930f36 100644 --- a/examples/pinot_quickstart_hybrid.py +++ b/examples/pinot_quickstart_hybrid.py @@ -55,28 +55,39 @@ def run_pinot_quickstart_hybrid_sqlalchemy_example() -> None: # engine = create_engine('pinot+http://localhost:8000/query/sql?controller=http://localhost:9000/') # engine = create_engine('pinot+https://localhost:8000/query/sql?controller=http://localhost:9000/') - airlineStats = Table("airlineStats", MetaData(bind=engine), autoload=True, schema="default") + metadata = MetaData() + airlineStats = Table( + "airlineStats", + metadata, + autoload_with=engine, + schema="default", + ) print(f"\nSending Count(*) SQL to Pinot") - query=select([func.count("*")], from_obj=airlineStats) - print(engine.execute(query).scalar()) + query = select(func.count()).select_from(airlineStats) + with engine.connect() as connection: + print(connection.execute(query).scalar()) from sqlalchemy.orm import sessionmaker Session = sessionmaker(bind=engine) session = Session() - query = select( - [column("OriginCityName"), func.sum(column("Cancelled")).label("sum_cancelled")], - from_obj=airlineStats, - whereclause=text("Year>2010"), - group_by=[column("OriginCityName")], - order_by=text("sum_cancelled DESC"), - limit="5", + query = ( + select( + column("OriginCityName"), + func.sum(column("Cancelled")).label("sum_cancelled"), + ) + .select_from(airlineStats) + .where(text("Year>2010")) + .group_by(column("OriginCityName")) + .order_by(text("sum_cancelled DESC")) + .limit(5) ) print( f'\nSending SQL: "SELECT OriginCityName, sum(Cancelled) AS sum_cancelled FROM "airlineStats" WHERE Year>2010 GROUP BY OriginCityName ORDER BY sum_cancelled DESC LIMIT 5" to Pinot' ) - print(engine.execute(query).fetchall()) + with engine.connect() as connection: + print(connection.execute(query).fetchall()) def run_main(): diff --git a/examples/pinot_quickstart_json_batch.py b/examples/pinot_quickstart_json_batch.py index e7caad5..3cf6f13 100644 --- a/examples/pinot_quickstart_json_batch.py +++ b/examples/pinot_quickstart_json_batch.py @@ -44,51 +44,62 @@ def run_quickstart_json_batch_sqlalchemy_example() -> None: # engine = create_engine('pinot+http://localhost:8000/query/sql?controller=http://localhost:9000/') # engine = create_engine('pinot+https://localhost:8000/query/sql?controller=http://localhost:9000/') - githubEvents = Table("githubEvents", MetaData(bind=engine), autoload=True, schema="default") + metadata = MetaData() + githubEvents = Table( + "githubEvents", + metadata, + autoload_with=engine, + schema="default", + ) print(f"\nSending Count(*) SQL to Pinot\nResults:") - query=select([func.count("*")], from_obj=githubEvents) - print(engine.execute(query).scalar()) + query = select(func.count()).select_from(githubEvents) + with engine.connect() as connection: + print(connection.execute(query).scalar()) Session = sessionmaker(bind=engine) session = Session() - query = select( - [text("json_extract_scalar(repo, \'$.name\', \'STRING\')"), func.count("*")], - from_obj=githubEvents, - whereclause=text("json_match(actor, '\"$.login\"=''LombiqBot''')"), - group_by=text("1"), - order_by=text("2 DESC"), - limit="10", + query = ( + select( + text("json_extract_scalar(repo, \'$.name\', \'STRING\')"), + func.count(), + ) + .select_from(githubEvents) + .where(text("json_match(actor, '\"$.login\"=''LombiqBot''')")) + .group_by(text("1")) + .order_by(text("2 DESC")) + .limit(10) ) print( f'\nSending SQL: "SELECT json_extract_scalar(repo, \'$.name\', \'STRING\'), count(*) FROM githubEvents' f' WHERE json_match(actor, \'\"$.login\"=''LombiqBot''\') GROUP BY 1 ORDER BY 2 DESC LIMIT 10" to Pinot' '\nResults:' ) - print(engine.execute(query).fetchall()) + with engine.connect() as connection: + print(connection.execute(query).fetchall()) Session = sessionmaker(bind=engine) session = Session() - query = select(["*"], - from_obj=githubEvents, - limit="10", - ) + query = select(githubEvents).limit(10) print( f'\nSending SQL: "SELECT * FROM githubEvents LIMIT 10" to Pinot' f'\nResults:' ) - print(engine.execute(query).fetchall()) + with engine.connect() as connection: + print(connection.execute(query).fetchall()) Session = sessionmaker(bind=engine) session = Session() - query = select([column("created_at_timestamp")], - from_obj=githubEvents, - limit="10", + query = ( + select(column("created_at_timestamp")) + .select_from(githubEvents) + .limit(10) ) print( f'\nSending SQL: "SELECT created_at_timestamp FROM githubEvents LIMIT 10" to Pinot' f'\nResults:' ) - print(engine.execute(query).fetchall()) + with engine.connect() as connection: + print(connection.execute(query).fetchall()) def run_main(): diff --git a/examples/pinot_quickstart_multi_stage.py b/examples/pinot_quickstart_multi_stage.py index 2219b42..e128203 100644 --- a/examples/pinot_quickstart_multi_stage.py +++ b/examples/pinot_quickstart_multi_stage.py @@ -50,7 +50,7 @@ def run_pinot_quickstart_multi_stage_sqlalchemy_example() -> None: def run_pinot_quickstart_multi_stage_sqlalchemy_example_2() -> None: engine = create_engine( - "pinot://localhost:8000/query?controller=http://localhost:9000/", + "pinot://localhost:8000/query/sql?controller=http://localhost:9000/", connect_args={"useMultistageEngine": "true"} ) # uses HTTP by default :( # engine = create_engine('pinot+http://localhost:8000/query/sql?controller=http://localhost:9000/') From 8d2bc4a25ca59d530ebf86fcee72d1b659892c92 Mon Sep 17 00:00:00 2001 From: Xiang Fu Date: Fri, 2 Jan 2026 05:48:30 -0800 Subject: [PATCH 3/9] Fix json batch example for Pinot column qualification --- examples/pinot_quickstart_json_batch.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/examples/pinot_quickstart_json_batch.py b/examples/pinot_quickstart_json_batch.py index 3cf6f13..8d37779 100644 --- a/examples/pinot_quickstart_json_batch.py +++ b/examples/pinot_quickstart_json_batch.py @@ -79,7 +79,10 @@ def run_quickstart_json_batch_sqlalchemy_example() -> None: Session = sessionmaker(bind=engine) session = Session() - query = select(githubEvents).limit(10) + # Pinot doesn't accept fully qualified reflected column references like + # `default.githubEvents.id` (emitted by SQLAlchemy when selecting the whole + # reflected table). Use a raw SELECT * instead. + query = text("SELECT * FROM githubEvents LIMIT 10") print( f'\nSending SQL: "SELECT * FROM githubEvents LIMIT 10" to Pinot' f'\nResults:' From 1f4fd144551ba893e001b4545c050999647a4aa6 Mon Sep 17 00:00:00 2001 From: Xiang Fu Date: Fri, 2 Jan 2026 05:55:34 -0800 Subject: [PATCH 4/9] Require SQLAlchemy >=2.0 and update docs --- README.md | 9 ++++++--- examples/pinot_live.py | 31 +++++++++++++++++++++---------- pinotdb/sqlalchemy.py | 17 +++++++++++++---- pyproject.toml | 2 +- 4 files changed, 41 insertions(+), 18 deletions(-) diff --git a/README.md b/README.md index 2cdcf91..eff7e7a 100644 --- a/README.md +++ b/README.md @@ -117,7 +117,7 @@ pinot+https://:@: None: connect_args={"query_options": "timeoutMs=10000"} ) # uses HTTP by default :( - airlineStats = Table("airlineStats", MetaData(bind=engine), autoload=True, schema="default") + metadata = MetaData() + airlineStats = Table( + "airlineStats", + metadata, + autoload_with=engine, + schema="default", + ) print(f"\nSending Count(*) SQL to Pinot") - query=select([func.count("*")], from_obj=airlineStats) - print(engine.execute(query).scalar()) + query = select(func.count()).select_from(airlineStats) + with engine.connect() as connection: + print(connection.execute(query).scalar()) Session = sessionmaker(bind=engine) session = Session() - query = select( - [column("AirlineID"), func.max(column("AirTime")).label("max_airtime")], - from_obj=airlineStats, - group_by=[column("AirlineID")], - order_by=text("max_airtime DESC"), - limit="10", + query = ( + select( + column("AirlineID"), + func.max(column("AirTime")).label("max_airtime"), + ) + .select_from(airlineStats) + .group_by(column("AirlineID")) + .order_by(text("max_airtime DESC")) + .limit(10) ) print( f'\nSending SQL: "SELECT playerName, sum(runs) AS sum_runs FROM "baseballStats"' f' WHERE yearID>=2000 GROUP BY playerName ORDER BY sum_runs DESC LIMIT 5" to Pinot' ) - print(engine.execute(query).fetchall()) + with engine.connect() as connection: + print(connection.execute(query).fetchall()) def run_main(): diff --git a/pinotdb/sqlalchemy.py b/pinotdb/sqlalchemy.py index 62daa56..31a9835 100644 --- a/pinotdb/sqlalchemy.py +++ b/pinotdb/sqlalchemy.py @@ -111,12 +111,15 @@ def __init__( escape_quote='"', omit_schema=True, ): + # SQLAlchemy 2.x changed the IdentifierPreparer __init__ signature. + # Use keyword args so omit_schema is applied correctly (we don't support + # schemas in Pinot and don't want emitted SQL like `"default"."table"`). super(PinotIdentifierPareparer, self).__init__( dialect, - initial_quote, - final_quote, - escape_quote, - omit_schema, + initial_quote=initial_quote, + final_quote=final_quote, + escape_quote=escape_quote, + omit_schema=omit_schema, ) @@ -195,6 +198,12 @@ def update_from_kwargs(self, givenkw): def dbapi(cls): return pinotdb + @classmethod + def import_dbapi(cls): + # SQLAlchemy 2.x renamed dbapi() -> import_dbapi(). Keep dbapi() above + # for backwards compatibility; prefer import_dbapi() going forward. + return pinotdb + def get_default_broker_port(self): if self.scheme.lower() == "https": return self.broker_https_port diff --git a/pyproject.toml b/pyproject.toml index f6e3b36..142f6af 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -12,7 +12,7 @@ homepage = "https://github.com/python-pinot-dbapi/pinot-dbapi" python = ">=3.10,<4" ciso8601 = "^2.1.3" httpx = ">=0.28.1,<0.29" -sqlalchemy = {version = ">=1.4,<3", optional = true} +sqlalchemy = {version = ">=2.0,<3", optional = true} requests = "^2.25.0" # Pin urllib3 to a known-safe version (requests depends on urllib3 transitively). urllib3 = "==2.6.0" From d1f54e8b9143bca6685a6f9a55a6b71e60d08e59 Mon Sep 17 00:00:00 2001 From: Xiang Fu Date: Fri, 2 Jan 2026 06:13:10 -0800 Subject: [PATCH 5/9] Drop Python 3.9 from CI --- .github/workflows/pinot_dbapi_test.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/pinot_dbapi_test.yml b/.github/workflows/pinot_dbapi_test.yml index 701ef40..7b66acd 100644 --- a/.github/workflows/pinot_dbapi_test.yml +++ b/.github/workflows/pinot_dbapi_test.yml @@ -40,7 +40,7 @@ jobs: runs-on: ubuntu-latest strategy: matrix: - python-version: ["3.9", "3.10", "3.11", "3.12"] + python-version: ["3.10", "3.11", "3.12"] name: Pinot DB API Integration Test Set steps: - uses: actions/checkout@v2 @@ -75,7 +75,7 @@ jobs: runs-on: ubuntu-latest strategy: matrix: - python-version: ["3.9", "3.10", "3.11", "3.12"] + python-version: ["3.10", "3.11", "3.12"] name: Pinot DB API Unit Test Set steps: - uses: actions/checkout@v2 From d6f9698e88f7f70e31da23f41cdd480d170ebfa0 Mon Sep 17 00:00:00 2001 From: Xiang Fu Date: Fri, 2 Jan 2026 06:23:38 -0800 Subject: [PATCH 6/9] Avoid Requests auth/header warnings --- pinotdb/sqlalchemy.py | 21 ++++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/pinotdb/sqlalchemy.py b/pinotdb/sqlalchemy.py index 31a9835..1b49747 100644 --- a/pinotdb/sqlalchemy.py +++ b/pinotdb/sqlalchemy.py @@ -230,7 +230,26 @@ def create_connect_args(self, url): def get_metadata_from_controller(self, path): url = parse.urljoin(self._controller, path) - r = requests.get(url, headers={"Accept": "application/json", "Database": self._database}, verify=self._verify_ssl, auth= HTTPBasicAuth(self._username, self._password)) + headers = {"Accept": "application/json"} + # Only send Database header when explicitly set; Requests will reject + # non-string header values. + if self._database: + headers["Database"] = self._database + + # Only send basic auth when credentials are provided; passing None here + # triggers deprecation warnings in Requests. + auth = ( + HTTPBasicAuth(self._username, self._password) + if self._username and self._password + else None + ) + + r = requests.get( + url, + headers=headers, + verify=self._verify_ssl, + auth=auth, + ) try: result = r.json() except ValueError as e: From 1d74444e548f0f7404098c308659320138cb0866 Mon Sep 17 00:00:00 2001 From: Xiang Fu Date: Fri, 2 Jan 2026 06:33:18 -0800 Subject: [PATCH 7/9] Fix example SQL messages and endpoint note --- examples/pinot_live.py | 4 ++-- examples/pinot_quickstart_multi_stage.py | 1 + 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/examples/pinot_live.py b/examples/pinot_live.py index 2567f02..dd155ae 100644 --- a/examples/pinot_live.py +++ b/examples/pinot_live.py @@ -48,8 +48,8 @@ def run_pinot_live_example() -> None: .limit(10) ) print( - f'\nSending SQL: "SELECT playerName, sum(runs) AS sum_runs FROM "baseballStats"' - f' WHERE yearID>=2000 GROUP BY playerName ORDER BY sum_runs DESC LIMIT 5" to Pinot' + '\nSending SQL: "SELECT AirlineID, max(AirTime) AS max_airtime FROM airlineStats ' + 'GROUP BY AirlineID ORDER BY max_airtime DESC LIMIT 10" to Pinot' ) with engine.connect() as connection: print(connection.execute(query).fetchall()) diff --git a/examples/pinot_quickstart_multi_stage.py b/examples/pinot_quickstart_multi_stage.py index e128203..3454283 100644 --- a/examples/pinot_quickstart_multi_stage.py +++ b/examples/pinot_quickstart_multi_stage.py @@ -49,6 +49,7 @@ def run_pinot_quickstart_multi_stage_sqlalchemy_example() -> None: def run_pinot_quickstart_multi_stage_sqlalchemy_example_2() -> None: + # Use /query/sql because the multi-stage engine expects SQL on this endpoint. engine = create_engine( "pinot://localhost:8000/query/sql?controller=http://localhost:9000/", connect_args={"useMultistageEngine": "true"} From 50a40dd97f4e6bf63a01eaf7d440075022d59847 Mon Sep 17 00:00:00 2001 From: Xiang Fu Date: Fri, 2 Jan 2026 08:34:05 -0800 Subject: [PATCH 8/9] Emit count(*) for Pinot SQLAlchemy --- pinotdb/sqlalchemy.py | 22 ++++++++++++++++++---- tests/unit/test_sqlalchemy.py | 14 +++++++++++++- 2 files changed, 31 insertions(+), 5 deletions(-) diff --git a/pinotdb/sqlalchemy.py b/pinotdb/sqlalchemy.py index 1b49747..40aa158 100644 --- a/pinotdb/sqlalchemy.py +++ b/pinotdb/sqlalchemy.py @@ -26,6 +26,20 @@ def visit_column(self, column, result_map=None, **kwargs): column.is_literal = True return super().visit_column(column, result_map, **kwargs) + def visit_function(self, func, **kw): + if func.name and func.name.lower() == "count": + # SQLAlchemy's internal function-arg storage differs by version: + # 2.x exposes func.clauses, while older 1.4-era builds used + # func.clause_expr.clauses. Check both to detect no-arg COUNT(). + clauses = getattr(func, "clauses", None) + if clauses is None: + clause_expr = getattr(func, "clause_expr", None) + clauses = getattr(clause_expr, "clauses", None) if clause_expr else None + if clauses is not None and len(clauses) == 0: + # Pinot requires COUNT(*) instead of COUNT() with no args. + return "count(*)" + return super().visit_function(func, **kw) + def escape_literal_column(self, text): # This is a hack to quote column names that conflict with reserved # words since 'column.is_literal = True' @@ -231,10 +245,10 @@ def create_connect_args(self, url): def get_metadata_from_controller(self, path): url = parse.urljoin(self._controller, path) headers = {"Accept": "application/json"} - # Only send Database header when explicitly set; Requests will reject - # non-string header values. - if self._database: - headers["Database"] = self._database + # Only send Database header when explicitly set to a non-None value, + # and always as a string; Requests will reject non-string header values. + if self._database is not None: + headers["Database"] = str(self._database) # Only send basic auth when credentials are provided; passing None here # triggers deprecation warnings in Requests. diff --git a/tests/unit/test_sqlalchemy.py b/tests/unit/test_sqlalchemy.py index 0752f22..31f8dce 100644 --- a/tests/unit/test_sqlalchemy.py +++ b/tests/unit/test_sqlalchemy.py @@ -16,7 +16,7 @@ import responses from sqlalchemy import ( BigInteger, Column, Integer, MetaData, String, Table, - column, select, types, + column, func, select, types, ) from sqlalchemy.engine import make_url @@ -407,6 +407,18 @@ def test_can_select_table_directly(self): 'SELECT some_table.some_column \nFROM some_table', ) + def test_count_without_args_compiles_to_count_star(self): + metadata = MetaData() + table = Table( + 'some_table', metadata, + Column('some_column', Integer) + ) + statement = select(func.count()).select_from(table) + + compiler = self.dialect.statement_compiler(self.dialect, statement) + + self.assertIn('count(*)', str(compiler)) + class PinotTypeCompilerTest(PinotTestCase): def setUp(self) -> None: From 5194d08660955bf02b98a693aa6cdb62ee86e22e Mon Sep 17 00:00:00 2001 From: Xiang Fu Date: Fri, 2 Jan 2026 09:32:32 -0800 Subject: [PATCH 9/9] Clarify Pinot SQLAlchemy notes and update CI --- examples/pinot_quickstart_multi_stage.py | 2 +- pinotdb/sqlalchemy.py | 9 ++------- 2 files changed, 3 insertions(+), 8 deletions(-) diff --git a/examples/pinot_quickstart_multi_stage.py b/examples/pinot_quickstart_multi_stage.py index 3454283..396ef2a 100644 --- a/examples/pinot_quickstart_multi_stage.py +++ b/examples/pinot_quickstart_multi_stage.py @@ -49,7 +49,7 @@ def run_pinot_quickstart_multi_stage_sqlalchemy_example() -> None: def run_pinot_quickstart_multi_stage_sqlalchemy_example_2() -> None: - # Use /query/sql because the multi-stage engine expects SQL on this endpoint. + # Multi-stage engine requires /query/sql endpoint. engine = create_engine( "pinot://localhost:8000/query/sql?controller=http://localhost:9000/", connect_args={"useMultistageEngine": "true"} diff --git a/pinotdb/sqlalchemy.py b/pinotdb/sqlalchemy.py index 40aa158..4a95230 100644 --- a/pinotdb/sqlalchemy.py +++ b/pinotdb/sqlalchemy.py @@ -28,13 +28,8 @@ def visit_column(self, column, result_map=None, **kwargs): def visit_function(self, func, **kw): if func.name and func.name.lower() == "count": - # SQLAlchemy's internal function-arg storage differs by version: - # 2.x exposes func.clauses, while older 1.4-era builds used - # func.clause_expr.clauses. Check both to detect no-arg COUNT(). + # Detect no-argument COUNT() and render it as COUNT(*) for Pinot. clauses = getattr(func, "clauses", None) - if clauses is None: - clause_expr = getattr(func, "clause_expr", None) - clauses = getattr(clause_expr, "clauses", None) if clause_expr else None if clauses is not None and len(clauses) == 0: # Pinot requires COUNT(*) instead of COUNT() with no args. return "count(*)" @@ -215,7 +210,7 @@ def dbapi(cls): @classmethod def import_dbapi(cls): # SQLAlchemy 2.x renamed dbapi() -> import_dbapi(). Keep dbapi() above - # for backwards compatibility; prefer import_dbapi() going forward. + # for external callers; SQLAlchemy itself will use import_dbapi(). return pinotdb def get_default_broker_port(self):