polyglot-sql 0.3.4__tar.gz → 0.3.5__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {polyglot_sql-0.3.4 → polyglot_sql-0.3.5}/Cargo.lock +5 -5
- {polyglot_sql-0.3.4 → polyglot_sql-0.3.5}/Cargo.toml +1 -1
- {polyglot_sql-0.3.4 → polyglot_sql-0.3.5}/PKG-INFO +1 -1
- {polyglot_sql-0.3.4 → polyglot_sql-0.3.5}/crates/polyglot-sql/Cargo.toml +1 -1
- {polyglot_sql-0.3.4 → polyglot_sql-0.3.5}/crates/polyglot-sql/src/dialects/mod.rs +85 -1
- {polyglot_sql-0.3.4 → polyglot_sql-0.3.5}/crates/polyglot-sql/src/dialects/sqlite.rs +201 -5
- {polyglot_sql-0.3.4 → polyglot_sql-0.3.5}/crates/polyglot-sql/src/parser.rs +29 -12
- polyglot_sql-0.3.5/crates/polyglot-sql/tests/postgres_sqlite_regression.rs +155 -0
- {polyglot_sql-0.3.4 → polyglot_sql-0.3.5}/crates/polyglot-sql/tests/snowflake_regression_test.rs +102 -0
- {polyglot_sql-0.3.4 → polyglot_sql-0.3.5}/crates/polyglot-sql/tests/tpch_transpile_stack.rs +5 -2
- {polyglot_sql-0.3.4 → polyglot_sql-0.3.5}/crates/polyglot-sql-python/tests/test_compat.py +2 -2
- {polyglot_sql-0.3.4 → polyglot_sql-0.3.5}/README.md +0 -0
- {polyglot_sql-0.3.4 → polyglot_sql-0.3.5}/crates/polyglot-sql/README.md +0 -0
- {polyglot_sql-0.3.4 → polyglot_sql-0.3.5}/crates/polyglot-sql/benches/in_list.rs +0 -0
- {polyglot_sql-0.3.4 → polyglot_sql-0.3.5}/crates/polyglot-sql/benches/parsing.rs +0 -0
- {polyglot_sql-0.3.4 → polyglot_sql-0.3.5}/crates/polyglot-sql/benches/rust_parsing.rs +0 -0
- {polyglot_sql-0.3.4 → polyglot_sql-0.3.5}/crates/polyglot-sql/benches/transpile.rs +0 -0
- {polyglot_sql-0.3.4 → polyglot_sql-0.3.5}/crates/polyglot-sql/examples/basic_usage.rs +0 -0
- {polyglot_sql-0.3.4 → polyglot_sql-0.3.5}/crates/polyglot-sql/examples/bench_json.rs +0 -0
- {polyglot_sql-0.3.4 → polyglot_sql-0.3.5}/crates/polyglot-sql/src/ast_transforms.rs +0 -0
- {polyglot_sql-0.3.4 → polyglot_sql-0.3.5}/crates/polyglot-sql/src/builder.rs +0 -0
- {polyglot_sql-0.3.4 → polyglot_sql-0.3.5}/crates/polyglot-sql/src/dialects/athena.rs +0 -0
- {polyglot_sql-0.3.4 → polyglot_sql-0.3.5}/crates/polyglot-sql/src/dialects/bigquery.rs +0 -0
- {polyglot_sql-0.3.4 → polyglot_sql-0.3.5}/crates/polyglot-sql/src/dialects/clickhouse.rs +0 -0
- {polyglot_sql-0.3.4 → polyglot_sql-0.3.5}/crates/polyglot-sql/src/dialects/cockroachdb.rs +0 -0
- {polyglot_sql-0.3.4 → polyglot_sql-0.3.5}/crates/polyglot-sql/src/dialects/databricks.rs +0 -0
- {polyglot_sql-0.3.4 → polyglot_sql-0.3.5}/crates/polyglot-sql/src/dialects/datafusion.rs +0 -0
- {polyglot_sql-0.3.4 → polyglot_sql-0.3.5}/crates/polyglot-sql/src/dialects/doris.rs +0 -0
- {polyglot_sql-0.3.4 → polyglot_sql-0.3.5}/crates/polyglot-sql/src/dialects/dremio.rs +0 -0
- {polyglot_sql-0.3.4 → polyglot_sql-0.3.5}/crates/polyglot-sql/src/dialects/drill.rs +0 -0
- {polyglot_sql-0.3.4 → polyglot_sql-0.3.5}/crates/polyglot-sql/src/dialects/druid.rs +0 -0
- {polyglot_sql-0.3.4 → polyglot_sql-0.3.5}/crates/polyglot-sql/src/dialects/duckdb.rs +0 -0
- {polyglot_sql-0.3.4 → polyglot_sql-0.3.5}/crates/polyglot-sql/src/dialects/dune.rs +0 -0
- {polyglot_sql-0.3.4 → polyglot_sql-0.3.5}/crates/polyglot-sql/src/dialects/exasol.rs +0 -0
- {polyglot_sql-0.3.4 → polyglot_sql-0.3.5}/crates/polyglot-sql/src/dialects/fabric.rs +0 -0
- {polyglot_sql-0.3.4 → polyglot_sql-0.3.5}/crates/polyglot-sql/src/dialects/generic.rs +0 -0
- {polyglot_sql-0.3.4 → polyglot_sql-0.3.5}/crates/polyglot-sql/src/dialects/hive.rs +0 -0
- {polyglot_sql-0.3.4 → polyglot_sql-0.3.5}/crates/polyglot-sql/src/dialects/materialize.rs +0 -0
- {polyglot_sql-0.3.4 → polyglot_sql-0.3.5}/crates/polyglot-sql/src/dialects/mysql.rs +0 -0
- {polyglot_sql-0.3.4 → polyglot_sql-0.3.5}/crates/polyglot-sql/src/dialects/oracle.rs +0 -0
- {polyglot_sql-0.3.4 → polyglot_sql-0.3.5}/crates/polyglot-sql/src/dialects/postgres.rs +0 -0
- {polyglot_sql-0.3.4 → polyglot_sql-0.3.5}/crates/polyglot-sql/src/dialects/presto.rs +0 -0
- {polyglot_sql-0.3.4 → polyglot_sql-0.3.5}/crates/polyglot-sql/src/dialects/redshift.rs +0 -0
- {polyglot_sql-0.3.4 → polyglot_sql-0.3.5}/crates/polyglot-sql/src/dialects/risingwave.rs +0 -0
- {polyglot_sql-0.3.4 → polyglot_sql-0.3.5}/crates/polyglot-sql/src/dialects/singlestore.rs +0 -0
- {polyglot_sql-0.3.4 → polyglot_sql-0.3.5}/crates/polyglot-sql/src/dialects/snowflake.rs +0 -0
- {polyglot_sql-0.3.4 → polyglot_sql-0.3.5}/crates/polyglot-sql/src/dialects/solr.rs +0 -0
- {polyglot_sql-0.3.4 → polyglot_sql-0.3.5}/crates/polyglot-sql/src/dialects/spark.rs +0 -0
- {polyglot_sql-0.3.4 → polyglot_sql-0.3.5}/crates/polyglot-sql/src/dialects/starrocks.rs +0 -0
- {polyglot_sql-0.3.4 → polyglot_sql-0.3.5}/crates/polyglot-sql/src/dialects/tableau.rs +0 -0
- {polyglot_sql-0.3.4 → polyglot_sql-0.3.5}/crates/polyglot-sql/src/dialects/teradata.rs +0 -0
- {polyglot_sql-0.3.4 → polyglot_sql-0.3.5}/crates/polyglot-sql/src/dialects/tidb.rs +0 -0
- {polyglot_sql-0.3.4 → polyglot_sql-0.3.5}/crates/polyglot-sql/src/dialects/trino.rs +0 -0
- {polyglot_sql-0.3.4 → polyglot_sql-0.3.5}/crates/polyglot-sql/src/dialects/tsql.rs +0 -0
- {polyglot_sql-0.3.4 → polyglot_sql-0.3.5}/crates/polyglot-sql/src/diff.rs +0 -0
- {polyglot_sql-0.3.4 → polyglot_sql-0.3.5}/crates/polyglot-sql/src/error.rs +0 -0
- {polyglot_sql-0.3.4 → polyglot_sql-0.3.5}/crates/polyglot-sql/src/expressions.rs +0 -0
- {polyglot_sql-0.3.4 → polyglot_sql-0.3.5}/crates/polyglot-sql/src/function_catalog.rs +0 -0
- {polyglot_sql-0.3.4 → polyglot_sql-0.3.5}/crates/polyglot-sql/src/function_registry.rs +0 -0
- {polyglot_sql-0.3.4 → polyglot_sql-0.3.5}/crates/polyglot-sql/src/generator.rs +0 -0
- {polyglot_sql-0.3.4 → polyglot_sql-0.3.5}/crates/polyglot-sql/src/helper.rs +0 -0
- {polyglot_sql-0.3.4 → polyglot_sql-0.3.5}/crates/polyglot-sql/src/lib.rs +0 -0
- {polyglot_sql-0.3.4 → polyglot_sql-0.3.5}/crates/polyglot-sql/src/lineage.rs +0 -0
- {polyglot_sql-0.3.4 → polyglot_sql-0.3.5}/crates/polyglot-sql/src/optimizer/annotate_types.rs +0 -0
- {polyglot_sql-0.3.4 → polyglot_sql-0.3.5}/crates/polyglot-sql/src/optimizer/canonicalize.rs +0 -0
- {polyglot_sql-0.3.4 → polyglot_sql-0.3.5}/crates/polyglot-sql/src/optimizer/eliminate_ctes.rs +0 -0
- {polyglot_sql-0.3.4 → polyglot_sql-0.3.5}/crates/polyglot-sql/src/optimizer/eliminate_joins.rs +0 -0
- {polyglot_sql-0.3.4 → polyglot_sql-0.3.5}/crates/polyglot-sql/src/optimizer/isolate_table_selects.rs +0 -0
- {polyglot_sql-0.3.4 → polyglot_sql-0.3.5}/crates/polyglot-sql/src/optimizer/mod.rs +0 -0
- {polyglot_sql-0.3.4 → polyglot_sql-0.3.5}/crates/polyglot-sql/src/optimizer/normalize.rs +0 -0
- {polyglot_sql-0.3.4 → polyglot_sql-0.3.5}/crates/polyglot-sql/src/optimizer/normalize_identifiers.rs +0 -0
- {polyglot_sql-0.3.4 → polyglot_sql-0.3.5}/crates/polyglot-sql/src/optimizer/optimize_joins.rs +0 -0
- {polyglot_sql-0.3.4 → polyglot_sql-0.3.5}/crates/polyglot-sql/src/optimizer/optimizer.rs +0 -0
- {polyglot_sql-0.3.4 → polyglot_sql-0.3.5}/crates/polyglot-sql/src/optimizer/pushdown_predicates.rs +0 -0
- {polyglot_sql-0.3.4 → polyglot_sql-0.3.5}/crates/polyglot-sql/src/optimizer/pushdown_projections.rs +0 -0
- {polyglot_sql-0.3.4 → polyglot_sql-0.3.5}/crates/polyglot-sql/src/optimizer/qualify_columns.rs +0 -0
- {polyglot_sql-0.3.4 → polyglot_sql-0.3.5}/crates/polyglot-sql/src/optimizer/qualify_tables.rs +0 -0
- {polyglot_sql-0.3.4 → polyglot_sql-0.3.5}/crates/polyglot-sql/src/optimizer/simplify.rs +0 -0
- {polyglot_sql-0.3.4 → polyglot_sql-0.3.5}/crates/polyglot-sql/src/optimizer/subquery.rs +0 -0
- {polyglot_sql-0.3.4 → polyglot_sql-0.3.5}/crates/polyglot-sql/src/planner.rs +0 -0
- {polyglot_sql-0.3.4 → polyglot_sql-0.3.5}/crates/polyglot-sql/src/resolver.rs +0 -0
- {polyglot_sql-0.3.4 → polyglot_sql-0.3.5}/crates/polyglot-sql/src/schema.rs +0 -0
- {polyglot_sql-0.3.4 → polyglot_sql-0.3.5}/crates/polyglot-sql/src/scope.rs +0 -0
- {polyglot_sql-0.3.4 → polyglot_sql-0.3.5}/crates/polyglot-sql/src/time.rs +0 -0
- {polyglot_sql-0.3.4 → polyglot_sql-0.3.5}/crates/polyglot-sql/src/tokens.rs +0 -0
- {polyglot_sql-0.3.4 → polyglot_sql-0.3.5}/crates/polyglot-sql/src/transforms.rs +0 -0
- {polyglot_sql-0.3.4 → polyglot_sql-0.3.5}/crates/polyglot-sql/src/traversal.rs +0 -0
- {polyglot_sql-0.3.4 → polyglot_sql-0.3.5}/crates/polyglot-sql/src/trie.rs +0 -0
- {polyglot_sql-0.3.4 → polyglot_sql-0.3.5}/crates/polyglot-sql/src/validation/tests.rs +0 -0
- {polyglot_sql-0.3.4 → polyglot_sql-0.3.5}/crates/polyglot-sql/src/validation.rs +0 -0
- {polyglot_sql-0.3.4 → polyglot_sql-0.3.5}/crates/polyglot-sql/tests/analyze_failures.rs +0 -0
- {polyglot_sql-0.3.4 → polyglot_sql-0.3.5}/crates/polyglot-sql/tests/clickhouse_regression.rs +0 -0
- {polyglot_sql-0.3.4 → polyglot_sql-0.3.5}/crates/polyglot-sql/tests/common/known_failures.rs +0 -0
- {polyglot_sql-0.3.4 → polyglot_sql-0.3.5}/crates/polyglot-sql/tests/common/mod.rs +0 -0
- {polyglot_sql-0.3.4 → polyglot_sql-0.3.5}/crates/polyglot-sql/tests/common/test_data.rs +0 -0
- {polyglot_sql-0.3.4 → polyglot_sql-0.3.5}/crates/polyglot-sql/tests/common/test_runner.rs +0 -0
- {polyglot_sql-0.3.4 → polyglot_sql-0.3.5}/crates/polyglot-sql/tests/custom_clickhouse_coverage.rs +0 -0
- {polyglot_sql-0.3.4 → polyglot_sql-0.3.5}/crates/polyglot-sql/tests/custom_clickhouse_parser.rs +0 -0
- {polyglot_sql-0.3.4 → polyglot_sql-0.3.5}/crates/polyglot-sql/tests/custom_dialect.rs +0 -0
- {polyglot_sql-0.3.4 → polyglot_sql-0.3.5}/crates/polyglot-sql/tests/custom_dialect_tests.rs +0 -0
- {polyglot_sql-0.3.4 → polyglot_sql-0.3.5}/crates/polyglot-sql/tests/custom_fixtures/datafusion/ddl.json +0 -0
- {polyglot_sql-0.3.4 → polyglot_sql-0.3.5}/crates/polyglot-sql/tests/custom_fixtures/datafusion/dml.json +0 -0
- {polyglot_sql-0.3.4 → polyglot_sql-0.3.5}/crates/polyglot-sql/tests/custom_fixtures/datafusion/functions.json +0 -0
- {polyglot_sql-0.3.4 → polyglot_sql-0.3.5}/crates/polyglot-sql/tests/custom_fixtures/datafusion/identity.json +0 -0
- {polyglot_sql-0.3.4 → polyglot_sql-0.3.5}/crates/polyglot-sql/tests/custom_fixtures/datafusion/operators.json +0 -0
- {polyglot_sql-0.3.4 → polyglot_sql-0.3.5}/crates/polyglot-sql/tests/custom_fixtures/datafusion/select.json +0 -0
- {polyglot_sql-0.3.4 → polyglot_sql-0.3.5}/crates/polyglot-sql/tests/custom_fixtures/datafusion/transpilation.json +0 -0
- {polyglot_sql-0.3.4 → polyglot_sql-0.3.5}/crates/polyglot-sql/tests/custom_fixtures/datafusion/types.json +0 -0
- {polyglot_sql-0.3.4 → polyglot_sql-0.3.5}/crates/polyglot-sql/tests/deep_nesting_regression.rs +0 -0
- {polyglot_sql-0.3.4 → polyglot_sql-0.3.5}/crates/polyglot-sql/tests/dialect_matrix.rs +0 -0
- {polyglot_sql-0.3.4 → polyglot_sql-0.3.5}/crates/polyglot-sql/tests/error_handling.rs +0 -0
- {polyglot_sql-0.3.4 → polyglot_sql-0.3.5}/crates/polyglot-sql/tests/fabric_regression.rs +0 -0
- {polyglot_sql-0.3.4 → polyglot_sql-0.3.5}/crates/polyglot-sql/tests/identity_roundtrip.rs +0 -0
- {polyglot_sql-0.3.4 → polyglot_sql-0.3.5}/crates/polyglot-sql/tests/sqlglot_compat.rs +0 -0
- {polyglot_sql-0.3.4 → polyglot_sql-0.3.5}/crates/polyglot-sql/tests/sqlglot_dialect_identity.rs +0 -0
- {polyglot_sql-0.3.4 → polyglot_sql-0.3.5}/crates/polyglot-sql/tests/sqlglot_identity.rs +0 -0
- {polyglot_sql-0.3.4 → polyglot_sql-0.3.5}/crates/polyglot-sql/tests/sqlglot_identity_detailed.rs +0 -0
- {polyglot_sql-0.3.4 → polyglot_sql-0.3.5}/crates/polyglot-sql/tests/sqlglot_parser.rs +0 -0
- {polyglot_sql-0.3.4 → polyglot_sql-0.3.5}/crates/polyglot-sql/tests/sqlglot_pretty.rs +0 -0
- {polyglot_sql-0.3.4 → polyglot_sql-0.3.5}/crates/polyglot-sql/tests/sqlglot_transpilation.rs +0 -0
- {polyglot_sql-0.3.4 → polyglot_sql-0.3.5}/crates/polyglot-sql/tests/sqlglot_transpile.rs +0 -0
- {polyglot_sql-0.3.4 → polyglot_sql-0.3.5}/crates/polyglot-sql/tests/transform_regression.rs +0 -0
- {polyglot_sql-0.3.4 → polyglot_sql-0.3.5}/crates/polyglot-sql/tests/tsql_regression.rs +0 -0
- {polyglot_sql-0.3.4 → polyglot_sql-0.3.5}/crates/polyglot-sql-function-catalogs/Cargo.toml +0 -0
- {polyglot_sql-0.3.4 → polyglot_sql-0.3.5}/crates/polyglot-sql-function-catalogs/README.md +0 -0
- {polyglot_sql-0.3.4 → polyglot_sql-0.3.5}/crates/polyglot-sql-function-catalogs/src/clickhouse.rs +0 -0
- {polyglot_sql-0.3.4 → polyglot_sql-0.3.5}/crates/polyglot-sql-function-catalogs/src/duckdb.rs +0 -0
- {polyglot_sql-0.3.4 → polyglot_sql-0.3.5}/crates/polyglot-sql-function-catalogs/src/lib.rs +0 -0
- {polyglot_sql-0.3.4 → polyglot_sql-0.3.5}/crates/polyglot-sql-function-catalogs/tools/clickhouse/extract_functions.py +0 -0
- {polyglot_sql-0.3.4 → polyglot_sql-0.3.5}/crates/polyglot-sql-function-catalogs/tools/duckdb/extract_functions.py +0 -0
- {polyglot_sql-0.3.4 → polyglot_sql-0.3.5}/crates/polyglot-sql-python/Cargo.toml +0 -0
- {polyglot_sql-0.3.4 → polyglot_sql-0.3.5}/crates/polyglot-sql-python/README.md +0 -0
- {polyglot_sql-0.3.4 → polyglot_sql-0.3.5}/crates/polyglot-sql-python/docs/api.md +0 -0
- {polyglot_sql-0.3.4 → polyglot_sql-0.3.5}/crates/polyglot-sql-python/docs/index.md +0 -0
- {polyglot_sql-0.3.4 → polyglot_sql-0.3.5}/crates/polyglot-sql-python/mkdocs.yml +0 -0
- {polyglot_sql-0.3.4 → polyglot_sql-0.3.5}/crates/polyglot-sql-python/src/annotate_types.rs +0 -0
- {polyglot_sql-0.3.4 → polyglot_sql-0.3.5}/crates/polyglot-sql-python/src/dialects.rs +0 -0
- {polyglot_sql-0.3.4 → polyglot_sql-0.3.5}/crates/polyglot-sql-python/src/diff.rs +0 -0
- {polyglot_sql-0.3.4 → polyglot_sql-0.3.5}/crates/polyglot-sql-python/src/errors.rs +0 -0
- {polyglot_sql-0.3.4 → polyglot_sql-0.3.5}/crates/polyglot-sql-python/src/expr.rs +0 -0
- {polyglot_sql-0.3.4 → polyglot_sql-0.3.5}/crates/polyglot-sql-python/src/expr_types.rs +0 -0
- {polyglot_sql-0.3.4 → polyglot_sql-0.3.5}/crates/polyglot-sql-python/src/format.rs +0 -0
- {polyglot_sql-0.3.4 → polyglot_sql-0.3.5}/crates/polyglot-sql-python/src/generate.rs +0 -0
- {polyglot_sql-0.3.4 → polyglot_sql-0.3.5}/crates/polyglot-sql-python/src/helpers.rs +0 -0
- {polyglot_sql-0.3.4 → polyglot_sql-0.3.5}/crates/polyglot-sql-python/src/lib.rs +0 -0
- {polyglot_sql-0.3.4 → polyglot_sql-0.3.5}/crates/polyglot-sql-python/src/lineage.rs +0 -0
- {polyglot_sql-0.3.4 → polyglot_sql-0.3.5}/crates/polyglot-sql-python/src/optimize.rs +0 -0
- {polyglot_sql-0.3.4 → polyglot_sql-0.3.5}/crates/polyglot-sql-python/src/parse.rs +0 -0
- {polyglot_sql-0.3.4 → polyglot_sql-0.3.5}/crates/polyglot-sql-python/src/tokenize.rs +0 -0
- {polyglot_sql-0.3.4 → polyglot_sql-0.3.5}/crates/polyglot-sql-python/src/transpile.rs +0 -0
- {polyglot_sql-0.3.4 → polyglot_sql-0.3.5}/crates/polyglot-sql-python/src/types.rs +0 -0
- {polyglot_sql-0.3.4 → polyglot_sql-0.3.5}/crates/polyglot-sql-python/src/validate.rs +0 -0
- {polyglot_sql-0.3.4 → polyglot_sql-0.3.5}/crates/polyglot-sql-python/tests/conftest.py +0 -0
- {polyglot_sql-0.3.4 → polyglot_sql-0.3.5}/crates/polyglot-sql-python/tests/test_dialects.py +0 -0
- {polyglot_sql-0.3.4 → polyglot_sql-0.3.5}/crates/polyglot-sql-python/tests/test_diff.py +0 -0
- {polyglot_sql-0.3.4 → polyglot_sql-0.3.5}/crates/polyglot-sql-python/tests/test_expression.py +0 -0
- {polyglot_sql-0.3.4 → polyglot_sql-0.3.5}/crates/polyglot-sql-python/tests/test_format.py +0 -0
- {polyglot_sql-0.3.4 → polyglot_sql-0.3.5}/crates/polyglot-sql-python/tests/test_generate.py +0 -0
- {polyglot_sql-0.3.4 → polyglot_sql-0.3.5}/crates/polyglot-sql-python/tests/test_lineage.py +0 -0
- {polyglot_sql-0.3.4 → polyglot_sql-0.3.5}/crates/polyglot-sql-python/tests/test_optimize.py +0 -0
- {polyglot_sql-0.3.4 → polyglot_sql-0.3.5}/crates/polyglot-sql-python/tests/test_parse.py +0 -0
- {polyglot_sql-0.3.4 → polyglot_sql-0.3.5}/crates/polyglot-sql-python/tests/test_transpile.py +0 -0
- {polyglot_sql-0.3.4 → polyglot_sql-0.3.5}/crates/polyglot-sql-python/tests/test_validate.py +0 -0
- {polyglot_sql-0.3.4 → polyglot_sql-0.3.5}/crates/polyglot-sql-python/uv.lock +0 -0
- {polyglot_sql-0.3.4 → polyglot_sql-0.3.5}/pyproject.toml +0 -0
- {polyglot_sql-0.3.4 → polyglot_sql-0.3.5}/python/polyglot_sql/__init__.py +0 -0
- {polyglot_sql-0.3.4 → polyglot_sql-0.3.5}/python/polyglot_sql/__init__.pyi +0 -0
- {polyglot_sql-0.3.4 → polyglot_sql-0.3.5}/python/polyglot_sql/py.typed +0 -0
|
@@ -605,7 +605,7 @@ dependencies = [
|
|
|
605
605
|
|
|
606
606
|
[[package]]
|
|
607
607
|
name = "polyglot-sql"
|
|
608
|
-
version = "0.3.
|
|
608
|
+
version = "0.3.5"
|
|
609
609
|
dependencies = [
|
|
610
610
|
"criterion",
|
|
611
611
|
"once_cell",
|
|
@@ -621,7 +621,7 @@ dependencies = [
|
|
|
621
621
|
|
|
622
622
|
[[package]]
|
|
623
623
|
name = "polyglot-sql-ffi"
|
|
624
|
-
version = "0.3.
|
|
624
|
+
version = "0.3.5"
|
|
625
625
|
dependencies = [
|
|
626
626
|
"cbindgen",
|
|
627
627
|
"polyglot-sql",
|
|
@@ -631,11 +631,11 @@ dependencies = [
|
|
|
631
631
|
|
|
632
632
|
[[package]]
|
|
633
633
|
name = "polyglot-sql-function-catalogs"
|
|
634
|
-
version = "0.3.
|
|
634
|
+
version = "0.3.5"
|
|
635
635
|
|
|
636
636
|
[[package]]
|
|
637
637
|
name = "polyglot-sql-python"
|
|
638
|
-
version = "0.3.
|
|
638
|
+
version = "0.3.5"
|
|
639
639
|
dependencies = [
|
|
640
640
|
"polyglot-sql",
|
|
641
641
|
"pyo3",
|
|
@@ -646,7 +646,7 @@ dependencies = [
|
|
|
646
646
|
|
|
647
647
|
[[package]]
|
|
648
648
|
name = "polyglot-sql-wasm"
|
|
649
|
-
version = "0.3.
|
|
649
|
+
version = "0.3.5"
|
|
650
650
|
dependencies = [
|
|
651
651
|
"console_error_panic_hook",
|
|
652
652
|
"js-sys",
|
|
@@ -83,7 +83,7 @@ thiserror = { workspace = true }
|
|
|
83
83
|
unicode-segmentation = { workspace = true }
|
|
84
84
|
stacker = { version = "0.1", optional = true }
|
|
85
85
|
ts-rs = { version = "12.0", features = ["serde-compat"], optional = true }
|
|
86
|
-
polyglot-sql-function-catalogs = { path = "../polyglot-sql-function-catalogs", version = "0.3.
|
|
86
|
+
polyglot-sql-function-catalogs = { path = "../polyglot-sql-function-catalogs", version = "0.3.5", optional = true, default-features = false }
|
|
87
87
|
|
|
88
88
|
[dev-dependencies]
|
|
89
89
|
pretty_assertions = "1.4"
|
|
@@ -161,7 +161,7 @@ use crate::error::Result;
|
|
|
161
161
|
use crate::expressions::{Expression, Function, FunctionBody, Identifier, Null};
|
|
162
162
|
use crate::generator::{Generator, GeneratorConfig};
|
|
163
163
|
use crate::parser::Parser;
|
|
164
|
-
use crate::tokens::{Token, Tokenizer, TokenizerConfig};
|
|
164
|
+
use crate::tokens::{Token, TokenType, Tokenizer, TokenizerConfig};
|
|
165
165
|
use serde::{Deserialize, Serialize};
|
|
166
166
|
use std::collections::HashMap;
|
|
167
167
|
use std::sync::{Arc, LazyLock, RwLock};
|
|
@@ -3750,6 +3750,11 @@ impl Dialect {
|
|
|
3750
3750
|
pretty: bool,
|
|
3751
3751
|
) -> Result<Vec<String>> {
|
|
3752
3752
|
let target = target_dialect.dialect_type;
|
|
3753
|
+
if matches!(self.dialect_type, DialectType::PostgreSQL)
|
|
3754
|
+
&& matches!(target, DialectType::SQLite)
|
|
3755
|
+
{
|
|
3756
|
+
self.reject_pgvector_distance_operators_for_sqlite(sql)?;
|
|
3757
|
+
}
|
|
3753
3758
|
let expressions = self.parse(sql)?;
|
|
3754
3759
|
let generic_identity =
|
|
3755
3760
|
self.dialect_type == DialectType::Generic && target == DialectType::Generic;
|
|
@@ -4063,6 +4068,14 @@ impl Dialect {
|
|
|
4063
4068
|
let normalized =
|
|
4064
4069
|
Self::cross_dialect_normalize(normalized, self.dialect_type, target)?;
|
|
4065
4070
|
|
|
4071
|
+
let normalized = if matches!(self.dialect_type, DialectType::PostgreSQL)
|
|
4072
|
+
&& matches!(target, DialectType::SQLite)
|
|
4073
|
+
{
|
|
4074
|
+
Self::normalize_postgres_to_sqlite_types(normalized)?
|
|
4075
|
+
} else {
|
|
4076
|
+
normalized
|
|
4077
|
+
};
|
|
4078
|
+
|
|
4066
4079
|
// For DuckDB target from BigQuery source: wrap UNNEST of struct arrays in
|
|
4067
4080
|
// (SELECT UNNEST(..., max_depth => 2)) subquery
|
|
4068
4081
|
// Must run BEFORE unnest_alias_to_column_alias since it changes alias structure
|
|
@@ -4222,6 +4235,77 @@ impl Dialect {
|
|
|
4222
4235
|
// Transpile-only methods: cross-dialect normalization and helpers
|
|
4223
4236
|
#[cfg(feature = "transpile")]
|
|
4224
4237
|
impl Dialect {
|
|
4238
|
+
fn reject_pgvector_distance_operators_for_sqlite(&self, sql: &str) -> Result<()> {
|
|
4239
|
+
let tokens = self.tokenize(sql)?;
|
|
4240
|
+
for (i, token) in tokens.iter().enumerate() {
|
|
4241
|
+
if token.token_type == TokenType::NullsafeEq {
|
|
4242
|
+
return Err(crate::error::Error::unsupported(
|
|
4243
|
+
"PostgreSQL pgvector cosine distance operator <=>",
|
|
4244
|
+
"SQLite",
|
|
4245
|
+
));
|
|
4246
|
+
}
|
|
4247
|
+
if token.token_type == TokenType::Lt
|
|
4248
|
+
&& tokens
|
|
4249
|
+
.get(i + 1)
|
|
4250
|
+
.is_some_and(|token| token.token_type == TokenType::Tilde)
|
|
4251
|
+
&& tokens
|
|
4252
|
+
.get(i + 2)
|
|
4253
|
+
.is_some_and(|token| token.token_type == TokenType::Gt)
|
|
4254
|
+
{
|
|
4255
|
+
return Err(crate::error::Error::unsupported(
|
|
4256
|
+
"PostgreSQL pgvector Hamming distance operator <~>",
|
|
4257
|
+
"SQLite",
|
|
4258
|
+
));
|
|
4259
|
+
}
|
|
4260
|
+
}
|
|
4261
|
+
Ok(())
|
|
4262
|
+
}
|
|
4263
|
+
|
|
4264
|
+
fn normalize_postgres_to_sqlite_types(expr: Expression) -> Result<Expression> {
|
|
4265
|
+
fn sqlite_type(dt: crate::expressions::DataType) -> crate::expressions::DataType {
|
|
4266
|
+
use crate::expressions::DataType;
|
|
4267
|
+
|
|
4268
|
+
match dt {
|
|
4269
|
+
DataType::Bit { .. } => DataType::Int {
|
|
4270
|
+
length: None,
|
|
4271
|
+
integer_spelling: true,
|
|
4272
|
+
},
|
|
4273
|
+
DataType::TextWithLength { .. } => DataType::Text,
|
|
4274
|
+
DataType::VarChar { .. } => DataType::Text,
|
|
4275
|
+
DataType::Char { .. } => DataType::Text,
|
|
4276
|
+
DataType::Timestamp { timezone: true, .. } => DataType::Text,
|
|
4277
|
+
DataType::Custom { name } => {
|
|
4278
|
+
let base = name
|
|
4279
|
+
.split_once('(')
|
|
4280
|
+
.map_or(name.as_str(), |(base, _)| base)
|
|
4281
|
+
.trim();
|
|
4282
|
+
if base.eq_ignore_ascii_case("TSVECTOR")
|
|
4283
|
+
|| base.eq_ignore_ascii_case("TIMESTAMPTZ")
|
|
4284
|
+
|| base.eq_ignore_ascii_case("TIMESTAMP WITH TIME ZONE")
|
|
4285
|
+
|| base.eq_ignore_ascii_case("NVARCHAR")
|
|
4286
|
+
|| base.eq_ignore_ascii_case("NCHAR")
|
|
4287
|
+
{
|
|
4288
|
+
DataType::Text
|
|
4289
|
+
} else {
|
|
4290
|
+
DataType::Custom { name }
|
|
4291
|
+
}
|
|
4292
|
+
}
|
|
4293
|
+
_ => dt,
|
|
4294
|
+
}
|
|
4295
|
+
}
|
|
4296
|
+
|
|
4297
|
+
transform_recursive(expr, &|e| match e {
|
|
4298
|
+
Expression::DataType(dt) => Ok(Expression::DataType(sqlite_type(dt))),
|
|
4299
|
+
Expression::CreateTable(mut ct) => {
|
|
4300
|
+
for column in &mut ct.columns {
|
|
4301
|
+
column.data_type = sqlite_type(column.data_type.clone());
|
|
4302
|
+
}
|
|
4303
|
+
Ok(Expression::CreateTable(ct))
|
|
4304
|
+
}
|
|
4305
|
+
_ => Ok(e),
|
|
4306
|
+
})
|
|
4307
|
+
}
|
|
4308
|
+
|
|
4225
4309
|
/// For DuckDB target: when FROM clause contains RANGE(n), replace
|
|
4226
4310
|
/// `(ROW_NUMBER() OVER (ORDER BY 1 NULLS FIRST) - 1)` with `range` in select expressions.
|
|
4227
4311
|
/// This handles SEQ1/2/4/8 → RANGE transpilation from Snowflake.
|
|
@@ -5,7 +5,8 @@
|
|
|
5
5
|
use super::{DialectImpl, DialectType};
|
|
6
6
|
use crate::error::Result;
|
|
7
7
|
use crate::expressions::{
|
|
8
|
-
AggFunc, BinaryFunc, BinaryOp, Case, Cast, CeilFunc,
|
|
8
|
+
AggFunc, BinaryFunc, BinaryOp, Case, Cast, CeilFunc, DataType, DateTimeField, DateTruncFunc,
|
|
9
|
+
Expression, ExtractFunc, Function, LikeOp, Literal, TrimFunc, TrimPosition, UnaryFunc,
|
|
9
10
|
VarArgFunc,
|
|
10
11
|
};
|
|
11
12
|
use crate::generator::GeneratorConfig;
|
|
@@ -138,6 +139,46 @@ impl DialectImpl for SQLiteDialect {
|
|
|
138
139
|
Ok(Expression::Pragma(p))
|
|
139
140
|
}
|
|
140
141
|
|
|
142
|
+
// PostgreSQL DATE '...' literals are not SQLite syntax.
|
|
143
|
+
Expression::Literal(lit) if matches!(lit.as_ref(), Literal::Date(_)) => {
|
|
144
|
+
let Literal::Date(date) = lit.as_ref() else {
|
|
145
|
+
unreachable!()
|
|
146
|
+
};
|
|
147
|
+
Ok(Self::string_literal(date))
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
// SQLite scalar MIN/MAX are multi-argument equivalents of LEAST/GREATEST.
|
|
151
|
+
Expression::Least(f) => Ok(Self::function("MIN", f.expressions)),
|
|
152
|
+
Expression::Greatest(f) => Ok(Self::function("MAX", f.expressions)),
|
|
153
|
+
|
|
154
|
+
// PostgreSQL EXTRACT(...) lowers to SQLite strftime() for common units.
|
|
155
|
+
Expression::Extract(f) => Self::transform_extract(*f),
|
|
156
|
+
|
|
157
|
+
// PostgreSQL DATE_TRUNC(...) lowers to date/strftime() for common units.
|
|
158
|
+
Expression::DateTrunc(f) => Self::transform_date_trunc(*f),
|
|
159
|
+
|
|
160
|
+
// SQLite uses comma-call SUBSTRING/SUBSTR syntax, not SQL-standard FROM/FOR.
|
|
161
|
+
Expression::Substring(mut f) => {
|
|
162
|
+
f.from_for_syntax = false;
|
|
163
|
+
Ok(Expression::Substring(f))
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
// SQLite supports LTRIM/RTRIM/TRIM(str, chars), not TRIM(LEADING ... FROM ...).
|
|
167
|
+
Expression::Trim(f) => Ok(Self::transform_trim(*f)),
|
|
168
|
+
|
|
169
|
+
// Strip PostgreSQL's default public schema in SQLite DDL.
|
|
170
|
+
Expression::CreateTable(mut ct)
|
|
171
|
+
if ct
|
|
172
|
+
.name
|
|
173
|
+
.schema
|
|
174
|
+
.as_ref()
|
|
175
|
+
.is_some_and(|schema| schema.name.eq_ignore_ascii_case("public"))
|
|
176
|
+
&& ct.name.catalog.is_none() =>
|
|
177
|
+
{
|
|
178
|
+
ct.name.schema = None;
|
|
179
|
+
Ok(Expression::CreateTable(ct))
|
|
180
|
+
}
|
|
181
|
+
|
|
141
182
|
// Generic function transformations
|
|
142
183
|
Expression::Function(f) => self.transform_function(*f),
|
|
143
184
|
|
|
@@ -177,6 +218,122 @@ impl DialectImpl for SQLiteDialect {
|
|
|
177
218
|
}
|
|
178
219
|
|
|
179
220
|
impl SQLiteDialect {
|
|
221
|
+
fn function(name: &str, args: Vec<Expression>) -> Expression {
|
|
222
|
+
Expression::Function(Box::new(Function::new(name.to_string(), args)))
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
fn string_literal(value: &str) -> Expression {
|
|
226
|
+
Expression::Literal(Box::new(Literal::String(value.to_string())))
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
fn strftime(format: &str, expr: Expression) -> Expression {
|
|
230
|
+
Expression::Function(Box::new(Function::new(
|
|
231
|
+
"STRFTIME".to_string(),
|
|
232
|
+
vec![Self::string_literal(format), expr],
|
|
233
|
+
)))
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
fn cast(expr: Expression, to: DataType) -> Expression {
|
|
237
|
+
Expression::Cast(Box::new(Cast {
|
|
238
|
+
this: expr,
|
|
239
|
+
to,
|
|
240
|
+
trailing_comments: Vec::new(),
|
|
241
|
+
double_colon_syntax: false,
|
|
242
|
+
format: None,
|
|
243
|
+
default: None,
|
|
244
|
+
inferred_type: None,
|
|
245
|
+
}))
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
fn sqlite_int_type() -> DataType {
|
|
249
|
+
DataType::Int {
|
|
250
|
+
length: None,
|
|
251
|
+
integer_spelling: true,
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
fn sqlite_real_type() -> DataType {
|
|
256
|
+
DataType::Float {
|
|
257
|
+
precision: None,
|
|
258
|
+
scale: None,
|
|
259
|
+
real_spelling: true,
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
fn transform_extract(f: ExtractFunc) -> Result<Expression> {
|
|
264
|
+
let strftime_format = match &f.field {
|
|
265
|
+
DateTimeField::Year => "%Y",
|
|
266
|
+
DateTimeField::Month => "%m",
|
|
267
|
+
DateTimeField::Day => "%d",
|
|
268
|
+
DateTimeField::Hour => "%H",
|
|
269
|
+
DateTimeField::Minute => "%M",
|
|
270
|
+
DateTimeField::Second => "%f",
|
|
271
|
+
DateTimeField::DayOfWeek => "%w",
|
|
272
|
+
DateTimeField::DayOfYear => "%j",
|
|
273
|
+
DateTimeField::Epoch => "%s",
|
|
274
|
+
_ => return Ok(Expression::Extract(Box::new(f))),
|
|
275
|
+
};
|
|
276
|
+
let target_type = if matches!(f.field, DateTimeField::Epoch | DateTimeField::Second) {
|
|
277
|
+
Self::sqlite_real_type()
|
|
278
|
+
} else {
|
|
279
|
+
Self::sqlite_int_type()
|
|
280
|
+
};
|
|
281
|
+
Ok(Self::cast(
|
|
282
|
+
Self::strftime(strftime_format, f.this),
|
|
283
|
+
target_type,
|
|
284
|
+
))
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
fn transform_date_trunc(f: DateTruncFunc) -> Result<Expression> {
|
|
288
|
+
match &f.unit {
|
|
289
|
+
DateTimeField::Day => Ok(Self::function("DATE", vec![f.this])),
|
|
290
|
+
DateTimeField::Hour => Ok(Self::strftime("%Y-%m-%d %H:00:00", f.this)),
|
|
291
|
+
DateTimeField::Minute => Ok(Self::strftime("%Y-%m-%d %H:%M:00", f.this)),
|
|
292
|
+
DateTimeField::Second => Ok(Self::strftime("%Y-%m-%d %H:%M:%S", f.this)),
|
|
293
|
+
DateTimeField::Month => Ok(Self::strftime("%Y-%m-01", f.this)),
|
|
294
|
+
DateTimeField::Year => Ok(Self::strftime("%Y-01-01", f.this)),
|
|
295
|
+
_ => Ok(Expression::DateTrunc(Box::new(f))),
|
|
296
|
+
}
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
fn datetime_field_from_expr(expr: &Expression) -> Option<DateTimeField> {
|
|
300
|
+
let unit = match expr {
|
|
301
|
+
Expression::Literal(lit) => match lit.as_ref() {
|
|
302
|
+
Literal::String(s) => s.as_str(),
|
|
303
|
+
_ => return None,
|
|
304
|
+
},
|
|
305
|
+
Expression::Identifier(id) => id.name.as_str(),
|
|
306
|
+
Expression::Var(v) => v.this.as_str(),
|
|
307
|
+
Expression::Column(col) if col.table.is_none() => col.name.name.as_str(),
|
|
308
|
+
_ => return None,
|
|
309
|
+
};
|
|
310
|
+
match unit.to_ascii_lowercase().as_str() {
|
|
311
|
+
"year" | "yyyy" | "yy" => Some(DateTimeField::Year),
|
|
312
|
+
"month" | "mon" | "mm" => Some(DateTimeField::Month),
|
|
313
|
+
"day" | "dd" => Some(DateTimeField::Day),
|
|
314
|
+
"hour" | "hours" | "h" | "hh" | "hr" | "hrs" => Some(DateTimeField::Hour),
|
|
315
|
+
"minute" | "minutes" | "mi" | "min" | "mins" => Some(DateTimeField::Minute),
|
|
316
|
+
"second" | "seconds" | "s" | "sec" | "secs" | "ss" => Some(DateTimeField::Second),
|
|
317
|
+
"dow" | "dayofweek" | "dw" => Some(DateTimeField::DayOfWeek),
|
|
318
|
+
"doy" | "dayofyear" | "dy" => Some(DateTimeField::DayOfYear),
|
|
319
|
+
"epoch" => Some(DateTimeField::Epoch),
|
|
320
|
+
_ => None,
|
|
321
|
+
}
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
fn transform_trim(f: TrimFunc) -> Expression {
|
|
325
|
+
let function_name = match f.position {
|
|
326
|
+
TrimPosition::Leading => "LTRIM",
|
|
327
|
+
TrimPosition::Trailing => "RTRIM",
|
|
328
|
+
TrimPosition::Both => "TRIM",
|
|
329
|
+
};
|
|
330
|
+
let mut args = vec![f.this];
|
|
331
|
+
if let Some(characters) = f.characters {
|
|
332
|
+
args.push(characters);
|
|
333
|
+
}
|
|
334
|
+
Expression::Function(Box::new(Function::new(function_name.to_string(), args)))
|
|
335
|
+
}
|
|
336
|
+
|
|
180
337
|
/// Check if an expression is already a CAST to a float type
|
|
181
338
|
fn is_float_cast(expr: &Expression) -> bool {
|
|
182
339
|
if let Expression::Cast(cast) = expr {
|
|
@@ -299,6 +456,18 @@ impl SQLiteDialect {
|
|
|
299
456
|
Function::new("EDITDIST3".to_string(), f.args),
|
|
300
457
|
))),
|
|
301
458
|
|
|
459
|
+
// PostgreSQL-compatible scalar/JSON rewrites.
|
|
460
|
+
"LEAST" if !f.args.is_empty() => Ok(Self::function("MIN", f.args)),
|
|
461
|
+
"GREATEST" if !f.args.is_empty() => Ok(Self::function("MAX", f.args)),
|
|
462
|
+
"JSON_BUILD_ARRAY" => Ok(Self::function("JSON_ARRAY", f.args)),
|
|
463
|
+
"JSON_BUILD_OBJECT" => Ok(Self::function("JSON_OBJECT", f.args)),
|
|
464
|
+
"JSON_AGG" | "JSONB_AGG" if f.args.len() == 1 => {
|
|
465
|
+
Ok(Self::function("JSON_GROUP_ARRAY", f.args))
|
|
466
|
+
}
|
|
467
|
+
"JSON_OBJECT_AGG" if f.args.len() == 2 => {
|
|
468
|
+
Ok(Self::function("JSON_GROUP_OBJECT", f.args))
|
|
469
|
+
}
|
|
470
|
+
|
|
302
471
|
// GETDATE -> CURRENT_TIMESTAMP
|
|
303
472
|
"GETDATE" => Ok(Expression::CurrentTimestamp(
|
|
304
473
|
crate::expressions::CurrentTimestamp {
|
|
@@ -328,10 +497,7 @@ impl SQLiteDialect {
|
|
|
328
497
|
)))),
|
|
329
498
|
|
|
330
499
|
// SUBSTRING is native to SQLite (keep as-is)
|
|
331
|
-
"SUBSTRING" => Ok(
|
|
332
|
-
"SUBSTRING".to_string(),
|
|
333
|
-
f.args,
|
|
334
|
-
)))),
|
|
500
|
+
"SUBSTRING" => Ok(Self::function("SUBSTRING", f.args)),
|
|
335
501
|
|
|
336
502
|
// STRING_AGG -> GROUP_CONCAT in SQLite
|
|
337
503
|
"STRING_AGG" if !f.args.is_empty() => Ok(Expression::Function(Box::new(
|
|
@@ -344,6 +510,28 @@ impl SQLiteDialect {
|
|
|
344
510
|
f.args,
|
|
345
511
|
)))),
|
|
346
512
|
|
|
513
|
+
"DATE_PART" if f.args.len() == 2 => {
|
|
514
|
+
let mut args = f.args;
|
|
515
|
+
let unit = args.remove(0);
|
|
516
|
+
let expr = args.remove(0);
|
|
517
|
+
if let Some(field) = Self::datetime_field_from_expr(&unit) {
|
|
518
|
+
Self::transform_extract(ExtractFunc { this: expr, field })
|
|
519
|
+
} else {
|
|
520
|
+
Ok(Self::function("DATE_PART", vec![unit, expr]))
|
|
521
|
+
}
|
|
522
|
+
}
|
|
523
|
+
|
|
524
|
+
"DATE_TRUNC" if f.args.len() == 2 => {
|
|
525
|
+
let mut args = f.args;
|
|
526
|
+
let unit = args.remove(0);
|
|
527
|
+
let expr = args.remove(0);
|
|
528
|
+
if let Some(unit) = Self::datetime_field_from_expr(&unit) {
|
|
529
|
+
Self::transform_date_trunc(DateTruncFunc { this: expr, unit })
|
|
530
|
+
} else {
|
|
531
|
+
Ok(Self::function("DATE_TRUNC", vec![unit, expr]))
|
|
532
|
+
}
|
|
533
|
+
}
|
|
534
|
+
|
|
347
535
|
// DATEDIFF(a, b, unit_string) -> JULIANDAY arithmetic for SQLite
|
|
348
536
|
"DATEDIFF" | "DATE_DIFF" if f.args.len() == 3 => {
|
|
349
537
|
let mut args = f.args;
|
|
@@ -534,6 +722,14 @@ impl SQLiteDialect {
|
|
|
534
722
|
f.args,
|
|
535
723
|
)))),
|
|
536
724
|
|
|
725
|
+
"JSON_AGG" | "JSONB_AGG" if f.args.len() == 1 => {
|
|
726
|
+
Ok(Self::function("JSON_GROUP_ARRAY", f.args))
|
|
727
|
+
}
|
|
728
|
+
|
|
729
|
+
"JSON_OBJECT_AGG" if f.args.len() == 2 => {
|
|
730
|
+
Ok(Self::function("JSON_GROUP_OBJECT", f.args))
|
|
731
|
+
}
|
|
732
|
+
|
|
537
733
|
// Pass through everything else
|
|
538
734
|
_ => Ok(Expression::AggregateFunction(f)),
|
|
539
735
|
}
|
|
@@ -4882,8 +4882,9 @@ impl Parser {
|
|
|
4882
4882
|
} else {
|
|
4883
4883
|
return Err(self.parse_error("Expected identifier after ${"));
|
|
4884
4884
|
}
|
|
4885
|
-
} else if self.check(TokenType::String) {
|
|
4885
|
+
} else if self.check(TokenType::String) || self.check(TokenType::DollarString) {
|
|
4886
4886
|
// DuckDB allows string literals as table names: SELECT * FROM 'x.y'
|
|
4887
|
+
// Snowflake JDBC uses dollar-quoted strings for stage paths: SELECT $1 FROM $$@%"table"/$$
|
|
4887
4888
|
// Convert to a quoted identifier
|
|
4888
4889
|
let string_token = self.advance();
|
|
4889
4890
|
let table_name = Identifier {
|
|
@@ -22741,9 +22742,14 @@ impl Parser {
|
|
|
22741
22742
|
while self.check(TokenType::Slash) {
|
|
22742
22743
|
self.skip(); // consume /
|
|
22743
22744
|
stage_path.push('/');
|
|
22744
|
-
|
|
22745
|
+
// Use `while` (not `if`) because a single path segment between slashes
|
|
22746
|
+
// may consist of multiple tokens — e.g., a UUID like `c8b31cea-a6d1-4413`
|
|
22747
|
+
// tokenizes as Identifier("c8b31cea") + Dash + Identifier("a6d1") + Dash + Number(4413).
|
|
22748
|
+
while (self.check(TokenType::Var)
|
|
22745
22749
|
|| self.check_keyword()
|
|
22746
|
-
|| self.is_identifier_token()
|
|
22750
|
+
|| self.is_identifier_token()
|
|
22751
|
+
|| self.check(TokenType::Number)
|
|
22752
|
+
|| self.check(TokenType::Dash))
|
|
22747
22753
|
&& !self.check_next(TokenType::Eq)
|
|
22748
22754
|
{
|
|
22749
22755
|
stage_path.push_str(&self.advance().text);
|
|
@@ -22790,10 +22796,14 @@ impl Parser {
|
|
|
22790
22796
|
while self.check(TokenType::Slash) {
|
|
22791
22797
|
self.skip(); // consume /
|
|
22792
22798
|
stage_path.push('/');
|
|
22793
|
-
//
|
|
22794
|
-
|
|
22799
|
+
// Use `while` (not `if`) because a single path segment between slashes
|
|
22800
|
+
// may consist of multiple tokens — e.g., a UUID like `c8b31cea-a6d1-4413`
|
|
22801
|
+
// tokenizes as Identifier("c8b31cea") + Dash + Identifier("a6d1") + Dash + Number(4413).
|
|
22802
|
+
while (self.check(TokenType::Var)
|
|
22795
22803
|
|| self.check_keyword()
|
|
22796
|
-
|| self.is_identifier_token()
|
|
22804
|
+
|| self.is_identifier_token()
|
|
22805
|
+
|| self.check(TokenType::Number)
|
|
22806
|
+
|| self.check(TokenType::Dash))
|
|
22797
22807
|
&& !self.check_next(TokenType::Eq)
|
|
22798
22808
|
{
|
|
22799
22809
|
stage_path.push_str(&self.advance().text);
|
|
@@ -22828,9 +22838,14 @@ impl Parser {
|
|
|
22828
22838
|
while self.check(TokenType::Slash) {
|
|
22829
22839
|
self.skip(); // consume /
|
|
22830
22840
|
stage_path.push('/');
|
|
22831
|
-
|
|
22841
|
+
// Use `while` (not `if`) because a single path segment between slashes
|
|
22842
|
+
// may consist of multiple tokens — e.g., a UUID like `c8b31cea-a6d1-4413`
|
|
22843
|
+
// tokenizes as Identifier("c8b31cea") + Dash + Identifier("a6d1") + Dash + Number(4413).
|
|
22844
|
+
while (self.check(TokenType::Var)
|
|
22832
22845
|
|| self.check_keyword()
|
|
22833
|
-
|| self.is_identifier_token()
|
|
22846
|
+
|| self.is_identifier_token()
|
|
22847
|
+
|| self.check(TokenType::Number)
|
|
22848
|
+
|| self.check(TokenType::Dash))
|
|
22834
22849
|
&& !self.check_next(TokenType::Eq)
|
|
22835
22850
|
{
|
|
22836
22851
|
stage_path.push_str(&self.advance().text);
|
|
@@ -22863,9 +22878,11 @@ impl Parser {
|
|
|
22863
22878
|
break;
|
|
22864
22879
|
}
|
|
22865
22880
|
// Stop at ? (placeholder for stage destination), quoted string
|
|
22866
|
-
// (e.g., '@SYSTEM$BIND/...'),
|
|
22881
|
+
// (e.g., '@SYSTEM$BIND/...'), dollar-quoted string (e.g., $$@%"table"$$),
|
|
22882
|
+
// or semicolon
|
|
22867
22883
|
if self.check(TokenType::Parameter)
|
|
22868
22884
|
|| self.check(TokenType::String)
|
|
22885
|
+
|| self.check(TokenType::DollarString)
|
|
22869
22886
|
|| self.check(TokenType::Semicolon)
|
|
22870
22887
|
{
|
|
22871
22888
|
break;
|
|
@@ -22876,11 +22893,11 @@ impl Parser {
|
|
|
22876
22893
|
(source_parts.join(""), false)
|
|
22877
22894
|
};
|
|
22878
22895
|
|
|
22879
|
-
// Parse target stage (@stage_name, ? placeholder,
|
|
22896
|
+
// Parse target stage (@stage_name, ? placeholder, quoted '@stage', or dollar-quoted $$@%"stage"$$)
|
|
22880
22897
|
let target = if self.match_token(TokenType::Parameter) {
|
|
22881
22898
|
Expression::Placeholder(Placeholder { index: None })
|
|
22882
|
-
} else if self.check(TokenType::String) {
|
|
22883
|
-
// Quoted stage: '@SYSTEM$BIND/path'
|
|
22899
|
+
} else if self.check(TokenType::String) || self.check(TokenType::DollarString) {
|
|
22900
|
+
// Quoted stage: '@SYSTEM$BIND/path' or $$@%"table"$$
|
|
22884
22901
|
let tok = self.advance();
|
|
22885
22902
|
Expression::Literal(Box::new(Literal::String(tok.text.clone())))
|
|
22886
22903
|
} else {
|
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
use polyglot_sql::{transpile, DialectType, Error};
|
|
2
|
+
|
|
3
|
+
fn pg_to_sqlite(sql: &str) -> String {
|
|
4
|
+
transpile(sql, DialectType::PostgreSQL, DialectType::SQLite)
|
|
5
|
+
.expect("PostgreSQL to SQLite transpilation should succeed")
|
|
6
|
+
.join("; ")
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
#[test]
|
|
10
|
+
fn postgres_to_sqlite_rewrites_scalar_json_and_dates() {
|
|
11
|
+
let cases = [
|
|
12
|
+
("SELECT LEAST(a, b) FROM t", "SELECT MIN(a, b) FROM t"),
|
|
13
|
+
("SELECT GREATEST(a, b) FROM t", "SELECT MAX(a, b) FROM t"),
|
|
14
|
+
(
|
|
15
|
+
"SELECT JSON_AGG(name) FROM t",
|
|
16
|
+
"SELECT JSON_GROUP_ARRAY(name) FROM t",
|
|
17
|
+
),
|
|
18
|
+
(
|
|
19
|
+
"SELECT JSONB_AGG(name) FROM t",
|
|
20
|
+
"SELECT JSON_GROUP_ARRAY(name) FROM t",
|
|
21
|
+
),
|
|
22
|
+
(
|
|
23
|
+
"SELECT JSON_OBJECT_AGG(k, v) FROM t",
|
|
24
|
+
"SELECT JSON_GROUP_OBJECT(k, v) FROM t",
|
|
25
|
+
),
|
|
26
|
+
(
|
|
27
|
+
"SELECT JSON_BUILD_OBJECT('id', id, 'name', name) FROM t",
|
|
28
|
+
"SELECT JSON_OBJECT('id', id, 'name', name) FROM t",
|
|
29
|
+
),
|
|
30
|
+
(
|
|
31
|
+
"SELECT JSON_BUILD_ARRAY(a, b, c) FROM t",
|
|
32
|
+
"SELECT JSON_ARRAY(a, b, c) FROM t",
|
|
33
|
+
),
|
|
34
|
+
(
|
|
35
|
+
"SELECT DATE_PART('year', ts) FROM t",
|
|
36
|
+
"SELECT CAST(STRFTIME('%Y', ts) AS INTEGER) FROM t",
|
|
37
|
+
),
|
|
38
|
+
(
|
|
39
|
+
"SELECT DATE_PART('hour', ts) FROM t",
|
|
40
|
+
"SELECT CAST(STRFTIME('%H', ts) AS INTEGER) FROM t",
|
|
41
|
+
),
|
|
42
|
+
(
|
|
43
|
+
"SELECT DATE_PART('minute', ts) FROM t",
|
|
44
|
+
"SELECT CAST(STRFTIME('%M', ts) AS INTEGER) FROM t",
|
|
45
|
+
),
|
|
46
|
+
(
|
|
47
|
+
"SELECT DATE_PART('second', ts) FROM t",
|
|
48
|
+
"SELECT CAST(STRFTIME('%f', ts) AS REAL) FROM t",
|
|
49
|
+
),
|
|
50
|
+
(
|
|
51
|
+
"SELECT DATE_PART('dow', ts) FROM t",
|
|
52
|
+
"SELECT CAST(STRFTIME('%w', ts) AS INTEGER) FROM t",
|
|
53
|
+
),
|
|
54
|
+
(
|
|
55
|
+
"SELECT DATE_PART('doy', ts) FROM t",
|
|
56
|
+
"SELECT CAST(STRFTIME('%j', ts) AS INTEGER) FROM t",
|
|
57
|
+
),
|
|
58
|
+
(
|
|
59
|
+
"SELECT DATE_PART('epoch', ts) FROM t",
|
|
60
|
+
"SELECT CAST(STRFTIME('%s', ts) AS REAL) FROM t",
|
|
61
|
+
),
|
|
62
|
+
(
|
|
63
|
+
"SELECT EXTRACT(YEAR FROM ts) FROM t",
|
|
64
|
+
"SELECT CAST(STRFTIME('%Y', ts) AS INTEGER) FROM t",
|
|
65
|
+
),
|
|
66
|
+
(
|
|
67
|
+
"SELECT EXTRACT(HOUR FROM ts) FROM t",
|
|
68
|
+
"SELECT CAST(STRFTIME('%H', ts) AS INTEGER) FROM t",
|
|
69
|
+
),
|
|
70
|
+
(
|
|
71
|
+
"SELECT EXTRACT(MINUTE FROM ts) FROM t",
|
|
72
|
+
"SELECT CAST(STRFTIME('%M', ts) AS INTEGER) FROM t",
|
|
73
|
+
),
|
|
74
|
+
(
|
|
75
|
+
"SELECT EXTRACT(SECOND FROM ts) FROM t",
|
|
76
|
+
"SELECT CAST(STRFTIME('%f', ts) AS REAL) FROM t",
|
|
77
|
+
),
|
|
78
|
+
(
|
|
79
|
+
"SELECT EXTRACT(DOW FROM ts) FROM t",
|
|
80
|
+
"SELECT CAST(STRFTIME('%w', ts) AS INTEGER) FROM t",
|
|
81
|
+
),
|
|
82
|
+
(
|
|
83
|
+
"SELECT EXTRACT(DOY FROM ts) FROM t",
|
|
84
|
+
"SELECT CAST(STRFTIME('%j', ts) AS INTEGER) FROM t",
|
|
85
|
+
),
|
|
86
|
+
(
|
|
87
|
+
"SELECT EXTRACT(EPOCH FROM ts) FROM t",
|
|
88
|
+
"SELECT CAST(STRFTIME('%s', ts) AS REAL) FROM t",
|
|
89
|
+
),
|
|
90
|
+
(
|
|
91
|
+
"SELECT DATE_TRUNC('month', ts) FROM t",
|
|
92
|
+
"SELECT STRFTIME('%Y-%m-01', ts) FROM t",
|
|
93
|
+
),
|
|
94
|
+
(
|
|
95
|
+
"SELECT DATE_TRUNC('hour', ts) FROM t",
|
|
96
|
+
"SELECT STRFTIME('%Y-%m-%d %H:00:00', ts) FROM t",
|
|
97
|
+
),
|
|
98
|
+
(
|
|
99
|
+
"SELECT DATE_TRUNC('minute', ts) FROM t",
|
|
100
|
+
"SELECT STRFTIME('%Y-%m-%d %H:%M:00', ts) FROM t",
|
|
101
|
+
),
|
|
102
|
+
(
|
|
103
|
+
"SELECT DATE_TRUNC('second', ts) FROM t",
|
|
104
|
+
"SELECT STRFTIME('%Y-%m-%d %H:%M:%S', ts) FROM t",
|
|
105
|
+
),
|
|
106
|
+
(
|
|
107
|
+
"SELECT DATE_TRUNC('day', ts) FROM t",
|
|
108
|
+
"SELECT DATE(ts) FROM t",
|
|
109
|
+
),
|
|
110
|
+
];
|
|
111
|
+
|
|
112
|
+
for (sql, expected) in cases {
|
|
113
|
+
assert_eq!(pg_to_sqlite(sql), expected, "input: {sql}");
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
#[test]
|
|
118
|
+
fn postgres_to_sqlite_rewrites_common_syntax_and_types() {
|
|
119
|
+
let cases = [
|
|
120
|
+
(
|
|
121
|
+
"SELECT SUBSTRING(str FROM 2 FOR 5) FROM t",
|
|
122
|
+
"SELECT SUBSTRING(str, 2, 5) FROM t",
|
|
123
|
+
),
|
|
124
|
+
(
|
|
125
|
+
"SELECT TRIM(LEADING 'x' FROM str) FROM t",
|
|
126
|
+
"SELECT LTRIM(str, 'x') FROM t",
|
|
127
|
+
),
|
|
128
|
+
("SELECT DATE '2023-01-01'", "SELECT '2023-01-01'"),
|
|
129
|
+
(
|
|
130
|
+
"CREATE TABLE public.t (id INT, name TEXT)",
|
|
131
|
+
"CREATE TABLE t (id INTEGER, name TEXT)",
|
|
132
|
+
),
|
|
133
|
+
(
|
|
134
|
+
"CREATE TABLE t (flag BIT, label NVARCHAR(100), doc TSVECTOR)",
|
|
135
|
+
"CREATE TABLE t (flag INTEGER, label TEXT, doc TEXT)",
|
|
136
|
+
),
|
|
137
|
+
(
|
|
138
|
+
"CREATE TABLE t (data JSONB, ts TIMESTAMPTZ)",
|
|
139
|
+
"CREATE TABLE t (data JSON, ts TEXT)",
|
|
140
|
+
),
|
|
141
|
+
];
|
|
142
|
+
|
|
143
|
+
for (sql, expected) in cases {
|
|
144
|
+
assert_eq!(pg_to_sqlite(sql), expected, "input: {sql}");
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
#[test]
|
|
149
|
+
fn postgres_to_sqlite_rejects_pgvector_distance_operators() {
|
|
150
|
+
for sql in ["SELECT a <=> b FROM t", "SELECT a <~> b FROM t"] {
|
|
151
|
+
let err = transpile(sql, DialectType::PostgreSQL, DialectType::SQLite)
|
|
152
|
+
.expect_err("pgvector distance operators have no SQLite equivalent");
|
|
153
|
+
assert!(matches!(err, Error::Unsupported { .. }), "{err}");
|
|
154
|
+
}
|
|
155
|
+
}
|