polyglot-sql 0.5.3__tar.gz → 0.5.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.5.3 → polyglot_sql-0.5.5}/Cargo.lock +5 -5
- {polyglot_sql-0.5.3 → polyglot_sql-0.5.5}/Cargo.toml +1 -1
- {polyglot_sql-0.5.3 → polyglot_sql-0.5.5}/PKG-INFO +18 -2
- {polyglot_sql-0.5.3/crates/polyglot-sql-python → polyglot_sql-0.5.5}/README.md +17 -1
- {polyglot_sql-0.5.3 → polyglot_sql-0.5.5}/crates/polyglot-sql/Cargo.toml +1 -1
- {polyglot_sql-0.5.3 → polyglot_sql-0.5.5}/crates/polyglot-sql/README.md +3 -0
- polyglot_sql-0.5.5/crates/polyglot-sql/src/ast_json.rs +278 -0
- {polyglot_sql-0.5.3 → polyglot_sql-0.5.5}/crates/polyglot-sql/src/ast_transforms.rs +119 -20
- {polyglot_sql-0.5.3 → polyglot_sql-0.5.5}/crates/polyglot-sql/src/builder.rs +27 -0
- {polyglot_sql-0.5.3 → polyglot_sql-0.5.5}/crates/polyglot-sql/src/dialects/tsql.rs +2 -0
- {polyglot_sql-0.5.3 → polyglot_sql-0.5.5}/crates/polyglot-sql/src/generator.rs +6 -1
- {polyglot_sql-0.5.3 → polyglot_sql-0.5.5}/crates/polyglot-sql/src/lib.rs +5 -2
- {polyglot_sql-0.5.3 → polyglot_sql-0.5.5}/crates/polyglot-sql/src/query_analysis.rs +227 -14
- {polyglot_sql-0.5.3 → polyglot_sql-0.5.5}/crates/polyglot-sql/tests/query_analysis.rs +95 -0
- polyglot_sql-0.5.5/crates/polyglot-sql/tests/query_analysis_regression.rs +79 -0
- {polyglot_sql-0.5.3 → polyglot_sql-0.5.5}/crates/polyglot-sql/tests/tsql_regression.rs +41 -1
- {polyglot_sql-0.5.3 → polyglot_sql-0.5.5/crates/polyglot-sql-python}/README.md +17 -1
- {polyglot_sql-0.5.3 → polyglot_sql-0.5.5}/crates/polyglot-sql-python/docs/index.md +3 -0
- {polyglot_sql-0.5.3 → polyglot_sql-0.5.5}/crates/polyglot-sql-python/src/expr.rs +4 -3
- {polyglot_sql-0.5.3 → polyglot_sql-0.5.5}/crates/polyglot-sql-python/src/helpers.rs +10 -3
- {polyglot_sql-0.5.3 → polyglot_sql-0.5.5}/crates/polyglot-sql-python/src/lib.rs +3 -0
- {polyglot_sql-0.5.3 → polyglot_sql-0.5.5}/crates/polyglot-sql-python/src/transforms.rs +50 -0
- {polyglot_sql-0.5.3 → polyglot_sql-0.5.5}/crates/polyglot-sql-python/tests/test_generate.py +27 -0
- {polyglot_sql-0.5.3 → polyglot_sql-0.5.5}/crates/polyglot-sql-python/tests/test_query_analysis.py +35 -0
- {polyglot_sql-0.5.3 → polyglot_sql-0.5.5}/crates/polyglot-sql-python/tests/test_transforms.py +13 -0
- {polyglot_sql-0.5.3 → polyglot_sql-0.5.5}/python/polyglot_sql/__init__.py +6 -0
- {polyglot_sql-0.5.3 → polyglot_sql-0.5.5}/python/polyglot_sql/__init__.pyi +36 -0
- {polyglot_sql-0.5.3 → polyglot_sql-0.5.5}/crates/polyglot-sql/benches/in_list.rs +0 -0
- {polyglot_sql-0.5.3 → polyglot_sql-0.5.5}/crates/polyglot-sql/benches/parsing.rs +0 -0
- {polyglot_sql-0.5.3 → polyglot_sql-0.5.5}/crates/polyglot-sql/benches/rust_parsing.rs +0 -0
- {polyglot_sql-0.5.3 → polyglot_sql-0.5.5}/crates/polyglot-sql/benches/transpile.rs +0 -0
- {polyglot_sql-0.5.3 → polyglot_sql-0.5.5}/crates/polyglot-sql/examples/basic_usage.rs +0 -0
- {polyglot_sql-0.5.3 → polyglot_sql-0.5.5}/crates/polyglot-sql/examples/bench_json.rs +0 -0
- {polyglot_sql-0.5.3 → polyglot_sql-0.5.5}/crates/polyglot-sql/src/dialects/athena.rs +0 -0
- {polyglot_sql-0.5.3 → polyglot_sql-0.5.5}/crates/polyglot-sql/src/dialects/bigquery.rs +0 -0
- {polyglot_sql-0.5.3 → polyglot_sql-0.5.5}/crates/polyglot-sql/src/dialects/clickhouse.rs +0 -0
- {polyglot_sql-0.5.3 → polyglot_sql-0.5.5}/crates/polyglot-sql/src/dialects/cockroachdb.rs +0 -0
- {polyglot_sql-0.5.3 → polyglot_sql-0.5.5}/crates/polyglot-sql/src/dialects/databricks.rs +0 -0
- {polyglot_sql-0.5.3 → polyglot_sql-0.5.5}/crates/polyglot-sql/src/dialects/datafusion.rs +0 -0
- {polyglot_sql-0.5.3 → polyglot_sql-0.5.5}/crates/polyglot-sql/src/dialects/doris.rs +0 -0
- {polyglot_sql-0.5.3 → polyglot_sql-0.5.5}/crates/polyglot-sql/src/dialects/dremio.rs +0 -0
- {polyglot_sql-0.5.3 → polyglot_sql-0.5.5}/crates/polyglot-sql/src/dialects/drill.rs +0 -0
- {polyglot_sql-0.5.3 → polyglot_sql-0.5.5}/crates/polyglot-sql/src/dialects/druid.rs +0 -0
- {polyglot_sql-0.5.3 → polyglot_sql-0.5.5}/crates/polyglot-sql/src/dialects/duckdb.rs +0 -0
- {polyglot_sql-0.5.3 → polyglot_sql-0.5.5}/crates/polyglot-sql/src/dialects/dune.rs +0 -0
- {polyglot_sql-0.5.3 → polyglot_sql-0.5.5}/crates/polyglot-sql/src/dialects/exasol.rs +0 -0
- {polyglot_sql-0.5.3 → polyglot_sql-0.5.5}/crates/polyglot-sql/src/dialects/fabric.rs +0 -0
- {polyglot_sql-0.5.3 → polyglot_sql-0.5.5}/crates/polyglot-sql/src/dialects/generic.rs +0 -0
- {polyglot_sql-0.5.3 → polyglot_sql-0.5.5}/crates/polyglot-sql/src/dialects/hive.rs +0 -0
- {polyglot_sql-0.5.3 → polyglot_sql-0.5.5}/crates/polyglot-sql/src/dialects/materialize.rs +0 -0
- {polyglot_sql-0.5.3 → polyglot_sql-0.5.5}/crates/polyglot-sql/src/dialects/mod.rs +0 -0
- {polyglot_sql-0.5.3 → polyglot_sql-0.5.5}/crates/polyglot-sql/src/dialects/mysql.rs +0 -0
- {polyglot_sql-0.5.3 → polyglot_sql-0.5.5}/crates/polyglot-sql/src/dialects/oracle.rs +0 -0
- {polyglot_sql-0.5.3 → polyglot_sql-0.5.5}/crates/polyglot-sql/src/dialects/postgres.rs +0 -0
- {polyglot_sql-0.5.3 → polyglot_sql-0.5.5}/crates/polyglot-sql/src/dialects/presto.rs +0 -0
- {polyglot_sql-0.5.3 → polyglot_sql-0.5.5}/crates/polyglot-sql/src/dialects/redshift.rs +0 -0
- {polyglot_sql-0.5.3 → polyglot_sql-0.5.5}/crates/polyglot-sql/src/dialects/risingwave.rs +0 -0
- {polyglot_sql-0.5.3 → polyglot_sql-0.5.5}/crates/polyglot-sql/src/dialects/singlestore.rs +0 -0
- {polyglot_sql-0.5.3 → polyglot_sql-0.5.5}/crates/polyglot-sql/src/dialects/snowflake.rs +0 -0
- {polyglot_sql-0.5.3 → polyglot_sql-0.5.5}/crates/polyglot-sql/src/dialects/solr.rs +0 -0
- {polyglot_sql-0.5.3 → polyglot_sql-0.5.5}/crates/polyglot-sql/src/dialects/spark.rs +0 -0
- {polyglot_sql-0.5.3 → polyglot_sql-0.5.5}/crates/polyglot-sql/src/dialects/sqlite.rs +0 -0
- {polyglot_sql-0.5.3 → polyglot_sql-0.5.5}/crates/polyglot-sql/src/dialects/starrocks.rs +0 -0
- {polyglot_sql-0.5.3 → polyglot_sql-0.5.5}/crates/polyglot-sql/src/dialects/tableau.rs +0 -0
- {polyglot_sql-0.5.3 → polyglot_sql-0.5.5}/crates/polyglot-sql/src/dialects/teradata.rs +0 -0
- {polyglot_sql-0.5.3 → polyglot_sql-0.5.5}/crates/polyglot-sql/src/dialects/tidb.rs +0 -0
- {polyglot_sql-0.5.3 → polyglot_sql-0.5.5}/crates/polyglot-sql/src/dialects/trino.rs +0 -0
- {polyglot_sql-0.5.3 → polyglot_sql-0.5.5}/crates/polyglot-sql/src/diff.rs +0 -0
- {polyglot_sql-0.5.3 → polyglot_sql-0.5.5}/crates/polyglot-sql/src/error.rs +0 -0
- {polyglot_sql-0.5.3 → polyglot_sql-0.5.5}/crates/polyglot-sql/src/expressions.rs +0 -0
- {polyglot_sql-0.5.3 → polyglot_sql-0.5.5}/crates/polyglot-sql/src/function_catalog.rs +0 -0
- {polyglot_sql-0.5.3 → polyglot_sql-0.5.5}/crates/polyglot-sql/src/function_registry.rs +0 -0
- {polyglot_sql-0.5.3 → polyglot_sql-0.5.5}/crates/polyglot-sql/src/helper.rs +0 -0
- {polyglot_sql-0.5.3 → polyglot_sql-0.5.5}/crates/polyglot-sql/src/lineage.rs +0 -0
- {polyglot_sql-0.5.3 → polyglot_sql-0.5.5}/crates/polyglot-sql/src/openlineage.rs +0 -0
- {polyglot_sql-0.5.3 → polyglot_sql-0.5.5}/crates/polyglot-sql/src/optimizer/annotate_types.rs +0 -0
- {polyglot_sql-0.5.3 → polyglot_sql-0.5.5}/crates/polyglot-sql/src/optimizer/canonicalize.rs +0 -0
- {polyglot_sql-0.5.3 → polyglot_sql-0.5.5}/crates/polyglot-sql/src/optimizer/eliminate_ctes.rs +0 -0
- {polyglot_sql-0.5.3 → polyglot_sql-0.5.5}/crates/polyglot-sql/src/optimizer/eliminate_joins.rs +0 -0
- {polyglot_sql-0.5.3 → polyglot_sql-0.5.5}/crates/polyglot-sql/src/optimizer/isolate_table_selects.rs +0 -0
- {polyglot_sql-0.5.3 → polyglot_sql-0.5.5}/crates/polyglot-sql/src/optimizer/mod.rs +0 -0
- {polyglot_sql-0.5.3 → polyglot_sql-0.5.5}/crates/polyglot-sql/src/optimizer/normalize.rs +0 -0
- {polyglot_sql-0.5.3 → polyglot_sql-0.5.5}/crates/polyglot-sql/src/optimizer/normalize_identifiers.rs +0 -0
- {polyglot_sql-0.5.3 → polyglot_sql-0.5.5}/crates/polyglot-sql/src/optimizer/optimize_joins.rs +0 -0
- {polyglot_sql-0.5.3 → polyglot_sql-0.5.5}/crates/polyglot-sql/src/optimizer/optimizer.rs +0 -0
- {polyglot_sql-0.5.3 → polyglot_sql-0.5.5}/crates/polyglot-sql/src/optimizer/pushdown_predicates.rs +0 -0
- {polyglot_sql-0.5.3 → polyglot_sql-0.5.5}/crates/polyglot-sql/src/optimizer/pushdown_projections.rs +0 -0
- {polyglot_sql-0.5.3 → polyglot_sql-0.5.5}/crates/polyglot-sql/src/optimizer/qualify_columns.rs +0 -0
- {polyglot_sql-0.5.3 → polyglot_sql-0.5.5}/crates/polyglot-sql/src/optimizer/qualify_tables.rs +0 -0
- {polyglot_sql-0.5.3 → polyglot_sql-0.5.5}/crates/polyglot-sql/src/optimizer/simplify.rs +0 -0
- {polyglot_sql-0.5.3 → polyglot_sql-0.5.5}/crates/polyglot-sql/src/optimizer/subquery.rs +0 -0
- {polyglot_sql-0.5.3 → polyglot_sql-0.5.5}/crates/polyglot-sql/src/parser.rs +0 -0
- {polyglot_sql-0.5.3 → polyglot_sql-0.5.5}/crates/polyglot-sql/src/planner.rs +0 -0
- {polyglot_sql-0.5.3 → polyglot_sql-0.5.5}/crates/polyglot-sql/src/resolver.rs +0 -0
- {polyglot_sql-0.5.3 → polyglot_sql-0.5.5}/crates/polyglot-sql/src/schema.rs +0 -0
- {polyglot_sql-0.5.3 → polyglot_sql-0.5.5}/crates/polyglot-sql/src/scope.rs +0 -0
- {polyglot_sql-0.5.3 → polyglot_sql-0.5.5}/crates/polyglot-sql/src/time.rs +0 -0
- {polyglot_sql-0.5.3 → polyglot_sql-0.5.5}/crates/polyglot-sql/src/tokens.rs +0 -0
- {polyglot_sql-0.5.3 → polyglot_sql-0.5.5}/crates/polyglot-sql/src/transforms.rs +0 -0
- {polyglot_sql-0.5.3 → polyglot_sql-0.5.5}/crates/polyglot-sql/src/traversal.rs +0 -0
- {polyglot_sql-0.5.3 → polyglot_sql-0.5.5}/crates/polyglot-sql/src/trie.rs +0 -0
- {polyglot_sql-0.5.3 → polyglot_sql-0.5.5}/crates/polyglot-sql/src/validation/tests.rs +0 -0
- {polyglot_sql-0.5.3 → polyglot_sql-0.5.5}/crates/polyglot-sql/src/validation.rs +0 -0
- {polyglot_sql-0.5.3 → polyglot_sql-0.5.5}/crates/polyglot-sql/tests/analyze_failures.rs +0 -0
- {polyglot_sql-0.5.3 → polyglot_sql-0.5.5}/crates/polyglot-sql/tests/clickhouse_regression.rs +0 -0
- {polyglot_sql-0.5.3 → polyglot_sql-0.5.5}/crates/polyglot-sql/tests/common/known_failures.rs +0 -0
- {polyglot_sql-0.5.3 → polyglot_sql-0.5.5}/crates/polyglot-sql/tests/common/mod.rs +0 -0
- {polyglot_sql-0.5.3 → polyglot_sql-0.5.5}/crates/polyglot-sql/tests/common/test_data.rs +0 -0
- {polyglot_sql-0.5.3 → polyglot_sql-0.5.5}/crates/polyglot-sql/tests/common/test_runner.rs +0 -0
- {polyglot_sql-0.5.3 → polyglot_sql-0.5.5}/crates/polyglot-sql/tests/custom_clickhouse_coverage.rs +0 -0
- {polyglot_sql-0.5.3 → polyglot_sql-0.5.5}/crates/polyglot-sql/tests/custom_clickhouse_parser.rs +0 -0
- {polyglot_sql-0.5.3 → polyglot_sql-0.5.5}/crates/polyglot-sql/tests/custom_dialect.rs +0 -0
- {polyglot_sql-0.5.3 → polyglot_sql-0.5.5}/crates/polyglot-sql/tests/custom_dialect_tests.rs +0 -0
- {polyglot_sql-0.5.3 → polyglot_sql-0.5.5}/crates/polyglot-sql/tests/custom_fixtures/datafusion/ddl.json +0 -0
- {polyglot_sql-0.5.3 → polyglot_sql-0.5.5}/crates/polyglot-sql/tests/custom_fixtures/datafusion/dml.json +0 -0
- {polyglot_sql-0.5.3 → polyglot_sql-0.5.5}/crates/polyglot-sql/tests/custom_fixtures/datafusion/functions.json +0 -0
- {polyglot_sql-0.5.3 → polyglot_sql-0.5.5}/crates/polyglot-sql/tests/custom_fixtures/datafusion/identity.json +0 -0
- {polyglot_sql-0.5.3 → polyglot_sql-0.5.5}/crates/polyglot-sql/tests/custom_fixtures/datafusion/operators.json +0 -0
- {polyglot_sql-0.5.3 → polyglot_sql-0.5.5}/crates/polyglot-sql/tests/custom_fixtures/datafusion/select.json +0 -0
- {polyglot_sql-0.5.3 → polyglot_sql-0.5.5}/crates/polyglot-sql/tests/custom_fixtures/datafusion/transpilation.json +0 -0
- {polyglot_sql-0.5.3 → polyglot_sql-0.5.5}/crates/polyglot-sql/tests/custom_fixtures/datafusion/types.json +0 -0
- {polyglot_sql-0.5.3 → polyglot_sql-0.5.5}/crates/polyglot-sql/tests/data_type_api.rs +0 -0
- {polyglot_sql-0.5.3 → polyglot_sql-0.5.5}/crates/polyglot-sql/tests/deep_nesting_regression.rs +0 -0
- {polyglot_sql-0.5.3 → polyglot_sql-0.5.5}/crates/polyglot-sql/tests/dialect_matrix.rs +0 -0
- {polyglot_sql-0.5.3 → polyglot_sql-0.5.5}/crates/polyglot-sql/tests/error_handling.rs +0 -0
- {polyglot_sql-0.5.3 → polyglot_sql-0.5.5}/crates/polyglot-sql/tests/fabric_regression.rs +0 -0
- {polyglot_sql-0.5.3 → polyglot_sql-0.5.5}/crates/polyglot-sql/tests/fabric_tpch_regression.rs +0 -0
- {polyglot_sql-0.5.3 → polyglot_sql-0.5.5}/crates/polyglot-sql/tests/identity_roundtrip.rs +0 -0
- {polyglot_sql-0.5.3 → polyglot_sql-0.5.5}/crates/polyglot-sql/tests/issue201_regression_test.rs +0 -0
- {polyglot_sql-0.5.3 → polyglot_sql-0.5.5}/crates/polyglot-sql/tests/issue210_regression_test.rs +0 -0
- {polyglot_sql-0.5.3 → polyglot_sql-0.5.5}/crates/polyglot-sql/tests/issue226_regression_test.rs +0 -0
- {polyglot_sql-0.5.3 → polyglot_sql-0.5.5}/crates/polyglot-sql/tests/issue227_strict_unsupported.rs +0 -0
- {polyglot_sql-0.5.3 → polyglot_sql-0.5.5}/crates/polyglot-sql/tests/postgres_sqlite_regression.rs +0 -0
- {polyglot_sql-0.5.3 → polyglot_sql-0.5.5}/crates/polyglot-sql/tests/snowflake_regression_test.rs +0 -0
- {polyglot_sql-0.5.3 → polyglot_sql-0.5.5}/crates/polyglot-sql/tests/sqlglot_compat.rs +0 -0
- {polyglot_sql-0.5.3 → polyglot_sql-0.5.5}/crates/polyglot-sql/tests/sqlglot_dialect_identity.rs +0 -0
- {polyglot_sql-0.5.3 → polyglot_sql-0.5.5}/crates/polyglot-sql/tests/sqlglot_identity.rs +0 -0
- {polyglot_sql-0.5.3 → polyglot_sql-0.5.5}/crates/polyglot-sql/tests/sqlglot_identity_detailed.rs +0 -0
- {polyglot_sql-0.5.3 → polyglot_sql-0.5.5}/crates/polyglot-sql/tests/sqlglot_parser.rs +0 -0
- {polyglot_sql-0.5.3 → polyglot_sql-0.5.5}/crates/polyglot-sql/tests/sqlglot_pretty.rs +0 -0
- {polyglot_sql-0.5.3 → polyglot_sql-0.5.5}/crates/polyglot-sql/tests/sqlglot_transpilation.rs +0 -0
- {polyglot_sql-0.5.3 → polyglot_sql-0.5.5}/crates/polyglot-sql/tests/sqlglot_transpile.rs +0 -0
- {polyglot_sql-0.5.3 → polyglot_sql-0.5.5}/crates/polyglot-sql/tests/tpch_transpile_stack.rs +0 -0
- {polyglot_sql-0.5.3 → polyglot_sql-0.5.5}/crates/polyglot-sql/tests/transform_regression.rs +0 -0
- {polyglot_sql-0.5.3 → polyglot_sql-0.5.5}/crates/polyglot-sql-function-catalogs/Cargo.toml +0 -0
- {polyglot_sql-0.5.3 → polyglot_sql-0.5.5}/crates/polyglot-sql-function-catalogs/README.md +0 -0
- {polyglot_sql-0.5.3 → polyglot_sql-0.5.5}/crates/polyglot-sql-function-catalogs/src/clickhouse.rs +0 -0
- {polyglot_sql-0.5.3 → polyglot_sql-0.5.5}/crates/polyglot-sql-function-catalogs/src/duckdb.rs +0 -0
- {polyglot_sql-0.5.3 → polyglot_sql-0.5.5}/crates/polyglot-sql-function-catalogs/src/lib.rs +0 -0
- {polyglot_sql-0.5.3 → polyglot_sql-0.5.5}/crates/polyglot-sql-function-catalogs/tools/clickhouse/extract_functions.py +0 -0
- {polyglot_sql-0.5.3 → polyglot_sql-0.5.5}/crates/polyglot-sql-function-catalogs/tools/duckdb/extract_functions.py +0 -0
- {polyglot_sql-0.5.3 → polyglot_sql-0.5.5}/crates/polyglot-sql-python/Cargo.toml +0 -0
- {polyglot_sql-0.5.3 → polyglot_sql-0.5.5}/crates/polyglot-sql-python/docs/api.md +0 -0
- {polyglot_sql-0.5.3 → polyglot_sql-0.5.5}/crates/polyglot-sql-python/mkdocs.yml +0 -0
- {polyglot_sql-0.5.3 → polyglot_sql-0.5.5}/crates/polyglot-sql-python/src/annotate_types.rs +0 -0
- {polyglot_sql-0.5.3 → polyglot_sql-0.5.5}/crates/polyglot-sql-python/src/dialects.rs +0 -0
- {polyglot_sql-0.5.3 → polyglot_sql-0.5.5}/crates/polyglot-sql-python/src/diff.rs +0 -0
- {polyglot_sql-0.5.3 → polyglot_sql-0.5.5}/crates/polyglot-sql-python/src/errors.rs +0 -0
- {polyglot_sql-0.5.3 → polyglot_sql-0.5.5}/crates/polyglot-sql-python/src/expr_types.rs +0 -0
- {polyglot_sql-0.5.3 → polyglot_sql-0.5.5}/crates/polyglot-sql-python/src/format.rs +0 -0
- {polyglot_sql-0.5.3 → polyglot_sql-0.5.5}/crates/polyglot-sql-python/src/generate.rs +0 -0
- {polyglot_sql-0.5.3 → polyglot_sql-0.5.5}/crates/polyglot-sql-python/src/lineage.rs +0 -0
- {polyglot_sql-0.5.3 → polyglot_sql-0.5.5}/crates/polyglot-sql-python/src/openlineage.rs +0 -0
- {polyglot_sql-0.5.3 → polyglot_sql-0.5.5}/crates/polyglot-sql-python/src/optimize.rs +0 -0
- {polyglot_sql-0.5.3 → polyglot_sql-0.5.5}/crates/polyglot-sql-python/src/parse.rs +0 -0
- {polyglot_sql-0.5.3 → polyglot_sql-0.5.5}/crates/polyglot-sql-python/src/query_analysis.rs +0 -0
- {polyglot_sql-0.5.3 → polyglot_sql-0.5.5}/crates/polyglot-sql-python/src/tokenize.rs +0 -0
- {polyglot_sql-0.5.3 → polyglot_sql-0.5.5}/crates/polyglot-sql-python/src/transpile.rs +0 -0
- {polyglot_sql-0.5.3 → polyglot_sql-0.5.5}/crates/polyglot-sql-python/src/types.rs +0 -0
- {polyglot_sql-0.5.3 → polyglot_sql-0.5.5}/crates/polyglot-sql-python/src/validate.rs +0 -0
- {polyglot_sql-0.5.3 → polyglot_sql-0.5.5}/crates/polyglot-sql-python/tests/conftest.py +0 -0
- {polyglot_sql-0.5.3 → polyglot_sql-0.5.5}/crates/polyglot-sql-python/tests/test_compat.py +0 -0
- {polyglot_sql-0.5.3 → polyglot_sql-0.5.5}/crates/polyglot-sql-python/tests/test_dialects.py +0 -0
- {polyglot_sql-0.5.3 → polyglot_sql-0.5.5}/crates/polyglot-sql-python/tests/test_diff.py +0 -0
- {polyglot_sql-0.5.3 → polyglot_sql-0.5.5}/crates/polyglot-sql-python/tests/test_expression.py +0 -0
- {polyglot_sql-0.5.3 → polyglot_sql-0.5.5}/crates/polyglot-sql-python/tests/test_format.py +0 -0
- {polyglot_sql-0.5.3 → polyglot_sql-0.5.5}/crates/polyglot-sql-python/tests/test_lineage.py +0 -0
- {polyglot_sql-0.5.3 → polyglot_sql-0.5.5}/crates/polyglot-sql-python/tests/test_optimize.py +0 -0
- {polyglot_sql-0.5.3 → polyglot_sql-0.5.5}/crates/polyglot-sql-python/tests/test_parse.py +0 -0
- {polyglot_sql-0.5.3 → polyglot_sql-0.5.5}/crates/polyglot-sql-python/tests/test_transpile.py +0 -0
- {polyglot_sql-0.5.3 → polyglot_sql-0.5.5}/crates/polyglot-sql-python/tests/test_validate.py +0 -0
- {polyglot_sql-0.5.3 → polyglot_sql-0.5.5}/crates/polyglot-sql-python/uv.lock +0 -0
- {polyglot_sql-0.5.3 → polyglot_sql-0.5.5}/pyproject.toml +0 -0
- {polyglot_sql-0.5.3 → polyglot_sql-0.5.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.5.
|
|
608
|
+
version = "0.5.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.5.
|
|
624
|
+
version = "0.5.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.5.
|
|
634
|
+
version = "0.5.5"
|
|
635
635
|
|
|
636
636
|
[[package]]
|
|
637
637
|
name = "polyglot-sql-python"
|
|
638
|
-
version = "0.5.
|
|
638
|
+
version = "0.5.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.5.
|
|
649
|
+
version = "0.5.5"
|
|
650
650
|
dependencies = [
|
|
651
651
|
"console_error_panic_hook",
|
|
652
652
|
"js-sys",
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: polyglot-sql
|
|
3
|
-
Version: 0.5.
|
|
3
|
+
Version: 0.5.5
|
|
4
4
|
Classifier: Development Status :: 4 - Beta
|
|
5
5
|
Classifier: Intended Audience :: Developers
|
|
6
6
|
Classifier: License :: OSI Approved :: MIT License
|
|
@@ -68,6 +68,16 @@ polyglot_sql.parse_one("VARCHAR(255)", dialect="duckdb", into=polyglot_sql.DataT
|
|
|
68
68
|
polyglot_sql.format_sql("SELECT a,b FROM t WHERE x=1", dialect="postgres")
|
|
69
69
|
```
|
|
70
70
|
|
|
71
|
+
```python
|
|
72
|
+
ast = polyglot_sql.parse_one("SELECT id FROM a UNION ALL SELECT id FROM b")
|
|
73
|
+
order_expr = polyglot_sql.parse_one("SELECT id").args["expressions"][0]
|
|
74
|
+
ast = polyglot_sql.set_limit(ast, 100)
|
|
75
|
+
ast = polyglot_sql.set_offset(ast, 10)
|
|
76
|
+
ast = polyglot_sql.set_order_by(ast, order_expr)
|
|
77
|
+
polyglot_sql.generate(ast)
|
|
78
|
+
# ["SELECT id FROM a UNION ALL SELECT id FROM b ORDER BY id LIMIT 100 OFFSET 10"]
|
|
79
|
+
```
|
|
80
|
+
|
|
71
81
|
### Format Guard Behavior
|
|
72
82
|
|
|
73
83
|
`format_sql` uses Rust core formatting guards with default limits:
|
|
@@ -145,16 +155,22 @@ print(analysis["cteFacts"][0]["bodySql"]) # "SELECT id, amount FROM or
|
|
|
145
155
|
print(analysis["starProjections"][0]["expandedColumns"]) # ["id", "amount"]
|
|
146
156
|
print(analysis["projections"][0]["nullability"]) # "non_null"
|
|
147
157
|
print(analysis["baseTables"][0]["name"]) # "orders"
|
|
158
|
+
print(analysis["baseTables"][0]["table"]) # "orders"
|
|
148
159
|
```
|
|
149
160
|
|
|
150
161
|
`analysis["relations"]` reports sources visible in the analyzed scope.
|
|
151
162
|
`analysis["baseTables"]` reports deduplicated physical table dependencies across
|
|
152
|
-
nested CTEs, derived tables, subqueries, and set-operation branches.
|
|
163
|
+
nested CTEs, derived tables, subqueries, and set-operation branches. For
|
|
164
|
+
physical relation facts, `name` remains the qualified display name while
|
|
165
|
+
`catalog`, `schema`, and `table` expose parsed identifier parts. Validation
|
|
153
166
|
uses broad type families, while query analysis preserves parseable detailed
|
|
154
167
|
schema type strings for projection `typeHint` values. `analysis["cteFacts"]`
|
|
155
168
|
reports top-level CTE definitions, `analysis["starProjections"]` records the
|
|
156
169
|
original star projections and schema-expanded columns, and each projection has
|
|
157
170
|
conservative `nullability`: `"non_null"`, `"nullable"`, or `"unknown"`.
|
|
171
|
+
Function-like projections may include `transformFunction` with the function
|
|
172
|
+
name, literal arguments, and column arguments, for example for
|
|
173
|
+
`DATE_TRUNC('month', created_at)`.
|
|
158
174
|
|
|
159
175
|
Validation schema dictionaries use:
|
|
160
176
|
|
|
@@ -41,6 +41,16 @@ polyglot_sql.parse_one("VARCHAR(255)", dialect="duckdb", into=polyglot_sql.DataT
|
|
|
41
41
|
polyglot_sql.format_sql("SELECT a,b FROM t WHERE x=1", dialect="postgres")
|
|
42
42
|
```
|
|
43
43
|
|
|
44
|
+
```python
|
|
45
|
+
ast = polyglot_sql.parse_one("SELECT id FROM a UNION ALL SELECT id FROM b")
|
|
46
|
+
order_expr = polyglot_sql.parse_one("SELECT id").args["expressions"][0]
|
|
47
|
+
ast = polyglot_sql.set_limit(ast, 100)
|
|
48
|
+
ast = polyglot_sql.set_offset(ast, 10)
|
|
49
|
+
ast = polyglot_sql.set_order_by(ast, order_expr)
|
|
50
|
+
polyglot_sql.generate(ast)
|
|
51
|
+
# ["SELECT id FROM a UNION ALL SELECT id FROM b ORDER BY id LIMIT 100 OFFSET 10"]
|
|
52
|
+
```
|
|
53
|
+
|
|
44
54
|
### Format Guard Behavior
|
|
45
55
|
|
|
46
56
|
`format_sql` uses Rust core formatting guards with default limits:
|
|
@@ -118,16 +128,22 @@ print(analysis["cteFacts"][0]["bodySql"]) # "SELECT id, amount FROM or
|
|
|
118
128
|
print(analysis["starProjections"][0]["expandedColumns"]) # ["id", "amount"]
|
|
119
129
|
print(analysis["projections"][0]["nullability"]) # "non_null"
|
|
120
130
|
print(analysis["baseTables"][0]["name"]) # "orders"
|
|
131
|
+
print(analysis["baseTables"][0]["table"]) # "orders"
|
|
121
132
|
```
|
|
122
133
|
|
|
123
134
|
`analysis["relations"]` reports sources visible in the analyzed scope.
|
|
124
135
|
`analysis["baseTables"]` reports deduplicated physical table dependencies across
|
|
125
|
-
nested CTEs, derived tables, subqueries, and set-operation branches.
|
|
136
|
+
nested CTEs, derived tables, subqueries, and set-operation branches. For
|
|
137
|
+
physical relation facts, `name` remains the qualified display name while
|
|
138
|
+
`catalog`, `schema`, and `table` expose parsed identifier parts. Validation
|
|
126
139
|
uses broad type families, while query analysis preserves parseable detailed
|
|
127
140
|
schema type strings for projection `typeHint` values. `analysis["cteFacts"]`
|
|
128
141
|
reports top-level CTE definitions, `analysis["starProjections"]` records the
|
|
129
142
|
original star projections and schema-expanded columns, and each projection has
|
|
130
143
|
conservative `nullability`: `"non_null"`, `"nullable"`, or `"unknown"`.
|
|
144
|
+
Function-like projections may include `transformFunction` with the function
|
|
145
|
+
name, literal arguments, and column arguments, for example for
|
|
146
|
+
`DATE_TRUNC('month', created_at)`.
|
|
131
147
|
|
|
132
148
|
Validation schema dictionaries use:
|
|
133
149
|
|
|
@@ -106,7 +106,7 @@ thiserror = { workspace = true }
|
|
|
106
106
|
unicode-segmentation = { workspace = true }
|
|
107
107
|
stacker = { version = "0.1", optional = true }
|
|
108
108
|
ts-rs = { version = "12.0", features = ["serde-compat"], optional = true }
|
|
109
|
-
polyglot-sql-function-catalogs = { path = "../polyglot-sql-function-catalogs", version = "0.5.
|
|
109
|
+
polyglot-sql-function-catalogs = { path = "../polyglot-sql-function-catalogs", version = "0.5.5", optional = true, default-features = false }
|
|
110
110
|
|
|
111
111
|
[dev-dependencies]
|
|
112
112
|
pretty_assertions = "1.4"
|
|
@@ -318,6 +318,8 @@ scope, while `base_tables` reports deduplicated physical table dependencies
|
|
|
318
318
|
across CTEs, derived tables, subqueries, and set-operation branches. When a
|
|
319
319
|
`ValidationSchema` is supplied, detailed type strings such as `DECIMAL(10,2)`
|
|
320
320
|
are preserved in projection `type_hint` values when parseable.
|
|
321
|
+
For physical table relations, `name` remains the qualified display name and
|
|
322
|
+
`catalog`, `schema`, and `table` expose parsed identifier parts.
|
|
321
323
|
`cte_facts` reports top-level CTE names, declared columns, original CTE body SQL,
|
|
322
324
|
and CTE output columns. `star_projections` reports the original top-level star
|
|
323
325
|
projection index, optional table qualifier, and schema-expanded columns when
|
|
@@ -353,6 +355,7 @@ assert_eq!(analysis.cte_facts[0].body_sql, "SELECT id, amount FROM orders");
|
|
|
353
355
|
assert_eq!(analysis.star_projections[0].expanded_columns, vec!["id", "amount"]);
|
|
354
356
|
assert_eq!(analysis.projections[0].nullability, ProjectionNullability::NonNull);
|
|
355
357
|
assert_eq!(analysis.base_tables[0].name, "orders");
|
|
358
|
+
assert_eq!(analysis.base_tables[0].table.as_deref(), Some("orders"));
|
|
356
359
|
```
|
|
357
360
|
|
|
358
361
|
External JSON schemas use this shape:
|
|
@@ -0,0 +1,278 @@
|
|
|
1
|
+
//! Compatibility helpers for Expression JSON accepted at public API boundaries.
|
|
2
|
+
//!
|
|
3
|
+
//! The canonical AST representation is the serde JSON form of [`Expression`].
|
|
4
|
+
//! Some wrappers historically exposed JavaScript values that stringify unit
|
|
5
|
+
//! variants such as `Expression::Null` as `{}`. These helpers repair that
|
|
6
|
+
//! narrow compatibility shape while preserving errors for unknown AST forms.
|
|
7
|
+
|
|
8
|
+
use crate::expressions::Expression;
|
|
9
|
+
use serde_json::{json, Value};
|
|
10
|
+
|
|
11
|
+
/// Deserialize a single [`Expression`] from JSON, accepting known compatibility
|
|
12
|
+
/// shapes used by SDK boundaries.
|
|
13
|
+
pub fn expression_from_str(input: &str) -> Result<Expression, String> {
|
|
14
|
+
let value: Value = serde_json::from_str(input).map_err(|error| error.to_string())?;
|
|
15
|
+
expression_from_value(value)
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
/// Deserialize a list of [`Expression`] values from JSON, accepting known
|
|
19
|
+
/// compatibility shapes used by SDK boundaries.
|
|
20
|
+
pub fn expressions_from_str(input: &str) -> Result<Vec<Expression>, String> {
|
|
21
|
+
let value: Value = serde_json::from_str(input).map_err(|error| error.to_string())?;
|
|
22
|
+
expressions_from_value(value)
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
/// Deserialize a single [`Expression`] from a JSON value.
|
|
26
|
+
pub fn expression_from_value(value: Value) -> Result<Expression, String> {
|
|
27
|
+
match serde_json::from_value::<Expression>(value.clone()) {
|
|
28
|
+
Ok(expression) => Ok(expression),
|
|
29
|
+
Err(original_error) => {
|
|
30
|
+
let mut repaired = value;
|
|
31
|
+
normalize_expression_value(&mut repaired);
|
|
32
|
+
serde_json::from_value::<Expression>(repaired)
|
|
33
|
+
.map_err(|error| format!("{error}; original error: {original_error}"))
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/// Deserialize a list of [`Expression`] values from a JSON value.
|
|
39
|
+
pub fn expressions_from_value(value: Value) -> Result<Vec<Expression>, String> {
|
|
40
|
+
match serde_json::from_value::<Vec<Expression>>(value.clone()) {
|
|
41
|
+
Ok(expressions) => Ok(expressions),
|
|
42
|
+
Err(original_error) => {
|
|
43
|
+
let mut repaired = value;
|
|
44
|
+
if let Value::Array(items) = &mut repaired {
|
|
45
|
+
for item in items {
|
|
46
|
+
normalize_expression_value(item);
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
serde_json::from_value::<Vec<Expression>>(repaired)
|
|
50
|
+
.map_err(|error| format!("{error}; original error: {original_error}"))
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/// Serialize an [`Expression`] through serde JSON first so wrapper layers expose
|
|
56
|
+
/// canonical JSON-compatible values instead of runtime-specific JS shapes.
|
|
57
|
+
pub fn expression_to_value(expression: &Expression) -> Result<Value, String> {
|
|
58
|
+
serde_json::to_value(expression).map_err(|error| error.to_string())
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
fn normalize_expression_value(value: &mut Value) {
|
|
62
|
+
match value {
|
|
63
|
+
Value::Object(map) if map.is_empty() => {
|
|
64
|
+
*value = json!({ "null": null });
|
|
65
|
+
}
|
|
66
|
+
Value::Object(map) if map.len() == 1 => {
|
|
67
|
+
let (kind, payload) = map.iter_mut().next().expect("single entry exists");
|
|
68
|
+
if kind == "null" && matches!(payload, Value::Object(inner) if inner.is_empty()) {
|
|
69
|
+
*payload = Value::Null;
|
|
70
|
+
return;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
if kind == "is_null" {
|
|
74
|
+
normalize_is_null_payload(payload);
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
match payload {
|
|
78
|
+
Value::Object(payload_map) => normalize_struct_fields(payload_map),
|
|
79
|
+
Value::Array(items) => {
|
|
80
|
+
for item in items {
|
|
81
|
+
normalize_expression_value(item);
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
_ => {}
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
Value::Object(map) => normalize_struct_fields(map),
|
|
88
|
+
Value::Array(items) => {
|
|
89
|
+
for item in items {
|
|
90
|
+
normalize_expression_value(item);
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
_ => {}
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
fn normalize_is_null_payload(payload: &mut Value) {
|
|
98
|
+
let Value::Array(items) = payload else {
|
|
99
|
+
return;
|
|
100
|
+
};
|
|
101
|
+
if items.len() != 1 {
|
|
102
|
+
return;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
let mut this = items.remove(0);
|
|
106
|
+
normalize_expression_value(&mut this);
|
|
107
|
+
*payload = json!({
|
|
108
|
+
"this": this,
|
|
109
|
+
"not": false,
|
|
110
|
+
"postfix_form": false
|
|
111
|
+
});
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
fn normalize_struct_fields(map: &mut serde_json::Map<String, Value>) {
|
|
115
|
+
for (key, value) in map.iter_mut() {
|
|
116
|
+
if is_expression_array_field(key) {
|
|
117
|
+
if let Value::Array(items) = value {
|
|
118
|
+
for item in items {
|
|
119
|
+
normalize_expression_value(item);
|
|
120
|
+
}
|
|
121
|
+
continue;
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
if is_expression_field(key) {
|
|
126
|
+
normalize_expression_value(value);
|
|
127
|
+
} else {
|
|
128
|
+
normalize_container_value(value);
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
fn normalize_container_value(value: &mut Value) {
|
|
134
|
+
match value {
|
|
135
|
+
Value::Object(map) => normalize_struct_fields(map),
|
|
136
|
+
Value::Array(items) => {
|
|
137
|
+
for item in items {
|
|
138
|
+
normalize_container_value(item);
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
_ => {}
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
fn is_expression_field(key: &str) -> bool {
|
|
146
|
+
matches!(
|
|
147
|
+
key,
|
|
148
|
+
"this"
|
|
149
|
+
| "left"
|
|
150
|
+
| "right"
|
|
151
|
+
| "low"
|
|
152
|
+
| "high"
|
|
153
|
+
| "expression"
|
|
154
|
+
| "condition"
|
|
155
|
+
| "query"
|
|
156
|
+
| "unnest"
|
|
157
|
+
| "on"
|
|
158
|
+
| "prewhere"
|
|
159
|
+
| "where"
|
|
160
|
+
| "where_clause"
|
|
161
|
+
| "having"
|
|
162
|
+
| "qualify"
|
|
163
|
+
| "source"
|
|
164
|
+
| "body"
|
|
165
|
+
| "default"
|
|
166
|
+
| "true_value"
|
|
167
|
+
| "false_value"
|
|
168
|
+
| "else_result"
|
|
169
|
+
| "target"
|
|
170
|
+
| "format"
|
|
171
|
+
| "limit"
|
|
172
|
+
| "offset"
|
|
173
|
+
)
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
fn is_expression_array_field(key: &str) -> bool {
|
|
177
|
+
matches!(
|
|
178
|
+
key,
|
|
179
|
+
"expressions"
|
|
180
|
+
| "args"
|
|
181
|
+
| "columns"
|
|
182
|
+
| "values"
|
|
183
|
+
| "partition_by"
|
|
184
|
+
| "order_by"
|
|
185
|
+
| "group_by"
|
|
186
|
+
| "distinct_on"
|
|
187
|
+
| "limit_by"
|
|
188
|
+
| "settings"
|
|
189
|
+
| "for_xml"
|
|
190
|
+
| "for_json"
|
|
191
|
+
| "exclude"
|
|
192
|
+
| "hints"
|
|
193
|
+
| "on_columns"
|
|
194
|
+
)
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
#[cfg(test)]
|
|
198
|
+
mod tests {
|
|
199
|
+
use super::*;
|
|
200
|
+
use crate::expressions::Expression;
|
|
201
|
+
|
|
202
|
+
fn column_json(name: &str) -> Value {
|
|
203
|
+
json!({
|
|
204
|
+
"column": {
|
|
205
|
+
"name": {
|
|
206
|
+
"name": name,
|
|
207
|
+
"quoted": false,
|
|
208
|
+
"trailing_comments": [],
|
|
209
|
+
"span": null
|
|
210
|
+
},
|
|
211
|
+
"table": null,
|
|
212
|
+
"join_mark": false,
|
|
213
|
+
"trailing_comments": [],
|
|
214
|
+
"span": null,
|
|
215
|
+
"inferred_type": null
|
|
216
|
+
}
|
|
217
|
+
})
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
#[test]
|
|
221
|
+
fn expression_from_value_accepts_legacy_empty_object_as_null() {
|
|
222
|
+
assert!(matches!(
|
|
223
|
+
expression_from_value(json!({})).expect("empty object should become NULL"),
|
|
224
|
+
Expression::Null(_)
|
|
225
|
+
));
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
#[test]
|
|
229
|
+
fn expression_from_value_accepts_empty_null_payload() {
|
|
230
|
+
assert!(matches!(
|
|
231
|
+
expression_from_value(json!({ "null": {} })).expect("empty null payload should work"),
|
|
232
|
+
Expression::Null(_)
|
|
233
|
+
));
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
#[test]
|
|
237
|
+
fn expression_from_value_repairs_nested_null_expression_fields() {
|
|
238
|
+
let expression = expression_from_value(json!({
|
|
239
|
+
"between": {
|
|
240
|
+
"this": column_json("created_at"),
|
|
241
|
+
"low": { "literal": { "literal_type": "string", "value": "2024-01-01" } },
|
|
242
|
+
"high": {},
|
|
243
|
+
"not": false,
|
|
244
|
+
"symmetric": null
|
|
245
|
+
}
|
|
246
|
+
}))
|
|
247
|
+
.expect("between high bound should be repaired");
|
|
248
|
+
|
|
249
|
+
match expression {
|
|
250
|
+
Expression::Between(between) => assert!(matches!(between.high, Expression::Null(_))),
|
|
251
|
+
other => panic!("expected between expression, got {other:?}"),
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
#[test]
|
|
256
|
+
fn expression_from_value_accepts_is_null_array_shorthand() {
|
|
257
|
+
let expression = expression_from_value(json!({
|
|
258
|
+
"is_null": [column_json("deleted_at")]
|
|
259
|
+
}))
|
|
260
|
+
.expect("is_null shorthand should be repaired");
|
|
261
|
+
|
|
262
|
+
match expression {
|
|
263
|
+
Expression::IsNull(is_null) => {
|
|
264
|
+
assert!(!is_null.not);
|
|
265
|
+
assert!(!is_null.postfix_form);
|
|
266
|
+
assert!(matches!(is_null.this, Expression::Column(_)));
|
|
267
|
+
}
|
|
268
|
+
other => panic!("expected is_null expression, got {other:?}"),
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
#[test]
|
|
273
|
+
fn expression_from_value_keeps_unknown_ast_as_error() {
|
|
274
|
+
let error = expression_from_value(json!({ "not_a_real_expression": {} }))
|
|
275
|
+
.expect_err("unknown expression should stay invalid");
|
|
276
|
+
assert!(error.contains("not_a_real_expression"));
|
|
277
|
+
}
|
|
278
|
+
}
|
|
@@ -98,33 +98,108 @@ pub fn remove_where(expr: Expression) -> Expression {
|
|
|
98
98
|
}
|
|
99
99
|
|
|
100
100
|
// ---------------------------------------------------------------------------
|
|
101
|
-
// LIMIT / OFFSET
|
|
101
|
+
// LIMIT / OFFSET / ORDER BY
|
|
102
102
|
// ---------------------------------------------------------------------------
|
|
103
103
|
|
|
104
|
-
/// Set the LIMIT on a SELECT.
|
|
104
|
+
/// Set the LIMIT on a SELECT or set operation.
|
|
105
105
|
pub fn set_limit(expr: Expression, limit: usize) -> Expression {
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
Expression::Select(sel)
|
|
113
|
-
|
|
114
|
-
|
|
106
|
+
set_limit_expr(expr, Expression::number(limit as i64))
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
/// Set the LIMIT on a SELECT or set operation using an expression.
|
|
110
|
+
pub fn set_limit_expr(expr: Expression, limit: Expression) -> Expression {
|
|
111
|
+
match expr {
|
|
112
|
+
Expression::Select(mut sel) => {
|
|
113
|
+
sel.limit = Some(Limit {
|
|
114
|
+
this: limit,
|
|
115
|
+
percent: false,
|
|
116
|
+
comments: Vec::new(),
|
|
117
|
+
});
|
|
118
|
+
Expression::Select(sel)
|
|
119
|
+
}
|
|
120
|
+
Expression::Union(mut union) => {
|
|
121
|
+
union.limit = Some(Box::new(limit));
|
|
122
|
+
Expression::Union(union)
|
|
123
|
+
}
|
|
124
|
+
Expression::Intersect(mut intersect) => {
|
|
125
|
+
intersect.limit = Some(Box::new(limit));
|
|
126
|
+
Expression::Intersect(intersect)
|
|
127
|
+
}
|
|
128
|
+
Expression::Except(mut except) => {
|
|
129
|
+
except.limit = Some(Box::new(limit));
|
|
130
|
+
Expression::Except(except)
|
|
131
|
+
}
|
|
132
|
+
other => other,
|
|
115
133
|
}
|
|
116
134
|
}
|
|
117
135
|
|
|
118
|
-
/// Set the OFFSET on a SELECT.
|
|
136
|
+
/// Set the OFFSET on a SELECT or set operation.
|
|
119
137
|
pub fn set_offset(expr: Expression, offset: usize) -> Expression {
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
138
|
+
set_offset_expr(expr, Expression::number(offset as i64))
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
/// Set the OFFSET on a SELECT or set operation using an expression.
|
|
142
|
+
pub fn set_offset_expr(expr: Expression, offset: Expression) -> Expression {
|
|
143
|
+
match expr {
|
|
144
|
+
Expression::Select(mut sel) => {
|
|
145
|
+
sel.offset = Some(Offset {
|
|
146
|
+
this: offset,
|
|
147
|
+
rows: None,
|
|
148
|
+
});
|
|
149
|
+
Expression::Select(sel)
|
|
150
|
+
}
|
|
151
|
+
Expression::Union(mut union) => {
|
|
152
|
+
union.offset = Some(Box::new(offset));
|
|
153
|
+
Expression::Union(union)
|
|
154
|
+
}
|
|
155
|
+
Expression::Intersect(mut intersect) => {
|
|
156
|
+
intersect.offset = Some(Box::new(offset));
|
|
157
|
+
Expression::Intersect(intersect)
|
|
158
|
+
}
|
|
159
|
+
Expression::Except(mut except) => {
|
|
160
|
+
except.offset = Some(Box::new(offset));
|
|
161
|
+
Expression::Except(except)
|
|
162
|
+
}
|
|
163
|
+
other => other,
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
/// Set the ORDER BY clause on a SELECT or set operation.
|
|
168
|
+
///
|
|
169
|
+
/// Bare expressions are normalized to ascending order expressions. Existing
|
|
170
|
+
/// `Ordered` expressions preserve their direction and null-ordering metadata.
|
|
171
|
+
pub fn set_order_by(expr: Expression, expressions: Vec<Expression>) -> Expression {
|
|
172
|
+
let order_by = OrderBy {
|
|
173
|
+
expressions: expressions.into_iter().map(normalize_ordered).collect(),
|
|
174
|
+
siblings: false,
|
|
175
|
+
comments: Vec::new(),
|
|
176
|
+
};
|
|
177
|
+
|
|
178
|
+
match expr {
|
|
179
|
+
Expression::Select(mut sel) => {
|
|
180
|
+
sel.order_by = Some(order_by);
|
|
181
|
+
Expression::Select(sel)
|
|
182
|
+
}
|
|
183
|
+
Expression::Union(mut union) => {
|
|
184
|
+
union.order_by = Some(order_by);
|
|
185
|
+
Expression::Union(union)
|
|
186
|
+
}
|
|
187
|
+
Expression::Intersect(mut intersect) => {
|
|
188
|
+
intersect.order_by = Some(order_by);
|
|
189
|
+
Expression::Intersect(intersect)
|
|
190
|
+
}
|
|
191
|
+
Expression::Except(mut except) => {
|
|
192
|
+
except.order_by = Some(order_by);
|
|
193
|
+
Expression::Except(except)
|
|
194
|
+
}
|
|
195
|
+
other => other,
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
fn normalize_ordered(expression: Expression) -> Ordered {
|
|
200
|
+
match expression {
|
|
201
|
+
Expression::Ordered(ordered) => *ordered,
|
|
202
|
+
other => Ordered::asc(other),
|
|
128
203
|
}
|
|
129
204
|
}
|
|
130
205
|
|
|
@@ -608,6 +683,14 @@ mod tests {
|
|
|
608
683
|
assert!(sql.contains("LIMIT 10"), "Expected LIMIT in: {}", sql);
|
|
609
684
|
}
|
|
610
685
|
|
|
686
|
+
#[test]
|
|
687
|
+
fn test_set_limit_on_set_operation() {
|
|
688
|
+
let expr = parse_one("SELECT a FROM t UNION ALL SELECT a FROM u");
|
|
689
|
+
let result = set_limit(expr, 10);
|
|
690
|
+
let sql = result.sql();
|
|
691
|
+
assert_eq!(sql, "SELECT a FROM t UNION ALL SELECT a FROM u LIMIT 10");
|
|
692
|
+
}
|
|
693
|
+
|
|
611
694
|
#[test]
|
|
612
695
|
fn test_set_offset() {
|
|
613
696
|
let expr = parse_one("SELECT a FROM t");
|
|
@@ -616,6 +699,22 @@ mod tests {
|
|
|
616
699
|
assert!(sql.contains("OFFSET 5"), "Expected OFFSET in: {}", sql);
|
|
617
700
|
}
|
|
618
701
|
|
|
702
|
+
#[test]
|
|
703
|
+
fn test_set_offset_on_set_operation() {
|
|
704
|
+
let expr = parse_one("SELECT a FROM t UNION ALL SELECT a FROM u");
|
|
705
|
+
let result = set_offset(expr, 5);
|
|
706
|
+
let sql = result.sql();
|
|
707
|
+
assert_eq!(sql, "SELECT a FROM t UNION ALL SELECT a FROM u OFFSET 5");
|
|
708
|
+
}
|
|
709
|
+
|
|
710
|
+
#[test]
|
|
711
|
+
fn test_set_order_by_on_set_operation() {
|
|
712
|
+
let expr = parse_one("SELECT a FROM t UNION ALL SELECT a FROM u");
|
|
713
|
+
let result = set_order_by(expr, vec![Expression::column("a")]);
|
|
714
|
+
let sql = result.sql();
|
|
715
|
+
assert_eq!(sql, "SELECT a FROM t UNION ALL SELECT a FROM u ORDER BY a");
|
|
716
|
+
}
|
|
717
|
+
|
|
619
718
|
#[test]
|
|
620
719
|
fn test_remove_limit_offset() {
|
|
621
720
|
let expr = parse_one("SELECT a FROM t LIMIT 10 OFFSET 5");
|
|
@@ -134,6 +134,27 @@ fn builder_table_ref(name: &str) -> TableRef {
|
|
|
134
134
|
/// assert_eq!(c.to_sql(), "users.name");
|
|
135
135
|
/// ```
|
|
136
136
|
pub fn col(name: &str) -> Expr {
|
|
137
|
+
let parts: Vec<&str> = name.split('.').collect();
|
|
138
|
+
if parts.len() >= 3 && parts.iter().all(|part| !part.is_empty()) {
|
|
139
|
+
let mut expr = Expression::boxed_column(Column {
|
|
140
|
+
name: builder_identifier(parts[1]),
|
|
141
|
+
table: Some(builder_identifier(parts[0])),
|
|
142
|
+
join_mark: false,
|
|
143
|
+
trailing_comments: Vec::new(),
|
|
144
|
+
span: None,
|
|
145
|
+
inferred_type: None,
|
|
146
|
+
});
|
|
147
|
+
|
|
148
|
+
for field in &parts[2..] {
|
|
149
|
+
expr = Expression::Dot(Box::new(DotAccess {
|
|
150
|
+
this: expr,
|
|
151
|
+
field: builder_identifier(field),
|
|
152
|
+
}));
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
return Expr(expr);
|
|
156
|
+
}
|
|
157
|
+
|
|
137
158
|
if let Some((table, column)) = name.rsplit_once('.') {
|
|
138
159
|
Expr(Expression::boxed_column(Column {
|
|
139
160
|
name: builder_identifier(column),
|
|
@@ -2891,6 +2912,12 @@ mod tests {
|
|
|
2891
2912
|
assert_eq!(sql, "SELECT u.id, u.name FROM users");
|
|
2892
2913
|
}
|
|
2893
2914
|
|
|
2915
|
+
#[test]
|
|
2916
|
+
fn test_nested_dot_column() {
|
|
2917
|
+
let sql = select([col("t.s.f")]).from("users").to_sql();
|
|
2918
|
+
assert_eq!(sql, "SELECT t.s.f FROM users");
|
|
2919
|
+
}
|
|
2920
|
+
|
|
2894
2921
|
#[test]
|
|
2895
2922
|
fn test_not_condition() {
|
|
2896
2923
|
let sql = select(["id"])
|
|
@@ -36,6 +36,8 @@ impl DialectImpl for TSQLDialect {
|
|
|
36
36
|
config.identifiers.insert('[', ']');
|
|
37
37
|
// SQL Server also supports double quotes (when QUOTED_IDENTIFIER is ON)
|
|
38
38
|
config.identifiers.insert('"', '"');
|
|
39
|
+
// SQL Server uses 0x-prefixed binary/varbinary hex literals.
|
|
40
|
+
config.hex_number_strings = true;
|
|
39
41
|
config
|
|
40
42
|
}
|
|
41
43
|
|
|
@@ -14733,8 +14733,13 @@ impl Generator {
|
|
|
14733
14733
|
}
|
|
14734
14734
|
}
|
|
14735
14735
|
Literal::HexString(h) => {
|
|
14736
|
-
// Most dialects use lowercase x'...' for hex literals
|
|
14736
|
+
// Most dialects use lowercase x'...' for hex literals.
|
|
14737
14737
|
match self.config.dialect {
|
|
14738
|
+
Some(DialectType::TSQL) | Some(DialectType::Fabric) => {
|
|
14739
|
+
self.write("0x");
|
|
14740
|
+
self.write(h);
|
|
14741
|
+
return Ok(());
|
|
14742
|
+
}
|
|
14738
14743
|
Some(DialectType::Spark)
|
|
14739
14744
|
| Some(DialectType::Databricks)
|
|
14740
14745
|
| Some(DialectType::Teradata) => self.write("X'"),
|