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.
Files changed (203) hide show
  1. {polyglot_sql-0.7.0 → polyglot_sql-0.8.0}/Cargo.lock +6 -6
  2. {polyglot_sql-0.7.0 → polyglot_sql-0.8.0}/Cargo.toml +1 -1
  3. {polyglot_sql-0.7.0 → polyglot_sql-0.8.0}/PKG-INFO +8 -1
  4. {polyglot_sql-0.7.0/crates/polyglot-sql-python → polyglot_sql-0.8.0}/README.md +7 -0
  5. {polyglot_sql-0.7.0 → polyglot_sql-0.8.0}/crates/polyglot-sql/Cargo.toml +2 -2
  6. {polyglot_sql-0.7.0 → polyglot_sql-0.8.0}/crates/polyglot-sql/README.md +11 -4
  7. {polyglot_sql-0.7.0 → polyglot_sql-0.8.0}/crates/polyglot-sql/src/lib.rs +3 -3
  8. {polyglot_sql-0.7.0 → polyglot_sql-0.8.0}/crates/polyglot-sql/src/lineage.rs +104 -5
  9. {polyglot_sql-0.7.0 → polyglot_sql-0.8.0}/crates/polyglot-sql/src/openlineage.rs +152 -16
  10. {polyglot_sql-0.7.0 → polyglot_sql-0.8.0}/crates/polyglot-sql/src/query_analysis.rs +33 -3
  11. {polyglot_sql-0.7.0 → polyglot_sql-0.8.0}/crates/polyglot-sql/tests/query_analysis.rs +31 -1
  12. {polyglot_sql-0.7.0 → polyglot_sql-0.8.0/crates/polyglot-sql-python}/README.md +7 -0
  13. {polyglot_sql-0.7.0 → polyglot_sql-0.8.0}/crates/polyglot-sql-python/tests/test_lineage.py +10 -0
  14. {polyglot_sql-0.7.0 → polyglot_sql-0.8.0}/crates/polyglot-sql/benches/allocation_hotspots.rs +0 -0
  15. {polyglot_sql-0.7.0 → polyglot_sql-0.8.0}/crates/polyglot-sql/benches/ast_traversal.rs +0 -0
  16. {polyglot_sql-0.7.0 → polyglot_sql-0.8.0}/crates/polyglot-sql/benches/in_list.rs +0 -0
  17. {polyglot_sql-0.7.0 → polyglot_sql-0.8.0}/crates/polyglot-sql/benches/lineage.rs +0 -0
  18. {polyglot_sql-0.7.0 → polyglot_sql-0.8.0}/crates/polyglot-sql/benches/parsing.rs +0 -0
  19. {polyglot_sql-0.7.0 → polyglot_sql-0.8.0}/crates/polyglot-sql/benches/performance_hotspots.rs +0 -0
  20. {polyglot_sql-0.7.0 → polyglot_sql-0.8.0}/crates/polyglot-sql/benches/rust_parsing.rs +0 -0
  21. {polyglot_sql-0.7.0 → polyglot_sql-0.8.0}/crates/polyglot-sql/benches/transpile.rs +0 -0
  22. {polyglot_sql-0.7.0 → polyglot_sql-0.8.0}/crates/polyglot-sql/examples/basic_usage.rs +0 -0
  23. {polyglot_sql-0.7.0 → polyglot_sql-0.8.0}/crates/polyglot-sql/examples/bench_json.rs +0 -0
  24. {polyglot_sql-0.7.0 → polyglot_sql-0.8.0}/crates/polyglot-sql/src/ast_children.rs +0 -0
  25. {polyglot_sql-0.7.0 → polyglot_sql-0.8.0}/crates/polyglot-sql/src/ast_json.rs +0 -0
  26. {polyglot_sql-0.7.0 → polyglot_sql-0.8.0}/crates/polyglot-sql/src/ast_transforms.rs +0 -0
  27. {polyglot_sql-0.7.0 → polyglot_sql-0.8.0}/crates/polyglot-sql/src/builder.rs +0 -0
  28. {polyglot_sql-0.7.0 → polyglot_sql-0.8.0}/crates/polyglot-sql/src/dialects/athena.rs +0 -0
  29. {polyglot_sql-0.7.0 → polyglot_sql-0.8.0}/crates/polyglot-sql/src/dialects/bigquery.rs +0 -0
  30. {polyglot_sql-0.7.0 → polyglot_sql-0.8.0}/crates/polyglot-sql/src/dialects/clickhouse.rs +0 -0
  31. {polyglot_sql-0.7.0 → polyglot_sql-0.8.0}/crates/polyglot-sql/src/dialects/cockroachdb.rs +0 -0
  32. {polyglot_sql-0.7.0 → polyglot_sql-0.8.0}/crates/polyglot-sql/src/dialects/databricks.rs +0 -0
  33. {polyglot_sql-0.7.0 → polyglot_sql-0.8.0}/crates/polyglot-sql/src/dialects/datafusion.rs +0 -0
  34. {polyglot_sql-0.7.0 → polyglot_sql-0.8.0}/crates/polyglot-sql/src/dialects/doris.rs +0 -0
  35. {polyglot_sql-0.7.0 → polyglot_sql-0.8.0}/crates/polyglot-sql/src/dialects/dremio.rs +0 -0
  36. {polyglot_sql-0.7.0 → polyglot_sql-0.8.0}/crates/polyglot-sql/src/dialects/drill.rs +0 -0
  37. {polyglot_sql-0.7.0 → polyglot_sql-0.8.0}/crates/polyglot-sql/src/dialects/druid.rs +0 -0
  38. {polyglot_sql-0.7.0 → polyglot_sql-0.8.0}/crates/polyglot-sql/src/dialects/duckdb.rs +0 -0
  39. {polyglot_sql-0.7.0 → polyglot_sql-0.8.0}/crates/polyglot-sql/src/dialects/dune.rs +0 -0
  40. {polyglot_sql-0.7.0 → polyglot_sql-0.8.0}/crates/polyglot-sql/src/dialects/exasol.rs +0 -0
  41. {polyglot_sql-0.7.0 → polyglot_sql-0.8.0}/crates/polyglot-sql/src/dialects/fabric.rs +0 -0
  42. {polyglot_sql-0.7.0 → polyglot_sql-0.8.0}/crates/polyglot-sql/src/dialects/generic.rs +0 -0
  43. {polyglot_sql-0.7.0 → polyglot_sql-0.8.0}/crates/polyglot-sql/src/dialects/hive.rs +0 -0
  44. {polyglot_sql-0.7.0 → polyglot_sql-0.8.0}/crates/polyglot-sql/src/dialects/materialize.rs +0 -0
  45. {polyglot_sql-0.7.0 → polyglot_sql-0.8.0}/crates/polyglot-sql/src/dialects/mod.rs +0 -0
  46. {polyglot_sql-0.7.0 → polyglot_sql-0.8.0}/crates/polyglot-sql/src/dialects/mysql.rs +0 -0
  47. {polyglot_sql-0.7.0 → polyglot_sql-0.8.0}/crates/polyglot-sql/src/dialects/normalization/aggregates.rs +0 -0
  48. {polyglot_sql-0.7.0 → polyglot_sql-0.8.0}/crates/polyglot-sql/src/dialects/normalization/collections.rs +0 -0
  49. {polyglot_sql-0.7.0 → polyglot_sql-0.8.0}/crates/polyglot-sql/src/dialects/normalization/json.rs +0 -0
  50. {polyglot_sql-0.7.0 → polyglot_sql-0.8.0}/crates/polyglot-sql/src/dialects/normalization/mod.rs +0 -0
  51. {polyglot_sql-0.7.0 → polyglot_sql-0.8.0}/crates/polyglot-sql/src/dialects/normalization/operators.rs +0 -0
  52. {polyglot_sql-0.7.0 → polyglot_sql-0.8.0}/crates/polyglot-sql/src/dialects/normalization/postgres_interval.rs +0 -0
  53. {polyglot_sql-0.7.0 → polyglot_sql-0.8.0}/crates/polyglot-sql/src/dialects/normalization/scalar.rs +0 -0
  54. {polyglot_sql-0.7.0 → polyglot_sql-0.8.0}/crates/polyglot-sql/src/dialects/normalization/statements.rs +0 -0
  55. {polyglot_sql-0.7.0 → polyglot_sql-0.8.0}/crates/polyglot-sql/src/dialects/normalization/temporal.rs +0 -0
  56. {polyglot_sql-0.7.0 → polyglot_sql-0.8.0}/crates/polyglot-sql/src/dialects/normalization/types.rs +0 -0
  57. {polyglot_sql-0.7.0 → polyglot_sql-0.8.0}/crates/polyglot-sql/src/dialects/oracle.rs +0 -0
  58. {polyglot_sql-0.7.0 → polyglot_sql-0.8.0}/crates/polyglot-sql/src/dialects/postgres.rs +0 -0
  59. {polyglot_sql-0.7.0 → polyglot_sql-0.8.0}/crates/polyglot-sql/src/dialects/presto.rs +0 -0
  60. {polyglot_sql-0.7.0 → polyglot_sql-0.8.0}/crates/polyglot-sql/src/dialects/redshift.rs +0 -0
  61. {polyglot_sql-0.7.0 → polyglot_sql-0.8.0}/crates/polyglot-sql/src/dialects/risingwave.rs +0 -0
  62. {polyglot_sql-0.7.0 → polyglot_sql-0.8.0}/crates/polyglot-sql/src/dialects/singlestore.rs +0 -0
  63. {polyglot_sql-0.7.0 → polyglot_sql-0.8.0}/crates/polyglot-sql/src/dialects/snowflake.rs +0 -0
  64. {polyglot_sql-0.7.0 → polyglot_sql-0.8.0}/crates/polyglot-sql/src/dialects/solr.rs +0 -0
  65. {polyglot_sql-0.7.0 → polyglot_sql-0.8.0}/crates/polyglot-sql/src/dialects/spark.rs +0 -0
  66. {polyglot_sql-0.7.0 → polyglot_sql-0.8.0}/crates/polyglot-sql/src/dialects/sqlite.rs +0 -0
  67. {polyglot_sql-0.7.0 → polyglot_sql-0.8.0}/crates/polyglot-sql/src/dialects/starrocks.rs +0 -0
  68. {polyglot_sql-0.7.0 → polyglot_sql-0.8.0}/crates/polyglot-sql/src/dialects/tableau.rs +0 -0
  69. {polyglot_sql-0.7.0 → polyglot_sql-0.8.0}/crates/polyglot-sql/src/dialects/teradata.rs +0 -0
  70. {polyglot_sql-0.7.0 → polyglot_sql-0.8.0}/crates/polyglot-sql/src/dialects/tidb.rs +0 -0
  71. {polyglot_sql-0.7.0 → polyglot_sql-0.8.0}/crates/polyglot-sql/src/dialects/trino.rs +0 -0
  72. {polyglot_sql-0.7.0 → polyglot_sql-0.8.0}/crates/polyglot-sql/src/dialects/tsql.rs +0 -0
  73. {polyglot_sql-0.7.0 → polyglot_sql-0.8.0}/crates/polyglot-sql/src/diff.rs +0 -0
  74. {polyglot_sql-0.7.0 → polyglot_sql-0.8.0}/crates/polyglot-sql/src/error.rs +0 -0
  75. {polyglot_sql-0.7.0 → polyglot_sql-0.8.0}/crates/polyglot-sql/src/expressions.rs +0 -0
  76. {polyglot_sql-0.7.0 → polyglot_sql-0.8.0}/crates/polyglot-sql/src/format_tokens.rs +0 -0
  77. {polyglot_sql-0.7.0 → polyglot_sql-0.8.0}/crates/polyglot-sql/src/function_catalog.rs +0 -0
  78. {polyglot_sql-0.7.0 → polyglot_sql-0.8.0}/crates/polyglot-sql/src/function_registry.rs +0 -0
  79. {polyglot_sql-0.7.0 → polyglot_sql-0.8.0}/crates/polyglot-sql/src/generator.rs +0 -0
  80. {polyglot_sql-0.7.0 → polyglot_sql-0.8.0}/crates/polyglot-sql/src/guard.rs +0 -0
  81. {polyglot_sql-0.7.0 → polyglot_sql-0.8.0}/crates/polyglot-sql/src/helper.rs +0 -0
  82. {polyglot_sql-0.7.0 → polyglot_sql-0.8.0}/crates/polyglot-sql/src/optimizer/annotate_types.rs +0 -0
  83. {polyglot_sql-0.7.0 → polyglot_sql-0.8.0}/crates/polyglot-sql/src/optimizer/canonicalize.rs +0 -0
  84. {polyglot_sql-0.7.0 → polyglot_sql-0.8.0}/crates/polyglot-sql/src/optimizer/eliminate_ctes.rs +0 -0
  85. {polyglot_sql-0.7.0 → polyglot_sql-0.8.0}/crates/polyglot-sql/src/optimizer/eliminate_joins.rs +0 -0
  86. {polyglot_sql-0.7.0 → polyglot_sql-0.8.0}/crates/polyglot-sql/src/optimizer/isolate_table_selects.rs +0 -0
  87. {polyglot_sql-0.7.0 → polyglot_sql-0.8.0}/crates/polyglot-sql/src/optimizer/mod.rs +0 -0
  88. {polyglot_sql-0.7.0 → polyglot_sql-0.8.0}/crates/polyglot-sql/src/optimizer/normalize.rs +0 -0
  89. {polyglot_sql-0.7.0 → polyglot_sql-0.8.0}/crates/polyglot-sql/src/optimizer/normalize_identifiers.rs +0 -0
  90. {polyglot_sql-0.7.0 → polyglot_sql-0.8.0}/crates/polyglot-sql/src/optimizer/optimize_joins.rs +0 -0
  91. {polyglot_sql-0.7.0 → polyglot_sql-0.8.0}/crates/polyglot-sql/src/optimizer/optimizer.rs +0 -0
  92. {polyglot_sql-0.7.0 → polyglot_sql-0.8.0}/crates/polyglot-sql/src/optimizer/pushdown_predicates.rs +0 -0
  93. {polyglot_sql-0.7.0 → polyglot_sql-0.8.0}/crates/polyglot-sql/src/optimizer/pushdown_projections.rs +0 -0
  94. {polyglot_sql-0.7.0 → polyglot_sql-0.8.0}/crates/polyglot-sql/src/optimizer/qualify_columns.rs +0 -0
  95. {polyglot_sql-0.7.0 → polyglot_sql-0.8.0}/crates/polyglot-sql/src/optimizer/qualify_tables.rs +0 -0
  96. {polyglot_sql-0.7.0 → polyglot_sql-0.8.0}/crates/polyglot-sql/src/optimizer/simplify.rs +0 -0
  97. {polyglot_sql-0.7.0 → polyglot_sql-0.8.0}/crates/polyglot-sql/src/optimizer/subquery.rs +0 -0
  98. {polyglot_sql-0.7.0 → polyglot_sql-0.8.0}/crates/polyglot-sql/src/parser.rs +0 -0
  99. {polyglot_sql-0.7.0 → polyglot_sql-0.8.0}/crates/polyglot-sql/src/planner.rs +0 -0
  100. {polyglot_sql-0.7.0 → polyglot_sql-0.8.0}/crates/polyglot-sql/src/resolver.rs +0 -0
  101. {polyglot_sql-0.7.0 → polyglot_sql-0.8.0}/crates/polyglot-sql/src/schema.rs +0 -0
  102. {polyglot_sql-0.7.0 → polyglot_sql-0.8.0}/crates/polyglot-sql/src/scope.rs +0 -0
  103. {polyglot_sql-0.7.0 → polyglot_sql-0.8.0}/crates/polyglot-sql/src/time.rs +0 -0
  104. {polyglot_sql-0.7.0 → polyglot_sql-0.8.0}/crates/polyglot-sql/src/tokens.rs +0 -0
  105. {polyglot_sql-0.7.0 → polyglot_sql-0.8.0}/crates/polyglot-sql/src/transforms.rs +0 -0
  106. {polyglot_sql-0.7.0 → polyglot_sql-0.8.0}/crates/polyglot-sql/src/traversal.rs +0 -0
  107. {polyglot_sql-0.7.0 → polyglot_sql-0.8.0}/crates/polyglot-sql/src/trie.rs +0 -0
  108. {polyglot_sql-0.7.0 → polyglot_sql-0.8.0}/crates/polyglot-sql/src/validation/tests.rs +0 -0
  109. {polyglot_sql-0.7.0 → polyglot_sql-0.8.0}/crates/polyglot-sql/src/validation.rs +0 -0
  110. {polyglot_sql-0.7.0 → polyglot_sql-0.8.0}/crates/polyglot-sql/tests/analyze_failures.rs +0 -0
  111. {polyglot_sql-0.7.0 → polyglot_sql-0.8.0}/crates/polyglot-sql/tests/clickhouse_regression.rs +0 -0
  112. {polyglot_sql-0.7.0 → polyglot_sql-0.8.0}/crates/polyglot-sql/tests/common/known_failures.rs +0 -0
  113. {polyglot_sql-0.7.0 → polyglot_sql-0.8.0}/crates/polyglot-sql/tests/common/mod.rs +0 -0
  114. {polyglot_sql-0.7.0 → polyglot_sql-0.8.0}/crates/polyglot-sql/tests/common/test_data.rs +0 -0
  115. {polyglot_sql-0.7.0 → polyglot_sql-0.8.0}/crates/polyglot-sql/tests/common/test_runner.rs +0 -0
  116. {polyglot_sql-0.7.0 → polyglot_sql-0.8.0}/crates/polyglot-sql/tests/create_view_security_comment_regression.rs +0 -0
  117. {polyglot_sql-0.7.0 → polyglot_sql-0.8.0}/crates/polyglot-sql/tests/custom_clickhouse_coverage.rs +0 -0
  118. {polyglot_sql-0.7.0 → polyglot_sql-0.8.0}/crates/polyglot-sql/tests/custom_clickhouse_parser.rs +0 -0
  119. {polyglot_sql-0.7.0 → polyglot_sql-0.8.0}/crates/polyglot-sql/tests/custom_dialect.rs +0 -0
  120. {polyglot_sql-0.7.0 → polyglot_sql-0.8.0}/crates/polyglot-sql/tests/custom_dialect_tests.rs +0 -0
  121. {polyglot_sql-0.7.0 → polyglot_sql-0.8.0}/crates/polyglot-sql/tests/custom_fixtures/datafusion/ddl.json +0 -0
  122. {polyglot_sql-0.7.0 → polyglot_sql-0.8.0}/crates/polyglot-sql/tests/custom_fixtures/datafusion/dml.json +0 -0
  123. {polyglot_sql-0.7.0 → polyglot_sql-0.8.0}/crates/polyglot-sql/tests/custom_fixtures/datafusion/functions.json +0 -0
  124. {polyglot_sql-0.7.0 → polyglot_sql-0.8.0}/crates/polyglot-sql/tests/custom_fixtures/datafusion/identity.json +0 -0
  125. {polyglot_sql-0.7.0 → polyglot_sql-0.8.0}/crates/polyglot-sql/tests/custom_fixtures/datafusion/operators.json +0 -0
  126. {polyglot_sql-0.7.0 → polyglot_sql-0.8.0}/crates/polyglot-sql/tests/custom_fixtures/datafusion/select.json +0 -0
  127. {polyglot_sql-0.7.0 → polyglot_sql-0.8.0}/crates/polyglot-sql/tests/custom_fixtures/datafusion/transpilation.json +0 -0
  128. {polyglot_sql-0.7.0 → polyglot_sql-0.8.0}/crates/polyglot-sql/tests/custom_fixtures/datafusion/types.json +0 -0
  129. {polyglot_sql-0.7.0 → polyglot_sql-0.8.0}/crates/polyglot-sql/tests/data_type_api.rs +0 -0
  130. {polyglot_sql-0.7.0 → polyglot_sql-0.8.0}/crates/polyglot-sql/tests/deep_nesting_regression.rs +0 -0
  131. {polyglot_sql-0.7.0 → polyglot_sql-0.8.0}/crates/polyglot-sql/tests/dialect_matrix.rs +0 -0
  132. {polyglot_sql-0.7.0 → polyglot_sql-0.8.0}/crates/polyglot-sql/tests/error_handling.rs +0 -0
  133. {polyglot_sql-0.7.0 → polyglot_sql-0.8.0}/crates/polyglot-sql/tests/fabric_regression.rs +0 -0
  134. {polyglot_sql-0.7.0 → polyglot_sql-0.8.0}/crates/polyglot-sql/tests/fabric_tpch_regression.rs +0 -0
  135. {polyglot_sql-0.7.0 → polyglot_sql-0.8.0}/crates/polyglot-sql/tests/identity_roundtrip.rs +0 -0
  136. {polyglot_sql-0.7.0 → polyglot_sql-0.8.0}/crates/polyglot-sql/tests/oracle_data_types_regression.rs +0 -0
  137. {polyglot_sql-0.7.0 → polyglot_sql-0.8.0}/crates/polyglot-sql/tests/postgres_explain_options.rs +0 -0
  138. {polyglot_sql-0.7.0 → polyglot_sql-0.8.0}/crates/polyglot-sql/tests/postgres_regexp_clickhouse.rs +0 -0
  139. {polyglot_sql-0.7.0 → polyglot_sql-0.8.0}/crates/polyglot-sql/tests/postgres_sqlite_regression.rs +0 -0
  140. {polyglot_sql-0.7.0 → polyglot_sql-0.8.0}/crates/polyglot-sql/tests/query_analysis_regression.rs +0 -0
  141. {polyglot_sql-0.7.0 → polyglot_sql-0.8.0}/crates/polyglot-sql/tests/snowflake_regression_test.rs +0 -0
  142. {polyglot_sql-0.7.0 → polyglot_sql-0.8.0}/crates/polyglot-sql/tests/sqlglot_compat.rs +0 -0
  143. {polyglot_sql-0.7.0 → polyglot_sql-0.8.0}/crates/polyglot-sql/tests/sqlglot_dialect_identity.rs +0 -0
  144. {polyglot_sql-0.7.0 → polyglot_sql-0.8.0}/crates/polyglot-sql/tests/sqlglot_identity.rs +0 -0
  145. {polyglot_sql-0.7.0 → polyglot_sql-0.8.0}/crates/polyglot-sql/tests/sqlglot_identity_detailed.rs +0 -0
  146. {polyglot_sql-0.7.0 → polyglot_sql-0.8.0}/crates/polyglot-sql/tests/sqlglot_parser.rs +0 -0
  147. {polyglot_sql-0.7.0 → polyglot_sql-0.8.0}/crates/polyglot-sql/tests/sqlglot_pretty.rs +0 -0
  148. {polyglot_sql-0.7.0 → polyglot_sql-0.8.0}/crates/polyglot-sql/tests/sqlglot_transpilation.rs +0 -0
  149. {polyglot_sql-0.7.0 → polyglot_sql-0.8.0}/crates/polyglot-sql/tests/sqlglot_transpile.rs +0 -0
  150. {polyglot_sql-0.7.0 → polyglot_sql-0.8.0}/crates/polyglot-sql/tests/tpch_transpile_stack.rs +0 -0
  151. {polyglot_sql-0.7.0 → polyglot_sql-0.8.0}/crates/polyglot-sql/tests/transform_regression.rs +0 -0
  152. {polyglot_sql-0.7.0 → polyglot_sql-0.8.0}/crates/polyglot-sql/tests/tsql_regression.rs +0 -0
  153. {polyglot_sql-0.7.0 → polyglot_sql-0.8.0}/crates/polyglot-sql-ast-derive/Cargo.toml +0 -0
  154. {polyglot_sql-0.7.0 → polyglot_sql-0.8.0}/crates/polyglot-sql-ast-derive/src/lib.rs +0 -0
  155. {polyglot_sql-0.7.0 → polyglot_sql-0.8.0}/crates/polyglot-sql-function-catalogs/Cargo.toml +0 -0
  156. {polyglot_sql-0.7.0 → polyglot_sql-0.8.0}/crates/polyglot-sql-function-catalogs/README.md +0 -0
  157. {polyglot_sql-0.7.0 → polyglot_sql-0.8.0}/crates/polyglot-sql-function-catalogs/src/clickhouse.rs +0 -0
  158. {polyglot_sql-0.7.0 → polyglot_sql-0.8.0}/crates/polyglot-sql-function-catalogs/src/duckdb.rs +0 -0
  159. {polyglot_sql-0.7.0 → polyglot_sql-0.8.0}/crates/polyglot-sql-function-catalogs/src/lib.rs +0 -0
  160. {polyglot_sql-0.7.0 → polyglot_sql-0.8.0}/crates/polyglot-sql-function-catalogs/tools/clickhouse/extract_functions.py +0 -0
  161. {polyglot_sql-0.7.0 → polyglot_sql-0.8.0}/crates/polyglot-sql-function-catalogs/tools/duckdb/extract_functions.py +0 -0
  162. {polyglot_sql-0.7.0 → polyglot_sql-0.8.0}/crates/polyglot-sql-python/Cargo.toml +0 -0
  163. {polyglot_sql-0.7.0 → polyglot_sql-0.8.0}/crates/polyglot-sql-python/docs/api.md +0 -0
  164. {polyglot_sql-0.7.0 → polyglot_sql-0.8.0}/crates/polyglot-sql-python/docs/index.md +0 -0
  165. {polyglot_sql-0.7.0 → polyglot_sql-0.8.0}/crates/polyglot-sql-python/mkdocs.yml +0 -0
  166. {polyglot_sql-0.7.0 → polyglot_sql-0.8.0}/crates/polyglot-sql-python/src/annotate_types.rs +0 -0
  167. {polyglot_sql-0.7.0 → polyglot_sql-0.8.0}/crates/polyglot-sql-python/src/dialects.rs +0 -0
  168. {polyglot_sql-0.7.0 → polyglot_sql-0.8.0}/crates/polyglot-sql-python/src/diff.rs +0 -0
  169. {polyglot_sql-0.7.0 → polyglot_sql-0.8.0}/crates/polyglot-sql-python/src/errors.rs +0 -0
  170. {polyglot_sql-0.7.0 → polyglot_sql-0.8.0}/crates/polyglot-sql-python/src/expr.rs +0 -0
  171. {polyglot_sql-0.7.0 → polyglot_sql-0.8.0}/crates/polyglot-sql-python/src/expr_types.rs +0 -0
  172. {polyglot_sql-0.7.0 → polyglot_sql-0.8.0}/crates/polyglot-sql-python/src/format.rs +0 -0
  173. {polyglot_sql-0.7.0 → polyglot_sql-0.8.0}/crates/polyglot-sql-python/src/generate.rs +0 -0
  174. {polyglot_sql-0.7.0 → polyglot_sql-0.8.0}/crates/polyglot-sql-python/src/helpers.rs +0 -0
  175. {polyglot_sql-0.7.0 → polyglot_sql-0.8.0}/crates/polyglot-sql-python/src/lib.rs +0 -0
  176. {polyglot_sql-0.7.0 → polyglot_sql-0.8.0}/crates/polyglot-sql-python/src/lineage.rs +0 -0
  177. {polyglot_sql-0.7.0 → polyglot_sql-0.8.0}/crates/polyglot-sql-python/src/openlineage.rs +0 -0
  178. {polyglot_sql-0.7.0 → polyglot_sql-0.8.0}/crates/polyglot-sql-python/src/optimize.rs +0 -0
  179. {polyglot_sql-0.7.0 → polyglot_sql-0.8.0}/crates/polyglot-sql-python/src/parse.rs +0 -0
  180. {polyglot_sql-0.7.0 → polyglot_sql-0.8.0}/crates/polyglot-sql-python/src/query_analysis.rs +0 -0
  181. {polyglot_sql-0.7.0 → polyglot_sql-0.8.0}/crates/polyglot-sql-python/src/tokenize.rs +0 -0
  182. {polyglot_sql-0.7.0 → polyglot_sql-0.8.0}/crates/polyglot-sql-python/src/transforms.rs +0 -0
  183. {polyglot_sql-0.7.0 → polyglot_sql-0.8.0}/crates/polyglot-sql-python/src/transpile.rs +0 -0
  184. {polyglot_sql-0.7.0 → polyglot_sql-0.8.0}/crates/polyglot-sql-python/src/types.rs +0 -0
  185. {polyglot_sql-0.7.0 → polyglot_sql-0.8.0}/crates/polyglot-sql-python/src/validate.rs +0 -0
  186. {polyglot_sql-0.7.0 → polyglot_sql-0.8.0}/crates/polyglot-sql-python/tests/conftest.py +0 -0
  187. {polyglot_sql-0.7.0 → polyglot_sql-0.8.0}/crates/polyglot-sql-python/tests/test_compat.py +0 -0
  188. {polyglot_sql-0.7.0 → polyglot_sql-0.8.0}/crates/polyglot-sql-python/tests/test_dialects.py +0 -0
  189. {polyglot_sql-0.7.0 → polyglot_sql-0.8.0}/crates/polyglot-sql-python/tests/test_diff.py +0 -0
  190. {polyglot_sql-0.7.0 → polyglot_sql-0.8.0}/crates/polyglot-sql-python/tests/test_expression.py +0 -0
  191. {polyglot_sql-0.7.0 → polyglot_sql-0.8.0}/crates/polyglot-sql-python/tests/test_format.py +0 -0
  192. {polyglot_sql-0.7.0 → polyglot_sql-0.8.0}/crates/polyglot-sql-python/tests/test_generate.py +0 -0
  193. {polyglot_sql-0.7.0 → polyglot_sql-0.8.0}/crates/polyglot-sql-python/tests/test_optimize.py +0 -0
  194. {polyglot_sql-0.7.0 → polyglot_sql-0.8.0}/crates/polyglot-sql-python/tests/test_parse.py +0 -0
  195. {polyglot_sql-0.7.0 → polyglot_sql-0.8.0}/crates/polyglot-sql-python/tests/test_query_analysis.py +0 -0
  196. {polyglot_sql-0.7.0 → polyglot_sql-0.8.0}/crates/polyglot-sql-python/tests/test_transforms.py +0 -0
  197. {polyglot_sql-0.7.0 → polyglot_sql-0.8.0}/crates/polyglot-sql-python/tests/test_transpile.py +0 -0
  198. {polyglot_sql-0.7.0 → polyglot_sql-0.8.0}/crates/polyglot-sql-python/tests/test_validate.py +0 -0
  199. {polyglot_sql-0.7.0 → polyglot_sql-0.8.0}/crates/polyglot-sql-python/uv.lock +0 -0
  200. {polyglot_sql-0.7.0 → polyglot_sql-0.8.0}/pyproject.toml +0 -0
  201. {polyglot_sql-0.7.0 → polyglot_sql-0.8.0}/python/polyglot_sql/__init__.py +0 -0
  202. {polyglot_sql-0.7.0 → polyglot_sql-0.8.0}/python/polyglot_sql/__init__.pyi +0 -0
  203. {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.7.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.7.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.7.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.7.0"
645
+ version = "0.8.0"
646
646
 
647
647
  [[package]]
648
648
  name = "polyglot-sql-python"
649
- version = "0.7.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.7.0"
660
+ version = "0.8.0"
661
661
  dependencies = [
662
662
  "console_error_panic_hook",
663
663
  "js-sys",
@@ -6,7 +6,7 @@ exclude = [
6
6
  ]
7
7
 
8
8
  [workspace.package]
9
- version = "0.7.0"
9
+ version = "0.8.0"
10
10
  edition = "2021"
11
11
  license = "MIT"
12
12
  authors = ["polyglot contributors"]
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: polyglot-sql
3
- Version: 0.7.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.7.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.7.0", optional = true, default-features = false }
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.7.0", default-features = false }
28
+ polyglot-sql = { version = "0.8.0", default-features = false }
29
29
  ```
30
30
 
31
31
  ```toml
32
32
  polyglot-sql = {
33
- version = "0.5",
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.5",
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.5",
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, TransformFunctionFact,
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 &context.indexed(scope_id).union_scopes {
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) => node.downstream.push(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 ["UNION ALL", "INTERSECT", "EXCEPT"] {
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 {operator} \
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 {operator}"
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 result = Vec::new();
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
- result.push(OpenLineageInputField {
596
- namespace: dataset.namespace,
597
- name: dataset.name,
598
- field: terminal.field,
599
- transformations: vec![OpenLineageTransformation {
600
- type_: "DIRECT".to_string(),
601
- subtype: subtype.to_string(),
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(result)
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
- if terminals.len() == 1 {
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(node: &LineageNode, terminals: &mut BTreeSet<TerminalField>) {
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(