polyglot-sql 0.4.1__tar.gz → 0.4.3__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.4.1 → polyglot_sql-0.4.3}/Cargo.lock +5 -5
- {polyglot_sql-0.4.1 → polyglot_sql-0.4.3}/Cargo.toml +1 -1
- {polyglot_sql-0.4.1 → polyglot_sql-0.4.3}/PKG-INFO +1 -1
- {polyglot_sql-0.4.1 → polyglot_sql-0.4.3}/crates/polyglot-sql/Cargo.toml +1 -1
- {polyglot_sql-0.4.1 → polyglot_sql-0.4.3}/crates/polyglot-sql/src/dialects/bigquery.rs +32 -0
- {polyglot_sql-0.4.1 → polyglot_sql-0.4.3}/crates/polyglot-sql/src/dialects/mod.rs +25 -0
- {polyglot_sql-0.4.1 → polyglot_sql-0.4.3}/crates/polyglot-sql/src/expressions.rs +24 -0
- {polyglot_sql-0.4.1 → polyglot_sql-0.4.3}/crates/polyglot-sql/src/generator.rs +36 -0
- {polyglot_sql-0.4.1 → polyglot_sql-0.4.3}/crates/polyglot-sql/src/lineage.rs +161 -6
- {polyglot_sql-0.4.1 → polyglot_sql-0.4.3}/crates/polyglot-sql/src/openlineage.rs +87 -0
- {polyglot_sql-0.4.1 → polyglot_sql-0.4.3}/crates/polyglot-sql/src/parser.rs +117 -20
- {polyglot_sql-0.4.1 → polyglot_sql-0.4.3}/crates/polyglot-sql/src/scope.rs +18 -0
- {polyglot_sql-0.4.1 → polyglot_sql-0.4.3}/crates/polyglot-sql/src/tokens.rs +3 -0
- {polyglot_sql-0.4.1 → polyglot_sql-0.4.3}/crates/polyglot-sql/src/traversal.rs +12 -0
- polyglot_sql-0.4.3/crates/polyglot-sql/tests/issue210_regression_test.rs +80 -0
- {polyglot_sql-0.4.1 → polyglot_sql-0.4.3}/crates/polyglot-sql-python/src/expr_types.rs +1 -0
- {polyglot_sql-0.4.1 → polyglot_sql-0.4.3}/crates/polyglot-sql-python/tests/test_lineage.py +6 -0
- {polyglot_sql-0.4.1 → polyglot_sql-0.4.3}/crates/polyglot-sql-python/tests/test_parse.py +20 -0
- {polyglot_sql-0.4.1 → polyglot_sql-0.4.3}/python/polyglot_sql/__init__.py +3 -0
- {polyglot_sql-0.4.1 → polyglot_sql-0.4.3}/python/polyglot_sql/__init__.pyi +1 -0
- {polyglot_sql-0.4.1 → polyglot_sql-0.4.3}/README.md +0 -0
- {polyglot_sql-0.4.1 → polyglot_sql-0.4.3}/crates/polyglot-sql/README.md +0 -0
- {polyglot_sql-0.4.1 → polyglot_sql-0.4.3}/crates/polyglot-sql/benches/in_list.rs +0 -0
- {polyglot_sql-0.4.1 → polyglot_sql-0.4.3}/crates/polyglot-sql/benches/parsing.rs +0 -0
- {polyglot_sql-0.4.1 → polyglot_sql-0.4.3}/crates/polyglot-sql/benches/rust_parsing.rs +0 -0
- {polyglot_sql-0.4.1 → polyglot_sql-0.4.3}/crates/polyglot-sql/benches/transpile.rs +0 -0
- {polyglot_sql-0.4.1 → polyglot_sql-0.4.3}/crates/polyglot-sql/examples/basic_usage.rs +0 -0
- {polyglot_sql-0.4.1 → polyglot_sql-0.4.3}/crates/polyglot-sql/examples/bench_json.rs +0 -0
- {polyglot_sql-0.4.1 → polyglot_sql-0.4.3}/crates/polyglot-sql/src/ast_transforms.rs +0 -0
- {polyglot_sql-0.4.1 → polyglot_sql-0.4.3}/crates/polyglot-sql/src/builder.rs +0 -0
- {polyglot_sql-0.4.1 → polyglot_sql-0.4.3}/crates/polyglot-sql/src/dialects/athena.rs +0 -0
- {polyglot_sql-0.4.1 → polyglot_sql-0.4.3}/crates/polyglot-sql/src/dialects/clickhouse.rs +0 -0
- {polyglot_sql-0.4.1 → polyglot_sql-0.4.3}/crates/polyglot-sql/src/dialects/cockroachdb.rs +0 -0
- {polyglot_sql-0.4.1 → polyglot_sql-0.4.3}/crates/polyglot-sql/src/dialects/databricks.rs +0 -0
- {polyglot_sql-0.4.1 → polyglot_sql-0.4.3}/crates/polyglot-sql/src/dialects/datafusion.rs +0 -0
- {polyglot_sql-0.4.1 → polyglot_sql-0.4.3}/crates/polyglot-sql/src/dialects/doris.rs +0 -0
- {polyglot_sql-0.4.1 → polyglot_sql-0.4.3}/crates/polyglot-sql/src/dialects/dremio.rs +0 -0
- {polyglot_sql-0.4.1 → polyglot_sql-0.4.3}/crates/polyglot-sql/src/dialects/drill.rs +0 -0
- {polyglot_sql-0.4.1 → polyglot_sql-0.4.3}/crates/polyglot-sql/src/dialects/druid.rs +0 -0
- {polyglot_sql-0.4.1 → polyglot_sql-0.4.3}/crates/polyglot-sql/src/dialects/duckdb.rs +0 -0
- {polyglot_sql-0.4.1 → polyglot_sql-0.4.3}/crates/polyglot-sql/src/dialects/dune.rs +0 -0
- {polyglot_sql-0.4.1 → polyglot_sql-0.4.3}/crates/polyglot-sql/src/dialects/exasol.rs +0 -0
- {polyglot_sql-0.4.1 → polyglot_sql-0.4.3}/crates/polyglot-sql/src/dialects/fabric.rs +0 -0
- {polyglot_sql-0.4.1 → polyglot_sql-0.4.3}/crates/polyglot-sql/src/dialects/generic.rs +0 -0
- {polyglot_sql-0.4.1 → polyglot_sql-0.4.3}/crates/polyglot-sql/src/dialects/hive.rs +0 -0
- {polyglot_sql-0.4.1 → polyglot_sql-0.4.3}/crates/polyglot-sql/src/dialects/materialize.rs +0 -0
- {polyglot_sql-0.4.1 → polyglot_sql-0.4.3}/crates/polyglot-sql/src/dialects/mysql.rs +0 -0
- {polyglot_sql-0.4.1 → polyglot_sql-0.4.3}/crates/polyglot-sql/src/dialects/oracle.rs +0 -0
- {polyglot_sql-0.4.1 → polyglot_sql-0.4.3}/crates/polyglot-sql/src/dialects/postgres.rs +0 -0
- {polyglot_sql-0.4.1 → polyglot_sql-0.4.3}/crates/polyglot-sql/src/dialects/presto.rs +0 -0
- {polyglot_sql-0.4.1 → polyglot_sql-0.4.3}/crates/polyglot-sql/src/dialects/redshift.rs +0 -0
- {polyglot_sql-0.4.1 → polyglot_sql-0.4.3}/crates/polyglot-sql/src/dialects/risingwave.rs +0 -0
- {polyglot_sql-0.4.1 → polyglot_sql-0.4.3}/crates/polyglot-sql/src/dialects/singlestore.rs +0 -0
- {polyglot_sql-0.4.1 → polyglot_sql-0.4.3}/crates/polyglot-sql/src/dialects/snowflake.rs +0 -0
- {polyglot_sql-0.4.1 → polyglot_sql-0.4.3}/crates/polyglot-sql/src/dialects/solr.rs +0 -0
- {polyglot_sql-0.4.1 → polyglot_sql-0.4.3}/crates/polyglot-sql/src/dialects/spark.rs +0 -0
- {polyglot_sql-0.4.1 → polyglot_sql-0.4.3}/crates/polyglot-sql/src/dialects/sqlite.rs +0 -0
- {polyglot_sql-0.4.1 → polyglot_sql-0.4.3}/crates/polyglot-sql/src/dialects/starrocks.rs +0 -0
- {polyglot_sql-0.4.1 → polyglot_sql-0.4.3}/crates/polyglot-sql/src/dialects/tableau.rs +0 -0
- {polyglot_sql-0.4.1 → polyglot_sql-0.4.3}/crates/polyglot-sql/src/dialects/teradata.rs +0 -0
- {polyglot_sql-0.4.1 → polyglot_sql-0.4.3}/crates/polyglot-sql/src/dialects/tidb.rs +0 -0
- {polyglot_sql-0.4.1 → polyglot_sql-0.4.3}/crates/polyglot-sql/src/dialects/trino.rs +0 -0
- {polyglot_sql-0.4.1 → polyglot_sql-0.4.3}/crates/polyglot-sql/src/dialects/tsql.rs +0 -0
- {polyglot_sql-0.4.1 → polyglot_sql-0.4.3}/crates/polyglot-sql/src/diff.rs +0 -0
- {polyglot_sql-0.4.1 → polyglot_sql-0.4.3}/crates/polyglot-sql/src/error.rs +0 -0
- {polyglot_sql-0.4.1 → polyglot_sql-0.4.3}/crates/polyglot-sql/src/function_catalog.rs +0 -0
- {polyglot_sql-0.4.1 → polyglot_sql-0.4.3}/crates/polyglot-sql/src/function_registry.rs +0 -0
- {polyglot_sql-0.4.1 → polyglot_sql-0.4.3}/crates/polyglot-sql/src/helper.rs +0 -0
- {polyglot_sql-0.4.1 → polyglot_sql-0.4.3}/crates/polyglot-sql/src/lib.rs +0 -0
- {polyglot_sql-0.4.1 → polyglot_sql-0.4.3}/crates/polyglot-sql/src/optimizer/annotate_types.rs +0 -0
- {polyglot_sql-0.4.1 → polyglot_sql-0.4.3}/crates/polyglot-sql/src/optimizer/canonicalize.rs +0 -0
- {polyglot_sql-0.4.1 → polyglot_sql-0.4.3}/crates/polyglot-sql/src/optimizer/eliminate_ctes.rs +0 -0
- {polyglot_sql-0.4.1 → polyglot_sql-0.4.3}/crates/polyglot-sql/src/optimizer/eliminate_joins.rs +0 -0
- {polyglot_sql-0.4.1 → polyglot_sql-0.4.3}/crates/polyglot-sql/src/optimizer/isolate_table_selects.rs +0 -0
- {polyglot_sql-0.4.1 → polyglot_sql-0.4.3}/crates/polyglot-sql/src/optimizer/mod.rs +0 -0
- {polyglot_sql-0.4.1 → polyglot_sql-0.4.3}/crates/polyglot-sql/src/optimizer/normalize.rs +0 -0
- {polyglot_sql-0.4.1 → polyglot_sql-0.4.3}/crates/polyglot-sql/src/optimizer/normalize_identifiers.rs +0 -0
- {polyglot_sql-0.4.1 → polyglot_sql-0.4.3}/crates/polyglot-sql/src/optimizer/optimize_joins.rs +0 -0
- {polyglot_sql-0.4.1 → polyglot_sql-0.4.3}/crates/polyglot-sql/src/optimizer/optimizer.rs +0 -0
- {polyglot_sql-0.4.1 → polyglot_sql-0.4.3}/crates/polyglot-sql/src/optimizer/pushdown_predicates.rs +0 -0
- {polyglot_sql-0.4.1 → polyglot_sql-0.4.3}/crates/polyglot-sql/src/optimizer/pushdown_projections.rs +0 -0
- {polyglot_sql-0.4.1 → polyglot_sql-0.4.3}/crates/polyglot-sql/src/optimizer/qualify_columns.rs +0 -0
- {polyglot_sql-0.4.1 → polyglot_sql-0.4.3}/crates/polyglot-sql/src/optimizer/qualify_tables.rs +0 -0
- {polyglot_sql-0.4.1 → polyglot_sql-0.4.3}/crates/polyglot-sql/src/optimizer/simplify.rs +0 -0
- {polyglot_sql-0.4.1 → polyglot_sql-0.4.3}/crates/polyglot-sql/src/optimizer/subquery.rs +0 -0
- {polyglot_sql-0.4.1 → polyglot_sql-0.4.3}/crates/polyglot-sql/src/planner.rs +0 -0
- {polyglot_sql-0.4.1 → polyglot_sql-0.4.3}/crates/polyglot-sql/src/resolver.rs +0 -0
- {polyglot_sql-0.4.1 → polyglot_sql-0.4.3}/crates/polyglot-sql/src/schema.rs +0 -0
- {polyglot_sql-0.4.1 → polyglot_sql-0.4.3}/crates/polyglot-sql/src/time.rs +0 -0
- {polyglot_sql-0.4.1 → polyglot_sql-0.4.3}/crates/polyglot-sql/src/transforms.rs +0 -0
- {polyglot_sql-0.4.1 → polyglot_sql-0.4.3}/crates/polyglot-sql/src/trie.rs +0 -0
- {polyglot_sql-0.4.1 → polyglot_sql-0.4.3}/crates/polyglot-sql/src/validation/tests.rs +0 -0
- {polyglot_sql-0.4.1 → polyglot_sql-0.4.3}/crates/polyglot-sql/src/validation.rs +0 -0
- {polyglot_sql-0.4.1 → polyglot_sql-0.4.3}/crates/polyglot-sql/tests/analyze_failures.rs +0 -0
- {polyglot_sql-0.4.1 → polyglot_sql-0.4.3}/crates/polyglot-sql/tests/clickhouse_regression.rs +0 -0
- {polyglot_sql-0.4.1 → polyglot_sql-0.4.3}/crates/polyglot-sql/tests/common/known_failures.rs +0 -0
- {polyglot_sql-0.4.1 → polyglot_sql-0.4.3}/crates/polyglot-sql/tests/common/mod.rs +0 -0
- {polyglot_sql-0.4.1 → polyglot_sql-0.4.3}/crates/polyglot-sql/tests/common/test_data.rs +0 -0
- {polyglot_sql-0.4.1 → polyglot_sql-0.4.3}/crates/polyglot-sql/tests/common/test_runner.rs +0 -0
- {polyglot_sql-0.4.1 → polyglot_sql-0.4.3}/crates/polyglot-sql/tests/custom_clickhouse_coverage.rs +0 -0
- {polyglot_sql-0.4.1 → polyglot_sql-0.4.3}/crates/polyglot-sql/tests/custom_clickhouse_parser.rs +0 -0
- {polyglot_sql-0.4.1 → polyglot_sql-0.4.3}/crates/polyglot-sql/tests/custom_dialect.rs +0 -0
- {polyglot_sql-0.4.1 → polyglot_sql-0.4.3}/crates/polyglot-sql/tests/custom_dialect_tests.rs +0 -0
- {polyglot_sql-0.4.1 → polyglot_sql-0.4.3}/crates/polyglot-sql/tests/custom_fixtures/datafusion/ddl.json +0 -0
- {polyglot_sql-0.4.1 → polyglot_sql-0.4.3}/crates/polyglot-sql/tests/custom_fixtures/datafusion/dml.json +0 -0
- {polyglot_sql-0.4.1 → polyglot_sql-0.4.3}/crates/polyglot-sql/tests/custom_fixtures/datafusion/functions.json +0 -0
- {polyglot_sql-0.4.1 → polyglot_sql-0.4.3}/crates/polyglot-sql/tests/custom_fixtures/datafusion/identity.json +0 -0
- {polyglot_sql-0.4.1 → polyglot_sql-0.4.3}/crates/polyglot-sql/tests/custom_fixtures/datafusion/operators.json +0 -0
- {polyglot_sql-0.4.1 → polyglot_sql-0.4.3}/crates/polyglot-sql/tests/custom_fixtures/datafusion/select.json +0 -0
- {polyglot_sql-0.4.1 → polyglot_sql-0.4.3}/crates/polyglot-sql/tests/custom_fixtures/datafusion/transpilation.json +0 -0
- {polyglot_sql-0.4.1 → polyglot_sql-0.4.3}/crates/polyglot-sql/tests/custom_fixtures/datafusion/types.json +0 -0
- {polyglot_sql-0.4.1 → polyglot_sql-0.4.3}/crates/polyglot-sql/tests/deep_nesting_regression.rs +0 -0
- {polyglot_sql-0.4.1 → polyglot_sql-0.4.3}/crates/polyglot-sql/tests/dialect_matrix.rs +0 -0
- {polyglot_sql-0.4.1 → polyglot_sql-0.4.3}/crates/polyglot-sql/tests/error_handling.rs +0 -0
- {polyglot_sql-0.4.1 → polyglot_sql-0.4.3}/crates/polyglot-sql/tests/fabric_regression.rs +0 -0
- {polyglot_sql-0.4.1 → polyglot_sql-0.4.3}/crates/polyglot-sql/tests/fabric_tpch_regression.rs +0 -0
- {polyglot_sql-0.4.1 → polyglot_sql-0.4.3}/crates/polyglot-sql/tests/identity_roundtrip.rs +0 -0
- {polyglot_sql-0.4.1 → polyglot_sql-0.4.3}/crates/polyglot-sql/tests/issue201_regression_test.rs +0 -0
- {polyglot_sql-0.4.1 → polyglot_sql-0.4.3}/crates/polyglot-sql/tests/postgres_sqlite_regression.rs +0 -0
- {polyglot_sql-0.4.1 → polyglot_sql-0.4.3}/crates/polyglot-sql/tests/snowflake_regression_test.rs +0 -0
- {polyglot_sql-0.4.1 → polyglot_sql-0.4.3}/crates/polyglot-sql/tests/sqlglot_compat.rs +0 -0
- {polyglot_sql-0.4.1 → polyglot_sql-0.4.3}/crates/polyglot-sql/tests/sqlglot_dialect_identity.rs +0 -0
- {polyglot_sql-0.4.1 → polyglot_sql-0.4.3}/crates/polyglot-sql/tests/sqlglot_identity.rs +0 -0
- {polyglot_sql-0.4.1 → polyglot_sql-0.4.3}/crates/polyglot-sql/tests/sqlglot_identity_detailed.rs +0 -0
- {polyglot_sql-0.4.1 → polyglot_sql-0.4.3}/crates/polyglot-sql/tests/sqlglot_parser.rs +0 -0
- {polyglot_sql-0.4.1 → polyglot_sql-0.4.3}/crates/polyglot-sql/tests/sqlglot_pretty.rs +0 -0
- {polyglot_sql-0.4.1 → polyglot_sql-0.4.3}/crates/polyglot-sql/tests/sqlglot_transpilation.rs +0 -0
- {polyglot_sql-0.4.1 → polyglot_sql-0.4.3}/crates/polyglot-sql/tests/sqlglot_transpile.rs +0 -0
- {polyglot_sql-0.4.1 → polyglot_sql-0.4.3}/crates/polyglot-sql/tests/tpch_transpile_stack.rs +0 -0
- {polyglot_sql-0.4.1 → polyglot_sql-0.4.3}/crates/polyglot-sql/tests/transform_regression.rs +0 -0
- {polyglot_sql-0.4.1 → polyglot_sql-0.4.3}/crates/polyglot-sql/tests/tsql_regression.rs +0 -0
- {polyglot_sql-0.4.1 → polyglot_sql-0.4.3}/crates/polyglot-sql-function-catalogs/Cargo.toml +0 -0
- {polyglot_sql-0.4.1 → polyglot_sql-0.4.3}/crates/polyglot-sql-function-catalogs/README.md +0 -0
- {polyglot_sql-0.4.1 → polyglot_sql-0.4.3}/crates/polyglot-sql-function-catalogs/src/clickhouse.rs +0 -0
- {polyglot_sql-0.4.1 → polyglot_sql-0.4.3}/crates/polyglot-sql-function-catalogs/src/duckdb.rs +0 -0
- {polyglot_sql-0.4.1 → polyglot_sql-0.4.3}/crates/polyglot-sql-function-catalogs/src/lib.rs +0 -0
- {polyglot_sql-0.4.1 → polyglot_sql-0.4.3}/crates/polyglot-sql-function-catalogs/tools/clickhouse/extract_functions.py +0 -0
- {polyglot_sql-0.4.1 → polyglot_sql-0.4.3}/crates/polyglot-sql-function-catalogs/tools/duckdb/extract_functions.py +0 -0
- {polyglot_sql-0.4.1 → polyglot_sql-0.4.3}/crates/polyglot-sql-python/Cargo.toml +0 -0
- {polyglot_sql-0.4.1 → polyglot_sql-0.4.3}/crates/polyglot-sql-python/README.md +0 -0
- {polyglot_sql-0.4.1 → polyglot_sql-0.4.3}/crates/polyglot-sql-python/docs/api.md +0 -0
- {polyglot_sql-0.4.1 → polyglot_sql-0.4.3}/crates/polyglot-sql-python/docs/index.md +0 -0
- {polyglot_sql-0.4.1 → polyglot_sql-0.4.3}/crates/polyglot-sql-python/mkdocs.yml +0 -0
- {polyglot_sql-0.4.1 → polyglot_sql-0.4.3}/crates/polyglot-sql-python/src/annotate_types.rs +0 -0
- {polyglot_sql-0.4.1 → polyglot_sql-0.4.3}/crates/polyglot-sql-python/src/dialects.rs +0 -0
- {polyglot_sql-0.4.1 → polyglot_sql-0.4.3}/crates/polyglot-sql-python/src/diff.rs +0 -0
- {polyglot_sql-0.4.1 → polyglot_sql-0.4.3}/crates/polyglot-sql-python/src/errors.rs +0 -0
- {polyglot_sql-0.4.1 → polyglot_sql-0.4.3}/crates/polyglot-sql-python/src/expr.rs +0 -0
- {polyglot_sql-0.4.1 → polyglot_sql-0.4.3}/crates/polyglot-sql-python/src/format.rs +0 -0
- {polyglot_sql-0.4.1 → polyglot_sql-0.4.3}/crates/polyglot-sql-python/src/generate.rs +0 -0
- {polyglot_sql-0.4.1 → polyglot_sql-0.4.3}/crates/polyglot-sql-python/src/helpers.rs +0 -0
- {polyglot_sql-0.4.1 → polyglot_sql-0.4.3}/crates/polyglot-sql-python/src/lib.rs +0 -0
- {polyglot_sql-0.4.1 → polyglot_sql-0.4.3}/crates/polyglot-sql-python/src/lineage.rs +0 -0
- {polyglot_sql-0.4.1 → polyglot_sql-0.4.3}/crates/polyglot-sql-python/src/openlineage.rs +0 -0
- {polyglot_sql-0.4.1 → polyglot_sql-0.4.3}/crates/polyglot-sql-python/src/optimize.rs +0 -0
- {polyglot_sql-0.4.1 → polyglot_sql-0.4.3}/crates/polyglot-sql-python/src/parse.rs +0 -0
- {polyglot_sql-0.4.1 → polyglot_sql-0.4.3}/crates/polyglot-sql-python/src/tokenize.rs +0 -0
- {polyglot_sql-0.4.1 → polyglot_sql-0.4.3}/crates/polyglot-sql-python/src/transforms.rs +0 -0
- {polyglot_sql-0.4.1 → polyglot_sql-0.4.3}/crates/polyglot-sql-python/src/transpile.rs +0 -0
- {polyglot_sql-0.4.1 → polyglot_sql-0.4.3}/crates/polyglot-sql-python/src/types.rs +0 -0
- {polyglot_sql-0.4.1 → polyglot_sql-0.4.3}/crates/polyglot-sql-python/src/validate.rs +0 -0
- {polyglot_sql-0.4.1 → polyglot_sql-0.4.3}/crates/polyglot-sql-python/tests/conftest.py +0 -0
- {polyglot_sql-0.4.1 → polyglot_sql-0.4.3}/crates/polyglot-sql-python/tests/test_compat.py +0 -0
- {polyglot_sql-0.4.1 → polyglot_sql-0.4.3}/crates/polyglot-sql-python/tests/test_dialects.py +0 -0
- {polyglot_sql-0.4.1 → polyglot_sql-0.4.3}/crates/polyglot-sql-python/tests/test_diff.py +0 -0
- {polyglot_sql-0.4.1 → polyglot_sql-0.4.3}/crates/polyglot-sql-python/tests/test_expression.py +0 -0
- {polyglot_sql-0.4.1 → polyglot_sql-0.4.3}/crates/polyglot-sql-python/tests/test_format.py +0 -0
- {polyglot_sql-0.4.1 → polyglot_sql-0.4.3}/crates/polyglot-sql-python/tests/test_generate.py +0 -0
- {polyglot_sql-0.4.1 → polyglot_sql-0.4.3}/crates/polyglot-sql-python/tests/test_optimize.py +0 -0
- {polyglot_sql-0.4.1 → polyglot_sql-0.4.3}/crates/polyglot-sql-python/tests/test_transforms.py +0 -0
- {polyglot_sql-0.4.1 → polyglot_sql-0.4.3}/crates/polyglot-sql-python/tests/test_transpile.py +0 -0
- {polyglot_sql-0.4.1 → polyglot_sql-0.4.3}/crates/polyglot-sql-python/tests/test_validate.py +0 -0
- {polyglot_sql-0.4.1 → polyglot_sql-0.4.3}/crates/polyglot-sql-python/uv.lock +0 -0
- {polyglot_sql-0.4.1 → polyglot_sql-0.4.3}/pyproject.toml +0 -0
- {polyglot_sql-0.4.1 → polyglot_sql-0.4.3}/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.4.
|
|
608
|
+
version = "0.4.3"
|
|
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.4.
|
|
624
|
+
version = "0.4.3"
|
|
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.4.
|
|
634
|
+
version = "0.4.3"
|
|
635
635
|
|
|
636
636
|
[[package]]
|
|
637
637
|
name = "polyglot-sql-python"
|
|
638
|
-
version = "0.4.
|
|
638
|
+
version = "0.4.3"
|
|
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.4.
|
|
649
|
+
version = "0.4.3"
|
|
650
650
|
dependencies = [
|
|
651
651
|
"console_error_panic_hook",
|
|
652
652
|
"js-sys",
|
|
@@ -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.4.
|
|
109
|
+
polyglot-sql-function-catalogs = { path = "../polyglot-sql-function-catalogs", version = "0.4.3", optional = true, default-features = false }
|
|
110
110
|
|
|
111
111
|
[dev-dependencies]
|
|
112
112
|
pretty_assertions = "1.4"
|
|
@@ -1587,6 +1587,38 @@ mod tests {
|
|
|
1587
1587
|
assert_eq!(result, expected, "SQL: {}", sql);
|
|
1588
1588
|
}
|
|
1589
1589
|
|
|
1590
|
+
#[test]
|
|
1591
|
+
fn test_safe_namespace_parses_as_function() {
|
|
1592
|
+
let expr = parse_one(
|
|
1593
|
+
"SELECT SAFE.PARSE_JSON(data) AS json_data FROM t",
|
|
1594
|
+
DialectType::BigQuery,
|
|
1595
|
+
)
|
|
1596
|
+
.expect("parse");
|
|
1597
|
+
|
|
1598
|
+
let Expression::Select(select) = expr else {
|
|
1599
|
+
panic!("expected SELECT");
|
|
1600
|
+
};
|
|
1601
|
+
let Expression::Alias(alias) = &select.expressions[0] else {
|
|
1602
|
+
panic!("expected alias");
|
|
1603
|
+
};
|
|
1604
|
+
let Expression::Function(function) = &alias.this else {
|
|
1605
|
+
panic!("expected SAFE namespace call to parse as Function");
|
|
1606
|
+
};
|
|
1607
|
+
|
|
1608
|
+
assert_eq!(function.name, "SAFE.PARSE_JSON");
|
|
1609
|
+
assert_eq!(function.args.len(), 1);
|
|
1610
|
+
}
|
|
1611
|
+
|
|
1612
|
+
#[test]
|
|
1613
|
+
fn test_safe_namespace_identity() {
|
|
1614
|
+
bigquery_identity("SAFE.PARSE_JSON(data)", "SAFE.PARSE_JSON(data)");
|
|
1615
|
+
bigquery_identity(
|
|
1616
|
+
"SAFE.PARSE_DATE('%Y-%m-%d', date_col)",
|
|
1617
|
+
"SAFE.PARSE_DATE('%F', date_col)",
|
|
1618
|
+
);
|
|
1619
|
+
bigquery_identity("SAFE.DIVIDE(a, b)", "SAFE.DIVIDE(a, b)");
|
|
1620
|
+
}
|
|
1621
|
+
|
|
1590
1622
|
#[test]
|
|
1591
1623
|
fn test_cast_char_to_string() {
|
|
1592
1624
|
bigquery_identity("CAST(x AS CHAR)", "CAST(x AS STRING)");
|
|
@@ -2918,6 +2918,31 @@ where
|
|
|
2918
2918
|
Expression::CreateTask(ct)
|
|
2919
2919
|
}
|
|
2920
2920
|
|
|
2921
|
+
// Prepare: recurse into the prepared statement body
|
|
2922
|
+
Expression::Prepare(mut prepare) => {
|
|
2923
|
+
prepare.statement = transform_recursive(prepare.statement, transform_fn)?;
|
|
2924
|
+
Expression::Prepare(prepare)
|
|
2925
|
+
}
|
|
2926
|
+
|
|
2927
|
+
// Execute: recurse into procedure/prepared name and argument values
|
|
2928
|
+
Expression::Execute(mut execute) => {
|
|
2929
|
+
execute.this = transform_recursive(execute.this, transform_fn)?;
|
|
2930
|
+
execute.arguments = execute
|
|
2931
|
+
.arguments
|
|
2932
|
+
.into_iter()
|
|
2933
|
+
.map(|argument| transform_recursive(argument, transform_fn))
|
|
2934
|
+
.collect::<Result<Vec<_>>>()?;
|
|
2935
|
+
execute.parameters = execute
|
|
2936
|
+
.parameters
|
|
2937
|
+
.into_iter()
|
|
2938
|
+
.map(|mut parameter| {
|
|
2939
|
+
parameter.value = transform_recursive(parameter.value, transform_fn)?;
|
|
2940
|
+
Ok(parameter)
|
|
2941
|
+
})
|
|
2942
|
+
.collect::<Result<Vec<_>>>()?;
|
|
2943
|
+
Expression::Execute(execute)
|
|
2944
|
+
}
|
|
2945
|
+
|
|
2921
2946
|
// CreateProcedure: recurse into body expressions
|
|
2922
2947
|
Expression::CreateProcedure(mut cp) => {
|
|
2923
2948
|
if let Some(body) = cp.body.take() {
|
|
@@ -587,6 +587,8 @@ pub enum Expression {
|
|
|
587
587
|
// Transaction and other commands
|
|
588
588
|
Command(Box<Command>),
|
|
589
589
|
Kill(Box<Kill>),
|
|
590
|
+
/// PREPARE statement (PostgreSQL/generic prepared statement definition)
|
|
591
|
+
Prepare(Box<PrepareStatement>),
|
|
590
592
|
/// EXEC/EXECUTE statement (TSQL stored procedure call)
|
|
591
593
|
Execute(Box<ExecuteStatement>),
|
|
592
594
|
|
|
@@ -1197,6 +1199,7 @@ impl Expression {
|
|
|
1197
1199
|
| Expression::Describe(_)
|
|
1198
1200
|
| Expression::Show(_)
|
|
1199
1201
|
| Expression::Kill(_)
|
|
1202
|
+
| Expression::Prepare(_)
|
|
1200
1203
|
| Expression::Execute(_)
|
|
1201
1204
|
| Expression::Declare(_)
|
|
1202
1205
|
| Expression::Refresh(_)
|
|
@@ -2235,6 +2238,7 @@ impl Expression {
|
|
|
2235
2238
|
Expression::Command(_) => "command",
|
|
2236
2239
|
Expression::TryCatch(_) => "try_catch",
|
|
2237
2240
|
Expression::Kill(_) => "kill",
|
|
2241
|
+
Expression::Prepare(_) => "prepare",
|
|
2238
2242
|
Expression::Execute(_) => "execute",
|
|
2239
2243
|
Expression::Raw(_) => "raw",
|
|
2240
2244
|
Expression::CreateTask(_) => "create_task",
|
|
@@ -6008,6 +6012,20 @@ pub struct Command {
|
|
|
6008
6012
|
pub this: String,
|
|
6009
6013
|
}
|
|
6010
6014
|
|
|
6015
|
+
/// PREPARE statement (PostgreSQL/generic prepared statement definition)
|
|
6016
|
+
/// Syntax: PREPARE name [(type, ...)] AS statement
|
|
6017
|
+
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
|
|
6018
|
+
#[cfg_attr(feature = "bindings", derive(TS))]
|
|
6019
|
+
pub struct PrepareStatement {
|
|
6020
|
+
/// The prepared statement name.
|
|
6021
|
+
pub name: Identifier,
|
|
6022
|
+
/// Optional PostgreSQL parameter type list.
|
|
6023
|
+
#[serde(default, skip_serializing_if = "Vec::is_empty")]
|
|
6024
|
+
pub parameter_types: Vec<DataType>,
|
|
6025
|
+
/// The statement to execute when the prepared statement is invoked.
|
|
6026
|
+
pub statement: Expression,
|
|
6027
|
+
}
|
|
6028
|
+
|
|
6011
6029
|
/// T-SQL TRY/CATCH block.
|
|
6012
6030
|
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
|
|
6013
6031
|
#[cfg_attr(feature = "bindings", derive(TS))]
|
|
@@ -6030,6 +6048,12 @@ pub struct ExecuteStatement {
|
|
|
6030
6048
|
/// Named parameters: @param=value pairs
|
|
6031
6049
|
#[serde(default)]
|
|
6032
6050
|
pub parameters: Vec<ExecuteParameter>,
|
|
6051
|
+
/// Positional prepared statement arguments, used by PostgreSQL EXECUTE name(...).
|
|
6052
|
+
#[serde(default, skip_serializing_if = "Vec::is_empty")]
|
|
6053
|
+
pub arguments: Vec<Expression>,
|
|
6054
|
+
/// Whether this statement represents PostgreSQL-style prepared statement execution.
|
|
6055
|
+
#[serde(default, skip_serializing_if = "std::ops::Not::not")]
|
|
6056
|
+
pub prepared: bool,
|
|
6033
6057
|
/// Trailing clause text (e.g. WITH RESULT SETS ((...)))
|
|
6034
6058
|
#[serde(default, skip_serializing_if = "Option::is_none")]
|
|
6035
6059
|
pub suffix: Option<String>,
|
|
@@ -3357,10 +3357,24 @@ impl Generator {
|
|
|
3357
3357
|
self.generate_expression(&kill.this)?;
|
|
3358
3358
|
Ok(())
|
|
3359
3359
|
}
|
|
3360
|
+
Expression::Prepare(prepare) => self.generate_prepare(prepare),
|
|
3360
3361
|
Expression::Execute(exec) => {
|
|
3361
3362
|
self.write_keyword("EXECUTE");
|
|
3362
3363
|
self.write_space();
|
|
3363
3364
|
self.generate_expression(&exec.this)?;
|
|
3365
|
+
if exec.prepared {
|
|
3366
|
+
if !exec.arguments.is_empty() {
|
|
3367
|
+
self.write("(");
|
|
3368
|
+
for (i, argument) in exec.arguments.iter().enumerate() {
|
|
3369
|
+
if i > 0 {
|
|
3370
|
+
self.write(", ");
|
|
3371
|
+
}
|
|
3372
|
+
self.generate_expression(argument)?;
|
|
3373
|
+
}
|
|
3374
|
+
self.write(")");
|
|
3375
|
+
}
|
|
3376
|
+
return Ok(());
|
|
3377
|
+
}
|
|
3364
3378
|
for (i, param) in exec.parameters.iter().enumerate() {
|
|
3365
3379
|
if i == 0 {
|
|
3366
3380
|
self.write_space();
|
|
@@ -15616,6 +15630,28 @@ impl Generator {
|
|
|
15616
15630
|
Ok(())
|
|
15617
15631
|
}
|
|
15618
15632
|
|
|
15633
|
+
fn generate_prepare(&mut self, prepare: &PrepareStatement) -> Result<()> {
|
|
15634
|
+
self.write_keyword("PREPARE");
|
|
15635
|
+
self.write_space();
|
|
15636
|
+
self.generate_identifier(&prepare.name)?;
|
|
15637
|
+
|
|
15638
|
+
if !prepare.parameter_types.is_empty() {
|
|
15639
|
+
self.write(" (");
|
|
15640
|
+
for (i, data_type) in prepare.parameter_types.iter().enumerate() {
|
|
15641
|
+
if i > 0 {
|
|
15642
|
+
self.write(", ");
|
|
15643
|
+
}
|
|
15644
|
+
self.generate_data_type(data_type)?;
|
|
15645
|
+
}
|
|
15646
|
+
self.write(")");
|
|
15647
|
+
}
|
|
15648
|
+
|
|
15649
|
+
self.write_space();
|
|
15650
|
+
self.write_keyword("AS");
|
|
15651
|
+
self.write_space();
|
|
15652
|
+
self.generate_expression(&prepare.statement)
|
|
15653
|
+
}
|
|
15654
|
+
|
|
15619
15655
|
/// Generate a pseudocolumn (Oracle ROWNUM, ROWID, LEVEL, etc.)
|
|
15620
15656
|
/// Pseudocolumns should NEVER be quoted, as quoting breaks them in Oracle
|
|
15621
15657
|
fn generate_pseudocolumn(&mut self, pc: &Pseudocolumn) -> Result<()> {
|
|
@@ -120,7 +120,8 @@ pub fn lineage(
|
|
|
120
120
|
trim_selects: bool,
|
|
121
121
|
) -> Result<LineageNode> {
|
|
122
122
|
// Fast path: skip clone when there are no CTEs to expand
|
|
123
|
-
let
|
|
123
|
+
let effective_sql = lineage_effective_expression(sql);
|
|
124
|
+
let has_with = matches!(effective_sql, Expression::Select(s) if s.with.is_some());
|
|
124
125
|
if !has_with {
|
|
125
126
|
return lineage_from_expression(column, sql, dialect, trim_selects);
|
|
126
127
|
}
|
|
@@ -181,6 +182,7 @@ fn lineage_from_expression(
|
|
|
181
182
|
dialect: Option<DialectType>,
|
|
182
183
|
trim_selects: bool,
|
|
183
184
|
) -> Result<LineageNode> {
|
|
185
|
+
let sql = lineage_effective_expression(sql);
|
|
184
186
|
let scope = build_scope(sql);
|
|
185
187
|
to_node(
|
|
186
188
|
ColumnRef::Name(column),
|
|
@@ -193,6 +195,13 @@ fn lineage_from_expression(
|
|
|
193
195
|
)
|
|
194
196
|
}
|
|
195
197
|
|
|
198
|
+
fn lineage_effective_expression(sql: &Expression) -> &Expression {
|
|
199
|
+
match sql {
|
|
200
|
+
Expression::Prepare(prepare) => &prepare.statement,
|
|
201
|
+
_ => sql,
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
|
|
196
205
|
// ---------------------------------------------------------------------------
|
|
197
206
|
// CTE star expansion
|
|
198
207
|
// ---------------------------------------------------------------------------
|
|
@@ -222,6 +231,11 @@ fn normalize_cte_name(ident: &Identifier) -> String {
|
|
|
222
231
|
/// case-insensitively (lowercased), while quoted names preserve their original case.
|
|
223
232
|
/// This matches sqlglot's `normalize_identifiers` behavior.
|
|
224
233
|
pub fn expand_cte_stars(expr: &mut Expression, schema: Option<&dyn Schema>) {
|
|
234
|
+
if let Expression::Prepare(prepare) = expr {
|
|
235
|
+
expand_cte_stars(&mut prepare.statement, schema);
|
|
236
|
+
return;
|
|
237
|
+
}
|
|
238
|
+
|
|
225
239
|
let select = match expr {
|
|
226
240
|
Expression::Select(s) => s,
|
|
227
241
|
_ => return,
|
|
@@ -480,6 +494,14 @@ fn get_select_sources(select: &Select) -> Vec<SourceInfo> {
|
|
|
480
494
|
let mut sources = Vec::new();
|
|
481
495
|
|
|
482
496
|
fn extract_source(expr: &Expression) -> Option<SourceInfo> {
|
|
497
|
+
fn virtual_source_info(alias: &Identifier) -> SourceInfo {
|
|
498
|
+
SourceInfo {
|
|
499
|
+
alias: alias.name.clone(),
|
|
500
|
+
normalized: normalize_cte_name(alias),
|
|
501
|
+
fq_name: alias.name.clone(),
|
|
502
|
+
}
|
|
503
|
+
}
|
|
504
|
+
|
|
483
505
|
match expr {
|
|
484
506
|
Expression::Table(t) => {
|
|
485
507
|
let normalized = normalize_cte_name(&t.name);
|
|
@@ -513,6 +535,10 @@ fn get_select_sources(select: &Select) -> Vec<SourceInfo> {
|
|
|
513
535
|
fq_name,
|
|
514
536
|
})
|
|
515
537
|
}
|
|
538
|
+
Expression::Unnest(u) => u.alias.as_ref().map(virtual_source_info),
|
|
539
|
+
Expression::Alias(a) if matches!(&a.this, Expression::Unnest(_)) => {
|
|
540
|
+
Some(virtual_source_info(&a.alias))
|
|
541
|
+
}
|
|
516
542
|
Expression::Paren(p) => extract_source(&p.this),
|
|
517
543
|
_ => None,
|
|
518
544
|
}
|
|
@@ -714,7 +740,7 @@ fn to_node_inner(
|
|
|
714
740
|
}
|
|
715
741
|
|
|
716
742
|
// 7. Column references — trace each column to its source
|
|
717
|
-
let col_refs = find_column_refs_in_expr(&select_expr);
|
|
743
|
+
let col_refs = find_column_refs_in_expr(&select_expr, dialect);
|
|
718
744
|
for col_ref in col_refs {
|
|
719
745
|
let col_name = &col_ref.column;
|
|
720
746
|
if let Some(ref table_id) = col_ref.table {
|
|
@@ -971,6 +997,10 @@ fn source_names_from_from_join(scope: &Scope) -> Vec<String> {
|
|
|
971
997
|
Expression::Subquery(subquery) => {
|
|
972
998
|
subquery.alias.as_ref().map(|alias| alias.name.clone())
|
|
973
999
|
}
|
|
1000
|
+
Expression::Unnest(unnest) => unnest.alias.as_ref().map(|alias| alias.name.clone()),
|
|
1001
|
+
Expression::Alias(alias) if matches!(&alias.this, Expression::Unnest(_)) => {
|
|
1002
|
+
Some(alias.alias.name.clone())
|
|
1003
|
+
}
|
|
974
1004
|
Expression::Paren(paren) => source_name(&paren.this),
|
|
975
1005
|
_ => None,
|
|
976
1006
|
}
|
|
@@ -1265,13 +1295,30 @@ struct SimpleColumnRef {
|
|
|
1265
1295
|
}
|
|
1266
1296
|
|
|
1267
1297
|
/// Find all column references in an expression (does not recurse into subqueries).
|
|
1268
|
-
fn find_column_refs_in_expr(
|
|
1298
|
+
fn find_column_refs_in_expr(
|
|
1299
|
+
expr: &Expression,
|
|
1300
|
+
dialect: Option<DialectType>,
|
|
1301
|
+
) -> Vec<SimpleColumnRef> {
|
|
1269
1302
|
let mut refs = Vec::new();
|
|
1270
|
-
collect_column_refs(expr, &mut refs);
|
|
1303
|
+
collect_column_refs(expr, dialect, &mut refs);
|
|
1271
1304
|
refs
|
|
1272
1305
|
}
|
|
1273
1306
|
|
|
1274
|
-
fn
|
|
1307
|
+
fn is_bigquery_safe_namespace_receiver(expr: &Expression) -> bool {
|
|
1308
|
+
match expr {
|
|
1309
|
+
Expression::Column(col) => {
|
|
1310
|
+
col.table.is_none() && !col.name.quoted && col.name.name.eq_ignore_ascii_case("SAFE")
|
|
1311
|
+
}
|
|
1312
|
+
Expression::Identifier(id) => !id.quoted && id.name.eq_ignore_ascii_case("SAFE"),
|
|
1313
|
+
_ => false,
|
|
1314
|
+
}
|
|
1315
|
+
}
|
|
1316
|
+
|
|
1317
|
+
fn collect_column_refs(
|
|
1318
|
+
expr: &Expression,
|
|
1319
|
+
dialect: Option<DialectType>,
|
|
1320
|
+
refs: &mut Vec<SimpleColumnRef>,
|
|
1321
|
+
) {
|
|
1275
1322
|
let mut stack: Vec<&Expression> = vec![expr];
|
|
1276
1323
|
|
|
1277
1324
|
while let Some(current) = stack.pop() {
|
|
@@ -1638,7 +1685,11 @@ fn collect_column_refs(expr: &Expression, refs: &mut Vec<SimpleColumnRef>) {
|
|
|
1638
1685
|
stack.push(&d.this);
|
|
1639
1686
|
}
|
|
1640
1687
|
Expression::MethodCall(m) => {
|
|
1641
|
-
|
|
1688
|
+
if !matches!(dialect, Some(DialectType::BigQuery))
|
|
1689
|
+
|| !is_bigquery_safe_namespace_receiver(&m.this)
|
|
1690
|
+
{
|
|
1691
|
+
stack.push(&m.this);
|
|
1692
|
+
}
|
|
1642
1693
|
for arg in &m.args {
|
|
1643
1694
|
stack.push(arg);
|
|
1644
1695
|
}
|
|
@@ -2465,6 +2516,45 @@ mod tests {
|
|
|
2465
2516
|
assert_eq!(node.name, "name");
|
|
2466
2517
|
}
|
|
2467
2518
|
|
|
2519
|
+
#[test]
|
|
2520
|
+
fn test_lineage_bigquery_unnest_alias_source_issue_209() {
|
|
2521
|
+
let expr = parse_one(
|
|
2522
|
+
r#"
|
|
2523
|
+
SELECT date_val AS week_start
|
|
2524
|
+
FROM UNNEST(GENERATE_DATE_ARRAY('2024-01-01', '2024-12-31', INTERVAL 1 WEEK)) AS date_val
|
|
2525
|
+
"#,
|
|
2526
|
+
DialectType::BigQuery,
|
|
2527
|
+
)
|
|
2528
|
+
.expect("parse");
|
|
2529
|
+
|
|
2530
|
+
let node = lineage("week_start", &expr, Some(DialectType::BigQuery), false)
|
|
2531
|
+
.expect("lineage should resolve UNNEST alias as a source");
|
|
2532
|
+
let child = node
|
|
2533
|
+
.downstream
|
|
2534
|
+
.first()
|
|
2535
|
+
.expect("week_start should have downstream lineage");
|
|
2536
|
+
|
|
2537
|
+
assert_eq!(child.name, "date_val.date_val");
|
|
2538
|
+
assert_eq!(child.source_name, "date_val");
|
|
2539
|
+
|
|
2540
|
+
let Expression::Column(column) = &child.expression else {
|
|
2541
|
+
panic!(
|
|
2542
|
+
"expected downstream column expression, got {:?}",
|
|
2543
|
+
child.expression
|
|
2544
|
+
);
|
|
2545
|
+
};
|
|
2546
|
+
assert_eq!(column.name.name, "date_val");
|
|
2547
|
+
assert_eq!(
|
|
2548
|
+
column.table.as_ref().map(|table| table.name.as_str()),
|
|
2549
|
+
Some("date_val")
|
|
2550
|
+
);
|
|
2551
|
+
assert!(
|
|
2552
|
+
matches!(&child.source, Expression::Alias(alias) if matches!(&alias.this, Expression::Unnest(_)) && alias.alias.name == "date_val"),
|
|
2553
|
+
"expected UNNEST source expression, got {:?}",
|
|
2554
|
+
child.source
|
|
2555
|
+
);
|
|
2556
|
+
}
|
|
2557
|
+
|
|
2468
2558
|
#[test]
|
|
2469
2559
|
fn test_lineage_with_schema_snowflake_datediff_date_part_issue_61() {
|
|
2470
2560
|
let expr = parse_one(
|
|
@@ -2989,6 +3079,71 @@ mod tests {
|
|
|
2989
3079
|
);
|
|
2990
3080
|
}
|
|
2991
3081
|
|
|
3082
|
+
#[test]
|
|
3083
|
+
fn test_lineage_bigquery_safe_namespace_issue207() {
|
|
3084
|
+
let query = r#"
|
|
3085
|
+
WITH import_cte AS (
|
|
3086
|
+
SELECT timestamp, data, operation
|
|
3087
|
+
FROM `project`.`dataset`.`source_table`
|
|
3088
|
+
),
|
|
3089
|
+
transform_cte AS (
|
|
3090
|
+
SELECT
|
|
3091
|
+
timestamp,
|
|
3092
|
+
SAFE.PARSE_JSON(data) AS json_data
|
|
3093
|
+
FROM import_cte
|
|
3094
|
+
)
|
|
3095
|
+
SELECT json_data FROM transform_cte
|
|
3096
|
+
"#;
|
|
3097
|
+
let expr = parse_one(query, DialectType::BigQuery).expect("parse");
|
|
3098
|
+
let node = lineage("json_data", &expr, Some(DialectType::BigQuery), false)
|
|
3099
|
+
.expect("lineage should resolve SAFE.PARSE_JSON arguments");
|
|
3100
|
+
let names: Vec<_> = node.walk().map(|n| n.name.clone()).collect();
|
|
3101
|
+
|
|
3102
|
+
assert!(
|
|
3103
|
+
names.iter().any(|name| name == "source_table.data"),
|
|
3104
|
+
"expected source_table.data in lineage, got {names:?}"
|
|
3105
|
+
);
|
|
3106
|
+
assert!(
|
|
3107
|
+
!names
|
|
3108
|
+
.iter()
|
|
3109
|
+
.any(|name| name.eq_ignore_ascii_case("import_cte.safe")),
|
|
3110
|
+
"did not expect SAFE namespace receiver in lineage, got {names:?}"
|
|
3111
|
+
);
|
|
3112
|
+
}
|
|
3113
|
+
|
|
3114
|
+
#[test]
|
|
3115
|
+
fn test_lineage_bigquery_safe_namespace_method_call_guard() {
|
|
3116
|
+
let expr = parse("SELECT SAFE.PARSE_JSON(data) AS json_data FROM t");
|
|
3117
|
+
let node = lineage("json_data", &expr, Some(DialectType::BigQuery), false)
|
|
3118
|
+
.expect("lineage should resolve SAFE.PARSE_JSON arguments");
|
|
3119
|
+
let names: Vec<_> = node.walk().map(|n| n.name.clone()).collect();
|
|
3120
|
+
|
|
3121
|
+
assert!(
|
|
3122
|
+
names.iter().any(|name| name == "t.data"),
|
|
3123
|
+
"expected t.data in lineage, got {names:?}"
|
|
3124
|
+
);
|
|
3125
|
+
assert!(
|
|
3126
|
+
!names.iter().any(|name| name.eq_ignore_ascii_case("t.safe")),
|
|
3127
|
+
"did not expect SAFE namespace receiver in lineage, got {names:?}"
|
|
3128
|
+
);
|
|
3129
|
+
}
|
|
3130
|
+
|
|
3131
|
+
#[test]
|
|
3132
|
+
fn test_lineage_method_call_receiver_control() {
|
|
3133
|
+
let expr = parse("SELECT obj.METHOD(arg) AS out FROM t");
|
|
3134
|
+
let node = lineage("out", &expr, None, false).expect("lineage");
|
|
3135
|
+
let names: Vec<_> = node.walk().map(|n| n.name.clone()).collect();
|
|
3136
|
+
|
|
3137
|
+
assert!(
|
|
3138
|
+
names.iter().any(|name| name == "t.obj"),
|
|
3139
|
+
"expected ordinary method receiver to remain in lineage, got {names:?}"
|
|
3140
|
+
);
|
|
3141
|
+
assert!(
|
|
3142
|
+
names.iter().any(|name| name == "t.arg"),
|
|
3143
|
+
"expected method argument in lineage, got {names:?}"
|
|
3144
|
+
);
|
|
3145
|
+
}
|
|
3146
|
+
|
|
2992
3147
|
#[test]
|
|
2993
3148
|
fn test_lineage_upper_function() {
|
|
2994
3149
|
let expr = parse("SELECT UPPER(name) AS upper_name FROM users");
|
|
@@ -363,6 +363,7 @@ fn analyze_statement(
|
|
|
363
363
|
warnings: &mut Vec<OpenLineageWarning>,
|
|
364
364
|
) -> Result<StatementAnalysis> {
|
|
365
365
|
match expr {
|
|
366
|
+
Expression::Prepare(prepare) => analyze_statement(&prepare.statement, options, warnings),
|
|
366
367
|
Expression::Select(select) => {
|
|
367
368
|
let output = if let Some(into) = &select.into {
|
|
368
369
|
dataset_from_expression(&into.this, options)?
|
|
@@ -914,6 +915,7 @@ fn collect_source_table(expr: &Expression, result: &mut Vec<(String, String)>) {
|
|
|
914
915
|
|
|
915
916
|
fn leftmost_select(expr: &Expression) -> Option<&Select> {
|
|
916
917
|
match expr {
|
|
918
|
+
Expression::Prepare(prepare) => leftmost_select(&prepare.statement),
|
|
917
919
|
Expression::Select(select) => Some(select),
|
|
918
920
|
Expression::Union(union) => leftmost_select(&union.left),
|
|
919
921
|
Expression::Intersect(intersect) => leftmost_select(&intersect.left),
|
|
@@ -1103,6 +1105,19 @@ mod tests {
|
|
|
1103
1105
|
assert_eq!(field.input_fields[0].transformations[0].subtype, "IDENTITY");
|
|
1104
1106
|
}
|
|
1105
1107
|
|
|
1108
|
+
#[test]
|
|
1109
|
+
fn emits_column_lineage_for_prepared_statement_body() {
|
|
1110
|
+
let result = openlineage_column_lineage(
|
|
1111
|
+
"PREPARE leak AS SELECT id FROM sensitive_table WHERE id = $1",
|
|
1112
|
+
&options(),
|
|
1113
|
+
)
|
|
1114
|
+
.expect("lineage");
|
|
1115
|
+
let field = result.facet.fields.get("id").expect("field id");
|
|
1116
|
+
assert_eq!(field.input_fields.len(), 1);
|
|
1117
|
+
assert_eq!(field.input_fields[0].name, "sensitive_table");
|
|
1118
|
+
assert_eq!(field.input_fields[0].field, "id");
|
|
1119
|
+
}
|
|
1120
|
+
|
|
1106
1121
|
#[test]
|
|
1107
1122
|
fn resolves_input_dataset_behind_table_alias() {
|
|
1108
1123
|
let result = openlineage_column_lineage("SELECT o.total FROM orders o", &options())
|
|
@@ -1126,6 +1141,78 @@ mod tests {
|
|
|
1126
1141
|
.all(|f| f.transformations[0].subtype == "TRANSFORMATION"));
|
|
1127
1142
|
}
|
|
1128
1143
|
|
|
1144
|
+
#[test]
|
|
1145
|
+
fn omits_bigquery_safe_namespace_from_column_lineage_issue207() {
|
|
1146
|
+
let mut opts = options();
|
|
1147
|
+
opts.dialect = DialectType::BigQuery;
|
|
1148
|
+
|
|
1149
|
+
let result = openlineage_column_lineage(
|
|
1150
|
+
r#"
|
|
1151
|
+
WITH import_cte AS (
|
|
1152
|
+
SELECT timestamp, data, operation
|
|
1153
|
+
FROM `project`.`dataset`.`source_table`
|
|
1154
|
+
),
|
|
1155
|
+
transform_cte AS (
|
|
1156
|
+
SELECT
|
|
1157
|
+
timestamp,
|
|
1158
|
+
SAFE.PARSE_JSON(data) AS json_data
|
|
1159
|
+
FROM import_cte
|
|
1160
|
+
)
|
|
1161
|
+
SELECT json_data FROM transform_cte
|
|
1162
|
+
"#,
|
|
1163
|
+
&opts,
|
|
1164
|
+
)
|
|
1165
|
+
.expect("lineage");
|
|
1166
|
+
let field = result.facet.fields.get("json_data").expect("json_data");
|
|
1167
|
+
|
|
1168
|
+
assert!(
|
|
1169
|
+
field.input_fields.iter().any(|input| input.field == "data"),
|
|
1170
|
+
"expected data input field, got {:?}",
|
|
1171
|
+
field.input_fields
|
|
1172
|
+
);
|
|
1173
|
+
assert!(
|
|
1174
|
+
!field
|
|
1175
|
+
.input_fields
|
|
1176
|
+
.iter()
|
|
1177
|
+
.any(|input| input.field.eq_ignore_ascii_case("safe")),
|
|
1178
|
+
"did not expect SAFE namespace as input field, got {:?}",
|
|
1179
|
+
field.input_fields
|
|
1180
|
+
);
|
|
1181
|
+
}
|
|
1182
|
+
|
|
1183
|
+
#[test]
|
|
1184
|
+
fn emits_bigquery_unnest_alias_column_lineage_issue209() {
|
|
1185
|
+
let mut opts = options();
|
|
1186
|
+
opts.dialect = DialectType::BigQuery;
|
|
1187
|
+
opts.dataset_namespace = Some("bigquery://warehouse".to_string());
|
|
1188
|
+
opts.output_dataset = Some(OpenLineageDatasetId::new(
|
|
1189
|
+
"bigquery://warehouse",
|
|
1190
|
+
"calendar",
|
|
1191
|
+
));
|
|
1192
|
+
|
|
1193
|
+
let result = openlineage_column_lineage(
|
|
1194
|
+
r#"
|
|
1195
|
+
SELECT date_val AS week_start
|
|
1196
|
+
FROM UNNEST(GENERATE_DATE_ARRAY('2024-01-01', '2024-12-31', INTERVAL 1 WEEK)) AS date_val
|
|
1197
|
+
"#,
|
|
1198
|
+
&opts,
|
|
1199
|
+
)
|
|
1200
|
+
.expect("lineage");
|
|
1201
|
+
let field = result.facet.fields.get("week_start").expect("week_start");
|
|
1202
|
+
|
|
1203
|
+
assert_eq!(field.input_fields.len(), 1);
|
|
1204
|
+
assert_eq!(field.input_fields[0].name, "date_val");
|
|
1205
|
+
assert_eq!(field.input_fields[0].field, "date_val");
|
|
1206
|
+
assert!(
|
|
1207
|
+
result
|
|
1208
|
+
.warnings
|
|
1209
|
+
.iter()
|
|
1210
|
+
.all(|warning| warning.code != "W_EMPTY_FIELD_LINEAGE"),
|
|
1211
|
+
"did not expect empty-lineage warning, got {:?}",
|
|
1212
|
+
result.warnings
|
|
1213
|
+
);
|
|
1214
|
+
}
|
|
1215
|
+
|
|
1129
1216
|
#[test]
|
|
1130
1217
|
fn emits_aggregation_column_lineage() {
|
|
1131
1218
|
let result =
|