From 965fd55a84b34aa828f910a5017799133b0ed8d6 Mon Sep 17 00:00:00 2001 From: Matthew Alexander Date: Tue, 4 Feb 2025 11:26:24 +1030 Subject: [PATCH 1/6] Initial Changes to support MSSQL --- dev/dev.env | 3 + docker-compose.yml | 17 ++- docs/supported-databases.md | 1 + poetry.lock | 72 ++++++++++- pyproject.toml | 7 +- sqeleton/databases/__init__.py | 11 +- sqeleton/databases/_connect.py | 2 + sqeleton/databases/mssql.py | 227 ++++++++++++++++++++++++++++++--- tests/common.py | 2 + tests/test_database.py | 1 + 10 files changed, 318 insertions(+), 25 deletions(-) diff --git a/dev/dev.env b/dev/dev.env index cb5eea1..9a9f31e 100644 --- a/dev/dev.env +++ b/dev/dev.env @@ -21,3 +21,6 @@ VERTICA_DB_NAME=vertica # leave VMART_DIR and VMART_ETL_SCRIPT empty. VMART_DIR= VMART_ETL_SCRIPT= + +ACCEPT_EULA=Y +MSSQL_SA_PASSWORD= \ No newline at end of file diff --git a/docker-compose.yml b/docker-compose.yml index b5d7ae3..d16aa78 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -117,13 +117,28 @@ services: networks: - local - + mssql: + container_name: dd-mssql + image: mcr.microsoft.com/mssql/server:2022-latest + restart: always + volumes: + - mssql-data:/var/opt/mssql/data:delegated + ports: + - '8001:1433' + expose: + - '8001' + env_file: + - dev/dev.env + tty: true + networks: + - local volumes: postgresql-data: mysql-data: clickhouse-data: vertica-data: + mssql-data: networks: local: diff --git a/docs/supported-databases.md b/docs/supported-databases.md index 9d4751d..aea24d9 100644 --- a/docs/supported-databases.md +++ b/docs/supported-databases.md @@ -14,6 +14,7 @@ | Clickhouse | 💛 | `clickhouse://:@:9000/` | | Vertica | 💛 | `vertica://:@:5433/` | | DuckDB | 💛 | | +| MsSQL | ⏳ | `mssql+pymssql://:@:/` | | ElasticSearch | 📝 | | | Planetscale | 📝 | | | Pinot | 📝 | | diff --git a/poetry.lock b/poetry.lock index 60201e6..9971531 100644 --- a/poetry.lock +++ b/poetry.lock @@ -1,4 +1,4 @@ -# This file is automatically @generated by Poetry 1.8.3 and should not be changed by hand. +# This file is automatically @generated by Poetry 1.8.5 and should not be changed by hand. [[package]] name = "asn1crypto" @@ -924,6 +924,73 @@ dev = ["coverage[toml] (==5.0.4)", "cryptography (>=3.4.0)", "pre-commit", "pyte docs = ["sphinx", "sphinx-rtd-theme", "zope.interface"] tests = ["coverage[toml] (==5.0.4)", "pytest (>=6.0.0,<7.0.0)"] +[[package]] +name = "pymssql" +version = "2.3.2" +description = "DB-API interface to Microsoft SQL Server for Python. (new Cython-based version)" +optional = false +python-versions = "*" +files = [ + {file = "pymssql-2.3.2-cp310-cp310-macosx_13_0_x86_64.whl", hash = "sha256:73fac766b448613d7ae26e6b304b2cb8a7ffebccaa373633bad3b3cbcc829935"}, + {file = "pymssql-2.3.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:eb629b5fb0376fbf39d575cf1365e504b84877b19f9e8d53caa5228fed56894a"}, + {file = "pymssql-2.3.2-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ab912d1178d5977e421cf9c4d4071958b223cbe4a2b6dd64611d521aa6bb7187"}, + {file = "pymssql-2.3.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:06883bc9bdb297ae9132d9371b5b1a3a223c8f93dd6a87d1c112c6a688f26d53"}, + {file = "pymssql-2.3.2-cp310-cp310-manylinux_2_28_x86_64.whl", hash = "sha256:c24ba6aedb9b5540b56f3e74bff92b687c6e90c00650823385729c7e55923cf5"}, + {file = "pymssql-2.3.2-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:26bdb7abd5f107b6be422635f03e2cecaa52a5f4c394a205014586abbff9e72a"}, + {file = "pymssql-2.3.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:72b6599963b6e066998c4b27b7bf207684c243b12b1e5dcc180b2af22802ae6c"}, + {file = "pymssql-2.3.2-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:23f5e2e2bdba1cf7cecbac66dd07de7631a8efca5692efee18ff46ebc087b757"}, + {file = "pymssql-2.3.2-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:82ed3dd560d3fb222d26ce3a7373f46dc3ad1d50b6e6417ef7399e87fa9aefe1"}, + {file = "pymssql-2.3.2-cp310-cp310-win32.whl", hash = "sha256:cbe9058b6520be74463476ff2cdb17bbab5ff60b60b3ed7bd8bd2d086bdfd9bd"}, + {file = "pymssql-2.3.2-cp310-cp310-win_amd64.whl", hash = "sha256:fdd774b26407babd0205ef85a098f90553e6b3da77a22322a1e7d2cb51f742c0"}, + {file = "pymssql-2.3.2-cp311-cp311-macosx_13_0_universal2.whl", hash = "sha256:2a44a0898dacba4e25cac8778d0ed112e297883fe862204e447081888da78dc4"}, + {file = "pymssql-2.3.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f9737c06b13ca2012b9900185fa3af72a37941c532da2e6373dd7c9ab16abddf"}, + {file = "pymssql-2.3.2-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0831c5c95aab0b9aba5142dc97e28f59c4130e1c34ffc13ecbfdd4d2fe45b8a0"}, + {file = "pymssql-2.3.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ae02cc1594f0addd748bf5ac1ccc7a73c03846ada9c553663c381b242b586606"}, + {file = "pymssql-2.3.2-cp311-cp311-manylinux_2_28_x86_64.whl", hash = "sha256:1c99dba4bf5b1ce10657e9e2885f18ba9179190251b63d1498e7d6d72e64f1ce"}, + {file = "pymssql-2.3.2-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:9e3d6fada7fbe7a5f5fafc420673f777bab3f399c78fa44e29de6a8cbc36e515"}, + {file = "pymssql-2.3.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:5904d78e61668ec89761d3ae01efd4b42b31d820f612929f449e93cd23ba3c54"}, + {file = "pymssql-2.3.2-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:9361593a89c9162fc631baf648a87e2666373382d9d54aacfb19edab9ceb2007"}, + {file = "pymssql-2.3.2-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:0768d90f96ae3267d7561d3bcfe94dd671d107489e870388b12570c3debbc552"}, + {file = "pymssql-2.3.2-cp311-cp311-win32.whl", hash = "sha256:97fbd8491ad3ece0adcb321acec6db48b8fe37bc74af4c91bb657d4d9347d634"}, + {file = "pymssql-2.3.2-cp311-cp311-win_amd64.whl", hash = "sha256:79cdc3ed1da3129ba56232127db86279728c4328595e2532ed4d0da6379a5c72"}, + {file = "pymssql-2.3.2-cp312-cp312-macosx_13_0_universal2.whl", hash = "sha256:235c230e56d8c8c5f289e665c538f31d967fec302d05ad269dcd64fa9d6eb3b7"}, + {file = "pymssql-2.3.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1bc33ed9af6d8ebea2d49144cd2317b7ae1105dd51dddfd46982c90c8f0cf6ab"}, + {file = "pymssql-2.3.2-cp312-cp312-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:793a93da1521fa66bf02b3b873065e22bf14bda5570e005ce3d5fae0776d7b92"}, + {file = "pymssql-2.3.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b156b15165f7a0bbb392a124d8e2d678145c93e5bfcfef3b637e4d87eadcc85b"}, + {file = "pymssql-2.3.2-cp312-cp312-manylinux_2_28_x86_64.whl", hash = "sha256:f2b1da4e68d618c7972e583ae19f386ae620258acb61564e8067c203f27cd769"}, + {file = "pymssql-2.3.2-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:2f4093b95f1f3a1232687fc92f652aaf675eb423db8549c16d146b91ac2f0eba"}, + {file = "pymssql-2.3.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:cc13c2e0f1b8efc3a46941de9db768fa59937b5a54081ec0cb0ff0da17d1fff3"}, + {file = "pymssql-2.3.2-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:6019d2939963112662288704f608f31634038bffcfd5cad1bc79cb167edb3cc1"}, + {file = "pymssql-2.3.2-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:41d09e1b2534229b288c37b88c1de3d964317af2c7eec58bfb97e01d679eba27"}, + {file = "pymssql-2.3.2-cp312-cp312-win32.whl", hash = "sha256:b16d5880f7028442d6c49c94801ce9bff3af8af0fbda7c6039febb936714aed5"}, + {file = "pymssql-2.3.2-cp312-cp312-win_amd64.whl", hash = "sha256:a3f9e7eb813dfeab6d01bf6474049bb76b0521235159d3e969ec82df384eac49"}, + {file = "pymssql-2.3.2-cp313-cp313-macosx_13_0_universal2.whl", hash = "sha256:f282e701dca155b3e7f1644d7e3b60c201ca5f3be8045bce34042d3c737d63ee"}, + {file = "pymssql-2.3.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f1791f4627c42fe2d2833c884d036b0c5c8cf628f2cdfa3536191c217acf729e"}, + {file = "pymssql-2.3.2-cp313-cp313-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3870085a49e5332bc67ecb24f217c586977d5352eb51598244fc7bc278eee3e1"}, + {file = "pymssql-2.3.2-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1afda7b7022eff9451bd83e3f64c450a1a8cdff4ba8b8e399866dcd2cb861a1e"}, + {file = "pymssql-2.3.2-cp313-cp313-manylinux_2_28_x86_64.whl", hash = "sha256:b0c2b11aca16617cacaf385fb94134e73ba0216a924f9b85778cc7e3d3713361"}, + {file = "pymssql-2.3.2-cp313-cp313-musllinux_1_1_i686.whl", hash = "sha256:2568944db3888996e161b40ad06c1b9e0fbb6cfcb38279a3abb98ece7a8e1c4a"}, + {file = "pymssql-2.3.2-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:ee8ee2c7c227c413ad9b88ddba1cb6a25e28c217ae73ecac1c7a6b8c29003604"}, + {file = "pymssql-2.3.2-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:8cd806380d362d4cef2d925a6baee6a4b2b151a92cac2cab5c4bfabed4be4849"}, + {file = "pymssql-2.3.2-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:ef0d29c705db552f9e75230f946b0ca9db0db903c5c9ee79ce8b88ad25ea9670"}, + {file = "pymssql-2.3.2-cp313-cp313-win32.whl", hash = "sha256:1037053e6c74d6fe14c428cc942968b4e4bf06854706a83fe8e822e475e3f107"}, + {file = "pymssql-2.3.2-cp313-cp313-win_amd64.whl", hash = "sha256:148b7714fff5a5b7ce038e92b02dd9bf68fe442c181a3aae32148e7b13f6db95"}, + {file = "pymssql-2.3.2-cp39-cp39-macosx_13_0_x86_64.whl", hash = "sha256:bac6f355c454f94b0e15a04b7841236e5c5c4ef44d2d1beed00a3ad7b50ccc53"}, + {file = "pymssql-2.3.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:33ad813092f8fb8f74578c5b5e37c818c4ae130fd4047cb28f0b256f2f107367"}, + {file = "pymssql-2.3.2-cp39-cp39-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:08facd25a50a7279385d1ffcee9d6d83c4e361db1af38e14519a87d7b1cadb10"}, + {file = "pymssql-2.3.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ab48de09864fa6f49c575ef569f6773981d0cd905ff7288b5b185f8079a5a21f"}, + {file = "pymssql-2.3.2-cp39-cp39-manylinux_2_28_x86_64.whl", hash = "sha256:b3eb201c402bcf4f5b9399df0bb20d522636d2e87d1c6957a0b6d772ee636c61"}, + {file = "pymssql-2.3.2-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:dd5fe7552edc81628e4242b4671f7bad5ff1ec790bae5c7615d989375620edac"}, + {file = "pymssql-2.3.2-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:fb8a7b197aaf466a7577ca6690aa9d747081b653ab212d052d71f3cc10587c3b"}, + {file = "pymssql-2.3.2-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:0a20a17db870fb0e446a6d6bf7664aaf84af7be58ab1fad025cafa4e092507a1"}, + {file = "pymssql-2.3.2-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:1cdc2619e7b4192b8d6619fd52ba8a2eae18b38b376f8649fb8f0727c4e88ff9"}, + {file = "pymssql-2.3.2-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:b14cc65369d1425f2fb517609113465a0f55f19a49648160f2d10be4cb43ff4d"}, + {file = "pymssql-2.3.2-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:22b1ce3a48f28ee7d06ebc9ed94276d0bf1c99051ee1df3d2377b74721bd62ef"}, + {file = "pymssql-2.3.2-cp39-cp39-win32.whl", hash = "sha256:aa08b6203b2b5ed5ce47f80d5c529459181300d7e0d0c1e84390a4d01d45e509"}, + {file = "pymssql-2.3.2-cp39-cp39-win_amd64.whl", hash = "sha256:22fb0fdd3b889bc10abbe3aa2abe7a008b30a6367b9ba159412d185d7d8fda9d"}, + {file = "pymssql-2.3.2.tar.gz", hash = "sha256:18089641b687be1ebd0f64f0d1ff977478a397ffa1af372bdf10dbec29cf6d2e"}, +] + [[package]] name = "pyopenssl" version = "24.3.0" @@ -1294,6 +1361,7 @@ files = [ clickhouse = ["clickhouse-driver"] databricks = [] duckdb = ["duckdb"] +mssql = ["pymssql"] mysql = ["mysql-connector-python"] oracle = [] postgresql = ["psycopg2-binary"] @@ -1306,4 +1374,4 @@ vertica = [] [metadata] lock-version = "2.0" python-versions = "^3.8" -content-hash = "47969195ee2be5c1237a38822b1c000302bf5bb6bb123dcb5afbb1c22923247f" +content-hash = "8f45691e16f715a30a5e3cb17052bde7fbdd45eef00c00f6f1b2b62f407e64b1" diff --git a/pyproject.toml b/pyproject.toml index 33a1844..3d676cc 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [tool.poetry] name = "sqeleton" -version = "0.1.7" +version = "0.1.8" description = "Python library for querying SQL databases" authors = ["Erez Shinan "] license = "MIT" @@ -23,7 +23,7 @@ packages = [{ include = "sqeleton" }] [tool.poetry.dependencies] python = "^3.8" -runtype = ">=0.5.0" +runtype = "0.5.0" dsnparse = "*" click = ">=8.1" rich = "*" @@ -41,6 +41,7 @@ textual = {version=">=0.9.1", optional=true} textual-select = {version="*", optional=true} pygments = {version=">=2.13.0", optional=true} prompt-toolkit = {version=">=3.0.36", optional=true} +pymssql = {version=">=2.3.2", optional=true} [tool.poetry.dev-dependencies] parameterized = "*" @@ -56,6 +57,7 @@ trino = ">=0.314.0" presto-python-client = "*" clickhouse-driver = "*" vertica-python = "*" +pymssql = "*" [tool.poetry.extras] mysql = ["mysql-connector-python"] @@ -69,6 +71,7 @@ clickhouse = ["clickhouse-driver"] vertica = ["vertica-python"] duckdb = ["duckdb"] tui = ["textual", "textual-select", "pygments", "prompt-toolkit"] +mssql = ["pymssql"] [build-system] requires = ["poetry-core>=1.0.0"] diff --git a/sqeleton/databases/__init__.py b/sqeleton/databases/__init__.py index b0c8f1f..9e8fad0 100644 --- a/sqeleton/databases/__init__.py +++ b/sqeleton/databases/__init__.py @@ -1,4 +1,12 @@ -from .base import MD5_HEXDIGITS, CHECKSUM_HEXDIGITS, QueryError, ConnectError, BaseDialect, Database, logger +from .base import ( + MD5_HEXDIGITS, + CHECKSUM_HEXDIGITS, + QueryError, + ConnectError, + BaseDialect, + Database, + logger, +) from ..abcs import DbPath, DbKey, DbTime from ._connect import Connect @@ -14,5 +22,6 @@ from .clickhouse import Clickhouse from .vertica import Vertica from .duckdb import DuckDB +from .mssql import MsSQL connect = Connect() diff --git a/sqeleton/databases/_connect.py b/sqeleton/databases/_connect.py index 40d9bfe..2691e43 100644 --- a/sqeleton/databases/_connect.py +++ b/sqeleton/databases/_connect.py @@ -22,6 +22,7 @@ from .clickhouse import Clickhouse from .vertica import Vertica from .duckdb import DuckDB +from .mssql import MsSQL @dataclass @@ -87,6 +88,7 @@ def match_path(self, dsn): "trino": Trino, "clickhouse": Clickhouse, "vertica": Vertica, + "pymssql": MsSQL, } diff --git a/sqeleton/databases/mssql.py b/sqeleton/databases/mssql.py index 8d394e3..18db077 100644 --- a/sqeleton/databases/mssql.py +++ b/sqeleton/databases/mssql.py @@ -1,25 +1,214 @@ -# class MsSQL(ThreadedDatabase): -# "AKA sql-server" +from typing import List +from datetime import datetime +from ..abcs.database_types import ( + DbPath, + Timestamp, + TimestampTZ, + Float, + Decimal, + Integer, + TemporalType, + Text, + FractionalType, + Boolean, + Date, +) +from typing import Dict +from ..abcs.mixins import AbstractMixin_MD5, AbstractMixin_NormalizeValue, AbstractMixin_Schema +from .base import BaseDialect, ThreadedDatabase, import_helper, ConnectError, Mixin_Schema +from ..abcs import Compilable +from ..queries import this, table, SKIP, Select +from ..queries.ast_classes import TablePath +from .base import TIMESTAMP_PRECISION_POS, Mixin_RandomSample, QueryError -# def __init__(self, host, port, user, password, *, database, thread_count, **kw): -# args = dict(server=host, port=port, database=database, user=user, password=password, **kw) -# self._args = {k: v for k, v in args.items() if v is not None} +SESSION_TIME_ZONE = None # Changed by the tests -# super().__init__(thread_count=thread_count) -# def create_connection(self): -# mssql = import_mssql() -# try: -# return mssql.connect(**self._args) -# except mssql.Error as e: -# raise ConnectError(*e.args) from e +@import_helper("mssql") +def import_mssql(): + import pymssql -# def quote(self, s: str): -# return f"[{s}]" + return pymssql -# def md5_as_int(self, s: str) -> str: -# return f"CONVERT(decimal(38,0), CONVERT(bigint, HashBytes('MD5', {s}), 2))" -# # return f"CONVERT(bigint, (CHECKSUM({s})))" -# def to_string(self, s: str): -# return f"CONVERT(varchar, {s})" +class Mixin_MD5(AbstractMixin_MD5): + def md5_as_int(self, s: str) -> str: + return f"CONVERT(decimal(38,0), CONVERT(bigint, HashBytes('MD5', {s}), 2))" + +class Mixin_NormalizeValue(AbstractMixin_NormalizeValue): + def normalize_timestamp(self, value: str, coltype: TemporalType) -> str: + timestamp = f"convert(varchar(26), {value} AT TIME ZONE 'UTC', 25)" + return ( + f"LEFT({timestamp} + REPLICATE(' ', {coltype.precision}), {TIMESTAMP_PRECISION_POS+6})" + ) + + def normalize_number(self, value: str, coltype: FractionalType) -> str: + return self.to_string(f"convert(varchar, convert(decimal(38, {coltype.precision}), {value}))") + + def normalize_boolean(self, value: str, _coltype: Boolean) -> str: + return self.to_string(f"convert(varchar, {value})") + +class Mixin_Schema(AbstractMixin_Schema): + def table_information(self) -> TablePath: + return table("information_schema", "tables") + + def list_tables(self, table_schema: str, like: Compilable = None) -> Select: + if table_schema == None: + query = ( + self.table_information() + .where( + this.table_name.like(like) if like is not None else SKIP, + this.table_type == "BASE TABLE", + ) + .select(this.table_name) + ) + else: + query = ( + self.table_information() + .where( + this.table_schema == table_schema, + this.table_name.like(like) if like is not None else SKIP, + this.table_type == "BASE TABLE", + ) + .select(this.table_name) + ) + + return query + + +class MsSQLDialect(BaseDialect, Mixin_Schema): + name = "MsSQL" + ROUNDS_ON_PREC_LOSS = True + SUPPORTS_PRIMARY_KEY = True + SUPPORTS_INDEXES = True + MIXINS = {Mixin_Schema, Mixin_MD5, Mixin_NormalizeValue, Mixin_RandomSample} + AT_TIMEZONE = False + + TYPE_CLASSES = { + # Numbers + "tinyint": Integer, + "smallint": Integer, + "int": Integer, + "bigint": Integer, + "decimal": Decimal, + "numeric": Decimal, + "money": Decimal, + "smallmoney": Decimal, + "float": Float, + "real": Float, + # Timestamps + "date": Date, + "time": Timestamp, + "datetime2": Timestamp, + "datetimeoffset": TimestampTZ, + "datetime": Timestamp, + "smalldatetime": Date, + # Text + "char": Text, + "varchar": Text, + "text": Text, + "nchar": Text, + "nvarchar": Text, + "ntext": Text, + # Boolean + "BIT": Boolean, + } + + # TSQL has EXPLAIN for Azure SQL Data warehouse + # But not yet included for the regular RDBMS SQL Server + def explain_as_text(self, query: str) -> str: + return f"""SET SHOWPLAN_ALL ON; + GO + {query} + GO + SET SHOWPLAN_ALL ON; + GO""" + + def quote(self, s: str): + return f'"{s}"' + + def to_string(self, s: str): + return f"CONVERT(VARCHAR(MAX), {s})" + + def concat(self, items: List[str]) -> str: + joined_exprs = ", ".join(items) + return f"CONCAT({joined_exprs})" + + def _convert_db_precision_to_digits(self, p: int) -> int: + # Subtracting 2 due to wierd precision issues in PostgreSQL + return super()._convert_db_precision_to_digits(p) - 2 + + def set_timezone_to_utc(self) -> str: + return "" + + def current_timestamp(self) -> str: + return "SYSUTCDATETIME()" + + def type_repr(self, t) -> str: + if isinstance(t, TimestampTZ): + return f"datetimeoffset" + try: + return { + str: "VARCHAR(1024)", + bool: "BIT", + datetime: "datetime2", + }[t] + except: + return super().type_repr(t) + +class MsSQL(ThreadedDatabase): + "AKA sql-server" + dialect = MsSQLDialect() + SUPPORTS_ALPHANUMS = False + SUPPORTS_UNIQUE_CONSTAINT = True + CONNECT_URI_HELP = "pymssql://:@:/" + CONNECT_URI_PARAMS = ["database"] + + def __init__(self, host, port, user, password, *, database, thread_count, **kw): + args = dict(server=host, port=port, database=database, user=user, password=password, conn_properties=['SET QUOTED_IDENTIFIER ON;'], **kw) + self._args = {k: v for k, v in args.items() if v is not None} + + super().__init__(thread_count=thread_count) + + def create_connection(self): + self.mssql = import_mssql() + try: + return self.mssql.connect(**self._args) + except self.mssql.Error as e: + raise ConnectError(*e.args) from e + + def _query_cursor(self, c, sql_code: str): + try: + return super()._query_cursor(c, sql_code) + except self.mssql.DatabaseError as e: + raise QueryError(e) + except e: + raise Exception(e) + + def select_table_schema(self, path: DbPath) -> str: + """Provide SQL for selecting the table schema as (name, type, date_prec, num_prec)""" + + schema, name = self._normalize_table_path(path) + if schema == None: + sql_code = ( + "SELECT column_name, data_type, datetime_precision, numeric_precision, numeric_scale " + "FROM information_schema.COLUMNS " + f"WHERE table_name = '{name}'" + ) + else: + sql_code = ( + "SELECT column_name, data_type, datetime_precision, numeric_precision, numeric_scale " + "FROM information_schema.COLUMNS " + f"WHERE table_name = '{name}' AND table_schema = '{schema}'" + ) + + return sql_code + + def query_table_schema(self, path: DbPath) -> Dict[str, tuple]: + rows = self.query(self.select_table_schema(path), list) + if not rows: + raise RuntimeError(f"{self.name}: Table '{'.'.join(path)}' does not exist, or has no columns") + + d = {r[0]: r for r in rows} + assert len(d) == len(rows) + return d \ No newline at end of file diff --git a/tests/common.py b/tests/common.py index bf8b80c..4c144c9 100644 --- a/tests/common.py +++ b/tests/common.py @@ -33,6 +33,7 @@ # vertica uri provided for docker - "vertica://vertica:Password1@localhost:5433/vertica" TEST_VERTICA_CONN_STRING: str = os.environ.get("VERTICA_URI") TEST_DUCKDB_CONN_STRING: str = "duckdb://main:@:memory:" +TEST_MSSQL_CONN_STRING: str = "pymssql://sa:@localhost:8001/master" DEFAULT_N_SAMPLES = 50 @@ -74,6 +75,7 @@ def get_git_revision_short_hash() -> str: db.Clickhouse: TEST_CLICKHOUSE_CONN_STRING, db.Vertica: TEST_VERTICA_CONN_STRING, db.DuckDB: TEST_DUCKDB_CONN_STRING, + db.MsSQL: TEST_MSSQL_CONN_STRING } _database_instances = {} diff --git a/tests/test_database.py b/tests/test_database.py index 461e9b5..fd46f7b 100644 --- a/tests/test_database.py +++ b/tests/test_database.py @@ -22,6 +22,7 @@ dbs.Presto, dbs.Trino, dbs.Vertica, + dbs.MsSQL, } test_each_database: Callable = make_test_each_database_in_list(TEST_DATABASES) From 1b2d04bcc3f3d9d5d685c8a83e65a145e68ac8f1 Mon Sep 17 00:00:00 2001 From: Matthew Alexander Date: Thu, 6 Feb 2025 16:08:05 +1030 Subject: [PATCH 2/6] Formatting changes to better conform to origin --- sqeleton/databases/__init__.py | 10 +--------- sqeleton/databases/mssql.py | 2 +- 2 files changed, 2 insertions(+), 10 deletions(-) diff --git a/sqeleton/databases/__init__.py b/sqeleton/databases/__init__.py index 9e8fad0..914ded7 100644 --- a/sqeleton/databases/__init__.py +++ b/sqeleton/databases/__init__.py @@ -1,12 +1,4 @@ -from .base import ( - MD5_HEXDIGITS, - CHECKSUM_HEXDIGITS, - QueryError, - ConnectError, - BaseDialect, - Database, - logger, -) +from .base import MD5_HEXDIGITS, CHECKSUM_HEXDIGITS, QueryError, ConnectError, BaseDialect, Database, logger from ..abcs import DbPath, DbKey, DbTime from ._connect import Connect diff --git a/sqeleton/databases/mssql.py b/sqeleton/databases/mssql.py index 18db077..e6f7854 100644 --- a/sqeleton/databases/mssql.py +++ b/sqeleton/databases/mssql.py @@ -17,7 +17,7 @@ from ..abcs.mixins import AbstractMixin_MD5, AbstractMixin_NormalizeValue, AbstractMixin_Schema from .base import BaseDialect, ThreadedDatabase, import_helper, ConnectError, Mixin_Schema from ..abcs import Compilable -from ..queries import this, table, SKIP, Select +from ..queries import this, table, Select from ..queries.ast_classes import TablePath from .base import TIMESTAMP_PRECISION_POS, Mixin_RandomSample, QueryError From 3b7ea6f1fd4b4b8aa367f1bffdd66012836ced1b Mon Sep 17 00:00:00 2001 From: Matthew Alexander Date: Fri, 7 Feb 2025 09:08:33 +1030 Subject: [PATCH 3/6] Fixes based on pull request feedback. --- sqeleton/databases/mssql.py | 23 ++++++----------------- 1 file changed, 6 insertions(+), 17 deletions(-) diff --git a/sqeleton/databases/mssql.py b/sqeleton/databases/mssql.py index e6f7854..7ac947f 100644 --- a/sqeleton/databases/mssql.py +++ b/sqeleton/databases/mssql.py @@ -17,7 +17,7 @@ from ..abcs.mixins import AbstractMixin_MD5, AbstractMixin_NormalizeValue, AbstractMixin_Schema from .base import BaseDialect, ThreadedDatabase, import_helper, ConnectError, Mixin_Schema from ..abcs import Compilable -from ..queries import this, table, Select +from ..queries import this, table, Select, SKIP from ..queries.ast_classes import TablePath from .base import TIMESTAMP_PRECISION_POS, Mixin_RandomSample, QueryError @@ -53,28 +53,16 @@ def table_information(self) -> TablePath: return table("information_schema", "tables") def list_tables(self, table_schema: str, like: Compilable = None) -> Select: - if table_schema == None: - query = ( - self.table_information() - .where( - this.table_name.like(like) if like is not None else SKIP, - this.table_type == "BASE TABLE", - ) - .select(this.table_name) - ) - else: - query = ( + return ( self.table_information() .where( - this.table_schema == table_schema, + this.table_schema == table_schema if table_schema is not None else SKIP, this.table_name.like(like) if like is not None else SKIP, this.table_type == "BASE TABLE", ) .select(this.table_name) ) - return query - class MsSQLDialect(BaseDialect, Mixin_Schema): name = "MsSQL" @@ -135,9 +123,10 @@ def concat(self, items: List[str]) -> str: return f"CONCAT({joined_exprs})" def _convert_db_precision_to_digits(self, p: int) -> int: - # Subtracting 2 due to wierd precision issues in PostgreSQL return super()._convert_db_precision_to_digits(p) - 2 + # Datetime is stored as UTC by default in MsSQL + # There is no current way to enforce a timezone for a session def set_timezone_to_utc(self) -> str: return "" @@ -153,7 +142,7 @@ def type_repr(self, t) -> str: bool: "BIT", datetime: "datetime2", }[t] - except: + except KeyError: return super().type_repr(t) class MsSQL(ThreadedDatabase): From f7268381fc59cb6c0e065ed3a8c1f31fb712b14c Mon Sep 17 00:00:00 2001 From: Matthew Alexander Date: Mon, 10 Feb 2025 14:41:33 +1030 Subject: [PATCH 4/6] 2nd wave of PR feedback. Also fix for foreign key column type. --- poetry.lock | 252 ++++++++++++++++++++++-------------- pyproject.toml | 4 +- sqeleton/databases/mssql.py | 33 +++-- 3 files changed, 173 insertions(+), 116 deletions(-) diff --git a/poetry.lock b/poetry.lock index 9971531..94230eb 100644 --- a/poetry.lock +++ b/poetry.lock @@ -1,4 +1,4 @@ -# This file is automatically @generated by Poetry 1.8.5 and should not be changed by hand. +# This file is automatically @generated by Poetry 2.0.1 and should not be changed by hand. [[package]] name = "asn1crypto" @@ -6,6 +6,7 @@ version = "1.5.1" description = "Fast ASN.1 parser and serializer with definitions for private keys, public keys, certificates, CRL, OCSP, CMS, PKCS#3, PKCS#7, PKCS#8, PKCS#12, PKCS#5, X.509 and TSP" optional = false python-versions = "*" +groups = ["main", "dev"] files = [ {file = "asn1crypto-1.5.1-py2.py3-none-any.whl", hash = "sha256:db4e40728b728508912cbb3d44f19ce188f218e9eba635821bb4b68564f8fd67"}, {file = "asn1crypto-1.5.1.tar.gz", hash = "sha256:13ae38502be632115abf8a24cbe5f4da52e3b5231990aff31123c805306ccb9c"}, @@ -17,6 +18,8 @@ version = "0.2.1" description = "Backport of the standard library zoneinfo module" optional = false python-versions = ">=3.6" +groups = ["main", "dev"] +markers = "python_version < \"3.9\"" files = [ {file = "backports.zoneinfo-0.2.1-cp36-cp36m-macosx_10_14_x86_64.whl", hash = "sha256:da6013fd84a690242c310d77ddb8441a559e9cb3d3d59ebac9aca1a57b2e18bc"}, {file = "backports.zoneinfo-0.2.1-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:89a48c0d158a3cc3f654da4c2de1ceba85263fafb861b98b59040a5086259722"}, @@ -41,13 +44,14 @@ tzdata = ["tzdata"] [[package]] name = "certifi" -version = "2024.12.14" +version = "2025.1.31" description = "Python package for providing Mozilla's CA Bundle." optional = false python-versions = ">=3.6" +groups = ["main", "dev"] files = [ - {file = "certifi-2024.12.14-py3-none-any.whl", hash = "sha256:1275f7a45be9464efc1173084eaa30f866fe2e47d389406136d332ed4967ec56"}, - {file = "certifi-2024.12.14.tar.gz", hash = "sha256:b650d30f370c2b724812bee08008be0c4163b163ddaec3f2546c1caf65f191db"}, + {file = "certifi-2025.1.31-py3-none-any.whl", hash = "sha256:ca78db4565a652026a4db2bcdf68f2fb589ea80d0be70e03929ed730746b84fe"}, + {file = "certifi-2025.1.31.tar.gz", hash = "sha256:3d5da6925056f6f18f119200434a4780a94263f10d1c21d032a6f6b2baa20651"}, ] [[package]] @@ -56,6 +60,7 @@ version = "1.17.1" description = "Foreign Function Interface for Python calling C code." optional = false python-versions = ">=3.8" +groups = ["main", "dev"] files = [ {file = "cffi-1.17.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:df8b1c11f177bc2313ec4b2d46baec87a5f3e71fc8b45dab2ee7cae86d9aba14"}, {file = "cffi-1.17.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:8f2cdc858323644ab277e9bb925ad72ae0e67f69e804f4898c070998d50b1a67"}, @@ -135,6 +140,7 @@ version = "3.4.1" description = "The Real First Universal Charset Detector. Open, modern and actively maintained alternative to Chardet." optional = false python-versions = ">=3.7" +groups = ["main", "dev"] files = [ {file = "charset_normalizer-3.4.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:91b36a978b5ae0ee86c394f5a54d6ef44db1de0815eb43de826d41d21e4af3de"}, {file = "charset_normalizer-3.4.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7461baadb4dc00fd9e0acbe254e3d7d2112e7f92ced2adc96e54ef6501c5f176"}, @@ -236,6 +242,7 @@ version = "8.1.8" description = "Composable command line interface toolkit" optional = false python-versions = ">=3.7" +groups = ["main", "dev"] files = [ {file = "click-8.1.8-py3-none-any.whl", hash = "sha256:63c132bbbed01578a06712a2d1f497bb62d9c1c0d329b7903a866228027263b2"}, {file = "click-8.1.8.tar.gz", hash = "sha256:ed53c9d8990d83c2a27deae68e4ee337473f6330c040a31d4225c9574d16096a"}, @@ -250,6 +257,7 @@ version = "0.2.9" description = "Python driver with native interface for ClickHouse" optional = false python-versions = "<4,>=3.7" +groups = ["main", "dev"] files = [ {file = "clickhouse-driver-0.2.9.tar.gz", hash = "sha256:050ea4870ead993910b39e7fae965dc1c347b2e8191dcd977cd4b385f9e19f87"}, {file = "clickhouse_driver-0.2.9-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:6ce04e9d0d0f39561f312d1ac1a8147bc9206e4267e1a23e20e0423ebac95534"}, @@ -372,6 +380,8 @@ version = "0.4.6" description = "Cross-platform colored terminal text." optional = false python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,>=2.7" +groups = ["main", "dev"] +markers = "platform_system == \"Windows\"" files = [ {file = "colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6"}, {file = "colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44"}, @@ -383,6 +393,7 @@ version = "7.6.1" description = "Code coverage measurement for Python" optional = false python-versions = ">=3.8" +groups = ["dev"] files = [ {file = "coverage-7.6.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:b06079abebbc0e89e6163b8e8f0e16270124c154dc6e4a47b413dd538859af16"}, {file = "coverage-7.6.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:cf4b19715bccd7ee27b6b120e7e9dd56037b9c0681dcc1adc9ba9db3d417fa36"}, @@ -467,6 +478,7 @@ version = "43.0.3" description = "cryptography is a package which provides cryptographic recipes and primitives to Python developers." optional = false python-versions = ">=3.7" +groups = ["main", "dev"] files = [ {file = "cryptography-43.0.3-cp37-abi3-macosx_10_9_universal2.whl", hash = "sha256:bf7a1932ac4176486eab36a19ed4c0492da5d97123f1406cf15e41b05e787d2e"}, {file = "cryptography-43.0.3-cp37-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:63efa177ff54aec6e1c0aefaa1a241232dcd37413835a9b674b6e3f0ae2bfd3e"}, @@ -516,69 +528,70 @@ version = "0.2.1" description = "parse dsn urls" optional = false python-versions = "*" +groups = ["main"] files = [ {file = "dsnparse-0.2.1.tar.gz", hash = "sha256:90956235967569c875994adbad0928c347a1592b5fc8ecd9c1b1a20397dae697"}, ] [[package]] name = "duckdb" -version = "1.1.3" +version = "1.2.0" description = "DuckDB in-process database" optional = false python-versions = ">=3.7.0" +groups = ["main", "dev"] files = [ - {file = "duckdb-1.1.3-cp310-cp310-macosx_12_0_arm64.whl", hash = "sha256:1c0226dc43e2ee4cc3a5a4672fddb2d76fd2cf2694443f395c02dd1bea0b7fce"}, - {file = "duckdb-1.1.3-cp310-cp310-macosx_12_0_universal2.whl", hash = "sha256:7c71169fa804c0b65e49afe423ddc2dc83e198640e3b041028da8110f7cd16f7"}, - {file = "duckdb-1.1.3-cp310-cp310-macosx_12_0_x86_64.whl", hash = "sha256:872d38b65b66e3219d2400c732585c5b4d11b13d7a36cd97908d7981526e9898"}, - {file = "duckdb-1.1.3-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:25fb02629418c0d4d94a2bc1776edaa33f6f6ccaa00bd84eb96ecb97ae4b50e9"}, - {file = "duckdb-1.1.3-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9e3f5cd604e7c39527e6060f430769b72234345baaa0987f9500988b2814f5e4"}, - {file = "duckdb-1.1.3-cp310-cp310-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:08935700e49c187fe0e9b2b86b5aad8a2ccd661069053e38bfaed3b9ff795efd"}, - {file = "duckdb-1.1.3-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:f9b47036945e1db32d70e414a10b1593aec641bd4c5e2056873d971cc21e978b"}, - {file = "duckdb-1.1.3-cp310-cp310-win_amd64.whl", hash = "sha256:35c420f58abc79a68a286a20fd6265636175fadeca1ce964fc8ef159f3acc289"}, - {file = "duckdb-1.1.3-cp311-cp311-macosx_12_0_arm64.whl", hash = "sha256:4f0e2e5a6f5a53b79aee20856c027046fba1d73ada6178ed8467f53c3877d5e0"}, - {file = "duckdb-1.1.3-cp311-cp311-macosx_12_0_universal2.whl", hash = "sha256:911d58c22645bfca4a5a049ff53a0afd1537bc18fedb13bc440b2e5af3c46148"}, - {file = "duckdb-1.1.3-cp311-cp311-macosx_12_0_x86_64.whl", hash = "sha256:c443d3d502335e69fc1e35295fcfd1108f72cb984af54c536adfd7875e79cee5"}, - {file = "duckdb-1.1.3-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0a55169d2d2e2e88077d91d4875104b58de45eff6a17a59c7dc41562c73df4be"}, - {file = "duckdb-1.1.3-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9d0767ada9f06faa5afcf63eb7ba1befaccfbcfdac5ff86f0168c673dd1f47aa"}, - {file = "duckdb-1.1.3-cp311-cp311-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:51c6d79e05b4a0933672b1cacd6338f882158f45ef9903aef350c4427d9fc898"}, - {file = "duckdb-1.1.3-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:183ac743f21c6a4d6adfd02b69013d5fd78e5e2cd2b4db023bc8a95457d4bc5d"}, - {file = "duckdb-1.1.3-cp311-cp311-win_amd64.whl", hash = "sha256:a30dd599b8090ea6eafdfb5a9f1b872d78bac318b6914ada2d35c7974d643640"}, - {file = "duckdb-1.1.3-cp312-cp312-macosx_12_0_arm64.whl", hash = "sha256:a433ae9e72c5f397c44abdaa3c781d94f94f4065bcbf99ecd39433058c64cb38"}, - {file = "duckdb-1.1.3-cp312-cp312-macosx_12_0_universal2.whl", hash = "sha256:d08308e0a46c748d9c30f1d67ee1143e9c5ea3fbcccc27a47e115b19e7e78aa9"}, - {file = "duckdb-1.1.3-cp312-cp312-macosx_12_0_x86_64.whl", hash = "sha256:5d57776539211e79b11e94f2f6d63de77885f23f14982e0fac066f2885fcf3ff"}, - {file = "duckdb-1.1.3-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e59087dbbb63705f2483544e01cccf07d5b35afa58be8931b224f3221361d537"}, - {file = "duckdb-1.1.3-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4ebf5f60ddbd65c13e77cddb85fe4af671d31b851f125a4d002a313696af43f1"}, - {file = "duckdb-1.1.3-cp312-cp312-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:e4ef7ba97a65bd39d66f2a7080e6fb60e7c3e41d4c1e19245f90f53b98e3ac32"}, - {file = "duckdb-1.1.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:f58db1b65593ff796c8ea6e63e2e144c944dd3d51c8d8e40dffa7f41693d35d3"}, - {file = "duckdb-1.1.3-cp312-cp312-win_amd64.whl", hash = "sha256:e86006958e84c5c02f08f9b96f4bc26990514eab329b1b4f71049b3727ce5989"}, - {file = "duckdb-1.1.3-cp313-cp313-macosx_12_0_arm64.whl", hash = "sha256:0897f83c09356206ce462f62157ce064961a5348e31ccb2a557a7531d814e70e"}, - {file = "duckdb-1.1.3-cp313-cp313-macosx_12_0_universal2.whl", hash = "sha256:cddc6c1a3b91dcc5f32493231b3ba98f51e6d3a44fe02839556db2b928087378"}, - {file = "duckdb-1.1.3-cp313-cp313-macosx_12_0_x86_64.whl", hash = "sha256:1d9ab6143e73bcf17d62566e368c23f28aa544feddfd2d8eb50ef21034286f24"}, - {file = "duckdb-1.1.3-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2f073d15d11a328f2e6d5964a704517e818e930800b7f3fa83adea47f23720d3"}, - {file = "duckdb-1.1.3-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d5724fd8a49e24d730be34846b814b98ba7c304ca904fbdc98b47fa95c0b0cee"}, - {file = "duckdb-1.1.3-cp313-cp313-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:51e7dbd968b393343b226ab3f3a7b5a68dee6d3fe59be9d802383bf916775cb8"}, - {file = "duckdb-1.1.3-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:00cca22df96aa3473fe4584f84888e2cf1c516e8c2dd837210daec44eadba586"}, - {file = "duckdb-1.1.3-cp313-cp313-win_amd64.whl", hash = "sha256:77f26884c7b807c7edd07f95cf0b00e6d47f0de4a534ac1706a58f8bc70d0d31"}, - {file = "duckdb-1.1.3-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a4748635875fc3c19a7320a6ae7410f9295557450c0ebab6d6712de12640929a"}, - {file = "duckdb-1.1.3-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b74e121ab65dbec5290f33ca92301e3a4e81797966c8d9feef6efdf05fc6dafd"}, - {file = "duckdb-1.1.3-cp37-cp37m-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:9c619e4849837c8c83666f2cd5c6c031300cd2601e9564b47aa5de458ff6e69d"}, - {file = "duckdb-1.1.3-cp37-cp37m-win_amd64.whl", hash = "sha256:0ba6baa0af33ded836b388b09433a69b8bec00263247f6bf0a05c65c897108d3"}, - {file = "duckdb-1.1.3-cp38-cp38-macosx_12_0_arm64.whl", hash = "sha256:ecb1dc9062c1cc4d2d88a5e5cd8cc72af7818ab5a3c0f796ef0ffd60cfd3efb4"}, - {file = "duckdb-1.1.3-cp38-cp38-macosx_12_0_universal2.whl", hash = "sha256:5ace6e4b1873afdd38bd6cc8fcf90310fb2d454f29c39a61d0c0cf1a24ad6c8d"}, - {file = "duckdb-1.1.3-cp38-cp38-macosx_12_0_x86_64.whl", hash = "sha256:a1fa0c502f257fa9caca60b8b1478ec0f3295f34bb2efdc10776fc731b8a6c5f"}, - {file = "duckdb-1.1.3-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6411e21a2128d478efbd023f2bdff12464d146f92bc3e9c49247240448ace5a6"}, - {file = "duckdb-1.1.3-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c5336939d83837af52731e02b6a78a446794078590aa71fd400eb17f083dda3e"}, - {file = "duckdb-1.1.3-cp38-cp38-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f549af9f7416573ee48db1cf8c9d27aeed245cb015f4b4f975289418c6cf7320"}, - {file = "duckdb-1.1.3-cp38-cp38-win_amd64.whl", hash = "sha256:2141c6b28162199999075d6031b5d63efeb97c1e68fb3d797279d31c65676269"}, - {file = "duckdb-1.1.3-cp39-cp39-macosx_12_0_arm64.whl", hash = "sha256:09c68522c30fc38fc972b8a75e9201616b96ae6da3444585f14cf0d116008c95"}, - {file = "duckdb-1.1.3-cp39-cp39-macosx_12_0_universal2.whl", hash = "sha256:8ee97ec337794c162c0638dda3b4a30a483d0587deda22d45e1909036ff0b739"}, - {file = "duckdb-1.1.3-cp39-cp39-macosx_12_0_x86_64.whl", hash = "sha256:a1f83c7217c188b7ab42e6a0963f42070d9aed114f6200e3c923c8899c090f16"}, - {file = "duckdb-1.1.3-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1aa3abec8e8995a03ff1a904b0e66282d19919f562dd0a1de02f23169eeec461"}, - {file = "duckdb-1.1.3-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:80158f4c7c7ada46245837d5b6869a336bbaa28436fbb0537663fa324a2750cd"}, - {file = "duckdb-1.1.3-cp39-cp39-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:647f17bd126170d96a38a9a6f25fca47ebb0261e5e44881e3782989033c94686"}, - {file = "duckdb-1.1.3-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:252d9b17d354beb9057098d4e5d5698e091a4f4a0d38157daeea5fc0ec161670"}, - {file = "duckdb-1.1.3-cp39-cp39-win_amd64.whl", hash = "sha256:eeacb598120040e9591f5a4edecad7080853aa8ac27e62d280f151f8c862afa3"}, - {file = "duckdb-1.1.3.tar.gz", hash = "sha256:68c3a46ab08836fe041d15dcbf838f74a990d551db47cb24ab1c4576fc19351c"}, + {file = "duckdb-1.2.0-cp310-cp310-macosx_12_0_arm64.whl", hash = "sha256:7452d3655063cc3062504b5b22f8968acb96ffcdc6c2b8207bbec9da1de1f884"}, + {file = "duckdb-1.2.0-cp310-cp310-macosx_12_0_universal2.whl", hash = "sha256:28d40a269212270e08b8541ea0922c3a893407897481cd484ad896bc2ba77a00"}, + {file = "duckdb-1.2.0-cp310-cp310-macosx_12_0_x86_64.whl", hash = "sha256:ed4586aa441a57f68e5fa5655b8a86509e1c3b6521ad4d40455ae4594e18cd59"}, + {file = "duckdb-1.2.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:07823a485bc656cf2f63020117fec5245aa7fb8d085a43700208ac8b7e728866"}, + {file = "duckdb-1.2.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d3b86475373cbd000035f34ba02420bc8ff432eaa646b09c5de975610120155d"}, + {file = "duckdb-1.2.0-cp310-cp310-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:be7a14d1380ea8345b27bf5bbe77209c14ee0277c7401f504a2519936f9d087e"}, + {file = "duckdb-1.2.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:c491485a14f806d12407d85510da8f09ad5d9a079ec449b7bff75eea5f9431c3"}, + {file = "duckdb-1.2.0-cp310-cp310-win_amd64.whl", hash = "sha256:c8f6c09c939deb0bccaa6485798dacef0969046d1aa845ef10063558c8ee14e0"}, + {file = "duckdb-1.2.0-cp311-cp311-macosx_12_0_arm64.whl", hash = "sha256:970a396b133608b5acb297cc172097866abbbce6cc57a2ec6b128b4f99a63ecd"}, + {file = "duckdb-1.2.0-cp311-cp311-macosx_12_0_universal2.whl", hash = "sha256:ecd713a8388c1e173ef04aa5545873e93d44cb950c2af5459b44668676abc873"}, + {file = "duckdb-1.2.0-cp311-cp311-macosx_12_0_x86_64.whl", hash = "sha256:9e1323ab11ca9ee72bb3c54dfb4919add4b2aa524085bac80c2a888ce673cdf0"}, + {file = "duckdb-1.2.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c22e4ddcf1a76b4cf90cac23de06910557b239b4ba783e6dec1e04210de897e9"}, + {file = "duckdb-1.2.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:55f2b0fbe63786061b028f48e41efcecfdcf3d5f8cb5ce415ee1d5885691c19f"}, + {file = "duckdb-1.2.0-cp311-cp311-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:d6dc9fd4c6f3505d7d69eed05d26a345d9652a4dab791b6d95ac18d6cdda2041"}, + {file = "duckdb-1.2.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:4788c1f6d588be232b4a9dbc2c4a3546cd1ced945a1182d785cf913a5bd122a3"}, + {file = "duckdb-1.2.0-cp311-cp311-win_amd64.whl", hash = "sha256:eeb5a517445d18949610cd30da1215303693cdae2942e6b1b7661314380f715e"}, + {file = "duckdb-1.2.0-cp312-cp312-macosx_12_0_arm64.whl", hash = "sha256:c0427501908d3b4fe464913b0ae2418ff52d1fa24b3982d864169b1d54b6bbee"}, + {file = "duckdb-1.2.0-cp312-cp312-macosx_12_0_universal2.whl", hash = "sha256:33df2430f35e68739fd9fb6bbe1a5f86f4f46b362c9abb3a3f74a989560ef597"}, + {file = "duckdb-1.2.0-cp312-cp312-macosx_12_0_x86_64.whl", hash = "sha256:fd8ca2910efb85f0dd0d50383eaed9b6b7e86e6cacb032c36712e84265855e58"}, + {file = "duckdb-1.2.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9747d3d2a3290c795c0343eb927dbc75ca78d0440726824c2a39d9634fba9394"}, + {file = "duckdb-1.2.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:91704accb74223267ae226f3470d71f7ad824549482b3f7fc91710a9fe5a1152"}, + {file = "duckdb-1.2.0-cp312-cp312-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:d9357737c6699b1f57e1d02b299371b2634bf08927d4e8386146ec5e4d1ebb31"}, + {file = "duckdb-1.2.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:8d61ba5272dd1bf772b7a74f4964e83080602f8f6e9a46a0fa7203a4e0e05249"}, + {file = "duckdb-1.2.0-cp312-cp312-win_amd64.whl", hash = "sha256:f317cfa2f6ff3bc209985715669f4b8dd601faa69e46a206163e53b8db61a1d1"}, + {file = "duckdb-1.2.0-cp313-cp313-macosx_12_0_arm64.whl", hash = "sha256:7feaaf185e89138e3d0f4dc9bf647767df1a3f080b4be719837613cb9288b79e"}, + {file = "duckdb-1.2.0-cp313-cp313-macosx_12_0_universal2.whl", hash = "sha256:a52bb5991656cab0b90537c5e05746019be09c5f63535db03ddbff6f65b2ccb3"}, + {file = "duckdb-1.2.0-cp313-cp313-macosx_12_0_x86_64.whl", hash = "sha256:4d10d5596667f82812b130f3e7fffb282a31c05525ee2f8adddfaa1a07529fe9"}, + {file = "duckdb-1.2.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:436b7c0cd40a63fdce8477b03868026b60b2376cf155812be07392213b707874"}, + {file = "duckdb-1.2.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1c6b8464d9bd5770071d4a00a457b4c09974b930ccb1fe99991cfa8ddda0b905"}, + {file = "duckdb-1.2.0-cp313-cp313-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:2835bc4828d2e1f8ad58f8ef946815af8beb55f9697e6e9d5a028b81abc02c62"}, + {file = "duckdb-1.2.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:b35284599ac6bf6a09ffd334bc7f4d5df47126bce054a0f73b53f3eac1a5688e"}, + {file = "duckdb-1.2.0-cp313-cp313-win_amd64.whl", hash = "sha256:5cf770fdd5244e47b3cbca6dd4ef2d13b6b9a6071f3fc7b55487e9ddff19e9cd"}, + {file = "duckdb-1.2.0-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:be0ccd37c1c000f2a3a7e8852d9cc64de4549ab484d4ecc05f8a3df76443d3b8"}, + {file = "duckdb-1.2.0-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d89d0111609383bd440f1afe2b540969ec02cd1e11959df0313efb644c14d061"}, + {file = "duckdb-1.2.0-cp37-cp37m-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:304c5395f9bd788b1e35a71407b80e3af116daa77b05dc417a6deb986ffd4def"}, + {file = "duckdb-1.2.0-cp38-cp38-macosx_12_0_arm64.whl", hash = "sha256:6effb33a2bed59ddaa53cb5e3cfb2ad47e2fb98a156f49073df7c755394ab52a"}, + {file = "duckdb-1.2.0-cp38-cp38-macosx_12_0_universal2.whl", hash = "sha256:a405579b402e49ad5b52e58953e29a489c4f611a0c768088a50a086baea5e134"}, + {file = "duckdb-1.2.0-cp38-cp38-macosx_12_0_x86_64.whl", hash = "sha256:eb4ce9c6cfc0f45d1cf827e5a10294fdfd235e221aeebf10d3a31e898e3a2e0e"}, + {file = "duckdb-1.2.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:648e2179e1a56cca884c1c993d12f07807f5a285d78972cb3a001736c8f6d332"}, + {file = "duckdb-1.2.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1b88bf1cc28d76e23534ae1485c5fefcac610ee98f61b378ec255530387fbf93"}, + {file = "duckdb-1.2.0-cp38-cp38-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:4548e068e8dfbda5839c3a5ed1f036f0773d984d02d933ee54395c864228fe9b"}, + {file = "duckdb-1.2.0-cp38-cp38-win_amd64.whl", hash = "sha256:a679ab1ab14bc2adf9ce8bc06ae64b9312a63b93925becc8959ff38350d913de"}, + {file = "duckdb-1.2.0-cp39-cp39-macosx_12_0_arm64.whl", hash = "sha256:f802ddf4d87d319fd957d5dbc283db750c970909b6456bd3e3a51f61e153b524"}, + {file = "duckdb-1.2.0-cp39-cp39-macosx_12_0_universal2.whl", hash = "sha256:238093c290e63f010684a970e1af0780f8974b3a812b4f6a734d78a73658bd3d"}, + {file = "duckdb-1.2.0-cp39-cp39-macosx_12_0_x86_64.whl", hash = "sha256:a7d2577229c699431f620bdd1e97175e558f8bfd0f56fa6bcc41f13841148b91"}, + {file = "duckdb-1.2.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8336c9e4c66ab7fd1ba8786a2551f96f2bbc9a8d6d86f109c5d4c86634635e4f"}, + {file = "duckdb-1.2.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0d01a72a4c6ba78882bc5d184b0768c9ac4351406af3e43a9da5810400acbdee"}, + {file = "duckdb-1.2.0-cp39-cp39-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5b4d0b997702f74669ffb43283f3291ee05ca464b68deabee9a365cd40fc729e"}, + {file = "duckdb-1.2.0-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:69ce703855e30aa253bf47a4002ee35a7c63ff970306879ae76ab355bfe03632"}, + {file = "duckdb-1.2.0-cp39-cp39-win_amd64.whl", hash = "sha256:a58c0763068fac7cf202a5ac9c0f85c0b6044a98185d73b5f049f955fd10b4e8"}, + {file = "duckdb-1.2.0.tar.gz", hash = "sha256:a5ce81828e6d1c3f06836d3bda38eef8355765f08ad5ce239abd6f56934dd1f8"}, ] [[package]] @@ -587,6 +600,7 @@ version = "3.16.1" description = "A platform independent file lock." optional = false python-versions = ">=3.8" +groups = ["main", "dev"] files = [ {file = "filelock-3.16.1-py3-none-any.whl", hash = "sha256:2082e5703d51fbf98ea75855d9d5527e33d8ff23099bec374a134febee6946b0"}, {file = "filelock-3.16.1.tar.gz", hash = "sha256:c249fbfcd5db47e5e2d6d62198e565475ee65e4831e2561c8e313fa7eb961435"}, @@ -603,6 +617,7 @@ version = "3.10" description = "Internationalized Domain Names in Applications (IDNA)" optional = false python-versions = ">=3.6" +groups = ["main", "dev"] files = [ {file = "idna-3.10-py3-none-any.whl", hash = "sha256:946d195a0d259cbba61165e88e65941f16e9b36ea6ddb97f00452bae8b1287d3"}, {file = "idna-3.10.tar.gz", hash = "sha256:12f65c9b470abda6dc35cf8e63cc574b1c52b11df2c86030af0ac09b01b13ea9"}, @@ -617,6 +632,8 @@ version = "2.0.3" description = "Links recognition library with FULL unicode support." optional = true python-versions = ">=3.7" +groups = ["main"] +markers = "extra == \"tui\"" files = [ {file = "linkify-it-py-2.0.3.tar.gz", hash = "sha256:68cda27e162e9215c17d786649d1da0021a451bdc436ef9e0fa0ba5234b9b048"}, {file = "linkify_it_py-2.0.3-py3-none-any.whl", hash = "sha256:6bcbc417b0ac14323382aef5c5192c0075bf8a9d6b41820a2b66371eac6b6d79"}, @@ -637,6 +654,7 @@ version = "3.0.0" description = "Python port of markdown-it. Markdown parsing, done right!" optional = false python-versions = ">=3.8" +groups = ["main"] files = [ {file = "markdown-it-py-3.0.0.tar.gz", hash = "sha256:e3f60a94fa066dc52ec76661e37c851cb232d92f9886b15cb560aaada2df8feb"}, {file = "markdown_it_py-3.0.0-py3-none-any.whl", hash = "sha256:355216845c60bd96232cd8d8c40e8f9765cc86f46880e43a8fd22dc1a1a8cab1"}, @@ -663,6 +681,8 @@ version = "0.4.2" description = "Collection of plugins for markdown-it-py" optional = true python-versions = ">=3.8" +groups = ["main"] +markers = "extra == \"tui\"" files = [ {file = "mdit_py_plugins-0.4.2-py3-none-any.whl", hash = "sha256:0c673c3f889399a33b95e88d2f0d111b4447bdfea7f237dab2d488f459835636"}, {file = "mdit_py_plugins-0.4.2.tar.gz", hash = "sha256:5f2cd1fdb606ddf152d37ec30e46101a60512bc0e5fa1a7002c36647b09e26b5"}, @@ -682,6 +702,7 @@ version = "0.1.2" description = "Markdown URL utilities" optional = false python-versions = ">=3.7" +groups = ["main"] files = [ {file = "mdurl-0.1.2-py3-none-any.whl", hash = "sha256:84008a41e51615a49fc9966191ff91509e3c40b939176e643fd50a5c2196b8f8"}, {file = "mdurl-0.1.2.tar.gz", hash = "sha256:bb413d29f5eea38f31dd4754dd7377d4465116fb207585f97bf925588687c1ba"}, @@ -693,6 +714,7 @@ version = "9.0.0" description = "MySQL driver written in Python" optional = false python-versions = ">=3.8" +groups = ["main", "dev"] files = [ {file = "mysql-connector-python-9.0.0.tar.gz", hash = "sha256:8a404db37864acca43fd76222d1fbc7ff8d17d4ce02d803289c2141c2693ce9e"}, {file = "mysql_connector_python-9.0.0-cp310-cp310-macosx_13_0_arm64.whl", hash = "sha256:72bfd0213364c2bea0244f6432ababb2f204cff43f4f886c65dca2be11f536ee"}, @@ -734,6 +756,7 @@ version = "24.2" description = "Core utilities for Python packages" optional = false python-versions = ">=3.8" +groups = ["main", "dev"] files = [ {file = "packaging-24.2-py3-none-any.whl", hash = "sha256:09abb1bccd265c01f4a3aa3f7a7db064b36514d2cba19a2f694fe6150451a759"}, {file = "packaging-24.2.tar.gz", hash = "sha256:c228a6dc5e932d346bc5739379109d49e8853dd8223571c7c5b55260edc0b97f"}, @@ -745,6 +768,7 @@ version = "0.9.0" description = "Parameterized testing with any Python test framework" optional = false python-versions = ">=3.7" +groups = ["dev"] files = [ {file = "parameterized-0.9.0-py2.py3-none-any.whl", hash = "sha256:4e0758e3d41bea3bbd05ec14fc2c24736723f243b28d702081aef438c9372b1b"}, {file = "parameterized-0.9.0.tar.gz", hash = "sha256:7fc905272cefa4f364c1a3429cbbe9c0f98b793988efb5bf90aac80f08db09b1"}, @@ -759,6 +783,7 @@ version = "4.3.6" description = "A small Python package for determining appropriate platform-specific dirs, e.g. a `user data dir`." optional = false python-versions = ">=3.8" +groups = ["main", "dev"] files = [ {file = "platformdirs-4.3.6-py3-none-any.whl", hash = "sha256:73e575e1408ab8103900836b97580d5307456908a03e92031bab39e4554cc3fb"}, {file = "platformdirs-4.3.6.tar.gz", hash = "sha256:357fb2acbc885b0419afd3ce3ed34564c13c9b95c89360cd9563f73aa5e2b907"}, @@ -775,6 +800,7 @@ version = "0.8.4" description = "Client for the Presto distributed SQL Engine" optional = false python-versions = "*" +groups = ["main", "dev"] files = [ {file = "presto-python-client-0.8.4.tar.gz", hash = "sha256:b5d9d7278c10af932c6b5ce3a417dfa883b539e947ece1f7f43ba4632c2994c5"}, {file = "presto_python_client-0.8.4-py3-none-any.whl", hash = "sha256:f8ae1e139f7676fb955dd180027ac9ac6450a8552d53b6b85b79fc1da6d37190"}, @@ -793,13 +819,15 @@ tests = ["google-auth", "httpretty", "pytest", "pytest-runner", "requests-kerber [[package]] name = "prompt-toolkit" -version = "3.0.48" +version = "3.0.50" description = "Library for building powerful interactive command lines in Python" optional = true -python-versions = ">=3.7.0" +python-versions = ">=3.8.0" +groups = ["main"] +markers = "extra == \"tui\"" files = [ - {file = "prompt_toolkit-3.0.48-py3-none-any.whl", hash = "sha256:f49a827f90062e411f1ce1f854f2aedb3c23353244f8108b89283587397ac10e"}, - {file = "prompt_toolkit-3.0.48.tar.gz", hash = "sha256:d6623ab0477a80df74e646bdbc93621143f5caf104206aa29294d53de1a03d90"}, + {file = "prompt_toolkit-3.0.50-py3-none-any.whl", hash = "sha256:9b6427eb19e479d98acff65196a307c555eb567989e6d88ebbb1b509d9779198"}, + {file = "prompt_toolkit-3.0.50.tar.gz", hash = "sha256:544748f3860a2623ca5cd6d2795e7a14f3d0e1c3c9728359013f79877fc89bab"}, ] [package.dependencies] @@ -811,6 +839,7 @@ version = "2.9.10" description = "psycopg2 - Python-PostgreSQL Database Adapter" optional = false python-versions = ">=3.8" +groups = ["main", "dev"] files = [ {file = "psycopg2-binary-2.9.10.tar.gz", hash = "sha256:4b3df0e6990aa98acda57d983942eff13d824135fe2250e6522edaa782a06de2"}, {file = "psycopg2_binary-2.9.10-cp310-cp310-macosx_12_0_x86_64.whl", hash = "sha256:0ea8e3d0ae83564f2fc554955d327fa081d065c8ca5cc6d2abb643e2c9c1200f"}, @@ -888,6 +917,7 @@ version = "2.22" description = "C parser in Python" optional = false python-versions = ">=3.8" +groups = ["main", "dev"] files = [ {file = "pycparser-2.22-py3-none-any.whl", hash = "sha256:c3702b6d3dd8c7abc1afa565d7e63d53a1d0bd86cdc24edd75470f4de499cfcc"}, {file = "pycparser-2.22.tar.gz", hash = "sha256:491c8be9c040f5390f5bf44a5b07752bd07f56edf992381b05c701439eec10f6"}, @@ -899,6 +929,7 @@ version = "2.19.1" description = "Pygments is a syntax highlighting package written in Python." optional = false python-versions = ">=3.8" +groups = ["main"] files = [ {file = "pygments-2.19.1-py3-none-any.whl", hash = "sha256:9ea1544ad55cecf4b8242fab6dd35a93bbce657034b0611ee383099054ab6d8c"}, {file = "pygments-2.19.1.tar.gz", hash = "sha256:61c16d2a8576dc0649d9f39e089b5f02bcd27fba10d8fb4dcc28173f7a45151f"}, @@ -913,6 +944,7 @@ version = "2.9.0" description = "JSON Web Token implementation in Python" optional = false python-versions = ">=3.8" +groups = ["main", "dev"] files = [ {file = "PyJWT-2.9.0-py3-none-any.whl", hash = "sha256:3b02fb0f44517787776cf48f2ae25d8e14f300e6d7545a4315cee571a415e850"}, {file = "pyjwt-2.9.0.tar.gz", hash = "sha256:7e1e5b56cc735432a7369cbfa0efe50fa113ebecdc04ae6922deba8b84582d0c"}, @@ -930,6 +962,7 @@ version = "2.3.2" description = "DB-API interface to Microsoft SQL Server for Python. (new Cython-based version)" optional = false python-versions = "*" +groups = ["main", "dev"] files = [ {file = "pymssql-2.3.2-cp310-cp310-macosx_13_0_x86_64.whl", hash = "sha256:73fac766b448613d7ae26e6b304b2cb8a7ffebccaa373633bad3b3cbcc829935"}, {file = "pymssql-2.3.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:eb629b5fb0376fbf39d575cf1365e504b84877b19f9e8d53caa5228fed56894a"}, @@ -997,6 +1030,7 @@ version = "24.3.0" description = "Python wrapper module around the OpenSSL library" optional = false python-versions = ">=3.7" +groups = ["main", "dev"] files = [ {file = "pyOpenSSL-24.3.0-py3-none-any.whl", hash = "sha256:e474f5a473cd7f92221cc04976e48f4d11502804657a08a989fb3be5514c904a"}, {file = "pyopenssl-24.3.0.tar.gz", hash = "sha256:49f7a019577d834746bc55c5fce6ecbcec0f2b4ec5ce1cf43a9a173b8138bb36"}, @@ -1015,6 +1049,7 @@ version = "2.9.0.post0" description = "Extensions to the standard Python datetime module" optional = false python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,>=2.7" +groups = ["main", "dev"] files = [ {file = "python-dateutil-2.9.0.post0.tar.gz", hash = "sha256:37dd54208da7e1cd875388217d5e00ebd4179249f90fb72437e91a35459a0ad3"}, {file = "python_dateutil-2.9.0.post0-py2.py3-none-any.whl", hash = "sha256:a8b2bc7bffae282281c8140a97d3aa9c14da0b136dfe83f850eea9a5f7470427"}, @@ -1025,13 +1060,14 @@ six = ">=1.5" [[package]] name = "pytz" -version = "2024.2" +version = "2025.1" description = "World timezone definitions, modern and historical" optional = false python-versions = "*" +groups = ["main", "dev"] files = [ - {file = "pytz-2024.2-py2.py3-none-any.whl", hash = "sha256:31c7c1817eb7fae7ca4b8c7ee50c72f93aa2dd863de768e1ef4245d426aa0725"}, - {file = "pytz-2024.2.tar.gz", hash = "sha256:2aa355083c50a0f93fa581709deac0c9ad65cca8a9e9beac660adcbd493c798a"}, + {file = "pytz-2025.1-py2.py3-none-any.whl", hash = "sha256:89dd22dca55b46eac6eda23b2d72721bf1bdfef212645d81513ef5d03038de57"}, + {file = "pytz-2025.1.tar.gz", hash = "sha256:c2db42be2a2518b28e65f9207c4d05e6ff547d1efa4086469ef855e4ab70178e"}, ] [[package]] @@ -1040,6 +1076,7 @@ version = "2.32.3" description = "Python HTTP for Humans." optional = false python-versions = ">=3.8" +groups = ["main", "dev"] files = [ {file = "requests-2.32.3-py3-none-any.whl", hash = "sha256:70761cfe03c773ceb22aa2f671b4757976145175cdfca038c02654d061d6dcc6"}, {file = "requests-2.32.3.tar.gz", hash = "sha256:55365417734eb18255590a9ff9eb97e9e1da868d4ccd6402399eaf68af20a760"}, @@ -1061,6 +1098,7 @@ version = "13.9.4" description = "Render rich text, tables, progress bars, syntax highlighting, markdown and more to the terminal" optional = false python-versions = ">=3.8.0" +groups = ["main"] files = [ {file = "rich-13.9.4-py3-none-any.whl", hash = "sha256:6049d5e6ec054bf2779ab3358186963bac2ea89175919d699e378b99738c2a90"}, {file = "rich-13.9.4.tar.gz", hash = "sha256:439594978a49a09530cff7ebc4b5c7103ef57baf48d5ea3184f21d9a2befa098"}, @@ -1080,6 +1118,7 @@ version = "0.5.2" description = "Type dispatch and validation for run-time Python" optional = false python-versions = "<4.0,>=3.8" +groups = ["main"] files = [ {file = "runtype-0.5.2-py3-none-any.whl", hash = "sha256:fb8b7098d62621dae682135f66ddf4e366a094e1c83e271730e1031c84b330fa"}, {file = "runtype-0.5.2.tar.gz", hash = "sha256:496797110f7a94001fefadc181708b26262a0be5b456eb780300b6436b79d0fd"}, @@ -1091,6 +1130,7 @@ version = "1.17.0" description = "Python 2 and 3 compatibility utilities" optional = false python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,>=2.7" +groups = ["main", "dev"] files = [ {file = "six-1.17.0-py2.py3-none-any.whl", hash = "sha256:4721f391ed90541fddacab5acf947aa0d3dc7d27b2e1e8eda2be8970586c3274"}, {file = "six-1.17.0.tar.gz", hash = "sha256:ff70335d468e7eb6ec65b95b99d3a2836546063f63acc5171de367e834932a81"}, @@ -1098,37 +1138,38 @@ files = [ [[package]] name = "snowflake-connector-python" -version = "3.12.4" +version = "3.13.2" description = "Snowflake Connector for Python" optional = false python-versions = ">=3.8" +groups = ["main", "dev"] files = [ - {file = "snowflake_connector_python-3.12.4-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:6f141c159e3244bd660279f87f32e39351b2845fcb75f8138f31d2219f983b05"}, - {file = "snowflake_connector_python-3.12.4-cp310-cp310-macosx_11_0_x86_64.whl", hash = "sha256:091458ba777c24adff659c5c28f0f5bb0bcca8a9b6ecc5641ae25b7c20a8f43d"}, - {file = "snowflake_connector_python-3.12.4-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:23049d341da681ec7131cead71cdf7b1761ae5bcc08bcbdb931dcef6c25e8a5f"}, - {file = "snowflake_connector_python-3.12.4-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:cc88a09d77a8ce7e445094b2409b606ddb208b5fc9f7c7a379d0255a8d566e9d"}, - {file = "snowflake_connector_python-3.12.4-cp310-cp310-win_amd64.whl", hash = "sha256:3c33fbba036805c1767ea48eb40ffc3fb79d61f2a4bb4e77b571ea6f6a998be8"}, - {file = "snowflake_connector_python-3.12.4-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:2ec5cfaa1526084cf4d0e7849d5ace601245cb4ad9675ab3cd7d799b3abea481"}, - {file = "snowflake_connector_python-3.12.4-cp311-cp311-macosx_11_0_x86_64.whl", hash = "sha256:ff225824b3a0fa5e822442de72172f97028f04ae183877f1305d538d8d6c5d11"}, - {file = "snowflake_connector_python-3.12.4-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a9beced2789dc75e8f1e749aa637e7ec9b03302b4ed4b793ae0f1ff32823370e"}, - {file = "snowflake_connector_python-3.12.4-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5ea47450a04ff713f3adf28053e34103bd990291e62daee9721c76597af4b2b5"}, - {file = "snowflake_connector_python-3.12.4-cp311-cp311-win_amd64.whl", hash = "sha256:748f9125854dca07ea471bb2bb3c5bb932a53f9b8a77ba348b50b738c77203ce"}, - {file = "snowflake_connector_python-3.12.4-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:4bcd0371b20d199f15e6a3c0b489bf18e27f2a88c84cf3194b2569ca039fa7d1"}, - {file = "snowflake_connector_python-3.12.4-cp312-cp312-macosx_11_0_x86_64.whl", hash = "sha256:7900d82a450b206fa2ed6c42cd65d9b3b9fd4547eca1696937175fac2a03ba37"}, - {file = "snowflake_connector_python-3.12.4-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:300f0562aeea55e40ee03b45205dbef7b78f5ba2f1787a278c7b807e7d8db22c"}, - {file = "snowflake_connector_python-3.12.4-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a6762a00948f003be55d7dc5de9de690315d01951a94371ec3db069d9303daba"}, - {file = "snowflake_connector_python-3.12.4-cp312-cp312-win_amd64.whl", hash = "sha256:83ca896790a7463b6c8cd42e1a29b8ea197cc920839ae6ee96a467475eab4ec2"}, - {file = "snowflake_connector_python-3.12.4-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:886d2cbf0aaa5eac81df05efefaff145773e732d88ded7f6e2465580a4c31d1e"}, - {file = "snowflake_connector_python-3.12.4-cp38-cp38-macosx_11_0_x86_64.whl", hash = "sha256:c06f553486ba6715f18bb2933446adace4cfbb40f54b7fa0d46839930ecf945a"}, - {file = "snowflake_connector_python-3.12.4-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:158f616e36453e72550937256b160badcea6d4fb38bff21fdf551813ebe409b4"}, - {file = "snowflake_connector_python-3.12.4-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:53b80d2e3233419920f116e277ad8d422fbbce172c25209acf9095fdde6293b0"}, - {file = "snowflake_connector_python-3.12.4-cp38-cp38-win_amd64.whl", hash = "sha256:f40bd66199064b73d3b313132d3a8c297aedc9da1489e38bfa2e925d17871f6d"}, - {file = "snowflake_connector_python-3.12.4-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:34947e4147d65b9211ab785f76c16cfb2f5d5a0b639358f72ccbf524ff3eda6f"}, - {file = "snowflake_connector_python-3.12.4-cp39-cp39-macosx_11_0_x86_64.whl", hash = "sha256:de703efc36b9b12eab6d75dffeec362b0bbcc2f33ffd9913b0d178d2bf30d8cd"}, - {file = "snowflake_connector_python-3.12.4-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5010cee09cfe20f72dbd9b1f3ee21d07fd280c03db243aa2c048708e7331f358"}, - {file = "snowflake_connector_python-3.12.4-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:28be3b68e2b1d880373506eab4bae5b323d7de6215630585435ab60d3fea7feb"}, - {file = "snowflake_connector_python-3.12.4-cp39-cp39-win_amd64.whl", hash = "sha256:bc8766d16c98a75a57e65fb7697c6732b871309c2fa79698b54fb18cbd36733c"}, - {file = "snowflake_connector_python-3.12.4.tar.gz", hash = "sha256:289e0691dfbf8ec8b7a8f58bcbb95a819890fe5e5b278fdbfc885059a63a946f"}, + {file = "snowflake_connector_python-3.13.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:c180dec770076409d422cfc25e4077561026316c4f0e17a001bc0a15ffbe9184"}, + {file = "snowflake_connector_python-3.13.2-cp310-cp310-macosx_11_0_x86_64.whl", hash = "sha256:f4503e841c9bb22fe7af168a6a4e3a76394d8e0d8731a29ad797273d5e9a62b3"}, + {file = "snowflake_connector_python-3.13.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:04ce1bba327868712a15f5b6d12f0ac5559d0bbf8c7c18f9847cf825e34f36f7"}, + {file = "snowflake_connector_python-3.13.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c27d59696b41cab854379d81577a0802db3b64dbe0fd18d5a562e3739ee12b7f"}, + {file = "snowflake_connector_python-3.13.2-cp310-cp310-win_amd64.whl", hash = "sha256:61634af1dd78203b41bdf89cea0d930b3e8cec19b50a038db3cea1a531a7d36c"}, + {file = "snowflake_connector_python-3.13.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:fd693b1db31c70a9669b5818980f718149d6f7b4624628bed087801dcd617051"}, + {file = "snowflake_connector_python-3.13.2-cp311-cp311-macosx_11_0_x86_64.whl", hash = "sha256:67fcde6666075cc8e6e2fd4ba9dbf1291af780567ffe55a5adbb808de715b39f"}, + {file = "snowflake_connector_python-3.13.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8092ec250b1dcc7c38d8a101a29e9118be56079d8e4f410a50159421c22b3b8e"}, + {file = "snowflake_connector_python-3.13.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8912334af6851d325a5f2bc72416e6a46be889d045e0e09412084b99602c3122"}, + {file = "snowflake_connector_python-3.13.2-cp311-cp311-win_amd64.whl", hash = "sha256:c11599b5d19b4aaab880b5e7b57525645dc1ee9768acc7dad11abf6998c75b22"}, + {file = "snowflake_connector_python-3.13.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:1cf34116687653b7467d519da1dfdbff4f58d0032932d31d61f9d27717e7d61f"}, + {file = "snowflake_connector_python-3.13.2-cp312-cp312-macosx_11_0_x86_64.whl", hash = "sha256:38426293ecf6c1fc3dd87d506e55d8b82dcf763fab1c827b0d09bc74f9852c50"}, + {file = "snowflake_connector_python-3.13.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fd95811b8378d8a268038b59ea8dba8d30dd0b96a1c323191805ae152224ff70"}, + {file = "snowflake_connector_python-3.13.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1fe0cd6fab07fccdea020394a6175baa1ddf57b3d1f4dc288bd7eebcf29c6a0b"}, + {file = "snowflake_connector_python-3.13.2-cp312-cp312-win_amd64.whl", hash = "sha256:82028c55d949889f759b78dd86b6249e9e4934cd6fcbe026cf7f41aefc9eb999"}, + {file = "snowflake_connector_python-3.13.2-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:f29ce0451482348993eed830c81f41a53efd8908691781dc6872d505b1aca12d"}, + {file = "snowflake_connector_python-3.13.2-cp38-cp38-macosx_11_0_x86_64.whl", hash = "sha256:8ffc295307f380dba2f054bf4c9df847dce5168d4e023bdf04ee08abf732672a"}, + {file = "snowflake_connector_python-3.13.2-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ea90169e12e1b60883a4de35129d9761920a92990f963108dc18072a0ee79fae"}, + {file = "snowflake_connector_python-3.13.2-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e7da0d18a4de0c0bd4b2d57093b60103ed0d89fd9e8b813ceb266fb8490ad615"}, + {file = "snowflake_connector_python-3.13.2-cp38-cp38-win_amd64.whl", hash = "sha256:d966edfe7a8fd61ed73dd56ee0de3ed0c79bd405fc4243391420f2db2e6b4d77"}, + {file = "snowflake_connector_python-3.13.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:2b8c807e4cc52cb343546624d90f0540c396bb924cce1c64b029d7ab94e9d571"}, + {file = "snowflake_connector_python-3.13.2-cp39-cp39-macosx_11_0_x86_64.whl", hash = "sha256:e7dd2acc26db04c718bb31f30a2bfdd7cc76eaf429dbfa1c31b29da70127125d"}, + {file = "snowflake_connector_python-3.13.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:92f50eb2a6f9ef94de875bfcbca27a8aa9a08eeec3099ca5fbfc53530a082c4c"}, + {file = "snowflake_connector_python-3.13.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:cfd5f30c6e8011f5f11fdd7c762a189d94a8a3f9259c4f13cbc6cd5d1ca31f85"}, + {file = "snowflake_connector_python-3.13.2-cp39-cp39-win_amd64.whl", hash = "sha256:db600d32b4854716ef5327cdebeb8ba087256819829f1a0c45697caabe898716"}, + {file = "snowflake_connector_python-3.13.2.tar.gz", hash = "sha256:c9954a5e237566420e087a4fcef227775e0c62fbfc821e0ef6f81325fd44c904"}, ] [package.dependencies] @@ -1161,6 +1202,7 @@ version = "2.4.0" description = "Sorted Containers -- Sorted List, Sorted Dict, Sorted Set" optional = false python-versions = "*" +groups = ["main", "dev"] files = [ {file = "sortedcontainers-2.4.0-py2.py3-none-any.whl", hash = "sha256:a163dcaede0f1c021485e957a39245190e74249897e2ae4b2aa38595db237ee0"}, {file = "sortedcontainers-2.4.0.tar.gz", hash = "sha256:25caa5a06cc30b6b83d11423433f65d1f9d76c4c6a0c90e3379eaa43b9bfdb88"}, @@ -1172,6 +1214,8 @@ version = "0.73.0" description = "Modern Text User Interface framework" optional = true python-versions = "<4.0,>=3.8" +groups = ["main"] +markers = "extra == \"tui\"" files = [ {file = "textual-0.73.0-py3-none-any.whl", hash = "sha256:4d93d80d203f7fb7ba51828a546e8777019700d529a1b405ceee313dea2edfc2"}, {file = "textual-0.73.0.tar.gz", hash = "sha256:ccd1e873370577f557dfdf2b3411f2a4f68b57d4365f9d83a00d084afb15f5a6"}, @@ -1191,6 +1235,8 @@ version = "0.3.4" description = "A select widget (aka dropdown) for Textual." optional = true python-versions = ">=3.7,<4.0" +groups = ["main"] +markers = "extra == \"tui\"" files = [ {file = "textual_select-0.3.4-py3-none-any.whl", hash = "sha256:8b315dd041178f95c20000bab4358d49abae4c46fb222c9ec71eebe11d9e3a88"}, {file = "textual_select-0.3.4.tar.gz", hash = "sha256:ae6767bb9078d2e451caf8f1606785cc9e99037f509a714942fe15660aeaa3e3"}, @@ -1205,6 +1251,7 @@ version = "0.10.2" description = "Python Library for Tom's Obvious, Minimal Language" optional = false python-versions = ">=2.6, !=3.0.*, !=3.1.*, !=3.2.*" +groups = ["main"] files = [ {file = "toml-0.10.2-py2.py3-none-any.whl", hash = "sha256:806143ae5bfb6a3c6e736a764057db0e6a0e05e338b5630894a5f779cabb4f9b"}, {file = "toml-0.10.2.tar.gz", hash = "sha256:b3bda1d108d5dd99f4a20d24d9c348e91c4db7ab1b749200bded2f839ccbe68f"}, @@ -1216,6 +1263,7 @@ version = "0.13.2" description = "Style preserving TOML library" optional = false python-versions = ">=3.8" +groups = ["main", "dev"] files = [ {file = "tomlkit-0.13.2-py3-none-any.whl", hash = "sha256:7a974427f6e119197f670fbbbeae7bef749a6c14e793db934baefc1b5f03efde"}, {file = "tomlkit-0.13.2.tar.gz", hash = "sha256:fff5fe59a87295b278abd31bec92c15d9bc4a06885ab12bcea52c71119392e79"}, @@ -1227,6 +1275,7 @@ version = "0.330.0" description = "Client for the Trino distributed SQL Engine" optional = false python-versions = ">=3.8" +groups = ["main", "dev"] files = [ {file = "trino-0.330.0-py3-none-any.whl", hash = "sha256:535f612d754338cfefa4b3fe86b63c8c000d21cb5ea476ae4ec4390d5cc37659"}, {file = "trino-0.330.0.tar.gz", hash = "sha256:1e731be22bc6fb4ce6537287419c3d221faaa8d089f5a05b0f01ef25b860e96e"}, @@ -1253,6 +1302,7 @@ version = "4.12.2" description = "Backported and Experimental Type Hints for Python 3.8+" optional = false python-versions = ">=3.8" +groups = ["main", "dev"] files = [ {file = "typing_extensions-4.12.2-py3-none-any.whl", hash = "sha256:04e5ca0351e0f3f85c6853954072df659d0d13fac324d0072316b67d7794700d"}, {file = "typing_extensions-4.12.2.tar.gz", hash = "sha256:1a7ead55c7e559dd4dee8856e3a88b41225abfe1ce8df57b7c13915fe121ffb8"}, @@ -1260,13 +1310,15 @@ files = [ [[package]] name = "tzdata" -version = "2024.2" +version = "2025.1" description = "Provider of IANA time zone data" optional = false python-versions = ">=2" +groups = ["main", "dev"] +markers = "platform_system == \"Windows\"" files = [ - {file = "tzdata-2024.2-py2.py3-none-any.whl", hash = "sha256:a48093786cdcde33cad18c2555e8532f34422074448fbc874186f0abd79565cd"}, - {file = "tzdata-2024.2.tar.gz", hash = "sha256:7d85cc416e9382e69095b7bdf4afd9e3880418a2413feec7069d533d6b4e31cc"}, + {file = "tzdata-2025.1-py2.py3-none-any.whl", hash = "sha256:7e127113816800496f027041c570f50bcd464a020098a3b6b199517772303639"}, + {file = "tzdata-2025.1.tar.gz", hash = "sha256:24894909e88cdb28bd1636c6887801df64cb485bd593f2fd83ef29075a81d694"}, ] [[package]] @@ -1275,6 +1327,7 @@ version = "5.2" description = "tzinfo object for the local timezone" optional = false python-versions = ">=3.8" +groups = ["main", "dev"] files = [ {file = "tzlocal-5.2-py3-none-any.whl", hash = "sha256:49816ef2fe65ea8ac19d19aa7a1ae0551c834303d5014c6d5a62e4cbda8047b8"}, {file = "tzlocal-5.2.tar.gz", hash = "sha256:8d399205578f1a9342816409cc1e46a93ebd5755e39ea2d85334bea911bf0e6e"}, @@ -1293,6 +1346,8 @@ version = "1.0.3" description = "Micro subset of unicode data files for linkify-it-py projects." optional = true python-versions = ">=3.7" +groups = ["main"] +markers = "extra == \"tui\"" files = [ {file = "uc-micro-py-1.0.3.tar.gz", hash = "sha256:d321b92cff673ec58027c04015fcaa8bb1e005478643ff4a500882eaab88c48a"}, {file = "uc_micro_py-1.0.3-py3-none-any.whl", hash = "sha256:db1dffff340817673d7b466ec86114a9dc0e9d4d9b5ba229d9d60e5c12600cd5"}, @@ -1307,6 +1362,7 @@ version = "1.7.1" description = "Parallel unit test runner with coverage support" optional = false python-versions = "*" +groups = ["dev"] files = [ {file = "unittest_parallel-1.7.1-py3-none-any.whl", hash = "sha256:0eece465f436957fe425a721ba08527c6b27c876ee1b1178f2436131da3d65e9"}, {file = "unittest_parallel-1.7.1.tar.gz", hash = "sha256:832b51b8a5b48cb7fa271f15f9e06cec02ec751629c1e0646c90f70a2edbb517"}, @@ -1321,6 +1377,7 @@ version = "1.26.20" description = "HTTP library with thread-safe connection pooling, file post, and more." optional = false python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,>=2.7" +groups = ["main", "dev"] files = [ {file = "urllib3-1.26.20-py2.py3-none-any.whl", hash = "sha256:0ed14ccfbf1c30a9072c7ca157e4319b70d65f623e91e7b32fadb2853431016e"}, {file = "urllib3-1.26.20.tar.gz", hash = "sha256:40c2dc0c681e47eb8f90e7e27bf6ff7df2e677421fd46756da1161c39ca70d32"}, @@ -1337,6 +1394,7 @@ version = "1.4.0" description = "Official native Python client for the Vertica database." optional = false python-versions = ">=3.7" +groups = ["dev"] files = [ {file = "vertica-python-1.4.0.tar.gz", hash = "sha256:542078ae2fedee694adedb04d81c6edc277b87ed7440302942107d86a0bcd445"}, {file = "vertica_python-1.4.0-py3-none-any.whl", hash = "sha256:50fecd7687f4b0b9f6dee6e2b35c195af2a4f702ece01bd12e080b51756e000b"}, @@ -1352,6 +1410,8 @@ version = "0.2.13" description = "Measures the displayed width of unicode strings in a terminal" optional = true python-versions = "*" +groups = ["main"] +markers = "extra == \"tui\"" files = [ {file = "wcwidth-0.2.13-py2.py3-none-any.whl", hash = "sha256:3da69048e4540d84af32131829ff948f1e022c1c6bdb8d6102117aac784f6859"}, {file = "wcwidth-0.2.13.tar.gz", hash = "sha256:72ea0c06399eb286d978fdedb6923a9eb47e1c486ce63e9b4e64fc18303972b5"}, @@ -1372,6 +1432,6 @@ tui = ["prompt-toolkit", "pygments", "textual", "textual-select"] vertica = [] [metadata] -lock-version = "2.0" +lock-version = "2.1" python-versions = "^3.8" -content-hash = "8f45691e16f715a30a5e3cb17052bde7fbdd45eef00c00f6f1b2b62f407e64b1" +content-hash = "fc287bba52d4206d738398eaa88adfb9cc4b8c7d0b40e1bdf5890185933f9b1c" diff --git a/pyproject.toml b/pyproject.toml index 3d676cc..9b63098 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [tool.poetry] name = "sqeleton" -version = "0.1.8" +version = "0.1.7" description = "Python library for querying SQL databases" authors = ["Erez Shinan "] license = "MIT" @@ -23,7 +23,7 @@ packages = [{ include = "sqeleton" }] [tool.poetry.dependencies] python = "^3.8" -runtype = "0.5.0" +runtype = ">=0.5.0" dsnparse = "*" click = ">=8.1" rich = "*" diff --git a/sqeleton/databases/mssql.py b/sqeleton/databases/mssql.py index 7ac947f..b8cb9c1 100644 --- a/sqeleton/databases/mssql.py +++ b/sqeleton/databases/mssql.py @@ -18,8 +18,8 @@ from .base import BaseDialect, ThreadedDatabase, import_helper, ConnectError, Mixin_Schema from ..abcs import Compilable from ..queries import this, table, Select, SKIP -from ..queries.ast_classes import TablePath -from .base import TIMESTAMP_PRECISION_POS, Mixin_RandomSample, QueryError +from ..queries.ast_classes import ForeignKey, TablePath +from .base import TIMESTAMP_PRECISION_POS, Mixin_RandomSample SESSION_TIME_ZONE = None # Changed by the tests @@ -136,14 +136,19 @@ def current_timestamp(self) -> str: def type_repr(self, t) -> str: if isinstance(t, TimestampTZ): return f"datetimeoffset" - try: - return { - str: "VARCHAR(1024)", - bool: "BIT", - datetime: "datetime2", - }[t] - except KeyError: - return super().type_repr(t) + elif isinstance(t, ForeignKey): + return self.type_repr(t.type) + elif isinstance(t, type): + try: + return { + str: "NVARCHAR(MAX)", + bool: "BIT", + datetime: "datetime2", + }[t] + except KeyError: + return super().type_repr(t) + + super().type_repr(t) class MsSQL(ThreadedDatabase): "AKA sql-server" @@ -165,14 +170,6 @@ def create_connection(self): return self.mssql.connect(**self._args) except self.mssql.Error as e: raise ConnectError(*e.args) from e - - def _query_cursor(self, c, sql_code: str): - try: - return super()._query_cursor(c, sql_code) - except self.mssql.DatabaseError as e: - raise QueryError(e) - except e: - raise Exception(e) def select_table_schema(self, path: DbPath) -> str: """Provide SQL for selecting the table schema as (name, type, date_prec, num_prec)""" From f429f22d68c839c4cc5e60228aeadc990ec85893 Mon Sep 17 00:00:00 2001 From: Matthew Alexander Date: Tue, 11 Feb 2025 09:54:42 +1030 Subject: [PATCH 5/6] Added in MSSQL support for three part ids. Updated test to cover new functionality --- sqeleton/databases/mssql.py | 23 ++++++++++++++++++++--- tests/test_database.py | 6 +++--- 2 files changed, 23 insertions(+), 6 deletions(-) diff --git a/sqeleton/databases/mssql.py b/sqeleton/databases/mssql.py index b8cb9c1..ac9d600 100644 --- a/sqeleton/databases/mssql.py +++ b/sqeleton/databases/mssql.py @@ -170,21 +170,38 @@ def create_connection(self): return self.mssql.connect(**self._args) except self.mssql.Error as e: raise ConnectError(*e.args) from e + + def _normalize_table_path(self, path: DbPath) -> DbPath: + if len(path) == 1: + return None, self.default_schema, path[0] + elif len(path) == 2: + return None, path[0], path[1] + elif len(path) == 3: + return path + + raise ValueError( + f"{self.name}: Bad table path for {self}: '{'.'.join(path)}'. Expected format: table, schema.table, or database.schema.table" + ) def select_table_schema(self, path: DbPath) -> str: """Provide SQL for selecting the table schema as (name, type, date_prec, num_prec)""" - schema, name = self._normalize_table_path(path) + database, schema, name = self._normalize_table_path(path) + + info_schema_path = ["information_schema", "COLUMNS"] + if database: + info_schema_path.insert(0, database) + if schema == None: sql_code = ( "SELECT column_name, data_type, datetime_precision, numeric_precision, numeric_scale " - "FROM information_schema.COLUMNS " + f"FROM {'.'.join(info_schema_path)} " f"WHERE table_name = '{name}'" ) else: sql_code = ( "SELECT column_name, data_type, datetime_precision, numeric_precision, numeric_scale " - "FROM information_schema.COLUMNS " + f"FROM {'.'.join(info_schema_path)} " f"WHERE table_name = '{name}' AND table_schema = '{schema}'" ) diff --git a/tests/test_database.py b/tests/test_database.py index fd46f7b..3b19f56 100644 --- a/tests/test_database.py +++ b/tests/test_database.py @@ -164,13 +164,13 @@ def test_foreign_key(self): @test_each_database class TestThreePartIds(unittest.TestCase): def test_three_part_support(self): - if self.db_cls not in [dbs.PostgreSQL, dbs.Redshift, dbs.Snowflake, dbs.DuckDB]: + if self.db_cls not in [dbs.PostgreSQL, dbs.Redshift, dbs.Snowflake, dbs.DuckDB, dbs.MsSQL]: self.skipTest("Limited support for 3 part ids") table_name = "tbl_" + random_table_suffix() db = get_conn(self.db_cls) - db_res = db.query("SELECT CURRENT_DATABASE()") - schema_res = db.query("SELECT CURRENT_SCHEMA()") + db_res = db.query("SELECT CURRENT_DATABASE()") if self.db_cls != dbs.MsSQL else db.query("SELECT DB_NAME() AS [Current Database]") + schema_res = db.query("SELECT CURRENT_SCHEMA()") if self.db_cls != dbs.MsSQL else db.query("SELECT SCHEMA_NAME()") db_name = db_res.rows[0][0] schema_name = schema_res.rows[0][0] From 632924c17cf8a7a7d131e93b157cb9db562cb63e Mon Sep 17 00:00:00 2001 From: Matthew Alexander Date: Fri, 21 Feb 2025 11:35:13 +1030 Subject: [PATCH 6/6] Changes to test MSSQL Connection details --- docker-compose.yml | 4 ++-- docs/supported-databases.md | 2 +- tests/common.py | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/docker-compose.yml b/docker-compose.yml index d16aa78..afc9c5e 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -124,9 +124,9 @@ services: volumes: - mssql-data:/var/opt/mssql/data:delegated ports: - - '8001:1433' + - '8020:1433' expose: - - '8001' + - 8020 env_file: - dev/dev.env tty: true diff --git a/docs/supported-databases.md b/docs/supported-databases.md index aea24d9..3346d5c 100644 --- a/docs/supported-databases.md +++ b/docs/supported-databases.md @@ -14,7 +14,7 @@ | Clickhouse | 💛 | `clickhouse://:@:9000/` | | Vertica | 💛 | `vertica://:@:5433/` | | DuckDB | 💛 | | -| MsSQL | ⏳ | `mssql+pymssql://:@:/` | +| MsSQL | ⏳ | `pymssql://:@:/` | | ElasticSearch | 📝 | | | Planetscale | 📝 | | | Pinot | 📝 | | diff --git a/tests/common.py b/tests/common.py index 4c144c9..1275286 100644 --- a/tests/common.py +++ b/tests/common.py @@ -33,7 +33,7 @@ # vertica uri provided for docker - "vertica://vertica:Password1@localhost:5433/vertica" TEST_VERTICA_CONN_STRING: str = os.environ.get("VERTICA_URI") TEST_DUCKDB_CONN_STRING: str = "duckdb://main:@:memory:" -TEST_MSSQL_CONN_STRING: str = "pymssql://sa:@localhost:8001/master" +TEST_MSSQL_CONN_STRING: str = "pymssql://sa:@localhost:8020/master" DEFAULT_N_SAMPLES = 50