polyglot-sql 0.5.2__tar.gz → 0.5.4__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.2 → polyglot_sql-0.5.4}/Cargo.lock +5 -5
- {polyglot_sql-0.5.2 → polyglot_sql-0.5.4}/Cargo.toml +1 -1
- {polyglot_sql-0.5.2 → polyglot_sql-0.5.4}/PKG-INFO +60 -7
- {polyglot_sql-0.5.2/crates/polyglot-sql-python → polyglot_sql-0.5.4}/README.md +59 -6
- {polyglot_sql-0.5.2 → polyglot_sql-0.5.4}/crates/polyglot-sql/Cargo.toml +1 -1
- {polyglot_sql-0.5.2 → polyglot_sql-0.5.4}/crates/polyglot-sql/README.md +50 -6
- polyglot_sql-0.5.4/crates/polyglot-sql/src/ast_json.rs +278 -0
- {polyglot_sql-0.5.2 → polyglot_sql-0.5.4}/crates/polyglot-sql/src/ast_transforms.rs +119 -20
- {polyglot_sql-0.5.2 → polyglot_sql-0.5.4}/crates/polyglot-sql/src/builder.rs +27 -0
- {polyglot_sql-0.5.2 → polyglot_sql-0.5.4}/crates/polyglot-sql/src/generator.rs +19 -0
- {polyglot_sql-0.5.2 → polyglot_sql-0.5.4}/crates/polyglot-sql/src/lib.rs +6 -3
- {polyglot_sql-0.5.2 → polyglot_sql-0.5.4}/crates/polyglot-sql/src/parser.rs +28 -0
- {polyglot_sql-0.5.2 → polyglot_sql-0.5.4}/crates/polyglot-sql/src/query_analysis.rs +679 -33
- {polyglot_sql-0.5.2 → polyglot_sql-0.5.4}/crates/polyglot-sql/tests/query_analysis.rs +278 -8
- {polyglot_sql-0.5.2 → polyglot_sql-0.5.4}/crates/polyglot-sql/tests/tsql_regression.rs +47 -1
- {polyglot_sql-0.5.2 → polyglot_sql-0.5.4/crates/polyglot-sql-python}/README.md +59 -6
- {polyglot_sql-0.5.2 → polyglot_sql-0.5.4}/crates/polyglot-sql-python/docs/index.md +44 -4
- {polyglot_sql-0.5.2 → polyglot_sql-0.5.4}/crates/polyglot-sql-python/src/expr.rs +4 -3
- {polyglot_sql-0.5.2 → polyglot_sql-0.5.4}/crates/polyglot-sql-python/src/helpers.rs +10 -3
- {polyglot_sql-0.5.2 → polyglot_sql-0.5.4}/crates/polyglot-sql-python/src/lib.rs +3 -0
- {polyglot_sql-0.5.2 → polyglot_sql-0.5.4}/crates/polyglot-sql-python/src/transforms.rs +50 -0
- {polyglot_sql-0.5.2 → polyglot_sql-0.5.4}/crates/polyglot-sql-python/tests/test_generate.py +27 -0
- polyglot_sql-0.5.4/crates/polyglot-sql-python/tests/test_query_analysis.py +132 -0
- {polyglot_sql-0.5.2 → polyglot_sql-0.5.4}/crates/polyglot-sql-python/tests/test_transforms.py +13 -0
- {polyglot_sql-0.5.2 → polyglot_sql-0.5.4}/python/polyglot_sql/__init__.py +6 -0
- {polyglot_sql-0.5.2 → polyglot_sql-0.5.4}/python/polyglot_sql/__init__.pyi +42 -1
- polyglot_sql-0.5.2/crates/polyglot-sql-python/tests/test_query_analysis.py +0 -71
- {polyglot_sql-0.5.2 → polyglot_sql-0.5.4}/crates/polyglot-sql/benches/in_list.rs +0 -0
- {polyglot_sql-0.5.2 → polyglot_sql-0.5.4}/crates/polyglot-sql/benches/parsing.rs +0 -0
- {polyglot_sql-0.5.2 → polyglot_sql-0.5.4}/crates/polyglot-sql/benches/rust_parsing.rs +0 -0
- {polyglot_sql-0.5.2 → polyglot_sql-0.5.4}/crates/polyglot-sql/benches/transpile.rs +0 -0
- {polyglot_sql-0.5.2 → polyglot_sql-0.5.4}/crates/polyglot-sql/examples/basic_usage.rs +0 -0
- {polyglot_sql-0.5.2 → polyglot_sql-0.5.4}/crates/polyglot-sql/examples/bench_json.rs +0 -0
- {polyglot_sql-0.5.2 → polyglot_sql-0.5.4}/crates/polyglot-sql/src/dialects/athena.rs +0 -0
- {polyglot_sql-0.5.2 → polyglot_sql-0.5.4}/crates/polyglot-sql/src/dialects/bigquery.rs +0 -0
- {polyglot_sql-0.5.2 → polyglot_sql-0.5.4}/crates/polyglot-sql/src/dialects/clickhouse.rs +0 -0
- {polyglot_sql-0.5.2 → polyglot_sql-0.5.4}/crates/polyglot-sql/src/dialects/cockroachdb.rs +0 -0
- {polyglot_sql-0.5.2 → polyglot_sql-0.5.4}/crates/polyglot-sql/src/dialects/databricks.rs +0 -0
- {polyglot_sql-0.5.2 → polyglot_sql-0.5.4}/crates/polyglot-sql/src/dialects/datafusion.rs +0 -0
- {polyglot_sql-0.5.2 → polyglot_sql-0.5.4}/crates/polyglot-sql/src/dialects/doris.rs +0 -0
- {polyglot_sql-0.5.2 → polyglot_sql-0.5.4}/crates/polyglot-sql/src/dialects/dremio.rs +0 -0
- {polyglot_sql-0.5.2 → polyglot_sql-0.5.4}/crates/polyglot-sql/src/dialects/drill.rs +0 -0
- {polyglot_sql-0.5.2 → polyglot_sql-0.5.4}/crates/polyglot-sql/src/dialects/druid.rs +0 -0
- {polyglot_sql-0.5.2 → polyglot_sql-0.5.4}/crates/polyglot-sql/src/dialects/duckdb.rs +0 -0
- {polyglot_sql-0.5.2 → polyglot_sql-0.5.4}/crates/polyglot-sql/src/dialects/dune.rs +0 -0
- {polyglot_sql-0.5.2 → polyglot_sql-0.5.4}/crates/polyglot-sql/src/dialects/exasol.rs +0 -0
- {polyglot_sql-0.5.2 → polyglot_sql-0.5.4}/crates/polyglot-sql/src/dialects/fabric.rs +0 -0
- {polyglot_sql-0.5.2 → polyglot_sql-0.5.4}/crates/polyglot-sql/src/dialects/generic.rs +0 -0
- {polyglot_sql-0.5.2 → polyglot_sql-0.5.4}/crates/polyglot-sql/src/dialects/hive.rs +0 -0
- {polyglot_sql-0.5.2 → polyglot_sql-0.5.4}/crates/polyglot-sql/src/dialects/materialize.rs +0 -0
- {polyglot_sql-0.5.2 → polyglot_sql-0.5.4}/crates/polyglot-sql/src/dialects/mod.rs +0 -0
- {polyglot_sql-0.5.2 → polyglot_sql-0.5.4}/crates/polyglot-sql/src/dialects/mysql.rs +0 -0
- {polyglot_sql-0.5.2 → polyglot_sql-0.5.4}/crates/polyglot-sql/src/dialects/oracle.rs +0 -0
- {polyglot_sql-0.5.2 → polyglot_sql-0.5.4}/crates/polyglot-sql/src/dialects/postgres.rs +0 -0
- {polyglot_sql-0.5.2 → polyglot_sql-0.5.4}/crates/polyglot-sql/src/dialects/presto.rs +0 -0
- {polyglot_sql-0.5.2 → polyglot_sql-0.5.4}/crates/polyglot-sql/src/dialects/redshift.rs +0 -0
- {polyglot_sql-0.5.2 → polyglot_sql-0.5.4}/crates/polyglot-sql/src/dialects/risingwave.rs +0 -0
- {polyglot_sql-0.5.2 → polyglot_sql-0.5.4}/crates/polyglot-sql/src/dialects/singlestore.rs +0 -0
- {polyglot_sql-0.5.2 → polyglot_sql-0.5.4}/crates/polyglot-sql/src/dialects/snowflake.rs +0 -0
- {polyglot_sql-0.5.2 → polyglot_sql-0.5.4}/crates/polyglot-sql/src/dialects/solr.rs +0 -0
- {polyglot_sql-0.5.2 → polyglot_sql-0.5.4}/crates/polyglot-sql/src/dialects/spark.rs +0 -0
- {polyglot_sql-0.5.2 → polyglot_sql-0.5.4}/crates/polyglot-sql/src/dialects/sqlite.rs +0 -0
- {polyglot_sql-0.5.2 → polyglot_sql-0.5.4}/crates/polyglot-sql/src/dialects/starrocks.rs +0 -0
- {polyglot_sql-0.5.2 → polyglot_sql-0.5.4}/crates/polyglot-sql/src/dialects/tableau.rs +0 -0
- {polyglot_sql-0.5.2 → polyglot_sql-0.5.4}/crates/polyglot-sql/src/dialects/teradata.rs +0 -0
- {polyglot_sql-0.5.2 → polyglot_sql-0.5.4}/crates/polyglot-sql/src/dialects/tidb.rs +0 -0
- {polyglot_sql-0.5.2 → polyglot_sql-0.5.4}/crates/polyglot-sql/src/dialects/trino.rs +0 -0
- {polyglot_sql-0.5.2 → polyglot_sql-0.5.4}/crates/polyglot-sql/src/dialects/tsql.rs +0 -0
- {polyglot_sql-0.5.2 → polyglot_sql-0.5.4}/crates/polyglot-sql/src/diff.rs +0 -0
- {polyglot_sql-0.5.2 → polyglot_sql-0.5.4}/crates/polyglot-sql/src/error.rs +0 -0
- {polyglot_sql-0.5.2 → polyglot_sql-0.5.4}/crates/polyglot-sql/src/expressions.rs +0 -0
- {polyglot_sql-0.5.2 → polyglot_sql-0.5.4}/crates/polyglot-sql/src/function_catalog.rs +0 -0
- {polyglot_sql-0.5.2 → polyglot_sql-0.5.4}/crates/polyglot-sql/src/function_registry.rs +0 -0
- {polyglot_sql-0.5.2 → polyglot_sql-0.5.4}/crates/polyglot-sql/src/helper.rs +0 -0
- {polyglot_sql-0.5.2 → polyglot_sql-0.5.4}/crates/polyglot-sql/src/lineage.rs +0 -0
- {polyglot_sql-0.5.2 → polyglot_sql-0.5.4}/crates/polyglot-sql/src/openlineage.rs +0 -0
- {polyglot_sql-0.5.2 → polyglot_sql-0.5.4}/crates/polyglot-sql/src/optimizer/annotate_types.rs +0 -0
- {polyglot_sql-0.5.2 → polyglot_sql-0.5.4}/crates/polyglot-sql/src/optimizer/canonicalize.rs +0 -0
- {polyglot_sql-0.5.2 → polyglot_sql-0.5.4}/crates/polyglot-sql/src/optimizer/eliminate_ctes.rs +0 -0
- {polyglot_sql-0.5.2 → polyglot_sql-0.5.4}/crates/polyglot-sql/src/optimizer/eliminate_joins.rs +0 -0
- {polyglot_sql-0.5.2 → polyglot_sql-0.5.4}/crates/polyglot-sql/src/optimizer/isolate_table_selects.rs +0 -0
- {polyglot_sql-0.5.2 → polyglot_sql-0.5.4}/crates/polyglot-sql/src/optimizer/mod.rs +0 -0
- {polyglot_sql-0.5.2 → polyglot_sql-0.5.4}/crates/polyglot-sql/src/optimizer/normalize.rs +0 -0
- {polyglot_sql-0.5.2 → polyglot_sql-0.5.4}/crates/polyglot-sql/src/optimizer/normalize_identifiers.rs +0 -0
- {polyglot_sql-0.5.2 → polyglot_sql-0.5.4}/crates/polyglot-sql/src/optimizer/optimize_joins.rs +0 -0
- {polyglot_sql-0.5.2 → polyglot_sql-0.5.4}/crates/polyglot-sql/src/optimizer/optimizer.rs +0 -0
- {polyglot_sql-0.5.2 → polyglot_sql-0.5.4}/crates/polyglot-sql/src/optimizer/pushdown_predicates.rs +0 -0
- {polyglot_sql-0.5.2 → polyglot_sql-0.5.4}/crates/polyglot-sql/src/optimizer/pushdown_projections.rs +0 -0
- {polyglot_sql-0.5.2 → polyglot_sql-0.5.4}/crates/polyglot-sql/src/optimizer/qualify_columns.rs +0 -0
- {polyglot_sql-0.5.2 → polyglot_sql-0.5.4}/crates/polyglot-sql/src/optimizer/qualify_tables.rs +0 -0
- {polyglot_sql-0.5.2 → polyglot_sql-0.5.4}/crates/polyglot-sql/src/optimizer/simplify.rs +0 -0
- {polyglot_sql-0.5.2 → polyglot_sql-0.5.4}/crates/polyglot-sql/src/optimizer/subquery.rs +0 -0
- {polyglot_sql-0.5.2 → polyglot_sql-0.5.4}/crates/polyglot-sql/src/planner.rs +0 -0
- {polyglot_sql-0.5.2 → polyglot_sql-0.5.4}/crates/polyglot-sql/src/resolver.rs +0 -0
- {polyglot_sql-0.5.2 → polyglot_sql-0.5.4}/crates/polyglot-sql/src/schema.rs +0 -0
- {polyglot_sql-0.5.2 → polyglot_sql-0.5.4}/crates/polyglot-sql/src/scope.rs +0 -0
- {polyglot_sql-0.5.2 → polyglot_sql-0.5.4}/crates/polyglot-sql/src/time.rs +0 -0
- {polyglot_sql-0.5.2 → polyglot_sql-0.5.4}/crates/polyglot-sql/src/tokens.rs +0 -0
- {polyglot_sql-0.5.2 → polyglot_sql-0.5.4}/crates/polyglot-sql/src/transforms.rs +0 -0
- {polyglot_sql-0.5.2 → polyglot_sql-0.5.4}/crates/polyglot-sql/src/traversal.rs +0 -0
- {polyglot_sql-0.5.2 → polyglot_sql-0.5.4}/crates/polyglot-sql/src/trie.rs +0 -0
- {polyglot_sql-0.5.2 → polyglot_sql-0.5.4}/crates/polyglot-sql/src/validation/tests.rs +0 -0
- {polyglot_sql-0.5.2 → polyglot_sql-0.5.4}/crates/polyglot-sql/src/validation.rs +0 -0
- {polyglot_sql-0.5.2 → polyglot_sql-0.5.4}/crates/polyglot-sql/tests/analyze_failures.rs +0 -0
- {polyglot_sql-0.5.2 → polyglot_sql-0.5.4}/crates/polyglot-sql/tests/clickhouse_regression.rs +0 -0
- {polyglot_sql-0.5.2 → polyglot_sql-0.5.4}/crates/polyglot-sql/tests/common/known_failures.rs +0 -0
- {polyglot_sql-0.5.2 → polyglot_sql-0.5.4}/crates/polyglot-sql/tests/common/mod.rs +0 -0
- {polyglot_sql-0.5.2 → polyglot_sql-0.5.4}/crates/polyglot-sql/tests/common/test_data.rs +0 -0
- {polyglot_sql-0.5.2 → polyglot_sql-0.5.4}/crates/polyglot-sql/tests/common/test_runner.rs +0 -0
- {polyglot_sql-0.5.2 → polyglot_sql-0.5.4}/crates/polyglot-sql/tests/custom_clickhouse_coverage.rs +0 -0
- {polyglot_sql-0.5.2 → polyglot_sql-0.5.4}/crates/polyglot-sql/tests/custom_clickhouse_parser.rs +0 -0
- {polyglot_sql-0.5.2 → polyglot_sql-0.5.4}/crates/polyglot-sql/tests/custom_dialect.rs +0 -0
- {polyglot_sql-0.5.2 → polyglot_sql-0.5.4}/crates/polyglot-sql/tests/custom_dialect_tests.rs +0 -0
- {polyglot_sql-0.5.2 → polyglot_sql-0.5.4}/crates/polyglot-sql/tests/custom_fixtures/datafusion/ddl.json +0 -0
- {polyglot_sql-0.5.2 → polyglot_sql-0.5.4}/crates/polyglot-sql/tests/custom_fixtures/datafusion/dml.json +0 -0
- {polyglot_sql-0.5.2 → polyglot_sql-0.5.4}/crates/polyglot-sql/tests/custom_fixtures/datafusion/functions.json +0 -0
- {polyglot_sql-0.5.2 → polyglot_sql-0.5.4}/crates/polyglot-sql/tests/custom_fixtures/datafusion/identity.json +0 -0
- {polyglot_sql-0.5.2 → polyglot_sql-0.5.4}/crates/polyglot-sql/tests/custom_fixtures/datafusion/operators.json +0 -0
- {polyglot_sql-0.5.2 → polyglot_sql-0.5.4}/crates/polyglot-sql/tests/custom_fixtures/datafusion/select.json +0 -0
- {polyglot_sql-0.5.2 → polyglot_sql-0.5.4}/crates/polyglot-sql/tests/custom_fixtures/datafusion/transpilation.json +0 -0
- {polyglot_sql-0.5.2 → polyglot_sql-0.5.4}/crates/polyglot-sql/tests/custom_fixtures/datafusion/types.json +0 -0
- {polyglot_sql-0.5.2 → polyglot_sql-0.5.4}/crates/polyglot-sql/tests/data_type_api.rs +0 -0
- {polyglot_sql-0.5.2 → polyglot_sql-0.5.4}/crates/polyglot-sql/tests/deep_nesting_regression.rs +0 -0
- {polyglot_sql-0.5.2 → polyglot_sql-0.5.4}/crates/polyglot-sql/tests/dialect_matrix.rs +0 -0
- {polyglot_sql-0.5.2 → polyglot_sql-0.5.4}/crates/polyglot-sql/tests/error_handling.rs +0 -0
- {polyglot_sql-0.5.2 → polyglot_sql-0.5.4}/crates/polyglot-sql/tests/fabric_regression.rs +0 -0
- {polyglot_sql-0.5.2 → polyglot_sql-0.5.4}/crates/polyglot-sql/tests/fabric_tpch_regression.rs +0 -0
- {polyglot_sql-0.5.2 → polyglot_sql-0.5.4}/crates/polyglot-sql/tests/identity_roundtrip.rs +0 -0
- {polyglot_sql-0.5.2 → polyglot_sql-0.5.4}/crates/polyglot-sql/tests/issue201_regression_test.rs +0 -0
- {polyglot_sql-0.5.2 → polyglot_sql-0.5.4}/crates/polyglot-sql/tests/issue210_regression_test.rs +0 -0
- {polyglot_sql-0.5.2 → polyglot_sql-0.5.4}/crates/polyglot-sql/tests/issue226_regression_test.rs +0 -0
- {polyglot_sql-0.5.2 → polyglot_sql-0.5.4}/crates/polyglot-sql/tests/issue227_strict_unsupported.rs +0 -0
- {polyglot_sql-0.5.2 → polyglot_sql-0.5.4}/crates/polyglot-sql/tests/postgres_sqlite_regression.rs +0 -0
- {polyglot_sql-0.5.2 → polyglot_sql-0.5.4}/crates/polyglot-sql/tests/snowflake_regression_test.rs +0 -0
- {polyglot_sql-0.5.2 → polyglot_sql-0.5.4}/crates/polyglot-sql/tests/sqlglot_compat.rs +0 -0
- {polyglot_sql-0.5.2 → polyglot_sql-0.5.4}/crates/polyglot-sql/tests/sqlglot_dialect_identity.rs +0 -0
- {polyglot_sql-0.5.2 → polyglot_sql-0.5.4}/crates/polyglot-sql/tests/sqlglot_identity.rs +0 -0
- {polyglot_sql-0.5.2 → polyglot_sql-0.5.4}/crates/polyglot-sql/tests/sqlglot_identity_detailed.rs +0 -0
- {polyglot_sql-0.5.2 → polyglot_sql-0.5.4}/crates/polyglot-sql/tests/sqlglot_parser.rs +0 -0
- {polyglot_sql-0.5.2 → polyglot_sql-0.5.4}/crates/polyglot-sql/tests/sqlglot_pretty.rs +0 -0
- {polyglot_sql-0.5.2 → polyglot_sql-0.5.4}/crates/polyglot-sql/tests/sqlglot_transpilation.rs +0 -0
- {polyglot_sql-0.5.2 → polyglot_sql-0.5.4}/crates/polyglot-sql/tests/sqlglot_transpile.rs +0 -0
- {polyglot_sql-0.5.2 → polyglot_sql-0.5.4}/crates/polyglot-sql/tests/tpch_transpile_stack.rs +0 -0
- {polyglot_sql-0.5.2 → polyglot_sql-0.5.4}/crates/polyglot-sql/tests/transform_regression.rs +0 -0
- {polyglot_sql-0.5.2 → polyglot_sql-0.5.4}/crates/polyglot-sql-function-catalogs/Cargo.toml +0 -0
- {polyglot_sql-0.5.2 → polyglot_sql-0.5.4}/crates/polyglot-sql-function-catalogs/README.md +0 -0
- {polyglot_sql-0.5.2 → polyglot_sql-0.5.4}/crates/polyglot-sql-function-catalogs/src/clickhouse.rs +0 -0
- {polyglot_sql-0.5.2 → polyglot_sql-0.5.4}/crates/polyglot-sql-function-catalogs/src/duckdb.rs +0 -0
- {polyglot_sql-0.5.2 → polyglot_sql-0.5.4}/crates/polyglot-sql-function-catalogs/src/lib.rs +0 -0
- {polyglot_sql-0.5.2 → polyglot_sql-0.5.4}/crates/polyglot-sql-function-catalogs/tools/clickhouse/extract_functions.py +0 -0
- {polyglot_sql-0.5.2 → polyglot_sql-0.5.4}/crates/polyglot-sql-function-catalogs/tools/duckdb/extract_functions.py +0 -0
- {polyglot_sql-0.5.2 → polyglot_sql-0.5.4}/crates/polyglot-sql-python/Cargo.toml +0 -0
- {polyglot_sql-0.5.2 → polyglot_sql-0.5.4}/crates/polyglot-sql-python/docs/api.md +0 -0
- {polyglot_sql-0.5.2 → polyglot_sql-0.5.4}/crates/polyglot-sql-python/mkdocs.yml +0 -0
- {polyglot_sql-0.5.2 → polyglot_sql-0.5.4}/crates/polyglot-sql-python/src/annotate_types.rs +0 -0
- {polyglot_sql-0.5.2 → polyglot_sql-0.5.4}/crates/polyglot-sql-python/src/dialects.rs +0 -0
- {polyglot_sql-0.5.2 → polyglot_sql-0.5.4}/crates/polyglot-sql-python/src/diff.rs +0 -0
- {polyglot_sql-0.5.2 → polyglot_sql-0.5.4}/crates/polyglot-sql-python/src/errors.rs +0 -0
- {polyglot_sql-0.5.2 → polyglot_sql-0.5.4}/crates/polyglot-sql-python/src/expr_types.rs +0 -0
- {polyglot_sql-0.5.2 → polyglot_sql-0.5.4}/crates/polyglot-sql-python/src/format.rs +0 -0
- {polyglot_sql-0.5.2 → polyglot_sql-0.5.4}/crates/polyglot-sql-python/src/generate.rs +0 -0
- {polyglot_sql-0.5.2 → polyglot_sql-0.5.4}/crates/polyglot-sql-python/src/lineage.rs +0 -0
- {polyglot_sql-0.5.2 → polyglot_sql-0.5.4}/crates/polyglot-sql-python/src/openlineage.rs +0 -0
- {polyglot_sql-0.5.2 → polyglot_sql-0.5.4}/crates/polyglot-sql-python/src/optimize.rs +0 -0
- {polyglot_sql-0.5.2 → polyglot_sql-0.5.4}/crates/polyglot-sql-python/src/parse.rs +0 -0
- {polyglot_sql-0.5.2 → polyglot_sql-0.5.4}/crates/polyglot-sql-python/src/query_analysis.rs +0 -0
- {polyglot_sql-0.5.2 → polyglot_sql-0.5.4}/crates/polyglot-sql-python/src/tokenize.rs +0 -0
- {polyglot_sql-0.5.2 → polyglot_sql-0.5.4}/crates/polyglot-sql-python/src/transpile.rs +0 -0
- {polyglot_sql-0.5.2 → polyglot_sql-0.5.4}/crates/polyglot-sql-python/src/types.rs +0 -0
- {polyglot_sql-0.5.2 → polyglot_sql-0.5.4}/crates/polyglot-sql-python/src/validate.rs +0 -0
- {polyglot_sql-0.5.2 → polyglot_sql-0.5.4}/crates/polyglot-sql-python/tests/conftest.py +0 -0
- {polyglot_sql-0.5.2 → polyglot_sql-0.5.4}/crates/polyglot-sql-python/tests/test_compat.py +0 -0
- {polyglot_sql-0.5.2 → polyglot_sql-0.5.4}/crates/polyglot-sql-python/tests/test_dialects.py +0 -0
- {polyglot_sql-0.5.2 → polyglot_sql-0.5.4}/crates/polyglot-sql-python/tests/test_diff.py +0 -0
- {polyglot_sql-0.5.2 → polyglot_sql-0.5.4}/crates/polyglot-sql-python/tests/test_expression.py +0 -0
- {polyglot_sql-0.5.2 → polyglot_sql-0.5.4}/crates/polyglot-sql-python/tests/test_format.py +0 -0
- {polyglot_sql-0.5.2 → polyglot_sql-0.5.4}/crates/polyglot-sql-python/tests/test_lineage.py +0 -0
- {polyglot_sql-0.5.2 → polyglot_sql-0.5.4}/crates/polyglot-sql-python/tests/test_optimize.py +0 -0
- {polyglot_sql-0.5.2 → polyglot_sql-0.5.4}/crates/polyglot-sql-python/tests/test_parse.py +0 -0
- {polyglot_sql-0.5.2 → polyglot_sql-0.5.4}/crates/polyglot-sql-python/tests/test_transpile.py +0 -0
- {polyglot_sql-0.5.2 → polyglot_sql-0.5.4}/crates/polyglot-sql-python/tests/test_validate.py +0 -0
- {polyglot_sql-0.5.2 → polyglot_sql-0.5.4}/crates/polyglot-sql-python/uv.lock +0 -0
- {polyglot_sql-0.5.2 → polyglot_sql-0.5.4}/pyproject.toml +0 -0
- {polyglot_sql-0.5.2 → polyglot_sql-0.5.4}/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.4"
|
|
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.4"
|
|
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.4"
|
|
635
635
|
|
|
636
636
|
[[package]]
|
|
637
637
|
name = "polyglot-sql-python"
|
|
638
|
-
version = "0.5.
|
|
638
|
+
version = "0.5.4"
|
|
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.4"
|
|
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.4
|
|
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:
|
|
@@ -125,29 +135,72 @@ emission are intentionally out of scope.
|
|
|
125
135
|
|
|
126
136
|
```python
|
|
127
137
|
analysis = polyglot_sql.analyze_query(
|
|
128
|
-
"
|
|
138
|
+
"WITH base AS (SELECT id, amount FROM orders) SELECT * FROM base",
|
|
129
139
|
{
|
|
130
140
|
"dialect": "generic",
|
|
131
141
|
"schema": {
|
|
132
142
|
"tables": [
|
|
133
143
|
{
|
|
134
144
|
"name": "orders",
|
|
135
|
-
"columns": [
|
|
145
|
+
"columns": [
|
|
146
|
+
{"name": "id", "type": "INT", "nullable": False},
|
|
147
|
+
{"name": "amount", "type": "DECIMAL(10,2)", "nullable": True},
|
|
148
|
+
],
|
|
136
149
|
}
|
|
137
150
|
]
|
|
138
151
|
},
|
|
139
152
|
},
|
|
140
153
|
)
|
|
141
|
-
print(analysis["
|
|
142
|
-
print(analysis["
|
|
154
|
+
print(analysis["cteFacts"][0]["bodySql"]) # "SELECT id, amount FROM orders"
|
|
155
|
+
print(analysis["starProjections"][0]["expandedColumns"]) # ["id", "amount"]
|
|
156
|
+
print(analysis["projections"][0]["nullability"]) # "non_null"
|
|
143
157
|
print(analysis["baseTables"][0]["name"]) # "orders"
|
|
158
|
+
print(analysis["baseTables"][0]["table"]) # "orders"
|
|
144
159
|
```
|
|
145
160
|
|
|
146
161
|
`analysis["relations"]` reports sources visible in the analyzed scope.
|
|
147
162
|
`analysis["baseTables"]` reports deduplicated physical table dependencies across
|
|
148
|
-
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
|
|
149
166
|
uses broad type families, while query analysis preserves parseable detailed
|
|
150
|
-
schema type strings for projection `typeHint` values.
|
|
167
|
+
schema type strings for projection `typeHint` values. `analysis["cteFacts"]`
|
|
168
|
+
reports top-level CTE definitions, `analysis["starProjections"]` records the
|
|
169
|
+
original star projections and schema-expanded columns, and each projection has
|
|
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)`.
|
|
174
|
+
|
|
175
|
+
Validation schema dictionaries use:
|
|
176
|
+
|
|
177
|
+
```python
|
|
178
|
+
schema = {
|
|
179
|
+
"strict": True,
|
|
180
|
+
"tables": [
|
|
181
|
+
{
|
|
182
|
+
"name": "orders",
|
|
183
|
+
"schema": "analytics",
|
|
184
|
+
"aliases": ["o"],
|
|
185
|
+
"primaryKey": ["id"],
|
|
186
|
+
"uniqueKeys": [["external_id"]],
|
|
187
|
+
"foreignKeys": [
|
|
188
|
+
{
|
|
189
|
+
"columns": ["customer_id"],
|
|
190
|
+
"references": {"table": "customers", "columns": ["id"]},
|
|
191
|
+
}
|
|
192
|
+
],
|
|
193
|
+
"columns": [
|
|
194
|
+
{"name": "id", "type": "INT", "nullable": False, "primaryKey": True},
|
|
195
|
+
{"name": "amount", "type": "DECIMAL(10,2)", "nullable": True},
|
|
196
|
+
],
|
|
197
|
+
}
|
|
198
|
+
],
|
|
199
|
+
}
|
|
200
|
+
```
|
|
201
|
+
|
|
202
|
+
Use the `type` key for column types. `dataType` / `data_type` are not accepted
|
|
203
|
+
aliases in this payload.
|
|
151
204
|
|
|
152
205
|
## API Reference
|
|
153
206
|
|
|
@@ -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:
|
|
@@ -98,29 +108,72 @@ emission are intentionally out of scope.
|
|
|
98
108
|
|
|
99
109
|
```python
|
|
100
110
|
analysis = polyglot_sql.analyze_query(
|
|
101
|
-
"
|
|
111
|
+
"WITH base AS (SELECT id, amount FROM orders) SELECT * FROM base",
|
|
102
112
|
{
|
|
103
113
|
"dialect": "generic",
|
|
104
114
|
"schema": {
|
|
105
115
|
"tables": [
|
|
106
116
|
{
|
|
107
117
|
"name": "orders",
|
|
108
|
-
"columns": [
|
|
118
|
+
"columns": [
|
|
119
|
+
{"name": "id", "type": "INT", "nullable": False},
|
|
120
|
+
{"name": "amount", "type": "DECIMAL(10,2)", "nullable": True},
|
|
121
|
+
],
|
|
109
122
|
}
|
|
110
123
|
]
|
|
111
124
|
},
|
|
112
125
|
},
|
|
113
126
|
)
|
|
114
|
-
print(analysis["
|
|
115
|
-
print(analysis["
|
|
127
|
+
print(analysis["cteFacts"][0]["bodySql"]) # "SELECT id, amount FROM orders"
|
|
128
|
+
print(analysis["starProjections"][0]["expandedColumns"]) # ["id", "amount"]
|
|
129
|
+
print(analysis["projections"][0]["nullability"]) # "non_null"
|
|
116
130
|
print(analysis["baseTables"][0]["name"]) # "orders"
|
|
131
|
+
print(analysis["baseTables"][0]["table"]) # "orders"
|
|
117
132
|
```
|
|
118
133
|
|
|
119
134
|
`analysis["relations"]` reports sources visible in the analyzed scope.
|
|
120
135
|
`analysis["baseTables"]` reports deduplicated physical table dependencies across
|
|
121
|
-
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
|
|
122
139
|
uses broad type families, while query analysis preserves parseable detailed
|
|
123
|
-
schema type strings for projection `typeHint` values.
|
|
140
|
+
schema type strings for projection `typeHint` values. `analysis["cteFacts"]`
|
|
141
|
+
reports top-level CTE definitions, `analysis["starProjections"]` records the
|
|
142
|
+
original star projections and schema-expanded columns, and each projection has
|
|
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)`.
|
|
147
|
+
|
|
148
|
+
Validation schema dictionaries use:
|
|
149
|
+
|
|
150
|
+
```python
|
|
151
|
+
schema = {
|
|
152
|
+
"strict": True,
|
|
153
|
+
"tables": [
|
|
154
|
+
{
|
|
155
|
+
"name": "orders",
|
|
156
|
+
"schema": "analytics",
|
|
157
|
+
"aliases": ["o"],
|
|
158
|
+
"primaryKey": ["id"],
|
|
159
|
+
"uniqueKeys": [["external_id"]],
|
|
160
|
+
"foreignKeys": [
|
|
161
|
+
{
|
|
162
|
+
"columns": ["customer_id"],
|
|
163
|
+
"references": {"table": "customers", "columns": ["id"]},
|
|
164
|
+
}
|
|
165
|
+
],
|
|
166
|
+
"columns": [
|
|
167
|
+
{"name": "id", "type": "INT", "nullable": False, "primaryKey": True},
|
|
168
|
+
{"name": "amount", "type": "DECIMAL(10,2)", "nullable": True},
|
|
169
|
+
],
|
|
170
|
+
}
|
|
171
|
+
],
|
|
172
|
+
}
|
|
173
|
+
```
|
|
174
|
+
|
|
175
|
+
Use the `type` key for column types. `dataType` / `data_type` are not accepted
|
|
176
|
+
aliases in this payload.
|
|
124
177
|
|
|
125
178
|
## API Reference
|
|
126
179
|
|
|
@@ -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.4", optional = true, default-features = false }
|
|
110
110
|
|
|
111
111
|
[dev-dependencies]
|
|
112
112
|
pretty_assertions = "1.4"
|
|
@@ -318,19 +318,31 @@ 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.
|
|
323
|
+
`cte_facts` reports top-level CTE names, declared columns, original CTE body SQL,
|
|
324
|
+
and CTE output columns. `star_projections` reports the original top-level star
|
|
325
|
+
projection index, optional table qualifier, and schema-expanded columns when
|
|
326
|
+
known. Each projection also includes conservative `nullability`:
|
|
327
|
+
`non_null`, `nullable`, or `unknown`.
|
|
321
328
|
|
|
322
329
|
```rust
|
|
323
|
-
use polyglot_sql::{
|
|
330
|
+
use polyglot_sql::{
|
|
331
|
+
analyze_query, AnalyzeQueryOptions, DialectType, ProjectionNullability, QueryShape,
|
|
332
|
+
};
|
|
324
333
|
|
|
325
334
|
let schema: polyglot_sql::ValidationSchema = serde_json::from_value(serde_json::json!({
|
|
326
335
|
"tables": [{
|
|
327
336
|
"name": "orders",
|
|
328
|
-
"columns": [
|
|
337
|
+
"columns": [
|
|
338
|
+
{"name": "id", "type": "INT", "nullable": false},
|
|
339
|
+
{"name": "amount", "type": "DECIMAL(10,2)", "nullable": true}
|
|
340
|
+
]
|
|
329
341
|
}]
|
|
330
342
|
})).unwrap();
|
|
331
343
|
|
|
332
344
|
let analysis = analyze_query(
|
|
333
|
-
"SELECT
|
|
345
|
+
"WITH base AS (SELECT id, amount FROM orders) SELECT * FROM base",
|
|
334
346
|
AnalyzeQueryOptions {
|
|
335
347
|
dialect: DialectType::Generic,
|
|
336
348
|
schema: Some(schema),
|
|
@@ -338,12 +350,44 @@ let analysis = analyze_query(
|
|
|
338
350
|
).unwrap();
|
|
339
351
|
|
|
340
352
|
assert_eq!(analysis.shape, QueryShape::Select);
|
|
341
|
-
assert_eq!(analysis.
|
|
342
|
-
assert_eq!(analysis.
|
|
353
|
+
assert_eq!(analysis.cte_facts[0].name, "base");
|
|
354
|
+
assert_eq!(analysis.cte_facts[0].body_sql, "SELECT id, amount FROM orders");
|
|
355
|
+
assert_eq!(analysis.star_projections[0].expanded_columns, vec!["id", "amount"]);
|
|
356
|
+
assert_eq!(analysis.projections[0].nullability, ProjectionNullability::NonNull);
|
|
343
357
|
assert_eq!(analysis.base_tables[0].name, "orders");
|
|
344
|
-
assert_eq!(analysis.base_tables[0].
|
|
358
|
+
assert_eq!(analysis.base_tables[0].table.as_deref(), Some("orders"));
|
|
359
|
+
```
|
|
360
|
+
|
|
361
|
+
External JSON schemas use this shape:
|
|
362
|
+
|
|
363
|
+
```json
|
|
364
|
+
{
|
|
365
|
+
"strict": true,
|
|
366
|
+
"tables": [
|
|
367
|
+
{
|
|
368
|
+
"name": "orders",
|
|
369
|
+
"schema": "analytics",
|
|
370
|
+
"aliases": ["o"],
|
|
371
|
+
"primaryKey": ["id"],
|
|
372
|
+
"uniqueKeys": [["external_id"]],
|
|
373
|
+
"foreignKeys": [
|
|
374
|
+
{
|
|
375
|
+
"columns": ["customer_id"],
|
|
376
|
+
"references": { "table": "customers", "columns": ["id"] }
|
|
377
|
+
}
|
|
378
|
+
],
|
|
379
|
+
"columns": [
|
|
380
|
+
{ "name": "id", "type": "INT", "nullable": false, "primaryKey": true },
|
|
381
|
+
{ "name": "amount", "type": "DECIMAL(10,2)", "nullable": true }
|
|
382
|
+
]
|
|
383
|
+
}
|
|
384
|
+
]
|
|
385
|
+
}
|
|
345
386
|
```
|
|
346
387
|
|
|
388
|
+
Use the `type` key for column types in JSON. `dataType` / `data_type` are not
|
|
389
|
+
accepted aliases.
|
|
390
|
+
|
|
347
391
|
### Tokenize
|
|
348
392
|
|
|
349
393
|
Access the raw token stream with full source position spans. Each token carries a `Span` with byte offsets and line/column numbers.
|
|
@@ -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
|
+
}
|