polyglot-sql 0.7.0__tar.gz → 0.8.0__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.7.0 → polyglot_sql-0.8.0}/Cargo.lock +6 -6
- {polyglot_sql-0.7.0 → polyglot_sql-0.8.0}/Cargo.toml +1 -1
- {polyglot_sql-0.7.0 → polyglot_sql-0.8.0}/PKG-INFO +8 -1
- {polyglot_sql-0.7.0/crates/polyglot-sql-python → polyglot_sql-0.8.0}/README.md +7 -0
- {polyglot_sql-0.7.0 → polyglot_sql-0.8.0}/crates/polyglot-sql/Cargo.toml +2 -2
- {polyglot_sql-0.7.0 → polyglot_sql-0.8.0}/crates/polyglot-sql/README.md +11 -4
- {polyglot_sql-0.7.0 → polyglot_sql-0.8.0}/crates/polyglot-sql/src/lib.rs +3 -3
- {polyglot_sql-0.7.0 → polyglot_sql-0.8.0}/crates/polyglot-sql/src/lineage.rs +104 -5
- {polyglot_sql-0.7.0 → polyglot_sql-0.8.0}/crates/polyglot-sql/src/openlineage.rs +152 -16
- {polyglot_sql-0.7.0 → polyglot_sql-0.8.0}/crates/polyglot-sql/src/query_analysis.rs +33 -3
- {polyglot_sql-0.7.0 → polyglot_sql-0.8.0}/crates/polyglot-sql/tests/query_analysis.rs +31 -1
- {polyglot_sql-0.7.0 → polyglot_sql-0.8.0/crates/polyglot-sql-python}/README.md +7 -0
- {polyglot_sql-0.7.0 → polyglot_sql-0.8.0}/crates/polyglot-sql-python/tests/test_lineage.py +10 -0
- {polyglot_sql-0.7.0 → polyglot_sql-0.8.0}/crates/polyglot-sql/benches/allocation_hotspots.rs +0 -0
- {polyglot_sql-0.7.0 → polyglot_sql-0.8.0}/crates/polyglot-sql/benches/ast_traversal.rs +0 -0
- {polyglot_sql-0.7.0 → polyglot_sql-0.8.0}/crates/polyglot-sql/benches/in_list.rs +0 -0
- {polyglot_sql-0.7.0 → polyglot_sql-0.8.0}/crates/polyglot-sql/benches/lineage.rs +0 -0
- {polyglot_sql-0.7.0 → polyglot_sql-0.8.0}/crates/polyglot-sql/benches/parsing.rs +0 -0
- {polyglot_sql-0.7.0 → polyglot_sql-0.8.0}/crates/polyglot-sql/benches/performance_hotspots.rs +0 -0
- {polyglot_sql-0.7.0 → polyglot_sql-0.8.0}/crates/polyglot-sql/benches/rust_parsing.rs +0 -0
- {polyglot_sql-0.7.0 → polyglot_sql-0.8.0}/crates/polyglot-sql/benches/transpile.rs +0 -0
- {polyglot_sql-0.7.0 → polyglot_sql-0.8.0}/crates/polyglot-sql/examples/basic_usage.rs +0 -0
- {polyglot_sql-0.7.0 → polyglot_sql-0.8.0}/crates/polyglot-sql/examples/bench_json.rs +0 -0
- {polyglot_sql-0.7.0 → polyglot_sql-0.8.0}/crates/polyglot-sql/src/ast_children.rs +0 -0
- {polyglot_sql-0.7.0 → polyglot_sql-0.8.0}/crates/polyglot-sql/src/ast_json.rs +0 -0
- {polyglot_sql-0.7.0 → polyglot_sql-0.8.0}/crates/polyglot-sql/src/ast_transforms.rs +0 -0
- {polyglot_sql-0.7.0 → polyglot_sql-0.8.0}/crates/polyglot-sql/src/builder.rs +0 -0
- {polyglot_sql-0.7.0 → polyglot_sql-0.8.0}/crates/polyglot-sql/src/dialects/athena.rs +0 -0
- {polyglot_sql-0.7.0 → polyglot_sql-0.8.0}/crates/polyglot-sql/src/dialects/bigquery.rs +0 -0
- {polyglot_sql-0.7.0 → polyglot_sql-0.8.0}/crates/polyglot-sql/src/dialects/clickhouse.rs +0 -0
- {polyglot_sql-0.7.0 → polyglot_sql-0.8.0}/crates/polyglot-sql/src/dialects/cockroachdb.rs +0 -0
- {polyglot_sql-0.7.0 → polyglot_sql-0.8.0}/crates/polyglot-sql/src/dialects/databricks.rs +0 -0
- {polyglot_sql-0.7.0 → polyglot_sql-0.8.0}/crates/polyglot-sql/src/dialects/datafusion.rs +0 -0
- {polyglot_sql-0.7.0 → polyglot_sql-0.8.0}/crates/polyglot-sql/src/dialects/doris.rs +0 -0
- {polyglot_sql-0.7.0 → polyglot_sql-0.8.0}/crates/polyglot-sql/src/dialects/dremio.rs +0 -0
- {polyglot_sql-0.7.0 → polyglot_sql-0.8.0}/crates/polyglot-sql/src/dialects/drill.rs +0 -0
- {polyglot_sql-0.7.0 → polyglot_sql-0.8.0}/crates/polyglot-sql/src/dialects/druid.rs +0 -0
- {polyglot_sql-0.7.0 → polyglot_sql-0.8.0}/crates/polyglot-sql/src/dialects/duckdb.rs +0 -0
- {polyglot_sql-0.7.0 → polyglot_sql-0.8.0}/crates/polyglot-sql/src/dialects/dune.rs +0 -0
- {polyglot_sql-0.7.0 → polyglot_sql-0.8.0}/crates/polyglot-sql/src/dialects/exasol.rs +0 -0
- {polyglot_sql-0.7.0 → polyglot_sql-0.8.0}/crates/polyglot-sql/src/dialects/fabric.rs +0 -0
- {polyglot_sql-0.7.0 → polyglot_sql-0.8.0}/crates/polyglot-sql/src/dialects/generic.rs +0 -0
- {polyglot_sql-0.7.0 → polyglot_sql-0.8.0}/crates/polyglot-sql/src/dialects/hive.rs +0 -0
- {polyglot_sql-0.7.0 → polyglot_sql-0.8.0}/crates/polyglot-sql/src/dialects/materialize.rs +0 -0
- {polyglot_sql-0.7.0 → polyglot_sql-0.8.0}/crates/polyglot-sql/src/dialects/mod.rs +0 -0
- {polyglot_sql-0.7.0 → polyglot_sql-0.8.0}/crates/polyglot-sql/src/dialects/mysql.rs +0 -0
- {polyglot_sql-0.7.0 → polyglot_sql-0.8.0}/crates/polyglot-sql/src/dialects/normalization/aggregates.rs +0 -0
- {polyglot_sql-0.7.0 → polyglot_sql-0.8.0}/crates/polyglot-sql/src/dialects/normalization/collections.rs +0 -0
- {polyglot_sql-0.7.0 → polyglot_sql-0.8.0}/crates/polyglot-sql/src/dialects/normalization/json.rs +0 -0
- {polyglot_sql-0.7.0 → polyglot_sql-0.8.0}/crates/polyglot-sql/src/dialects/normalization/mod.rs +0 -0
- {polyglot_sql-0.7.0 → polyglot_sql-0.8.0}/crates/polyglot-sql/src/dialects/normalization/operators.rs +0 -0
- {polyglot_sql-0.7.0 → polyglot_sql-0.8.0}/crates/polyglot-sql/src/dialects/normalization/postgres_interval.rs +0 -0
- {polyglot_sql-0.7.0 → polyglot_sql-0.8.0}/crates/polyglot-sql/src/dialects/normalization/scalar.rs +0 -0
- {polyglot_sql-0.7.0 → polyglot_sql-0.8.0}/crates/polyglot-sql/src/dialects/normalization/statements.rs +0 -0
- {polyglot_sql-0.7.0 → polyglot_sql-0.8.0}/crates/polyglot-sql/src/dialects/normalization/temporal.rs +0 -0
- {polyglot_sql-0.7.0 → polyglot_sql-0.8.0}/crates/polyglot-sql/src/dialects/normalization/types.rs +0 -0
- {polyglot_sql-0.7.0 → polyglot_sql-0.8.0}/crates/polyglot-sql/src/dialects/oracle.rs +0 -0
- {polyglot_sql-0.7.0 → polyglot_sql-0.8.0}/crates/polyglot-sql/src/dialects/postgres.rs +0 -0
- {polyglot_sql-0.7.0 → polyglot_sql-0.8.0}/crates/polyglot-sql/src/dialects/presto.rs +0 -0
- {polyglot_sql-0.7.0 → polyglot_sql-0.8.0}/crates/polyglot-sql/src/dialects/redshift.rs +0 -0
- {polyglot_sql-0.7.0 → polyglot_sql-0.8.0}/crates/polyglot-sql/src/dialects/risingwave.rs +0 -0
- {polyglot_sql-0.7.0 → polyglot_sql-0.8.0}/crates/polyglot-sql/src/dialects/singlestore.rs +0 -0
- {polyglot_sql-0.7.0 → polyglot_sql-0.8.0}/crates/polyglot-sql/src/dialects/snowflake.rs +0 -0
- {polyglot_sql-0.7.0 → polyglot_sql-0.8.0}/crates/polyglot-sql/src/dialects/solr.rs +0 -0
- {polyglot_sql-0.7.0 → polyglot_sql-0.8.0}/crates/polyglot-sql/src/dialects/spark.rs +0 -0
- {polyglot_sql-0.7.0 → polyglot_sql-0.8.0}/crates/polyglot-sql/src/dialects/sqlite.rs +0 -0
- {polyglot_sql-0.7.0 → polyglot_sql-0.8.0}/crates/polyglot-sql/src/dialects/starrocks.rs +0 -0
- {polyglot_sql-0.7.0 → polyglot_sql-0.8.0}/crates/polyglot-sql/src/dialects/tableau.rs +0 -0
- {polyglot_sql-0.7.0 → polyglot_sql-0.8.0}/crates/polyglot-sql/src/dialects/teradata.rs +0 -0
- {polyglot_sql-0.7.0 → polyglot_sql-0.8.0}/crates/polyglot-sql/src/dialects/tidb.rs +0 -0
- {polyglot_sql-0.7.0 → polyglot_sql-0.8.0}/crates/polyglot-sql/src/dialects/trino.rs +0 -0
- {polyglot_sql-0.7.0 → polyglot_sql-0.8.0}/crates/polyglot-sql/src/dialects/tsql.rs +0 -0
- {polyglot_sql-0.7.0 → polyglot_sql-0.8.0}/crates/polyglot-sql/src/diff.rs +0 -0
- {polyglot_sql-0.7.0 → polyglot_sql-0.8.0}/crates/polyglot-sql/src/error.rs +0 -0
- {polyglot_sql-0.7.0 → polyglot_sql-0.8.0}/crates/polyglot-sql/src/expressions.rs +0 -0
- {polyglot_sql-0.7.0 → polyglot_sql-0.8.0}/crates/polyglot-sql/src/format_tokens.rs +0 -0
- {polyglot_sql-0.7.0 → polyglot_sql-0.8.0}/crates/polyglot-sql/src/function_catalog.rs +0 -0
- {polyglot_sql-0.7.0 → polyglot_sql-0.8.0}/crates/polyglot-sql/src/function_registry.rs +0 -0
- {polyglot_sql-0.7.0 → polyglot_sql-0.8.0}/crates/polyglot-sql/src/generator.rs +0 -0
- {polyglot_sql-0.7.0 → polyglot_sql-0.8.0}/crates/polyglot-sql/src/guard.rs +0 -0
- {polyglot_sql-0.7.0 → polyglot_sql-0.8.0}/crates/polyglot-sql/src/helper.rs +0 -0
- {polyglot_sql-0.7.0 → polyglot_sql-0.8.0}/crates/polyglot-sql/src/optimizer/annotate_types.rs +0 -0
- {polyglot_sql-0.7.0 → polyglot_sql-0.8.0}/crates/polyglot-sql/src/optimizer/canonicalize.rs +0 -0
- {polyglot_sql-0.7.0 → polyglot_sql-0.8.0}/crates/polyglot-sql/src/optimizer/eliminate_ctes.rs +0 -0
- {polyglot_sql-0.7.0 → polyglot_sql-0.8.0}/crates/polyglot-sql/src/optimizer/eliminate_joins.rs +0 -0
- {polyglot_sql-0.7.0 → polyglot_sql-0.8.0}/crates/polyglot-sql/src/optimizer/isolate_table_selects.rs +0 -0
- {polyglot_sql-0.7.0 → polyglot_sql-0.8.0}/crates/polyglot-sql/src/optimizer/mod.rs +0 -0
- {polyglot_sql-0.7.0 → polyglot_sql-0.8.0}/crates/polyglot-sql/src/optimizer/normalize.rs +0 -0
- {polyglot_sql-0.7.0 → polyglot_sql-0.8.0}/crates/polyglot-sql/src/optimizer/normalize_identifiers.rs +0 -0
- {polyglot_sql-0.7.0 → polyglot_sql-0.8.0}/crates/polyglot-sql/src/optimizer/optimize_joins.rs +0 -0
- {polyglot_sql-0.7.0 → polyglot_sql-0.8.0}/crates/polyglot-sql/src/optimizer/optimizer.rs +0 -0
- {polyglot_sql-0.7.0 → polyglot_sql-0.8.0}/crates/polyglot-sql/src/optimizer/pushdown_predicates.rs +0 -0
- {polyglot_sql-0.7.0 → polyglot_sql-0.8.0}/crates/polyglot-sql/src/optimizer/pushdown_projections.rs +0 -0
- {polyglot_sql-0.7.0 → polyglot_sql-0.8.0}/crates/polyglot-sql/src/optimizer/qualify_columns.rs +0 -0
- {polyglot_sql-0.7.0 → polyglot_sql-0.8.0}/crates/polyglot-sql/src/optimizer/qualify_tables.rs +0 -0
- {polyglot_sql-0.7.0 → polyglot_sql-0.8.0}/crates/polyglot-sql/src/optimizer/simplify.rs +0 -0
- {polyglot_sql-0.7.0 → polyglot_sql-0.8.0}/crates/polyglot-sql/src/optimizer/subquery.rs +0 -0
- {polyglot_sql-0.7.0 → polyglot_sql-0.8.0}/crates/polyglot-sql/src/parser.rs +0 -0
- {polyglot_sql-0.7.0 → polyglot_sql-0.8.0}/crates/polyglot-sql/src/planner.rs +0 -0
- {polyglot_sql-0.7.0 → polyglot_sql-0.8.0}/crates/polyglot-sql/src/resolver.rs +0 -0
- {polyglot_sql-0.7.0 → polyglot_sql-0.8.0}/crates/polyglot-sql/src/schema.rs +0 -0
- {polyglot_sql-0.7.0 → polyglot_sql-0.8.0}/crates/polyglot-sql/src/scope.rs +0 -0
- {polyglot_sql-0.7.0 → polyglot_sql-0.8.0}/crates/polyglot-sql/src/time.rs +0 -0
- {polyglot_sql-0.7.0 → polyglot_sql-0.8.0}/crates/polyglot-sql/src/tokens.rs +0 -0
- {polyglot_sql-0.7.0 → polyglot_sql-0.8.0}/crates/polyglot-sql/src/transforms.rs +0 -0
- {polyglot_sql-0.7.0 → polyglot_sql-0.8.0}/crates/polyglot-sql/src/traversal.rs +0 -0
- {polyglot_sql-0.7.0 → polyglot_sql-0.8.0}/crates/polyglot-sql/src/trie.rs +0 -0
- {polyglot_sql-0.7.0 → polyglot_sql-0.8.0}/crates/polyglot-sql/src/validation/tests.rs +0 -0
- {polyglot_sql-0.7.0 → polyglot_sql-0.8.0}/crates/polyglot-sql/src/validation.rs +0 -0
- {polyglot_sql-0.7.0 → polyglot_sql-0.8.0}/crates/polyglot-sql/tests/analyze_failures.rs +0 -0
- {polyglot_sql-0.7.0 → polyglot_sql-0.8.0}/crates/polyglot-sql/tests/clickhouse_regression.rs +0 -0
- {polyglot_sql-0.7.0 → polyglot_sql-0.8.0}/crates/polyglot-sql/tests/common/known_failures.rs +0 -0
- {polyglot_sql-0.7.0 → polyglot_sql-0.8.0}/crates/polyglot-sql/tests/common/mod.rs +0 -0
- {polyglot_sql-0.7.0 → polyglot_sql-0.8.0}/crates/polyglot-sql/tests/common/test_data.rs +0 -0
- {polyglot_sql-0.7.0 → polyglot_sql-0.8.0}/crates/polyglot-sql/tests/common/test_runner.rs +0 -0
- {polyglot_sql-0.7.0 → polyglot_sql-0.8.0}/crates/polyglot-sql/tests/create_view_security_comment_regression.rs +0 -0
- {polyglot_sql-0.7.0 → polyglot_sql-0.8.0}/crates/polyglot-sql/tests/custom_clickhouse_coverage.rs +0 -0
- {polyglot_sql-0.7.0 → polyglot_sql-0.8.0}/crates/polyglot-sql/tests/custom_clickhouse_parser.rs +0 -0
- {polyglot_sql-0.7.0 → polyglot_sql-0.8.0}/crates/polyglot-sql/tests/custom_dialect.rs +0 -0
- {polyglot_sql-0.7.0 → polyglot_sql-0.8.0}/crates/polyglot-sql/tests/custom_dialect_tests.rs +0 -0
- {polyglot_sql-0.7.0 → polyglot_sql-0.8.0}/crates/polyglot-sql/tests/custom_fixtures/datafusion/ddl.json +0 -0
- {polyglot_sql-0.7.0 → polyglot_sql-0.8.0}/crates/polyglot-sql/tests/custom_fixtures/datafusion/dml.json +0 -0
- {polyglot_sql-0.7.0 → polyglot_sql-0.8.0}/crates/polyglot-sql/tests/custom_fixtures/datafusion/functions.json +0 -0
- {polyglot_sql-0.7.0 → polyglot_sql-0.8.0}/crates/polyglot-sql/tests/custom_fixtures/datafusion/identity.json +0 -0
- {polyglot_sql-0.7.0 → polyglot_sql-0.8.0}/crates/polyglot-sql/tests/custom_fixtures/datafusion/operators.json +0 -0
- {polyglot_sql-0.7.0 → polyglot_sql-0.8.0}/crates/polyglot-sql/tests/custom_fixtures/datafusion/select.json +0 -0
- {polyglot_sql-0.7.0 → polyglot_sql-0.8.0}/crates/polyglot-sql/tests/custom_fixtures/datafusion/transpilation.json +0 -0
- {polyglot_sql-0.7.0 → polyglot_sql-0.8.0}/crates/polyglot-sql/tests/custom_fixtures/datafusion/types.json +0 -0
- {polyglot_sql-0.7.0 → polyglot_sql-0.8.0}/crates/polyglot-sql/tests/data_type_api.rs +0 -0
- {polyglot_sql-0.7.0 → polyglot_sql-0.8.0}/crates/polyglot-sql/tests/deep_nesting_regression.rs +0 -0
- {polyglot_sql-0.7.0 → polyglot_sql-0.8.0}/crates/polyglot-sql/tests/dialect_matrix.rs +0 -0
- {polyglot_sql-0.7.0 → polyglot_sql-0.8.0}/crates/polyglot-sql/tests/error_handling.rs +0 -0
- {polyglot_sql-0.7.0 → polyglot_sql-0.8.0}/crates/polyglot-sql/tests/fabric_regression.rs +0 -0
- {polyglot_sql-0.7.0 → polyglot_sql-0.8.0}/crates/polyglot-sql/tests/fabric_tpch_regression.rs +0 -0
- {polyglot_sql-0.7.0 → polyglot_sql-0.8.0}/crates/polyglot-sql/tests/identity_roundtrip.rs +0 -0
- {polyglot_sql-0.7.0 → polyglot_sql-0.8.0}/crates/polyglot-sql/tests/oracle_data_types_regression.rs +0 -0
- {polyglot_sql-0.7.0 → polyglot_sql-0.8.0}/crates/polyglot-sql/tests/postgres_explain_options.rs +0 -0
- {polyglot_sql-0.7.0 → polyglot_sql-0.8.0}/crates/polyglot-sql/tests/postgres_regexp_clickhouse.rs +0 -0
- {polyglot_sql-0.7.0 → polyglot_sql-0.8.0}/crates/polyglot-sql/tests/postgres_sqlite_regression.rs +0 -0
- {polyglot_sql-0.7.0 → polyglot_sql-0.8.0}/crates/polyglot-sql/tests/query_analysis_regression.rs +0 -0
- {polyglot_sql-0.7.0 → polyglot_sql-0.8.0}/crates/polyglot-sql/tests/snowflake_regression_test.rs +0 -0
- {polyglot_sql-0.7.0 → polyglot_sql-0.8.0}/crates/polyglot-sql/tests/sqlglot_compat.rs +0 -0
- {polyglot_sql-0.7.0 → polyglot_sql-0.8.0}/crates/polyglot-sql/tests/sqlglot_dialect_identity.rs +0 -0
- {polyglot_sql-0.7.0 → polyglot_sql-0.8.0}/crates/polyglot-sql/tests/sqlglot_identity.rs +0 -0
- {polyglot_sql-0.7.0 → polyglot_sql-0.8.0}/crates/polyglot-sql/tests/sqlglot_identity_detailed.rs +0 -0
- {polyglot_sql-0.7.0 → polyglot_sql-0.8.0}/crates/polyglot-sql/tests/sqlglot_parser.rs +0 -0
- {polyglot_sql-0.7.0 → polyglot_sql-0.8.0}/crates/polyglot-sql/tests/sqlglot_pretty.rs +0 -0
- {polyglot_sql-0.7.0 → polyglot_sql-0.8.0}/crates/polyglot-sql/tests/sqlglot_transpilation.rs +0 -0
- {polyglot_sql-0.7.0 → polyglot_sql-0.8.0}/crates/polyglot-sql/tests/sqlglot_transpile.rs +0 -0
- {polyglot_sql-0.7.0 → polyglot_sql-0.8.0}/crates/polyglot-sql/tests/tpch_transpile_stack.rs +0 -0
- {polyglot_sql-0.7.0 → polyglot_sql-0.8.0}/crates/polyglot-sql/tests/transform_regression.rs +0 -0
- {polyglot_sql-0.7.0 → polyglot_sql-0.8.0}/crates/polyglot-sql/tests/tsql_regression.rs +0 -0
- {polyglot_sql-0.7.0 → polyglot_sql-0.8.0}/crates/polyglot-sql-ast-derive/Cargo.toml +0 -0
- {polyglot_sql-0.7.0 → polyglot_sql-0.8.0}/crates/polyglot-sql-ast-derive/src/lib.rs +0 -0
- {polyglot_sql-0.7.0 → polyglot_sql-0.8.0}/crates/polyglot-sql-function-catalogs/Cargo.toml +0 -0
- {polyglot_sql-0.7.0 → polyglot_sql-0.8.0}/crates/polyglot-sql-function-catalogs/README.md +0 -0
- {polyglot_sql-0.7.0 → polyglot_sql-0.8.0}/crates/polyglot-sql-function-catalogs/src/clickhouse.rs +0 -0
- {polyglot_sql-0.7.0 → polyglot_sql-0.8.0}/crates/polyglot-sql-function-catalogs/src/duckdb.rs +0 -0
- {polyglot_sql-0.7.0 → polyglot_sql-0.8.0}/crates/polyglot-sql-function-catalogs/src/lib.rs +0 -0
- {polyglot_sql-0.7.0 → polyglot_sql-0.8.0}/crates/polyglot-sql-function-catalogs/tools/clickhouse/extract_functions.py +0 -0
- {polyglot_sql-0.7.0 → polyglot_sql-0.8.0}/crates/polyglot-sql-function-catalogs/tools/duckdb/extract_functions.py +0 -0
- {polyglot_sql-0.7.0 → polyglot_sql-0.8.0}/crates/polyglot-sql-python/Cargo.toml +0 -0
- {polyglot_sql-0.7.0 → polyglot_sql-0.8.0}/crates/polyglot-sql-python/docs/api.md +0 -0
- {polyglot_sql-0.7.0 → polyglot_sql-0.8.0}/crates/polyglot-sql-python/docs/index.md +0 -0
- {polyglot_sql-0.7.0 → polyglot_sql-0.8.0}/crates/polyglot-sql-python/mkdocs.yml +0 -0
- {polyglot_sql-0.7.0 → polyglot_sql-0.8.0}/crates/polyglot-sql-python/src/annotate_types.rs +0 -0
- {polyglot_sql-0.7.0 → polyglot_sql-0.8.0}/crates/polyglot-sql-python/src/dialects.rs +0 -0
- {polyglot_sql-0.7.0 → polyglot_sql-0.8.0}/crates/polyglot-sql-python/src/diff.rs +0 -0
- {polyglot_sql-0.7.0 → polyglot_sql-0.8.0}/crates/polyglot-sql-python/src/errors.rs +0 -0
- {polyglot_sql-0.7.0 → polyglot_sql-0.8.0}/crates/polyglot-sql-python/src/expr.rs +0 -0
- {polyglot_sql-0.7.0 → polyglot_sql-0.8.0}/crates/polyglot-sql-python/src/expr_types.rs +0 -0
- {polyglot_sql-0.7.0 → polyglot_sql-0.8.0}/crates/polyglot-sql-python/src/format.rs +0 -0
- {polyglot_sql-0.7.0 → polyglot_sql-0.8.0}/crates/polyglot-sql-python/src/generate.rs +0 -0
- {polyglot_sql-0.7.0 → polyglot_sql-0.8.0}/crates/polyglot-sql-python/src/helpers.rs +0 -0
- {polyglot_sql-0.7.0 → polyglot_sql-0.8.0}/crates/polyglot-sql-python/src/lib.rs +0 -0
- {polyglot_sql-0.7.0 → polyglot_sql-0.8.0}/crates/polyglot-sql-python/src/lineage.rs +0 -0
- {polyglot_sql-0.7.0 → polyglot_sql-0.8.0}/crates/polyglot-sql-python/src/openlineage.rs +0 -0
- {polyglot_sql-0.7.0 → polyglot_sql-0.8.0}/crates/polyglot-sql-python/src/optimize.rs +0 -0
- {polyglot_sql-0.7.0 → polyglot_sql-0.8.0}/crates/polyglot-sql-python/src/parse.rs +0 -0
- {polyglot_sql-0.7.0 → polyglot_sql-0.8.0}/crates/polyglot-sql-python/src/query_analysis.rs +0 -0
- {polyglot_sql-0.7.0 → polyglot_sql-0.8.0}/crates/polyglot-sql-python/src/tokenize.rs +0 -0
- {polyglot_sql-0.7.0 → polyglot_sql-0.8.0}/crates/polyglot-sql-python/src/transforms.rs +0 -0
- {polyglot_sql-0.7.0 → polyglot_sql-0.8.0}/crates/polyglot-sql-python/src/transpile.rs +0 -0
- {polyglot_sql-0.7.0 → polyglot_sql-0.8.0}/crates/polyglot-sql-python/src/types.rs +0 -0
- {polyglot_sql-0.7.0 → polyglot_sql-0.8.0}/crates/polyglot-sql-python/src/validate.rs +0 -0
- {polyglot_sql-0.7.0 → polyglot_sql-0.8.0}/crates/polyglot-sql-python/tests/conftest.py +0 -0
- {polyglot_sql-0.7.0 → polyglot_sql-0.8.0}/crates/polyglot-sql-python/tests/test_compat.py +0 -0
- {polyglot_sql-0.7.0 → polyglot_sql-0.8.0}/crates/polyglot-sql-python/tests/test_dialects.py +0 -0
- {polyglot_sql-0.7.0 → polyglot_sql-0.8.0}/crates/polyglot-sql-python/tests/test_diff.py +0 -0
- {polyglot_sql-0.7.0 → polyglot_sql-0.8.0}/crates/polyglot-sql-python/tests/test_expression.py +0 -0
- {polyglot_sql-0.7.0 → polyglot_sql-0.8.0}/crates/polyglot-sql-python/tests/test_format.py +0 -0
- {polyglot_sql-0.7.0 → polyglot_sql-0.8.0}/crates/polyglot-sql-python/tests/test_generate.py +0 -0
- {polyglot_sql-0.7.0 → polyglot_sql-0.8.0}/crates/polyglot-sql-python/tests/test_optimize.py +0 -0
- {polyglot_sql-0.7.0 → polyglot_sql-0.8.0}/crates/polyglot-sql-python/tests/test_parse.py +0 -0
- {polyglot_sql-0.7.0 → polyglot_sql-0.8.0}/crates/polyglot-sql-python/tests/test_query_analysis.py +0 -0
- {polyglot_sql-0.7.0 → polyglot_sql-0.8.0}/crates/polyglot-sql-python/tests/test_transforms.py +0 -0
- {polyglot_sql-0.7.0 → polyglot_sql-0.8.0}/crates/polyglot-sql-python/tests/test_transpile.py +0 -0
- {polyglot_sql-0.7.0 → polyglot_sql-0.8.0}/crates/polyglot-sql-python/tests/test_validate.py +0 -0
- {polyglot_sql-0.7.0 → polyglot_sql-0.8.0}/crates/polyglot-sql-python/uv.lock +0 -0
- {polyglot_sql-0.7.0 → polyglot_sql-0.8.0}/pyproject.toml +0 -0
- {polyglot_sql-0.7.0 → polyglot_sql-0.8.0}/python/polyglot_sql/__init__.py +0 -0
- {polyglot_sql-0.7.0 → polyglot_sql-0.8.0}/python/polyglot_sql/__init__.pyi +0 -0
- {polyglot_sql-0.7.0 → polyglot_sql-0.8.0}/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.
|
|
608
|
+
version = "0.8.0"
|
|
609
609
|
dependencies = [
|
|
610
610
|
"criterion",
|
|
611
611
|
"once_cell",
|
|
@@ -623,7 +623,7 @@ dependencies = [
|
|
|
623
623
|
|
|
624
624
|
[[package]]
|
|
625
625
|
name = "polyglot-sql-ast-derive"
|
|
626
|
-
version = "0.
|
|
626
|
+
version = "0.8.0"
|
|
627
627
|
dependencies = [
|
|
628
628
|
"proc-macro2",
|
|
629
629
|
"quote",
|
|
@@ -632,7 +632,7 @@ dependencies = [
|
|
|
632
632
|
|
|
633
633
|
[[package]]
|
|
634
634
|
name = "polyglot-sql-ffi"
|
|
635
|
-
version = "0.
|
|
635
|
+
version = "0.8.0"
|
|
636
636
|
dependencies = [
|
|
637
637
|
"cbindgen",
|
|
638
638
|
"polyglot-sql",
|
|
@@ -642,11 +642,11 @@ dependencies = [
|
|
|
642
642
|
|
|
643
643
|
[[package]]
|
|
644
644
|
name = "polyglot-sql-function-catalogs"
|
|
645
|
-
version = "0.
|
|
645
|
+
version = "0.8.0"
|
|
646
646
|
|
|
647
647
|
[[package]]
|
|
648
648
|
name = "polyglot-sql-python"
|
|
649
|
-
version = "0.
|
|
649
|
+
version = "0.8.0"
|
|
650
650
|
dependencies = [
|
|
651
651
|
"polyglot-sql",
|
|
652
652
|
"pyo3",
|
|
@@ -657,7 +657,7 @@ dependencies = [
|
|
|
657
657
|
|
|
658
658
|
[[package]]
|
|
659
659
|
name = "polyglot-sql-wasm"
|
|
660
|
-
version = "0.
|
|
660
|
+
version = "0.8.0"
|
|
661
661
|
dependencies = [
|
|
662
662
|
"console_error_panic_hook",
|
|
663
663
|
"js-sys",
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: polyglot-sql
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.8.0
|
|
4
4
|
Classifier: Development Status :: 4 - Beta
|
|
5
5
|
Classifier: Intended Audience :: Developers
|
|
6
6
|
Classifier: License :: OSI Approved :: MIT License
|
|
@@ -177,6 +177,13 @@ Function-like projections may include `transformFunction` with the function
|
|
|
177
177
|
name, literal arguments, and column arguments, for example for
|
|
178
178
|
`DATE_TRUNC('month', created_at)`.
|
|
179
179
|
|
|
180
|
+
Each `analysis["setOperations"][...]["branches"]` entry includes a `role` of
|
|
181
|
+
`"value"` or `"filter"`. Lineage results attach optional `set_branch` metadata
|
|
182
|
+
to immediate set-operation branch roots with the `operator`, original
|
|
183
|
+
zero-based `ordinal`, and `all` flag; omitted branches do not renumber the
|
|
184
|
+
surviving nodes. In OpenLineage output, `EXCEPT` and `INTERSECT` right-hand
|
|
185
|
+
inputs are emitted as indirect `FILTER` dependencies.
|
|
186
|
+
|
|
180
187
|
Validation schema dictionaries use:
|
|
181
188
|
|
|
182
189
|
```python
|
|
@@ -150,6 +150,13 @@ Function-like projections may include `transformFunction` with the function
|
|
|
150
150
|
name, literal arguments, and column arguments, for example for
|
|
151
151
|
`DATE_TRUNC('month', created_at)`.
|
|
152
152
|
|
|
153
|
+
Each `analysis["setOperations"][...]["branches"]` entry includes a `role` of
|
|
154
|
+
`"value"` or `"filter"`. Lineage results attach optional `set_branch` metadata
|
|
155
|
+
to immediate set-operation branch roots with the `operator`, original
|
|
156
|
+
zero-based `ordinal`, and `all` flag; omitted branches do not renumber the
|
|
157
|
+
surviving nodes. In OpenLineage output, `EXCEPT` and `INTERSECT` right-hand
|
|
158
|
+
inputs are emitted as indirect `FILTER` dependencies.
|
|
159
|
+
|
|
153
160
|
Validation schema dictionaries use:
|
|
154
161
|
|
|
155
162
|
```python
|
|
@@ -100,14 +100,14 @@ function-catalog-all-dialects = [
|
|
|
100
100
|
]
|
|
101
101
|
|
|
102
102
|
[dependencies]
|
|
103
|
-
polyglot-sql-ast-derive = { path = "../polyglot-sql-ast-derive", version = "0.
|
|
103
|
+
polyglot-sql-ast-derive = { path = "../polyglot-sql-ast-derive", version = "0.8.0" }
|
|
104
104
|
serde = { workspace = true }
|
|
105
105
|
serde_json = { workspace = true }
|
|
106
106
|
thiserror = { workspace = true }
|
|
107
107
|
unicode-segmentation = { workspace = true }
|
|
108
108
|
stacker = { version = "0.1", optional = true }
|
|
109
109
|
ts-rs = { version = "12.0", features = ["serde-compat"], optional = true }
|
|
110
|
-
polyglot-sql-function-catalogs = { path = "../polyglot-sql-function-catalogs", version = "0.
|
|
110
|
+
polyglot-sql-function-catalogs = { path = "../polyglot-sql-function-catalogs", version = "0.8.0", optional = true, default-features = false }
|
|
111
111
|
|
|
112
112
|
[dev-dependencies]
|
|
113
113
|
pretty_assertions = "1.4"
|
|
@@ -25,12 +25,12 @@ By default, `polyglot-sql` enables the full public API. Parser-only consumers ca
|
|
|
25
25
|
disable default features and opt into only the dialect parsers they need:
|
|
26
26
|
|
|
27
27
|
```toml
|
|
28
|
-
polyglot-sql = { version = "0.
|
|
28
|
+
polyglot-sql = { version = "0.8.0", default-features = false }
|
|
29
29
|
```
|
|
30
30
|
|
|
31
31
|
```toml
|
|
32
32
|
polyglot-sql = {
|
|
33
|
-
version = "0.
|
|
33
|
+
version = "0.8.0",
|
|
34
34
|
default-features = false,
|
|
35
35
|
features = ["dialect-clickhouse"],
|
|
36
36
|
}
|
|
@@ -44,14 +44,14 @@ Examples:
|
|
|
44
44
|
```toml
|
|
45
45
|
# Parse and generate SQL for one dialect.
|
|
46
46
|
polyglot-sql = {
|
|
47
|
-
version = "0.
|
|
47
|
+
version = "0.8.0",
|
|
48
48
|
default-features = false,
|
|
49
49
|
features = ["generate", "dialect-clickhouse"],
|
|
50
50
|
}
|
|
51
51
|
|
|
52
52
|
# Cross-dialect transpilation.
|
|
53
53
|
polyglot-sql = {
|
|
54
|
-
version = "0.
|
|
54
|
+
version = "0.8.0",
|
|
55
55
|
default-features = false,
|
|
56
56
|
features = ["transpile", "dialect-clickhouse", "dialect-postgresql"],
|
|
57
57
|
}
|
|
@@ -332,6 +332,13 @@ and CTE output columns. `star_projections` reports the original top-level star
|
|
|
332
332
|
projection index, optional table qualifier, and schema-expanded columns when
|
|
333
333
|
known. Each projection also includes conservative `nullability`:
|
|
334
334
|
`non_null`, `nullable`, or `unknown`.
|
|
335
|
+
Each `set_operations` branch includes a semantic `role`: both `UNION` branches
|
|
336
|
+
are `value`, while the right branch of `EXCEPT` and `INTERSECT` is `filter`.
|
|
337
|
+
|
|
338
|
+
Lineage nodes at immediate set-operation branch roots expose optional
|
|
339
|
+
`set_branch` metadata with the operator, original zero-based branch ordinal,
|
|
340
|
+
and `ALL` flag. The ordinal remains stable when another branch cannot be
|
|
341
|
+
resolved.
|
|
335
342
|
|
|
336
343
|
```rust
|
|
337
344
|
use polyglot_sql::{
|
|
@@ -98,7 +98,7 @@ pub use helper::{
|
|
|
98
98
|
#[cfg(feature = "semantic")]
|
|
99
99
|
pub use lineage::{
|
|
100
100
|
lineage, lineage_at, lineage_at_with_schema, lineage_with_schema, output_columns,
|
|
101
|
-
output_columns_with_schema, LineageNode, OutputColumn, QueryOutput,
|
|
101
|
+
output_columns_with_schema, LineageNode, OutputColumn, QueryOutput, SetBranch, SetOperator,
|
|
102
102
|
};
|
|
103
103
|
#[cfg(feature = "semantic")]
|
|
104
104
|
pub use optimizer::{
|
|
@@ -109,8 +109,8 @@ pub use parser::Parser;
|
|
|
109
109
|
pub use query_analysis::{
|
|
110
110
|
analyze_query, AnalyzeQueryOptions, ColumnReferenceFact, CteFact, ProjectionFact,
|
|
111
111
|
ProjectionNullability, QueryAnalysis, QueryShape, ReferenceConfidence, RelationFact,
|
|
112
|
-
SetOperationBranchFact, SetOperationFact, StarProjectionFact,
|
|
113
|
-
TransformKind,
|
|
112
|
+
SetOperationBranchFact, SetOperationBranchRole, SetOperationFact, StarProjectionFact,
|
|
113
|
+
TransformFunctionFact, TransformKind,
|
|
114
114
|
};
|
|
115
115
|
#[cfg(feature = "semantic")]
|
|
116
116
|
pub use resolver::{is_column_ambiguous, resolve_column, Resolver, ResolverError, ResolverResult};
|
|
@@ -55,6 +55,27 @@ pub enum OutputColumn {
|
|
|
55
55
|
},
|
|
56
56
|
}
|
|
57
57
|
|
|
58
|
+
/// The set operator that owns an immediate lineage branch.
|
|
59
|
+
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
|
|
60
|
+
#[serde(rename_all = "snake_case")]
|
|
61
|
+
pub enum SetOperator {
|
|
62
|
+
Union,
|
|
63
|
+
Intersect,
|
|
64
|
+
Except,
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
/// Metadata describing a lineage node's position in an immediate set operation.
|
|
68
|
+
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
|
|
69
|
+
#[serde(rename_all = "camelCase")]
|
|
70
|
+
pub struct SetBranch {
|
|
71
|
+
/// The set operator that owns this branch.
|
|
72
|
+
pub operator: SetOperator,
|
|
73
|
+
/// The zero-based branch ordinal within the immediate binary operation.
|
|
74
|
+
pub ordinal: usize,
|
|
75
|
+
/// Whether the immediate set operation uses `ALL` semantics.
|
|
76
|
+
pub all: bool,
|
|
77
|
+
}
|
|
78
|
+
|
|
58
79
|
/// A node in the column lineage graph
|
|
59
80
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
|
60
81
|
pub struct LineageNode {
|
|
@@ -73,6 +94,9 @@ pub struct LineageNode {
|
|
|
73
94
|
/// User-written source alias when different from canonical source name.
|
|
74
95
|
#[serde(default, skip_serializing_if = "Option::is_none")]
|
|
75
96
|
pub source_alias: Option<String>,
|
|
97
|
+
/// Immediate set-operation branch metadata, when this node is a branch root.
|
|
98
|
+
#[serde(default, skip_serializing_if = "Option::is_none")]
|
|
99
|
+
pub set_branch: Option<SetBranch>,
|
|
76
100
|
/// Optional reference node name (e.g., for CTEs)
|
|
77
101
|
pub reference_node_name: String,
|
|
78
102
|
}
|
|
@@ -88,6 +112,7 @@ impl LineageNode {
|
|
|
88
112
|
source_name: String::new(),
|
|
89
113
|
source_kind: SourceKind::Unknown,
|
|
90
114
|
source_alias: None,
|
|
115
|
+
set_branch: None,
|
|
91
116
|
reference_node_name: String::new(),
|
|
92
117
|
}
|
|
93
118
|
}
|
|
@@ -1314,6 +1339,27 @@ fn query_expressions_in_scope(expr: &Expression) -> Vec<&Expression> {
|
|
|
1314
1339
|
// Set operation handling
|
|
1315
1340
|
// ---------------------------------------------------------------------------
|
|
1316
1341
|
|
|
1342
|
+
fn set_branch_metadata(expression: &Expression, ordinal: usize) -> SetBranch {
|
|
1343
|
+
match expression {
|
|
1344
|
+
Expression::Union(union) => SetBranch {
|
|
1345
|
+
operator: SetOperator::Union,
|
|
1346
|
+
ordinal,
|
|
1347
|
+
all: union.all,
|
|
1348
|
+
},
|
|
1349
|
+
Expression::Intersect(intersect) => SetBranch {
|
|
1350
|
+
operator: SetOperator::Intersect,
|
|
1351
|
+
ordinal,
|
|
1352
|
+
all: intersect.all,
|
|
1353
|
+
},
|
|
1354
|
+
Expression::Except(except) => SetBranch {
|
|
1355
|
+
operator: SetOperator::Except,
|
|
1356
|
+
ordinal,
|
|
1357
|
+
all: except.all,
|
|
1358
|
+
},
|
|
1359
|
+
_ => unreachable!("set-operation metadata requires a set operation"),
|
|
1360
|
+
}
|
|
1361
|
+
}
|
|
1362
|
+
|
|
1317
1363
|
fn handle_set_operation(
|
|
1318
1364
|
column: &ColumnRef<'_>,
|
|
1319
1365
|
context: &LineageScopeContext,
|
|
@@ -1361,7 +1407,9 @@ fn handle_set_operation(
|
|
|
1361
1407
|
|
|
1362
1408
|
// Recurse into each set-operation branch. Resolution failures are branch-local,
|
|
1363
1409
|
// but genuine parser/internal errors must not be silently discarded.
|
|
1364
|
-
for &branch_scope_id in
|
|
1410
|
+
for (branch_ordinal, &branch_scope_id) in
|
|
1411
|
+
context.indexed(scope_id).union_scopes.iter().enumerate()
|
|
1412
|
+
{
|
|
1365
1413
|
let branch_column = if trace_wildcard_by_name {
|
|
1366
1414
|
match column {
|
|
1367
1415
|
ColumnRef::Name(name) => ColumnRef::Name(name),
|
|
@@ -1383,7 +1431,10 @@ fn handle_set_operation(
|
|
|
1383
1431
|
ancestor_cte_scopes,
|
|
1384
1432
|
depth + 1,
|
|
1385
1433
|
) {
|
|
1386
|
-
Ok(child) =>
|
|
1434
|
+
Ok(mut child) => {
|
|
1435
|
+
child.set_branch = Some(set_branch_metadata(scope_expr, branch_ordinal));
|
|
1436
|
+
node.downstream.push(child);
|
|
1437
|
+
}
|
|
1387
1438
|
Err(Error::ColumnResolution { reason, .. }) => {
|
|
1388
1439
|
resolution_failure = Some(merge_resolution_reason(resolution_failure, reason));
|
|
1389
1440
|
}
|
|
@@ -4224,6 +4275,7 @@ select col_a from unioned";
|
|
|
4224
4275
|
source_name: String::new(),
|
|
4225
4276
|
source_kind: SourceKind::Unknown,
|
|
4226
4277
|
source_alias: None,
|
|
4278
|
+
set_branch: None,
|
|
4227
4279
|
reference_node_name: String::new(),
|
|
4228
4280
|
};
|
|
4229
4281
|
|
|
@@ -5488,6 +5540,31 @@ FROM t JOIN UNNEST(t.items) AS item ON TRUE
|
|
|
5488
5540
|
|
|
5489
5541
|
assert_lineage_contains(&node, "known_source.known_second");
|
|
5490
5542
|
assert_eq!(node.downstream.len(), 1);
|
|
5543
|
+
assert_eq!(
|
|
5544
|
+
node.downstream[0].set_branch,
|
|
5545
|
+
Some(SetBranch {
|
|
5546
|
+
operator: SetOperator::Union,
|
|
5547
|
+
ordinal: 1,
|
|
5548
|
+
all: true,
|
|
5549
|
+
})
|
|
5550
|
+
);
|
|
5551
|
+
|
|
5552
|
+
let expr = parse(
|
|
5553
|
+
"SELECT known_first, known_second FROM known_source EXCEPT \
|
|
5554
|
+
SELECT * FROM unknown_source",
|
|
5555
|
+
);
|
|
5556
|
+
let node = lineage_at(1, &expr, None, false).expect("left-only ordinal lineage");
|
|
5557
|
+
|
|
5558
|
+
assert_lineage_contains(&node, "known_source.known_second");
|
|
5559
|
+
assert_eq!(node.downstream.len(), 1);
|
|
5560
|
+
assert_eq!(
|
|
5561
|
+
node.downstream[0].set_branch,
|
|
5562
|
+
Some(SetBranch {
|
|
5563
|
+
operator: SetOperator::Except,
|
|
5564
|
+
ordinal: 0,
|
|
5565
|
+
all: false,
|
|
5566
|
+
})
|
|
5567
|
+
);
|
|
5491
5568
|
}
|
|
5492
5569
|
|
|
5493
5570
|
#[test]
|
|
@@ -5519,16 +5596,38 @@ FROM t JOIN UNNEST(t.items) AS item ON TRUE
|
|
|
5519
5596
|
|
|
5520
5597
|
#[test]
|
|
5521
5598
|
fn test_issue_383_lineage_at_supports_all_set_operations() {
|
|
5522
|
-
for operator in [
|
|
5599
|
+
for (sql_operator, operator, all) in [
|
|
5600
|
+
("UNION ALL", SetOperator::Union, true),
|
|
5601
|
+
("INTERSECT", SetOperator::Intersect, false),
|
|
5602
|
+
("EXCEPT", SetOperator::Except, false),
|
|
5603
|
+
] {
|
|
5523
5604
|
let expr = parse(&format!(
|
|
5524
|
-
"SELECT left_value AS left_name FROM left_source {
|
|
5605
|
+
"SELECT left_value AS left_name FROM left_source {sql_operator} \
|
|
5525
5606
|
SELECT right_value AS right_name FROM right_source"
|
|
5526
5607
|
));
|
|
5527
5608
|
let node = lineage_at(0, &expr, None, false).expect("ordinal lineage");
|
|
5528
5609
|
assert_eq!(
|
|
5529
5610
|
node.downstream.len(),
|
|
5530
5611
|
2,
|
|
5531
|
-
"expected both branches for {
|
|
5612
|
+
"expected both branches for {sql_operator}"
|
|
5613
|
+
);
|
|
5614
|
+
assert_eq!(
|
|
5615
|
+
node.downstream
|
|
5616
|
+
.iter()
|
|
5617
|
+
.map(|child| child.set_branch)
|
|
5618
|
+
.collect::<Vec<_>>(),
|
|
5619
|
+
vec![
|
|
5620
|
+
Some(SetBranch {
|
|
5621
|
+
operator,
|
|
5622
|
+
ordinal: 0,
|
|
5623
|
+
all,
|
|
5624
|
+
}),
|
|
5625
|
+
Some(SetBranch {
|
|
5626
|
+
operator,
|
|
5627
|
+
ordinal: 1,
|
|
5628
|
+
all,
|
|
5629
|
+
}),
|
|
5630
|
+
]
|
|
5532
5631
|
);
|
|
5533
5632
|
}
|
|
5534
5633
|
}
|
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
|
|
7
7
|
use crate::dialects::{Dialect, DialectType};
|
|
8
8
|
use crate::expressions::*;
|
|
9
|
-
use crate::lineage::{self, LineageNode};
|
|
9
|
+
use crate::lineage::{self, LineageNode, SetOperator};
|
|
10
10
|
use crate::schema::Schema;
|
|
11
11
|
use crate::scope::SourceKind;
|
|
12
12
|
use crate::traversal::ExpressionWalk;
|
|
@@ -180,6 +180,13 @@ struct OutputField {
|
|
|
180
180
|
struct TerminalField {
|
|
181
181
|
table: String,
|
|
182
182
|
field: String,
|
|
183
|
+
dependency: TerminalDependency,
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
|
|
187
|
+
enum TerminalDependency {
|
|
188
|
+
Direct,
|
|
189
|
+
Filter,
|
|
183
190
|
}
|
|
184
191
|
|
|
185
192
|
/// Produce a standalone OpenLineage columnLineage facet plus inferred datasets.
|
|
@@ -386,6 +393,23 @@ fn analyze_statement(
|
|
|
386
393
|
output_column_names: Vec::new(),
|
|
387
394
|
})
|
|
388
395
|
}
|
|
396
|
+
Expression::Union(_) | Expression::Intersect(_) | Expression::Except(_) => {
|
|
397
|
+
let output = options.output_dataset.clone().ok_or_else(|| {
|
|
398
|
+
Error::parse(
|
|
399
|
+
"OpenLineage outputDataset is required for set-operation queries",
|
|
400
|
+
0,
|
|
401
|
+
0,
|
|
402
|
+
0,
|
|
403
|
+
0,
|
|
404
|
+
)
|
|
405
|
+
})?;
|
|
406
|
+
Ok(StatementAnalysis {
|
|
407
|
+
query: expr.clone(),
|
|
408
|
+
inputs: collect_input_datasets(expr, options, Some(&output), warnings)?,
|
|
409
|
+
output,
|
|
410
|
+
output_column_names: Vec::new(),
|
|
411
|
+
})
|
|
412
|
+
}
|
|
389
413
|
Expression::Insert(insert) => {
|
|
390
414
|
let output = dataset_from_table_ref(&insert.table, options)?;
|
|
391
415
|
let query = insert.query.clone().ok_or_else(|| {
|
|
@@ -494,6 +518,7 @@ fn input_fields_for_output(
|
|
|
494
518
|
vec![TerminalField {
|
|
495
519
|
table: table.clone(),
|
|
496
520
|
field: output_field.lineage_name.clone(),
|
|
521
|
+
dependency: TerminalDependency::Direct,
|
|
497
522
|
}],
|
|
498
523
|
"IDENTITY",
|
|
499
524
|
Some(format!("SELECT {}", output_field.lineage_name)),
|
|
@@ -534,7 +559,7 @@ fn input_fields_for_output(
|
|
|
534
559
|
};
|
|
535
560
|
|
|
536
561
|
let mut terminals = BTreeSet::new();
|
|
537
|
-
collect_terminal_fields(&node, &mut terminals);
|
|
562
|
+
collect_terminal_fields(&node, TerminalDependency::Direct, &mut terminals);
|
|
538
563
|
let terminals: Vec<TerminalField> = terminals.into_iter().collect();
|
|
539
564
|
|
|
540
565
|
if terminals.is_empty() {
|
|
@@ -580,7 +605,8 @@ fn terminal_fields_to_openlineage(
|
|
|
580
605
|
options: &OpenLineageOptions,
|
|
581
606
|
warnings: &mut Vec<OpenLineageWarning>,
|
|
582
607
|
) -> Result<Vec<OpenLineageInputField>> {
|
|
583
|
-
let mut
|
|
608
|
+
let mut grouped =
|
|
609
|
+
BTreeMap::<(String, String, String), BTreeSet<OpenLineageTransformation>>::new();
|
|
584
610
|
for terminal in terminals {
|
|
585
611
|
let dataset = dataset_from_table_name(&terminal.table, options).map_err(|err| {
|
|
586
612
|
warnings.push(OpenLineageWarning::new(
|
|
@@ -592,19 +618,31 @@ fn terminal_fields_to_openlineage(
|
|
|
592
618
|
));
|
|
593
619
|
err
|
|
594
620
|
})?;
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
621
|
+
let (type_, transformation_subtype) = match terminal.dependency {
|
|
622
|
+
TerminalDependency::Direct => ("DIRECT", subtype),
|
|
623
|
+
TerminalDependency::Filter => ("INDIRECT", "FILTER"),
|
|
624
|
+
};
|
|
625
|
+
grouped
|
|
626
|
+
.entry((dataset.namespace, dataset.name, terminal.field))
|
|
627
|
+
.or_default()
|
|
628
|
+
.insert(OpenLineageTransformation {
|
|
629
|
+
type_: type_.to_string(),
|
|
630
|
+
subtype: transformation_subtype.to_string(),
|
|
602
631
|
description: description.clone(),
|
|
603
632
|
masking: Some(false),
|
|
604
|
-
}
|
|
605
|
-
});
|
|
633
|
+
});
|
|
606
634
|
}
|
|
607
|
-
Ok(
|
|
635
|
+
Ok(grouped
|
|
636
|
+
.into_iter()
|
|
637
|
+
.map(
|
|
638
|
+
|((namespace, name, field), transformations)| OpenLineageInputField {
|
|
639
|
+
namespace,
|
|
640
|
+
name,
|
|
641
|
+
field,
|
|
642
|
+
transformations: transformations.into_iter().collect(),
|
|
643
|
+
},
|
|
644
|
+
)
|
|
645
|
+
.collect())
|
|
608
646
|
}
|
|
609
647
|
|
|
610
648
|
fn transformation_subtype(expr: Option<&Expression>, terminals: &[TerminalField]) -> &'static str {
|
|
@@ -615,7 +653,11 @@ fn transformation_subtype(expr: Option<&Expression>, terminals: &[TerminalField]
|
|
|
615
653
|
if expression_contains_aggregate(unaliased) {
|
|
616
654
|
return "AGGREGATION";
|
|
617
655
|
}
|
|
618
|
-
|
|
656
|
+
let distinct_fields = terminals
|
|
657
|
+
.iter()
|
|
658
|
+
.map(|terminal| (&terminal.table, &terminal.field))
|
|
659
|
+
.collect::<BTreeSet<_>>();
|
|
660
|
+
if distinct_fields.len() == 1 {
|
|
619
661
|
if let Expression::Column(col) = unaliased {
|
|
620
662
|
if col.name.name == terminals[0].field {
|
|
621
663
|
return "IDENTITY";
|
|
@@ -625,7 +667,23 @@ fn transformation_subtype(expr: Option<&Expression>, terminals: &[TerminalField]
|
|
|
625
667
|
"TRANSFORMATION"
|
|
626
668
|
}
|
|
627
669
|
|
|
628
|
-
fn collect_terminal_fields(
|
|
670
|
+
fn collect_terminal_fields(
|
|
671
|
+
node: &LineageNode,
|
|
672
|
+
inherited_dependency: TerminalDependency,
|
|
673
|
+
terminals: &mut BTreeSet<TerminalField>,
|
|
674
|
+
) {
|
|
675
|
+
let dependency = if inherited_dependency == TerminalDependency::Filter
|
|
676
|
+
|| matches!(
|
|
677
|
+
node.set_branch,
|
|
678
|
+
Some(branch)
|
|
679
|
+
if branch.ordinal == 1
|
|
680
|
+
&& matches!(branch.operator, SetOperator::Intersect | SetOperator::Except)
|
|
681
|
+
) {
|
|
682
|
+
TerminalDependency::Filter
|
|
683
|
+
} else {
|
|
684
|
+
TerminalDependency::Direct
|
|
685
|
+
};
|
|
686
|
+
|
|
629
687
|
if node.downstream.is_empty() {
|
|
630
688
|
if node.source_kind == SourceKind::Virtual {
|
|
631
689
|
return;
|
|
@@ -642,6 +700,7 @@ fn collect_terminal_fields(node: &LineageNode, terminals: &mut BTreeSet<Terminal
|
|
|
642
700
|
terminals.insert(TerminalField {
|
|
643
701
|
table,
|
|
644
702
|
field: column.name.name.clone(),
|
|
703
|
+
dependency,
|
|
645
704
|
});
|
|
646
705
|
}
|
|
647
706
|
}
|
|
@@ -649,7 +708,7 @@ fn collect_terminal_fields(node: &LineageNode, terminals: &mut BTreeSet<Terminal
|
|
|
649
708
|
}
|
|
650
709
|
|
|
651
710
|
for child in &node.downstream {
|
|
652
|
-
collect_terminal_fields(child, terminals);
|
|
711
|
+
collect_terminal_fields(child, dependency, terminals);
|
|
653
712
|
}
|
|
654
713
|
}
|
|
655
714
|
|
|
@@ -1119,6 +1178,83 @@ mod tests {
|
|
|
1119
1178
|
assert_eq!(field.input_fields[0].transformations[0].subtype, "IDENTITY");
|
|
1120
1179
|
}
|
|
1121
1180
|
|
|
1181
|
+
#[test]
|
|
1182
|
+
fn emits_set_operation_value_and_filter_dependencies() {
|
|
1183
|
+
let union = openlineage_column_lineage(
|
|
1184
|
+
"SELECT a FROM left_table UNION ALL SELECT b FROM right_table",
|
|
1185
|
+
&options(),
|
|
1186
|
+
)
|
|
1187
|
+
.expect("union lineage");
|
|
1188
|
+
let union_field = union.facet.fields.get("a").expect("union field");
|
|
1189
|
+
assert_eq!(union_field.input_fields.len(), 2);
|
|
1190
|
+
assert!(union_field.input_fields.iter().all(|input| input
|
|
1191
|
+
.transformations
|
|
1192
|
+
.iter()
|
|
1193
|
+
.all(|transformation| transformation.type_ == "DIRECT")));
|
|
1194
|
+
|
|
1195
|
+
for operator in ["EXCEPT", "INTERSECT"] {
|
|
1196
|
+
let result = openlineage_column_lineage(
|
|
1197
|
+
&format!("SELECT a FROM left_table {operator} SELECT b FROM right_table"),
|
|
1198
|
+
&options(),
|
|
1199
|
+
)
|
|
1200
|
+
.unwrap_or_else(|error| panic!("{operator} lineage failed: {error}"));
|
|
1201
|
+
let field = result.facet.fields.get("a").expect("output field");
|
|
1202
|
+
let left = field
|
|
1203
|
+
.input_fields
|
|
1204
|
+
.iter()
|
|
1205
|
+
.find(|input| input.name == "left_table")
|
|
1206
|
+
.expect("left input");
|
|
1207
|
+
let right = field
|
|
1208
|
+
.input_fields
|
|
1209
|
+
.iter()
|
|
1210
|
+
.find(|input| input.name == "right_table")
|
|
1211
|
+
.expect("right input");
|
|
1212
|
+
assert!(left
|
|
1213
|
+
.transformations
|
|
1214
|
+
.iter()
|
|
1215
|
+
.all(|transformation| transformation.type_ == "DIRECT"));
|
|
1216
|
+
assert!(right.transformations.iter().any(|transformation| {
|
|
1217
|
+
transformation.type_ == "INDIRECT" && transformation.subtype == "FILTER"
|
|
1218
|
+
}));
|
|
1219
|
+
}
|
|
1220
|
+
|
|
1221
|
+
let merged = openlineage_column_lineage(
|
|
1222
|
+
"SELECT a FROM shared_table EXCEPT SELECT a FROM shared_table",
|
|
1223
|
+
&options(),
|
|
1224
|
+
)
|
|
1225
|
+
.expect("merged dependency lineage");
|
|
1226
|
+
let merged_field = merged.facet.fields.get("a").expect("merged field");
|
|
1227
|
+
assert_eq!(merged_field.input_fields.len(), 1);
|
|
1228
|
+
assert!(merged_field.input_fields[0]
|
|
1229
|
+
.transformations
|
|
1230
|
+
.iter()
|
|
1231
|
+
.any(|transformation| transformation.type_ == "DIRECT"));
|
|
1232
|
+
assert!(merged_field.input_fields[0]
|
|
1233
|
+
.transformations
|
|
1234
|
+
.iter()
|
|
1235
|
+
.any(|transformation| {
|
|
1236
|
+
transformation.type_ == "INDIRECT" && transformation.subtype == "FILTER"
|
|
1237
|
+
}));
|
|
1238
|
+
|
|
1239
|
+
let nested = openlineage_column_lineage(
|
|
1240
|
+
"SELECT a FROM left_table EXCEPT \
|
|
1241
|
+
(SELECT b FROM right_table UNION ALL SELECT c FROM third_table)",
|
|
1242
|
+
&options(),
|
|
1243
|
+
)
|
|
1244
|
+
.expect("nested set-operation lineage");
|
|
1245
|
+
let nested_field = nested.facet.fields.get("a").expect("nested field");
|
|
1246
|
+
for table in ["right_table", "third_table"] {
|
|
1247
|
+
let input = nested_field
|
|
1248
|
+
.input_fields
|
|
1249
|
+
.iter()
|
|
1250
|
+
.find(|input| input.name == table)
|
|
1251
|
+
.unwrap_or_else(|| panic!("missing nested input {table}"));
|
|
1252
|
+
assert!(input.transformations.iter().any(|transformation| {
|
|
1253
|
+
transformation.type_ == "INDIRECT" && transformation.subtype == "FILTER"
|
|
1254
|
+
}));
|
|
1255
|
+
}
|
|
1256
|
+
}
|
|
1257
|
+
|
|
1122
1258
|
#[test]
|
|
1123
1259
|
fn emits_column_lineage_for_prepared_statement_body() {
|
|
1124
1260
|
let result = openlineage_column_lineage(
|