diff --git a/.github/labeler.yml b/.github/labeler.yml index 3dd06e829..cb1c962f3 100644 --- a/.github/labeler.yml +++ b/.github/labeler.yml @@ -22,10 +22,14 @@ documentation: - changed-files: - any-glob-to-any-file: ['**/*.md'] -trino: -- changed-files: - - any-glob-to-any-file: ['**/*trino*'] - bigquery: - changed-files: - any-glob-to-any-file: ['**/*bigquery*'] + +ci: +- changed-files: + - any-glob-to-any-file: ['.github/**'] + +v1-engine-changed: +- changed-files: + - any-glob-to-any-file: ['wren-base/**', 'wren-main/**', 'wren-server/**', 'wren-tests/**', 'trino-parser/**', 'pom.xml'] diff --git a/.github/workflows/build-image.yml b/.github/workflows/build-image.yml index e241d780c..f9085e2b0 100644 --- a/.github/workflows/build-image.yml +++ b/.github/workflows/build-image.yml @@ -24,7 +24,7 @@ jobs: run: | if [ "${{ github.event_name }}" = "push" ]; then echo "type=sha" > tags.txt - echo "type=raw,value=nightly" > tags.txt + echo "type=schedule" >> tags.txt fi if [ "${{ github.event_name }}" = "workflow_dispatch" ]; then if [ -n "${{ github.event.inputs.docker_image_tag_name }}" ]; then @@ -56,7 +56,8 @@ jobs: uses: docker/metadata-action@v5 with: images: ${{ env.ENGINE_IMAGE }} - tags: ${{ needs.prepare-tag.outputs.tags }} + tags: | + ${{ needs.prepare-tag.outputs.tags }} - name: Login to GitHub Container Registry uses: docker/login-action@v3 with: @@ -152,7 +153,8 @@ jobs: uses: docker/metadata-action@v5 with: images: ${{ env.IBIS_IMAGE }} - tags: ${{ needs.prepare-tag.outputs.tags }} + tags: | + ${{ needs.prepare-tag.outputs.tags }} - name: Login to GitHub Container Registry uses: docker/login-action@v3 with: @@ -162,9 +164,10 @@ jobs: - name: Create manifest list and push working-directory: /tmp/digests run: | + TAGS=$(echo "${{ steps.meta.outputs.tags }}" | awk '{printf "--tag %s ", $0}') docker buildx imagetools create $(jq -cr '.tags | map("-t " + .) | join(" ")' <<< "$DOCKER_METADATA_OUTPUT_JSON") \ $(printf '${{ env.IBIS_IMAGE }}@sha256:%s ' *) \ - --tag ${{ steps.meta.outputs.tags }} + $TAGS - name: Inspect image run: | docker buildx imagetools inspect ${{ steps.meta.outputs.tags }} diff --git a/.github/workflows/core-py-mult-platform-ci.yml b/.github/workflows/core-py-mult-platform-ci.yml deleted file mode 100644 index ec6f5e184..000000000 --- a/.github/workflows/core-py-mult-platform-ci.yml +++ /dev/null @@ -1,197 +0,0 @@ -# This file is autogenerated by maturin v1.6.0 -# To update, run -# -# maturin generate-ci --pytest github -# -name: Core Python CI (multi-platform) - -# We don't ready to run this workflow on stable release flow yet -on: - workflow_dispatch: - - -concurrency: - group: ${{ github.workflow }}-${{ github.event.number }} - cancel-in-progress: true - -permissions: - contents: read - -jobs: - linux: - runs-on: ${{ matrix.platform.runner }} - strategy: - matrix: - platform: - - runner: ubuntu-latest - target: x86_64 - - runner: ubuntu-latest - target: x86 - - runner: ubuntu-latest - target: aarch64 - - runner: ubuntu-latest - target: armv7 - steps: - - uses: actions/checkout@v4 - - uses: actions/setup-python@v5 - with: - python-version: 3.11 - - name: Build wheels - uses: PyO3/maturin-action@v1 - with: - working-directory: wren-core-py - target: ${{ matrix.platform.target }} - args: -o dist --find-interpreter - sccache: 'true' - manylinux: auto - - name: pytest - if: ${{ startsWith(matrix.platform.target, 'x86_64') }} - shell: bash - run: | - set -e - cd wren-core-py - python3 -m venv .venv - source .venv/bin/activate - pip install wren-core-py --find-links dist --force-reinstall - pip install pytest - pytest - - name: pytest - if: ${{ !startsWith(matrix.platform.target, 'x86') && matrix.platform.target != 'ppc64' }} - uses: uraimo/run-on-arch-action@v2 - with: - arch: ${{ matrix.platform.target }} - distro: ubuntu22.04 - githubToken: ${{ github.token }} - install: | - apt-get update - apt-get install -y --no-install-recommends python3 python3-pip - pip3 install -U pip pytest - run: | - set -e - cd wren-core-py - pip3 install wren-core-py --find-links dist --force-reinstall - pytest - - musllinux: - runs-on: ${{ matrix.platform.runner }} - strategy: - matrix: - platform: - - runner: ubuntu-latest - target: x86_64 - - runner: ubuntu-latest - target: x86 - - runner: ubuntu-latest - target: aarch64 - - runner: ubuntu-latest - target: armv7 - steps: - - uses: actions/checkout@v4 - - uses: actions/setup-python@v5 - with: - python-version: 3.11 - - name: Build wheels - uses: PyO3/maturin-action@v1 - with: - working-directory: wren-core-py - target: ${{ matrix.platform.target }} - args: -o dist --find-interpreter - sccache: 'true' - manylinux: musllinux_1_2 - - name: pytest - if: ${{ startsWith(matrix.platform.target, 'x86_64') }} - uses: addnab/docker-run-action@v3 - with: - image: alpine:latest - options: -v ${{ github.workspace }}:/io -w /io - run: | - set -e - apk add py3-pip py3-virtualenv - cd wren-core-py - python3 -m virtualenv .venv - source .venv/bin/activate - pip install wren-core-py --no-index --find-links dist --force-reinstall - pip install pytest - pytest - - name: pytest - if: ${{ !startsWith(matrix.platform.target, 'x86') }} - uses: uraimo/run-on-arch-action@v2 - with: - arch: ${{ matrix.platform.target }} - distro: alpine_latest - githubToken: ${{ github.token }} - install: | - apk add py3-virtualenv - run: | - set -e - cd wren-core-py - python3 -m virtualenv .venv - source .venv/bin/activate - pip install pytest - pip install wren-core-py --find-links dist --force-reinstall - pytest - - windows: - runs-on: ${{ matrix.platform.runner }} - strategy: - matrix: - platform: - - runner: windows-latest - target: x64 - - runner: windows-latest - target: x86 - steps: - - uses: actions/checkout@v4 - - uses: actions/setup-python@v5 - with: - python-version: 3.11 - architecture: ${{ matrix.platform.target }} - - name: Build wheels - uses: PyO3/maturin-action@v1 - with: - working-directory: wren-core-py - target: ${{ matrix.platform.target }} - args: -o dist --find-interpreter - sccache: 'true' - - name: pytest - if: ${{ !startsWith(matrix.platform.target, 'aarch64') }} - shell: bash - run: | - set -e - cd wren-core-py - python3 -m venv .venv - source .venv/Scripts/activate - pip install wren-core-py --find-links dist --force-reinstall - pip install pytest - pytest - - macos: - runs-on: ${{ matrix.platform.runner }} - strategy: - matrix: - platform: - - runner: macos-12 - target: x86_64 - - runner: macos-14 - target: aarch64 - steps: - - uses: actions/checkout@v4 - - uses: actions/setup-python@v5 - with: - python-version: 3.11 - - name: Build wheels - uses: PyO3/maturin-action@v1 - with: - working-directory: wren-core-py - target: ${{ matrix.platform.target }} - args: -o dist --find-interpreter - sccache: 'true' - - name: pytest - run: | - set -e - cd wren-core-py - python3 -m venv .venv - source .venv/bin/activate - pip install wren-core-py --find-links dist --force-reinstall - pip install pytest - pytest diff --git a/.github/workflows/maven-tests.yml b/.github/workflows/maven-tests.yml index a5dad947b..a36de39f0 100644 --- a/.github/workflows/maven-tests.yml +++ b/.github/workflows/maven-tests.yml @@ -17,7 +17,7 @@ concurrency: jobs: maven-tests: runs-on: ubuntu-latest - if: ${{ github.event.label.name == 'v1-engine-changed' }} + if: contains(github.event.pull_request.labels.*.name, 'v1-engine-changed') steps: - uses: actions/checkout@v4 - name: Set up JDK 21 diff --git a/.github/workflows/stable-release.yml b/.github/workflows/stable-release.yml index 98b136df0..817e9122a 100644 --- a/.github/workflows/stable-release.yml +++ b/.github/workflows/stable-release.yml @@ -201,9 +201,7 @@ jobs: - name: Create manifest list and push working-directory: /tmp/digests run: | + TAGS=$(echo "${{ steps.meta.outputs.tags }}" | awk '{printf "--tag %s ", $0}') docker buildx imagetools create $(jq -cr '.tags | map("-t " + .) | join(" ")' <<< "$DOCKER_METADATA_OUTPUT_JSON") \ $(printf '${{ env.IBIS_IMAGE }}@sha256:%s ' *) \ - --tag ${{ steps.meta.outputs.tags }} - - name: Inspect image - run: | - docker buildx imagetools inspect ${{ steps.meta.outputs.tags }} + $TAGS diff --git a/ibis-server/app/mdl/analyzer.py b/ibis-server/app/mdl/analyzer.py index acbe58dd7..13f144103 100644 --- a/ibis-server/app/mdl/analyzer.py +++ b/ibis-server/app/mdl/analyzer.py @@ -17,7 +17,7 @@ def analyze(manifest_str: str, sql: str) -> list[dict]: ) return r.raise_for_status().json() except httpx.ConnectError as e: - raise ConnectionError(f"Can not connect to Wren Engine: {e}") from e + raise ConnectionError(f"Can not connect to Java Engine: {e}") from e except httpx.HTTPStatusError as e: raise AnalyzeError(e.response.text) @@ -32,7 +32,7 @@ def analyze_batch(manifest_str: str, sqls: list[str]) -> list[list[dict]]: ) return r.raise_for_status().json() except httpx.ConnectError as e: - raise ConnectionError(f"Can not connect to Wren Engine: {e}") from e + raise ConnectionError(f"Can not connect to Java Engine: {e}") from e except httpx.HTTPStatusError as e: raise AnalyzeError(e.response.text) diff --git a/ibis-server/app/mdl/context.py b/ibis-server/app/mdl/context.py index d8e10dcb6..100765367 100644 --- a/ibis-server/app/mdl/context.py +++ b/ibis-server/app/mdl/context.py @@ -1,8 +1,8 @@ from functools import cache -from wren_core import SessionContext - @cache -def get_session_context(manifest_str: str, function_path: str) -> SessionContext: +def get_session_context(manifest_str: str, function_path: str): + from wren_core import SessionContext + return SessionContext(manifest_str, function_path) diff --git a/ibis-server/app/mdl/rewriter.py b/ibis-server/app/mdl/rewriter.py index 13b86e585..fc63f1a7a 100644 --- a/ibis-server/app/mdl/rewriter.py +++ b/ibis-server/app/mdl/rewriter.py @@ -70,9 +70,9 @@ def rewrite(self, sql: str) -> str: ) return r.raise_for_status().text.replace("\n", " ") except httpx.ConnectError as e: - raise WrenEngineError(f"Can not connect to Wren Engine: {e}") + raise WrenEngineError(f"Can not connect to Java Engine: {e}") except httpx.TimeoutException as e: - raise WrenEngineError(f"Timeout when connecting to Wren Engine: {e}") + raise WrenEngineError(f"Timeout when connecting to Java Engine: {e}") except httpx.HTTPStatusError as e: raise RewriteError(e.response.text) diff --git a/ibis-server/app/middleware/__init__.py b/ibis-server/app/middleware/__init__.py index c1a7fbb4a..abfa6204b 100644 --- a/ibis-server/app/middleware/__init__.py +++ b/ibis-server/app/middleware/__init__.py @@ -17,7 +17,7 @@ async def dispatch(self, request: Request, call_next) -> Response: if body: json_obj = orjson.loads(body) if "connectionInfo" in json_obj: - json_obj["connectionInfo"] = "REMOVED_SENSITIVE_DATA" + json_obj["connectionInfo"] = "REDACTED" body = orjson.dumps(json_obj) logger.info("Request body: {body}", body=body.decode("utf-8")) try: diff --git a/ibis-server/app/model/metadata/bigquery.py b/ibis-server/app/model/metadata/bigquery.py index 92607baf6..09e966a0f 100644 --- a/ibis-server/app/model/metadata/bigquery.py +++ b/ibis-server/app/model/metadata/bigquery.py @@ -4,6 +4,7 @@ Column, Constraint, ConstraintType, + RustWrenEngineColumnType, Table, TableProperties, ) @@ -17,6 +18,8 @@ def __init__(self, connection_info: BigQueryConnectionInfo): def get_table_list(self) -> list[Table]: dataset_id = self.connection_info.dataset_id.get_secret_value() + + # filter out columns with GEOGRAPHY & RANGE types sql = f""" SELECT c.table_catalog, @@ -46,14 +49,11 @@ def get_table_list(self) -> list[Table]: AND cf.column_name = c.column_name LEFT JOIN {dataset_id}.INFORMATION_SCHEMA.TABLE_OPTIONS table_options ON c.table_name = table_options.table_name + WHERE cf.data_type != 'GEOGRAPHY' + AND cf.data_type NOT LIKE 'RANGE%' """ response = self.connection.sql(sql).to_pandas().to_dict(orient="records") - def get_data_type(data_type) -> str: - if "STRUCT" in data_type: - return "RECORD" - return data_type - def get_column(row, nestedColumns=None) -> Column: return Column( # field_path supports both column & nested column @@ -139,3 +139,30 @@ def get_constraints(self) -> list[Constraint]: def get_version(self) -> str: return "Follow BigQuery release version" + + def _transform_column_type(self, data_type): + # lower case the data_type + data_type = data_type.lower() + + # if data_type start with "array" or "struct", by pass it + if data_type.startswith(("array", "struct")): + return data_type + + # Map BigQuery types to RustWrenEngineColumnType + switcher = { + # GEOGRAPHY and RANGE columns were filtered out + "bytes": RustWrenEngineColumnType.BYTES, + "date": RustWrenEngineColumnType.DATE, + "datetime": RustWrenEngineColumnType.DATETIME, + "interval": RustWrenEngineColumnType.INTERVAL, + "json": RustWrenEngineColumnType.JSON, + "int64": RustWrenEngineColumnType.INT64, + "numeric": RustWrenEngineColumnType.NUMERIC, + "bignumeric": RustWrenEngineColumnType.BIGNUMERIC, + "float64": RustWrenEngineColumnType.FLOAT64, + "string": RustWrenEngineColumnType.STRING, + "time": RustWrenEngineColumnType.TIME, + "timestamp": RustWrenEngineColumnType.TIMESTAMPTZ, + } + + return switcher.get(data_type, RustWrenEngineColumnType.UNKNOWN) diff --git a/ibis-server/app/model/metadata/canner.py b/ibis-server/app/model/metadata/canner.py index 2024b8e2f..a7699d59d 100644 --- a/ibis-server/app/model/metadata/canner.py +++ b/ibis-server/app/model/metadata/canner.py @@ -1,3 +1,4 @@ +import re from urllib.parse import urlparse from gql import Client, gql @@ -7,6 +8,7 @@ from app.model.metadata.dto import ( Column, Constraint, + RustWrenEngineColumnType, Table, TableProperties, ) @@ -181,7 +183,7 @@ def _build_columns(cls, columns: list[dict]) -> list[Column]: return [ Column( name=column["originalColumn"]["name"], - type=column["originalColumn"]["type"], + type=cls._transform_column_type(column["originalColumn"]["type"]), notNull=column["originalColumn"]["properties"].get( "jdbc-nullable", False ), @@ -190,3 +192,45 @@ def _build_columns(cls, columns: list[dict]) -> list[Column]: ) for column in columns ] + + @classmethod + def _transform_column_type(self, data_type): + # all possible types listed here: https://trino.io/docs/current/language/types.html + # trim the (all characters) at the end of the data_type if exists + data_type = re.sub(r"\(.*\)", "", data_type).strip() + + switcher = { + # String Types (ignore Binary and Spatial Types for now) + "char": RustWrenEngineColumnType.CHAR, + "varchar": RustWrenEngineColumnType.VARCHAR, + "tinytext": RustWrenEngineColumnType.TEXT, + "text": RustWrenEngineColumnType.TEXT, + "mediumtext": RustWrenEngineColumnType.TEXT, + "longtext": RustWrenEngineColumnType.TEXT, + "enum": RustWrenEngineColumnType.VARCHAR, + "set": RustWrenEngineColumnType.VARCHAR, + # Numeric Types(https://dev.mysql.com/doc/refman/8.4/en/numeric-types.html) + "bit": RustWrenEngineColumnType.TINYINT, + "tinyint": RustWrenEngineColumnType.TINYINT, + "smallint": RustWrenEngineColumnType.SMALLINT, + "mediumint": RustWrenEngineColumnType.INTEGER, + "int": RustWrenEngineColumnType.INTEGER, + "integer": RustWrenEngineColumnType.INTEGER, + "bigint": RustWrenEngineColumnType.BIGINT, + # boolean + "bool": RustWrenEngineColumnType.BOOL, + "boolean": RustWrenEngineColumnType.BOOL, + # Decimal + "float": RustWrenEngineColumnType.FLOAT8, + "double": RustWrenEngineColumnType.DOUBLE, + "decimal": RustWrenEngineColumnType.DECIMAL, + "numeric": RustWrenEngineColumnType.NUMERIC, + # Date and Time Types(https://dev.mysql.com/doc/refman/8.4/en/date-and-time-types.html) + "date": RustWrenEngineColumnType.DATE, + "datetime": RustWrenEngineColumnType.TIMESTAMP, + "timestamp": RustWrenEngineColumnType.TIMESTAMPTZ, + # JSON Type + "json": RustWrenEngineColumnType.JSON, + } + + return switcher.get(data_type.lower(), RustWrenEngineColumnType.UNKNOWN) diff --git a/ibis-server/app/model/metadata/clickhouse.py b/ibis-server/app/model/metadata/clickhouse.py index 897c89740..07dc335a9 100644 --- a/ibis-server/app/model/metadata/clickhouse.py +++ b/ibis-server/app/model/metadata/clickhouse.py @@ -3,9 +3,9 @@ from app.model.metadata.dto import ( Column, Constraint, + RustWrenEngineColumnType, Table, TableProperties, - WrenEngineColumnType, ) from app.model.metadata.metadata import Metadata @@ -80,29 +80,29 @@ def _transform_column_type(self, data_type): # lower case the data_type data_type = data_type.lower() - # Map ClickHouse types to WrenEngineColumnType + # Map ClickHouse types to RustWrenEngineColumnType switcher = { - "boolean": WrenEngineColumnType.BOOLEAN, - "int8": WrenEngineColumnType.TINYINT, - "uint8": WrenEngineColumnType.INT2, - "int16": WrenEngineColumnType.INT2, - "uint16": WrenEngineColumnType.INT2, - "int32": WrenEngineColumnType.INT4, - "uint32": WrenEngineColumnType.INT4, - "int64": WrenEngineColumnType.INT8, - "uint64": WrenEngineColumnType.INT8, - "float32": WrenEngineColumnType.FLOAT4, - "float64": WrenEngineColumnType.FLOAT8, - "decimal": WrenEngineColumnType.DECIMAL, - "date": WrenEngineColumnType.DATE, - "datetime": WrenEngineColumnType.TIMESTAMP, - "string": WrenEngineColumnType.VARCHAR, - "fixedstring": WrenEngineColumnType.CHAR, - "uuid": WrenEngineColumnType.UUID, - "enum8": WrenEngineColumnType.STRING, # Enums can be mapped to strings - "enum16": WrenEngineColumnType.STRING, # Enums can be mapped to strings - "ipv4": WrenEngineColumnType.INET, - "ipv6": WrenEngineColumnType.INET, + "boolean": RustWrenEngineColumnType.BOOL, + "int8": RustWrenEngineColumnType.TINYINT, + "uint8": RustWrenEngineColumnType.INT2, + "int16": RustWrenEngineColumnType.INT2, + "uint16": RustWrenEngineColumnType.INT2, + "int32": RustWrenEngineColumnType.INT4, + "uint32": RustWrenEngineColumnType.INT4, + "int64": RustWrenEngineColumnType.INT8, + "uint64": RustWrenEngineColumnType.INT8, + "float32": RustWrenEngineColumnType.FLOAT4, + "float64": RustWrenEngineColumnType.FLOAT8, + "decimal": RustWrenEngineColumnType.DECIMAL, + "date": RustWrenEngineColumnType.DATE, + "datetime": RustWrenEngineColumnType.TIMESTAMP, + "string": RustWrenEngineColumnType.VARCHAR, + "fixedstring": RustWrenEngineColumnType.CHAR, + "uuid": RustWrenEngineColumnType.UUID, + "enum8": RustWrenEngineColumnType.STRING, # Enums can be mapped to strings + "enum16": RustWrenEngineColumnType.STRING, # Enums can be mapped to strings + "ipv4": RustWrenEngineColumnType.INET, + "ipv6": RustWrenEngineColumnType.INET, } - return switcher.get(data_type, WrenEngineColumnType.UNKNOWN) + return switcher.get(data_type, RustWrenEngineColumnType.UNKNOWN) diff --git a/ibis-server/app/model/metadata/dto.py b/ibis-server/app/model/metadata/dto.py index ca1b1cc2a..6d6706929 100644 --- a/ibis-server/app/model/metadata/dto.py +++ b/ibis-server/app/model/metadata/dto.py @@ -10,59 +10,46 @@ class MetadataDTO(BaseModel): connection_info: ConnectionInfo = Field(alias="connectionInfo") -class WrenEngineColumnType(Enum): - # Boolean Types - BOOLEAN = "BOOLEAN" - - # Numeric Types +class RustWrenEngineColumnType(Enum): + BOOL = "BOOL" TINYINT = "TINYINT" INT2 = "INT2" - SMALLINT = "SMALLINT" # alias for INT2 + SMALLINT = "SMALLINT" INT4 = "INT4" - INTEGER = "INTEGER" # alias for INT4 + INT = "INT" + INTEGER = "INTEGER" INT8 = "INT8" - BIGINT = "BIGINT" # alias for INT8 + BIGINT = "BIGINT" NUMERIC = "NUMERIC" DECIMAL = "DECIMAL" - - # Floating-Point Types - FLOAT4 = "FLOAT4" - REAL = "REAL" # alias for FLOAT4 - FLOAT8 = "FLOAT8" - DOUBLE = "DOUBLE" # alias for FLOAT8 - - # Character Types VARCHAR = "VARCHAR" CHAR = "CHAR" - BPCHAR = "BPCHAR" # BPCHAR is fixed-length blank padded string - TEXT = "TEXT" # alias for VARCHAR - STRING = "STRING" # alias for VARCHAR - NAME = "NAME" # alias for VARCHAR - - # Date/Time Types + BPCHAR = "BPCHAR" + TEXT = "TEXT" + STRING = "STRING" + NAME = "NAME" + FLOAT4 = "FLOAT4" + REAL = "REAL" + FLOAT = "FLOAT" + FLOAT8 = "FLOAT8" + DOUBLE = "DOUBLE" TIMESTAMP = "TIMESTAMP" - TIMESTAMPTZ = "TIMESTAMP WITH TIME ZONE" + TIMESTAMPTZ = "TIMESTAMPTZ" DATE = "DATE" INTERVAL = "INTERVAL" - - # JSON Types JSON = "JSON" - - # Object identifiers (OIDs) are used internally by PostgreSQL as primary keys for various system tables. - # https:#www.postgresql.org/docs/current/datatype-oid.html OID = "OID" - - # Binary Data Types BYTEA = "BYTEA" - - # UUID Type UUID = "UUID" - - # Network Address Types INET = "INET" - - # Unknown Type UNKNOWN = "UNKNOWN" + BIGNUMERIC = "BIGNUMERIC" + BYTES = "BYTES" + DATETIME = "DATETIME" + FLOAT64 = "FLOAT64" + INT64 = "INT64" + TIME = "TIME" + NULL = "NULL" class Column(BaseModel): diff --git a/ibis-server/app/model/metadata/factory.py b/ibis-server/app/model/metadata/factory.py index 6b0d5539b..b27a4a946 100644 --- a/ibis-server/app/model/metadata/factory.py +++ b/ibis-server/app/model/metadata/factory.py @@ -6,6 +6,7 @@ from app.model.metadata.mssql import MSSQLMetadata from app.model.metadata.mysql import MySQLMetadata from app.model.metadata.postgres import PostgresMetadata +from app.model.metadata.snowflake import SnowflakeMetadata from app.model.metadata.trino import TrinoMetadata mapping = { @@ -16,6 +17,7 @@ DataSource.mysql: MySQLMetadata, DataSource.postgres: PostgresMetadata, DataSource.trino: TrinoMetadata, + DataSource.snowflake: SnowflakeMetadata, } diff --git a/ibis-server/app/model/metadata/mssql.py b/ibis-server/app/model/metadata/mssql.py index 25f13d35f..c3a135a84 100644 --- a/ibis-server/app/model/metadata/mssql.py +++ b/ibis-server/app/model/metadata/mssql.py @@ -4,9 +4,9 @@ Column, Constraint, ConstraintType, + RustWrenEngineColumnType, Table, TableProperties, - WrenEngineColumnType, ) from app.model.metadata.metadata import Metadata @@ -173,40 +173,40 @@ def _format_constraint_name( return f"{table_name}_{column_name}_{referenced_table_name}_{referenced_column_name}" def _transform_column_type(self, data_type): - # Define the mapping of MSSQL data types to WrenEngineColumnType + # Define the mapping of MSSQL data types to RustWrenEngineColumnType # ref: https://learn.microsoft.com/en-us/sql/t-sql/data-types/data-types-transact-sql?view=sql-server-ver15#exact-numerics switcher = { # String Types - "char": WrenEngineColumnType.CHAR, - "varchar": WrenEngineColumnType.VARCHAR, - "text": WrenEngineColumnType.TEXT, - "nchar": WrenEngineColumnType.CHAR, - "nvarchar": WrenEngineColumnType.VARCHAR, - "ntext": WrenEngineColumnType.TEXT, + "char": RustWrenEngineColumnType.CHAR, + "varchar": RustWrenEngineColumnType.VARCHAR, + "text": RustWrenEngineColumnType.TEXT, + "nchar": RustWrenEngineColumnType.CHAR, + "nvarchar": RustWrenEngineColumnType.VARCHAR, + "ntext": RustWrenEngineColumnType.TEXT, # Numeric Types - "bit": WrenEngineColumnType.TINYINT, - "tinyint": WrenEngineColumnType.TINYINT, - "smallint": WrenEngineColumnType.SMALLINT, - "int": WrenEngineColumnType.INTEGER, - "bigint": WrenEngineColumnType.BIGINT, + "bit": RustWrenEngineColumnType.TINYINT, + "tinyint": RustWrenEngineColumnType.TINYINT, + "smallint": RustWrenEngineColumnType.SMALLINT, + "int": RustWrenEngineColumnType.INTEGER, + "bigint": RustWrenEngineColumnType.BIGINT, # Boolean - "boolean": WrenEngineColumnType.BOOLEAN, + "boolean": RustWrenEngineColumnType.BOOL, # Decimal - "float": WrenEngineColumnType.FLOAT8, - "real": WrenEngineColumnType.FLOAT8, - "decimal": WrenEngineColumnType.DECIMAL, - "numeric": WrenEngineColumnType.NUMERIC, - "money": WrenEngineColumnType.DECIMAL, - "smallmoney": WrenEngineColumnType.DECIMAL, + "float": RustWrenEngineColumnType.FLOAT8, + "real": RustWrenEngineColumnType.FLOAT8, + "decimal": RustWrenEngineColumnType.DECIMAL, + "numeric": RustWrenEngineColumnType.NUMERIC, + "money": RustWrenEngineColumnType.DECIMAL, + "smallmoney": RustWrenEngineColumnType.DECIMAL, # Date and Time Types - "date": WrenEngineColumnType.DATE, - "datetime": WrenEngineColumnType.TIMESTAMP, - "datetime2": WrenEngineColumnType.TIMESTAMPTZ, - "smalldatetime": WrenEngineColumnType.TIMESTAMP, - "time": WrenEngineColumnType.INTERVAL, - "datetimeoffset": WrenEngineColumnType.TIMESTAMPTZ, + "date": RustWrenEngineColumnType.DATE, + "datetime": RustWrenEngineColumnType.TIMESTAMP, + "datetime2": RustWrenEngineColumnType.TIMESTAMP, + "smalldatetime": RustWrenEngineColumnType.TIMESTAMP, + "time": RustWrenEngineColumnType.INTERVAL, + "datetimeoffset": RustWrenEngineColumnType.TIMESTAMPTZ, # JSON Type (Note: MSSQL supports JSON natively as a string type) - "json": WrenEngineColumnType.JSON, + "json": RustWrenEngineColumnType.JSON, } - return switcher.get(data_type.lower(), WrenEngineColumnType.UNKNOWN) + return switcher.get(data_type.lower(), RustWrenEngineColumnType.UNKNOWN) diff --git a/ibis-server/app/model/metadata/mysql.py b/ibis-server/app/model/metadata/mysql.py index e71e8c11a..13f34d357 100644 --- a/ibis-server/app/model/metadata/mysql.py +++ b/ibis-server/app/model/metadata/mysql.py @@ -4,9 +4,9 @@ Column, Constraint, ConstraintType, + RustWrenEngineColumnType, Table, TableProperties, - WrenEngineColumnType, ) from app.model.metadata.metadata import Metadata @@ -132,36 +132,36 @@ def _transform_column_type(self, data_type): # all possible types listed here: https://dev.mysql.com/doc/refman/8.4/en/data-types.html switcher = { # String Types (ignore Binary and Spatial Types for now) - "char": WrenEngineColumnType.CHAR, - "varchar": WrenEngineColumnType.VARCHAR, - "tinytext": WrenEngineColumnType.TEXT, - "text": WrenEngineColumnType.TEXT, - "mediumtext": WrenEngineColumnType.TEXT, - "longtext": WrenEngineColumnType.TEXT, - "enum": WrenEngineColumnType.VARCHAR, - "set": WrenEngineColumnType.VARCHAR, + "char": RustWrenEngineColumnType.CHAR, + "varchar": RustWrenEngineColumnType.VARCHAR, + "tinytext": RustWrenEngineColumnType.TEXT, + "text": RustWrenEngineColumnType.TEXT, + "mediumtext": RustWrenEngineColumnType.TEXT, + "longtext": RustWrenEngineColumnType.TEXT, + "enum": RustWrenEngineColumnType.VARCHAR, + "set": RustWrenEngineColumnType.VARCHAR, # Numeric Types(https://dev.mysql.com/doc/refman/8.4/en/numeric-types.html) - "bit": WrenEngineColumnType.TINYINT, - "tinyint": WrenEngineColumnType.TINYINT, - "smallint": WrenEngineColumnType.SMALLINT, - "mediumint": WrenEngineColumnType.INTEGER, - "int": WrenEngineColumnType.INTEGER, - "integer": WrenEngineColumnType.INTEGER, - "bigint": WrenEngineColumnType.BIGINT, + "bit": RustWrenEngineColumnType.TINYINT, + "tinyint": RustWrenEngineColumnType.TINYINT, + "smallint": RustWrenEngineColumnType.SMALLINT, + "mediumint": RustWrenEngineColumnType.INTEGER, + "int": RustWrenEngineColumnType.INTEGER, + "integer": RustWrenEngineColumnType.INTEGER, + "bigint": RustWrenEngineColumnType.BIGINT, # boolean - "bool": WrenEngineColumnType.BOOLEAN, - "boolean": WrenEngineColumnType.BOOLEAN, + "bool": RustWrenEngineColumnType.BOOL, + "boolean": RustWrenEngineColumnType.BOOL, # Decimal - "float": WrenEngineColumnType.FLOAT8, - "double": WrenEngineColumnType.DOUBLE, - "decimal": WrenEngineColumnType.DECIMAL, - "numeric": WrenEngineColumnType.NUMERIC, + "float": RustWrenEngineColumnType.FLOAT8, + "double": RustWrenEngineColumnType.DOUBLE, + "decimal": RustWrenEngineColumnType.DECIMAL, + "numeric": RustWrenEngineColumnType.NUMERIC, # Date and Time Types(https://dev.mysql.com/doc/refman/8.4/en/date-and-time-types.html) - "date": WrenEngineColumnType.DATE, - "datetime": WrenEngineColumnType.TIMESTAMP, - "timestamp": WrenEngineColumnType.TIMESTAMPTZ, + "date": RustWrenEngineColumnType.DATE, + "datetime": RustWrenEngineColumnType.TIMESTAMP, + "timestamp": RustWrenEngineColumnType.TIMESTAMPTZ, # JSON Type - "json": WrenEngineColumnType.JSON, + "json": RustWrenEngineColumnType.JSON, } - return switcher.get(data_type.lower(), WrenEngineColumnType.UNKNOWN) + return switcher.get(data_type.lower(), RustWrenEngineColumnType.UNKNOWN) diff --git a/ibis-server/app/model/metadata/postgres.py b/ibis-server/app/model/metadata/postgres.py index 2d9619094..6a5b5720d 100644 --- a/ibis-server/app/model/metadata/postgres.py +++ b/ibis-server/app/model/metadata/postgres.py @@ -4,9 +4,9 @@ Column, Constraint, ConstraintType, + RustWrenEngineColumnType, Table, TableProperties, - WrenEngineColumnType, ) from app.model.metadata.metadata import Metadata @@ -141,31 +141,31 @@ def _transform_postgres_column_type(self, data_type): # all possible types listed here: https://www.postgresql.org/docs/current/datatype.html#DATATYPE-TABLE switcher = { - "text": WrenEngineColumnType.TEXT, - "char": WrenEngineColumnType.CHAR, - "character": WrenEngineColumnType.CHAR, - "bpchar": WrenEngineColumnType.CHAR, - "name": WrenEngineColumnType.CHAR, - "character varying": WrenEngineColumnType.VARCHAR, - "bigint": WrenEngineColumnType.BIGINT, - "int": WrenEngineColumnType.INTEGER, - "integer": WrenEngineColumnType.INTEGER, - "smallint": WrenEngineColumnType.SMALLINT, - "real": WrenEngineColumnType.REAL, - "double precision": WrenEngineColumnType.DOUBLE, - "numeric": WrenEngineColumnType.DECIMAL, - "decimal": WrenEngineColumnType.DECIMAL, - "boolean": WrenEngineColumnType.BOOLEAN, - "timestamp": WrenEngineColumnType.TIMESTAMP, - "timestamp without time zone": WrenEngineColumnType.TIMESTAMP, - "timestamp with time zone": WrenEngineColumnType.TIMESTAMPTZ, - "date": WrenEngineColumnType.DATE, - "interval": WrenEngineColumnType.INTERVAL, - "json": WrenEngineColumnType.JSON, - "bytea": WrenEngineColumnType.BYTEA, - "uuid": WrenEngineColumnType.UUID, - "inet": WrenEngineColumnType.INET, - "oid": WrenEngineColumnType.OID, + "text": RustWrenEngineColumnType.TEXT, + "char": RustWrenEngineColumnType.CHAR, + "character": RustWrenEngineColumnType.CHAR, + "bpchar": RustWrenEngineColumnType.CHAR, + "name": RustWrenEngineColumnType.CHAR, + "character varying": RustWrenEngineColumnType.VARCHAR, + "bigint": RustWrenEngineColumnType.BIGINT, + "int": RustWrenEngineColumnType.INTEGER, + "integer": RustWrenEngineColumnType.INTEGER, + "smallint": RustWrenEngineColumnType.SMALLINT, + "real": RustWrenEngineColumnType.REAL, + "double precision": RustWrenEngineColumnType.DOUBLE, + "numeric": RustWrenEngineColumnType.DECIMAL, + "decimal": RustWrenEngineColumnType.DECIMAL, + "boolean": RustWrenEngineColumnType.BOOL, + "timestamp": RustWrenEngineColumnType.TIMESTAMP, + "timestamp without time zone": RustWrenEngineColumnType.TIMESTAMP, + "timestamp with time zone": RustWrenEngineColumnType.TIMESTAMPTZ, + "date": RustWrenEngineColumnType.DATE, + "interval": RustWrenEngineColumnType.INTERVAL, + "json": RustWrenEngineColumnType.JSON, + "bytea": RustWrenEngineColumnType.BYTEA, + "uuid": RustWrenEngineColumnType.UUID, + "inet": RustWrenEngineColumnType.INET, + "oid": RustWrenEngineColumnType.OID, } - return switcher.get(data_type, WrenEngineColumnType.UNKNOWN) + return switcher.get(data_type, RustWrenEngineColumnType.UNKNOWN) diff --git a/ibis-server/app/model/metadata/snowflake.py b/ibis-server/app/model/metadata/snowflake.py new file mode 100644 index 000000000..7998b9f09 --- /dev/null +++ b/ibis-server/app/model/metadata/snowflake.py @@ -0,0 +1,161 @@ +from contextlib import closing + +from app.model import SnowflakeConnectionInfo +from app.model.data_source import DataSource +from app.model.metadata.dto import ( + Column, + Constraint, + ConstraintType, + RustWrenEngineColumnType, + Table, + TableProperties, +) +from app.model.metadata.metadata import Metadata + + +class SnowflakeMetadata(Metadata): + def __init__(self, connection_info: SnowflakeConnectionInfo): + super().__init__(connection_info) + self.connection = DataSource.snowflake.get_connection(connection_info) + + def get_table_list(self) -> list[Table]: + schema = self._get_schema_name() + sql = f""" + SELECT + c.TABLE_CATALOG AS TABLE_CATALOG, + c.TABLE_SCHEMA AS TABLE_SCHEMA, + c.TABLE_NAME AS TABLE_NAME, + c.COLUMN_NAME AS COLUMN_NAME, + c.DATA_TYPE AS DATA_TYPE, + c.IS_NULLABLE AS IS_NULLABLE, + c.COMMENT AS COLUMN_COMMENT, + t.COMMENT AS TABLE_COMMENT + FROM + INFORMATION_SCHEMA.COLUMNS c + JOIN + INFORMATION_SCHEMA.TABLES t + ON c.TABLE_SCHEMA = t.TABLE_SCHEMA + AND c.TABLE_NAME = t.TABLE_NAME + WHERE + c.TABLE_SCHEMA = '{schema}'; + """ + response = self.connection.sql(sql).to_pandas().to_dict(orient="records") + + unique_tables = {} + for row in response: + # generate unique table name + schema_table = self._format_compact_table_name( + row["TABLE_SCHEMA"], row["TABLE_NAME"] + ) + # init table if not exists + if schema_table not in unique_tables: + unique_tables[schema_table] = Table( + name=schema_table, + description=row["TABLE_COMMENT"], + columns=[], + properties=TableProperties( + schema=row["TABLE_SCHEMA"], + catalog=row["TABLE_CATALOG"], + table=row["TABLE_NAME"], + ), + primaryKey="", + ) + + # table exists, and add column to the table + unique_tables[schema_table].columns.append( + Column( + name=row["COLUMN_NAME"], + type=self._transform_column_type(row["DATA_TYPE"]), + notNull=row["IS_NULLABLE"].lower() == "no", + description=row["COLUMN_COMMENT"], + properties=None, + ) + ) + return list(unique_tables.values()) + + def get_constraints(self) -> list[Constraint]: + database = self._get_database_name() + schema = self._get_schema_name() + sql = f""" + SHOW IMPORTED KEYS IN SCHEMA {database}.{schema}; + """ + with closing(self.connection.raw_sql(sql)) as cur: + fields = [field[0] for field in cur.description] + res = [dict(zip(fields, row)) for row in cur.fetchall()] + constraints = [] + for row in res: + constraints.append( + Constraint( + constraintName=self._format_constraint_name( + row["pk_table_name"], + row["pk_column_name"], + row["fk_table_name"], + row["fk_column_name"], + ), + constraintTable=self._format_compact_table_name( + row["pk_schema_name"], row["pk_table_name"] + ), + constraintColumn=row["pk_column_name"], + constraintedTable=self._format_compact_table_name( + row["fk_schema_name"], row["fk_table_name"] + ), + constraintedColumn=row["fk_column_name"], + constraintType=ConstraintType.FOREIGN_KEY, + ) + ) + return constraints + + def get_version(self) -> str: + return self.connection.sql("SELECT CURRENT_VERSION()").to_pandas().iloc[0, 0] + + def _get_database_name(self): + return self.connection_info.database.get_secret_value() + + def _get_schema_name(self): + return self.connection_info.sf_schema.get_secret_value() + + def _format_compact_table_name(self, schema: str, table: str): + return f"{schema}.{table}" + + def _format_constraint_name( + self, table_name, column_name, referenced_table_name, referenced_column_name + ): + return f"{table_name}_{column_name}_{referenced_table_name}_{referenced_column_name}" + + def _transform_column_type(self, data_type): + # all possible types listed here: https://docs.snowflake.com/en/sql-reference/intro-summary-data-types + switcher = { + # Numeric Types + "number": RustWrenEngineColumnType.NUMERIC, + "decimal": RustWrenEngineColumnType.NUMERIC, + "numeric": RustWrenEngineColumnType.NUMERIC, + "int": RustWrenEngineColumnType.INTEGER, + "integer": RustWrenEngineColumnType.INTEGER, + "bigint": RustWrenEngineColumnType.BIGINT, + "smallint": RustWrenEngineColumnType.SMALLINT, + "tinyint": RustWrenEngineColumnType.TINYINT, + "byteint": RustWrenEngineColumnType.TINYINT, + # Float + "float4": RustWrenEngineColumnType.FLOAT4, + "float": RustWrenEngineColumnType.FLOAT8, + "float8": RustWrenEngineColumnType.FLOAT8, + "double": RustWrenEngineColumnType.DOUBLE, + "double precision": RustWrenEngineColumnType.DOUBLE, + "real": RustWrenEngineColumnType.REAL, + # String Types + "varchar": RustWrenEngineColumnType.VARCHAR, + "char": RustWrenEngineColumnType.CHAR, + "character": RustWrenEngineColumnType.CHAR, + "string": RustWrenEngineColumnType.STRING, + "text": RustWrenEngineColumnType.TEXT, + # Boolean + "boolean": RustWrenEngineColumnType.BOOL, + # Date and Time Types + "date": RustWrenEngineColumnType.DATE, + "datetime": RustWrenEngineColumnType.TIMESTAMP, + "timestamp": RustWrenEngineColumnType.TIMESTAMP, + "timestamp_ntz": RustWrenEngineColumnType.TIMESTAMP, + "timestamp_tz": RustWrenEngineColumnType.TIMESTAMPTZ, + } + + return switcher.get(data_type.lower(), RustWrenEngineColumnType.UNKNOWN) diff --git a/ibis-server/app/model/metadata/trino.py b/ibis-server/app/model/metadata/trino.py index 1273de14b..0f063d91b 100644 --- a/ibis-server/app/model/metadata/trino.py +++ b/ibis-server/app/model/metadata/trino.py @@ -1,3 +1,4 @@ +import re from urllib.parse import urlparse from app.model import TrinoConnectionInfo @@ -5,9 +6,9 @@ from app.model.metadata.dto import ( Column, Constraint, + RustWrenEngineColumnType, Table, TableProperties, - WrenEngineColumnType, ) from app.model.metadata.metadata import Metadata @@ -97,38 +98,40 @@ def _get_schema_name(self): def _transform_column_type(self, data_type): # all possible types listed here: https://trino.io/docs/current/language/types.html + # trim the (all characters) at the end of the data_type if exists + data_type = re.sub(r"\(.*\)", "", data_type).strip() switcher = { # String Types (ignore Binary and Spatial Types for now) - "char": WrenEngineColumnType.CHAR, - "varchar": WrenEngineColumnType.VARCHAR, - "tinytext": WrenEngineColumnType.TEXT, - "text": WrenEngineColumnType.TEXT, - "mediumtext": WrenEngineColumnType.TEXT, - "longtext": WrenEngineColumnType.TEXT, - "enum": WrenEngineColumnType.VARCHAR, - "set": WrenEngineColumnType.VARCHAR, + "char": RustWrenEngineColumnType.CHAR, + "varchar": RustWrenEngineColumnType.VARCHAR, + "tinytext": RustWrenEngineColumnType.TEXT, + "text": RustWrenEngineColumnType.TEXT, + "mediumtext": RustWrenEngineColumnType.TEXT, + "longtext": RustWrenEngineColumnType.TEXT, + "enum": RustWrenEngineColumnType.VARCHAR, + "set": RustWrenEngineColumnType.VARCHAR, # Numeric Types(https://dev.mysql.com/doc/refman/8.4/en/numeric-types.html) - "bit": WrenEngineColumnType.TINYINT, - "tinyint": WrenEngineColumnType.TINYINT, - "smallint": WrenEngineColumnType.SMALLINT, - "mediumint": WrenEngineColumnType.INTEGER, - "int": WrenEngineColumnType.INTEGER, - "integer": WrenEngineColumnType.INTEGER, - "bigint": WrenEngineColumnType.BIGINT, + "bit": RustWrenEngineColumnType.TINYINT, + "tinyint": RustWrenEngineColumnType.TINYINT, + "smallint": RustWrenEngineColumnType.SMALLINT, + "mediumint": RustWrenEngineColumnType.INTEGER, + "int": RustWrenEngineColumnType.INTEGER, + "integer": RustWrenEngineColumnType.INTEGER, + "bigint": RustWrenEngineColumnType.BIGINT, # boolean - "bool": WrenEngineColumnType.BOOLEAN, - "boolean": WrenEngineColumnType.BOOLEAN, + "bool": RustWrenEngineColumnType.BOOL, + "boolean": RustWrenEngineColumnType.BOOL, # Decimal - "float": WrenEngineColumnType.FLOAT8, - "double": WrenEngineColumnType.DOUBLE, - "decimal": WrenEngineColumnType.DECIMAL, - "numeric": WrenEngineColumnType.NUMERIC, + "float": RustWrenEngineColumnType.FLOAT4, + "double": RustWrenEngineColumnType.DOUBLE, + "decimal": RustWrenEngineColumnType.DECIMAL, + "numeric": RustWrenEngineColumnType.NUMERIC, # Date and Time Types(https://dev.mysql.com/doc/refman/8.4/en/date-and-time-types.html) - "date": WrenEngineColumnType.DATE, - "datetime": WrenEngineColumnType.TIMESTAMP, - "timestamp": WrenEngineColumnType.TIMESTAMPTZ, + "date": RustWrenEngineColumnType.DATE, + "datetime": RustWrenEngineColumnType.TIMESTAMP, + "timestamp": RustWrenEngineColumnType.TIMESTAMPTZ, # JSON Type - "json": WrenEngineColumnType.JSON, + "json": RustWrenEngineColumnType.JSON, } - return switcher.get(data_type.lower(), WrenEngineColumnType.UNKNOWN) + return switcher.get(data_type.lower(), RustWrenEngineColumnType.UNKNOWN) diff --git a/ibis-server/docs/development.md b/ibis-server/docs/development.md index 18b150bc3..3c1fe2802 100644 --- a/ibis-server/docs/development.md +++ b/ibis-server/docs/development.md @@ -28,6 +28,7 @@ This installs the pre-commit hooks. To get the application running: 1. Execute `just install` to install the dependencies 2. Create a `.env` file and fill in the required environment variables (see [Environment Variables](#Environment-Variables)) +3. If you want to use `wren_core`, you need to install the core by `just install-core`. After you modify the core, you can update it by `just update-core`. To start the server: - Execute `just run` to start the server @@ -39,8 +40,8 @@ To start the server: - Run the container: `just docker-run` ### Run the testing -- Prepare the Wren Engine server (see [Wren Engine Example](../../example/README.md)) -- Use the `.env` file to set the `WREN_ENGINE_ENDPOINT` environment variable to change the endpoint of the Wren Engine server. +- Prepare the Java Engine server (see [Java Engine Example](../../example/README.md)) +- Use the `.env` file to set the `WREN_ENGINE_ENDPOINT` environment variable to change the endpoint of the Java Engine server. ``` WREN_ENGINE_ENDPOINT=http://localhost:8080 ``` diff --git a/ibis-server/poetry.lock b/ibis-server/poetry.lock index cf3a9316a..54e22efd7 100644 --- a/ibis-server/poetry.lock +++ b/ibis-server/poetry.lock @@ -13,102 +13,102 @@ files = [ [[package]] name = "aiohttp" -version = "3.10.10" +version = "3.10.11" description = "Async http client/server framework (asyncio)" optional = false python-versions = ">=3.8" files = [ - {file = "aiohttp-3.10.10-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:be7443669ae9c016b71f402e43208e13ddf00912f47f623ee5994e12fc7d4b3f"}, - {file = "aiohttp-3.10.10-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:7b06b7843929e41a94ea09eb1ce3927865387e3e23ebe108e0d0d09b08d25be9"}, - {file = "aiohttp-3.10.10-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:333cf6cf8e65f6a1e06e9eb3e643a0c515bb850d470902274239fea02033e9a8"}, - {file = "aiohttp-3.10.10-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:274cfa632350225ce3fdeb318c23b4a10ec25c0e2c880eff951a3842cf358ac1"}, - {file = "aiohttp-3.10.10-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d9e5e4a85bdb56d224f412d9c98ae4cbd032cc4f3161818f692cd81766eee65a"}, - {file = "aiohttp-3.10.10-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2b606353da03edcc71130b52388d25f9a30a126e04caef1fd637e31683033abd"}, - {file = "aiohttp-3.10.10-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ab5a5a0c7a7991d90446a198689c0535be89bbd6b410a1f9a66688f0880ec026"}, - {file = "aiohttp-3.10.10-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:578a4b875af3e0daaf1ac6fa983d93e0bbfec3ead753b6d6f33d467100cdc67b"}, - {file = "aiohttp-3.10.10-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:8105fd8a890df77b76dd3054cddf01a879fc13e8af576805d667e0fa0224c35d"}, - {file = "aiohttp-3.10.10-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:3bcd391d083f636c06a68715e69467963d1f9600f85ef556ea82e9ef25f043f7"}, - {file = "aiohttp-3.10.10-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:fbc6264158392bad9df19537e872d476f7c57adf718944cc1e4495cbabf38e2a"}, - {file = "aiohttp-3.10.10-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:e48d5021a84d341bcaf95c8460b152cfbad770d28e5fe14a768988c461b821bc"}, - {file = "aiohttp-3.10.10-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:2609e9ab08474702cc67b7702dbb8a80e392c54613ebe80db7e8dbdb79837c68"}, - {file = "aiohttp-3.10.10-cp310-cp310-win32.whl", hash = "sha256:84afcdea18eda514c25bc68b9af2a2b1adea7c08899175a51fe7c4fb6d551257"}, - {file = "aiohttp-3.10.10-cp310-cp310-win_amd64.whl", hash = "sha256:9c72109213eb9d3874f7ac8c0c5fa90e072d678e117d9061c06e30c85b4cf0e6"}, - {file = "aiohttp-3.10.10-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:c30a0eafc89d28e7f959281b58198a9fa5e99405f716c0289b7892ca345fe45f"}, - {file = "aiohttp-3.10.10-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:258c5dd01afc10015866114e210fb7365f0d02d9d059c3c3415382ab633fcbcb"}, - {file = "aiohttp-3.10.10-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:15ecd889a709b0080f02721255b3f80bb261c2293d3c748151274dfea93ac871"}, - {file = "aiohttp-3.10.10-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f3935f82f6f4a3820270842e90456ebad3af15810cf65932bd24da4463bc0a4c"}, - {file = "aiohttp-3.10.10-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:413251f6fcf552a33c981c4709a6bba37b12710982fec8e558ae944bfb2abd38"}, - {file = "aiohttp-3.10.10-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d1720b4f14c78a3089562b8875b53e36b51c97c51adc53325a69b79b4b48ebcb"}, - {file = "aiohttp-3.10.10-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:679abe5d3858b33c2cf74faec299fda60ea9de62916e8b67e625d65bf069a3b7"}, - {file = "aiohttp-3.10.10-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:79019094f87c9fb44f8d769e41dbb664d6e8fcfd62f665ccce36762deaa0e911"}, - {file = "aiohttp-3.10.10-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:fe2fb38c2ed905a2582948e2de560675e9dfbee94c6d5ccdb1301c6d0a5bf092"}, - {file = "aiohttp-3.10.10-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:a3f00003de6eba42d6e94fabb4125600d6e484846dbf90ea8e48a800430cc142"}, - {file = "aiohttp-3.10.10-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:1bbb122c557a16fafc10354b9d99ebf2f2808a660d78202f10ba9d50786384b9"}, - {file = "aiohttp-3.10.10-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:30ca7c3b94708a9d7ae76ff281b2f47d8eaf2579cd05971b5dc681db8caac6e1"}, - {file = "aiohttp-3.10.10-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:df9270660711670e68803107d55c2b5949c2e0f2e4896da176e1ecfc068b974a"}, - {file = "aiohttp-3.10.10-cp311-cp311-win32.whl", hash = "sha256:aafc8ee9b742ce75044ae9a4d3e60e3d918d15a4c2e08a6c3c3e38fa59b92d94"}, - {file = "aiohttp-3.10.10-cp311-cp311-win_amd64.whl", hash = "sha256:362f641f9071e5f3ee6f8e7d37d5ed0d95aae656adf4ef578313ee585b585959"}, - {file = "aiohttp-3.10.10-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:9294bbb581f92770e6ed5c19559e1e99255e4ca604a22c5c6397b2f9dd3ee42c"}, - {file = "aiohttp-3.10.10-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:a8fa23fe62c436ccf23ff930149c047f060c7126eae3ccea005f0483f27b2e28"}, - {file = "aiohttp-3.10.10-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:5c6a5b8c7926ba5d8545c7dd22961a107526562da31a7a32fa2456baf040939f"}, - {file = "aiohttp-3.10.10-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:007ec22fbc573e5eb2fb7dec4198ef8f6bf2fe4ce20020798b2eb5d0abda6138"}, - {file = "aiohttp-3.10.10-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9627cc1a10c8c409b5822a92d57a77f383b554463d1884008e051c32ab1b3742"}, - {file = "aiohttp-3.10.10-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:50edbcad60d8f0e3eccc68da67f37268b5144ecc34d59f27a02f9611c1d4eec7"}, - {file = "aiohttp-3.10.10-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a45d85cf20b5e0d0aa5a8dca27cce8eddef3292bc29d72dcad1641f4ed50aa16"}, - {file = "aiohttp-3.10.10-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0b00807e2605f16e1e198f33a53ce3c4523114059b0c09c337209ae55e3823a8"}, - {file = "aiohttp-3.10.10-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:f2d4324a98062be0525d16f768a03e0bbb3b9fe301ceee99611dc9a7953124e6"}, - {file = "aiohttp-3.10.10-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:438cd072f75bb6612f2aca29f8bd7cdf6e35e8f160bc312e49fbecab77c99e3a"}, - {file = "aiohttp-3.10.10-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:baa42524a82f75303f714108fea528ccacf0386af429b69fff141ffef1c534f9"}, - {file = "aiohttp-3.10.10-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:a7d8d14fe962153fc681f6366bdec33d4356f98a3e3567782aac1b6e0e40109a"}, - {file = "aiohttp-3.10.10-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:c1277cd707c465cd09572a774559a3cc7c7a28802eb3a2a9472588f062097205"}, - {file = "aiohttp-3.10.10-cp312-cp312-win32.whl", hash = "sha256:59bb3c54aa420521dc4ce3cc2c3fe2ad82adf7b09403fa1f48ae45c0cbde6628"}, - {file = "aiohttp-3.10.10-cp312-cp312-win_amd64.whl", hash = "sha256:0e1b370d8007c4ae31ee6db7f9a2fe801a42b146cec80a86766e7ad5c4a259cf"}, - {file = "aiohttp-3.10.10-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:ad7593bb24b2ab09e65e8a1d385606f0f47c65b5a2ae6c551db67d6653e78c28"}, - {file = "aiohttp-3.10.10-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:1eb89d3d29adaf533588f209768a9c02e44e4baf832b08118749c5fad191781d"}, - {file = "aiohttp-3.10.10-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:3fe407bf93533a6fa82dece0e74dbcaaf5d684e5a51862887f9eaebe6372cd79"}, - {file = "aiohttp-3.10.10-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:50aed5155f819873d23520919e16703fc8925e509abbb1a1491b0087d1cd969e"}, - {file = "aiohttp-3.10.10-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:4f05e9727ce409358baa615dbeb9b969db94324a79b5a5cea45d39bdb01d82e6"}, - {file = "aiohttp-3.10.10-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3dffb610a30d643983aeb185ce134f97f290f8935f0abccdd32c77bed9388b42"}, - {file = "aiohttp-3.10.10-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:aa6658732517ddabe22c9036479eabce6036655ba87a0224c612e1ae6af2087e"}, - {file = "aiohttp-3.10.10-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:741a46d58677d8c733175d7e5aa618d277cd9d880301a380fd296975a9cdd7bc"}, - {file = "aiohttp-3.10.10-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:e00e3505cd80440f6c98c6d69269dcc2a119f86ad0a9fd70bccc59504bebd68a"}, - {file = "aiohttp-3.10.10-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:ffe595f10566f8276b76dc3a11ae4bb7eba1aac8ddd75811736a15b0d5311414"}, - {file = "aiohttp-3.10.10-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:bdfcf6443637c148c4e1a20c48c566aa694fa5e288d34b20fcdc58507882fed3"}, - {file = "aiohttp-3.10.10-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:d183cf9c797a5291e8301790ed6d053480ed94070637bfaad914dd38b0981f67"}, - {file = "aiohttp-3.10.10-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:77abf6665ae54000b98b3c742bc6ea1d1fb31c394bcabf8b5d2c1ac3ebfe7f3b"}, - {file = "aiohttp-3.10.10-cp313-cp313-win32.whl", hash = "sha256:4470c73c12cd9109db8277287d11f9dd98f77fc54155fc71a7738a83ffcc8ea8"}, - {file = "aiohttp-3.10.10-cp313-cp313-win_amd64.whl", hash = "sha256:486f7aabfa292719a2753c016cc3a8f8172965cabb3ea2e7f7436c7f5a22a151"}, - {file = "aiohttp-3.10.10-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:1b66ccafef7336a1e1f0e389901f60c1d920102315a56df85e49552308fc0486"}, - {file = "aiohttp-3.10.10-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:acd48d5b80ee80f9432a165c0ac8cbf9253eaddb6113269a5e18699b33958dbb"}, - {file = "aiohttp-3.10.10-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:3455522392fb15ff549d92fbf4b73b559d5e43dc522588f7eb3e54c3f38beee7"}, - {file = "aiohttp-3.10.10-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:45c3b868724137f713a38376fef8120c166d1eadd50da1855c112fe97954aed8"}, - {file = "aiohttp-3.10.10-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:da1dee8948d2137bb51fbb8a53cce6b1bcc86003c6b42565f008438b806cccd8"}, - {file = "aiohttp-3.10.10-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c5ce2ce7c997e1971b7184ee37deb6ea9922ef5163c6ee5aa3c274b05f9e12fa"}, - {file = "aiohttp-3.10.10-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:28529e08fde6f12eba8677f5a8608500ed33c086f974de68cc65ab218713a59d"}, - {file = "aiohttp-3.10.10-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f7db54c7914cc99d901d93a34704833568d86c20925b2762f9fa779f9cd2e70f"}, - {file = "aiohttp-3.10.10-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:03a42ac7895406220124c88911ebee31ba8b2d24c98507f4a8bf826b2937c7f2"}, - {file = "aiohttp-3.10.10-cp38-cp38-musllinux_1_2_i686.whl", hash = "sha256:7e338c0523d024fad378b376a79faff37fafb3c001872a618cde1d322400a572"}, - {file = "aiohttp-3.10.10-cp38-cp38-musllinux_1_2_ppc64le.whl", hash = "sha256:038f514fe39e235e9fef6717fbf944057bfa24f9b3db9ee551a7ecf584b5b480"}, - {file = "aiohttp-3.10.10-cp38-cp38-musllinux_1_2_s390x.whl", hash = "sha256:64f6c17757251e2b8d885d728b6433d9d970573586a78b78ba8929b0f41d045a"}, - {file = "aiohttp-3.10.10-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:93429602396f3383a797a2a70e5f1de5df8e35535d7806c9f91df06f297e109b"}, - {file = "aiohttp-3.10.10-cp38-cp38-win32.whl", hash = "sha256:c823bc3971c44ab93e611ab1a46b1eafeae474c0c844aff4b7474287b75fe49c"}, - {file = "aiohttp-3.10.10-cp38-cp38-win_amd64.whl", hash = "sha256:54ca74df1be3c7ca1cf7f4c971c79c2daf48d9aa65dea1a662ae18926f5bc8ce"}, - {file = "aiohttp-3.10.10-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:01948b1d570f83ee7bbf5a60ea2375a89dfb09fd419170e7f5af029510033d24"}, - {file = "aiohttp-3.10.10-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:9fc1500fd2a952c5c8e3b29aaf7e3cc6e27e9cfc0a8819b3bce48cc1b849e4cc"}, - {file = "aiohttp-3.10.10-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:f614ab0c76397661b90b6851a030004dac502e48260ea10f2441abd2207fbcc7"}, - {file = "aiohttp-3.10.10-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:00819de9e45d42584bed046314c40ea7e9aea95411b38971082cad449392b08c"}, - {file = "aiohttp-3.10.10-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:05646ebe6b94cc93407b3bf34b9eb26c20722384d068eb7339de802154d61bc5"}, - {file = "aiohttp-3.10.10-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:998f3bd3cfc95e9424a6acd7840cbdd39e45bc09ef87533c006f94ac47296090"}, - {file = "aiohttp-3.10.10-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d9010c31cd6fa59438da4e58a7f19e4753f7f264300cd152e7f90d4602449762"}, - {file = "aiohttp-3.10.10-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7ea7ffc6d6d6f8a11e6f40091a1040995cdff02cfc9ba4c2f30a516cb2633554"}, - {file = "aiohttp-3.10.10-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:ef9c33cc5cbca35808f6c74be11eb7f5f6b14d2311be84a15b594bd3e58b5527"}, - {file = "aiohttp-3.10.10-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:ce0cdc074d540265bfeb31336e678b4e37316849d13b308607efa527e981f5c2"}, - {file = "aiohttp-3.10.10-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:597a079284b7ee65ee102bc3a6ea226a37d2b96d0418cc9047490f231dc09fe8"}, - {file = "aiohttp-3.10.10-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:7789050d9e5d0c309c706953e5e8876e38662d57d45f936902e176d19f1c58ab"}, - {file = "aiohttp-3.10.10-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:e7f8b04d83483577fd9200461b057c9f14ced334dcb053090cea1da9c8321a91"}, - {file = "aiohttp-3.10.10-cp39-cp39-win32.whl", hash = "sha256:c02a30b904282777d872266b87b20ed8cc0d1501855e27f831320f471d54d983"}, - {file = "aiohttp-3.10.10-cp39-cp39-win_amd64.whl", hash = "sha256:edfe3341033a6b53a5c522c802deb2079eee5cbfbb0af032a55064bd65c73a23"}, - {file = "aiohttp-3.10.10.tar.gz", hash = "sha256:0631dd7c9f0822cc61c88586ca76d5b5ada26538097d0f1df510b082bad3411a"}, + {file = "aiohttp-3.10.11-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:5077b1a5f40ffa3ba1f40d537d3bec4383988ee51fbba6b74aa8fb1bc466599e"}, + {file = "aiohttp-3.10.11-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:8d6a14a4d93b5b3c2891fca94fa9d41b2322a68194422bef0dd5ec1e57d7d298"}, + {file = "aiohttp-3.10.11-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:ffbfde2443696345e23a3c597049b1dd43049bb65337837574205e7368472177"}, + {file = "aiohttp-3.10.11-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:20b3d9e416774d41813bc02fdc0663379c01817b0874b932b81c7f777f67b217"}, + {file = "aiohttp-3.10.11-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:2b943011b45ee6bf74b22245c6faab736363678e910504dd7531a58c76c9015a"}, + {file = "aiohttp-3.10.11-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:48bc1d924490f0d0b3658fe5c4b081a4d56ebb58af80a6729d4bd13ea569797a"}, + {file = "aiohttp-3.10.11-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e12eb3f4b1f72aaaf6acd27d045753b18101524f72ae071ae1c91c1cd44ef115"}, + {file = "aiohttp-3.10.11-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f14ebc419a568c2eff3c1ed35f634435c24ead2fe19c07426af41e7adb68713a"}, + {file = "aiohttp-3.10.11-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:72b191cdf35a518bfc7ca87d770d30941decc5aaf897ec8b484eb5cc8c7706f3"}, + {file = "aiohttp-3.10.11-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:5ab2328a61fdc86424ee540d0aeb8b73bbcad7351fb7cf7a6546fc0bcffa0038"}, + {file = "aiohttp-3.10.11-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:aa93063d4af05c49276cf14e419550a3f45258b6b9d1f16403e777f1addf4519"}, + {file = "aiohttp-3.10.11-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:30283f9d0ce420363c24c5c2421e71a738a2155f10adbb1a11a4d4d6d2715cfc"}, + {file = "aiohttp-3.10.11-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:e5358addc8044ee49143c546d2182c15b4ac3a60be01c3209374ace05af5733d"}, + {file = "aiohttp-3.10.11-cp310-cp310-win32.whl", hash = "sha256:e1ffa713d3ea7cdcd4aea9cddccab41edf6882fa9552940344c44e59652e1120"}, + {file = "aiohttp-3.10.11-cp310-cp310-win_amd64.whl", hash = "sha256:778cbd01f18ff78b5dd23c77eb82987ee4ba23408cbed233009fd570dda7e674"}, + {file = "aiohttp-3.10.11-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:80ff08556c7f59a7972b1e8919f62e9c069c33566a6d28586771711e0eea4f07"}, + {file = "aiohttp-3.10.11-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:2c8f96e9ee19f04c4914e4e7a42a60861066d3e1abf05c726f38d9d0a466e695"}, + {file = "aiohttp-3.10.11-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:fb8601394d537da9221947b5d6e62b064c9a43e88a1ecd7414d21a1a6fba9c24"}, + {file = "aiohttp-3.10.11-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2ea224cf7bc2d8856d6971cea73b1d50c9c51d36971faf1abc169a0d5f85a382"}, + {file = "aiohttp-3.10.11-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:db9503f79e12d5d80b3efd4d01312853565c05367493379df76d2674af881caa"}, + {file = "aiohttp-3.10.11-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:0f449a50cc33f0384f633894d8d3cd020e3ccef81879c6e6245c3c375c448625"}, + {file = "aiohttp-3.10.11-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:82052be3e6d9e0c123499127782a01a2b224b8af8c62ab46b3f6197035ad94e9"}, + {file = "aiohttp-3.10.11-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:20063c7acf1eec550c8eb098deb5ed9e1bb0521613b03bb93644b810986027ac"}, + {file = "aiohttp-3.10.11-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:489cced07a4c11488f47aab1f00d0c572506883f877af100a38f1fedaa884c3a"}, + {file = "aiohttp-3.10.11-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:ea9b3bab329aeaa603ed3bf605f1e2a6f36496ad7e0e1aa42025f368ee2dc07b"}, + {file = "aiohttp-3.10.11-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:ca117819d8ad113413016cb29774b3f6d99ad23c220069789fc050267b786c16"}, + {file = "aiohttp-3.10.11-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:2dfb612dcbe70fb7cdcf3499e8d483079b89749c857a8f6e80263b021745c730"}, + {file = "aiohttp-3.10.11-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:f9b615d3da0d60e7d53c62e22b4fd1c70f4ae5993a44687b011ea3a2e49051b8"}, + {file = "aiohttp-3.10.11-cp311-cp311-win32.whl", hash = "sha256:29103f9099b6068bbdf44d6a3d090e0a0b2be6d3c9f16a070dd9d0d910ec08f9"}, + {file = "aiohttp-3.10.11-cp311-cp311-win_amd64.whl", hash = "sha256:236b28ceb79532da85d59aa9b9bf873b364e27a0acb2ceaba475dc61cffb6f3f"}, + {file = "aiohttp-3.10.11-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:7480519f70e32bfb101d71fb9a1f330fbd291655a4c1c922232a48c458c52710"}, + {file = "aiohttp-3.10.11-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:f65267266c9aeb2287a6622ee2bb39490292552f9fbf851baabc04c9f84e048d"}, + {file = "aiohttp-3.10.11-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:7400a93d629a0608dc1d6c55f1e3d6e07f7375745aaa8bd7f085571e4d1cee97"}, + {file = "aiohttp-3.10.11-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f34b97e4b11b8d4eb2c3a4f975be626cc8af99ff479da7de49ac2c6d02d35725"}, + {file = "aiohttp-3.10.11-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1e7b825da878464a252ccff2958838f9caa82f32a8dbc334eb9b34a026e2c636"}, + {file = "aiohttp-3.10.11-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f9f92a344c50b9667827da308473005f34767b6a2a60d9acff56ae94f895f385"}, + {file = "aiohttp-3.10.11-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bc6f1ab987a27b83c5268a17218463c2ec08dbb754195113867a27b166cd6087"}, + {file = "aiohttp-3.10.11-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1dc0f4ca54842173d03322793ebcf2c8cc2d34ae91cc762478e295d8e361e03f"}, + {file = "aiohttp-3.10.11-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:7ce6a51469bfaacff146e59e7fb61c9c23006495d11cc24c514a455032bcfa03"}, + {file = "aiohttp-3.10.11-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:aad3cd91d484d065ede16f3cf15408254e2469e3f613b241a1db552c5eb7ab7d"}, + {file = "aiohttp-3.10.11-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:f4df4b8ca97f658c880fb4b90b1d1ec528315d4030af1ec763247ebfd33d8b9a"}, + {file = "aiohttp-3.10.11-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:2e4e18a0a2d03531edbc06c366954e40a3f8d2a88d2b936bbe78a0c75a3aab3e"}, + {file = "aiohttp-3.10.11-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:6ce66780fa1a20e45bc753cda2a149daa6dbf1561fc1289fa0c308391c7bc0a4"}, + {file = "aiohttp-3.10.11-cp312-cp312-win32.whl", hash = "sha256:a919c8957695ea4c0e7a3e8d16494e3477b86f33067478f43106921c2fef15bb"}, + {file = "aiohttp-3.10.11-cp312-cp312-win_amd64.whl", hash = "sha256:b5e29706e6389a2283a91611c91bf24f218962717c8f3b4e528ef529d112ee27"}, + {file = "aiohttp-3.10.11-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:703938e22434d7d14ec22f9f310559331f455018389222eed132808cd8f44127"}, + {file = "aiohttp-3.10.11-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:9bc50b63648840854e00084c2b43035a62e033cb9b06d8c22b409d56eb098413"}, + {file = "aiohttp-3.10.11-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:5f0463bf8b0754bc744e1feb61590706823795041e63edf30118a6f0bf577461"}, + {file = "aiohttp-3.10.11-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f6c6dec398ac5a87cb3a407b068e1106b20ef001c344e34154616183fe684288"}, + {file = "aiohttp-3.10.11-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:bcaf2d79104d53d4dcf934f7ce76d3d155302d07dae24dff6c9fffd217568067"}, + {file = "aiohttp-3.10.11-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:25fd5470922091b5a9aeeb7e75be609e16b4fba81cdeaf12981393fb240dd10e"}, + {file = "aiohttp-3.10.11-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bbde2ca67230923a42161b1f408c3992ae6e0be782dca0c44cb3206bf330dee1"}, + {file = "aiohttp-3.10.11-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:249c8ff8d26a8b41a0f12f9df804e7c685ca35a207e2410adbd3e924217b9006"}, + {file = "aiohttp-3.10.11-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:878ca6a931ee8c486a8f7b432b65431d095c522cbeb34892bee5be97b3481d0f"}, + {file = "aiohttp-3.10.11-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:8663f7777ce775f0413324be0d96d9730959b2ca73d9b7e2c2c90539139cbdd6"}, + {file = "aiohttp-3.10.11-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:6cd3f10b01f0c31481fba8d302b61603a2acb37b9d30e1d14e0f5a58b7b18a31"}, + {file = "aiohttp-3.10.11-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:4e8d8aad9402d3aa02fdc5ca2fe68bcb9fdfe1f77b40b10410a94c7f408b664d"}, + {file = "aiohttp-3.10.11-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:38e3c4f80196b4f6c3a85d134a534a56f52da9cb8d8e7af1b79a32eefee73a00"}, + {file = "aiohttp-3.10.11-cp313-cp313-win32.whl", hash = "sha256:fc31820cfc3b2863c6e95e14fcf815dc7afe52480b4dc03393c4873bb5599f71"}, + {file = "aiohttp-3.10.11-cp313-cp313-win_amd64.whl", hash = "sha256:4996ff1345704ffdd6d75fb06ed175938c133425af616142e7187f28dc75f14e"}, + {file = "aiohttp-3.10.11-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:74baf1a7d948b3d640badeac333af581a367ab916b37e44cf90a0334157cdfd2"}, + {file = "aiohttp-3.10.11-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:473aebc3b871646e1940c05268d451f2543a1d209f47035b594b9d4e91ce8339"}, + {file = "aiohttp-3.10.11-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:c2f746a6968c54ab2186574e15c3f14f3e7f67aef12b761e043b33b89c5b5f95"}, + {file = "aiohttp-3.10.11-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d110cabad8360ffa0dec8f6ec60e43286e9d251e77db4763a87dcfe55b4adb92"}, + {file = "aiohttp-3.10.11-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e0099c7d5d7afff4202a0c670e5b723f7718810000b4abcbc96b064129e64bc7"}, + {file = "aiohttp-3.10.11-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:0316e624b754dbbf8c872b62fe6dcb395ef20c70e59890dfa0de9eafccd2849d"}, + {file = "aiohttp-3.10.11-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5a5f7ab8baf13314e6b2485965cbacb94afff1e93466ac4d06a47a81c50f9cca"}, + {file = "aiohttp-3.10.11-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c891011e76041e6508cbfc469dd1a8ea09bc24e87e4c204e05f150c4c455a5fa"}, + {file = "aiohttp-3.10.11-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:9208299251370ee815473270c52cd3f7069ee9ed348d941d574d1457d2c73e8b"}, + {file = "aiohttp-3.10.11-cp38-cp38-musllinux_1_2_i686.whl", hash = "sha256:459f0f32c8356e8125f45eeff0ecf2b1cb6db1551304972702f34cd9e6c44658"}, + {file = "aiohttp-3.10.11-cp38-cp38-musllinux_1_2_ppc64le.whl", hash = "sha256:14cdc8c1810bbd4b4b9f142eeee23cda528ae4e57ea0923551a9af4820980e39"}, + {file = "aiohttp-3.10.11-cp38-cp38-musllinux_1_2_s390x.whl", hash = "sha256:971aa438a29701d4b34e4943e91b5e984c3ae6ccbf80dd9efaffb01bd0b243a9"}, + {file = "aiohttp-3.10.11-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:9a309c5de392dfe0f32ee57fa43ed8fc6ddf9985425e84bd51ed66bb16bce3a7"}, + {file = "aiohttp-3.10.11-cp38-cp38-win32.whl", hash = "sha256:9ec1628180241d906a0840b38f162a3215114b14541f1a8711c368a8739a9be4"}, + {file = "aiohttp-3.10.11-cp38-cp38-win_amd64.whl", hash = "sha256:9c6e0ffd52c929f985c7258f83185d17c76d4275ad22e90aa29f38e211aacbec"}, + {file = "aiohttp-3.10.11-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:cdc493a2e5d8dc79b2df5bec9558425bcd39aff59fc949810cbd0832e294b106"}, + {file = "aiohttp-3.10.11-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:b3e70f24e7d0405be2348da9d5a7836936bf3a9b4fd210f8c37e8d48bc32eca6"}, + {file = "aiohttp-3.10.11-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:968b8fb2a5eee2770eda9c7b5581587ef9b96fbdf8dcabc6b446d35ccc69df01"}, + {file = "aiohttp-3.10.11-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:deef4362af9493d1382ef86732ee2e4cbc0d7c005947bd54ad1a9a16dd59298e"}, + {file = "aiohttp-3.10.11-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:686b03196976e327412a1b094f4120778c7c4b9cff9bce8d2fdfeca386b89829"}, + {file = "aiohttp-3.10.11-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3bf6d027d9d1d34e1c2e1645f18a6498c98d634f8e373395221121f1c258ace8"}, + {file = "aiohttp-3.10.11-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:099fd126bf960f96d34a760e747a629c27fb3634da5d05c7ef4d35ef4ea519fc"}, + {file = "aiohttp-3.10.11-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c73c4d3dae0b4644bc21e3de546530531d6cdc88659cdeb6579cd627d3c206aa"}, + {file = "aiohttp-3.10.11-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:0c5580f3c51eea91559db3facd45d72e7ec970b04528b4709b1f9c2555bd6d0b"}, + {file = "aiohttp-3.10.11-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:fdf6429f0caabfd8a30c4e2eaecb547b3c340e4730ebfe25139779b9815ba138"}, + {file = "aiohttp-3.10.11-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:d97187de3c276263db3564bb9d9fad9e15b51ea10a371ffa5947a5ba93ad6777"}, + {file = "aiohttp-3.10.11-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:0acafb350cfb2eba70eb5d271f55e08bd4502ec35e964e18ad3e7d34d71f7261"}, + {file = "aiohttp-3.10.11-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:c13ed0c779911c7998a58e7848954bd4d63df3e3575f591e321b19a2aec8df9f"}, + {file = "aiohttp-3.10.11-cp39-cp39-win32.whl", hash = "sha256:22b7c540c55909140f63ab4f54ec2c20d2635c0289cdd8006da46f3327f971b9"}, + {file = "aiohttp-3.10.11-cp39-cp39-win_amd64.whl", hash = "sha256:7b26b1551e481012575dab8e3727b16fe7dd27eb2711d2e63ced7368756268fb"}, + {file = "aiohttp-3.10.11.tar.gz", hash = "sha256:9dc2b8f3dcab2e39e0fa309c8da50c3b55e6f34ab25f1a71d3288f24924d33a7"}, ] [package.dependencies] @@ -479,90 +479,90 @@ colorama = {version = "*", markers = "platform_system == \"Windows\""} [[package]] name = "clickhouse-connect" -version = "0.8.6" +version = "0.8.7" description = "ClickHouse Database Core Driver for Python, Pandas, and Superset" optional = false python-versions = "~=3.8" files = [ - {file = "clickhouse-connect-0.8.6.tar.gz", hash = "sha256:70d683dade55bba2730e3ac2deb47f5501145be4cc8ee48ac595e73842a3d136"}, - {file = "clickhouse_connect-0.8.6-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:2e86f30182175018248b544bc8da5b1dac7928400ed9eb30f47b40aa6306c933"}, - {file = "clickhouse_connect-0.8.6-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:1a8ad85945966c61e0b6fc036edb9861fe8e5e1efb8fd35dd1d7799103a85a96"}, - {file = "clickhouse_connect-0.8.6-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f876f087f3e1ebc0df53a8be911042160176666df7d9afb0e741a48a92434181"}, - {file = "clickhouse_connect-0.8.6-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b94cf18e7cb294d694325f14abc64b6f36c7ee42a99897ddd4f603df57d5f20d"}, - {file = "clickhouse_connect-0.8.6-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b88f79007a0c257184c5091bdc38dff83ea5d3b9db982b544e8a8c7b770f7880"}, - {file = "clickhouse_connect-0.8.6-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:2121d9031d500af616f4ac2ef73b9434c7305c252a694b88565497ce98b29802"}, - {file = "clickhouse_connect-0.8.6-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:d9a01a3cdcc71c557ac1dc4a225bd0d97591706d13899e4399fa643b04541eea"}, - {file = "clickhouse_connect-0.8.6-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:0842a86f814c085a072e38ffdf7974342ab63f4b23fce916a51ecc16c5d9e463"}, - {file = "clickhouse_connect-0.8.6-cp310-cp310-win32.whl", hash = "sha256:d50cc0689247c740f0f015971413a866b245f9b36a42e2f54ade04810e1e5658"}, - {file = "clickhouse_connect-0.8.6-cp310-cp310-win_amd64.whl", hash = "sha256:da0fcfcabb7881e29125fec604f4141666da483f9b450d8ca5ff3f97d6ae0d05"}, - {file = "clickhouse_connect-0.8.6-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:0e3975c7edddf43b197c2a71c9fd25c8fac4f21b27628c2ea897cf8b70715529"}, - {file = "clickhouse_connect-0.8.6-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:e4fa6827e114eed5aed60671242aa97d52f0db5cc05f0f96dddbb306295d5121"}, - {file = "clickhouse_connect-0.8.6-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d70a02ffe8902b99076dc4cc82733e6131e1c598b8104a7b4288ab019f55b909"}, - {file = "clickhouse_connect-0.8.6-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:002875e14fd9a95df6d25e0c9600feb707aca0895b92b5b597aa1b3bff91baf5"}, - {file = "clickhouse_connect-0.8.6-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:bed4259cf266d11e76c8ff7dc2e6fb1543127a15a5c35d492114fa87eeb63410"}, - {file = "clickhouse_connect-0.8.6-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:c9e25f313aaecae2d341ee1a008d0f35224bae23959d54e115f3a43e94b60790"}, - {file = "clickhouse_connect-0.8.6-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:ab9cbde8fc9bc06ae2af4c441fc10f966f151e59f44a212358e080796b219982"}, - {file = "clickhouse_connect-0.8.6-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:650f1e7f71e4eeba6ad1eaa2d927f8979a08281f026397228dbdcba28a945a2c"}, - {file = "clickhouse_connect-0.8.6-cp311-cp311-win32.whl", hash = "sha256:45e5ab11af64705c901477d4f5442c8907b5ec060463f5f1155b9a76c663204c"}, - {file = "clickhouse_connect-0.8.6-cp311-cp311-win_amd64.whl", hash = "sha256:c645f755bb740e22828e8c52cef2bea19f74e313adccc0b1209ffe6f60bb9481"}, - {file = "clickhouse_connect-0.8.6-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:5beaaec13fccf0b47679a1e5cd37bc81bdd83d45b807710489f82fbf5a13e369"}, - {file = "clickhouse_connect-0.8.6-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:12c30e9b126079366c49b6182b8ac97aa6e179a8db41a058fb47db7bba5e79b9"}, - {file = "clickhouse_connect-0.8.6-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4ac5f4b6674018552c853fa52a678f66e0e6fa9f65e44d09ad804c63333b3883"}, - {file = "clickhouse_connect-0.8.6-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e602c6f2f4f4cccd5b4bf1fb82fb567b340196ecba37849f2664609b697137a8"}, - {file = "clickhouse_connect-0.8.6-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b6e2ef9839c1839da7970f95abaed9406c84a93b2372169eadb2547c71c8e8fc"}, - {file = "clickhouse_connect-0.8.6-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:716338fd37c292450c1386bd07e2fbf34cd9eabfa50ac63c5debc764cebf20dd"}, - {file = "clickhouse_connect-0.8.6-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:84bd74fdcfac6be3f4ada42b7d7bed16eb9195c0431c8d8400e8d427d4838e75"}, - {file = "clickhouse_connect-0.8.6-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:2364602bd5c0b667313c5e9d4eac3d6a6d594f5d179b13927f1ce42022cca589"}, - {file = "clickhouse_connect-0.8.6-cp312-cp312-win32.whl", hash = "sha256:775098feb6675af21467690dbfa88ddb6c91a84a15d03f668279e59596c4e660"}, - {file = "clickhouse_connect-0.8.6-cp312-cp312-win_amd64.whl", hash = "sha256:e70b0edafedbcdfe84f3610e3bf25eb789c12f13143ff34f022e62fbc40cd356"}, - {file = "clickhouse_connect-0.8.6-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:1ffd3c939157ba163d7b85847662cf12ff81dd2c75f91521391ba7519e7c235a"}, - {file = "clickhouse_connect-0.8.6-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:0014e92d98277ebf0c102eb10ea70837126873b4668c3b92a087ef4c58c94b7e"}, - {file = "clickhouse_connect-0.8.6-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:58532c4bd4bbc21b7ccd5a341fbdb87677e9c1714c0ddfb2afc899c5b9ff963d"}, - {file = "clickhouse_connect-0.8.6-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1cec2bbd12abcbc26a8c8a27e6e04cc66d6519bc6fdd9cdd41d7dd4217a6cb21"}, - {file = "clickhouse_connect-0.8.6-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:89e7e86dbb6b5edc667ef50eeca1dca995409150563b58944f080411a82a4b0a"}, - {file = "clickhouse_connect-0.8.6-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:c3f0ecec40b768875804c5be67f949e62fe3cdae0373c8f3bf23ace3863c0f8f"}, - {file = "clickhouse_connect-0.8.6-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:baec3b45f2fe7f3743e8b38d54487e6f950c07353951c5cf0cb0bbb2c884b456"}, - {file = "clickhouse_connect-0.8.6-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:02920166879f78887f396a6307cda70d9bb1cccd43b706ffaef9d34a9df6bb72"}, - {file = "clickhouse_connect-0.8.6-cp313-cp313-win32.whl", hash = "sha256:52698acf3c777a1d5852f7d1a0e05b838f5a9bcc60f345c16d9080a45a6d5d01"}, - {file = "clickhouse_connect-0.8.6-cp313-cp313-win_amd64.whl", hash = "sha256:1030b47adfce8e624075936639958508f9fee5f397e2a904163e5aa54123ceb4"}, - {file = "clickhouse_connect-0.8.6-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:50917e3111b6acc86ca0eb978755c3b5615ba42fb9bea7f38b8f89c1bbf94110"}, - {file = "clickhouse_connect-0.8.6-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:60335a96fef90864b7a4bca34189e6374b2b770fbf8af9de9420f2d7d6fe9752"}, - {file = "clickhouse_connect-0.8.6-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f6a374ed7902152847ffb378225e926d58e66a3b830a7920c6573f4e102f8175"}, - {file = "clickhouse_connect-0.8.6-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:24213690e74f4a1ab15f5cfb5a78e32264302e11515051b18dc04d945aed8e00"}, - {file = "clickhouse_connect-0.8.6-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ccbc8096590233e965fa6a8d199cbdc57588470825b00fd808c36a6e6b991d8a"}, - {file = "clickhouse_connect-0.8.6-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:19d9dbf09d2cc4da2fea8c93aff050124334c6c476066480e989e8987d83f57c"}, - {file = "clickhouse_connect-0.8.6-cp38-cp38-musllinux_1_2_i686.whl", hash = "sha256:44a6260823e60b1b31432c347f4ec35e4770cbd7a3a78fe08840137d35a3775d"}, - {file = "clickhouse_connect-0.8.6-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:3e029d4442b7c2ea7e7b33925c13304b71734b3e9985a8f61b8a9a5691b9d2ad"}, - {file = "clickhouse_connect-0.8.6-cp38-cp38-win32.whl", hash = "sha256:c530e0a9a7d7c394d4310e9b439b31ccd7a32d83cf32b76ede6f185c2bf2902a"}, - {file = "clickhouse_connect-0.8.6-cp38-cp38-win_amd64.whl", hash = "sha256:3926f6770c03d199ef69f440b2094e6e08c462f439226ce03af06272c4da54e3"}, - {file = "clickhouse_connect-0.8.6-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:b3a8935450d17d631c19d00dd408a6490255ff334dabf1fa22f6ae46ef090416"}, - {file = "clickhouse_connect-0.8.6-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:f97619fe00462ef82e48e7db636ec406fe7eac5799c8320d03dbcce6e3c08ba5"}, - {file = "clickhouse_connect-0.8.6-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bc41d90b693f8706c8c46c477c979b67018fdbe4d0b9e7685c45d35a13f4261d"}, - {file = "clickhouse_connect-0.8.6-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7830af11e620de5e4774898a0326df12cb6078c04b8570a06e1089ac6fd5f436"}, - {file = "clickhouse_connect-0.8.6-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:dba5168c30d22241c056b1566dd10006a978de87af635e35988aea5caa704cff"}, - {file = "clickhouse_connect-0.8.6-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:421e303151735e487cca21aef463dc90a73f2d2ae95694cb7916918ab0738b82"}, - {file = "clickhouse_connect-0.8.6-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:e87efd50e84bdbd8805e4b21ffb27f0c9eb317c69bd4af9709cd88e8244edd24"}, - {file = "clickhouse_connect-0.8.6-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:f7b857ee092241de5a86f3ff337cd02219daa40aae1d211ae6fb08c5bae62f83"}, - {file = "clickhouse_connect-0.8.6-cp39-cp39-win32.whl", hash = "sha256:66601d411b9708c9b1afac42f8e9bfad6a53a37f4a3ae93880e7dd90ea52208e"}, - {file = "clickhouse_connect-0.8.6-cp39-cp39-win_amd64.whl", hash = "sha256:a678e88cf2989e2eb0304c1146e797178747bb38f9886514f8c9b736839c1e8c"}, - {file = "clickhouse_connect-0.8.6-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:ac34c2722082f52aaba8b5b9ed21b2e9d019e7d4495324ddf688f54b0556d9b2"}, - {file = "clickhouse_connect-0.8.6-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:55068ec6adbc1ed6492e24d058086cda924038fe1c912aad9137205e89ce43fb"}, - {file = "clickhouse_connect-0.8.6-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6c77ebe047703bb058601d0644e8fd3069fae10e7d2a21c5cb6ff02baca7254c"}, - {file = "clickhouse_connect-0.8.6-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bbe53fb989c74ef9bad3d9f8430c9e4bfc81490c81884d94f37a71ea6956a7b5"}, - {file = "clickhouse_connect-0.8.6-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:80e90b7852972b55e4928f0aabc203aba1cd9e6c45305f1ea5e5aafa0c13c38a"}, - {file = "clickhouse_connect-0.8.6-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:6e356a5aa9883c5f19357058644549ebf5005213cd347505f8e3f5f9e8040a01"}, - {file = "clickhouse_connect-0.8.6-pp38-pypy38_pp73-macosx_10_9_x86_64.whl", hash = "sha256:73af8a7924d59a585e1cbdf3691603a218d01df889dff5823df03ec009700b15"}, - {file = "clickhouse_connect-0.8.6-pp38-pypy38_pp73-macosx_11_0_arm64.whl", hash = "sha256:7213b27a44d7ccd2efde8c27ecda147aa7f89a23cf1732e1f083d1f43ddb7e50"}, - {file = "clickhouse_connect-0.8.6-pp38-pypy38_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9d4c6ebd0ee08a552307bfc7590430014a19fabac8e6f08658643351bd68c4e3"}, - {file = "clickhouse_connect-0.8.6-pp38-pypy38_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a732d2de064046f4a935ef5b6e7feb9bb7eca7d8a654031389404f46db174445"}, - {file = "clickhouse_connect-0.8.6-pp38-pypy38_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:8bf1d39e19f56a09948a9e1a479acd41bdcfbb822a69afc77c093c69d25cb4c3"}, - {file = "clickhouse_connect-0.8.6-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:388a2583f167fd7efd68be5e464e1e956f9a02a88f4d77e60f33a427e9d4230e"}, - {file = "clickhouse_connect-0.8.6-pp39-pypy39_pp73-macosx_10_15_x86_64.whl", hash = "sha256:72e8d4b0c809ecde6a49ed9d58f662fe27d0171de51bb899ed0b3d13460695f0"}, - {file = "clickhouse_connect-0.8.6-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:f485f743e12202b3b9711031b59707b1476ad42b0fba9ceed200bdd196e68532"}, - {file = "clickhouse_connect-0.8.6-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:56b43c6c9983975fd65ccd4ca57f5c85f0a24ccd93004bbfa7b7a59dfda1920f"}, - {file = "clickhouse_connect-0.8.6-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4c1cb78659da6f67c7ee6d0ab82e97e31830af928e48dbe5864dbd507d90a9d3"}, - {file = "clickhouse_connect-0.8.6-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:14647de8e44cc03df6b9a87df6189101a792dba7ea5c96602310df15f00700c7"}, - {file = "clickhouse_connect-0.8.6-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:1877b7233b5aba21232d8d89a73db76de465e30583ef6a85e8238173b793b337"}, + {file = "clickhouse-connect-0.8.7.tar.gz", hash = "sha256:cf03661207b8f341be43be12815656578ccf3e6857cd3cb46deb80e5fae7609b"}, + {file = "clickhouse_connect-0.8.7-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:3ba5ecd3daaa35cab9c09a7062ae715bf22862104dc43a59a78bf521dde353d2"}, + {file = "clickhouse_connect-0.8.7-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:5a0d4fa68d6f3ddbdcb97c1bff134e1cd27a0604d558a28e1384d2355da38d5d"}, + {file = "clickhouse_connect-0.8.7-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8d5bc4db99601e56ba612d72e9b5a936d927e7e3a823bc51927bdb1a462b8ea7"}, + {file = "clickhouse_connect-0.8.7-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:48490b34aa3e4b8352827c1c765a055b4d9413da19e2fa18108842c5c78d7e1b"}, + {file = "clickhouse_connect-0.8.7-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d368b303994ab748dd971bc4ab6c198576656e31f415084b1ca6f6ac94013b96"}, + {file = "clickhouse_connect-0.8.7-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:ffbe61d26575ea17b3dab1dfc6145eda25a101b8b9c71d7ec257f4db06fab5ed"}, + {file = "clickhouse_connect-0.8.7-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:69cb71587c599dabff7c14ebe4d3c76f79d235c856a3a165249b2039d7001060"}, + {file = "clickhouse_connect-0.8.7-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:bf59e6b31fc2a1af4c6bd7d987b51f25de1a26bd5abc56af8b38b2a9875cc482"}, + {file = "clickhouse_connect-0.8.7-cp310-cp310-win32.whl", hash = "sha256:7fea5272cac250107740d66f6bbeed1b432683e43a420684719745d1951eeb0e"}, + {file = "clickhouse_connect-0.8.7-cp310-cp310-win_amd64.whl", hash = "sha256:e21eb04eae0ba877a6175399dd95d45aab9c9f599678b4b72272dbc24bd1d9de"}, + {file = "clickhouse_connect-0.8.7-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:b6605f3c5f52341a230d040e10c154f578a0f61518f3350fc0a241559239ea64"}, + {file = "clickhouse_connect-0.8.7-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:c60436cd79b6dfba9bbebccc1942890a5ec4d9a10e4edeb9ac10a0c2f3b3682b"}, + {file = "clickhouse_connect-0.8.7-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:58beb74fb04eee6ae436087fbfbb0373d2b795b8e7198b3ec5701e0fc7e4c43e"}, + {file = "clickhouse_connect-0.8.7-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9b1a31664b78d7b22e871f25bdfdb526a7829e3c244c51d024706f5d01a481e6"}, + {file = "clickhouse_connect-0.8.7-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ff94dfa15fd24659c0512d4c5e9f3a644cbd5314b5d078c04089a110bb3747f1"}, + {file = "clickhouse_connect-0.8.7-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:6a14b67b5914c748c6b9014533c32d1c33057c29795b46095249f8709fa5d5a3"}, + {file = "clickhouse_connect-0.8.7-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:62a82669ab8445c8511ad982688ef2ef86d917251e37a9a01cbb4db26d699659"}, + {file = "clickhouse_connect-0.8.7-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:993510cf045fbffb38faec36cd020a73393ca4c94bf88d601828e13bd8da18cb"}, + {file = "clickhouse_connect-0.8.7-cp311-cp311-win32.whl", hash = "sha256:3241221f43df1016ea44c79cea4364d42f03610feb84d32ea112cdc6b03fd265"}, + {file = "clickhouse_connect-0.8.7-cp311-cp311-win_amd64.whl", hash = "sha256:fffc372acc700db8fc02bdc09fe77a392686e86ebb809ae7e979a889c00eda2d"}, + {file = "clickhouse_connect-0.8.7-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:a4491b8f05ad115d5b57204bb2c961a5e051f4f8352b7e005f3eeba308d3d999"}, + {file = "clickhouse_connect-0.8.7-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:48d1a11df119254cf6509695640592dbcd0e03750e560e0954e0e47eca035218"}, + {file = "clickhouse_connect-0.8.7-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3d89864d5f4a84a1fd311f18eb9fa58bf1681c789fe830853b6085135a96475a"}, + {file = "clickhouse_connect-0.8.7-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c5731344b1b6063820cbfa543d9571fa42871a985df6afead6cef428457ce6aa"}, + {file = "clickhouse_connect-0.8.7-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a602d3ab3e8b86b375e441492d66afc90aff0baf1683d6573109d8df8bda71cc"}, + {file = "clickhouse_connect-0.8.7-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:5989229d86a1f068d29a1ac573400c76a8f8c18f5f5bb458684bdbf468a06d7b"}, + {file = "clickhouse_connect-0.8.7-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:d65c416eaa00e280af0a0f54f40aaef7942f596ccd81009ba429d53dff4d00ee"}, + {file = "clickhouse_connect-0.8.7-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:6955c8fb02e8909fd1c5815b4cf212ff89fba0c3fcf0f5aa5f801c1437520977"}, + {file = "clickhouse_connect-0.8.7-cp312-cp312-win32.whl", hash = "sha256:ac92ec6da2a0e92b834a8c85be399d91e77f9fd45912447f97ca25c597512f59"}, + {file = "clickhouse_connect-0.8.7-cp312-cp312-win_amd64.whl", hash = "sha256:f2aa3490a3a9ba1ff7f46247bc34de2519c524ca44952e6e1ab529b8463b1d5e"}, + {file = "clickhouse_connect-0.8.7-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:f2c1d46ceaaa085ab095af56bcd027383722b1f3eb874bdfc6e3be7c60a6d4f4"}, + {file = "clickhouse_connect-0.8.7-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:a00d935768f627e410dcb0fbc60b3762a6e669f9e95a2e02c331b8e3114d4269"}, + {file = "clickhouse_connect-0.8.7-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bf95c36458651688380a5b5d5b705b85fef790f3f0d9078ed685801101f68ee6"}, + {file = "clickhouse_connect-0.8.7-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9419bad9434d3d0048f8e21bfcfe6e625593476b389b37950c7b14b0b758099a"}, + {file = "clickhouse_connect-0.8.7-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b748e53d29697bac0dd4c31fdfbce6ab1ae0089b6d83221fbe620f3a5a9dbcd7"}, + {file = "clickhouse_connect-0.8.7-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:3102e788a9bdf0109712a285abbac0dcf2ee1d9b55f2d36cb8892f81b1c7727f"}, + {file = "clickhouse_connect-0.8.7-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:f1ec5b0db0a54ebb60bc8838a0cb6f392cce374f4ef9d4bb9fb602ce6c458b80"}, + {file = "clickhouse_connect-0.8.7-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:44b047c95526a85a3a54300ff0a42fb46a169df5203b426d05afd0f21b3f519a"}, + {file = "clickhouse_connect-0.8.7-cp313-cp313-win32.whl", hash = "sha256:9ffd782d96d01b28f868f91ea5973db0bdca50d339fb49e6b68bd0253c4b8d9b"}, + {file = "clickhouse_connect-0.8.7-cp313-cp313-win_amd64.whl", hash = "sha256:6d568f163b58adac7389c7c3f05421200286f5bf9dcc418e54c89ceed52ec28a"}, + {file = "clickhouse_connect-0.8.7-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:d78f6d2bebbf06edbe5046e21f0f3df9566561382542b91d3e6cb3c1c3f8f2b4"}, + {file = "clickhouse_connect-0.8.7-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:ca6edf31fe8c26cb69f0eda61b3541deda0a94bb62db0cad5bccb707f47a88ec"}, + {file = "clickhouse_connect-0.8.7-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d8f56e35451c2c794db088718fb9cb1a346e5098740d0060fa827ec7f704b2c3"}, + {file = "clickhouse_connect-0.8.7-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ea304ab5765d3e38d8bc6921f023ba62998777f196b9acce3edd7a19acb55dda"}, + {file = "clickhouse_connect-0.8.7-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c308df38bf5757e7a65e79e1709e0b604fab81405c5c530757be559174626bbd"}, + {file = "clickhouse_connect-0.8.7-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:153c63b1f44464c76eb7d6686ea0ac8e00ab6c0e59a86a9c2868d4709687a2ce"}, + {file = "clickhouse_connect-0.8.7-cp38-cp38-musllinux_1_2_i686.whl", hash = "sha256:f6c37b7bc4bc63ee3696e314bc4125b7a88c6809248f106468354cf2d70e53e1"}, + {file = "clickhouse_connect-0.8.7-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:094dfea5295484ebcc3e75184fadbae210e6e4ae149aedfb0d08795f0fe690b8"}, + {file = "clickhouse_connect-0.8.7-cp38-cp38-win32.whl", hash = "sha256:97ab4ca32995a771030a549007b76987fd16efbb36a70859b3074db67afae879"}, + {file = "clickhouse_connect-0.8.7-cp38-cp38-win_amd64.whl", hash = "sha256:48f8154fe92833035e07c528a4890da8cf07a958f401cf453031e280bc1ea567"}, + {file = "clickhouse_connect-0.8.7-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:05c5bb1c724643796bcf168d140ae297ec667278c7a7c69c8cbb9170c2667bd0"}, + {file = "clickhouse_connect-0.8.7-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:628b731e2e0effbeb886086150dfef6917adb567f2a7ded109cd5bf3d68ad446"}, + {file = "clickhouse_connect-0.8.7-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e8cf442fbe94ff89f4d480abba23680470f28a9b4f19278682a1f6d8d35c7b6a"}, + {file = "clickhouse_connect-0.8.7-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f6e5c5f02d797d67d4e87349af07a272f4688fa1da63d6c178cad18586dba341"}, + {file = "clickhouse_connect-0.8.7-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6a523a638a246672b2d2ee28611dea5e66445dec71c0d47d0cd7683ff275fb18"}, + {file = "clickhouse_connect-0.8.7-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:bf489bc50dccea8677fc42ad06b48121cb3a585fbf6d40758a044d4fff6ddaa1"}, + {file = "clickhouse_connect-0.8.7-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:6b944cb8f31029ee70af0784714e0667960ac35d743a1a1d9e8245c862d848b4"}, + {file = "clickhouse_connect-0.8.7-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:9481619ce4ad6f27650a50fff77bc00fb78d4ba2cee13aba4552cc597557d4f3"}, + {file = "clickhouse_connect-0.8.7-cp39-cp39-win32.whl", hash = "sha256:26194ad96dc2b7954f6243a5ea9183a99051f145557ffee316ba86493218d6fc"}, + {file = "clickhouse_connect-0.8.7-cp39-cp39-win_amd64.whl", hash = "sha256:ea9b05e4b997e0f04e1eeaacd2c47156a5d2ff0f5e1d1d7eb6aeb300ef6f7c7a"}, + {file = "clickhouse_connect-0.8.7-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:7ca5b872807ec291cf1028406f01fe3e53395c552a2dffac894dc556126096dc"}, + {file = "clickhouse_connect-0.8.7-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:3ffa3d36319000c29e8feb9715d4cf7f8ad0f794f2ebaafe68b5858ecb21b7ae"}, + {file = "clickhouse_connect-0.8.7-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0bcd9694b48f83a065f6496f610c94ee91779844f3bcb5e83c6271660c19c0ba"}, + {file = "clickhouse_connect-0.8.7-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:00e7bae9de8850f9f8315d00b15e52817be3be254bdcd68dcd735acb5277adf8"}, + {file = "clickhouse_connect-0.8.7-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6b091c77c1bb0f899aa734a8b6df0e4ec472c8e75b0d929c971067d76f7d5d3f"}, + {file = "clickhouse_connect-0.8.7-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:523a9cac266380e8b1815b3066eccb7f51f817592b1bc471f1a104bbd134b42d"}, + {file = "clickhouse_connect-0.8.7-pp38-pypy38_pp73-macosx_10_9_x86_64.whl", hash = "sha256:a5fcce7cae97b2dc4b51a518995b2021be20868adcdda62ff15d73c83d7c125f"}, + {file = "clickhouse_connect-0.8.7-pp38-pypy38_pp73-macosx_11_0_arm64.whl", hash = "sha256:6d56f2906a41f6f074343739307dd312f122529fde514cde4d24dc7852939b7e"}, + {file = "clickhouse_connect-0.8.7-pp38-pypy38_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:191ee43256512321bc5890188018a57bc6e616e2197af6345ce70fb76f4d36ed"}, + {file = "clickhouse_connect-0.8.7-pp38-pypy38_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dafdc551e6b9f3b2631d371f6bde396ef3ee746dcdfddb136884f40439c7a09b"}, + {file = "clickhouse_connect-0.8.7-pp38-pypy38_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:eddadf5c78ae9053878c1e3b9cfc156ac33c2f6e23e7cc7df57ffbdceeb09f15"}, + {file = "clickhouse_connect-0.8.7-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:b27a1366256eb2b00570c8d1a2c9cb2e4068df03029d0427da7cfca6a90d8c98"}, + {file = "clickhouse_connect-0.8.7-pp39-pypy39_pp73-macosx_10_15_x86_64.whl", hash = "sha256:72857f00d9748ff9b2bae082568718abb7c48f634ca4760a7b86e320a0a36cfd"}, + {file = "clickhouse_connect-0.8.7-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:fc1c2efe29d2218e4cd904516a4349f5e03348ad53ecfa7ac3504ed7edc13b3e"}, + {file = "clickhouse_connect-0.8.7-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:05c99b35a3866282ddb5c43640d880243d02bd9002fb08880fc0f1a7f0620fdb"}, + {file = "clickhouse_connect-0.8.7-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b2190c97e1cb3b6c24961b24b5a32d95990f46126e50949f239e8a7930367fcb"}, + {file = "clickhouse_connect-0.8.7-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:bf6a09a86ba4ee90baa5a8addb89085112e4339cc9a8a485936902ea159c572f"}, + {file = "clickhouse_connect-0.8.7-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:d44a00a259905b70421a764b5b209b02a6656184338bb918e8b0b618bc158609"}, ] [package.dependencies] @@ -852,13 +852,13 @@ idna = ">=2.0.0" [[package]] name = "fastapi" -version = "0.115.4" +version = "0.115.5" description = "FastAPI framework, high performance, easy to learn, fast to code, ready for production" optional = false python-versions = ">=3.8" files = [ - {file = "fastapi-0.115.4-py3-none-any.whl", hash = "sha256:0b504a063ffb3cf96a5e27dc1bc32c80ca743a2528574f9cdc77daa2d31b4742"}, - {file = "fastapi-0.115.4.tar.gz", hash = "sha256:db653475586b091cb8b2fec2ac54a680ac6a158e07406e1abae31679e8826349"}, + {file = "fastapi-0.115.5-py3-none-any.whl", hash = "sha256:596b95adbe1474da47049e802f9a65ab2ffa9c2b07e7efee70eb8a66c9f2f796"}, + {file = "fastapi-0.115.5.tar.gz", hash = "sha256:0e7a4d0dc0d01c68df21887cce0945e72d3c48b9f4f79dfe7a7d53aa08fbb289"}, ] [package.dependencies] @@ -1039,13 +1039,13 @@ grpcio-gcp = ["grpcio-gcp (>=0.2.2,<1.0.dev0)"] [[package]] name = "google-auth" -version = "2.35.0" +version = "2.36.0" description = "Google Authentication Library" optional = false python-versions = ">=3.7" files = [ - {file = "google_auth-2.35.0-py2.py3-none-any.whl", hash = "sha256:25df55f327ef021de8be50bad0dfd4a916ad0de96da86cd05661c9297723ad3f"}, - {file = "google_auth-2.35.0.tar.gz", hash = "sha256:f4c64ed4e01e8e8b646ef34c018f8bf3338df0c8e37d8b3bba40e7f574a3278a"}, + {file = "google_auth-2.36.0-py2.py3-none-any.whl", hash = "sha256:51a15d47028b66fd36e5c64a82d2d57480075bccc7da37cde257fc94177a61fb"}, + {file = "google_auth-2.36.0.tar.gz", hash = "sha256:545e9618f2df0bcbb7dcbc45a546485b1212624716975a1ea5ae8149ce769ab1"}, ] [package.dependencies] @@ -2027,69 +2027,86 @@ signedtoken = ["cryptography (>=3.0.0)", "pyjwt (>=2.0.0,<3)"] [[package]] name = "orjson" -version = "3.10.11" +version = "3.10.12" description = "Fast, correct Python JSON library supporting dataclasses, datetimes, and numpy" optional = false python-versions = ">=3.8" files = [ - {file = "orjson-3.10.11-cp310-cp310-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:6dade64687f2bd7c090281652fe18f1151292d567a9302b34c2dbb92a3872f1f"}, - {file = "orjson-3.10.11-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:82f07c550a6ccd2b9290849b22316a609023ed851a87ea888c0456485a7d196a"}, - {file = "orjson-3.10.11-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:bd9a187742d3ead9df2e49240234d728c67c356516cf4db018833a86f20ec18c"}, - {file = "orjson-3.10.11-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:77b0fed6f209d76c1c39f032a70df2d7acf24b1812ca3e6078fd04e8972685a3"}, - {file = "orjson-3.10.11-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:63fc9d5fe1d4e8868f6aae547a7b8ba0a2e592929245fff61d633f4caccdcdd6"}, - {file = "orjson-3.10.11-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:65cd3e3bb4fbb4eddc3c1e8dce10dc0b73e808fcb875f9fab40c81903dd9323e"}, - {file = "orjson-3.10.11-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:6f67c570602300c4befbda12d153113b8974a3340fdcf3d6de095ede86c06d92"}, - {file = "orjson-3.10.11-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:1f39728c7f7d766f1f5a769ce4d54b5aaa4c3f92d5b84817053cc9995b977acc"}, - {file = "orjson-3.10.11-cp310-none-win32.whl", hash = "sha256:1789d9db7968d805f3d94aae2c25d04014aae3a2fa65b1443117cd462c6da647"}, - {file = "orjson-3.10.11-cp310-none-win_amd64.whl", hash = "sha256:5576b1e5a53a5ba8f8df81872bb0878a112b3ebb1d392155f00f54dd86c83ff6"}, - {file = "orjson-3.10.11-cp311-cp311-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:1444f9cb7c14055d595de1036f74ecd6ce15f04a715e73f33bb6326c9cef01b6"}, - {file = "orjson-3.10.11-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cdec57fe3b4bdebcc08a946db3365630332dbe575125ff3d80a3272ebd0ddafe"}, - {file = "orjson-3.10.11-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:4eed32f33a0ea6ef36ccc1d37f8d17f28a1d6e8eefae5928f76aff8f1df85e67"}, - {file = "orjson-3.10.11-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:80df27dd8697242b904f4ea54820e2d98d3f51f91e97e358fc13359721233e4b"}, - {file = "orjson-3.10.11-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:705f03cee0cb797256d54de6695ef219e5bc8c8120b6654dd460848d57a9af3d"}, - {file = "orjson-3.10.11-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:03246774131701de8e7059b2e382597da43144a9a7400f178b2a32feafc54bd5"}, - {file = "orjson-3.10.11-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:8b5759063a6c940a69c728ea70d7c33583991c6982915a839c8da5f957e0103a"}, - {file = "orjson-3.10.11-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:677f23e32491520eebb19c99bb34675daf5410c449c13416f7f0d93e2cf5f981"}, - {file = "orjson-3.10.11-cp311-none-win32.whl", hash = "sha256:a11225d7b30468dcb099498296ffac36b4673a8398ca30fdaec1e6c20df6aa55"}, - {file = "orjson-3.10.11-cp311-none-win_amd64.whl", hash = "sha256:df8c677df2f9f385fcc85ab859704045fa88d4668bc9991a527c86e710392bec"}, - {file = "orjson-3.10.11-cp312-cp312-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:360a4e2c0943da7c21505e47cf6bd725588962ff1d739b99b14e2f7f3545ba51"}, - {file = "orjson-3.10.11-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:496e2cb45de21c369079ef2d662670a4892c81573bcc143c4205cae98282ba97"}, - {file = "orjson-3.10.11-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:7dfa8db55c9792d53c5952900c6a919cfa377b4f4534c7a786484a6a4a350c19"}, - {file = "orjson-3.10.11-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:51f3382415747e0dbda9dade6f1e1a01a9d37f630d8c9049a8ed0e385b7a90c0"}, - {file = "orjson-3.10.11-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f35a1b9f50a219f470e0e497ca30b285c9f34948d3c8160d5ad3a755d9299433"}, - {file = "orjson-3.10.11-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e2f3b7c5803138e67028dde33450e054c87e0703afbe730c105f1fcd873496d5"}, - {file = "orjson-3.10.11-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:f91d9eb554310472bd09f5347950b24442600594c2edc1421403d7610a0998fd"}, - {file = "orjson-3.10.11-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:dfbb2d460a855c9744bbc8e36f9c3a997c4b27d842f3d5559ed54326e6911f9b"}, - {file = "orjson-3.10.11-cp312-none-win32.whl", hash = "sha256:d4a62c49c506d4d73f59514986cadebb7e8d186ad510c518f439176cf8d5359d"}, - {file = "orjson-3.10.11-cp312-none-win_amd64.whl", hash = "sha256:f1eec3421a558ff7a9b010a6c7effcfa0ade65327a71bb9b02a1c3b77a247284"}, - {file = "orjson-3.10.11-cp313-cp313-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:c46294faa4e4d0eb73ab68f1a794d2cbf7bab33b1dda2ac2959ffb7c61591899"}, - {file = "orjson-3.10.11-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:52e5834d7d6e58a36846e059d00559cb9ed20410664f3ad156cd2cc239a11230"}, - {file = "orjson-3.10.11-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a2fc947e5350fdce548bfc94f434e8760d5cafa97fb9c495d2fef6757aa02ec0"}, - {file = "orjson-3.10.11-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:0efabbf839388a1dab5b72b5d3baedbd6039ac83f3b55736eb9934ea5494d258"}, - {file = "orjson-3.10.11-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:a3f29634260708c200c4fe148e42b4aae97d7b9fee417fbdd74f8cfc265f15b0"}, - {file = "orjson-3.10.11-cp313-none-win32.whl", hash = "sha256:1a1222ffcee8a09476bbdd5d4f6f33d06d0d6642df2a3d78b7a195ca880d669b"}, - {file = "orjson-3.10.11-cp313-none-win_amd64.whl", hash = "sha256:bc274ac261cc69260913b2d1610760e55d3c0801bb3457ba7b9004420b6b4270"}, - {file = "orjson-3.10.11-cp38-cp38-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:19b3763e8bbf8ad797df6b6b5e0fc7c843ec2e2fc0621398534e0c6400098f87"}, - {file = "orjson-3.10.11-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1be83a13312e5e58d633580c5eb8d0495ae61f180da2722f20562974188af205"}, - {file = "orjson-3.10.11-cp38-cp38-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:afacfd1ab81f46dedd7f6001b6d4e8de23396e4884cd3c3436bd05defb1a6446"}, - {file = "orjson-3.10.11-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:cb4d0bea56bba596723d73f074c420aec3b2e5d7d30698bc56e6048066bd560c"}, - {file = "orjson-3.10.11-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:96ed1de70fcb15d5fed529a656df29f768187628727ee2788344e8a51e1c1350"}, - {file = "orjson-3.10.11-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4bfb30c891b530f3f80e801e3ad82ef150b964e5c38e1fb8482441c69c35c61c"}, - {file = "orjson-3.10.11-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:d496c74fc2b61341e3cefda7eec21b7854c5f672ee350bc55d9a4997a8a95204"}, - {file = "orjson-3.10.11-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:655a493bac606655db9a47fe94d3d84fc7f3ad766d894197c94ccf0c5408e7d3"}, - {file = "orjson-3.10.11-cp38-none-win32.whl", hash = "sha256:b9546b278c9fb5d45380f4809e11b4dd9844ca7aaf1134024503e134ed226161"}, - {file = "orjson-3.10.11-cp38-none-win_amd64.whl", hash = "sha256:b592597fe551d518f42c5a2eb07422eb475aa8cfdc8c51e6da7054b836b26782"}, - {file = "orjson-3.10.11-cp39-cp39-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:c95f2ecafe709b4e5c733b5e2768ac569bed308623c85806c395d9cca00e08af"}, - {file = "orjson-3.10.11-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:80c00d4acded0c51c98754fe8218cb49cb854f0f7eb39ea4641b7f71732d2cb7"}, - {file = "orjson-3.10.11-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:461311b693d3d0a060439aa669c74f3603264d4e7a08faa68c47ae5a863f352d"}, - {file = "orjson-3.10.11-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:52ca832f17d86a78cbab86cdc25f8c13756ebe182b6fc1a97d534051c18a08de"}, - {file = "orjson-3.10.11-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f4c57ea78a753812f528178aa2f1c57da633754c91d2124cb28991dab4c79a54"}, - {file = "orjson-3.10.11-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b7fcfc6f7ca046383fb954ba528587e0f9336828b568282b27579c49f8e16aad"}, - {file = "orjson-3.10.11-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:86b9dd983857970c29e4c71bb3e95ff085c07d3e83e7c46ebe959bac07ebd80b"}, - {file = "orjson-3.10.11-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:4d83f87582d223e54efb2242a79547611ba4ebae3af8bae1e80fa9a0af83bb7f"}, - {file = "orjson-3.10.11-cp39-none-win32.whl", hash = "sha256:9fd0ad1c129bc9beb1154c2655f177620b5beaf9a11e0d10bac63ef3fce96950"}, - {file = "orjson-3.10.11-cp39-none-win_amd64.whl", hash = "sha256:10f416b2a017c8bd17f325fb9dee1fb5cdd7a54e814284896b7c3f2763faa017"}, - {file = "orjson-3.10.11.tar.gz", hash = "sha256:e35b6d730de6384d5b2dab5fd23f0d76fae8bbc8c353c2f78210aa5fa4beb3ef"}, + {file = "orjson-3.10.12-cp310-cp310-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:ece01a7ec71d9940cc654c482907a6b65df27251255097629d0dea781f255c6d"}, + {file = "orjson-3.10.12-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c34ec9aebc04f11f4b978dd6caf697a2df2dd9b47d35aa4cc606cabcb9df69d7"}, + {file = "orjson-3.10.12-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:fd6ec8658da3480939c79b9e9e27e0db31dffcd4ba69c334e98c9976ac29140e"}, + {file = "orjson-3.10.12-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f17e6baf4cf01534c9de8a16c0c611f3d94925d1701bf5f4aff17003677d8ced"}, + {file = "orjson-3.10.12-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:6402ebb74a14ef96f94a868569f5dccf70d791de49feb73180eb3c6fda2ade56"}, + {file = "orjson-3.10.12-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0000758ae7c7853e0a4a6063f534c61656ebff644391e1f81698c1b2d2fc8cd2"}, + {file = "orjson-3.10.12-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:888442dcee99fd1e5bd37a4abb94930915ca6af4db50e23e746cdf4d1e63db13"}, + {file = "orjson-3.10.12-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:c1f7a3ce79246aa0e92f5458d86c54f257fb5dfdc14a192651ba7ec2c00f8a05"}, + {file = "orjson-3.10.12-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:802a3935f45605c66fb4a586488a38af63cb37aaad1c1d94c982c40dcc452e85"}, + {file = "orjson-3.10.12-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:1da1ef0113a2be19bb6c557fb0ec2d79c92ebd2fed4cfb1b26bab93f021fb885"}, + {file = "orjson-3.10.12-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:7a3273e99f367f137d5b3fecb5e9f45bcdbfac2a8b2f32fbc72129bbd48789c2"}, + {file = "orjson-3.10.12-cp310-none-win32.whl", hash = "sha256:475661bf249fd7907d9b0a2a2421b4e684355a77ceef85b8352439a9163418c3"}, + {file = "orjson-3.10.12-cp310-none-win_amd64.whl", hash = "sha256:87251dc1fb2b9e5ab91ce65d8f4caf21910d99ba8fb24b49fd0c118b2362d509"}, + {file = "orjson-3.10.12-cp311-cp311-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:a734c62efa42e7df94926d70fe7d37621c783dea9f707a98cdea796964d4cf74"}, + {file = "orjson-3.10.12-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:750f8b27259d3409eda8350c2919a58b0cfcd2054ddc1bd317a643afc646ef23"}, + {file = "orjson-3.10.12-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:bb52c22bfffe2857e7aa13b4622afd0dd9d16ea7cc65fd2bf318d3223b1b6252"}, + {file = "orjson-3.10.12-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:440d9a337ac8c199ff8251e100c62e9488924c92852362cd27af0e67308c16ef"}, + {file = "orjson-3.10.12-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a9e15c06491c69997dfa067369baab3bf094ecb74be9912bdc4339972323f252"}, + {file = "orjson-3.10.12-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:362d204ad4b0b8724cf370d0cd917bb2dc913c394030da748a3bb632445ce7c4"}, + {file = "orjson-3.10.12-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:2b57cbb4031153db37b41622eac67329c7810e5f480fda4cfd30542186f006ae"}, + {file = "orjson-3.10.12-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:165c89b53ef03ce0d7c59ca5c82fa65fe13ddf52eeb22e859e58c237d4e33b9b"}, + {file = "orjson-3.10.12-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:5dee91b8dfd54557c1a1596eb90bcd47dbcd26b0baaed919e6861f076583e9da"}, + {file = "orjson-3.10.12-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:77a4e1cfb72de6f905bdff061172adfb3caf7a4578ebf481d8f0530879476c07"}, + {file = "orjson-3.10.12-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:038d42c7bc0606443459b8fe2d1f121db474c49067d8d14c6a075bbea8bf14dd"}, + {file = "orjson-3.10.12-cp311-none-win32.whl", hash = "sha256:03b553c02ab39bed249bedd4abe37b2118324d1674e639b33fab3d1dafdf4d79"}, + {file = "orjson-3.10.12-cp311-none-win_amd64.whl", hash = "sha256:8b8713b9e46a45b2af6b96f559bfb13b1e02006f4242c156cbadef27800a55a8"}, + {file = "orjson-3.10.12-cp312-cp312-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:53206d72eb656ca5ac7d3a7141e83c5bbd3ac30d5eccfe019409177a57634b0d"}, + {file = "orjson-3.10.12-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ac8010afc2150d417ebda810e8df08dd3f544e0dd2acab5370cfa6bcc0662f8f"}, + {file = "orjson-3.10.12-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:ed459b46012ae950dd2e17150e838ab08215421487371fa79d0eced8d1461d70"}, + {file = "orjson-3.10.12-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8dcb9673f108a93c1b52bfc51b0af422c2d08d4fc710ce9c839faad25020bb69"}, + {file = "orjson-3.10.12-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:22a51ae77680c5c4652ebc63a83d5255ac7d65582891d9424b566fb3b5375ee9"}, + {file = "orjson-3.10.12-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:910fdf2ac0637b9a77d1aad65f803bac414f0b06f720073438a7bd8906298192"}, + {file = "orjson-3.10.12-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:24ce85f7100160936bc2116c09d1a8492639418633119a2224114f67f63a4559"}, + {file = "orjson-3.10.12-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:8a76ba5fc8dd9c913640292df27bff80a685bed3a3c990d59aa6ce24c352f8fc"}, + {file = "orjson-3.10.12-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:ff70ef093895fd53f4055ca75f93f047e088d1430888ca1229393a7c0521100f"}, + {file = "orjson-3.10.12-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:f4244b7018b5753ecd10a6d324ec1f347da130c953a9c88432c7fbc8875d13be"}, + {file = "orjson-3.10.12-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:16135ccca03445f37921fa4b585cff9a58aa8d81ebcb27622e69bfadd220b32c"}, + {file = "orjson-3.10.12-cp312-none-win32.whl", hash = "sha256:2d879c81172d583e34153d524fcba5d4adafbab8349a7b9f16ae511c2cee8708"}, + {file = "orjson-3.10.12-cp312-none-win_amd64.whl", hash = "sha256:fc23f691fa0f5c140576b8c365bc942d577d861a9ee1142e4db468e4e17094fb"}, + {file = "orjson-3.10.12-cp313-cp313-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:47962841b2a8aa9a258b377f5188db31ba49af47d4003a32f55d6f8b19006543"}, + {file = "orjson-3.10.12-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6334730e2532e77b6054e87ca84f3072bee308a45a452ea0bffbbbc40a67e296"}, + {file = "orjson-3.10.12-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:accfe93f42713c899fdac2747e8d0d5c659592df2792888c6c5f829472e4f85e"}, + {file = "orjson-3.10.12-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:a7974c490c014c48810d1dede6c754c3cc46598da758c25ca3b4001ac45b703f"}, + {file = "orjson-3.10.12-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:3f250ce7727b0b2682f834a3facff88e310f52f07a5dcfd852d99637d386e79e"}, + {file = "orjson-3.10.12-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:f31422ff9486ae484f10ffc51b5ab2a60359e92d0716fcce1b3593d7bb8a9af6"}, + {file = "orjson-3.10.12-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:5f29c5d282bb2d577c2a6bbde88d8fdcc4919c593f806aac50133f01b733846e"}, + {file = "orjson-3.10.12-cp313-none-win32.whl", hash = "sha256:f45653775f38f63dc0e6cd4f14323984c3149c05d6007b58cb154dd080ddc0dc"}, + {file = "orjson-3.10.12-cp313-none-win_amd64.whl", hash = "sha256:229994d0c376d5bdc91d92b3c9e6be2f1fbabd4cc1b59daae1443a46ee5e9825"}, + {file = "orjson-3.10.12-cp38-cp38-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:7d69af5b54617a5fac5c8e5ed0859eb798e2ce8913262eb522590239db6c6763"}, + {file = "orjson-3.10.12-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7ed119ea7d2953365724a7059231a44830eb6bbb0cfead33fcbc562f5fd8f935"}, + {file = "orjson-3.10.12-cp38-cp38-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:9c5fc1238ef197e7cad5c91415f524aaa51e004be5a9b35a1b8a84ade196f73f"}, + {file = "orjson-3.10.12-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:43509843990439b05f848539d6f6198d4ac86ff01dd024b2f9a795c0daeeab60"}, + {file = "orjson-3.10.12-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f72e27a62041cfb37a3de512247ece9f240a561e6c8662276beaf4d53d406db4"}, + {file = "orjson-3.10.12-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9a904f9572092bb6742ab7c16c623f0cdccbad9eeb2d14d4aa06284867bddd31"}, + {file = "orjson-3.10.12-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:855c0833999ed5dc62f64552db26f9be767434917d8348d77bacaab84f787d7b"}, + {file = "orjson-3.10.12-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:897830244e2320f6184699f598df7fb9db9f5087d6f3f03666ae89d607e4f8ed"}, + {file = "orjson-3.10.12-cp38-cp38-musllinux_1_2_armv7l.whl", hash = "sha256:0b32652eaa4a7539f6f04abc6243619c56f8530c53bf9b023e1269df5f7816dd"}, + {file = "orjson-3.10.12-cp38-cp38-musllinux_1_2_i686.whl", hash = "sha256:36b4aa31e0f6a1aeeb6f8377769ca5d125db000f05c20e54163aef1d3fe8e833"}, + {file = "orjson-3.10.12-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:5535163054d6cbf2796f93e4f0dbc800f61914c0e3c4ed8499cf6ece22b4a3da"}, + {file = "orjson-3.10.12-cp38-none-win32.whl", hash = "sha256:90a5551f6f5a5fa07010bf3d0b4ca2de21adafbbc0af6cb700b63cd767266cb9"}, + {file = "orjson-3.10.12-cp38-none-win_amd64.whl", hash = "sha256:703a2fb35a06cdd45adf5d733cf613cbc0cb3ae57643472b16bc22d325b5fb6c"}, + {file = "orjson-3.10.12-cp39-cp39-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:f29de3ef71a42a5822765def1febfb36e0859d33abf5c2ad240acad5c6a1b78d"}, + {file = "orjson-3.10.12-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:de365a42acc65d74953f05e4772c974dad6c51cfc13c3240899f534d611be967"}, + {file = "orjson-3.10.12-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:91a5a0158648a67ff0004cb0df5df7dcc55bfc9ca154d9c01597a23ad54c8d0c"}, + {file = "orjson-3.10.12-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c47ce6b8d90fe9646a25b6fb52284a14ff215c9595914af63a5933a49972ce36"}, + {file = "orjson-3.10.12-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:0eee4c2c5bfb5c1b47a5db80d2ac7aaa7e938956ae88089f098aff2c0f35d5d8"}, + {file = "orjson-3.10.12-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:35d3081bbe8b86587eb5c98a73b97f13d8f9fea685cf91a579beddacc0d10566"}, + {file = "orjson-3.10.12-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:73c23a6e90383884068bc2dba83d5222c9fcc3b99a0ed2411d38150734236755"}, + {file = "orjson-3.10.12-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:5472be7dc3269b4b52acba1433dac239215366f89dc1d8d0e64029abac4e714e"}, + {file = "orjson-3.10.12-cp39-cp39-musllinux_1_2_armv7l.whl", hash = "sha256:7319cda750fca96ae5973efb31b17d97a5c5225ae0bc79bf5bf84df9e1ec2ab6"}, + {file = "orjson-3.10.12-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:74d5ca5a255bf20b8def6a2b96b1e18ad37b4a122d59b154c458ee9494377f80"}, + {file = "orjson-3.10.12-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:ff31d22ecc5fb85ef62c7d4afe8301d10c558d00dd24274d4bbe464380d3cd69"}, + {file = "orjson-3.10.12-cp39-none-win32.whl", hash = "sha256:c22c3ea6fba91d84fcb4cda30e64aff548fcf0c44c876e681f47d61d24b12e6b"}, + {file = "orjson-3.10.12-cp39-none-win_amd64.whl", hash = "sha256:be604f60d45ace6b0b33dd990a66b4526f1a7a186ac411c942674625456ca548"}, + {file = "orjson-3.10.12.tar.gz", hash = "sha256:0a78bbda3aea0f9f079057ee1ee8a1ecf790d4f1af88dd67493c6b8ee52506ff"}, ] [[package]] @@ -2507,19 +2524,19 @@ files = [ [[package]] name = "pydantic" -version = "2.9.2" +version = "2.10.1" description = "Data validation using Python type hints" optional = false python-versions = ">=3.8" files = [ - {file = "pydantic-2.9.2-py3-none-any.whl", hash = "sha256:f048cec7b26778210e28a0459867920654d48e5e62db0958433636cde4254f12"}, - {file = "pydantic-2.9.2.tar.gz", hash = "sha256:d155cef71265d1e9807ed1c32b4c8deec042a44a50a4188b25ac67ecd81a9c0f"}, + {file = "pydantic-2.10.1-py3-none-any.whl", hash = "sha256:a8d20db84de64cf4a7d59e899c2caf0fe9d660c7cfc482528e7020d7dd189a7e"}, + {file = "pydantic-2.10.1.tar.gz", hash = "sha256:a4daca2dc0aa429555e0656d6bf94873a7dc5f54ee42b1f5873d666fb3f35560"}, ] [package.dependencies] annotated-types = ">=0.6.0" -pydantic-core = "2.23.4" -typing-extensions = {version = ">=4.6.1", markers = "python_version < \"3.13\""} +pydantic-core = "2.27.1" +typing-extensions = ">=4.12.2" [package.extras] email = ["email-validator (>=2.0.0)"] @@ -2527,100 +2544,111 @@ timezone = ["tzdata"] [[package]] name = "pydantic-core" -version = "2.23.4" +version = "2.27.1" description = "Core functionality for Pydantic validation and serialization" optional = false python-versions = ">=3.8" files = [ - {file = "pydantic_core-2.23.4-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:b10bd51f823d891193d4717448fab065733958bdb6a6b351967bd349d48d5c9b"}, - {file = "pydantic_core-2.23.4-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:4fc714bdbfb534f94034efaa6eadd74e5b93c8fa6315565a222f7b6f42ca1166"}, - {file = "pydantic_core-2.23.4-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:63e46b3169866bd62849936de036f901a9356e36376079b05efa83caeaa02ceb"}, - {file = "pydantic_core-2.23.4-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:ed1a53de42fbe34853ba90513cea21673481cd81ed1be739f7f2efb931b24916"}, - {file = "pydantic_core-2.23.4-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:cfdd16ab5e59fc31b5e906d1a3f666571abc367598e3e02c83403acabc092e07"}, - {file = "pydantic_core-2.23.4-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:255a8ef062cbf6674450e668482456abac99a5583bbafb73f9ad469540a3a232"}, - {file = "pydantic_core-2.23.4-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4a7cd62e831afe623fbb7aabbb4fe583212115b3ef38a9f6b71869ba644624a2"}, - {file = "pydantic_core-2.23.4-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:f09e2ff1f17c2b51f2bc76d1cc33da96298f0a036a137f5440ab3ec5360b624f"}, - {file = "pydantic_core-2.23.4-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:e38e63e6f3d1cec5a27e0afe90a085af8b6806ee208b33030e65b6516353f1a3"}, - {file = "pydantic_core-2.23.4-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:0dbd8dbed2085ed23b5c04afa29d8fd2771674223135dc9bc937f3c09284d071"}, - {file = "pydantic_core-2.23.4-cp310-none-win32.whl", hash = "sha256:6531b7ca5f951d663c339002e91aaebda765ec7d61b7d1e3991051906ddde119"}, - {file = "pydantic_core-2.23.4-cp310-none-win_amd64.whl", hash = "sha256:7c9129eb40958b3d4500fa2467e6a83356b3b61bfff1b414c7361d9220f9ae8f"}, - {file = "pydantic_core-2.23.4-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:77733e3892bb0a7fa797826361ce8a9184d25c8dffaec60b7ffe928153680ba8"}, - {file = "pydantic_core-2.23.4-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:1b84d168f6c48fabd1f2027a3d1bdfe62f92cade1fb273a5d68e621da0e44e6d"}, - {file = "pydantic_core-2.23.4-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:df49e7a0861a8c36d089c1ed57d308623d60416dab2647a4a17fe050ba85de0e"}, - {file = "pydantic_core-2.23.4-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:ff02b6d461a6de369f07ec15e465a88895f3223eb75073ffea56b84d9331f607"}, - {file = "pydantic_core-2.23.4-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:996a38a83508c54c78a5f41456b0103c30508fed9abcad0a59b876d7398f25fd"}, - {file = "pydantic_core-2.23.4-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d97683ddee4723ae8c95d1eddac7c192e8c552da0c73a925a89fa8649bf13eea"}, - {file = "pydantic_core-2.23.4-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:216f9b2d7713eb98cb83c80b9c794de1f6b7e3145eef40400c62e86cee5f4e1e"}, - {file = "pydantic_core-2.23.4-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:6f783e0ec4803c787bcea93e13e9932edab72068f68ecffdf86a99fd5918878b"}, - {file = "pydantic_core-2.23.4-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:d0776dea117cf5272382634bd2a5c1b6eb16767c223c6a5317cd3e2a757c61a0"}, - {file = "pydantic_core-2.23.4-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:d5f7a395a8cf1621939692dba2a6b6a830efa6b3cee787d82c7de1ad2930de64"}, - {file = "pydantic_core-2.23.4-cp311-none-win32.whl", hash = "sha256:74b9127ffea03643e998e0c5ad9bd3811d3dac8c676e47db17b0ee7c3c3bf35f"}, - {file = "pydantic_core-2.23.4-cp311-none-win_amd64.whl", hash = "sha256:98d134c954828488b153d88ba1f34e14259284f256180ce659e8d83e9c05eaa3"}, - {file = "pydantic_core-2.23.4-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:f3e0da4ebaef65158d4dfd7d3678aad692f7666877df0002b8a522cdf088f231"}, - {file = "pydantic_core-2.23.4-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:f69a8e0b033b747bb3e36a44e7732f0c99f7edd5cea723d45bc0d6e95377ffee"}, - {file = "pydantic_core-2.23.4-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:723314c1d51722ab28bfcd5240d858512ffd3116449c557a1336cbe3919beb87"}, - {file = "pydantic_core-2.23.4-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:bb2802e667b7051a1bebbfe93684841cc9351004e2badbd6411bf357ab8d5ac8"}, - {file = "pydantic_core-2.23.4-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d18ca8148bebe1b0a382a27a8ee60350091a6ddaf475fa05ef50dc35b5df6327"}, - {file = "pydantic_core-2.23.4-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:33e3d65a85a2a4a0dc3b092b938a4062b1a05f3a9abde65ea93b233bca0e03f2"}, - {file = "pydantic_core-2.23.4-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:128585782e5bfa515c590ccee4b727fb76925dd04a98864182b22e89a4e6ed36"}, - {file = "pydantic_core-2.23.4-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:68665f4c17edcceecc112dfed5dbe6f92261fb9d6054b47d01bf6371a6196126"}, - {file = "pydantic_core-2.23.4-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:20152074317d9bed6b7a95ade3b7d6054845d70584216160860425f4fbd5ee9e"}, - {file = "pydantic_core-2.23.4-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:9261d3ce84fa1d38ed649c3638feefeae23d32ba9182963e465d58d62203bd24"}, - {file = "pydantic_core-2.23.4-cp312-none-win32.whl", hash = "sha256:4ba762ed58e8d68657fc1281e9bb72e1c3e79cc5d464be146e260c541ec12d84"}, - {file = "pydantic_core-2.23.4-cp312-none-win_amd64.whl", hash = "sha256:97df63000f4fea395b2824da80e169731088656d1818a11b95f3b173747b6cd9"}, - {file = "pydantic_core-2.23.4-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:7530e201d10d7d14abce4fb54cfe5b94a0aefc87da539d0346a484ead376c3cc"}, - {file = "pydantic_core-2.23.4-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:df933278128ea1cd77772673c73954e53a1c95a4fdf41eef97c2b779271bd0bd"}, - {file = "pydantic_core-2.23.4-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0cb3da3fd1b6a5d0279a01877713dbda118a2a4fc6f0d821a57da2e464793f05"}, - {file = "pydantic_core-2.23.4-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:42c6dcb030aefb668a2b7009c85b27f90e51e6a3b4d5c9bc4c57631292015b0d"}, - {file = "pydantic_core-2.23.4-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:696dd8d674d6ce621ab9d45b205df149399e4bb9aa34102c970b721554828510"}, - {file = "pydantic_core-2.23.4-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2971bb5ffe72cc0f555c13e19b23c85b654dd2a8f7ab493c262071377bfce9f6"}, - {file = "pydantic_core-2.23.4-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8394d940e5d400d04cad4f75c0598665cbb81aecefaca82ca85bd28264af7f9b"}, - {file = "pydantic_core-2.23.4-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:0dff76e0602ca7d4cdaacc1ac4c005e0ce0dcfe095d5b5259163a80d3a10d327"}, - {file = "pydantic_core-2.23.4-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:7d32706badfe136888bdea71c0def994644e09fff0bfe47441deaed8e96fdbc6"}, - {file = "pydantic_core-2.23.4-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:ed541d70698978a20eb63d8c5d72f2cc6d7079d9d90f6b50bad07826f1320f5f"}, - {file = "pydantic_core-2.23.4-cp313-none-win32.whl", hash = "sha256:3d5639516376dce1940ea36edf408c554475369f5da2abd45d44621cb616f769"}, - {file = "pydantic_core-2.23.4-cp313-none-win_amd64.whl", hash = "sha256:5a1504ad17ba4210df3a045132a7baeeba5a200e930f57512ee02909fc5c4cb5"}, - {file = "pydantic_core-2.23.4-cp38-cp38-macosx_10_12_x86_64.whl", hash = "sha256:d4488a93b071c04dc20f5cecc3631fc78b9789dd72483ba15d423b5b3689b555"}, - {file = "pydantic_core-2.23.4-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:81965a16b675b35e1d09dd14df53f190f9129c0202356ed44ab2728b1c905658"}, - {file = "pydantic_core-2.23.4-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4ffa2ebd4c8530079140dd2d7f794a9d9a73cbb8e9d59ffe24c63436efa8f271"}, - {file = "pydantic_core-2.23.4-cp38-cp38-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:61817945f2fe7d166e75fbfb28004034b48e44878177fc54d81688e7b85a3665"}, - {file = "pydantic_core-2.23.4-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:29d2c342c4bc01b88402d60189f3df065fb0dda3654744d5a165a5288a657368"}, - {file = "pydantic_core-2.23.4-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5e11661ce0fd30a6790e8bcdf263b9ec5988e95e63cf901972107efc49218b13"}, - {file = "pydantic_core-2.23.4-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9d18368b137c6295db49ce7218b1a9ba15c5bc254c96d7c9f9e924a9bc7825ad"}, - {file = "pydantic_core-2.23.4-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:ec4e55f79b1c4ffb2eecd8a0cfba9955a2588497d96851f4c8f99aa4a1d39b12"}, - {file = "pydantic_core-2.23.4-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:374a5e5049eda9e0a44c696c7ade3ff355f06b1fe0bb945ea3cac2bc336478a2"}, - {file = "pydantic_core-2.23.4-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:5c364564d17da23db1106787675fc7af45f2f7b58b4173bfdd105564e132e6fb"}, - {file = "pydantic_core-2.23.4-cp38-none-win32.whl", hash = "sha256:d7a80d21d613eec45e3d41eb22f8f94ddc758a6c4720842dc74c0581f54993d6"}, - {file = "pydantic_core-2.23.4-cp38-none-win_amd64.whl", hash = "sha256:5f5ff8d839f4566a474a969508fe1c5e59c31c80d9e140566f9a37bba7b8d556"}, - {file = "pydantic_core-2.23.4-cp39-cp39-macosx_10_12_x86_64.whl", hash = "sha256:a4fa4fc04dff799089689f4fd502ce7d59de529fc2f40a2c8836886c03e0175a"}, - {file = "pydantic_core-2.23.4-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:0a7df63886be5e270da67e0966cf4afbae86069501d35c8c1b3b6c168f42cb36"}, - {file = "pydantic_core-2.23.4-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:dcedcd19a557e182628afa1d553c3895a9f825b936415d0dbd3cd0bbcfd29b4b"}, - {file = "pydantic_core-2.23.4-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:5f54b118ce5de9ac21c363d9b3caa6c800341e8c47a508787e5868c6b79c9323"}, - {file = "pydantic_core-2.23.4-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:86d2f57d3e1379a9525c5ab067b27dbb8a0642fb5d454e17a9ac434f9ce523e3"}, - {file = "pydantic_core-2.23.4-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:de6d1d1b9e5101508cb37ab0d972357cac5235f5c6533d1071964c47139257df"}, - {file = "pydantic_core-2.23.4-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1278e0d324f6908e872730c9102b0112477a7f7cf88b308e4fc36ce1bdb6d58c"}, - {file = "pydantic_core-2.23.4-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:9a6b5099eeec78827553827f4c6b8615978bb4b6a88e5d9b93eddf8bb6790f55"}, - {file = "pydantic_core-2.23.4-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:e55541f756f9b3ee346b840103f32779c695a19826a4c442b7954550a0972040"}, - {file = "pydantic_core-2.23.4-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:a5c7ba8ffb6d6f8f2ab08743be203654bb1aaa8c9dcb09f82ddd34eadb695605"}, - {file = "pydantic_core-2.23.4-cp39-none-win32.whl", hash = "sha256:37b0fe330e4a58d3c58b24d91d1eb102aeec675a3db4c292ec3928ecd892a9a6"}, - {file = "pydantic_core-2.23.4-cp39-none-win_amd64.whl", hash = "sha256:1498bec4c05c9c787bde9125cfdcc63a41004ff167f495063191b863399b1a29"}, - {file = "pydantic_core-2.23.4-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:f455ee30a9d61d3e1a15abd5068827773d6e4dc513e795f380cdd59932c782d5"}, - {file = "pydantic_core-2.23.4-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:1e90d2e3bd2c3863d48525d297cd143fe541be8bbf6f579504b9712cb6b643ec"}, - {file = "pydantic_core-2.23.4-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2e203fdf807ac7e12ab59ca2bfcabb38c7cf0b33c41efeb00f8e5da1d86af480"}, - {file = "pydantic_core-2.23.4-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e08277a400de01bc72436a0ccd02bdf596631411f592ad985dcee21445bd0068"}, - {file = "pydantic_core-2.23.4-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:f220b0eea5965dec25480b6333c788fb72ce5f9129e8759ef876a1d805d00801"}, - {file = "pydantic_core-2.23.4-pp310-pypy310_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:d06b0c8da4f16d1d1e352134427cb194a0a6e19ad5db9161bf32b2113409e728"}, - {file = "pydantic_core-2.23.4-pp310-pypy310_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:ba1a0996f6c2773bd83e63f18914c1de3c9dd26d55f4ac302a7efe93fb8e7433"}, - {file = "pydantic_core-2.23.4-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:9a5bce9d23aac8f0cf0836ecfc033896aa8443b501c58d0602dbfd5bd5b37753"}, - {file = "pydantic_core-2.23.4-pp39-pypy39_pp73-macosx_10_12_x86_64.whl", hash = "sha256:78ddaaa81421a29574a682b3179d4cf9e6d405a09b99d93ddcf7e5239c742e21"}, - {file = "pydantic_core-2.23.4-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:883a91b5dd7d26492ff2f04f40fbb652de40fcc0afe07e8129e8ae779c2110eb"}, - {file = "pydantic_core-2.23.4-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:88ad334a15b32a791ea935af224b9de1bf99bcd62fabf745d5f3442199d86d59"}, - {file = "pydantic_core-2.23.4-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:233710f069d251feb12a56da21e14cca67994eab08362207785cf8c598e74577"}, - {file = "pydantic_core-2.23.4-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:19442362866a753485ba5e4be408964644dd6a09123d9416c54cd49171f50744"}, - {file = "pydantic_core-2.23.4-pp39-pypy39_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:624e278a7d29b6445e4e813af92af37820fafb6dcc55c012c834f9e26f9aaaef"}, - {file = "pydantic_core-2.23.4-pp39-pypy39_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:f5ef8f42bec47f21d07668a043f077d507e5bf4e668d5c6dfe6aaba89de1a5b8"}, - {file = "pydantic_core-2.23.4-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:aea443fffa9fbe3af1a9ba721a87f926fe548d32cab71d188a6ede77d0ff244e"}, - {file = "pydantic_core-2.23.4.tar.gz", hash = "sha256:2584f7cf844ac4d970fba483a717dbe10c1c1c96a969bf65d61ffe94df1b2863"}, + {file = "pydantic_core-2.27.1-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:71a5e35c75c021aaf400ac048dacc855f000bdfed91614b4a726f7432f1f3d6a"}, + {file = "pydantic_core-2.27.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:f82d068a2d6ecfc6e054726080af69a6764a10015467d7d7b9f66d6ed5afa23b"}, + {file = "pydantic_core-2.27.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:121ceb0e822f79163dd4699e4c54f5ad38b157084d97b34de8b232bcaad70278"}, + {file = "pydantic_core-2.27.1-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:4603137322c18eaf2e06a4495f426aa8d8388940f3c457e7548145011bb68e05"}, + {file = "pydantic_core-2.27.1-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a33cd6ad9017bbeaa9ed78a2e0752c5e250eafb9534f308e7a5f7849b0b1bfb4"}, + {file = "pydantic_core-2.27.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:15cc53a3179ba0fcefe1e3ae50beb2784dede4003ad2dfd24f81bba4b23a454f"}, + {file = "pydantic_core-2.27.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:45d9c5eb9273aa50999ad6adc6be5e0ecea7e09dbd0d31bd0c65a55a2592ca08"}, + {file = "pydantic_core-2.27.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:8bf7b66ce12a2ac52d16f776b31d16d91033150266eb796967a7e4621707e4f6"}, + {file = "pydantic_core-2.27.1-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:655d7dd86f26cb15ce8a431036f66ce0318648f8853d709b4167786ec2fa4807"}, + {file = "pydantic_core-2.27.1-cp310-cp310-musllinux_1_1_armv7l.whl", hash = "sha256:5556470f1a2157031e676f776c2bc20acd34c1990ca5f7e56f1ebf938b9ab57c"}, + {file = "pydantic_core-2.27.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:f69ed81ab24d5a3bd93861c8c4436f54afdf8e8cc421562b0c7504cf3be58206"}, + {file = "pydantic_core-2.27.1-cp310-none-win32.whl", hash = "sha256:f5a823165e6d04ccea61a9f0576f345f8ce40ed533013580e087bd4d7442b52c"}, + {file = "pydantic_core-2.27.1-cp310-none-win_amd64.whl", hash = "sha256:57866a76e0b3823e0b56692d1a0bf722bffb324839bb5b7226a7dbd6c9a40b17"}, + {file = "pydantic_core-2.27.1-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:ac3b20653bdbe160febbea8aa6c079d3df19310d50ac314911ed8cc4eb7f8cb8"}, + {file = "pydantic_core-2.27.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:a5a8e19d7c707c4cadb8c18f5f60c843052ae83c20fa7d44f41594c644a1d330"}, + {file = "pydantic_core-2.27.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7f7059ca8d64fea7f238994c97d91f75965216bcbe5f695bb44f354893f11d52"}, + {file = "pydantic_core-2.27.1-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:bed0f8a0eeea9fb72937ba118f9db0cb7e90773462af7962d382445f3005e5a4"}, + {file = "pydantic_core-2.27.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a3cb37038123447cf0f3ea4c74751f6a9d7afef0eb71aa07bf5f652b5e6a132c"}, + {file = "pydantic_core-2.27.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:84286494f6c5d05243456e04223d5a9417d7f443c3b76065e75001beb26f88de"}, + {file = "pydantic_core-2.27.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:acc07b2cfc5b835444b44a9956846b578d27beeacd4b52e45489e93276241025"}, + {file = "pydantic_core-2.27.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:4fefee876e07a6e9aad7a8c8c9f85b0cdbe7df52b8a9552307b09050f7512c7e"}, + {file = "pydantic_core-2.27.1-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:258c57abf1188926c774a4c94dd29237e77eda19462e5bb901d88adcab6af919"}, + {file = "pydantic_core-2.27.1-cp311-cp311-musllinux_1_1_armv7l.whl", hash = "sha256:35c14ac45fcfdf7167ca76cc80b2001205a8d5d16d80524e13508371fb8cdd9c"}, + {file = "pydantic_core-2.27.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:d1b26e1dff225c31897696cab7d4f0a315d4c0d9e8666dbffdb28216f3b17fdc"}, + {file = "pydantic_core-2.27.1-cp311-none-win32.whl", hash = "sha256:2cdf7d86886bc6982354862204ae3b2f7f96f21a3eb0ba5ca0ac42c7b38598b9"}, + {file = "pydantic_core-2.27.1-cp311-none-win_amd64.whl", hash = "sha256:3af385b0cee8df3746c3f406f38bcbfdc9041b5c2d5ce3e5fc6637256e60bbc5"}, + {file = "pydantic_core-2.27.1-cp311-none-win_arm64.whl", hash = "sha256:81f2ec23ddc1b476ff96563f2e8d723830b06dceae348ce02914a37cb4e74b89"}, + {file = "pydantic_core-2.27.1-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:9cbd94fc661d2bab2bc702cddd2d3370bbdcc4cd0f8f57488a81bcce90c7a54f"}, + {file = "pydantic_core-2.27.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:5f8c4718cd44ec1580e180cb739713ecda2bdee1341084c1467802a417fe0f02"}, + {file = "pydantic_core-2.27.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:15aae984e46de8d376df515f00450d1522077254ef6b7ce189b38ecee7c9677c"}, + {file = "pydantic_core-2.27.1-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:1ba5e3963344ff25fc8c40da90f44b0afca8cfd89d12964feb79ac1411a260ac"}, + {file = "pydantic_core-2.27.1-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:992cea5f4f3b29d6b4f7f1726ed8ee46c8331c6b4eed6db5b40134c6fe1768bb"}, + {file = "pydantic_core-2.27.1-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:0325336f348dbee6550d129b1627cb8f5351a9dc91aad141ffb96d4937bd9529"}, + {file = "pydantic_core-2.27.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7597c07fbd11515f654d6ece3d0e4e5093edc30a436c63142d9a4b8e22f19c35"}, + {file = "pydantic_core-2.27.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:3bbd5d8cc692616d5ef6fbbbd50dbec142c7e6ad9beb66b78a96e9c16729b089"}, + {file = "pydantic_core-2.27.1-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:dc61505e73298a84a2f317255fcc72b710b72980f3a1f670447a21efc88f8381"}, + {file = "pydantic_core-2.27.1-cp312-cp312-musllinux_1_1_armv7l.whl", hash = "sha256:e1f735dc43da318cad19b4173dd1ffce1d84aafd6c9b782b3abc04a0d5a6f5bb"}, + {file = "pydantic_core-2.27.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:f4e5658dbffe8843a0f12366a4c2d1c316dbe09bb4dfbdc9d2d9cd6031de8aae"}, + {file = "pydantic_core-2.27.1-cp312-none-win32.whl", hash = "sha256:672ebbe820bb37988c4d136eca2652ee114992d5d41c7e4858cdd90ea94ffe5c"}, + {file = "pydantic_core-2.27.1-cp312-none-win_amd64.whl", hash = "sha256:66ff044fd0bb1768688aecbe28b6190f6e799349221fb0de0e6f4048eca14c16"}, + {file = "pydantic_core-2.27.1-cp312-none-win_arm64.whl", hash = "sha256:9a3b0793b1bbfd4146304e23d90045f2a9b5fd5823aa682665fbdaf2a6c28f3e"}, + {file = "pydantic_core-2.27.1-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:f216dbce0e60e4d03e0c4353c7023b202d95cbaeff12e5fd2e82ea0a66905073"}, + {file = "pydantic_core-2.27.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:a2e02889071850bbfd36b56fd6bc98945e23670773bc7a76657e90e6b6603c08"}, + {file = "pydantic_core-2.27.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:42b0e23f119b2b456d07ca91b307ae167cc3f6c846a7b169fca5326e32fdc6cf"}, + {file = "pydantic_core-2.27.1-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:764be71193f87d460a03f1f7385a82e226639732214b402f9aa61f0d025f0737"}, + {file = "pydantic_core-2.27.1-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1c00666a3bd2f84920a4e94434f5974d7bbc57e461318d6bb34ce9cdbbc1f6b2"}, + {file = "pydantic_core-2.27.1-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3ccaa88b24eebc0f849ce0a4d09e8a408ec5a94afff395eb69baf868f5183107"}, + {file = "pydantic_core-2.27.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c65af9088ac534313e1963443d0ec360bb2b9cba6c2909478d22c2e363d98a51"}, + {file = "pydantic_core-2.27.1-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:206b5cf6f0c513baffaeae7bd817717140770c74528f3e4c3e1cec7871ddd61a"}, + {file = "pydantic_core-2.27.1-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:062f60e512fc7fff8b8a9d680ff0ddaaef0193dba9fa83e679c0c5f5fbd018bc"}, + {file = "pydantic_core-2.27.1-cp313-cp313-musllinux_1_1_armv7l.whl", hash = "sha256:a0697803ed7d4af5e4c1adf1670af078f8fcab7a86350e969f454daf598c4960"}, + {file = "pydantic_core-2.27.1-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:58ca98a950171f3151c603aeea9303ef6c235f692fe555e883591103da709b23"}, + {file = "pydantic_core-2.27.1-cp313-none-win32.whl", hash = "sha256:8065914ff79f7eab1599bd80406681f0ad08f8e47c880f17b416c9f8f7a26d05"}, + {file = "pydantic_core-2.27.1-cp313-none-win_amd64.whl", hash = "sha256:ba630d5e3db74c79300d9a5bdaaf6200172b107f263c98a0539eeecb857b2337"}, + {file = "pydantic_core-2.27.1-cp313-none-win_arm64.whl", hash = "sha256:45cf8588c066860b623cd11c4ba687f8d7175d5f7ef65f7129df8a394c502de5"}, + {file = "pydantic_core-2.27.1-cp38-cp38-macosx_10_12_x86_64.whl", hash = "sha256:5897bec80a09b4084aee23f9b73a9477a46c3304ad1d2d07acca19723fb1de62"}, + {file = "pydantic_core-2.27.1-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:d0165ab2914379bd56908c02294ed8405c252250668ebcb438a55494c69f44ab"}, + {file = "pydantic_core-2.27.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6b9af86e1d8e4cfc82c2022bfaa6f459381a50b94a29e95dcdda8442d6d83864"}, + {file = "pydantic_core-2.27.1-cp38-cp38-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:5f6c8a66741c5f5447e047ab0ba7a1c61d1e95580d64bce852e3df1f895c4067"}, + {file = "pydantic_core-2.27.1-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9a42d6a8156ff78981f8aa56eb6394114e0dedb217cf8b729f438f643608cbcd"}, + {file = "pydantic_core-2.27.1-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:64c65f40b4cd8b0e049a8edde07e38b476da7e3aaebe63287c899d2cff253fa5"}, + {file = "pydantic_core-2.27.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9fdcf339322a3fae5cbd504edcefddd5a50d9ee00d968696846f089b4432cf78"}, + {file = "pydantic_core-2.27.1-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:bf99c8404f008750c846cb4ac4667b798a9f7de673ff719d705d9b2d6de49c5f"}, + {file = "pydantic_core-2.27.1-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:8f1edcea27918d748c7e5e4d917297b2a0ab80cad10f86631e488b7cddf76a36"}, + {file = "pydantic_core-2.27.1-cp38-cp38-musllinux_1_1_armv7l.whl", hash = "sha256:159cac0a3d096f79ab6a44d77a961917219707e2a130739c64d4dd46281f5c2a"}, + {file = "pydantic_core-2.27.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:029d9757eb621cc6e1848fa0b0310310de7301057f623985698ed7ebb014391b"}, + {file = "pydantic_core-2.27.1-cp38-none-win32.whl", hash = "sha256:a28af0695a45f7060e6f9b7092558a928a28553366519f64083c63a44f70e618"}, + {file = "pydantic_core-2.27.1-cp38-none-win_amd64.whl", hash = "sha256:2d4567c850905d5eaaed2f7a404e61012a51caf288292e016360aa2b96ff38d4"}, + {file = "pydantic_core-2.27.1-cp39-cp39-macosx_10_12_x86_64.whl", hash = "sha256:e9386266798d64eeb19dd3677051f5705bf873e98e15897ddb7d76f477131967"}, + {file = "pydantic_core-2.27.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:4228b5b646caa73f119b1ae756216b59cc6e2267201c27d3912b592c5e323b60"}, + {file = "pydantic_core-2.27.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0b3dfe500de26c52abe0477dde16192ac39c98f05bf2d80e76102d394bd13854"}, + {file = "pydantic_core-2.27.1-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:aee66be87825cdf72ac64cb03ad4c15ffef4143dbf5c113f64a5ff4f81477bf9"}, + {file = "pydantic_core-2.27.1-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3b748c44bb9f53031c8cbc99a8a061bc181c1000c60a30f55393b6e9c45cc5bd"}, + {file = "pydantic_core-2.27.1-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5ca038c7f6a0afd0b2448941b6ef9d5e1949e999f9e5517692eb6da58e9d44be"}, + {file = "pydantic_core-2.27.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6e0bd57539da59a3e4671b90a502da9a28c72322a4f17866ba3ac63a82c4498e"}, + {file = "pydantic_core-2.27.1-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:ac6c2c45c847bbf8f91930d88716a0fb924b51e0c6dad329b793d670ec5db792"}, + {file = "pydantic_core-2.27.1-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:b94d4ba43739bbe8b0ce4262bcc3b7b9f31459ad120fb595627eaeb7f9b9ca01"}, + {file = "pydantic_core-2.27.1-cp39-cp39-musllinux_1_1_armv7l.whl", hash = "sha256:00e6424f4b26fe82d44577b4c842d7df97c20be6439e8e685d0d715feceb9fb9"}, + {file = "pydantic_core-2.27.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:38de0a70160dd97540335b7ad3a74571b24f1dc3ed33f815f0880682e6880131"}, + {file = "pydantic_core-2.27.1-cp39-none-win32.whl", hash = "sha256:7ccebf51efc61634f6c2344da73e366c75e735960b5654b63d7e6f69a5885fa3"}, + {file = "pydantic_core-2.27.1-cp39-none-win_amd64.whl", hash = "sha256:a57847b090d7892f123726202b7daa20df6694cbd583b67a592e856bff603d6c"}, + {file = "pydantic_core-2.27.1-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:3fa80ac2bd5856580e242dbc202db873c60a01b20309c8319b5c5986fbe53ce6"}, + {file = "pydantic_core-2.27.1-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:d950caa237bb1954f1b8c9227b5065ba6875ac9771bb8ec790d956a699b78676"}, + {file = "pydantic_core-2.27.1-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0e4216e64d203e39c62df627aa882f02a2438d18a5f21d7f721621f7a5d3611d"}, + {file = "pydantic_core-2.27.1-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:02a3d637bd387c41d46b002f0e49c52642281edacd2740e5a42f7017feea3f2c"}, + {file = "pydantic_core-2.27.1-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:161c27ccce13b6b0c8689418da3885d3220ed2eae2ea5e9b2f7f3d48f1d52c27"}, + {file = "pydantic_core-2.27.1-pp310-pypy310_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:19910754e4cc9c63bc1c7f6d73aa1cfee82f42007e407c0f413695c2f7ed777f"}, + {file = "pydantic_core-2.27.1-pp310-pypy310_pp73-musllinux_1_1_armv7l.whl", hash = "sha256:e173486019cc283dc9778315fa29a363579372fe67045e971e89b6365cc035ed"}, + {file = "pydantic_core-2.27.1-pp310-pypy310_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:af52d26579b308921b73b956153066481f064875140ccd1dfd4e77db89dbb12f"}, + {file = "pydantic_core-2.27.1-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:981fb88516bd1ae8b0cbbd2034678a39dedc98752f264ac9bc5839d3923fa04c"}, + {file = "pydantic_core-2.27.1-pp39-pypy39_pp73-macosx_10_12_x86_64.whl", hash = "sha256:5fde892e6c697ce3e30c61b239330fc5d569a71fefd4eb6512fc6caec9dd9e2f"}, + {file = "pydantic_core-2.27.1-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:816f5aa087094099fff7edabb5e01cc370eb21aa1a1d44fe2d2aefdfb5599b31"}, + {file = "pydantic_core-2.27.1-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9c10c309e18e443ddb108f0ef64e8729363adbfd92d6d57beec680f6261556f3"}, + {file = "pydantic_core-2.27.1-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:98476c98b02c8e9b2eec76ac4156fd006628b1b2d0ef27e548ffa978393fd154"}, + {file = "pydantic_core-2.27.1-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:c3027001c28434e7ca5a6e1e527487051136aa81803ac812be51802150d880dd"}, + {file = "pydantic_core-2.27.1-pp39-pypy39_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:7699b1df36a48169cdebda7ab5a2bac265204003f153b4bd17276153d997670a"}, + {file = "pydantic_core-2.27.1-pp39-pypy39_pp73-musllinux_1_1_armv7l.whl", hash = "sha256:1c39b07d90be6b48968ddc8c19e7585052088fd7ec8d568bb31ff64c70ae3c97"}, + {file = "pydantic_core-2.27.1-pp39-pypy39_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:46ccfe3032b3915586e469d4972973f893c0a2bb65669194a5bdea9bacc088c2"}, + {file = "pydantic_core-2.27.1-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:62ba45e21cf6571d7f716d903b5b7b6d2617e2d5d67c0923dc47b9d41369f840"}, + {file = "pydantic_core-2.27.1.tar.gz", hash = "sha256:62a763352879b84aa31058fc931884055fd75089cccbd9d58bb6afd01141b235"}, ] [package.dependencies] @@ -3077,29 +3105,29 @@ pyasn1 = ">=0.1.3" [[package]] name = "ruff" -version = "0.7.2" +version = "0.8.0" description = "An extremely fast Python linter and code formatter, written in Rust." optional = false python-versions = ">=3.7" files = [ - {file = "ruff-0.7.2-py3-none-linux_armv6l.whl", hash = "sha256:b73f873b5f52092e63ed540adefc3c36f1f803790ecf2590e1df8bf0a9f72cb8"}, - {file = "ruff-0.7.2-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:5b813ef26db1015953daf476202585512afd6a6862a02cde63f3bafb53d0b2d4"}, - {file = "ruff-0.7.2-py3-none-macosx_11_0_arm64.whl", hash = "sha256:853277dbd9675810c6826dad7a428d52a11760744508340e66bf46f8be9701d9"}, - {file = "ruff-0.7.2-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:21aae53ab1490a52bf4e3bf520c10ce120987b047c494cacf4edad0ba0888da2"}, - {file = "ruff-0.7.2-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:ccc7e0fc6e0cb3168443eeadb6445285abaae75142ee22b2b72c27d790ab60ba"}, - {file = "ruff-0.7.2-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:fd77877a4e43b3a98e5ef4715ba3862105e299af0c48942cc6d51ba3d97dc859"}, - {file = "ruff-0.7.2-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:e00163fb897d35523c70d71a46fbaa43bf7bf9af0f4534c53ea5b96b2e03397b"}, - {file = "ruff-0.7.2-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f3c54b538633482dc342e9b634d91168fe8cc56b30a4b4f99287f4e339103e88"}, - {file = "ruff-0.7.2-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7b792468e9804a204be221b14257566669d1db5c00d6bb335996e5cd7004ba80"}, - {file = "ruff-0.7.2-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dba53ed84ac19ae4bfb4ea4bf0172550a2285fa27fbb13e3746f04c80f7fa088"}, - {file = "ruff-0.7.2-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:b19fafe261bf741bca2764c14cbb4ee1819b67adb63ebc2db6401dcd652e3748"}, - {file = "ruff-0.7.2-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:28bd8220f4d8f79d590db9e2f6a0674f75ddbc3847277dd44ac1f8d30684b828"}, - {file = "ruff-0.7.2-py3-none-musllinux_1_2_i686.whl", hash = "sha256:9fd67094e77efbea932e62b5d2483006154794040abb3a5072e659096415ae1e"}, - {file = "ruff-0.7.2-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:576305393998b7bd6c46018f8104ea3a9cb3fa7908c21d8580e3274a3b04b691"}, - {file = "ruff-0.7.2-py3-none-win32.whl", hash = "sha256:fa993cfc9f0ff11187e82de874dfc3611df80852540331bc85c75809c93253a8"}, - {file = "ruff-0.7.2-py3-none-win_amd64.whl", hash = "sha256:dd8800cbe0254e06b8fec585e97554047fb82c894973f7ff18558eee33d1cb88"}, - {file = "ruff-0.7.2-py3-none-win_arm64.whl", hash = "sha256:bb8368cd45bba3f57bb29cbb8d64b4a33f8415d0149d2655c5c8539452ce7760"}, - {file = "ruff-0.7.2.tar.gz", hash = "sha256:2b14e77293380e475b4e3a7a368e14549288ed2931fce259a6f99978669e844f"}, + {file = "ruff-0.8.0-py3-none-linux_armv6l.whl", hash = "sha256:fcb1bf2cc6706adae9d79c8d86478677e3bbd4ced796ccad106fd4776d395fea"}, + {file = "ruff-0.8.0-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:295bb4c02d58ff2ef4378a1870c20af30723013f441c9d1637a008baaf928c8b"}, + {file = "ruff-0.8.0-py3-none-macosx_11_0_arm64.whl", hash = "sha256:7b1f1c76b47c18fa92ee78b60d2d20d7e866c55ee603e7d19c1e991fad933a9a"}, + {file = "ruff-0.8.0-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:eb0d4f250a7711b67ad513fde67e8870109e5ce590a801c3722580fe98c33a99"}, + {file = "ruff-0.8.0-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:0e55cce9aa93c5d0d4e3937e47b169035c7e91c8655b0974e61bb79cf398d49c"}, + {file = "ruff-0.8.0-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3f4cd64916d8e732ce6b87f3f5296a8942d285bbbc161acee7fe561134af64f9"}, + {file = "ruff-0.8.0-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:c5c1466be2a2ebdf7c5450dd5d980cc87c8ba6976fb82582fea18823da6fa362"}, + {file = "ruff-0.8.0-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:2dabfd05b96b7b8f2da00d53c514eea842bff83e41e1cceb08ae1966254a51df"}, + {file = "ruff-0.8.0-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:facebdfe5a5af6b1588a1d26d170635ead6892d0e314477e80256ef4a8470cf3"}, + {file = "ruff-0.8.0-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:87a8e86bae0dbd749c815211ca11e3a7bd559b9710746c559ed63106d382bd9c"}, + {file = "ruff-0.8.0-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:85e654f0ded7befe2d61eeaf3d3b1e4ef3894469cd664ffa85006c7720f1e4a2"}, + {file = "ruff-0.8.0-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:83a55679c4cb449fa527b8497cadf54f076603cc36779b2170b24f704171ce70"}, + {file = "ruff-0.8.0-py3-none-musllinux_1_2_i686.whl", hash = "sha256:812e2052121634cf13cd6fddf0c1871d0ead1aad40a1a258753c04c18bb71bbd"}, + {file = "ruff-0.8.0-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:780d5d8523c04202184405e60c98d7595bdb498c3c6abba3b6d4cdf2ca2af426"}, + {file = "ruff-0.8.0-py3-none-win32.whl", hash = "sha256:5fdb6efecc3eb60bba5819679466471fd7d13c53487df7248d6e27146e985468"}, + {file = "ruff-0.8.0-py3-none-win_amd64.whl", hash = "sha256:582891c57b96228d146725975fbb942e1f30a0c4ba19722e692ca3eb25cc9b4f"}, + {file = "ruff-0.8.0-py3-none-win_arm64.whl", hash = "sha256:ba93e6294e9a737cd726b74b09a6972e36bb511f9a102f1d9a7e1ce94dd206a6"}, + {file = "ruff-0.8.0.tar.gz", hash = "sha256:a7ccfe6331bf8c8dad715753e157457faf7351c2b69f62f32c165c2dbcbacd44"}, ] [[package]] @@ -4118,4 +4146,4 @@ cffi = ["cffi (>=1.11)"] [metadata] lock-version = "2.0" python-versions = ">=3.11,<3.12" -content-hash = "cb9a45bbb19fc063c751e9fb3cd47dfedfa9de890b3e8cc41497ff5a4fc08603" +content-hash = "3d79487fb66b6bcee3b45f90db93e2ea2747b25888972f3dcbf2e3f0a69052b1" diff --git a/ibis-server/pyproject.toml b/ibis-server/pyproject.toml index 37277efb0..594d5a77a 100644 --- a/ibis-server/pyproject.toml +++ b/ibis-server/pyproject.toml @@ -1,6 +1,6 @@ [tool.poetry] name = "wren-engine" -version = "0.11.2" +version = "0.12.2" description = "" authors = ["Canner "] readme = "README.md" @@ -8,8 +8,8 @@ packages = [{ include = "app" }] [tool.poetry.dependencies] python = ">=3.11,<3.12" -fastapi = { version = "0.115.4", extras = ["standard"] } -pydantic = "2.9.2" +fastapi = { version = "0.115.5", extras = ["standard"] } +pydantic = "2.10.1" ibis-framework = { version = "9.5.0", extras = [ "bigquery", "clickhouse", @@ -19,10 +19,10 @@ ibis-framework = { version = "9.5.0", extras = [ "snowflake", "trino", ] } -google-auth = "2.35.0" +google-auth = "2.36.0" httpx = "0.27.2" python-dotenv = "1.0.1" -orjson = "3.10.11" +orjson = "3.10.12" pandas = "2.2.3" sqlglot = ">=23.4,<25.21" loguru = "0.7.2" @@ -40,10 +40,10 @@ testcontainers = { version = "4.8.2", extras = [ ] } sqlalchemy = "2.0.36" pre-commit = "4.0.1" -ruff = "0.7.2" +ruff = "0.8.0" trino = ">=0.321,<1" psycopg2 = ">=2.8.4,<3" -clickhouse-connect = "0.8.6" +clickhouse-connect = "0.8.7" [tool.pytest.ini_options] addopts = ["--strict-markers"] diff --git a/ibis-server/resources/function_list/bigquery.csv b/ibis-server/resources/function_list/bigquery.csv index d84bebf65..11e33a675 100644 --- a/ibis-server/resources/function_list/bigquery.csv +++ b/ibis-server/resources/function_list/bigquery.csv @@ -45,7 +45,7 @@ scalar,array_concat,ARRAY,"Concatenates multiple arrays into one." scalar,array_to_string,STRING,"Converts an array to a single string." scalar,generate_array,ARRAY,"Generates an array of values in a range." scalar,generate_date_array,ARRAY,"Generates an array of dates in a range." -scalar,parse_timestamp,TIMESTAMP,"Parses a timestamp from a string." +scalar,parse_timestamp,TIMESTAMPTZ,"Parses a timestamp from a string." scalar,string_to_array,ARRAY,"Splits a string into an array of substrings." scalar,safe_divide,FLOAT64,"Divides two numbers, returning NULL if the divisor is zero." scalar,safe_multiply,FLOAT64,"Multiplies two numbers, returning NULL if an overflow occurs." @@ -74,17 +74,18 @@ scalar,substr,STRING,"Returns a substring." scalar,cast,ANY,"Converts a value to a different data type." scalar,safe_cast,ANY,"Converts a value to a different data type, returning NULL on error." scalar,current_date,DATE,"Returns the current date." +scalar,current_datetime,TIMESTAMP,"Returns the current date." scalar,date_add,DATE,"Adds a specified interval to a date." scalar,date_sub,DATE,"Subtracts a specified interval from a date." scalar,date_diff,INT64,"Returns the difference between two dates." scalar,date_trunc,DATE,"Truncates a date to a specified granularity." -scalar,timestamp_add,TIMESTAMP,"Adds a specified interval to a timestamp." -scalar,timestamp_sub,TIMESTAMP,"Subtracts a specified interval from a timestamp." +scalar,timestamp_add,TIMESTAMPTZ,"Adds a specified interval to a timestamp." +scalar,timestamp_sub,TIMESTAMPTZ,"Subtracts a specified interval from a timestamp." scalar,timestamp_diff,INT64,"Returns the difference between two timestamps." -scalar,timestamp_trunc,TIMESTAMP,"Truncates a timestamp to a specified granularity." -scalar,timestamp_micros,TIMESTAMP,"Converts the number of microseconds since 1970-01-01 00:00:00 UTC to a TIMESTAMP.", -scalar,timestamp_millis,TIMESTAMP,"Converts the number of milliseconds since 1970-01-01 00:00:00 UTC to a TIMESTAMP.", -scalar,timestamp_seconds,TIMESTAMP,"Converts the number of seconds since 1970-01-01 00:00:00 UTC to a TIMESTAMP.", +scalar,timestamp_trunc,TIMESTAMPTZ,"Truncates a timestamp to a specified granularity." +scalar,timestamp_micros,TIMESTAMPTZ,"Converts the number of microseconds since 1970-01-01 00:00:00 UTC to a TIMESTAMP." +scalar,timestamp_millis,TIMESTAMPTZ,"Converts the number of milliseconds since 1970-01-01 00:00:00 UTC to a TIMESTAMP." +scalar,timestamp_seconds,TIMESTAMPTZ,"Converts the number of seconds since 1970-01-01 00:00:00 UTC to a TIMESTAMP." scalar,format_date,STRING,"Formats a date according to the specified format string." scalar,format_timestamp,STRING,"Formats a timestamp according to the specified format string." scalar,parse_date,DATE,"Parses a date from a string." diff --git a/ibis-server/tests/conftest.py b/ibis-server/tests/conftest.py index 0c2e17194..6fae2f0bd 100644 --- a/ibis-server/tests/conftest.py +++ b/ibis-server/tests/conftest.py @@ -3,3 +3,6 @@ def file_path(path: str) -> str: return os.path.join(os.path.dirname(__file__), path) + + +DATAFUSION_FUNCTION_COUNT = 270 diff --git a/ibis-server/tests/routers/v2/connector/test_mssql.py b/ibis-server/tests/routers/v2/connector/test_mssql.py index 045a79484..63b0d9e5a 100644 --- a/ibis-server/tests/routers/v2/connector/test_mssql.py +++ b/ibis-server/tests/routers/v2/connector/test_mssql.py @@ -239,6 +239,24 @@ def test_query_with_dry_run_and_invalid_sql( assert response.status_code == 422 assert "Invalid object name 'X'" in response.text + @pytest.mark.skip( + reason="Ibis use non-nvarchar sql to get schema, it will get question mark. Wait https://github.com/ibis-project/ibis/pull/10490" + ) + def test_query_non_ascii_column(manifest_str, mssql: SqlServerContainer): + connection_info = _to_connection_info(mssql) + response = client.post( + url=f"{base_url}/query", + params={"limit": 1}, + json={ + "connectionInfo": connection_info, + "manifestStr": manifest_str, + "sql": 'SELECT 1 AS "калона"', + }, + ) + assert response.status_code == 200 + result = response.json() + assert result["columns"] == ["калона"] + def test_validate_with_unknown_rule(manifest_str, mssql: SqlServerContainer): connection_info = _to_connection_info(mssql) response = client.post( diff --git a/ibis-server/tests/routers/v2/connector/test_postgres.py b/ibis-server/tests/routers/v2/connector/test_postgres.py index 0c3092fa2..a1d72d936 100644 --- a/ibis-server/tests/routers/v2/connector/test_postgres.py +++ b/ibis-server/tests/routers/v2/connector/test_postgres.py @@ -67,6 +67,36 @@ ], "primaryKey": "orderkey", }, + { + "name": "Customer", + "refSql": "SELECT * FROM public.customer", + "columns": [ + { + "name": "custkey", + "type": "integer", + "expression": "c_custkey", + }, + { + "name": "orders", + "type": "Orders", + "relationship": "CustomerOrders", + }, + { + "name": "orders_key", + "type": "varchar", + "isCalculated": True, + "expression": "orders.orderkey", + }, + ], + }, + ], + "relationships": [ + { + "name": "CustomerOrders", + "models": ["Customer", "Orders"], + "joinType": "ONE_TO_MANY", + "condition": "Customer.custkey = Orders.custkey", + } ], } @@ -83,6 +113,9 @@ def postgres(request) -> PostgresContainer: pd.read_parquet(file_path("resource/tpch/data/orders.parquet")).to_sql( "orders", engine, index=False ) + pd.read_parquet(file_path("resource/tpch/data/customer.parquet")).to_sql( + "customer", engine, index=False + ) with engine.begin() as conn: conn.execute(text("COMMENT ON TABLE orders IS 'This is a table comment'")) conn.execute(text("COMMENT ON COLUMN orders.o_comment IS 'This is a comment'")) @@ -148,6 +181,29 @@ def test_query_with_connection_url(manifest_str, postgres: PostgresContainer): assert result["data"][0][0] == 1 assert result["dtypes"] is not None + def test_query_with_dot_all(manifest_str, postgres: PostgresContainer): + connection_info = _to_connection_info(postgres) + test_sqls = [ + 'SELECT "Customer".* FROM "Customer"', + 'SELECT c.* FROM "Customer" AS c', + 'SELECT c.* FROM "Customer" AS c JOIN "Orders" AS o ON c.custkey = o.custkey', + ] + for sql in test_sqls: + response = client.post( + url=f"{base_url}/query", + params={"limit": 1}, + json={ + "connectionInfo": connection_info, + "manifestStr": manifest_str, + "sql": sql, + }, + ) + assert response.status_code == 200 + result = response.json() + assert len(result["columns"]) == 1 # Not include calculated column + assert len(result["data"]) == 1 + assert result["dtypes"] is not None + def test_dry_run_with_connection_url_and_password_with_bracket_should_not_raise_value_error( manifest_str, postgres: PostgresContainer ): diff --git a/ibis-server/tests/routers/v2/connector/test_snowflake.py b/ibis-server/tests/routers/v2/connector/test_snowflake.py index 47d7bcb5a..fdd436919 100644 --- a/ibis-server/tests/routers/v2/connector/test_snowflake.py +++ b/ibis-server/tests/routers/v2/connector/test_snowflake.py @@ -94,7 +94,7 @@ def test_query(manifest_str): 36901, "O", "173665.47", - "1996-01-02 00:00:00.000000", + "1996-01-02", "1_36901", "2024-01-01 23:59:59.000000", "2024-01-01 23:59:59.000000 UTC", @@ -261,14 +261,48 @@ def test_validate_rule_column_is_valid_without_one_parameter(manifest_str): assert response.status_code == 422 assert response.text == "Missing required parameter: `modelName`" - @pytest.mark.skip(reason="Not implemented") def test_metadata_list_tables(): - pass + response = client.post( + url=f"{base_url}/metadata/tables", + json={"connectionInfo": connection_info}, + ) + assert response.status_code == 200 + tables = response.json() + assert len(tables) == 8 + table = next(filter(lambda t: t["name"] == "TPCH_SF1.ORDERS", tables)) + assert table["name"] == "TPCH_SF1.ORDERS" + assert table["primaryKey"] is not None + assert table["description"] == "Orders data as defined by TPC-H" + assert table["properties"] == { + "catalog": "SNOWFLAKE_SAMPLE_DATA", + "schema": "TPCH_SF1", + "table": "ORDERS", + } + assert len(table["columns"]) == 9 + column = next(filter(lambda c: c["name"] == "O_COMMENT", table["columns"])) + assert column == { + "name": "O_COMMENT", + "nestedColumns": None, + "type": "TEXT", + "notNull": True, + "description": None, + "properties": None, + } - @pytest.mark.skip(reason="Not implemented") def test_metadata_list_constraints(): - pass + response = client.post( + url=f"{base_url}/metadata/constraints", + json={"connectionInfo": connection_info}, + ) + assert response.status_code == 200 + + result = response.json() + assert len(result) == 0 - @pytest.mark.skip(reason="Not implemented") def test_metadata_get_version(): - pass + response = client.post( + url=f"{base_url}/metadata/version", + json={"connectionInfo": connection_info}, + ) + assert response.status_code == 200 + assert response.text is not None diff --git a/ibis-server/tests/routers/v2/connector/test_trino.py b/ibis-server/tests/routers/v2/connector/test_trino.py index fc6ea3677..f3a23e94a 100644 --- a/ibis-server/tests/routers/v2/connector/test_trino.py +++ b/ibis-server/tests/routers/v2/connector/test_trino.py @@ -72,7 +72,7 @@ def trino(request) -> TrinoContainer: db = TrinoContainer().start() # To avoid `TrinoQueryError(type=INTERNAL_ERROR, name=GENERIC_INTERNAL_ERROR, message="nodes is empty")` - time.sleep(10) + time.sleep(5) conn = connect( host=db.get_container_host_ip(), @@ -385,7 +385,7 @@ def test_metadata_list_tables(trino: TrinoContainer): assert column == { "name": "comment", "nestedColumns": None, - "type": "UNKNOWN", + "type": "VARCHAR", "notNull": False, "description": "This is a comment", "properties": None, diff --git a/ibis-server/tests/routers/v3/connector/bigquery/conftest.py b/ibis-server/tests/routers/v3/connector/bigquery/conftest.py index b0bfc0770..579ed7022 100644 --- a/ibis-server/tests/routers/v3/connector/bigquery/conftest.py +++ b/ibis-server/tests/routers/v3/connector/bigquery/conftest.py @@ -3,9 +3,13 @@ import pytest +from app.config import get_config +from tests.conftest import file_path + pytestmark = pytest.mark.bigquery base_url = "/v3/connector/bigquery" +function_list_path = file_path("../resources/function_list") def pytest_collection_modifyitems(items): @@ -22,3 +26,11 @@ def connection_info(): "dataset_id": "tpch_tiny", "credentials": os.getenv("TEST_BIG_QUERY_CREDENTIALS_BASE64_JSON"), } + + +@pytest.fixture(autouse=True) +def set_remote_function_list_path(): + config = get_config() + config.set_remote_function_list_path(function_list_path) + yield + config.set_remote_function_list_path(None) diff --git a/ibis-server/tests/routers/v3/connector/bigquery/test_functions.py b/ibis-server/tests/routers/v3/connector/bigquery/test_functions.py index c9a07c578..61bcc1990 100644 --- a/ibis-server/tests/routers/v3/connector/bigquery/test_functions.py +++ b/ibis-server/tests/routers/v3/connector/bigquery/test_functions.py @@ -6,8 +6,8 @@ from app.config import get_config from app.main import app -from tests.conftest import file_path -from tests.routers.v3.connector.bigquery.conftest import base_url +from tests.conftest import DATAFUSION_FUNCTION_COUNT +from tests.routers.v3.connector.bigquery.conftest import base_url, function_list_path manifest = { "catalog": "my_catalog", @@ -26,22 +26,12 @@ ], } -function_list_path = file_path("../resources/function_list") - @pytest.fixture def manifest_str(): return base64.b64encode(orjson.dumps(manifest)).decode("utf-8") -@pytest.fixture(autouse=True) -def set_remote_function_list_path(): - config = get_config() - config.set_remote_function_list_path(function_list_path) - yield - config.set_remote_function_list_path(None) - - with TestClient(app) as client: def test_function_list(): @@ -51,13 +41,13 @@ def test_function_list(): response = client.get(url=f"{base_url}/functions") assert response.status_code == 200 result = response.json() - assert len(result) == 269 + assert len(result) == DATAFUSION_FUNCTION_COUNT config.set_remote_function_list_path(function_list_path) response = client.get(url=f"{base_url}/functions") assert response.status_code == 200 result = response.json() - assert len(result) == 299 + assert len(result) == DATAFUSION_FUNCTION_COUNT + 33 the_func = next(filter(lambda x: x["name"] == "abs", result)) assert the_func == { "name": "abs", @@ -72,7 +62,7 @@ def test_function_list(): response = client.get(url=f"{base_url}/functions") assert response.status_code == 200 result = response.json() - assert len(result) == 269 + assert len(result) == DATAFUSION_FUNCTION_COUNT def test_scalar_function(manifest_str: str, connection_info): response = client.post( diff --git a/ibis-server/tests/routers/v3/connector/bigquery/test_query.py b/ibis-server/tests/routers/v3/connector/bigquery/test_query.py new file mode 100644 index 000000000..141a9bd8a --- /dev/null +++ b/ibis-server/tests/routers/v3/connector/bigquery/test_query.py @@ -0,0 +1,258 @@ +import base64 + +import orjson +import pytest +from fastapi.testclient import TestClient + +from app.main import app +from tests.routers.v3.connector.bigquery.conftest import base_url + +manifest = { + "catalog": "wren", + "schema": "public", + "models": [ + { + "name": "orders", + "tableReference": { + "schema": "tpch_tiny", + "table": "orders", + }, + "columns": [ + {"name": "o_orderkey", "type": "integer"}, + {"name": "o_custkey", "type": "integer"}, + { + "name": "o_orderstatus", + "type": "varchar", + }, + { + "name": "o_totalprice", + "type": "double", + }, + {"name": "o_orderdate", "type": "date"}, + { + "name": "order_cust_key", + "expression": "concat(o_orderkey, '_', o_custkey)", + "type": "varchar", + }, + { + "name": "timestamp", + "expression": "cast('2024-01-01T23:59:59' as timestamp)", + "type": "timestamp", + }, + { + "name": "timestamptz", + "expression": "cast('2024-01-01T23:59:59' as timestamp with time zone)", + "type": "timestamptz", + }, + { + "name": "dst_utc_minus_5", + "expression": "cast('2024-01-15 23:00:00 America/New_York' as timestamp with time zone)", + "type": "timestamptz", + }, + { + "name": "dst_utc_minus_4", + "expression": "cast('2024-07-15 23:00:00 America/New_York' as timestamp with time zone)", + "type": "timestamptz", + }, + ], + "primaryKey": "o_orderkey", + }, + ], +} + + +@pytest.fixture +def manifest_str(): + return base64.b64encode(orjson.dumps(manifest)).decode("utf-8") + + +with TestClient(app) as client: + + def test_query(manifest_str, connection_info): + response = client.post( + url=f"{base_url}/query", + json={ + "connectionInfo": connection_info, + "manifestStr": manifest_str, + "sql": "SELECT * FROM wren.public.orders LIMIT 1", + }, + ) + assert response.status_code == 200 + result = response.json() + assert len(result["columns"]) == len(manifest["models"][0]["columns"]) + assert len(result["data"]) == 1 + assert result["data"][0] == [ + "2024-01-01 23:59:59.000000", + "2024-01-01 23:59:59.000000 UTC", + "2024-01-16 04:00:00.000000 UTC", # utc-5 + "2024-07-16 03:00:00.000000 UTC", # utc-4 + "36485_1202", + 1202, + "1992-06-06", + 36485, + "F", + 356711.63, + ] + assert result["dtypes"] == { + "o_orderkey": "int64", + "o_custkey": "int64", + "o_orderstatus": "object", + "o_totalprice": "float64", + "o_orderdate": "object", + "order_cust_key": "object", + "timestamp": "object", + "timestamptz": "object", + "dst_utc_minus_5": "object", + "dst_utc_minus_4": "object", + } + + def test_query_with_limit(manifest_str, connection_info): + response = client.post( + url=f"{base_url}/query", + params={"limit": 1}, + json={ + "connectionInfo": connection_info, + "manifestStr": manifest_str, + "sql": "SELECT * FROM wren.public.orders", + }, + ) + assert response.status_code == 200 + result = response.json() + assert len(result["data"]) == 1 + + response = client.post( + url=f"{base_url}/query", + params={"limit": 1}, + json={ + "connectionInfo": connection_info, + "manifestStr": manifest_str, + "sql": "SELECT * FROM wren.public.orders LIMIT 10", + }, + ) + assert response.status_code == 200 + result = response.json() + assert len(result["data"]) == 1 + + def test_query_with_invalid_manifest_str(connection_info): + response = client.post( + url=f"{base_url}/query", + json={ + "connectionInfo": connection_info, + "manifestStr": "xxx", + "sql": "SELECT * FROM wren.public.orders LIMIT 1", + }, + ) + assert response.status_code == 422 + assert response.text == "Base64 decode error: Invalid padding" + + def test_query_without_manifest(connection_info): + response = client.post( + url=f"{base_url}/query", + json={ + "connectionInfo": connection_info, + "sql": "SELECT * FROM wren.public.orders LIMIT 1", + }, + ) + assert response.status_code == 422 + result = response.json() + assert result["detail"][0] is not None + assert result["detail"][0]["type"] == "missing" + assert result["detail"][0]["loc"] == ["body", "manifestStr"] + assert result["detail"][0]["msg"] == "Field required" + + def test_query_without_sql(manifest_str, connection_info): + response = client.post( + url=f"{base_url}/query", + json={"connectionInfo": connection_info, "manifestStr": manifest_str}, + ) + assert response.status_code == 422 + result = response.json() + assert result["detail"][0] is not None + assert result["detail"][0]["type"] == "missing" + assert result["detail"][0]["loc"] == ["body", "sql"] + assert result["detail"][0]["msg"] == "Field required" + + def test_query_without_connection_info(manifest_str): + response = client.post( + url=f"{base_url}/query", + json={ + "manifestStr": manifest_str, + "sql": "SELECT * FROM wren.public.orders LIMIT 1", + }, + ) + assert response.status_code == 422 + result = response.json() + assert result["detail"][0] is not None + assert result["detail"][0]["type"] == "missing" + assert result["detail"][0]["loc"] == ["body", "connectionInfo"] + assert result["detail"][0]["msg"] == "Field required" + + def test_query_with_dry_run(manifest_str, connection_info): + response = client.post( + url=f"{base_url}/query", + params={"dryRun": True}, + json={ + "connectionInfo": connection_info, + "manifestStr": manifest_str, + "sql": "SELECT * FROM wren.public.orders LIMIT 1", + }, + ) + assert response.status_code == 204 + + def test_query_with_dry_run_and_invalid_sql(manifest_str, connection_info): + response = client.post( + url=f"{base_url}/query", + params={"dryRun": True}, + json={ + "connectionInfo": connection_info, + "manifestStr": manifest_str, + "sql": "SELECT * FROM X", + }, + ) + assert response.status_code == 422 + assert response.text is not None + + def test_timestamp_func(manifest_str, connection_info): + response = client.post( + url=f"{base_url}/query", + json={ + "connectionInfo": connection_info, + "manifestStr": manifest_str, + "sql": "SELECT timestamp_millis(1000000) as millis, \ + timestamp_micros(1000000) as micros, \ + timestamp_seconds(1000000) as seconds", + }, + ) + assert response.status_code == 200 + result = response.json() + assert len(result["columns"]) == 3 + assert len(result["data"]) == 1 + assert result["data"][0] == [ + "1970-01-01 00:16:40.000000 UTC", + "1970-01-01 00:00:01.000000 UTC", + "1970-01-12 13:46:40.000000 UTC", + ] + assert result["dtypes"] == { + "millis": "object", + "micros": "object", + "seconds": "object", + } + + response = client.post( + url=f"{base_url}/query", + json={ + "connectionInfo": connection_info, + "manifestStr": manifest_str, + "sql": "SELECT timestamp with time zone '2000-01-01 10:00:00' < current_datetime() as compare", + }, + ) + assert response.status_code == 200 + result = response.json() + assert len(result["columns"]) == 1 + assert len(result["data"]) == 1 + assert result["data"][0] == [ + True, + ] + assert result["dtypes"] == { + "compare": "bool", + } diff --git a/ibis-server/tests/routers/v3/connector/canner/test_functions.py b/ibis-server/tests/routers/v3/connector/canner/test_functions.py index a6d88e3a2..dbe642595 100644 --- a/ibis-server/tests/routers/v3/connector/canner/test_functions.py +++ b/ibis-server/tests/routers/v3/connector/canner/test_functions.py @@ -6,7 +6,7 @@ from app.config import get_config from app.main import app -from tests.conftest import file_path +from tests.conftest import DATAFUSION_FUNCTION_COUNT, file_path from tests.routers.v3.connector.canner.conftest import base_url manifest = { @@ -51,13 +51,13 @@ def test_function_list(): response = client.get(url=f"{base_url}/functions") assert response.status_code == 200 result = response.json() - assert len(result) == 269 + assert len(result) == DATAFUSION_FUNCTION_COUNT config.set_remote_function_list_path(function_list_path) response = client.get(url=f"{base_url}/functions") assert response.status_code == 200 result = response.json() - assert len(result) == 299 + assert len(result) == DATAFUSION_FUNCTION_COUNT + 30 the_func = next(filter(lambda x: x["name"] == "abs", result)) assert the_func == { "name": "abs", @@ -72,7 +72,7 @@ def test_function_list(): response = client.get(url=f"{base_url}/functions") assert response.status_code == 200 result = response.json() - assert len(result) == 269 + assert len(result) == DATAFUSION_FUNCTION_COUNT def test_scalar_function(manifest_str: str, connection_info): response = client.post( diff --git a/ibis-server/tests/routers/v3/connector/clickhouse/test_functions.py b/ibis-server/tests/routers/v3/connector/clickhouse/test_functions.py index 453a359ba..95ecdba41 100644 --- a/ibis-server/tests/routers/v3/connector/clickhouse/test_functions.py +++ b/ibis-server/tests/routers/v3/connector/clickhouse/test_functions.py @@ -6,7 +6,7 @@ from app.config import get_config from app.main import app -from tests.conftest import file_path +from tests.conftest import DATAFUSION_FUNCTION_COUNT, file_path from tests.routers.v3.connector.clickhouse.conftest import base_url manifest = { @@ -51,13 +51,13 @@ def test_function_list(): response = client.get(url=f"{base_url}/functions") assert response.status_code == 200 result = response.json() - assert len(result) == 269 + assert len(result) == DATAFUSION_FUNCTION_COUNT config.set_remote_function_list_path(function_list_path) response = client.get(url=f"{base_url}/functions") assert response.status_code == 200 result = response.json() - assert len(result) == 348 + assert len(result) == DATAFUSION_FUNCTION_COUNT + 79 the_func = next(filter(lambda x: x["name"] == "abs", result)) assert the_func == { "name": "abs", @@ -72,7 +72,7 @@ def test_function_list(): response = client.get(url=f"{base_url}/functions") assert response.status_code == 200 result = response.json() - assert len(result) == 269 + assert len(result) == DATAFUSION_FUNCTION_COUNT def test_scalar_function(manifest_str: str, connection_info): response = client.post( diff --git a/ibis-server/tests/routers/v3/connector/mssql/test_functions.py b/ibis-server/tests/routers/v3/connector/mssql/test_functions.py index 4b0f5f4ab..5400334e7 100644 --- a/ibis-server/tests/routers/v3/connector/mssql/test_functions.py +++ b/ibis-server/tests/routers/v3/connector/mssql/test_functions.py @@ -6,7 +6,7 @@ from app.config import get_config from app.main import app -from tests.conftest import file_path +from tests.conftest import DATAFUSION_FUNCTION_COUNT, file_path from tests.routers.v3.connector.mssql.conftest import base_url manifest = { @@ -51,13 +51,13 @@ def test_function_list(): response = client.get(url=f"{base_url}/functions") assert response.status_code == 200 result = response.json() - assert len(result) == 269 + assert len(result) == DATAFUSION_FUNCTION_COUNT config.set_remote_function_list_path(function_list_path) response = client.get(url=f"{base_url}/functions") assert response.status_code == 200 result = response.json() - assert len(result) == 321 + assert len(result) == DATAFUSION_FUNCTION_COUNT + 52 the_func = next(filter(lambda x: x["name"] == "abs", result)) assert the_func == { "name": "abs", @@ -72,7 +72,7 @@ def test_function_list(): response = client.get(url=f"{base_url}/functions") assert response.status_code == 200 result = response.json() - assert len(result) == 269 + assert len(result) == DATAFUSION_FUNCTION_COUNT def test_scalar_function(manifest_str: str, connection_info): response = client.post( diff --git a/ibis-server/tests/routers/v3/connector/mysql/test_functions.py b/ibis-server/tests/routers/v3/connector/mysql/test_functions.py index 1b36be49a..e18afbb80 100644 --- a/ibis-server/tests/routers/v3/connector/mysql/test_functions.py +++ b/ibis-server/tests/routers/v3/connector/mysql/test_functions.py @@ -6,7 +6,7 @@ from app.config import get_config from app.main import app -from tests.conftest import file_path +from tests.conftest import DATAFUSION_FUNCTION_COUNT, file_path from tests.routers.v3.connector.mysql.conftest import base_url manifest = { @@ -51,13 +51,13 @@ def test_function_list(): response = client.get(url=f"{base_url}/functions") assert response.status_code == 200 result = response.json() - assert len(result) == 269 + assert len(result) == DATAFUSION_FUNCTION_COUNT config.set_remote_function_list_path(function_list_path) response = client.get(url=f"{base_url}/functions") assert response.status_code == 200 result = response.json() - assert len(result) == 304 + assert len(result) == DATAFUSION_FUNCTION_COUNT + 35 the_func = next(filter(lambda x: x["name"] == "abs", result)) assert the_func == { "name": "abs", @@ -72,7 +72,7 @@ def test_function_list(): response = client.get(url=f"{base_url}/functions") assert response.status_code == 200 result = response.json() - assert len(result) == 269 + assert len(result) == DATAFUSION_FUNCTION_COUNT def test_scalar_function(manifest_str: str, connection_info): response = client.post( diff --git a/ibis-server/tests/routers/v3/connector/postgres/test_functions.py b/ibis-server/tests/routers/v3/connector/postgres/test_functions.py index 7a335a4b9..1d661fca4 100644 --- a/ibis-server/tests/routers/v3/connector/postgres/test_functions.py +++ b/ibis-server/tests/routers/v3/connector/postgres/test_functions.py @@ -6,7 +6,7 @@ from app.config import get_config from app.main import app -from tests.conftest import file_path +from tests.conftest import DATAFUSION_FUNCTION_COUNT, file_path from tests.routers.v3.connector.postgres.conftest import base_url manifest = { @@ -51,13 +51,13 @@ def test_function_list(): response = client.get(url=f"{base_url}/functions") assert response.status_code == 200 result = response.json() - assert len(result) == 269 + assert len(result) == DATAFUSION_FUNCTION_COUNT config.set_remote_function_list_path(function_list_path) response = client.get(url=f"{base_url}/functions") assert response.status_code == 200 result = response.json() - assert len(result) == 318 + assert len(result) == DATAFUSION_FUNCTION_COUNT + 48 the_func = next(filter(lambda x: x["name"] == "abs", result)) assert the_func == { "name": "abs", @@ -72,7 +72,7 @@ def test_function_list(): response = client.get(url=f"{base_url}/functions") assert response.status_code == 200 result = response.json() - assert len(result) == 269 + assert len(result) == DATAFUSION_FUNCTION_COUNT def test_scalar_function(manifest_str: str, connection_info): response = client.post( diff --git a/ibis-server/tests/routers/v3/connector/postgres/test_query.py b/ibis-server/tests/routers/v3/connector/postgres/test_query.py index 104e7c7d9..685472fec 100644 --- a/ibis-server/tests/routers/v3/connector/postgres/test_query.py +++ b/ibis-server/tests/routers/v3/connector/postgres/test_query.py @@ -42,7 +42,17 @@ { "name": "timestamptz", "expression": "cast('2024-01-01T23:59:59' as timestamp with time zone)", - "type": "timestamp", + "type": "timestamptz", + }, + { + "name": "dst_utc_minus_5", + "expression": "cast('2024-01-15 23:00:00 America/New_York' as timestamp with time zone)", + "type": "timestamptz", + }, + { + "name": "dst_utc_minus_4", + "expression": "cast('2024-07-15 23:00:00 America/New_York' as timestamp with time zone)", + "type": "timestamptz", }, ], "primaryKey": "o_orderkey", @@ -74,6 +84,8 @@ def test_query(manifest_str, connection_info): assert result["data"][0] == [ "2024-01-01 23:59:59.000000", "2024-01-01 23:59:59.000000 UTC", + "2024-01-16 04:00:00.000000 UTC", # utc-5 + "2024-07-16 03:00:00.000000 UTC", # utc-4 "1_370", 370, "1996-01-02", @@ -90,6 +102,8 @@ def test_query(manifest_str, connection_info): "order_cust_key": "object", "timestamp": "object", "timestamptz": "object", + "dst_utc_minus_5": "object", + "dst_utc_minus_4": "object", } def test_query_with_connection_url(manifest_str, connection_url): diff --git a/ibis-server/tests/routers/v3/connector/snowflake/test_functions.py b/ibis-server/tests/routers/v3/connector/snowflake/test_functions.py index 89eb40c03..e4c6d9c2b 100644 --- a/ibis-server/tests/routers/v3/connector/snowflake/test_functions.py +++ b/ibis-server/tests/routers/v3/connector/snowflake/test_functions.py @@ -6,7 +6,7 @@ from app.config import get_config from app.main import app -from tests.conftest import file_path +from tests.conftest import DATAFUSION_FUNCTION_COUNT, file_path from tests.routers.v3.connector.snowflake.conftest import base_url manifest = { @@ -51,13 +51,13 @@ def test_function_list(): response = client.get(url=f"{base_url}/functions") assert response.status_code == 200 result = response.json() - assert len(result) == 269 + assert len(result) == DATAFUSION_FUNCTION_COUNT config.set_remote_function_list_path(function_list_path) response = client.get(url=f"{base_url}/functions") assert response.status_code == 200 result = response.json() - assert len(result) == 340 + assert len(result) == DATAFUSION_FUNCTION_COUNT + 70 the_func = next(filter(lambda x: x["name"] == "abs", result)) assert the_func == { "name": "abs", @@ -72,7 +72,7 @@ def test_function_list(): response = client.get(url=f"{base_url}/functions") assert response.status_code == 200 result = response.json() - assert len(result) == 269 + assert len(result) == DATAFUSION_FUNCTION_COUNT def test_scalar_function(manifest_str: str, connection_info): response = client.post( @@ -86,9 +86,9 @@ def test_scalar_function(manifest_str: str, connection_info): assert response.status_code == 200 result = response.json() assert result == { - "columns": ["col"], + "columns": ["COL"], "data": [[1]], - "dtypes": {"col": "int32"}, + "dtypes": {"COL": "int64"}, } def test_aggregate_function(manifest_str: str, connection_info): @@ -103,7 +103,7 @@ def test_aggregate_function(manifest_str: str, connection_info): assert response.status_code == 200 result = response.json() assert result == { - "columns": ["col"], + "columns": ["COL"], "data": [[1]], - "dtypes": {"col": "int64"}, + "dtypes": {"COL": "int64"}, } diff --git a/ibis-server/tests/routers/v3/connector/trino/test_functions.py b/ibis-server/tests/routers/v3/connector/trino/test_functions.py index 10b33b509..ddf8c51ad 100644 --- a/ibis-server/tests/routers/v3/connector/trino/test_functions.py +++ b/ibis-server/tests/routers/v3/connector/trino/test_functions.py @@ -6,7 +6,7 @@ from app.config import get_config from app.main import app -from tests.conftest import file_path +from tests.conftest import DATAFUSION_FUNCTION_COUNT, file_path from tests.routers.v3.connector.trino.conftest import base_url manifest = { @@ -51,13 +51,13 @@ def test_function_list(): response = client.get(url=f"{base_url}/functions") assert response.status_code == 200 result = response.json() - assert len(result) == 269 + assert len(result) == DATAFUSION_FUNCTION_COUNT config.set_remote_function_list_path(function_list_path) response = client.get(url=f"{base_url}/functions") assert response.status_code == 200 result = response.json() - assert len(result) == 299 + assert len(result) == DATAFUSION_FUNCTION_COUNT + 30 the_func = next(filter(lambda x: x["name"] == "abs", result)) assert the_func == { "name": "abs", @@ -72,7 +72,7 @@ def test_function_list(): response = client.get(url=f"{base_url}/functions") assert response.status_code == 200 result = response.json() - assert len(result) == 269 + assert len(result) == DATAFUSION_FUNCTION_COUNT def test_scalar_function(manifest_str: str, connection_info): response = client.post( diff --git a/ibis-server/tools/mdl_validation.py b/ibis-server/tools/mdl_validation.py index 5b27e0409..aa708515e 100644 --- a/ibis-server/tools/mdl_validation.py +++ b/ibis-server/tools/mdl_validation.py @@ -12,6 +12,7 @@ import base64 import json +from loguru import logger from wren_core import SessionContext # Set up argument parsing @@ -39,9 +40,10 @@ sql = f"select \"{column['name']}\" from \"{model['name']}\"" try: planned_sql = session_context.transform_sql(sql) - except Exception: + except Exception as e: error_cases.append((model, column)) - print(f"Error transforming {model['name']} {column['name']}") + logger.info(f"Error transforming {model['name']} {column['name']}") + logger.debug(e) if len(error_cases) > 0: raise Exception(f"Error transforming {len(error_cases)} columns") diff --git a/ibis-server/tools/query_local_run.py b/ibis-server/tools/query_local_run.py index 0c934af37..fd07ceed6 100644 --- a/ibis-server/tools/query_local_run.py +++ b/ibis-server/tools/query_local_run.py @@ -42,7 +42,7 @@ print("# Function List Path:", function_list_path) print("# Connection Info Path:", connection_info_path) print("# Data Source:", data_source) -print("# SQL Query:", sql) +print("# SQL Query:\n", sql) print("#") # Read and encode the JSON data @@ -60,11 +60,11 @@ print("#") session_context = SessionContext(encoded_str, function_list_path) planned_sql = session_context.transform_sql(sql) -print("# Planned SQL:", planned_sql) +print("# Planned SQL:\n", planned_sql) # Transpile the planned SQL dialect_sql = sqlglot.transpile(planned_sql, read="trino", write=data_source)[0] -print("# Dialect SQL:", dialect_sql) +print("# Dialect SQL:\n", dialect_sql) print("#") if data_source == "bigquery": diff --git a/pom.xml b/pom.xml index 1c05c40a5..d29d78ce2 100644 --- a/pom.xml +++ b/pom.xml @@ -5,12 +5,12 @@ io.airlift airbase - 196 + 200 io.wren wren-root - 0.11.3-SNAPSHOT + 0.12.3-SNAPSHOT pom WrenEngine @@ -67,8 +67,8 @@ 269 ${dep.airlift.version} 1.22 - 1.20.3 - 2.35.1 + 1.20.4 + 2.36.0 ${dep.plugin.surefire.version} 4.1.45.Final @@ -174,7 +174,7 @@ com.zaxxer HikariCP - 6.0.0 + 6.2.1 org.slf4j @@ -355,7 +355,7 @@ org.duckdb duckdb_jdbc - 1.1.2 + 1.1.3 diff --git a/trino-parser/pom.xml b/trino-parser/pom.xml index 776629ee5..a06a43978 100644 --- a/trino-parser/pom.xml +++ b/trino-parser/pom.xml @@ -5,7 +5,7 @@ io.wren wren-root - 0.11.3-SNAPSHOT + 0.12.3-SNAPSHOT ../pom.xml diff --git a/wren-base/pom.xml b/wren-base/pom.xml index dc2ee3ee6..16597f439 100644 --- a/wren-base/pom.xml +++ b/wren-base/pom.xml @@ -18,7 +18,7 @@ io.wren wren-root - 0.11.3-SNAPSHOT + 0.12.3-SNAPSHOT ../pom.xml diff --git a/wren-base/src/main/java/io/wren/base/WrenMDL.java b/wren-base/src/main/java/io/wren/base/WrenMDL.java index a47f84ffa..0f0a80968 100644 --- a/wren-base/src/main/java/io/wren/base/WrenMDL.java +++ b/wren-base/src/main/java/io/wren/base/WrenMDL.java @@ -98,8 +98,7 @@ private Manifest renderManifest(Manifest original) processed, model.getPrimaryKey(), model.isCached(), - model.getRefreshTime(), - model.getProperties()); + model.getRefreshTime()); }).collect(toList()); List renderedMetrics = original.getMetrics().stream().map(metric -> @@ -109,8 +108,7 @@ private Manifest renderManifest(Manifest original) metric.getMeasure().stream().map(column -> renderExpression(column, macroTags, original)).collect(toList()), metric.getTimeGrain(), metric.isCached(), - metric.getRefreshTime(), - metric.getProperties()) + metric.getRefreshTime()) ).collect(toList()); return Manifest.builder(original) @@ -132,8 +130,7 @@ private io.wren.base.dto.Column renderExpression(io.wren.base.dto.Column origina original.getRelationship().orElse(null), original.isCalculated(), original.isNotNull(), - expression, - original.getProperties()); + expression); } public String getCatalog() diff --git a/wren-base/src/main/java/io/wren/base/dto/Column.java b/wren-base/src/main/java/io/wren/base/dto/Column.java index f65533342..7b8fb2e5d 100644 --- a/wren-base/src/main/java/io/wren/base/dto/Column.java +++ b/wren-base/src/main/java/io/wren/base/dto/Column.java @@ -16,9 +16,7 @@ import com.fasterxml.jackson.annotation.JsonCreator; import com.fasterxml.jackson.annotation.JsonProperty; -import com.google.common.collect.ImmutableMap; -import java.util.Map; import java.util.Objects; import java.util.Optional; @@ -34,26 +32,25 @@ public class Column private final String relationship; private final String expression; private final boolean isCalculated; - private final Map properties; public static Column column(String name, String type, String relationship, boolean notNull) { - return new Column(name, type, relationship, false, notNull, null, null); + return new Column(name, type, relationship, false, notNull, null); } public static Column column(String name, String type, String relationship, boolean notNull, String expression) { - return new Column(name, type, relationship, false, notNull, expression, null); + return new Column(name, type, relationship, false, notNull, expression); } public static Column relationshipColumn(String name, String type, String relationship) { - return new Column(name, type, relationship, false, false, null, null); + return new Column(name, type, relationship, false, false, null); } - public static Column caluclatedColumn(String name, String type, String expression) + public static Column calculatedColumn(String name, String type, String expression) { - return new Column(name, type, null, true, false, expression, null); + return new Column(name, type, null, true, false, expression); } @JsonCreator @@ -63,8 +60,7 @@ public Column( @JsonProperty("relationship") String relationship, @JsonProperty("isCalculated") boolean isCalculated, @JsonProperty("notNull") boolean notNull, - @JsonProperty("expression") String expression, - @JsonProperty("properties") Map properties) + @JsonProperty("expression") String expression) { this.name = requireNonNullEmpty(name, "name is null or empty"); this.type = requireNonNullEmpty(type, "type is null or empty"); @@ -72,7 +68,6 @@ public Column( this.isCalculated = isCalculated; this.notNull = notNull; this.expression = expression == null || expression.isEmpty() ? null : expression; - this.properties = properties == null ? ImmutableMap.of() : properties; } @JsonProperty @@ -111,12 +106,6 @@ public boolean isCalculated() return isCalculated; } - @JsonProperty - public Map getProperties() - { - return properties; - } - public String getSqlExpression() { return getExpression().orElse(quote(name)); @@ -137,14 +126,13 @@ public boolean equals(Object obj) Objects.equals(name, that.name) && Objects.equals(type, that.type) && Objects.equals(relationship, that.relationship) && - Objects.equals(expression, that.expression) && - Objects.equals(properties, that.properties); + Objects.equals(expression, that.expression); } @Override public int hashCode() { - return Objects.hash(name, type, isCalculated, notNull, relationship, expression, properties); + return Objects.hash(name, type, isCalculated, notNull, relationship, expression); } @Override @@ -157,7 +145,6 @@ public String toString() .add("isCalculated", isCalculated) .add("relationship", relationship) .add("expression", expression) - .add("properties", properties) .toString(); } diff --git a/wren-base/src/main/java/io/wren/base/dto/CumulativeMetric.java b/wren-base/src/main/java/io/wren/base/dto/CumulativeMetric.java index 1fe2ddc2f..df82707f0 100644 --- a/wren-base/src/main/java/io/wren/base/dto/CumulativeMetric.java +++ b/wren-base/src/main/java/io/wren/base/dto/CumulativeMetric.java @@ -16,10 +16,8 @@ import com.fasterxml.jackson.annotation.JsonCreator; import com.fasterxml.jackson.annotation.JsonProperty; -import com.google.common.collect.ImmutableMap; import io.airlift.units.Duration; -import java.util.Map; import java.util.Objects; import static com.google.common.base.MoreObjects.toStringHelper; @@ -35,7 +33,7 @@ public static CumulativeMetric cumulativeMetric( Measure measure, Window window) { - return new CumulativeMetric(name, baseObject, measure, window, false, null, ImmutableMap.of()); + return new CumulativeMetric(name, baseObject, measure, window, false, null); } private final String name; @@ -44,7 +42,6 @@ public static CumulativeMetric cumulativeMetric( private final Window window; private final boolean cached; private final Duration refreshTime; - private final Map properties; @JsonCreator public CumulativeMetric( @@ -53,8 +50,7 @@ public CumulativeMetric( @JsonProperty("measure") Measure measure, @JsonProperty("window") Window window, @JsonProperty("cached") boolean cached, - @JsonProperty("refreshTime") Duration refreshTime, - @JsonProperty("properties") Map properties) + @JsonProperty("refreshTime") Duration refreshTime) { this.name = requireNonNullEmpty(name, "name is null or empty"); this.baseObject = requireNonNullEmpty(baseObject, "baseObject is null or empty"); @@ -62,7 +58,6 @@ public CumulativeMetric( this.window = requireNonNull(window, "window is null"); this.cached = cached; this.refreshTime = refreshTime; - this.properties = properties == null ? ImmutableMap.of() : properties; } @JsonProperty @@ -101,16 +96,10 @@ public Duration getRefreshTime() return refreshTime; } - @JsonProperty - public Map getProperties() - { - return properties; - } - @Override public int hashCode() { - return Objects.hash(name, baseObject, measure, window, cached, refreshTime, properties); + return Objects.hash(name, baseObject, measure, window, cached, refreshTime); } @Override @@ -130,8 +119,7 @@ public boolean equals(Object o) Objects.equals(baseObject, that.baseObject) && Objects.equals(measure, that.measure) && Objects.equals(window, that.window) && - Objects.equals(refreshTime, that.refreshTime) && - Objects.equals(properties, that.properties); + Objects.equals(refreshTime, that.refreshTime); } @Override @@ -144,7 +132,6 @@ public String toString() .add("window", window) .add("cached", cached) .add("refreshTime", refreshTime) - .add("properties", properties) .toString(); } } diff --git a/wren-base/src/main/java/io/wren/base/dto/DateSpine.java b/wren-base/src/main/java/io/wren/base/dto/DateSpine.java index 17870648e..256d17fb0 100644 --- a/wren-base/src/main/java/io/wren/base/dto/DateSpine.java +++ b/wren-base/src/main/java/io/wren/base/dto/DateSpine.java @@ -16,9 +16,7 @@ import com.fasterxml.jackson.annotation.JsonCreator; import com.fasterxml.jackson.annotation.JsonProperty; -import com.google.common.collect.ImmutableMap; -import java.util.Map; import java.util.Objects; import static com.google.common.base.MoreObjects.toStringHelper; @@ -27,24 +25,21 @@ public class DateSpine { - public static final DateSpine DEFAULT = new DateSpine(TimeUnit.DAY, "1970-01-01", "2077-12-31", null); + public static final DateSpine DEFAULT = new DateSpine(TimeUnit.DAY, "1970-01-01", "2077-12-31"); private final TimeUnit unit; private final String start; private final String end; - private final Map properties; @JsonCreator public DateSpine( @JsonProperty("unit") TimeUnit unit, @JsonProperty("start") String start, - @JsonProperty("end") String end, - @JsonProperty("properties") Map properties) + @JsonProperty("end") String end) { this.unit = requireNonNull(unit, "unit is null"); this.start = requireNonNullEmpty(start, "start is null or empty"); this.end = requireNonNullEmpty(end, "end is null or empty"); - this.properties = properties == null ? ImmutableMap.of() : properties; } @JsonProperty @@ -65,12 +60,6 @@ public String getEnd() return end; } - @JsonProperty - public Map getProperties() - { - return properties; - } - @Override public String toString() { @@ -78,7 +67,6 @@ public String toString() .add("unit", unit) .add("start", start) .add("end", end) - .add("properties", properties) .toString(); } @@ -94,13 +82,12 @@ public boolean equals(Object o) DateSpine dateSpine = (DateSpine) o; return unit == dateSpine.unit && Objects.equals(start, dateSpine.start) && - Objects.equals(end, dateSpine.end) && - Objects.equals(properties, dateSpine.properties); + Objects.equals(end, dateSpine.end); } @Override public int hashCode() { - return Objects.hash(unit, start, end, properties); + return Objects.hash(unit, start, end); } } diff --git a/wren-base/src/main/java/io/wren/base/dto/EnumDefinition.java b/wren-base/src/main/java/io/wren/base/dto/EnumDefinition.java index ed2db016a..507ee5e81 100644 --- a/wren-base/src/main/java/io/wren/base/dto/EnumDefinition.java +++ b/wren-base/src/main/java/io/wren/base/dto/EnumDefinition.java @@ -16,10 +16,8 @@ import com.fasterxml.jackson.annotation.JsonCreator; import com.fasterxml.jackson.annotation.JsonProperty; -import com.google.common.collect.ImmutableMap; import java.util.List; -import java.util.Map; import java.util.Objects; import java.util.Optional; @@ -31,22 +29,19 @@ public class EnumDefinition { public static EnumDefinition enumDefinition(String name, List values) { - return new EnumDefinition(name, values, ImmutableMap.of()); + return new EnumDefinition(name, values); } private final String name; private final List values; - private final Map properties; @JsonCreator public EnumDefinition( @JsonProperty("name") String name, - @JsonProperty("values") List values, - @JsonProperty("properties") Map properties) + @JsonProperty("values") List values) { this.name = requireNonNullEmpty(name, "name is null or empty"); this.values = requireNonNull(values); - this.properties = properties == null ? ImmutableMap.of() : properties; } @JsonProperty @@ -61,12 +56,6 @@ public String getName() return name; } - @JsonProperty - public Map getProperties() - { - return properties; - } - public Optional valueOf(String enumValueName) { return values.stream() @@ -85,8 +74,7 @@ public boolean equals(Object obj) } EnumDefinition that = (EnumDefinition) obj; return Objects.equals(name, that.name) && - Objects.equals(values, that.values) && - Objects.equals(properties, that.properties); + Objects.equals(values, that.values); } @Override @@ -94,8 +82,7 @@ public int hashCode() { return Objects.hash( name, - values, - properties); + values); } @Override @@ -104,7 +91,6 @@ public String toString() return toStringHelper(this) .add("name", name) .add("values", values) - .add("properties", properties) .toString(); } } diff --git a/wren-base/src/main/java/io/wren/base/dto/EnumValue.java b/wren-base/src/main/java/io/wren/base/dto/EnumValue.java index c7b0c6c9d..cb79a0f69 100644 --- a/wren-base/src/main/java/io/wren/base/dto/EnumValue.java +++ b/wren-base/src/main/java/io/wren/base/dto/EnumValue.java @@ -16,9 +16,7 @@ import com.fasterxml.jackson.annotation.JsonCreator; import com.fasterxml.jackson.annotation.JsonProperty; -import com.google.common.collect.ImmutableMap; -import java.util.Map; import java.util.Objects; import java.util.Optional; @@ -34,22 +32,19 @@ public static EnumValue enumValue(String name) public static EnumValue enumValue(String name, String value) { - return new EnumValue(name, value, null); + return new EnumValue(name, value); } private final String name; private final String value; - private final Map properties; @JsonCreator public EnumValue( @JsonProperty("name") String name, - @JsonProperty("value") String value, - @JsonProperty("properties") Map properties) + @JsonProperty("value") String value) { this.name = requireNonNullEmpty(name, "name is null or empty"); this.value = value; - this.properties = properties == null ? ImmutableMap.of() : properties; } @JsonProperty @@ -64,12 +59,6 @@ public String getValue() return Optional.ofNullable(value).orElse(name); } - @JsonProperty - public Map getProperties() - { - return properties; - } - @Override public boolean equals(Object o) { @@ -81,14 +70,13 @@ public boolean equals(Object o) } EnumValue enumValue = (EnumValue) o; return Objects.equals(name, enumValue.name) && - Objects.equals(value, enumValue.value) && - Objects.equals(properties, enumValue.properties); + Objects.equals(value, enumValue.value); } @Override public int hashCode() { - return Objects.hash(name, value, properties); + return Objects.hash(name, value); } @Override @@ -97,7 +85,6 @@ public String toString() return toStringHelper(this) .add("name", name) .add("value", value) - .add("properties", properties) .toString(); } } diff --git a/wren-base/src/main/java/io/wren/base/dto/Macro.java b/wren-base/src/main/java/io/wren/base/dto/Macro.java index 65ddccab4..884eadacc 100644 --- a/wren-base/src/main/java/io/wren/base/dto/Macro.java +++ b/wren-base/src/main/java/io/wren/base/dto/Macro.java @@ -16,12 +16,10 @@ import com.fasterxml.jackson.annotation.JsonCreator; import com.fasterxml.jackson.annotation.JsonProperty; -import com.google.common.collect.ImmutableMap; import io.wren.base.macro.Parameter; import io.wren.base.macro.ParameterListParser; import java.util.List; -import java.util.Map; import java.util.Objects; import static com.google.common.base.MoreObjects.toStringHelper; @@ -33,20 +31,18 @@ public class Macro { public static Macro macro(String name, String definition) { - return new Macro(name, definition, ImmutableMap.of()); + return new Macro(name, definition); } private final String name; private final String definition; private final List parameters; private final String body; - private final Map properties; @JsonCreator public Macro( @JsonProperty("name") String name, - @JsonProperty("definition") String definition, - @JsonProperty("properties") Map properties) + @JsonProperty("definition") String definition) { this.name = requireNonNullEmpty(name, "name is null or empty"); this.definition = requireNonNullEmpty(definition, "definition is null or empty"); @@ -56,7 +52,6 @@ public Macro( String body = split[1].trim(); this.parameters = new ParameterListParser().parse(paramString); this.body = body; - this.properties = properties == null ? ImmutableMap.of() : properties; } @JsonProperty @@ -81,12 +76,6 @@ public String getBody() return body; } - @JsonProperty - public Map getProperties() - { - return properties; - } - @Override public boolean equals(Object o) { @@ -99,14 +88,13 @@ public boolean equals(Object o) Macro macro = (Macro) o; return Objects.equals(name, macro.name) && Objects.equals(parameters, macro.parameters) && - Objects.equals(body, macro.body) && - Objects.equals(properties, macro.properties); + Objects.equals(body, macro.body); } @Override public int hashCode() { - return Objects.hash(name, parameters, body, properties); + return Objects.hash(name, parameters, body); } @Override @@ -116,7 +104,6 @@ public String toString() .add("name", name) .add("parameters", parameters) .add("body", body) - .add("properties", properties) .toString(); } } diff --git a/wren-base/src/main/java/io/wren/base/dto/Measure.java b/wren-base/src/main/java/io/wren/base/dto/Measure.java index 27f29d6ae..f89a6fbcc 100644 --- a/wren-base/src/main/java/io/wren/base/dto/Measure.java +++ b/wren-base/src/main/java/io/wren/base/dto/Measure.java @@ -17,7 +17,6 @@ import com.fasterxml.jackson.annotation.JsonCreator; import com.fasterxml.jackson.annotation.JsonProperty; -import java.util.Map; import java.util.Objects; import static com.google.common.base.MoreObjects.toStringHelper; @@ -27,28 +26,25 @@ public class Measure { public static Measure measure(String name, String type, String operator, String refColumn) { - return new Measure(name, type, operator, refColumn, null); + return new Measure(name, type, operator, refColumn); } private final String name; private final String type; private final String operator; private final String refColumn; - private final Map properties; @JsonCreator public Measure( @JsonProperty("name") String name, @JsonProperty("type") String type, @JsonProperty("operator") String operator, - @JsonProperty("refColumn") String refColumn, - @JsonProperty("properties") Map properties) + @JsonProperty("refColumn") String refColumn) { this.name = requireNonNullEmpty(name, "name is null or empty"); this.type = requireNonNullEmpty(type, "type is null or empty"); this.operator = requireNonNullEmpty(operator, "operator is null or empty"); this.refColumn = requireNonNullEmpty(refColumn, "refColumn is null or empty"); - this.properties = properties; } @JsonProperty @@ -75,15 +71,9 @@ public String getRefColumn() return refColumn; } - @JsonProperty - public Map getProperties() - { - return properties; - } - public Column toColumn() { - return new Column(name, type, null, false, false, refColumn, properties); + return new Column(name, type, null, false, false, refColumn); } @Override @@ -99,14 +89,13 @@ public boolean equals(Object o) return Objects.equals(name, measure.name) && Objects.equals(type, measure.type) && Objects.equals(operator, measure.operator) && - Objects.equals(refColumn, measure.refColumn) && - Objects.equals(properties, measure.properties); + Objects.equals(refColumn, measure.refColumn); } @Override public int hashCode() { - return Objects.hash(name, type, operator, refColumn, properties); + return Objects.hash(name, type, operator, refColumn); } @Override @@ -117,7 +106,6 @@ public String toString() .add("type", type) .add("operator", operator) .add("refColumn", refColumn) - .add("properties", properties) .toString(); } } diff --git a/wren-base/src/main/java/io/wren/base/dto/Metric.java b/wren-base/src/main/java/io/wren/base/dto/Metric.java index e46368297..38ab9475b 100644 --- a/wren-base/src/main/java/io/wren/base/dto/Metric.java +++ b/wren-base/src/main/java/io/wren/base/dto/Metric.java @@ -17,11 +17,9 @@ import com.fasterxml.jackson.annotation.JsonCreator; import com.fasterxml.jackson.annotation.JsonProperty; import com.google.common.collect.ImmutableList; -import com.google.common.collect.ImmutableMap; import io.airlift.units.Duration; import java.util.List; -import java.util.Map; import java.util.Objects; import java.util.Optional; @@ -40,7 +38,6 @@ public class Metric private final List timeGrain; private final boolean cached; private final Duration refreshTime; - private final Map properties; public static Metric metric(String name, String baseObject, List dimension, List measure) { @@ -54,7 +51,7 @@ public static Metric metric(String name, String baseObject, List dimensi public static Metric metric(String name, String baseObject, List dimension, List measure, List timeGrain, boolean cached) { - return new Metric(name, baseObject, dimension, measure, timeGrain, cached, null, ImmutableMap.of()); + return new Metric(name, baseObject, dimension, measure, timeGrain, cached, null); } @JsonCreator @@ -65,8 +62,7 @@ public Metric( @JsonProperty("measure") List measure, @JsonProperty("timeGrain") List timeGrain, @JsonProperty("cached") boolean cached, - @JsonProperty("refreshTime") Duration refreshTime, - @JsonProperty("properties") Map properties) + @JsonProperty("refreshTime") Duration refreshTime) { this.name = requireNonNullEmpty(name, "name is null or empty"); this.baseObject = requireNonNullEmpty(baseObject, "baseObject is null or empty"); @@ -76,7 +72,6 @@ public Metric( checkArgument(!measure.isEmpty(), "the number of measures should be one at least"); this.timeGrain = timeGrain == null ? ImmutableList.of() : timeGrain; this.refreshTime = refreshTime == null ? defaultRefreshTime : refreshTime; - this.properties = properties == null ? ImmutableMap.of() : properties; } @Override @@ -138,12 +133,6 @@ public Duration getRefreshTime() return refreshTime; } - @JsonProperty - public Map getProperties() - { - return properties; - } - @Override public boolean equals(Object obj) { @@ -160,8 +149,7 @@ public boolean equals(Object obj) Objects.equals(dimension, that.dimension) && Objects.equals(measure, that.measure) && Objects.equals(timeGrain, that.timeGrain) && - Objects.equals(refreshTime, that.refreshTime) && - Objects.equals(properties, that.properties); + Objects.equals(refreshTime, that.refreshTime); } @Override @@ -174,8 +162,7 @@ public int hashCode() measure, timeGrain, cached, - refreshTime, - properties); + refreshTime); } @Override @@ -189,7 +176,6 @@ public String toString() .add("timeGrain", timeGrain) .add("cached", cached) .add("refreshTime", refreshTime) - .add("properties", properties) .toString(); } } diff --git a/wren-base/src/main/java/io/wren/base/dto/Model.java b/wren-base/src/main/java/io/wren/base/dto/Model.java index 5585f475b..23dab92c4 100644 --- a/wren-base/src/main/java/io/wren/base/dto/Model.java +++ b/wren-base/src/main/java/io/wren/base/dto/Model.java @@ -16,11 +16,9 @@ import com.fasterxml.jackson.annotation.JsonCreator; import com.fasterxml.jackson.annotation.JsonProperty; -import com.google.common.collect.ImmutableMap; import io.airlift.units.Duration; import java.util.List; -import java.util.Map; import java.util.Objects; import java.util.stream.Stream; @@ -39,7 +37,6 @@ public class Model private final String primaryKey; private final boolean cached; private final Duration refreshTime; - private final Map properties; public static Model model(String name, String refSql, List columns) { @@ -48,22 +45,22 @@ public static Model model(String name, String refSql, List columns) public static Model model(String name, String refSql, List columns, boolean cached) { - return new Model(name, refSql, null, null, columns, null, cached, null, ImmutableMap.of()); + return new Model(name, refSql, null, null, columns, null, cached, null); } public static Model model(String name, String refSql, List columns, String primaryKey) { - return new Model(name, refSql, null, null, columns, primaryKey, false, null, ImmutableMap.of()); + return new Model(name, refSql, null, null, columns, primaryKey, false, null); } public static Model onBaseObject(String name, String baseObject, List columns, String primaryKey) { - return new Model(name, null, baseObject, null, columns, primaryKey, false, null, ImmutableMap.of()); + return new Model(name, null, baseObject, null, columns, primaryKey, false, null); } public static Model onTableReference(String name, TableReference tableReference, List columns, String primaryKey) { - return new Model(name, null, null, tableReference, columns, primaryKey, false, null, ImmutableMap.of()); + return new Model(name, null, null, tableReference, columns, primaryKey, false, null); } @JsonCreator @@ -75,8 +72,7 @@ public Model( @JsonProperty("columns") List columns, @JsonProperty("primaryKey") String primaryKey, @JsonProperty("cached") boolean cached, - @JsonProperty("refreshTime") Duration refreshTime, - @JsonProperty("properties") Map properties) + @JsonProperty("refreshTime") Duration refreshTime) { this.name = requireNonNullEmpty(name, "name is null or empty"); checkArgument(Stream.of(refSql, baseObject, tableReference).filter(Model::isNonNullOrNonEmpty).count() == 1, @@ -88,7 +84,6 @@ public Model( this.primaryKey = primaryKey; this.cached = cached; this.refreshTime = refreshTime == null ? defaultRefreshTime : refreshTime; - this.properties = properties == null ? ImmutableMap.of() : properties; } private static boolean isNonNullOrNonEmpty(Object value) @@ -154,12 +149,6 @@ public Duration getRefreshTime() return refreshTime; } - @JsonProperty - public Map getProperties() - { - return properties; - } - @Override public boolean equals(Object obj) { @@ -177,14 +166,13 @@ public boolean equals(Object obj) Objects.equals(tableReference, that.tableReference) && Objects.equals(columns, that.columns) && Objects.equals(primaryKey, that.primaryKey) && - Objects.equals(refreshTime, that.refreshTime) && - Objects.equals(properties, that.properties); + Objects.equals(refreshTime, that.refreshTime); } @Override public int hashCode() { - return Objects.hash(name, refSql, baseObject, tableReference, columns, primaryKey, properties); + return Objects.hash(name, refSql, baseObject, tableReference, columns, primaryKey); } @Override @@ -198,7 +186,6 @@ public String toString() .add("columns", columns) .add("cached", cached) .add("refreshTime", refreshTime) - .add("properties", properties) .toString(); } } diff --git a/wren-base/src/main/java/io/wren/base/dto/Relationship.java b/wren-base/src/main/java/io/wren/base/dto/Relationship.java index ee8fc4d37..5d5fcea14 100644 --- a/wren-base/src/main/java/io/wren/base/dto/Relationship.java +++ b/wren-base/src/main/java/io/wren/base/dto/Relationship.java @@ -16,7 +16,6 @@ import com.fasterxml.jackson.annotation.JsonCreator; import com.fasterxml.jackson.annotation.JsonProperty; -import com.google.common.collect.ImmutableMap; import com.google.common.collect.Lists; import io.trino.sql.tree.DereferenceExpression; import io.trino.sql.tree.Expression; @@ -26,7 +25,6 @@ import java.util.Arrays; import java.util.List; -import java.util.Map; import java.util.Objects; import static com.google.common.base.MoreObjects.toStringHelper; @@ -46,7 +44,6 @@ public class Relationship // for debugging internally private final boolean isReverse; private final List manySideSortKeys; - private final Map properties; public static Relationship relationship(String name, List models, JoinType joinType, String condition) { @@ -55,7 +52,7 @@ public static Relationship relationship(String name, List models, JoinTy public static Relationship relationship(String name, List models, JoinType joinType, String condition, List manySideSortKeys) { - return new Relationship(name, models, joinType, condition, manySideSortKeys, ImmutableMap.of()); + return new Relationship(name, models, joinType, condition, manySideSortKeys); } public static Relationship reverse(Relationship relationship) @@ -66,8 +63,7 @@ public static Relationship reverse(Relationship relationship) JoinType.reverse(relationship.joinType), relationship.getCondition(), true, - relationship.getManySideSortKeys(), - ImmutableMap.of()); + relationship.getManySideSortKeys()); } @JsonCreator @@ -76,10 +72,9 @@ public Relationship( @JsonProperty("models") List models, @JsonProperty("joinType") JoinType joinType, @JsonProperty("condition") String condition, - @JsonProperty("manySideSortKeys") List manySideSortKeys, - @JsonProperty("properties") Map properties) + @JsonProperty("manySideSortKeys") List manySideSortKeys) { - this(name, models, joinType, condition, false, manySideSortKeys, properties); + this(name, models, joinType, condition, false, manySideSortKeys); } public Relationship( @@ -88,8 +83,7 @@ public Relationship( JoinType joinType, String condition, boolean isReverse, - List manySideSortKeys, - Map properties) + List manySideSortKeys) { this.name = requireNonNullEmpty(name, "name is null or empty"); checkArgument(models != null && models.size() >= 2, "relationship should contain at least 2 models"); @@ -99,7 +93,6 @@ public Relationship( this.qualifiedCondition = qualifiedCondition(condition); this.isReverse = isReverse; this.manySideSortKeys = manySideSortKeys == null ? List.of() : manySideSortKeys; - this.properties = properties == null ? ImmutableMap.of() : properties; } private Expression qualifiedCondition(String condition) @@ -170,12 +163,6 @@ public boolean isReverse() return isReverse; } - @JsonProperty("properties") - public Map getProperties() - { - return properties; - } - @Override public boolean equals(Object obj) { @@ -191,14 +178,13 @@ public boolean equals(Object obj) joinType == that.joinType && Objects.equals(condition, that.condition) && isReverse == that.isReverse && - Objects.equals(manySideSortKeys, that.manySideSortKeys) && - Objects.equals(properties, that.properties); + Objects.equals(manySideSortKeys, that.manySideSortKeys); } @Override public int hashCode() { - return Objects.hash(name, models, joinType, condition, isReverse, manySideSortKeys, properties); + return Objects.hash(name, models, joinType, condition, isReverse, manySideSortKeys); } @Override @@ -211,7 +197,6 @@ public String toString() .add("condition", condition) .add("isReverse", isReverse) .add("manySideSortKeys", manySideSortKeys) - .add("properties", properties) .toString(); } diff --git a/wren-base/src/main/java/io/wren/base/dto/View.java b/wren-base/src/main/java/io/wren/base/dto/View.java index b1ccb2d05..833e81186 100644 --- a/wren-base/src/main/java/io/wren/base/dto/View.java +++ b/wren-base/src/main/java/io/wren/base/dto/View.java @@ -16,7 +16,6 @@ import com.fasterxml.jackson.annotation.JsonCreator; import com.fasterxml.jackson.annotation.JsonProperty; -import com.google.common.collect.ImmutableMap; import java.util.Map; import java.util.Objects; @@ -28,7 +27,6 @@ public class View { private final String name; private final String statement; - private final Map properties; public static View view(String name, String statement) { @@ -43,7 +41,6 @@ public View( { this.name = requireNonNullEmpty(name, "name is null or empty"); this.statement = requireNonNullEmpty(statement, "statement is null or empty"); - this.properties = properties == null ? ImmutableMap.of() : properties; } @JsonProperty @@ -58,12 +55,6 @@ public String getStatement() return statement; } - @JsonProperty - public Map getProperties() - { - return properties; - } - @Override public boolean equals(Object o) { @@ -75,8 +66,7 @@ public boolean equals(Object o) } View view = (View) o; return Objects.equals(name, view.name) && - Objects.equals(statement, view.statement) && - Objects.equals(properties, view.properties); + Objects.equals(statement, view.statement); } @Override @@ -84,8 +74,7 @@ public int hashCode() { return Objects.hash( name, - statement, - properties); + statement); } @Override @@ -94,7 +83,6 @@ public String toString() return toStringHelper(this) .add("name", name) .add("statement", statement) - .add("properties", properties) .toString(); } } diff --git a/wren-base/src/main/java/io/wren/base/dto/Window.java b/wren-base/src/main/java/io/wren/base/dto/Window.java index 606ed0c19..38f4faea3 100644 --- a/wren-base/src/main/java/io/wren/base/dto/Window.java +++ b/wren-base/src/main/java/io/wren/base/dto/Window.java @@ -16,10 +16,8 @@ import com.fasterxml.jackson.annotation.JsonCreator; import com.fasterxml.jackson.annotation.JsonProperty; -import com.google.common.collect.ImmutableMap; import io.wren.base.WrenTypes; -import java.util.Map; import java.util.Objects; import static com.google.common.base.MoreObjects.toStringHelper; @@ -36,11 +34,10 @@ public class Window private final String start; private final String end; - private final Map properties; public static Window window(String name, String refColumn, TimeUnit timeUnit, String start, String end) { - return new Window(name, refColumn, timeUnit, start, end, null); + return new Window(name, refColumn, timeUnit, start, end); } @JsonCreator @@ -49,15 +46,13 @@ public Window( @JsonProperty("refColumn") String refColumn, @JsonProperty("timeUnit") TimeUnit timeUnit, @JsonProperty("start") String start, - @JsonProperty("end") String end, - @JsonProperty("properties") Map properties) + @JsonProperty("end") String end) { this.name = requireNonNullEmpty(name, "name is null or empty"); this.refColumn = requireNonNullEmpty(refColumn, "refColumn is null or empty"); this.timeUnit = requireNonNull(timeUnit, "timeUnit is null or empty"); this.start = requireNonNullEmpty(start, "start is null or empty"); this.end = requireNonNullEmpty(end, "end is null or empty"); - this.properties = properties == null ? ImmutableMap.of() : properties; } @JsonProperty @@ -90,15 +85,9 @@ public String getEnd() return end; } - @JsonProperty - public Map getProperties() - { - return properties; - } - public Column toColumn() { - return new Column(name, WrenTypes.TIMESTAMP, null, false, false, refColumn, properties); + return new Column(name, WrenTypes.TIMESTAMP, null, false, false, refColumn); } @Override @@ -110,7 +99,6 @@ public String toString() .add("timeUnit", timeUnit) .add("start", start) .add("end", end) - .add("properties", properties) .toString(); } @@ -129,13 +117,12 @@ public boolean equals(Object o) Objects.equals(refColumn, window.refColumn) && timeUnit == window.timeUnit && Objects.equals(start, window.start) && - Objects.equals(end, window.end) && - Objects.equals(properties, window.properties); + Objects.equals(end, window.end); } @Override public int hashCode() { - return Objects.hash(name, refColumn, timeUnit, start, end, properties); + return Objects.hash(name, refColumn, timeUnit, start, end); } } diff --git a/wren-base/src/main/java/io/wren/base/sqlrewrite/analyzer/StatementAnalyzer.java b/wren-base/src/main/java/io/wren/base/sqlrewrite/analyzer/StatementAnalyzer.java index f1cd51ec3..7575de23e 100644 --- a/wren-base/src/main/java/io/wren/base/sqlrewrite/analyzer/StatementAnalyzer.java +++ b/wren-base/src/main/java/io/wren/base/sqlrewrite/analyzer/StatementAnalyzer.java @@ -62,6 +62,7 @@ import java.util.Optional; import java.util.Set; import java.util.concurrent.atomic.AtomicInteger; +import java.util.stream.Stream; import static com.google.common.base.Preconditions.checkState; import static com.google.common.collect.ImmutableList.toImmutableList; @@ -374,21 +375,26 @@ else if (item instanceof SingleColumn) { private void analyzeSelectAllColumns(AllColumns allColumns, Scope scope, ImmutableList.Builder outputExpressions) { + List fields = scope.getRelationType().getFields(); + Stream collectedColumns = fields.stream().filter(f -> f.getSourceColumn().map(c -> !c.isCalculated()).orElse(true)); + Stream outputExpressionStream = fields.stream(); + if (allColumns.getTarget().isPresent()) { - // TODO handle target.* - } - else { - List fields = scope.getRelationType() - .getFields() - .stream() - .filter(f -> f.getSourceColumn().map(c -> !c.isCalculated()).orElse(true)) - .collect(toImmutableList()); - analysis.addCollectedColumns(fields); - scope.getRelationType().getFields().stream().map(field -> - field.getRelationAlias().map(DereferenceExpression::from) - .orElse(DereferenceExpression.from(QualifiedName.of(field.getTableName().getSchemaTableName().getTableName(), field.getColumnName())))) - .forEach(outputExpressions::add); + QualifiedName target = QualifiedName.of(((Identifier) allColumns.getTarget().get()).getValue()); + collectedColumns = collectedColumns + .filter(field -> + field.getRelationAlias().map(ra -> ra.equals(target)).orElse(false) + || field.getTableName().getSchemaTableName().getTableName().equals(target.getParts().getFirst())); + outputExpressionStream = outputExpressionStream + .filter(field -> field.getRelationAlias().map(ra -> ra.equals(target)).orElse(false)); } + + analysis.addCollectedColumns(collectedColumns.collect(toImmutableList())); + outputExpressionStream + .map(field -> + field.getRelationAlias().map(DereferenceExpression::from) + .orElse(DereferenceExpression.from(QualifiedName.of(field.getTableName().getSchemaTableName().getTableName(), field.getColumnName())))) + .forEach(outputExpressions::add); } private void analyzeSelectSingleColumn(SingleColumn singleColumn, Scope scope, ImmutableList.Builder outputExpressions) diff --git a/wren-base/src/test/java/io/wren/base/dto/TestManifestSerDe.java b/wren-base/src/test/java/io/wren/base/dto/TestManifestSerDe.java index 99957f3b3..05cd8a185 100644 --- a/wren-base/src/test/java/io/wren/base/dto/TestManifestSerDe.java +++ b/wren-base/src/test/java/io/wren/base/dto/TestManifestSerDe.java @@ -49,76 +49,69 @@ private static Manifest createManifest() null, null, List.of( - new Column("orderkey", "integer", null, false, true, null, - ImmutableMap.of("description", "the key of each order")), - new Column("custkey", "integer", null, false, true, null, null), - new Column("orderstatus", "string", null, false, true, null, null), - new Column("totalprice", "double", null, false, true, null, null), - new Column("orderdate", "date", null, false, true, null, null), - new Column("orderpriority", "string", null, false, true, null, null), - new Column("clerk", "string", null, false, true, null, null), - new Column("shippriority", "integer", null, false, true, null, null), - new Column("comment", "string", null, false, true, null, null), - new Column("customer", "CustomerModel", "OrdersCustomer", false, true, null, null)), + new Column("orderkey", "integer", null, false, true, null), + new Column("custkey", "integer", null, false, true, null), + new Column("orderstatus", "string", null, false, true, null), + new Column("totalprice", "double", null, false, true, null), + new Column("orderdate", "date", null, false, true, null), + new Column("orderpriority", "string", null, false, true, null), + new Column("clerk", "string", null, false, true, null), + new Column("shippriority", "integer", null, false, true, null), + new Column("comment", "string", null, false, true, null), + new Column("customer", "CustomerModel", "OrdersCustomer", false, true, null)), "orderkey", false, - null, - ImmutableMap.of("description", "tpch tiny orders table")), + null), new Model("LineitemModel", "select * from lineitem", null, null, List.of( - new Column("orderkey", "integer", null, false, true, null, null), - new Column("linenumber", "integer", null, false, true, null, null), - new Column("extendedprice", "integer", null, false, true, null, null)), + new Column("orderkey", "integer", null, false, true, null), + new Column("linenumber", "integer", null, false, true, null), + new Column("extendedprice", "integer", null, false, true, null)), null, false, - null, null), new Model("CustomerModel", null, null, new TableReference("test-catalog", "test-schema", "customer"), List.of( - new Column("custkey", "integer", null, false, true, null, null), - new Column("name", "string", null, false, true, null, null), - new Column("address", "string", null, false, true, null, null), - new Column("nationkey", "integer", null, false, true, null, null), - new Column("phone", "string", null, false, true, null, null), - new Column("acctbal", "double", null, false, true, null, null), - new Column("mktsegment", "string", null, false, true, null, null), - new Column("comment", "string", null, false, true, null, null), - new Column("orders", "OrdersModel", "OrdersCustomer", false, true, null, null), + new Column("custkey", "integer", null, false, true, null), + new Column("name", "string", null, false, true, null), + new Column("address", "string", null, false, true, null), + new Column("nationkey", "integer", null, false, true, null), + new Column("phone", "string", null, false, true, null), + new Column("acctbal", "double", null, false, true, null), + new Column("mktsegment", "string", null, false, true, null), + new Column("comment", "string", null, false, true, null), + new Column("orders", "OrdersModel", "OrdersCustomer", false, true, null), // calculated field - new Column("orders_totalprice", WrenTypes.VARCHAR, null, true, false, "SUM(orders.totalprice)", null)), + new Column("orders_totalprice", WrenTypes.VARCHAR, null, true, false, "SUM(orders.totalprice)")), "custkey", false, - null, null))) .setRelationships(List.of( new Relationship("OrdersCustomer", List.of("OrdersModel", "CustomerModel"), JoinType.MANY_TO_ONE, "OrdersModel.custkey = CustomerModel.custkey", - List.of(new Relationship.SortKey("orderkey", Relationship.SortKey.Ordering.ASC)), - ImmutableMap.of("description", "the relationship between orders and customers")))) + List.of(new Relationship.SortKey("orderkey", Relationship.SortKey.Ordering.ASC))))) .setEnumDefinitions(List.of( new EnumDefinition("OrderStatus", List.of( - new EnumValue("PENDING", "pending", ImmutableMap.of("description", "pending")), - new EnumValue("PROCESSING", "processing", null), - new EnumValue("SHIPPED", "shipped", null), - new EnumValue("COMPLETE", "complete", null)), - ImmutableMap.of("description", "the status of an order")))) + new EnumValue("PENDING", "pending"), + new EnumValue("PROCESSING", "processing"), + new EnumValue("SHIPPED", "shipped"), + new EnumValue("COMPLETE", "complete"))))) .setMetrics(List.of( new Metric("Revenue", "OrdersModel", - List.of(new Column("orderkey", "string", null, false, true, null, null)), - List.of(new Column("total", "integer", null, false, true, null, null)), + List.of(new Column("orderkey", "string", null, false, true, null)), + List.of(new Column("total", "integer", null, false, true, null)), List.of(new TimeGrain("orderdate", "orderdate", List.of(TimeUnit.DAY, TimeUnit.MONTH))), true, - null, - ImmutableMap.of("description", "the revenue of an order")))) + null))) .setViews(List.of( new View("useMetric", "select * from Revenue", @@ -126,20 +119,18 @@ private static Manifest createManifest() .setCumulativeMetrics(List.of( new CumulativeMetric("DailyRevenue", "Orders", - new Measure("totalprice", WrenTypes.INTEGER, "sum", "totalprice", ImmutableMap.of("description", "totalprice")), - new Window("orderdate", "orderdate", TimeUnit.DAY, "1994-01-01", "1994-12-31", ImmutableMap.of("description", "orderdate")), + new Measure("totalprice", WrenTypes.INTEGER, "sum", "totalprice"), + new Window("orderdate", "orderdate", TimeUnit.DAY, "1994-01-01", "1994-12-31"), false, - null, - ImmutableMap.of("description", "daily revenue")), + null), new CumulativeMetric("WeeklyRevenue", "Orders", - new Measure("totalprice", WrenTypes.INTEGER, "sum", "totalprice", null), - new Window("orderdate", "orderdate", TimeUnit.WEEK, "1994-01-01", "1994-12-31", null), + new Measure("totalprice", WrenTypes.INTEGER, "sum", "totalprice"), + new Window("orderdate", "orderdate", TimeUnit.WEEK, "1994-01-01", "1994-12-31"), false, - null, null))) - .setDateSpine(new DateSpine(TimeUnit.DAY, "1970-01-01", "2077-12-31", ImmutableMap.of("description", "a date spine"))) - .setMacros(List.of(new Macro("test", "(a: Expression) => a + 1", ImmutableMap.of("description", "a macro")))) + .setDateSpine(new DateSpine(TimeUnit.DAY, "1970-01-01", "2077-12-31")) + .setMacros(List.of(new Macro("test", "(a: Expression) => a + 1"))) .build(); } diff --git a/wren-base/src/test/java/io/wren/base/sqlrewrite/AbstractTestFramework.java b/wren-base/src/test/java/io/wren/base/sqlrewrite/AbstractTestFramework.java index 351eebb85..ccd8ac98a 100644 --- a/wren-base/src/test/java/io/wren/base/sqlrewrite/AbstractTestFramework.java +++ b/wren-base/src/test/java/io/wren/base/sqlrewrite/AbstractTestFramework.java @@ -60,8 +60,7 @@ public static Model addColumnsToModel(Model model, Column... columns) .build(), model.getPrimaryKey(), model.isCached(), - model.getRefreshTime(), - model.getProperties()); + model.getRefreshTime()); } @BeforeClass diff --git a/wren-base/src/test/java/io/wren/base/sqlrewrite/AbstractTestModel.java b/wren-base/src/test/java/io/wren/base/sqlrewrite/AbstractTestModel.java index abb064c06..9aacb64cf 100644 --- a/wren-base/src/test/java/io/wren/base/sqlrewrite/AbstractTestModel.java +++ b/wren-base/src/test/java/io/wren/base/sqlrewrite/AbstractTestModel.java @@ -113,10 +113,10 @@ public void testToManyCalculated() Model newCustomer = addColumnsToModel( customer, Column.column("orders", "Orders", "OrdersCustomer", true), - Column.caluclatedColumn("totalprice", WrenTypes.BIGINT, "sum(orders.totalprice)"), - Column.caluclatedColumn("buy_item_count", WrenTypes.BIGINT, "count(distinct orders.lineitem.orderkey_linenumber)"), - Column.caluclatedColumn("lineitem_totalprice", WrenTypes.BIGINT, "sum(orders.lineitem.discount * orders.lineitem.extendedprice)"), - Column.caluclatedColumn("test_col", WrenTypes.BIGINT, "sum(orders.lineitem.discount * nationkey)")); + Column.calculatedColumn("totalprice", WrenTypes.BIGINT, "sum(orders.totalprice)"), + Column.calculatedColumn("buy_item_count", WrenTypes.BIGINT, "count(distinct orders.lineitem.orderkey_linenumber)"), + Column.calculatedColumn("lineitem_totalprice", WrenTypes.BIGINT, "sum(orders.lineitem.discount * orders.lineitem.extendedprice)"), + Column.calculatedColumn("test_col", WrenTypes.BIGINT, "sum(orders.lineitem.discount * nationkey)")); Manifest manifest = withDefaultCatalogSchema() .setModels(List.of(newCustomer, orders, lineitem)) .setRelationships(List.of(ordersCustomer, ordersLineitem)) @@ -156,8 +156,8 @@ public void testToOneCalculated() Model newLineitem = addColumnsToModel( lineitem, Column.column("orders", "Orders", "OrdersLineitem", true), - Column.caluclatedColumn("col_1", WrenTypes.BIGINT, "orders.totalprice + orders.totalprice"), - Column.caluclatedColumn("col_2", WrenTypes.BIGINT, "concat(orders.orderkey, '#', orders.customer.custkey)")); + Column.calculatedColumn("col_1", WrenTypes.BIGINT, "orders.totalprice + orders.totalprice"), + Column.calculatedColumn("col_2", WrenTypes.BIGINT, "concat(orders.orderkey, '#', orders.customer.custkey)")); Model newOrders = addColumnsToModel( orders, Column.column("customer", "Customer", "OrdersCustomer", true)); @@ -221,11 +221,11 @@ public void testModelWithCycle() Model newCustomer = addColumnsToModel( customer, Column.column("orders", "Orders", "OrdersCustomer", true), - Column.caluclatedColumn("total_price", WrenTypes.BIGINT, "sum(orders.totalprice)")); + Column.calculatedColumn("total_price", WrenTypes.BIGINT, "sum(orders.totalprice)")); Model newOrders = addColumnsToModel( orders, Column.column("customer", "Customer", "OrdersCustomer", true), - Column.caluclatedColumn("customer_name", WrenTypes.BIGINT, "customer.name")); + Column.calculatedColumn("customer_name", WrenTypes.BIGINT, "customer.name")); Manifest manifest = withDefaultCatalogSchema() .setModels(List.of(newCustomer, newOrders)) .setRelationships(List.of(ordersCustomer, ordersLineitem)) @@ -252,7 +252,7 @@ public void testModelOnModel() Model newCustomer = addColumnsToModel( customer, Column.column("orders", "Orders", "OrdersCustomer", true), - Column.caluclatedColumn("totalprice", WrenTypes.BIGINT, "sum(orders.totalprice)")); + Column.calculatedColumn("totalprice", WrenTypes.BIGINT, "sum(orders.totalprice)")); Model onCustomer = Model.onBaseObject( "OnCustomer", "Customer", @@ -282,17 +282,17 @@ public void testCalculatedUseAnotherCalculated() Model newCustomer = addColumnsToModel( customer, Column.column("orders", "Orders", "OrdersCustomer", true), - Column.caluclatedColumn("total_price", WrenTypes.BIGINT, "sum(orders.totalprice)")); + Column.calculatedColumn("total_price", WrenTypes.BIGINT, "sum(orders.totalprice)")); Model newOrders = addColumnsToModel( orders, Column.column("customer", "Customer", "OrdersCustomer", true), Column.column("lineitem", "Lineitem", "OrdersLineitem", true), - Column.caluclatedColumn("customer_name", WrenTypes.BIGINT, "customer.name"), - Column.caluclatedColumn("extended_price", WrenTypes.BIGINT, "sum(lineitem.extendedprice)")); + Column.calculatedColumn("customer_name", WrenTypes.BIGINT, "customer.name"), + Column.calculatedColumn("extended_price", WrenTypes.BIGINT, "sum(lineitem.extendedprice)")); Model newLineitem = addColumnsToModel( lineitem, Column.column("orders", "Orders", "OrdersLineitem", true), - Column.caluclatedColumn("test_column", WrenTypes.BIGINT, "orders.customer.total_price + extendedprice")); + Column.calculatedColumn("test_column", WrenTypes.BIGINT, "orders.customer.total_price + extendedprice")); Manifest manifest = withDefaultCatalogSchema() .setModels(List.of(newCustomer, newOrders, newLineitem)) .setRelationships(List.of(ordersCustomer, ordersLineitem)) @@ -309,17 +309,17 @@ public void testSelectEmpty() Model newCustomer = addColumnsToModel( customer, Column.column("orders", "Orders", "OrdersCustomer", true), - Column.caluclatedColumn("total_price", WrenTypes.BIGINT, "sum(orders.totalprice)")); + Column.calculatedColumn("total_price", WrenTypes.BIGINT, "sum(orders.totalprice)")); Model newOrders = addColumnsToModel( orders, Column.column("customer", "Customer", "OrdersCustomer", true), Column.column("lineitem", "Lineitem", "OrdersLineitem", true), - Column.caluclatedColumn("customer_name", WrenTypes.BIGINT, "customer.name"), - Column.caluclatedColumn("extended_price", WrenTypes.BIGINT, "sum(lineitem.extendedprice)")); + Column.calculatedColumn("customer_name", WrenTypes.BIGINT, "customer.name"), + Column.calculatedColumn("extended_price", WrenTypes.BIGINT, "sum(lineitem.extendedprice)")); Model newLineitem = addColumnsToModel( lineitem, Column.column("orders", "Orders", "OrdersLineitem", true), - Column.caluclatedColumn("test_column", WrenTypes.BIGINT, "orders.customer.total_price + extendedprice")); + Column.calculatedColumn("test_column", WrenTypes.BIGINT, "orders.customer.total_price + extendedprice")); Manifest manifest = withDefaultCatalogSchema() .setModels(List.of(newCustomer, newOrders, newLineitem)) .setRelationships(List.of(ordersCustomer, ordersLineitem)) @@ -383,7 +383,7 @@ public void testBuildModelFailed() private void buildFailedModel(String refSql, String baseObject, TableReference tableReference) { - new Model("failed", refSql, baseObject, tableReference, null, null, false, null, null); + new Model("failed", refSql, baseObject, tableReference, null, null, false, null); } private void assertQuery(WrenMDL mdl, @Language("SQL") String wrenSql, @Language("SQL") String duckDBSql) diff --git a/wren-base/src/test/java/io/wren/base/sqlrewrite/TestAllRulesRewrite.java b/wren-base/src/test/java/io/wren/base/sqlrewrite/TestAllRulesRewrite.java index a80344a73..f9d44b350 100644 --- a/wren-base/src/test/java/io/wren/base/sqlrewrite/TestAllRulesRewrite.java +++ b/wren-base/src/test/java/io/wren/base/sqlrewrite/TestAllRulesRewrite.java @@ -56,7 +56,7 @@ public TestAllRulesRewrite() Column.relationshipColumn("band", "Band", "AlbumBand"), Column.column("price", WrenTypes.INTEGER, null, true), Column.column("bandId", WrenTypes.INTEGER, null, true), - Column.caluclatedColumn("bandName", WrenTypes.VARCHAR, "band.name"), + Column.calculatedColumn("bandName", WrenTypes.VARCHAR, "band.name"), Column.column("status", "Inventory", null, true), Column.column("statusA", "InventoryA", null, true), Column.relationshipColumn("orders", "Order", "AlbumOrder")), diff --git a/wren-base/src/test/java/io/wren/base/sqlrewrite/TestCumulativeMetric.java b/wren-base/src/test/java/io/wren/base/sqlrewrite/TestCumulativeMetric.java index 424a2733d..dd1060f07 100644 --- a/wren-base/src/test/java/io/wren/base/sqlrewrite/TestCumulativeMetric.java +++ b/wren-base/src/test/java/io/wren/base/sqlrewrite/TestCumulativeMetric.java @@ -75,7 +75,7 @@ public TestCumulativeMetric() CumulativeMetric.cumulativeMetric("YearlyRevenue", "Orders", Measure.measure("totalprice", WrenTypes.INTEGER, "sum", "totalprice"), Window.window("orderdate", "orderdate", TimeUnit.YEAR, "1994-01-01", "1998-12-31")))) - .setDateSpine(new DateSpine(TimeUnit.DAY, "1970-01-01", "2077-12-31", null)) + .setDateSpine(new DateSpine(TimeUnit.DAY, "1970-01-01", "2077-12-31")) .build(); wrenMDL = WrenMDL.fromManifest(manifest); } diff --git a/wren-base/src/test/java/io/wren/base/sqlrewrite/TestMetric.java b/wren-base/src/test/java/io/wren/base/sqlrewrite/TestMetric.java index d92977c02..b8304f8e1 100644 --- a/wren-base/src/test/java/io/wren/base/sqlrewrite/TestMetric.java +++ b/wren-base/src/test/java/io/wren/base/sqlrewrite/TestMetric.java @@ -60,8 +60,8 @@ public TestMetric() Column.column("clerk", WrenTypes.VARCHAR, null, true), Column.column("shippriority", WrenTypes.INTEGER, null, true), Column.column("comment", WrenTypes.VARCHAR, null, true), - Column.caluclatedColumn("customer_name", WrenTypes.VARCHAR, "customer.name"), - Column.caluclatedColumn("cumstomer_address", WrenTypes.VARCHAR, "customer.address"), + Column.calculatedColumn("customer_name", WrenTypes.VARCHAR, "customer.name"), + Column.calculatedColumn("cumstomer_address", WrenTypes.VARCHAR, "customer.address"), Column.column("customer", "Customer", "OrdersCustomer", true), Column.column("lineitem", "Lineitem", "OrdersLineitem", true)), "orderkey"), diff --git a/wren-base/src/test/java/io/wren/base/sqlrewrite/TestWrenDataLineage.java b/wren-base/src/test/java/io/wren/base/sqlrewrite/TestWrenDataLineage.java index c7f194357..67a8d7d68 100644 --- a/wren-base/src/test/java/io/wren/base/sqlrewrite/TestWrenDataLineage.java +++ b/wren-base/src/test/java/io/wren/base/sqlrewrite/TestWrenDataLineage.java @@ -108,20 +108,20 @@ public void testAnalyze() Model newCustomer = addColumnsToModel( customer, Column.column("orders", "Orders", "OrdersCustomer", true), - Column.caluclatedColumn("total_price", WrenTypes.BIGINT, "sum(orders.totalprice)"), - Column.caluclatedColumn("discount_extended_price", WrenTypes.BIGINT, "sum(orders.lineitem.discount + orders.extended_price)"), - Column.caluclatedColumn("lineitem_price", WrenTypes.BIGINT, "sum(orders.lineitem.discount * orders.lineitem.extendedprice)")); + Column.calculatedColumn("total_price", WrenTypes.BIGINT, "sum(orders.totalprice)"), + Column.calculatedColumn("discount_extended_price", WrenTypes.BIGINT, "sum(orders.lineitem.discount + orders.extended_price)"), + Column.calculatedColumn("lineitem_price", WrenTypes.BIGINT, "sum(orders.lineitem.discount * orders.lineitem.extendedprice)")); Model newOrders = addColumnsToModel( orders, Column.column("customer", "Customer", "OrdersCustomer", true), Column.column("lineitem", "Lineitem", "OrdersLineitem", true), - Column.caluclatedColumn("customer_name", WrenTypes.BIGINT, "customer.name"), - Column.caluclatedColumn("extended_price", WrenTypes.BIGINT, "sum(lineitem.extendedprice)"), - Column.caluclatedColumn("extended_price_2", WrenTypes.BIGINT, "sum(lineitem.extendedprice + totalprice)")); + Column.calculatedColumn("customer_name", WrenTypes.BIGINT, "customer.name"), + Column.calculatedColumn("extended_price", WrenTypes.BIGINT, "sum(lineitem.extendedprice)"), + Column.calculatedColumn("extended_price_2", WrenTypes.BIGINT, "sum(lineitem.extendedprice + totalprice)")); Model newLineitem = addColumnsToModel( lineitem, Column.column("orders", "Orders", "OrdersLineitem", true), - Column.caluclatedColumn("test_column", WrenTypes.BIGINT, "orders.customer.total_price + extendedprice")); + Column.calculatedColumn("test_column", WrenTypes.BIGINT, "orders.customer.total_price + extendedprice")); Manifest manifest = withDefaultCatalogSchema() .setModels(List.of(newCustomer, newOrders, newLineitem)) .setRelationships(List.of(ordersCustomer, ordersLineitem)) @@ -203,7 +203,7 @@ public void testAnalyzeModelOnModel() Model newCustomer = addColumnsToModel( customer, Column.column("orders", "Orders", "OrdersCustomer", true), - Column.caluclatedColumn("total_price", WrenTypes.BIGINT, "sum(orders.totalprice)")); + Column.calculatedColumn("total_price", WrenTypes.BIGINT, "sum(orders.totalprice)")); Model onCustomer = Model.onBaseObject( "OnCustomer", "Customer", @@ -215,7 +215,7 @@ public void testAnalyzeModelOnModel() Model newOrders = addColumnsToModel( orders, Column.column("on_customer", "OnCustomer", "OrdersOnCustomer", true), - Column.caluclatedColumn("customer_name", WrenTypes.BIGINT, "on_customer.mom_name")); + Column.calculatedColumn("customer_name", WrenTypes.BIGINT, "on_customer.mom_name")); Relationship ordersOnCustomer = Relationship.relationship("OrdersOnCustomer", List.of("Orders", "OnCustomer"), JoinType.MANY_TO_ONE, "Orders.custkey = OnCustomer.mom_custkey"); Manifest manifest = withDefaultCatalogSchema() .setModels(List.of(newOrders, newCustomer, onCustomer)) @@ -516,7 +516,7 @@ public void testGetSourceColumns() Model newCustomer = addColumnsToModel( customer, Column.column("orders", "Orders", "OrdersCustomer", true), - Column.caluclatedColumn("discount_extended_price", WrenTypes.BIGINT, "sum(orders.lineitem.discount + orders.lineitem.extendedprice)")); + Column.calculatedColumn("discount_extended_price", WrenTypes.BIGINT, "sum(orders.lineitem.discount + orders.lineitem.extendedprice)")); Manifest manifest = withDefaultCatalogSchema() .setModels(List.of(newCustomer, orders, lineitem)) .setRelationships(List.of(ordersCustomer, ordersLineitem)) diff --git a/wren-base/src/test/java/io/wren/base/sqlrewrite/analyzer/TestDecisionPointAnalyzer.java b/wren-base/src/test/java/io/wren/base/sqlrewrite/analyzer/TestDecisionPointAnalyzer.java index 5d90c92a4..8aabd66e6 100644 --- a/wren-base/src/test/java/io/wren/base/sqlrewrite/analyzer/TestDecisionPointAnalyzer.java +++ b/wren-base/src/test/java/io/wren/base/sqlrewrite/analyzer/TestDecisionPointAnalyzer.java @@ -76,7 +76,7 @@ public TestDecisionPointAnalyzer() Column.column("shippriority", WrenTypes.INTEGER, null, true), Column.column("comment", WrenTypes.VARCHAR, null, true), Column.column("customer", "customer", "CustomerOrders", false), - Column.caluclatedColumn("customer_name", WrenTypes.VARCHAR, "customer.name")); + Column.calculatedColumn("customer_name", WrenTypes.VARCHAR, "customer.name")); List lineitemColumns = List.of( Column.column("orderkey", WrenTypes.INTEGER, null, true), Column.column("partkey", WrenTypes.INTEGER, null, true), diff --git a/wren-base/src/test/java/io/wren/base/sqlrewrite/analyzer/TestStatementAnalyzer.java b/wren-base/src/test/java/io/wren/base/sqlrewrite/analyzer/TestStatementAnalyzer.java index baba5046e..c71b323f8 100644 --- a/wren-base/src/test/java/io/wren/base/sqlrewrite/analyzer/TestStatementAnalyzer.java +++ b/wren-base/src/test/java/io/wren/base/sqlrewrite/analyzer/TestStatementAnalyzer.java @@ -37,7 +37,10 @@ import static io.wren.base.CatalogSchemaTableName.catalogSchemaTableName; import static io.wren.base.WrenMDL.EMPTY; import static io.wren.base.WrenMDL.fromManifest; +import static io.wren.base.dto.Column.calculatedColumn; +import static io.wren.base.dto.Column.column; import static io.wren.base.dto.Column.relationshipColumn; +import static io.wren.base.dto.Model.model; import static io.wren.base.dto.Relationship.relationship; import static io.wren.base.sqlrewrite.Utils.parseSql; import static io.wren.base.sqlrewrite.analyzer.StatementAnalyzer.analyze; @@ -235,6 +238,36 @@ public void testScopeWithRelationship() assertThat(analysis.getCollectedColumns().get(catalogSchemaTableName("test", "test", "table_2"))).containsExactly("c1", "c2"); } + @Test + public void testTargetDotAll() + { + Manifest manifest = Manifest.builder() + .setCatalog("test") + .setSchema("test") + .setModels(List.of( + model("Orders", "SELECT * FROM tpch.orders", + List.of(column("orderkey", "integer", null, false, "o_orderkey"), + column("custkey", "integer", null, false, "o_custkey"), + column("customer", "Customer", "CustomerOrders", false), + calculatedColumn("customer_name", "varchar", "customer.name")), + "orderkey"), + model("Customer", "SELECT * FROM tpch.customer", + List.of(column("custkey", "integer", null, false, "c_custkey"), + column("name", "varchar", null, false, "c_name"))))) + .setRelationships(List.of(relationship("CustomerOrders", List.of("Customer", "Orders"), JoinType.ONE_TO_MANY, "Customer.custkey = Orders.custkey"))) + .build(); + List testSqls = ImmutableList.of( + "SELECT Orders.* FROM Orders", + "SELECT o.* FROM Orders AS o", + "SELECT o.* FROM Orders AS o JOIN Customer AS c ON o.custkey = c.custkey"); + for (String sql : testSqls) { + Statement statement = parseSql(sql); + Analysis analysis = new Analysis(statement); + analyze(analysis, statement, DEFAULT_SESSION_CONTEXT, fromManifest(manifest)); + assertThat(analysis.getCollectedColumns().get(catalogSchemaTableName("test", "test", "Orders"))).containsExactly("custkey", "orderkey", "customer"); + } + } + private static Column varcharColumn(String name) { return Column.column(name, "VARCHAR", null, false, null); diff --git a/wren-core-py/Cargo.lock b/wren-core-py/Cargo.lock index 23e07dfd9..6839ba972 100644 --- a/wren-core-py/Cargo.lock +++ b/wren-core-py/Cargo.lock @@ -139,9 +139,9 @@ checksum = "7c02d123df017efcdfbd739ef81735b36c5ba83ec3c59c80a9d7ecc718f92e50" [[package]] name = "arrow" -version = "53.2.0" +version = "53.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4caf25cdc4a985f91df42ed9e9308e1adbcd341a31a72605c697033fcef163e3" +checksum = "c91839b07e474b3995035fd8ac33ee54f9c9ccbbb1ea33d9909c71bffdf1259d" dependencies = [ "arrow-arith", "arrow-array", @@ -160,9 +160,9 @@ dependencies = [ [[package]] name = "arrow-arith" -version = "53.2.0" +version = "53.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "91f2dfd1a7ec0aca967dfaa616096aec49779adc8eccec005e2f5e4111b1192a" +checksum = "855c57c4efd26722b044dcd3e348252560e3e0333087fb9f6479dc0bf744054f" dependencies = [ "arrow-array", "arrow-buffer", @@ -175,9 +175,9 @@ dependencies = [ [[package]] name = "arrow-array" -version = "53.2.0" +version = "53.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d39387ca628be747394890a6e47f138ceac1aa912eab64f02519fed24b637af8" +checksum = "bd03279cea46569acf9295f6224fbc370c5df184b4d2ecfe97ccb131d5615a7f" dependencies = [ "ahash", "arrow-buffer", @@ -186,15 +186,15 @@ dependencies = [ "chrono", "chrono-tz", "half", - "hashbrown 0.14.5", + "hashbrown 0.15.1", "num", ] [[package]] name = "arrow-buffer" -version = "53.2.0" +version = "53.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9e51e05228852ffe3eb391ce7178a0f97d2cf80cc6ef91d3c4a6b3cb688049ec" +checksum = "9e4a9b9b1d6d7117f6138e13bc4dd5daa7f94e671b70e8c9c4dc37b4f5ecfc16" dependencies = [ "bytes", "half", @@ -203,9 +203,9 @@ dependencies = [ [[package]] name = "arrow-cast" -version = "53.2.0" +version = "53.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d09aea56ec9fa267f3f3f6cdab67d8a9974cbba90b3aa38c8fe9d0bb071bd8c1" +checksum = "bc70e39916e60c5b7af7a8e2719e3ae589326039e1e863675a008bee5ffe90fd" dependencies = [ "arrow-array", "arrow-buffer", @@ -224,9 +224,9 @@ dependencies = [ [[package]] name = "arrow-csv" -version = "53.2.0" +version = "53.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c07b5232be87d115fde73e32f2ca7f1b353bff1b44ac422d3c6fc6ae38f11f0d" +checksum = "789b2af43c1049b03a8d088ff6b2257cdcea1756cd76b174b1f2600356771b97" dependencies = [ "arrow-array", "arrow-buffer", @@ -243,9 +243,9 @@ dependencies = [ [[package]] name = "arrow-data" -version = "53.2.0" +version = "53.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b98ae0af50890b494cebd7d6b04b35e896205c1d1df7b29a6272c5d0d0249ef5" +checksum = "e4e75edf21ffd53744a9b8e3ed11101f610e7ceb1a29860432824f1834a1f623" dependencies = [ "arrow-buffer", "arrow-schema", @@ -255,9 +255,9 @@ dependencies = [ [[package]] name = "arrow-ipc" -version = "53.2.0" +version = "53.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0ed91bdeaff5a1c00d28d8f73466bcb64d32bbd7093b5a30156b4b9f4dba3eee" +checksum = "d186a909dece9160bf8312f5124d797884f608ef5435a36d9d608e0b2a9bcbf8" dependencies = [ "arrow-array", "arrow-buffer", @@ -270,9 +270,9 @@ dependencies = [ [[package]] name = "arrow-json" -version = "53.2.0" +version = "53.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0471f51260a5309307e5d409c9dc70aede1cd9cf1d4ff0f0a1e8e1a2dd0e0d3c" +checksum = "b66ff2fedc1222942d0bd2fd391cb14a85baa3857be95c9373179bd616753b85" dependencies = [ "arrow-array", "arrow-buffer", @@ -290,9 +290,9 @@ dependencies = [ [[package]] name = "arrow-ord" -version = "53.2.0" +version = "53.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2883d7035e0b600fb4c30ce1e50e66e53d8656aa729f2bfa4b51d359cf3ded52" +checksum = "ece7b5bc1180e6d82d1a60e1688c199829e8842e38497563c3ab6ea813e527fd" dependencies = [ "arrow-array", "arrow-buffer", @@ -305,9 +305,9 @@ dependencies = [ [[package]] name = "arrow-row" -version = "53.2.0" +version = "53.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "552907e8e587a6fde4f8843fd7a27a576a260f65dab6c065741ea79f633fc5be" +checksum = "745c114c8f0e8ce211c83389270de6fbe96a9088a7b32c2a041258a443fe83ff" dependencies = [ "ahash", "arrow-array", @@ -319,15 +319,15 @@ dependencies = [ [[package]] name = "arrow-schema" -version = "53.2.0" +version = "53.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "539ada65246b949bd99ffa0881a9a15a4a529448af1a07a9838dd78617dafab1" +checksum = "b95513080e728e4cec37f1ff5af4f12c9688d47795d17cda80b6ec2cf74d4678" [[package]] name = "arrow-select" -version = "53.2.0" +version = "53.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6259e566b752da6dceab91766ed8b2e67bf6270eb9ad8a6e07a33c1bede2b125" +checksum = "8e415279094ea70323c032c6e739c48ad8d80e78a09bef7117b8718ad5bf3722" dependencies = [ "ahash", "arrow-array", @@ -339,9 +339,9 @@ dependencies = [ [[package]] name = "arrow-string" -version = "53.2.0" +version = "53.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f3179ccbd18ebf04277a095ba7321b93fd1f774f18816bd5f6b3ce2f594edb6c" +checksum = "11d956cae7002eb8d83a27dbd34daaea1cf5b75852f0b84deb4d93a276e92bbf" dependencies = [ "arrow-array", "arrow-buffer", @@ -734,8 +734,8 @@ dependencies = [ [[package]] name = "datafusion" -version = "42.2.0" -source = "git+https://github.com/apache/datafusion.git?rev=7c6f891b4b5a007e29fb3890ed5315ef916ae1d3#7c6f891b4b5a007e29fb3890ed5315ef916ae1d3" +version = "43.0.0" +source = "git+https://github.com/goldmedal/datafusion.git?branch=wren/support-array-struct#1862366b3543b7522e0601a7e29a7bd97adc599c" dependencies = [ "ahash", "arrow", @@ -790,8 +790,8 @@ dependencies = [ [[package]] name = "datafusion-catalog" -version = "42.2.0" -source = "git+https://github.com/apache/datafusion.git?rev=7c6f891b4b5a007e29fb3890ed5315ef916ae1d3#7c6f891b4b5a007e29fb3890ed5315ef916ae1d3" +version = "43.0.0" +source = "git+https://github.com/goldmedal/datafusion.git?branch=wren/support-array-struct#1862366b3543b7522e0601a7e29a7bd97adc599c" dependencies = [ "arrow-schema", "async-trait", @@ -804,8 +804,8 @@ dependencies = [ [[package]] name = "datafusion-common" -version = "42.2.0" -source = "git+https://github.com/apache/datafusion.git?rev=7c6f891b4b5a007e29fb3890ed5315ef916ae1d3#7c6f891b4b5a007e29fb3890ed5315ef916ae1d3" +version = "43.0.0" +source = "git+https://github.com/goldmedal/datafusion.git?branch=wren/support-array-struct#1862366b3543b7522e0601a7e29a7bd97adc599c" dependencies = [ "ahash", "arrow", @@ -816,29 +816,35 @@ dependencies = [ "half", "hashbrown 0.14.5", "indexmap 2.6.0", - "instant", "libc", "num_cpus", "object_store", "parquet", "paste", + "recursive", "sqlparser", "tokio", + "web-time", ] [[package]] name = "datafusion-common-runtime" -version = "42.2.0" -source = "git+https://github.com/apache/datafusion.git?rev=7c6f891b4b5a007e29fb3890ed5315ef916ae1d3#7c6f891b4b5a007e29fb3890ed5315ef916ae1d3" +version = "43.0.0" +source = "git+https://github.com/goldmedal/datafusion.git?branch=wren/support-array-struct#1862366b3543b7522e0601a7e29a7bd97adc599c" dependencies = [ "log", "tokio", ] +[[package]] +name = "datafusion-doc" +version = "43.0.0" +source = "git+https://github.com/goldmedal/datafusion.git?branch=wren/support-array-struct#1862366b3543b7522e0601a7e29a7bd97adc599c" + [[package]] name = "datafusion-execution" -version = "42.2.0" -source = "git+https://github.com/apache/datafusion.git?rev=7c6f891b4b5a007e29fb3890ed5315ef916ae1d3#7c6f891b4b5a007e29fb3890ed5315ef916ae1d3" +version = "43.0.0" +source = "git+https://github.com/goldmedal/datafusion.git?branch=wren/support-array-struct#1862366b3543b7522e0601a7e29a7bd97adc599c" dependencies = [ "arrow", "chrono", @@ -857,8 +863,8 @@ dependencies = [ [[package]] name = "datafusion-expr" -version = "42.2.0" -source = "git+https://github.com/apache/datafusion.git?rev=7c6f891b4b5a007e29fb3890ed5315ef916ae1d3#7c6f891b4b5a007e29fb3890ed5315ef916ae1d3" +version = "43.0.0" +source = "git+https://github.com/goldmedal/datafusion.git?branch=wren/support-array-struct#1862366b3543b7522e0601a7e29a7bd97adc599c" dependencies = [ "ahash", "arrow", @@ -866,12 +872,14 @@ dependencies = [ "arrow-buffer", "chrono", "datafusion-common", + "datafusion-doc", "datafusion-expr-common", "datafusion-functions-aggregate-common", "datafusion-functions-window-common", "datafusion-physical-expr-common", "indexmap 2.6.0", "paste", + "recursive", "serde_json", "sqlparser", "strum", @@ -880,8 +888,8 @@ dependencies = [ [[package]] name = "datafusion-expr-common" -version = "42.2.0" -source = "git+https://github.com/apache/datafusion.git?rev=7c6f891b4b5a007e29fb3890ed5315ef916ae1d3#7c6f891b4b5a007e29fb3890ed5315ef916ae1d3" +version = "43.0.0" +source = "git+https://github.com/goldmedal/datafusion.git?branch=wren/support-array-struct#1862366b3543b7522e0601a7e29a7bd97adc599c" dependencies = [ "arrow", "datafusion-common", @@ -891,8 +899,8 @@ dependencies = [ [[package]] name = "datafusion-functions" -version = "42.2.0" -source = "git+https://github.com/apache/datafusion.git?rev=7c6f891b4b5a007e29fb3890ed5315ef916ae1d3#7c6f891b4b5a007e29fb3890ed5315ef916ae1d3" +version = "43.0.0" +source = "git+https://github.com/goldmedal/datafusion.git?branch=wren/support-array-struct#1862366b3543b7522e0601a7e29a7bd97adc599c" dependencies = [ "arrow", "arrow-buffer", @@ -901,8 +909,10 @@ dependencies = [ "blake3", "chrono", "datafusion-common", + "datafusion-doc", "datafusion-execution", "datafusion-expr", + "datafusion-macros", "hashbrown 0.14.5", "hex", "itertools", @@ -917,8 +927,8 @@ dependencies = [ [[package]] name = "datafusion-functions-aggregate" -version = "42.2.0" -source = "git+https://github.com/apache/datafusion.git?rev=7c6f891b4b5a007e29fb3890ed5315ef916ae1d3#7c6f891b4b5a007e29fb3890ed5315ef916ae1d3" +version = "43.0.0" +source = "git+https://github.com/goldmedal/datafusion.git?branch=wren/support-array-struct#1862366b3543b7522e0601a7e29a7bd97adc599c" dependencies = [ "ahash", "arrow", @@ -937,8 +947,8 @@ dependencies = [ [[package]] name = "datafusion-functions-aggregate-common" -version = "42.2.0" -source = "git+https://github.com/apache/datafusion.git?rev=7c6f891b4b5a007e29fb3890ed5315ef916ae1d3#7c6f891b4b5a007e29fb3890ed5315ef916ae1d3" +version = "43.0.0" +source = "git+https://github.com/goldmedal/datafusion.git?branch=wren/support-array-struct#1862366b3543b7522e0601a7e29a7bd97adc599c" dependencies = [ "ahash", "arrow", @@ -950,8 +960,8 @@ dependencies = [ [[package]] name = "datafusion-functions-nested" -version = "42.2.0" -source = "git+https://github.com/apache/datafusion.git?rev=7c6f891b4b5a007e29fb3890ed5315ef916ae1d3#7c6f891b4b5a007e29fb3890ed5315ef916ae1d3" +version = "43.0.0" +source = "git+https://github.com/goldmedal/datafusion.git?branch=wren/support-array-struct#1862366b3543b7522e0601a7e29a7bd97adc599c" dependencies = [ "arrow", "arrow-array", @@ -972,8 +982,8 @@ dependencies = [ [[package]] name = "datafusion-functions-window" -version = "42.2.0" -source = "git+https://github.com/apache/datafusion.git?rev=7c6f891b4b5a007e29fb3890ed5315ef916ae1d3#7c6f891b4b5a007e29fb3890ed5315ef916ae1d3" +version = "43.0.0" +source = "git+https://github.com/goldmedal/datafusion.git?branch=wren/support-array-struct#1862366b3543b7522e0601a7e29a7bd97adc599c" dependencies = [ "datafusion-common", "datafusion-expr", @@ -986,17 +996,28 @@ dependencies = [ [[package]] name = "datafusion-functions-window-common" -version = "42.2.0" -source = "git+https://github.com/apache/datafusion.git?rev=7c6f891b4b5a007e29fb3890ed5315ef916ae1d3#7c6f891b4b5a007e29fb3890ed5315ef916ae1d3" +version = "43.0.0" +source = "git+https://github.com/goldmedal/datafusion.git?branch=wren/support-array-struct#1862366b3543b7522e0601a7e29a7bd97adc599c" dependencies = [ "datafusion-common", "datafusion-physical-expr-common", ] +[[package]] +name = "datafusion-macros" +version = "43.0.0" +source = "git+https://github.com/goldmedal/datafusion.git?branch=wren/support-array-struct#1862366b3543b7522e0601a7e29a7bd97adc599c" +dependencies = [ + "datafusion-doc", + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "datafusion-optimizer" -version = "42.2.0" -source = "git+https://github.com/apache/datafusion.git?rev=7c6f891b4b5a007e29fb3890ed5315ef916ae1d3#7c6f891b4b5a007e29fb3890ed5315ef916ae1d3" +version = "43.0.0" +source = "git+https://github.com/goldmedal/datafusion.git?branch=wren/support-array-struct#1862366b3543b7522e0601a7e29a7bd97adc599c" dependencies = [ "arrow", "async-trait", @@ -1009,13 +1030,15 @@ dependencies = [ "itertools", "log", "paste", + "recursive", + "regex", "regex-syntax", ] [[package]] name = "datafusion-physical-expr" -version = "42.2.0" -source = "git+https://github.com/apache/datafusion.git?rev=7c6f891b4b5a007e29fb3890ed5315ef916ae1d3#7c6f891b4b5a007e29fb3890ed5315ef916ae1d3" +version = "43.0.0" +source = "git+https://github.com/goldmedal/datafusion.git?branch=wren/support-array-struct#1862366b3543b7522e0601a7e29a7bd97adc599c" dependencies = [ "ahash", "arrow", @@ -1041,8 +1064,8 @@ dependencies = [ [[package]] name = "datafusion-physical-expr-common" -version = "42.2.0" -source = "git+https://github.com/apache/datafusion.git?rev=7c6f891b4b5a007e29fb3890ed5315ef916ae1d3#7c6f891b4b5a007e29fb3890ed5315ef916ae1d3" +version = "43.0.0" +source = "git+https://github.com/goldmedal/datafusion.git?branch=wren/support-array-struct#1862366b3543b7522e0601a7e29a7bd97adc599c" dependencies = [ "ahash", "arrow", @@ -1055,23 +1078,26 @@ dependencies = [ [[package]] name = "datafusion-physical-optimizer" -version = "42.2.0" -source = "git+https://github.com/apache/datafusion.git?rev=7c6f891b4b5a007e29fb3890ed5315ef916ae1d3#7c6f891b4b5a007e29fb3890ed5315ef916ae1d3" +version = "43.0.0" +source = "git+https://github.com/goldmedal/datafusion.git?branch=wren/support-array-struct#1862366b3543b7522e0601a7e29a7bd97adc599c" dependencies = [ "arrow", "arrow-schema", "datafusion-common", "datafusion-execution", "datafusion-expr-common", + "datafusion-optimizer", "datafusion-physical-expr", "datafusion-physical-plan", "itertools", + "log", + "recursive", ] [[package]] name = "datafusion-physical-plan" -version = "42.2.0" -source = "git+https://github.com/apache/datafusion.git?rev=7c6f891b4b5a007e29fb3890ed5315ef916ae1d3#7c6f891b4b5a007e29fb3890ed5315ef916ae1d3" +version = "43.0.0" +source = "git+https://github.com/goldmedal/datafusion.git?branch=wren/support-array-struct#1862366b3543b7522e0601a7e29a7bd97adc599c" dependencies = [ "ahash", "arrow", @@ -1104,8 +1130,8 @@ dependencies = [ [[package]] name = "datafusion-sql" -version = "42.2.0" -source = "git+https://github.com/apache/datafusion.git?rev=7c6f891b4b5a007e29fb3890ed5315ef916ae1d3#7c6f891b4b5a007e29fb3890ed5315ef916ae1d3" +version = "43.0.0" +source = "git+https://github.com/goldmedal/datafusion.git?branch=wren/support-array-struct#1862366b3543b7522e0601a7e29a7bd97adc599c" dependencies = [ "arrow", "arrow-array", @@ -1114,6 +1140,7 @@ dependencies = [ "datafusion-expr", "indexmap 2.6.0", "log", + "recursive", "regex", "sqlparser", "strum", @@ -1624,18 +1651,6 @@ version = "2.0.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b248f5224d1d606005e02c97f5aa4e88eeb230488bcc03bc9ca4d7991399f2b5" -[[package]] -name = "instant" -version = "0.1.13" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e0242819d153cba4b4b05a5a8f2a7e9bbf97b6055b2a002b395c96b5ff3c0222" -dependencies = [ - "cfg-if", - "js-sys", - "wasm-bindgen", - "web-sys", -] - [[package]] name = "integer-encoding" version = "3.0.4" @@ -2005,9 +2020,9 @@ dependencies = [ [[package]] name = "parquet" -version = "53.2.0" +version = "53.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dea02606ba6f5e856561d8d507dba8bac060aefca2a6c0f1aa1d361fed91ff3e" +checksum = "2b449890367085eb65d7d3321540abc3d7babbd179ce31df0016e90719114191" dependencies = [ "ahash", "arrow-array", @@ -2024,7 +2039,7 @@ dependencies = [ "flate2", "futures", "half", - "hashbrown 0.14.5", + "hashbrown 0.15.1", "lz4_flex", "num", "num-bigint", @@ -2166,6 +2181,15 @@ dependencies = [ "unicode-ident", ] +[[package]] +name = "psm" +version = "0.1.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "200b9ff220857e53e184257720a14553b2f4aa02577d2ed9842d45d4b9654810" +dependencies = [ + "cc", +] + [[package]] name = "pyo3" version = "0.21.2" @@ -2278,6 +2302,26 @@ dependencies = [ "getrandom", ] +[[package]] +name = "recursive" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0786a43debb760f491b1bc0269fe5e84155353c67482b9e60d0cfb596054b43e" +dependencies = [ + "recursive-proc-macro-impl", + "stacker", +] + +[[package]] +name = "recursive-proc-macro-impl" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "76009fbe0614077fc1a2ce255e3a1881a2e3a3527097d5dc6d8212c585e7e38b" +dependencies = [ + "quote", + "syn", +] + [[package]] name = "redox_syscall" version = "0.5.7" @@ -2512,9 +2556,8 @@ checksum = "1b6b67fb9a61334225b5b790716f609cd58395f895b3fe8b328786812a40bc3b" [[package]] name = "sqlparser" -version = "0.51.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5fe11944a61da0da3f592e19a45ebe5ab92dc14a779907ff1f08fbb797bfefc7" +version = "0.52.0" +source = "git+https://github.com/goldmedal/sqlparser-rs.git?branch=wren/0.12.3-array-struct#bb1833fe81508ac84be0b757d0b52394f3c97a6f" dependencies = [ "log", "sqlparser_derive", @@ -2523,8 +2566,7 @@ dependencies = [ [[package]] name = "sqlparser_derive" version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "01b2e185515564f15375f593fb966b5718bc624ba77fe49fa4616ad619690554" +source = "git+https://github.com/goldmedal/sqlparser-rs.git?branch=wren/0.12.3-array-struct#bb1833fe81508ac84be0b757d0b52394f3c97a6f" dependencies = [ "proc-macro2", "quote", @@ -2537,6 +2579,19 @@ version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3" +[[package]] +name = "stacker" +version = "0.1.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "799c883d55abdb5e98af1a7b3f23b9b6de8ecada0ecac058672d7635eb48ca7b" +dependencies = [ + "cc", + "cfg-if", + "libc", + "psm", + "windows-sys 0.52.0", +] + [[package]] name = "static_assertions" version = "1.1.0" @@ -2922,10 +2977,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "65fc09f10666a9f147042251e0dda9c18f166ff7de300607007e96bdebc1068d" [[package]] -name = "web-sys" -version = "0.3.72" +name = "web-time" +version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f6488b90108c040df0fe62fa815cbdee25124641df01814dd7282749234c6112" +checksum = "5a6580f308b1fad9207618087a65c04e7a10bc77e02c8e84e9b00dd4b12fa0bb" dependencies = [ "js-sys", "wasm-bindgen", diff --git a/wren-core-py/src/context.rs b/wren-core-py/src/context.rs index 2f0c32e8c..32ce7bd67 100644 --- a/wren-core-py/src/context.rs +++ b/wren-core-py/src/context.rs @@ -106,10 +106,11 @@ impl PySessionContext { .block_on(create_ctx_with_mdl(&ctx, Arc::clone(&analyzed_mdl), false)) .map_err(CoreError::from)?; - remote_functions.iter().for_each(|remote_function| { + remote_functions.iter().try_for_each(|remote_function| { debug!("Registering remote function: {:?}", remote_function); - Self::register_remote_function(&ctx, remote_function); - }); + Self::register_remote_function(&ctx, remote_function)?; + Ok::<(), CoreError>(()) + })?; Ok(Self { ctx, @@ -199,27 +200,31 @@ impl PySessionContext { fn register_remote_function( ctx: &wren_core::SessionContext, remote_function: &RemoteFunction, - ) { + ) -> PyResult<()> { match &remote_function.function_type { FunctionType::Scalar => { ctx.register_udf(ScalarUDF::new_from_impl(ByPassScalarUDF::new( &remote_function.name, - map_data_type(&remote_function.return_type), + map_data_type(&remote_function.return_type) + .map_err(CoreError::from)?, ))) } FunctionType::Aggregate => { ctx.register_udaf(AggregateUDF::new_from_impl(ByPassAggregateUDF::new( &remote_function.name, - map_data_type(&remote_function.return_type), + map_data_type(&remote_function.return_type) + .map_err(CoreError::from)?, ))) } FunctionType::Window => { ctx.register_udwf(WindowUDF::new_from_impl(ByPassWindowFunction::new( &remote_function.name, - map_data_type(&remote_function.return_type), + map_data_type(&remote_function.return_type) + .map_err(CoreError::from)?, ))) } } + Ok(()) } fn read_remote_function_list(path: Option<&str>) -> PyResult> { diff --git a/wren-core-py/src/remote_functions.rs b/wren-core-py/src/remote_functions.rs index 9ba07d380..2d96f0fe0 100644 --- a/wren-core-py/src/remote_functions.rs +++ b/wren-core-py/src/remote_functions.rs @@ -28,8 +28,10 @@ pub struct PyRemoteFunction { pub function_type: String, pub name: String, pub return_type: Option, - pub param_names: Option>, - pub param_types: Option>, + /// It's a comma separated string of parameter names + pub param_names: Option, + /// It's a comma separated string of parameter types + pub param_types: Option, pub description: Option, } @@ -54,12 +56,26 @@ impl PyRemoteFunction { impl From for PyRemoteFunction { fn from(remote_function: wren_core::mdl::function::RemoteFunction) -> Self { + let param_names = remote_function.param_names.map(|names| { + names + .iter() + .map(|name| name.to_string()) + .collect::>() + .join(",") + }); + let param_types = remote_function.param_types.map(|types| { + types + .iter() + .map(|t| t.to_string()) + .collect::>() + .join(",") + }); Self { function_type: remote_function.function_type.to_string(), name: remote_function.name, return_type: Some(remote_function.return_type), - param_names: remote_function.param_names, - param_types: remote_function.param_types, + param_names, + param_types, description: remote_function.description, } } @@ -69,14 +85,26 @@ impl From for wren_core::mdl::function::RemoteFunction { fn from( remote_function: PyRemoteFunction, ) -> wren_core::mdl::function::RemoteFunction { + let param_names = remote_function.param_names.map(|names| { + names + .split(",") + .map(|name| name.to_string()) + .collect::>() + }); + let param_types = remote_function.param_types.map(|types| { + types + .split(",") + .map(|t| t.to_string()) + .collect::>() + }); wren_core::mdl::function::RemoteFunction { function_type: FunctionType::from_str(&remote_function.function_type) .unwrap(), name: remote_function.name, // TODO: Get the return type form DataFusion SessionState return_type: remote_function.return_type.unwrap_or("string".to_string()), - param_names: remote_function.param_names, - param_types: remote_function.param_types, + param_names, + param_types, description: remote_function.description, } } diff --git a/wren-core-py/tests/functions.csv b/wren-core-py/tests/functions.csv index 33d302c19..cf9464767 100644 --- a/wren-core-py/tests/functions.csv +++ b/wren-core-py/tests/functions.csv @@ -1,3 +1,3 @@ -function_type,name,return_type,description -scalar,add_two,int,"Adds two numbers together." -window,max_if,int,"If the condition is true, returns the maximum value in the window." +function_type,name,return_type,param_names,param_types,description +scalar,add_two,int,"f1,f2","int,int","Adds two numbers together." +window,max_if,int,,,"If the condition is true, returns the maximum value in the window." diff --git a/wren-core-py/tests/test_modeling_core.py b/wren-core-py/tests/test_modeling_core.py index 307ad620b..f891b6941 100644 --- a/wren-core-py/tests/test_modeling_core.py +++ b/wren-core-py/tests/test_modeling_core.py @@ -46,14 +46,14 @@ def test_read_function_list(): path = "tests/functions.csv" session_context = SessionContext(manifest_str, path) functions = session_context.get_available_functions() - assert len(functions) == 271 + assert len(functions) == 272 rewritten_sql = session_context.transform_sql("SELECT add_two(c_custkey) FROM my_catalog.my_schema.customer") assert rewritten_sql == 'SELECT add_two(customer.c_custkey) FROM (SELECT customer.c_custkey FROM (SELECT customer.c_custkey AS c_custkey FROM main.customer) AS customer) AS customer' session_context = SessionContext(manifest_str, None) functions = session_context.get_available_functions() - assert len(functions) == 269 + assert len(functions) == 270 def test_get_available_functions(): @@ -63,3 +63,13 @@ def test_get_available_functions(): assert add_two["name"] == "add_two" assert add_two["function_type"] == "scalar" assert add_two["description"] == "Adds two numbers together." + assert add_two["return_type"] == "int" + assert add_two["param_names"] == "f1,f2" + assert add_two["param_types"] == "int,int" + + max_if = next(filter(lambda x: x["name"] == "max_if", map(lambda x: x.to_dict(), functions))) + assert max_if["name"] == "max_if" + assert max_if["function_type"] == "window" + assert max_if["param_names"] is None + assert max_if["param_types"] is None + diff --git a/wren-core/Cargo.toml b/wren-core/Cargo.toml index a705546ff..5ff81c851 100644 --- a/wren-core/Cargo.toml +++ b/wren-core/Cargo.toml @@ -16,8 +16,11 @@ version = "0.1.0" async-trait = "0.1.80" # We require the following commits # https://github.com/apache/datafusion/pull/13241 -datafusion = { git = "https://github.com/apache/datafusion.git", rev = "7c6f891b4b5a007e29fb3890ed5315ef916ae1d3" } +# https://github.com/goldmedal/datafusion/pull/1 +# https://github.com/goldmedal/sqlparser-rs/pull/1 +datafusion = { git = "https://github.com/goldmedal/datafusion.git", branch = "wren/support-array-struct" } env_logger = "0.11.3" +hashbrown = "0.15.2" log = { version = "0.4.14" } serde = { version = "1.0.201", features = ["derive", "rc"] } serde_json = { version = "1.0.117" } diff --git a/wren-core/core/src/logical_plan/analyze/plan.rs b/wren-core/core/src/logical_plan/analyze/plan.rs index d7511bcd9..a114fc768 100644 --- a/wren-core/core/src/logical_plan/analyze/plan.rs +++ b/wren-core/core/src/logical_plan/analyze/plan.rs @@ -227,7 +227,7 @@ impl ModelPlanNodeBuilder { Some(TableReference::bare(quoted(model.name()))), Arc::new(Field::new( column.name(), - map_data_type(&column.r#type), + map_data_type(&column.r#type)?, column.not_null, )), )); @@ -735,7 +735,7 @@ impl ModelSourceNode { Some(TableReference::bare(quoted(model.name()))), Arc::new(Field::new( column.name(), - map_data_type(&column.r#type), + map_data_type(&column.r#type)?, column.not_null, )), )); @@ -775,7 +775,7 @@ impl ModelSourceNode { Some(TableReference::bare(quoted(model.name()))), Arc::new(Field::new( column.name(), - map_data_type(&column.r#type), + map_data_type(&column.r#type)?, column.not_null, )), )); @@ -869,12 +869,12 @@ impl CalculationPlanNode { let output_field = vec![ Arc::new(Field::new( calculation.column.name(), - map_data_type(&calculation.column.r#type), + map_data_type(&calculation.column.r#type)?, calculation.column.not_null, )), Arc::new(Field::new( pk_column.name(), - map_data_type(&pk_column.r#type), + map_data_type(&pk_column.r#type)?, pk_column.not_null, )), ] diff --git a/wren-core/core/src/logical_plan/mod.rs b/wren-core/core/src/logical_plan/mod.rs index 73d99608a..fc8a26e7a 100644 --- a/wren-core/core/src/logical_plan/mod.rs +++ b/wren-core/core/src/logical_plan/mod.rs @@ -1,3 +1,4 @@ pub mod analyze; pub mod context_provider; +pub mod optimize; pub mod utils; diff --git a/wren-core/core/src/logical_plan/optimize/mod.rs b/wren-core/core/src/logical_plan/optimize/mod.rs new file mode 100644 index 000000000..d6afc3d58 --- /dev/null +++ b/wren-core/core/src/logical_plan/optimize/mod.rs @@ -0,0 +1,20 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +pub mod simplify_timestamp; diff --git a/wren-core/core/src/logical_plan/optimize/simplify_timestamp.rs b/wren-core/core/src/logical_plan/optimize/simplify_timestamp.rs new file mode 100644 index 000000000..b29c2f517 --- /dev/null +++ b/wren-core/core/src/logical_plan/optimize/simplify_timestamp.rs @@ -0,0 +1,176 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +use datafusion::arrow::datatypes::{DataType, TimeUnit}; +use datafusion::common::tree_node::{ + Transformed, TransformedResult, TreeNode, TreeNodeRewriter, +}; +use datafusion::common::ScalarValue::{ + TimestampMicrosecond, TimestampMillisecond, TimestampSecond, +}; +use datafusion::common::{DFSchema, DFSchemaRef, Result, ScalarValue}; +use datafusion::config::ConfigOptions; +use datafusion::execution::context::ExecutionProps; +use datafusion::logical_expr::expr_rewriter::NamePreserver; +use datafusion::logical_expr::simplify::SimplifyContext; +use datafusion::logical_expr::utils::merge_schema; +use datafusion::logical_expr::{cast, Cast, LogicalPlan, TryCast}; +use datafusion::optimizer::simplify_expressions::ExprSimplifier; +use datafusion::optimizer::AnalyzerRule; +use datafusion::prelude::Expr; +use datafusion::scalar::ScalarValue::TimestampNanosecond; +use std::sync::Arc; + +/// Simplify the casting to [DataType::Timestamp] expression in the logical plan. +/// It's modified from [datafusion::optimizer::simplify_expressions::SimplifyExpressions]. +/// Only the `Expr::Cast` with [DataType::Timestamp] is handled here. +#[derive(Debug, Default)] +pub struct TimestampSimplify {} + +impl TimestampSimplify { + pub fn new() -> Self { + Self {} + } +} + +impl AnalyzerRule for TimestampSimplify { + fn analyze(&self, plan: LogicalPlan, _config: &ConfigOptions) -> Result { + Self::analyze_internal(plan).data() + } + + fn name(&self) -> &str { + "simplify_timestamp_expressions" + } +} + +impl TimestampSimplify { + fn analyze_internal(plan: LogicalPlan) -> Result> { + let schema = if !plan.inputs().is_empty() { + DFSchemaRef::new(merge_schema(&plan.inputs())) + } else if let LogicalPlan::TableScan(scan) = &plan { + // When predicates are pushed into a table scan, there is no input + // schema to resolve predicates against, so it must be handled specially + // + // Note that this is not `plan.schema()` which is the *output* + // schema, and reflects any pushed down projection. The output schema + // will not contain columns that *only* appear in pushed down predicates + // (and no where else) in the plan. + // + // Thus, use the full schema of the inner provider without any + // projection applied for simplification + Arc::new(DFSchema::try_from_qualified_schema( + scan.table_name.clone(), + &scan.source.schema(), + )?) + } else { + Arc::new(DFSchema::empty()) + }; + let execution_props = ExecutionProps::default(); + let info = SimplifyContext::new(&execution_props).with_schema(schema); + + // Inputs have already been rewritten (due to bottom-up traversal handled by Optimizer) + // Just need to rewrite our own expressions + + let simplifier = ExprSimplifier::new(info); + + // The left and right expressions in a Join on clause are not + // commutative, for reasons that are not entirely clear. Thus, do not + // reorder expressions in Join while simplifying. + // + // This is likely related to the fact that order of the columns must + // match the order of the children. see + // https://github.com/apache/datafusion/pull/8780 for more details + let simplifier = if let LogicalPlan::Join(_) = plan { + simplifier.with_canonicalize(false) + } else { + simplifier + }; + + // Preserve expression names to avoid changing the schema of the plan. + let name_preserver = NamePreserver::new(&plan); + let mut rewriter = ExprRewriter { + simplifier: &simplifier, + name_preserver, + }; + plan.map_expressions(|e| e.rewrite(&mut rewriter)) + } +} + +/// Rewriter for simplifying expressions in the logical plan. +/// Try to evaluate the expression and replace it with a constant if possible. +struct ExprRewriter<'a> { + simplifier: &'a ExprSimplifier>, + name_preserver: NamePreserver, +} + +impl TreeNodeRewriter for ExprRewriter<'_> { + type Node = Expr; + + fn f_down(&mut self, expr: Expr) -> Result> { + match &expr { + // we only simplify the cast expression for the literal value + Expr::Cast(Cast { + data_type, + expr: sub_expr, + }) + | Expr::TryCast(TryCast { + data_type, + expr: sub_expr, + }) if is_timestamp(data_type) + && matches!(sub_expr.as_ref(), Expr::Literal(_)) => + { + let original_name = self.name_preserver.save(&expr); + let new_e = self + .simplifier + .simplify(expr) + .map(|expr| original_name.restore(expr))?; + // TODO it would be nice to have a way to know if the expression was simplified + // or not. For now conservatively return Transformed::yes + Ok(Transformed::yes(new_e)) + } + Expr::Literal(value) => { + if let Some(cast_type) = cast_to_utc(value) { + let cast_to_utc_expr = cast(expr.clone(), cast_type); + let new_e = self.simplifier.simplify(cast_to_utc_expr)?; + Ok(Transformed::yes(new_e)) + } else { + Ok(Transformed::no(expr)) + } + } + _ => Ok(Transformed::no(expr)), + } + } +} + +fn is_timestamp(data_type: &DataType) -> bool { + matches!(data_type, DataType::Timestamp(_, _)) +} + +fn cast_to_utc(value: &ScalarValue) -> Option { + match value { + TimestampSecond(..) => Some(DataType::Timestamp(TimeUnit::Second, None)), + TimestampMillisecond(..) => { + Some(DataType::Timestamp(TimeUnit::Millisecond, None)) + } + TimestampMicrosecond(..) => { + Some(DataType::Timestamp(TimeUnit::Microsecond, None)) + } + TimestampNanosecond(..) => Some(DataType::Timestamp(TimeUnit::Nanosecond, None)), + _ => None, + } +} diff --git a/wren-core/core/src/logical_plan/utils.rs b/wren-core/core/src/logical_plan/utils.rs index 36c241d7e..db5a78188 100644 --- a/wren-core/core/src/logical_plan/utils.rs +++ b/wren-core/core/src/logical_plan/utils.rs @@ -9,42 +9,102 @@ use datafusion::arrow::datatypes::{ DataType, Field, IntervalUnit, Schema, SchemaBuilder, SchemaRef, TimeUnit, }; use datafusion::catalog_common::TableReference; +use datafusion::common::plan_err; use datafusion::common::tree_node::{TreeNode, TreeNodeRecursion}; use datafusion::datasource::DefaultTableSource; use datafusion::error::Result; +use datafusion::logical_expr::sqlparser::ast::ArrayElemTypeDef; +use datafusion::logical_expr::sqlparser::dialect::GenericDialect; use datafusion::logical_expr::{builder::LogicalTableSource, Expr, TableSource}; +use datafusion::sql::sqlparser::ast; +use datafusion::sql::sqlparser::parser::Parser; use log::debug; use petgraph::dot::{Config, Dot}; use petgraph::Graph; use std::collections::HashSet; use std::{collections::HashMap, sync::Arc}; -fn create_mock_list_type() -> DataType { - let string_filed = Arc::new(Field::new("string", DataType::Utf8, false)); - DataType::List(string_filed) +fn create_list_type(array_type: &str) -> Result { + // Workaround for the array type without an element type + if array_type.len() == "array".len() { + return create_list_type("array"); + } + if let ast::DataType::Array(value) = parse_type(array_type)? { + let data_type = match value { + ArrayElemTypeDef::None => { + return plan_err!("Array type must have an element type") + } + ArrayElemTypeDef::AngleBracket(data_type) => { + map_data_type(&data_type.to_string())? + } + ArrayElemTypeDef::SquareBracket(_, _) => { + unreachable!() + } + ArrayElemTypeDef::Parenthesis(_) => { + return plan_err!( + "The format of the array type should be 'array'" + ) + } + }; + return Ok(DataType::List(Arc::new(Field::new( + "element", data_type, false, + )))); + } + unreachable!() } -fn create_mock_struct_type() -> DataType { +fn create_struct_type(struct_type: &str) -> Result { + let sql_type = parse_type(struct_type)?; let mut builder = SchemaBuilder::new(); - builder.push(Field::new("a", DataType::Boolean, false)); + let mut counter = 0; + match sql_type { + ast::DataType::Struct(fields, ..) => { + if fields.is_empty() { + return plan_err!("struct must have at least one field"); + } + for field in fields { + let data_type = map_data_type(field.field_type.to_string().as_str())?; + let field = Field::new( + field + .field_name + .map(|f| f.to_string()) + .unwrap_or_else(|| format!("c{}", counter)), + data_type, + true, + ); + counter += 1; + builder.push(field); + } + } + _ => { + unreachable!() + } + } let fields = builder.finish().fields; - DataType::Struct(fields) + Ok(DataType::Struct(fields)) +} + +fn parse_type(struct_type: &str) -> Result { + let dialect = GenericDialect {}; + Ok(Parser::new(&dialect) + .try_with_sql(struct_type)? + .parse_data_type()?) } -pub fn map_data_type(data_type: &str) -> DataType { +pub fn map_data_type(data_type: &str) -> Result { let lower = data_type.to_lowercase(); let data_type = lower.as_str(); // Currently, we don't care about the element type of the array or struct. // We only care about the array or struct itself. if data_type.starts_with("array") { - return create_mock_list_type(); + return create_list_type(data_type); } if data_type.starts_with("struct") { - return create_mock_struct_type(); + return create_struct_type(data_type); } - match data_type { + let result = match data_type { // Wren Definition Types - "bool" => DataType::Boolean, + "bool" | "boolean" => DataType::Boolean, "tinyint" => DataType::Int8, "int2" => DataType::Int16, "smallint" => DataType::Int16, @@ -66,8 +126,10 @@ pub fn map_data_type(data_type: &str) -> DataType { "float" => DataType::Float32, "float8" => DataType::Float64, "double" => DataType::Float64, - "timestamp" => DataType::Timestamp(TimeUnit::Nanosecond, None), // chose the smallest time unit - "timestamptz" => DataType::Timestamp(TimeUnit::Nanosecond, None), // don't care about the time zone + "timestamp" | "datetime" => DataType::Timestamp(TimeUnit::Nanosecond, None), // chose the smallest time unit + "timestamptz" | "timestamp_with_timezone" | "timestamp_with_time_zone" => { + DataType::Timestamp(TimeUnit::Nanosecond, Some("UTC".into())) + } "date" => DataType::Date32, "interval" => DataType::Interval(IntervalUnit::DayTime), "json" => DataType::Utf8, // we don't have a JSON type, so we map it to Utf8 @@ -79,7 +141,6 @@ pub fn map_data_type(data_type: &str) -> DataType { // BigQuery Compatible Types "bignumeric" => DataType::Decimal128(38, 10), // set the default precision and scale "bytes" => DataType::Binary, - "datetime" => DataType::Timestamp(TimeUnit::Nanosecond, None), // chose the smallest time unit "float64" => DataType::Float64, "int64" => DataType::Int64, "time" => DataType::Time32(TimeUnit::Nanosecond), // chose the smallest time unit @@ -89,7 +150,8 @@ pub fn map_data_type(data_type: &str) -> DataType { debug!("map unknown type {} to Utf8", data_type); DataType::Utf8 } - } + }; + Ok(result) } pub fn create_table_source(model: &Model) -> Result> { @@ -101,7 +163,7 @@ pub fn create_schema(columns: Vec>) -> Result { let fields: Vec = columns .iter() .map(|column| { - let data_type = map_data_type(&column.r#type); + let data_type = map_data_type(&column.r#type)?; Ok(Field::new(&column.name, data_type, column.not_null)) }) .collect::>>()?; @@ -243,15 +305,17 @@ pub fn expr_to_columns( #[cfg(test)] mod test { - use datafusion::arrow::datatypes::{DataType, IntervalUnit, TimeUnit}; + use crate::logical_plan::utils::{ + create_list_type, create_struct_type, map_data_type, + }; + use datafusion::arrow::datatypes::{DataType, Field, Fields, IntervalUnit, TimeUnit}; use datafusion::common::Result; - use crate::logical_plan::utils::{create_mock_list_type, create_mock_struct_type}; - #[test] pub fn test_map_data_type() -> Result<()> { let test_cases = vec![ ("bool", DataType::Boolean), + ("boolean", DataType::Boolean), ("tinyint", DataType::Int8), ("int2", DataType::Int16), ("smallint", DataType::Int16), @@ -274,7 +338,15 @@ mod test { ("timestamp", DataType::Timestamp(TimeUnit::Nanosecond, None)), ( "timestamptz", - DataType::Timestamp(TimeUnit::Nanosecond, None), + DataType::Timestamp(TimeUnit::Nanosecond, Some("UTC".into())), + ), + ( + "timestamp_with_timezone", + DataType::Timestamp(TimeUnit::Nanosecond, Some("UTC".into())), + ), + ( + "timestamp_with_time_zone", + DataType::Timestamp(TimeUnit::Nanosecond, Some("UTC".into())), ), ("date", DataType::Date32), ("interval", DataType::Interval(IntervalUnit::DayTime)), @@ -293,16 +365,80 @@ mod test { ("null", DataType::Null), ("geography", DataType::Utf8), ("range", DataType::Utf8), - ("array", create_mock_list_type()), - ("struct", create_mock_struct_type()), + ("array", create_list_type("array")?), + ("array", create_list_type("array")?), + ( + "struct", + create_struct_type("struct")?, + ), ]; for (data_type, expected) in test_cases { - let result = super::map_data_type(data_type); + let result = map_data_type(data_type)?; assert_eq!(result, expected); // test case insensitivity - let result = super::map_data_type(&data_type.to_uppercase()); + let result = map_data_type(&data_type.to_uppercase())?; assert_eq!(result, expected); } + + let _ = map_data_type("array").map_err(|e| { + assert_eq!( + e.to_string(), + "SQL error: ParserError(\"Expected: <, found: EOF\")" + ); + }); + + let _ = map_data_type("array<>").map_err(|e| { + assert_eq!( + e.to_string(), + "SQL error: ParserError(\"Expected: <, found: <> at Line: 1, Column: 6\")" + ); + }); + + let _ = map_data_type("array(int64)").map_err(|e| { + assert_eq!( + e.to_string(), + "SQL error: ParserError(\"Expected: <, found: ( at Line: 1, Column: 6\")" + ); + }); + + let _ = map_data_type("struct").map_err(|e| { + assert_eq!( + e.to_string(), + "Error during planning: struct must have at least one field" + ); + }); + + Ok(()) + } + + #[test] + fn test_parse_struct() -> Result<()> { + let struct_string = "STRUCT"; + let result = create_struct_type(struct_string)?; + let fields: Fields = vec![ + Field::new("name", DataType::Utf8, true), + Field::new("age", DataType::Int32, true), + ] + .into(); + let expected = DataType::Struct(fields); + assert_eq!(result, expected); + + let struct_string = "STRUCT"; + let result = create_struct_type(struct_string)?; + let fields: Fields = vec![ + Field::new("c0", DataType::Utf8, true), + Field::new("c1", DataType::Int32, true), + ] + .into(); + let expected = DataType::Struct(fields); + assert_eq!(result, expected); + let struct_string = "STRUCT<>"; + let _ = create_struct_type(struct_string).map_err(|e| { + assert_eq!( + e.to_string(), + "Error during planning: struct must have at least one field" + ) + }); Ok(()) } } diff --git a/wren-core/core/src/mdl/context.rs b/wren-core/core/src/mdl/context.rs index 8b785c233..5d86abc77 100644 --- a/wren-core/core/src/mdl/context.rs +++ b/wren-core/core/src/mdl/context.rs @@ -5,6 +5,7 @@ use std::sync::Arc; use crate::logical_plan::analyze::expand_view::ExpandWrenViewRule; use crate::logical_plan::analyze::model_anlayze::ModelAnalyzeRule; use crate::logical_plan::analyze::model_generation::ModelGenerationRule; +use crate::logical_plan::optimize::simplify_timestamp::TimestampSimplify; use crate::logical_plan::utils::create_schema; use crate::mdl::manifest::Model; use crate::mdl::{AnalyzedWrenMDL, SessionStateRef, WrenMDL}; @@ -21,7 +22,6 @@ use datafusion::optimizer::analyzer::count_wildcard_rule::CountWildcardRule; use datafusion::optimizer::analyzer::expand_wildcard_rule::ExpandWildcardRule; use datafusion::optimizer::analyzer::inline_table_scan::InlineTableScan; use datafusion::optimizer::analyzer::type_coercion::TypeCoercion; -use datafusion::optimizer::common_subexpr_eliminate::CommonSubexprEliminate; use datafusion::optimizer::decorrelate_predicate_subquery::DecorrelatePredicateSubquery; use datafusion::optimizer::eliminate_cross_join::EliminateCrossJoin; use datafusion::optimizer::eliminate_duplicated_expr::EliminateDuplicatedExpr; @@ -35,7 +35,6 @@ use datafusion::optimizer::eliminate_outer_join::EliminateOuterJoin; use datafusion::optimizer::extract_equijoin_predicate::ExtractEquijoinPredicate; use datafusion::optimizer::filter_null_join_keys::FilterNullJoinKeys; use datafusion::optimizer::propagate_empty_relation::PropagateEmptyRelation; -use datafusion::optimizer::push_down_filter::PushDownFilter; use datafusion::optimizer::replace_distinct_aggregate::ReplaceDistinctWithAggregate; use datafusion::optimizer::scalar_subquery_to_join::ScalarSubqueryToJoin; use datafusion::optimizer::single_distinct_to_groupby::SingleDistinctToGroupBy; @@ -140,6 +139,9 @@ fn analyze_rule_for_unparsing( Arc::new(InlineTableScan::new()), // Every rule that will generate [Expr::Wildcard] should be placed in front of [ExpandWildcardRule]. Arc::new(ExpandWildcardRule::new()), + // TimestampSimplify should be placed before TypeCoercion because the simplified timestamp should + // be casted to the target type if needed + Arc::new(TimestampSimplify::new()), // [Expr::Wildcard] should be expanded before [TypeCoercion] Arc::new(TypeCoercion::new()), // Disable it to avoid generate the alias name, `count(*)` because BigQuery doesn't allow @@ -165,7 +167,8 @@ fn optimize_rule_for_unparsing() -> Vec> { Arc::new(EliminateDuplicatedExpr::new()), Arc::new(EliminateFilter::new()), Arc::new(EliminateCrossJoin::new()), - Arc::new(CommonSubexprEliminate::new()), + // Disable CommonSubexprEliminate to avoid generate invalid projection plan + // Arc::new(CommonSubexprEliminate::new()), Arc::new(EliminateLimit::new()), Arc::new(PropagateEmptyRelation::new()), // Must be after PropagateEmptyRelation @@ -175,12 +178,14 @@ fn optimize_rule_for_unparsing() -> Vec> { // Filters can't be pushed down past Limits, we should do PushDownFilter after PushDownLimit // TODO: Sort with pushdown-limit doesn't support to be unparse // Arc::new(PushDownLimit::new()), - Arc::new(PushDownFilter::new()), + // Disable PushDownFilter to avoid the casting for bigquery (datetime/timestamp) column be removed + // Arc::new(PushDownFilter::new()), Arc::new(SingleDistinctToGroupBy::new()), // Disable SimplifyExpressions to avoid apply some function locally // Arc::new(SimplifyExpressions::new()), Arc::new(UnwrapCastInComparison::new()), - Arc::new(CommonSubexprEliminate::new()), + // Disable CommonSubexprEliminate to avoid generate invalid projection plan + // Arc::new(CommonSubexprEliminate::new()), Arc::new(EliminateGroupByConstant::new()), // TODO: This rule would generate a plan that is not supported by the current unparser // Arc::new(OptimizeProjections::new()), diff --git a/wren-core/core/src/mdl/dataset.rs b/wren-core/core/src/mdl/dataset.rs index 6a0eee477..d1b5880c9 100644 --- a/wren-core/core/src/mdl/dataset.rs +++ b/wren-core/core/src/mdl/dataset.rs @@ -58,9 +58,9 @@ impl Column { } /// Transform the column to a datafusion field - pub fn to_field(&self) -> Field { - let data_type = map_data_type(&self.r#type); - Field::new(&self.name, data_type, self.not_null) + pub fn to_field(&self) -> Result { + let data_type = map_data_type(&self.r#type)?; + Ok(Field::new(&self.name, data_type, self.not_null)) } /// Transform the column to a datafusion field for a remote table @@ -72,12 +72,12 @@ impl Column { session_state.config_options().sql_parser.dialect.as_str(), )?; let columns = Self::collect_columns(expr); - Ok(columns + columns .into_iter() - .map(|c| Field::new(c.value, map_data_type(&self.r#type), false)) - .collect()) + .map(|c| Ok(Field::new(c.value, map_data_type(&self.r#type)?, false))) + .collect::>() } else { - Ok(vec![self.to_field()]) + Ok(vec![self.to_field()?]) } } @@ -123,7 +123,7 @@ impl Dataset { .get_physical_columns() .iter() .map(|c| c.to_field()) - .collect(); + .collect::>()?; let arrow_schema = datafusion::arrow::datatypes::Schema::new(fields); DFSchema::try_from_qualified_schema(quoted(&model.name), &arrow_schema) } diff --git a/wren-core/core/src/mdl/dialect.rs b/wren-core/core/src/mdl/dialect.rs new file mode 100644 index 000000000..9e5e47793 --- /dev/null +++ b/wren-core/core/src/mdl/dialect.rs @@ -0,0 +1,171 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +use datafusion::common::{internal_err, plan_err, Result, ScalarValue}; +use datafusion::logical_expr::sqlparser::ast::{Ident, Subscript}; +use datafusion::logical_expr::sqlparser::keywords::ALL_KEYWORDS; +use datafusion::logical_expr::Expr; +use datafusion::sql::sqlparser::ast; +use datafusion::sql::sqlparser::ast::{Array, Value}; +use datafusion::sql::unparser::dialect::{Dialect, IntervalStyle}; +use datafusion::sql::unparser::Unparser; +use regex::Regex; + +/// WrenDialect is a dialect for Wren engine. Handle the identifier quote style based on the +/// original Datafusion Dialect implementation but with more strict rules. +/// If the identifier isn't lowercase, it will be quoted. +pub struct WrenDialect {} + +impl Dialect for WrenDialect { + fn identifier_quote_style(&self, identifier: &str) -> Option { + let identifier_regex = Regex::new(r"^[a-zA-Z_][a-zA-Z0-9_]*$").unwrap(); + if ALL_KEYWORDS.contains(&identifier.to_uppercase().as_str()) + || !identifier_regex.is_match(identifier) + || non_lowercase(identifier) + { + Some('"') + } else { + None + } + } + + fn interval_style(&self) -> IntervalStyle { + IntervalStyle::SQLStandard + } + + fn scalar_function_to_sql_overrides( + &self, + unparser: &Unparser<'_>, + func_name: &str, + args: &[Expr], + ) -> Result> { + match func_name { + "make_array" => { + let sql = self.make_array_to_sql(args, unparser)?; + Ok(Some(sql)) + } + "array_element" => { + let sql = self.array_element_to_sql(args, unparser)?; + Ok(Some(sql)) + } + "get_field" => self.get_fields_to_sql(args, unparser), + "named_struct" => { + let sql = self.named_struct_to_sql(args, unparser)?; + Ok(Some(sql)) + } + _ => Ok(None), + } + } +} + +impl WrenDialect { + fn make_array_to_sql(&self, args: &[Expr], unparser: &Unparser) -> Result { + let args = args + .iter() + .map(|e| unparser.expr_to_sql(e)) + .collect::>>()?; + Ok(ast::Expr::Array(Array { + elem: args, + named: false, + })) + } + + fn array_element_to_sql( + &self, + args: &[Expr], + unparser: &Unparser, + ) -> Result { + if args.len() != 2 { + return internal_err!("array_element must have exactly 2 arguments"); + } + let array = unparser.expr_to_sql(&args[0])?; + let index = unparser.expr_to_sql(&args[1])?; + Ok(ast::Expr::Subscript { + expr: Box::new(array), + subscript: Box::new(Subscript::Index { index }), + }) + } + + fn named_struct_to_sql( + &self, + args: &[Expr], + unparser: &Unparser, + ) -> Result { + if args.is_empty() { + return plan_err!("struct must have at least one field"); + } + if args.len() % 2 != 0 { + return internal_err!( + "named_struct must have an even number of arguments or more than 0" + ); + } + let fields = args + .chunks(2) + .map(|pair| { + let name = match &pair[0] { + Expr::Literal(ScalarValue::Utf8(Some(s))) => s, + _ => { + return internal_err!("named_struct field name must be a string") + } + }; + let value = unparser.expr_to_sql(&pair[1])?; + Ok(ast::DictionaryField { + key: self.new_ident_quoted_if_needs(name.to_string()), + value: Box::new(value), + }) + }) + .collect::>>()?; + Ok(ast::Expr::Dictionary(fields)) + } + + fn get_fields_to_sql( + &self, + args: &[Expr], + unparser: &Unparser, + ) -> Result> { + if args.len() != 2 { + return internal_err!("get_fields must have exactly 2 argument"); + } + + let sql = unparser.expr_to_sql(&args[0])?; + if let ast::Expr::Value(Value::SingleQuotedString(field_name)) = + unparser.expr_to_sql(&args[1])? + { + let key = self.new_ident_quoted_if_needs(field_name); + return Ok(Some(ast::Expr::CompositeAccess { + expr: Box::new(sql), + key, + })); + } + + Ok(None) + } + + fn new_ident_quoted_if_needs(&self, ident: String) -> Ident { + let quote_style = self.identifier_quote_style(&ident); + Ident { + value: ident, + quote_style, + } + } +} + +fn non_lowercase(sql: &str) -> bool { + let lowercase = sql.to_lowercase(); + lowercase != sql +} diff --git a/wren-core/core/src/mdl/function.rs b/wren-core/core/src/mdl/function.rs index 348ab8472..d1505ddde 100644 --- a/wren-core/core/src/mdl/function.rs +++ b/wren-core/core/src/mdl/function.rs @@ -71,10 +71,7 @@ impl ByPassScalarUDF { name: name.to_string(), return_type, signature: Signature::one_of( - vec![ - TypeSignature::VariadicAny, - TypeSignature::Uniform(0, vec![]), - ], + vec![TypeSignature::VariadicAny, TypeSignature::NullAry], Volatility::Volatile, ), } @@ -117,7 +114,10 @@ impl ByPassAggregateUDF { Self { name: name.to_string(), return_type, - signature: Signature::variadic_any(Volatility::Immutable), + signature: Signature::one_of( + vec![TypeSignature::VariadicAny, TypeSignature::NullAry], + Volatility::Volatile, + ), } } } @@ -158,7 +158,10 @@ impl ByPassWindowFunction { Self { name: name.to_string(), return_type, - signature: Signature::variadic_any(Volatility::Immutable), + signature: Signature::one_of( + vec![TypeSignature::VariadicAny, TypeSignature::NullAry], + Volatility::Volatile, + ), } } } @@ -214,6 +217,14 @@ mod test { .into_unoptimized_plan(); let expected = "Projection: date_diff(Int64(1), Int64(2))\n EmptyRelation"; assert_eq!(format!("{plan}"), expected); + + ctx.register_udf(ScalarUDF::new_from_impl(ByPassScalarUDF::new( + "today", + DataType::Utf8, + ))); + let plan_2 = ctx.sql("SELECT today()").await?.into_unoptimized_plan(); + assert_eq!(format!("{plan_2}"), "Projection: today()\n EmptyRelation"); + Ok(()) } @@ -230,6 +241,23 @@ mod test { \n Projection: column1 AS c1, column2 AS c2\ \n Values: (Int64(1), Int64(2)), (Int64(2), Int64(3)), (Int64(3), Int64(4))"; assert_eq!(format!("{plan}"), expected); + + ctx.register_udaf(AggregateUDF::new_from_impl(ByPassAggregateUDF::new( + "total_count", + DataType::Int64, + ))); + let plan_2 = ctx + .sql("SELECT total_count() AS total_count FROM (VALUES (1), (2), (3)) AS val(x)") + .await? + .into_unoptimized_plan(); + assert_eq!( + format!("{plan_2}"), + "Projection: total_count() AS total_count\ + \n Aggregate: groupBy=[[]], aggr=[[total_count()]]\ + \n SubqueryAlias: val\n Projection: column1 AS x\ + \n Values: (Int64(1)), (Int64(2)), (Int64(3))" + ); + Ok(()) } @@ -247,6 +275,19 @@ mod test { \n WindowAggr: windowExpr=[[custom_window(Int64(1), Int64(2)) ROWS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING]]\ \n EmptyRelation"; assert_eq!(format!("{plan}"), expected); + + ctx.register_udwf(WindowUDF::new_from_impl(ByPassWindowFunction::new( + "cume_dist", + DataType::Int64, + ))); + let plan_2 = ctx + .sql("SELECT cume_dist() OVER ()") + .await? + .into_unoptimized_plan(); + assert_eq!(format!("{plan_2}"), "Projection: cume_dist() ROWS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING\ + \n WindowAggr: windowExpr=[[cume_dist() ROWS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING]]\ + \n EmptyRelation"); + Ok(()) } } diff --git a/wren-core/core/src/mdl/mod.rs b/wren-core/core/src/mdl/mod.rs index 51313c758..74685974d 100644 --- a/wren-core/core/src/mdl/mod.rs +++ b/wren-core/core/src/mdl/mod.rs @@ -1,36 +1,36 @@ use crate::logical_plan::utils::{from_qualified_name_str, map_data_type}; use crate::mdl::builder::ManifestBuilder; use crate::mdl::context::{create_ctx_with_mdl, WrenDataSource}; +use crate::mdl::dialect::WrenDialect; use crate::mdl::function::{ ByPassAggregateUDF, ByPassScalarUDF, ByPassWindowFunction, FunctionType, RemoteFunction, }; use crate::mdl::manifest::{Column, Manifest, Model, View}; +use crate::DataFusionError; use datafusion::arrow::datatypes::Field; use datafusion::common::internal_datafusion_err; use datafusion::datasource::TableProvider; use datafusion::error::Result; use datafusion::execution::context::SessionState; -use datafusion::logical_expr::sqlparser::keywords::ALL_KEYWORDS; use datafusion::logical_expr::{AggregateUDF, ScalarUDF, WindowUDF}; use datafusion::prelude::SessionContext; use datafusion::sql::parser::DFParser; use datafusion::sql::sqlparser::ast::{Expr, Ident}; use datafusion::sql::sqlparser::dialect::dialect_from_str; -use datafusion::sql::unparser::dialect::{Dialect, IntervalStyle}; use datafusion::sql::unparser::Unparser; use datafusion::sql::TableReference; pub use dataset::Dataset; use log::{debug, info}; use manifest::Relationship; use parking_lot::RwLock; -use regex::Regex; use std::hash::Hash; use std::{collections::HashMap, sync::Arc}; pub mod builder; pub mod context; pub(crate) mod dataset; +mod dialect; pub mod function; pub mod lineage; pub mod manifest; @@ -63,7 +63,7 @@ impl Default for AnalyzedWrenMDL { impl AnalyzedWrenMDL { pub fn analyze(manifest: Manifest) -> Result { - let wren_mdl = Arc::new(WrenMDL::infer_and_register_remote_table(manifest)); + let wren_mdl = Arc::new(WrenMDL::infer_and_register_remote_table(manifest)?); let lineage = Arc::new(lineage::Lineage::new(&wren_mdl)?); Ok(AnalyzedWrenMDL { wren_mdl, lineage }) } @@ -171,7 +171,7 @@ impl WrenMDL { /// Create a WrenMDL from a manifest and register the table reference of the model as a remote table. /// All the column without expression will be considered a column - pub fn infer_and_register_remote_table(manifest: Manifest) -> Self { + pub fn infer_and_register_remote_table(manifest: Manifest) -> Result { let mut mdl = WrenMDL::new(manifest); let sources: Vec<_> = mdl .models() @@ -181,7 +181,7 @@ impl WrenMDL { let fields: Vec<_> = model .columns .iter() - .filter_map(|column| Self::infer_source_column(column)) + .filter_map(|column| Self::infer_source_column(column).ok().flatten()) .collect(); let schema = Arc::new(datafusion::arrow::datatypes::Schema::new(fields)); let datasource = WrenDataSource::new_with_schema(schema); @@ -191,7 +191,7 @@ impl WrenMDL { sources .into_iter() .for_each(|(name, ds_ref)| mdl.register_table(name, ds_ref)); - mdl + Ok(mdl) } /// Infer the source column from the column expression. @@ -202,23 +202,25 @@ impl WrenMDL { /// If the expression is a simple column reference, it's the source column name. /// If the expression is a complex expression, it can't be inferred. /// - fn infer_source_column(column: &Column) -> Option { + fn infer_source_column(column: &Column) -> Result> { if column.is_calculated || column.relationship.is_some() { - return None; + return Ok(None); } if let Some(expression) = column.expression() { - let expr = WrenMDL::sql_to_expr(expression).ok()?; + let expr = WrenMDL::sql_to_expr(expression)?; // if the column is a simple column reference, we can infer the column name - Self::collect_one_column(&expr).map(|name| { - Field::new( + if let Some(name) = Self::collect_one_column(&expr) { + Ok(Some(Field::new( name.value.clone(), - map_data_type(&column.r#type), + map_data_type(&column.r#type)?, column.not_null, - ) - }) + ))) + } else { + Ok(None) + } } else { - Some(column.to_field()) + Ok(Some(column.to_field()?)) } } @@ -328,10 +330,11 @@ pub async fn transform_sql_with_ctx( sql: &str, ) -> Result { info!("wren-core received SQL: {}", sql); - remote_functions.iter().for_each(|remote_function| { + remote_functions.iter().try_for_each(|remote_function| { debug!("Registering remote function: {:?}", remote_function); - register_remote_function(ctx, remote_function); - }); + register_remote_function(ctx, remote_function)?; + Ok::<_, DataFusionError>(()) + })?; let ctx = create_ctx_with_mdl(ctx, Arc::clone(&analyzed_mdl), false).await?; let plan = ctx.state().create_logical_plan(sql).await?; debug!("wren-core original plan:\n {plan}"); @@ -353,55 +356,31 @@ pub async fn transform_sql_with_ctx( } } -fn register_remote_function(ctx: &SessionContext, remote_function: &RemoteFunction) { +fn register_remote_function( + ctx: &SessionContext, + remote_function: &RemoteFunction, +) -> Result<()> { match &remote_function.function_type { FunctionType::Scalar => { ctx.register_udf(ScalarUDF::new_from_impl(ByPassScalarUDF::new( &remote_function.name, - map_data_type(&remote_function.return_type), + map_data_type(&remote_function.return_type)?, ))) } FunctionType::Aggregate => { ctx.register_udaf(AggregateUDF::new_from_impl(ByPassAggregateUDF::new( &remote_function.name, - map_data_type(&remote_function.return_type), + map_data_type(&remote_function.return_type)?, ))) } FunctionType::Window => { ctx.register_udwf(WindowUDF::new_from_impl(ByPassWindowFunction::new( &remote_function.name, - map_data_type(&remote_function.return_type), + map_data_type(&remote_function.return_type)?, ))) } - } -} - -/// WrenDialect is a dialect for Wren engine. Handle the identifier quote style based on the -/// original Datafusion Dialect implementation but with more strict rules. -/// If the identifier isn't lowercase, it will be quoted. -pub struct WrenDialect {} - -impl Dialect for WrenDialect { - fn identifier_quote_style(&self, identifier: &str) -> Option { - let identifier_regex = Regex::new(r"^[a-zA-Z_][a-zA-Z0-9_]*$").unwrap(); - if ALL_KEYWORDS.contains(&identifier.to_uppercase().as_str()) - || !identifier_regex.is_match(identifier) - || non_lowercase(identifier) - { - Some('"') - } else { - None - } - } - - fn interval_style(&self) -> IntervalStyle { - IntervalStyle::SQLStandard - } -} - -fn non_lowercase(sql: &str) -> bool { - let lowercase = sql.to_lowercase(); - lowercase != sql + }; + Ok(()) } /// Analyze the decision point. It's same as the /v1/analysis/sql API in wren engine @@ -426,20 +405,24 @@ impl ColumnReference { #[cfg(test)] mod test { + use std::collections::HashMap; use std::fs; use std::path::PathBuf; use std::sync::Arc; use crate::mdl::builder::{ColumnBuilder, ManifestBuilder, ModelBuilder}; + use crate::mdl::context::create_ctx_with_mdl; use crate::mdl::function::RemoteFunction; use crate::mdl::manifest::Manifest; use crate::mdl::{self, transform_sql_with_ctx, AnalyzedWrenMDL}; use datafusion::arrow::array::{ ArrayRef, Int64Array, RecordBatch, StringArray, TimestampNanosecondArray, }; + use datafusion::assert_batches_eq; use datafusion::common::not_impl_err; use datafusion::common::Result; - use datafusion::prelude::SessionContext; + use datafusion::config::ConfigOptions; + use datafusion::prelude::{SessionConfig, SessionContext}; use datafusion::sql::unparser::plan_to_sql; #[test] @@ -893,6 +876,332 @@ mod test { } } + #[tokio::test] + async fn test_simplify_timestamp() -> Result<()> { + let ctx = SessionContext::new(); + let analyzed_mdl = Arc::new(AnalyzedWrenMDL::default()); + let sql = "select timestamp '2011-01-01 18:00:00 +08:00'"; + let actual = + transform_sql_with_ctx(&ctx, Arc::clone(&analyzed_mdl), &[], sql).await?; + assert_eq!(actual, "SELECT CAST('2011-01-01 10:00:00' AS TIMESTAMP) AS \"Utf8(\"\"2011-01-01 18:00:00 +08:00\"\")\""); + + let sql = "select timestamp '2011-01-01 18:00:00 Asia/Taipei'"; + let actual = + transform_sql_with_ctx(&ctx, Arc::clone(&analyzed_mdl), &[], sql).await?; + assert_eq!(actual, "SELECT CAST('2011-01-01 10:00:00' AS TIMESTAMP) AS \"Utf8(\"\"2011-01-01 18:00:00 Asia/Taipei\"\")\""); + Ok(()) + } + + #[tokio::test] + async fn test_eval_timestamp_with_session_timezone() -> Result<()> { + let mut config = ConfigOptions::new(); + config.execution.time_zone = Some("+08:00".to_string()); + let session_config = SessionConfig::from(config); + let ctx = SessionContext::new_with_config(session_config); + let analyzed_mdl = Arc::new(AnalyzedWrenMDL::default()); + let sql = "select timestamp '2011-01-01 18:00:00'"; + let actual = + transform_sql_with_ctx(&ctx, Arc::clone(&analyzed_mdl), &[], sql).await?; + // TIMESTAMP doesn't have timezone, so the timezone will be ignored + assert_eq!(actual, "SELECT CAST('2011-01-01 18:00:00' AS TIMESTAMP) AS \"Utf8(\"\"2011-01-01 18:00:00\"\")\""); + + let sql = "select timestamp with time zone '2011-01-01 18:00:00'"; + let actual = + transform_sql_with_ctx(&ctx, Arc::clone(&analyzed_mdl), &[], sql).await?; + // TIMESTAMP WITH TIME ZONE will be converted to the session timezone + assert_eq!(actual, "SELECT CAST('2011-01-01 10:00:00' AS TIMESTAMP) AS \"Utf8(\"\"2011-01-01 18:00:00\"\")\""); + + let mut config = ConfigOptions::new(); + config.execution.time_zone = Some("America/New_York".to_string()); + let session_config = SessionConfig::from(config); + let ctx = SessionContext::new_with_config(session_config); + let analyzed_mdl = Arc::new(AnalyzedWrenMDL::default()); + // TIMESTAMP WITH TIME ZONE will be converted to the session timezone with daylight saving (UTC -5) + let sql = "select timestamp with time zone '2024-01-15 18:00:00'"; + let actual = + transform_sql_with_ctx(&ctx, Arc::clone(&analyzed_mdl), &[], sql).await?; + assert_eq!(actual, "SELECT CAST('2024-01-15 23:00:00' AS TIMESTAMP) AS \"Utf8(\"\"2024-01-15 18:00:00\"\")\""); + + // TIMESTAMP WITH TIME ZONE will be converted to the session timezone without daylight saving (UTC -4) + let sql = "select timestamp with time zone '2024-07-15 18:00:00'"; + let actual = + transform_sql_with_ctx(&ctx, Arc::clone(&analyzed_mdl), &[], sql).await?; + assert_eq!(actual, "SELECT CAST('2024-07-15 22:00:00' AS TIMESTAMP) AS \"Utf8(\"\"2024-07-15 18:00:00\"\")\""); + Ok(()) + } + + #[tokio::test] + async fn test_disable_pushdown_filter() -> Result<()> { + let ctx = SessionContext::new(); + ctx.register_batch("artist", artist())?; + let manifest = ManifestBuilder::new() + .catalog("wren") + .schema("test") + .model( + ModelBuilder::new("artist") + .table_reference("artist") + .column( + ColumnBuilder::new("出道時間", "timestamp") + .hidden(true) + .build(), + ) + .column( + ColumnBuilder::new("cast_timestamptz", "timestamptz") + .expression(r#"cast("出道時間" as timestamp with time zone)"#) + .build(), + ) + .build(), + ) + .build(); + + let analyzed_mdl = Arc::new(AnalyzedWrenMDL::analyze(manifest)?); + let sql = r#"select count(*) from wren.test.artist where cast(cast_timestamptz as timestamp) > timestamp '2011-01-01 21:00:00'"#; + let actual = transform_sql_with_ctx( + &SessionContext::new(), + Arc::clone(&analyzed_mdl), + &[], + sql, + ) + .await?; + assert_eq!(actual, + "SELECT count(*) FROM (SELECT artist.cast_timestamptz FROM (SELECT CAST(artist.\"出道時間\" AS TIMESTAMP WITH TIME ZONE) AS cast_timestamptz \ + FROM artist) AS artist) AS artist WHERE CAST(artist.cast_timestamptz AS TIMESTAMP) > CAST('2011-01-01 21:00:00' AS TIMESTAMP)"); + Ok(()) + } + + #[tokio::test] + async fn test_register_timestamptz() -> Result<()> { + let ctx = SessionContext::new(); + ctx.register_batch("timestamp_table", timestamp_table())?; + let provider = ctx + .catalog("datafusion") + .unwrap() + .schema("public") + .unwrap() + .table("timestamp_table") + .await? + .unwrap(); + let mut registers = HashMap::new(); + registers.insert( + "datafusion.public.timestamp_table".to_string(), + Arc::clone(&provider), + ); + let manifest = ManifestBuilder::new() + .catalog("wren") + .schema("test") + .model( + ModelBuilder::new("timestamp_table") + .table_reference("datafusion.public.timestamp_table") + .column(ColumnBuilder::new("timestamp_col", "timestamp").build()) + .column(ColumnBuilder::new("timestamptz_col", "timestamptz").build()) + .build(), + ) + .build(); + + let analyzed_mdl = + Arc::new(AnalyzedWrenMDL::analyze_with_tables(manifest, registers)?); + let ctx = create_ctx_with_mdl(&ctx, Arc::clone(&analyzed_mdl), true).await?; + let sql = r#"select arrow_typeof(timestamp_col), arrow_typeof(timestamptz_col) from wren.test.timestamp_table limit 1"#; + let result = ctx.sql(sql).await?.collect().await?; + let expected = vec![ + "+---------------------------------------------+-----------------------------------------------+", + "| arrow_typeof(timestamp_table.timestamp_col) | arrow_typeof(timestamp_table.timestamptz_col) |", + "+---------------------------------------------+-----------------------------------------------+", + "| Timestamp(Nanosecond, None) | Timestamp(Nanosecond, Some(\"UTC\")) |", + "+---------------------------------------------+-----------------------------------------------+", + ]; + assert_batches_eq!(&expected, &result); + Ok(()) + } + + #[tokio::test] + async fn test_coercion_timestamptz() -> Result<()> { + let ctx = SessionContext::new(); + ctx.register_batch("timestamp_table", timestamp_table())?; + for timezone_type in [ + "timestamptz", + "timestamp_with_timezone", + "timestamp_with_time_zone", + ] { + let manifest = ManifestBuilder::new() + .catalog("wren") + .schema("test") + .model( + ModelBuilder::new("timestamp_table") + .table_reference("datafusion.public.timestamp_table") + .column(ColumnBuilder::new("timestamp_col", "timestamp").build()) + .column( + ColumnBuilder::new("timestamptz_col", timezone_type).build(), + ) + .build(), + ) + .build(); + let analyzed_mdl = Arc::new(AnalyzedWrenMDL::analyze(manifest)?); + let sql = r#"select timestamp_col = timestamptz_col from wren.test.timestamp_table"#; + let actual = transform_sql_with_ctx( + &SessionContext::new(), + Arc::clone(&analyzed_mdl), + &[], + sql, + ) + .await?; + assert_eq!(actual, + "SELECT CAST(timestamp_table.timestamp_col AS TIMESTAMP WITH TIME ZONE) = timestamp_table.timestamptz_col FROM \ + (SELECT timestamp_table.timestamp_col, timestamp_table.timestamptz_col FROM \ + (SELECT timestamp_table.timestamp_col AS timestamp_col, timestamp_table.timestamptz_col AS timestamptz_col \ + FROM datafusion.public.timestamp_table) AS timestamp_table) AS timestamp_table"); + + let sql = r#"select timestamptz_col > cast('2011-01-01 18:00:00' as TIMESTAMP WITH TIME ZONE) from wren.test.timestamp_table"#; + let actual = transform_sql_with_ctx( + &SessionContext::new(), + Arc::clone(&analyzed_mdl), + &[], + sql, + ) + .await?; + // assert the simplified literal will be casted to the timestamp tz + assert_eq!(actual, + "SELECT timestamp_table.timestamptz_col > CAST(CAST('2011-01-01 18:00:00' AS TIMESTAMP) AS TIMESTAMP WITH TIME ZONE) \ + FROM (SELECT timestamp_table.timestamptz_col FROM (SELECT timestamp_table.timestamptz_col AS timestamptz_col \ + FROM datafusion.public.timestamp_table) AS timestamp_table) AS timestamp_table"); + + let sql = r#"select timestamptz_col > '2011-01-01 18:00:00' from wren.test.timestamp_table"#; + let actual = transform_sql_with_ctx( + &SessionContext::new(), + Arc::clone(&analyzed_mdl), + &[], + sql, + ) + .await?; + // assert the string literal will be casted to the timestamp tz + assert_eq!(actual, + "SELECT timestamp_table.timestamptz_col > CAST('2011-01-01 18:00:00' AS TIMESTAMP WITH TIME ZONE) \ + FROM (SELECT timestamp_table.timestamptz_col FROM (SELECT timestamp_table.timestamptz_col AS timestamptz_col \ + FROM datafusion.public.timestamp_table) AS timestamp_table) AS timestamp_table"); + + let sql = r#"select timestamp_col > cast('2011-01-01 18:00:00' as TIMESTAMP WITH TIME ZONE) from wren.test.timestamp_table"#; + let actual = transform_sql_with_ctx( + &SessionContext::new(), + Arc::clone(&analyzed_mdl), + &[], + sql, + ) + .await?; + // assert the simplified literal won't be casted to the timestamp tz + assert_eq!(actual, + "SELECT timestamp_table.timestamp_col > CAST('2011-01-01 18:00:00' AS TIMESTAMP) FROM \ + (SELECT timestamp_table.timestamp_col FROM (SELECT timestamp_table.timestamp_col AS timestamp_col \ + FROM datafusion.public.timestamp_table) AS timestamp_table) AS timestamp_table"); + } + Ok(()) + } + + #[tokio::test] + async fn test_list() -> Result<()> { + let ctx = SessionContext::new(); + let manifest = ManifestBuilder::new() + .catalog("wren") + .schema("test") + .model( + ModelBuilder::new("list_table") + .table_reference("list_table") + .column(ColumnBuilder::new("list_col", "array").build()) + .build(), + ) + .build(); + let analyzed_mdl = Arc::new(AnalyzedWrenMDL::analyze(manifest)?); + let sql = "select list_col[1] from wren.test.list_table"; + let actual = + transform_sql_with_ctx(&ctx, Arc::clone(&analyzed_mdl), &[], sql).await?; + assert_eq!(actual, "SELECT list_table.list_col[1] FROM (SELECT list_table.list_col FROM \ + (SELECT list_table.list_col AS list_col FROM list_table) AS list_table) AS list_table"); + Ok(()) + } + + #[tokio::test] + async fn test_struct() -> Result<()> { + let ctx = SessionContext::new(); + let manifest = ManifestBuilder::new() + .catalog("wren") + .schema("test") + .model( + ModelBuilder::new("struct_table") + .table_reference("struct_table") + .column( + ColumnBuilder::new( + "struct_col", + "struct", + ) + .build(), + ) + .column( + ColumnBuilder::new( + "struct_array_col", + "array>", + ) + .build(), + ) + .build(), + ) + .build(); + let analyzed_mdl = Arc::new(AnalyzedWrenMDL::analyze(manifest)?); + let sql = "select struct_col.float_field from wren.test.struct_table"; + let actual = + transform_sql_with_ctx(&ctx, Arc::clone(&analyzed_mdl), &[], sql).await?; + assert_eq!(actual, "SELECT struct_table.struct_col.float_field FROM \ + (SELECT struct_table.struct_col FROM (SELECT struct_table.struct_col AS struct_col \ + FROM struct_table) AS struct_table) AS struct_table"); + + let sql = "select struct_array_col[1].float_field from wren.test.struct_table"; + let actual = + transform_sql_with_ctx(&ctx, Arc::clone(&analyzed_mdl), &[], sql).await?; + assert_eq!(actual, "SELECT struct_table.struct_array_col[1].float_field FROM \ + (SELECT struct_table.struct_array_col FROM (SELECT struct_table.struct_array_col AS struct_array_col \ + FROM struct_table) AS struct_table) AS struct_table"); + + let sql = + "select {float_field: 1.0, time_field: timestamp '2021-01-01 00:00:00'}"; + let actual = + transform_sql_with_ctx(&ctx, Arc::clone(&analyzed_mdl), &[], sql).await?; + assert_eq!(actual, "SELECT {float_field: 1.0, time_field: CAST('2021-01-01 00:00:00' AS TIMESTAMP)}"); + + let manifest = ManifestBuilder::new() + .catalog("wren") + .schema("test") + .model( + ModelBuilder::new("struct_table") + .table_reference("struct_table") + .column(ColumnBuilder::new("struct_col", "struct<>").build()) + .build(), + ) + .build(); + let analyzed_mdl = Arc::new(AnalyzedWrenMDL::analyze(manifest)?); + let sql = "select struct_col.float_field from wren.test.struct_table"; + let _ = transform_sql_with_ctx(&ctx, Arc::clone(&analyzed_mdl), &[], sql) + .await + .map_err(|e| { + assert_eq!( + e.to_string(), + "Error during planning: struct must have at least one field" + ) + }); + Ok(()) + } + + #[tokio::test] + async fn test_disable_common_expression_eliminate() -> Result<()> { + let ctx = SessionContext::new(); + let sql = + "SELECT CAST(TIMESTAMP '2021-01-01 00:00:00' as TIMESTAMP WITH TIME ZONE) = \ + CAST(TIMESTAMP '2021-01-01 00:00:00' as TIMESTAMP WITH TIME ZONE)"; + let result = + transform_sql_with_ctx(&ctx, Arc::new(AnalyzedWrenMDL::default()), &[], sql) + .await?; + assert_eq!(result, "SELECT CAST(CAST('2021-01-01 00:00:00' AS TIMESTAMP) AS TIMESTAMP WITH TIME ZONE) = \ + CAST(CAST('2021-01-01 00:00:00' AS TIMESTAMP) AS TIMESTAMP WITH TIME ZONE)"); + Ok(()) + } + /// Return a RecordBatch with made up data about customer fn customer() -> RecordBatch { let custkey: ArrayRef = Arc::new(Int64Array::from(vec![1, 2, 3])); @@ -945,4 +1254,15 @@ mod test { ]) .unwrap() } + + fn timestamp_table() -> RecordBatch { + let timestamp: ArrayRef = Arc::new(TimestampNanosecondArray::from(vec![1, 2, 3])); + let timestamptz: ArrayRef = + Arc::new(TimestampNanosecondArray::from(vec![1, 2, 3]).with_timezone("UTC")); + RecordBatch::try_from_iter(vec![ + ("timestamp_col", timestamp), + ("timestamptz_col", timestamptz), + ]) + .unwrap() + } } diff --git a/wren-core/sqllogictest/Cargo.toml b/wren-core/sqllogictest/Cargo.toml index 969ca4efc..01176a47d 100644 --- a/wren-core/sqllogictest/Cargo.toml +++ b/wren-core/sqllogictest/Cargo.toml @@ -20,8 +20,8 @@ datafusion = { workspace = true, default-features = true } half = { version = "2.4.1", default-features = true } log = { workspace = true } rust_decimal = { version = "1.27.0" } -sqllogictest = "0.22.0" -thiserror = "1.0.61" +sqllogictest = "0.23.0" +thiserror = "2.0.3" tokio = { workspace = true } wren-core = { path = "../core" } diff --git a/wren-core/sqllogictest/test_files/tpch/q7.slt.part b/wren-core/sqllogictest/test_files/tpch/q7.slt.part index a62aaf972..c806bafa5 100644 --- a/wren-core/sqllogictest/test_files/tpch/q7.slt.part +++ b/wren-core/sqllogictest/test_files/tpch/q7.slt.part @@ -16,7 +16,7 @@ # specific language governing permissions and limitations # under the License. -query TTRR +query TTIR select supp_nation, cust_nation, diff --git a/wren-core/sqllogictest/test_files/tpch/q8.slt.part b/wren-core/sqllogictest/test_files/tpch/q8.slt.part index bdd6b1669..27f907780 100644 --- a/wren-core/sqllogictest/test_files/tpch/q8.slt.part +++ b/wren-core/sqllogictest/test_files/tpch/q8.slt.part @@ -16,7 +16,7 @@ # specific language governing permissions and limitations # under the License. -query RR +query IR select o_year, cast(cast(sum(case diff --git a/wren-core/sqllogictest/test_files/tpch/q9.slt.part b/wren-core/sqllogictest/test_files/tpch/q9.slt.part index 0e3e7aafc..94ed4845b 100644 --- a/wren-core/sqllogictest/test_files/tpch/q9.slt.part +++ b/wren-core/sqllogictest/test_files/tpch/q9.slt.part @@ -16,7 +16,7 @@ # specific language governing permissions and limitations # under the License. -query TRR +query TIR select nation, o_year, diff --git a/wren-main/pom.xml b/wren-main/pom.xml index 70259d417..f2d8dbe4a 100644 --- a/wren-main/pom.xml +++ b/wren-main/pom.xml @@ -5,7 +5,7 @@ io.wren wren-root - 0.11.3-SNAPSHOT + 0.12.3-SNAPSHOT ../pom.xml diff --git a/wren-server/pom.xml b/wren-server/pom.xml index 0f538518f..48d28b08c 100644 --- a/wren-server/pom.xml +++ b/wren-server/pom.xml @@ -5,7 +5,7 @@ io.wren wren-root - 0.11.3-SNAPSHOT + 0.12.3-SNAPSHOT ../pom.xml diff --git a/wren-tests/pom.xml b/wren-tests/pom.xml index 0b226ca85..6faf7edc3 100644 --- a/wren-tests/pom.xml +++ b/wren-tests/pom.xml @@ -18,7 +18,7 @@ io.wren wren-root - 0.11.3-SNAPSHOT + 0.12.3-SNAPSHOT ../pom.xml diff --git a/wren-tests/src/test/java/io/wren/testing/TestMDLResource.java b/wren-tests/src/test/java/io/wren/testing/TestMDLResource.java index fc1562cde..71c02fb7d 100644 --- a/wren-tests/src/test/java/io/wren/testing/TestMDLResource.java +++ b/wren-tests/src/test/java/io/wren/testing/TestMDLResource.java @@ -31,7 +31,7 @@ import static io.wren.base.config.WrenConfig.DataSourceType.DUCKDB; import static io.wren.base.config.WrenConfig.WREN_DATASOURCE_TYPE; import static io.wren.base.config.WrenConfig.WREN_ENABLE_DYNAMIC_FIELDS; -import static io.wren.base.dto.Column.caluclatedColumn; +import static io.wren.base.dto.Column.calculatedColumn; import static io.wren.base.dto.Column.column; import static io.wren.base.dto.Model.model; import static io.wren.base.dto.Relationship.relationship; @@ -114,7 +114,7 @@ public void testDryRunAndDryPlan() List.of(column("orderkey", "integer", null, false, "o_orderkey"), column("custkey", "integer", null, false, "o_custkey"), column("customer", "Customer", "CustomerOrders", false), - caluclatedColumn("customer_name", "varchar", "customer.name")), + calculatedColumn("customer_name", "varchar", "customer.name")), "orderkey"))) .setRelationships(List.of(relationship("CustomerOrders", List.of("Customer", "Orders"), JoinType.ONE_TO_MANY, "Customer.custkey = Orders.custkey"))) .build(); diff --git a/wren-tests/src/test/java/io/wren/testing/TestMDLResourceV2.java b/wren-tests/src/test/java/io/wren/testing/TestMDLResourceV2.java index d10cb9a6e..1c160da67 100644 --- a/wren-tests/src/test/java/io/wren/testing/TestMDLResourceV2.java +++ b/wren-tests/src/test/java/io/wren/testing/TestMDLResourceV2.java @@ -30,7 +30,7 @@ import static io.wren.base.config.WrenConfig.WREN_DATASOURCE_TYPE; import static io.wren.base.config.WrenConfig.WREN_DIRECTORY; import static io.wren.base.config.WrenConfig.WREN_ENABLE_DYNAMIC_FIELDS; -import static io.wren.base.dto.Column.caluclatedColumn; +import static io.wren.base.dto.Column.calculatedColumn; import static io.wren.base.dto.Column.column; import static io.wren.base.dto.Model.model; import static io.wren.base.dto.Relationship.relationship; @@ -76,7 +76,7 @@ public void testDryPlan() List.of(column("orderkey", "integer", null, false, "o_orderkey"), column("custkey", "integer", null, false, "o_custkey"), column("customer", "Customer", "CustomerOrders", false), - caluclatedColumn("customer_name", "varchar", "customer.name")), + calculatedColumn("customer_name", "varchar", "customer.name")), "orderkey"))) .setRelationships(List.of(relationship("CustomerOrders", List.of("Customer", "Orders"), JoinType.ONE_TO_MANY, "Customer.custkey = Orders.custkey"))) .build(); @@ -206,7 +206,7 @@ public void testSetManyToMany() List.of(column("orderkey", "integer", null, false, "o_orderkey"), column("custkey", "integer", null, false, "o_custkey"), column("customer", "Customer", "CustomerOrders", false), - caluclatedColumn("customer_name", "varchar", "customer.name")), + calculatedColumn("customer_name", "varchar", "customer.name")), "orderkey"))) .setRelationships(List.of(relationship("CustomerOrders", List.of("Customer", "Orders"), JoinType.MANY_TO_MANY, "Customer.custkey = Orders.custkey"))) .build(); diff --git a/wren-tests/src/test/java/io/wren/testing/duckdb/TestDynamicFields.java b/wren-tests/src/test/java/io/wren/testing/duckdb/TestDynamicFields.java index 628515f39..448c820ff 100644 --- a/wren-tests/src/test/java/io/wren/testing/duckdb/TestDynamicFields.java +++ b/wren-tests/src/test/java/io/wren/testing/duckdb/TestDynamicFields.java @@ -14,7 +14,9 @@ package io.wren.testing.duckdb; +import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableMap; +import io.wren.base.Column; import io.wren.base.dto.Manifest; import io.wren.main.web.dto.QueryResultDto; import io.wren.testing.AbstractTestFramework; @@ -23,8 +25,9 @@ import java.nio.file.Files; import java.nio.file.Path; -import java.sql.SQLException; +import java.util.List; +import static io.wren.base.Column.column; import static io.wren.base.config.WrenConfig.DataSourceType.DUCKDB; import static io.wren.base.config.WrenConfig.WREN_DATASOURCE_TYPE; import static io.wren.base.config.WrenConfig.WREN_ENABLE_DYNAMIC_FIELDS; @@ -56,7 +59,6 @@ protected TestingWrenServer createWrenServer() @Test public void testDynamicMetric() - throws SQLException { // select one dimension and measure QueryResultDto actual = query(manifest, "SELECT customer, totalprice FROM CustomerDailyRevenue WHERE customer = 'Customer#000000048'"); @@ -68,7 +70,31 @@ public void testDynamicMetric() // select two dimensions and measure actual = query(manifest, "SELECT customer, date, totalprice FROM CustomerDailyRevenue WHERE customer = 'Customer#000000048' ORDER BY 1, 2"); expected = query(manifest, "SELECT c.name as customer, o.orderdate as date, SUM(o.totalprice) as totalprice FROM Orders o LEFT JOIN Customer c ON o.custkey = c.custkey\n" + - "WHERE c.name = 'Customer#000000048' GROUP BY 1, 2 ORDER BY 1, 2"); + "WHERE c.name = 'Customer#000000048' GROUP BY 1, 2 ORDER BY 1, 2"); assertThat(actual).isEqualTo(expected); } + + @Test + public void testTargetDotAllWillNotIncludeCalculatedField() + { + // Show that there is a calculated field in Orders. + QueryResultDto calculated = query(manifest, "SELECT nation_name FROM \"Orders\" LIMIT 1"); + assertThat(calculated.getColumns()).containsExactly(column("nation_name", "VARCHAR")); + + List expectedColumns = ImmutableList.of( + column("orderkey", "INTEGER"), + column("custkey", "INTEGER"), + column("orderstatus", "VARCHAR"), + column("totalprice", "DECIMAL(15,2)"), + column("orderdate", "DATE")); + + QueryResultDto case1 = query(manifest, "SELECT \"Orders\".* FROM \"Orders\" LIMIT 1"); + assertThat(case1.getColumns()).isEqualTo(expectedColumns); + + QueryResultDto case2 = query(manifest, "SELECT o.* FROM \"Orders\" AS o LIMIT 1"); + assertThat(case2.getColumns()).isEqualTo(expectedColumns); + + QueryResultDto case3 = query(manifest, "SELECT o.* FROM \"Orders\" AS o JOIN \"Customer\" AS c ON o.custkey = c.custkey LIMIT 1"); + assertThat(case3.getColumns()).isEqualTo(expectedColumns); + } } diff --git a/wren-tests/src/test/resources/duckdb/mdl.json b/wren-tests/src/test/resources/duckdb/mdl.json index 8df23a197..145e5edb8 100644 --- a/wren-tests/src/test/resources/duckdb/mdl.json +++ b/wren-tests/src/test/resources/duckdb/mdl.json @@ -9,7 +9,14 @@ { "name": "orderkey", "expression": "o_orderkey", - "type": "int4" + "type": "int4", + "properties": { + "field": "test1", + "nested": { + "n1": "test1", + "n2": "test2" + } + } }, { "name": "custkey", diff --git a/wren-tests/src/test/resources/tpch_mdl.json b/wren-tests/src/test/resources/tpch_mdl.json index d8f3b15bc..6592a28a1 100644 --- a/wren-tests/src/test/resources/tpch_mdl.json +++ b/wren-tests/src/test/resources/tpch_mdl.json @@ -9,7 +9,14 @@ { "name": "orderkey", "expression": "o_orderkey", - "type": "int4" + "type": "int4", + "properties": { + "field": "test1", + "nested": { + "n1": "test1", + "n2": "test2" + } + } }, { "name": "custkey",