polyglot-sql 0.5.2__tar.gz → 0.5.4__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (184) hide show
  1. {polyglot_sql-0.5.2 → polyglot_sql-0.5.4}/Cargo.lock +5 -5
  2. {polyglot_sql-0.5.2 → polyglot_sql-0.5.4}/Cargo.toml +1 -1
  3. {polyglot_sql-0.5.2 → polyglot_sql-0.5.4}/PKG-INFO +60 -7
  4. {polyglot_sql-0.5.2/crates/polyglot-sql-python → polyglot_sql-0.5.4}/README.md +59 -6
  5. {polyglot_sql-0.5.2 → polyglot_sql-0.5.4}/crates/polyglot-sql/Cargo.toml +1 -1
  6. {polyglot_sql-0.5.2 → polyglot_sql-0.5.4}/crates/polyglot-sql/README.md +50 -6
  7. polyglot_sql-0.5.4/crates/polyglot-sql/src/ast_json.rs +278 -0
  8. {polyglot_sql-0.5.2 → polyglot_sql-0.5.4}/crates/polyglot-sql/src/ast_transforms.rs +119 -20
  9. {polyglot_sql-0.5.2 → polyglot_sql-0.5.4}/crates/polyglot-sql/src/builder.rs +27 -0
  10. {polyglot_sql-0.5.2 → polyglot_sql-0.5.4}/crates/polyglot-sql/src/generator.rs +19 -0
  11. {polyglot_sql-0.5.2 → polyglot_sql-0.5.4}/crates/polyglot-sql/src/lib.rs +6 -3
  12. {polyglot_sql-0.5.2 → polyglot_sql-0.5.4}/crates/polyglot-sql/src/parser.rs +28 -0
  13. {polyglot_sql-0.5.2 → polyglot_sql-0.5.4}/crates/polyglot-sql/src/query_analysis.rs +679 -33
  14. {polyglot_sql-0.5.2 → polyglot_sql-0.5.4}/crates/polyglot-sql/tests/query_analysis.rs +278 -8
  15. {polyglot_sql-0.5.2 → polyglot_sql-0.5.4}/crates/polyglot-sql/tests/tsql_regression.rs +47 -1
  16. {polyglot_sql-0.5.2 → polyglot_sql-0.5.4/crates/polyglot-sql-python}/README.md +59 -6
  17. {polyglot_sql-0.5.2 → polyglot_sql-0.5.4}/crates/polyglot-sql-python/docs/index.md +44 -4
  18. {polyglot_sql-0.5.2 → polyglot_sql-0.5.4}/crates/polyglot-sql-python/src/expr.rs +4 -3
  19. {polyglot_sql-0.5.2 → polyglot_sql-0.5.4}/crates/polyglot-sql-python/src/helpers.rs +10 -3
  20. {polyglot_sql-0.5.2 → polyglot_sql-0.5.4}/crates/polyglot-sql-python/src/lib.rs +3 -0
  21. {polyglot_sql-0.5.2 → polyglot_sql-0.5.4}/crates/polyglot-sql-python/src/transforms.rs +50 -0
  22. {polyglot_sql-0.5.2 → polyglot_sql-0.5.4}/crates/polyglot-sql-python/tests/test_generate.py +27 -0
  23. polyglot_sql-0.5.4/crates/polyglot-sql-python/tests/test_query_analysis.py +132 -0
  24. {polyglot_sql-0.5.2 → polyglot_sql-0.5.4}/crates/polyglot-sql-python/tests/test_transforms.py +13 -0
  25. {polyglot_sql-0.5.2 → polyglot_sql-0.5.4}/python/polyglot_sql/__init__.py +6 -0
  26. {polyglot_sql-0.5.2 → polyglot_sql-0.5.4}/python/polyglot_sql/__init__.pyi +42 -1
  27. polyglot_sql-0.5.2/crates/polyglot-sql-python/tests/test_query_analysis.py +0 -71
  28. {polyglot_sql-0.5.2 → polyglot_sql-0.5.4}/crates/polyglot-sql/benches/in_list.rs +0 -0
  29. {polyglot_sql-0.5.2 → polyglot_sql-0.5.4}/crates/polyglot-sql/benches/parsing.rs +0 -0
  30. {polyglot_sql-0.5.2 → polyglot_sql-0.5.4}/crates/polyglot-sql/benches/rust_parsing.rs +0 -0
  31. {polyglot_sql-0.5.2 → polyglot_sql-0.5.4}/crates/polyglot-sql/benches/transpile.rs +0 -0
  32. {polyglot_sql-0.5.2 → polyglot_sql-0.5.4}/crates/polyglot-sql/examples/basic_usage.rs +0 -0
  33. {polyglot_sql-0.5.2 → polyglot_sql-0.5.4}/crates/polyglot-sql/examples/bench_json.rs +0 -0
  34. {polyglot_sql-0.5.2 → polyglot_sql-0.5.4}/crates/polyglot-sql/src/dialects/athena.rs +0 -0
  35. {polyglot_sql-0.5.2 → polyglot_sql-0.5.4}/crates/polyglot-sql/src/dialects/bigquery.rs +0 -0
  36. {polyglot_sql-0.5.2 → polyglot_sql-0.5.4}/crates/polyglot-sql/src/dialects/clickhouse.rs +0 -0
  37. {polyglot_sql-0.5.2 → polyglot_sql-0.5.4}/crates/polyglot-sql/src/dialects/cockroachdb.rs +0 -0
  38. {polyglot_sql-0.5.2 → polyglot_sql-0.5.4}/crates/polyglot-sql/src/dialects/databricks.rs +0 -0
  39. {polyglot_sql-0.5.2 → polyglot_sql-0.5.4}/crates/polyglot-sql/src/dialects/datafusion.rs +0 -0
  40. {polyglot_sql-0.5.2 → polyglot_sql-0.5.4}/crates/polyglot-sql/src/dialects/doris.rs +0 -0
  41. {polyglot_sql-0.5.2 → polyglot_sql-0.5.4}/crates/polyglot-sql/src/dialects/dremio.rs +0 -0
  42. {polyglot_sql-0.5.2 → polyglot_sql-0.5.4}/crates/polyglot-sql/src/dialects/drill.rs +0 -0
  43. {polyglot_sql-0.5.2 → polyglot_sql-0.5.4}/crates/polyglot-sql/src/dialects/druid.rs +0 -0
  44. {polyglot_sql-0.5.2 → polyglot_sql-0.5.4}/crates/polyglot-sql/src/dialects/duckdb.rs +0 -0
  45. {polyglot_sql-0.5.2 → polyglot_sql-0.5.4}/crates/polyglot-sql/src/dialects/dune.rs +0 -0
  46. {polyglot_sql-0.5.2 → polyglot_sql-0.5.4}/crates/polyglot-sql/src/dialects/exasol.rs +0 -0
  47. {polyglot_sql-0.5.2 → polyglot_sql-0.5.4}/crates/polyglot-sql/src/dialects/fabric.rs +0 -0
  48. {polyglot_sql-0.5.2 → polyglot_sql-0.5.4}/crates/polyglot-sql/src/dialects/generic.rs +0 -0
  49. {polyglot_sql-0.5.2 → polyglot_sql-0.5.4}/crates/polyglot-sql/src/dialects/hive.rs +0 -0
  50. {polyglot_sql-0.5.2 → polyglot_sql-0.5.4}/crates/polyglot-sql/src/dialects/materialize.rs +0 -0
  51. {polyglot_sql-0.5.2 → polyglot_sql-0.5.4}/crates/polyglot-sql/src/dialects/mod.rs +0 -0
  52. {polyglot_sql-0.5.2 → polyglot_sql-0.5.4}/crates/polyglot-sql/src/dialects/mysql.rs +0 -0
  53. {polyglot_sql-0.5.2 → polyglot_sql-0.5.4}/crates/polyglot-sql/src/dialects/oracle.rs +0 -0
  54. {polyglot_sql-0.5.2 → polyglot_sql-0.5.4}/crates/polyglot-sql/src/dialects/postgres.rs +0 -0
  55. {polyglot_sql-0.5.2 → polyglot_sql-0.5.4}/crates/polyglot-sql/src/dialects/presto.rs +0 -0
  56. {polyglot_sql-0.5.2 → polyglot_sql-0.5.4}/crates/polyglot-sql/src/dialects/redshift.rs +0 -0
  57. {polyglot_sql-0.5.2 → polyglot_sql-0.5.4}/crates/polyglot-sql/src/dialects/risingwave.rs +0 -0
  58. {polyglot_sql-0.5.2 → polyglot_sql-0.5.4}/crates/polyglot-sql/src/dialects/singlestore.rs +0 -0
  59. {polyglot_sql-0.5.2 → polyglot_sql-0.5.4}/crates/polyglot-sql/src/dialects/snowflake.rs +0 -0
  60. {polyglot_sql-0.5.2 → polyglot_sql-0.5.4}/crates/polyglot-sql/src/dialects/solr.rs +0 -0
  61. {polyglot_sql-0.5.2 → polyglot_sql-0.5.4}/crates/polyglot-sql/src/dialects/spark.rs +0 -0
  62. {polyglot_sql-0.5.2 → polyglot_sql-0.5.4}/crates/polyglot-sql/src/dialects/sqlite.rs +0 -0
  63. {polyglot_sql-0.5.2 → polyglot_sql-0.5.4}/crates/polyglot-sql/src/dialects/starrocks.rs +0 -0
  64. {polyglot_sql-0.5.2 → polyglot_sql-0.5.4}/crates/polyglot-sql/src/dialects/tableau.rs +0 -0
  65. {polyglot_sql-0.5.2 → polyglot_sql-0.5.4}/crates/polyglot-sql/src/dialects/teradata.rs +0 -0
  66. {polyglot_sql-0.5.2 → polyglot_sql-0.5.4}/crates/polyglot-sql/src/dialects/tidb.rs +0 -0
  67. {polyglot_sql-0.5.2 → polyglot_sql-0.5.4}/crates/polyglot-sql/src/dialects/trino.rs +0 -0
  68. {polyglot_sql-0.5.2 → polyglot_sql-0.5.4}/crates/polyglot-sql/src/dialects/tsql.rs +0 -0
  69. {polyglot_sql-0.5.2 → polyglot_sql-0.5.4}/crates/polyglot-sql/src/diff.rs +0 -0
  70. {polyglot_sql-0.5.2 → polyglot_sql-0.5.4}/crates/polyglot-sql/src/error.rs +0 -0
  71. {polyglot_sql-0.5.2 → polyglot_sql-0.5.4}/crates/polyglot-sql/src/expressions.rs +0 -0
  72. {polyglot_sql-0.5.2 → polyglot_sql-0.5.4}/crates/polyglot-sql/src/function_catalog.rs +0 -0
  73. {polyglot_sql-0.5.2 → polyglot_sql-0.5.4}/crates/polyglot-sql/src/function_registry.rs +0 -0
  74. {polyglot_sql-0.5.2 → polyglot_sql-0.5.4}/crates/polyglot-sql/src/helper.rs +0 -0
  75. {polyglot_sql-0.5.2 → polyglot_sql-0.5.4}/crates/polyglot-sql/src/lineage.rs +0 -0
  76. {polyglot_sql-0.5.2 → polyglot_sql-0.5.4}/crates/polyglot-sql/src/openlineage.rs +0 -0
  77. {polyglot_sql-0.5.2 → polyglot_sql-0.5.4}/crates/polyglot-sql/src/optimizer/annotate_types.rs +0 -0
  78. {polyglot_sql-0.5.2 → polyglot_sql-0.5.4}/crates/polyglot-sql/src/optimizer/canonicalize.rs +0 -0
  79. {polyglot_sql-0.5.2 → polyglot_sql-0.5.4}/crates/polyglot-sql/src/optimizer/eliminate_ctes.rs +0 -0
  80. {polyglot_sql-0.5.2 → polyglot_sql-0.5.4}/crates/polyglot-sql/src/optimizer/eliminate_joins.rs +0 -0
  81. {polyglot_sql-0.5.2 → polyglot_sql-0.5.4}/crates/polyglot-sql/src/optimizer/isolate_table_selects.rs +0 -0
  82. {polyglot_sql-0.5.2 → polyglot_sql-0.5.4}/crates/polyglot-sql/src/optimizer/mod.rs +0 -0
  83. {polyglot_sql-0.5.2 → polyglot_sql-0.5.4}/crates/polyglot-sql/src/optimizer/normalize.rs +0 -0
  84. {polyglot_sql-0.5.2 → polyglot_sql-0.5.4}/crates/polyglot-sql/src/optimizer/normalize_identifiers.rs +0 -0
  85. {polyglot_sql-0.5.2 → polyglot_sql-0.5.4}/crates/polyglot-sql/src/optimizer/optimize_joins.rs +0 -0
  86. {polyglot_sql-0.5.2 → polyglot_sql-0.5.4}/crates/polyglot-sql/src/optimizer/optimizer.rs +0 -0
  87. {polyglot_sql-0.5.2 → polyglot_sql-0.5.4}/crates/polyglot-sql/src/optimizer/pushdown_predicates.rs +0 -0
  88. {polyglot_sql-0.5.2 → polyglot_sql-0.5.4}/crates/polyglot-sql/src/optimizer/pushdown_projections.rs +0 -0
  89. {polyglot_sql-0.5.2 → polyglot_sql-0.5.4}/crates/polyglot-sql/src/optimizer/qualify_columns.rs +0 -0
  90. {polyglot_sql-0.5.2 → polyglot_sql-0.5.4}/crates/polyglot-sql/src/optimizer/qualify_tables.rs +0 -0
  91. {polyglot_sql-0.5.2 → polyglot_sql-0.5.4}/crates/polyglot-sql/src/optimizer/simplify.rs +0 -0
  92. {polyglot_sql-0.5.2 → polyglot_sql-0.5.4}/crates/polyglot-sql/src/optimizer/subquery.rs +0 -0
  93. {polyglot_sql-0.5.2 → polyglot_sql-0.5.4}/crates/polyglot-sql/src/planner.rs +0 -0
  94. {polyglot_sql-0.5.2 → polyglot_sql-0.5.4}/crates/polyglot-sql/src/resolver.rs +0 -0
  95. {polyglot_sql-0.5.2 → polyglot_sql-0.5.4}/crates/polyglot-sql/src/schema.rs +0 -0
  96. {polyglot_sql-0.5.2 → polyglot_sql-0.5.4}/crates/polyglot-sql/src/scope.rs +0 -0
  97. {polyglot_sql-0.5.2 → polyglot_sql-0.5.4}/crates/polyglot-sql/src/time.rs +0 -0
  98. {polyglot_sql-0.5.2 → polyglot_sql-0.5.4}/crates/polyglot-sql/src/tokens.rs +0 -0
  99. {polyglot_sql-0.5.2 → polyglot_sql-0.5.4}/crates/polyglot-sql/src/transforms.rs +0 -0
  100. {polyglot_sql-0.5.2 → polyglot_sql-0.5.4}/crates/polyglot-sql/src/traversal.rs +0 -0
  101. {polyglot_sql-0.5.2 → polyglot_sql-0.5.4}/crates/polyglot-sql/src/trie.rs +0 -0
  102. {polyglot_sql-0.5.2 → polyglot_sql-0.5.4}/crates/polyglot-sql/src/validation/tests.rs +0 -0
  103. {polyglot_sql-0.5.2 → polyglot_sql-0.5.4}/crates/polyglot-sql/src/validation.rs +0 -0
  104. {polyglot_sql-0.5.2 → polyglot_sql-0.5.4}/crates/polyglot-sql/tests/analyze_failures.rs +0 -0
  105. {polyglot_sql-0.5.2 → polyglot_sql-0.5.4}/crates/polyglot-sql/tests/clickhouse_regression.rs +0 -0
  106. {polyglot_sql-0.5.2 → polyglot_sql-0.5.4}/crates/polyglot-sql/tests/common/known_failures.rs +0 -0
  107. {polyglot_sql-0.5.2 → polyglot_sql-0.5.4}/crates/polyglot-sql/tests/common/mod.rs +0 -0
  108. {polyglot_sql-0.5.2 → polyglot_sql-0.5.4}/crates/polyglot-sql/tests/common/test_data.rs +0 -0
  109. {polyglot_sql-0.5.2 → polyglot_sql-0.5.4}/crates/polyglot-sql/tests/common/test_runner.rs +0 -0
  110. {polyglot_sql-0.5.2 → polyglot_sql-0.5.4}/crates/polyglot-sql/tests/custom_clickhouse_coverage.rs +0 -0
  111. {polyglot_sql-0.5.2 → polyglot_sql-0.5.4}/crates/polyglot-sql/tests/custom_clickhouse_parser.rs +0 -0
  112. {polyglot_sql-0.5.2 → polyglot_sql-0.5.4}/crates/polyglot-sql/tests/custom_dialect.rs +0 -0
  113. {polyglot_sql-0.5.2 → polyglot_sql-0.5.4}/crates/polyglot-sql/tests/custom_dialect_tests.rs +0 -0
  114. {polyglot_sql-0.5.2 → polyglot_sql-0.5.4}/crates/polyglot-sql/tests/custom_fixtures/datafusion/ddl.json +0 -0
  115. {polyglot_sql-0.5.2 → polyglot_sql-0.5.4}/crates/polyglot-sql/tests/custom_fixtures/datafusion/dml.json +0 -0
  116. {polyglot_sql-0.5.2 → polyglot_sql-0.5.4}/crates/polyglot-sql/tests/custom_fixtures/datafusion/functions.json +0 -0
  117. {polyglot_sql-0.5.2 → polyglot_sql-0.5.4}/crates/polyglot-sql/tests/custom_fixtures/datafusion/identity.json +0 -0
  118. {polyglot_sql-0.5.2 → polyglot_sql-0.5.4}/crates/polyglot-sql/tests/custom_fixtures/datafusion/operators.json +0 -0
  119. {polyglot_sql-0.5.2 → polyglot_sql-0.5.4}/crates/polyglot-sql/tests/custom_fixtures/datafusion/select.json +0 -0
  120. {polyglot_sql-0.5.2 → polyglot_sql-0.5.4}/crates/polyglot-sql/tests/custom_fixtures/datafusion/transpilation.json +0 -0
  121. {polyglot_sql-0.5.2 → polyglot_sql-0.5.4}/crates/polyglot-sql/tests/custom_fixtures/datafusion/types.json +0 -0
  122. {polyglot_sql-0.5.2 → polyglot_sql-0.5.4}/crates/polyglot-sql/tests/data_type_api.rs +0 -0
  123. {polyglot_sql-0.5.2 → polyglot_sql-0.5.4}/crates/polyglot-sql/tests/deep_nesting_regression.rs +0 -0
  124. {polyglot_sql-0.5.2 → polyglot_sql-0.5.4}/crates/polyglot-sql/tests/dialect_matrix.rs +0 -0
  125. {polyglot_sql-0.5.2 → polyglot_sql-0.5.4}/crates/polyglot-sql/tests/error_handling.rs +0 -0
  126. {polyglot_sql-0.5.2 → polyglot_sql-0.5.4}/crates/polyglot-sql/tests/fabric_regression.rs +0 -0
  127. {polyglot_sql-0.5.2 → polyglot_sql-0.5.4}/crates/polyglot-sql/tests/fabric_tpch_regression.rs +0 -0
  128. {polyglot_sql-0.5.2 → polyglot_sql-0.5.4}/crates/polyglot-sql/tests/identity_roundtrip.rs +0 -0
  129. {polyglot_sql-0.5.2 → polyglot_sql-0.5.4}/crates/polyglot-sql/tests/issue201_regression_test.rs +0 -0
  130. {polyglot_sql-0.5.2 → polyglot_sql-0.5.4}/crates/polyglot-sql/tests/issue210_regression_test.rs +0 -0
  131. {polyglot_sql-0.5.2 → polyglot_sql-0.5.4}/crates/polyglot-sql/tests/issue226_regression_test.rs +0 -0
  132. {polyglot_sql-0.5.2 → polyglot_sql-0.5.4}/crates/polyglot-sql/tests/issue227_strict_unsupported.rs +0 -0
  133. {polyglot_sql-0.5.2 → polyglot_sql-0.5.4}/crates/polyglot-sql/tests/postgres_sqlite_regression.rs +0 -0
  134. {polyglot_sql-0.5.2 → polyglot_sql-0.5.4}/crates/polyglot-sql/tests/snowflake_regression_test.rs +0 -0
  135. {polyglot_sql-0.5.2 → polyglot_sql-0.5.4}/crates/polyglot-sql/tests/sqlglot_compat.rs +0 -0
  136. {polyglot_sql-0.5.2 → polyglot_sql-0.5.4}/crates/polyglot-sql/tests/sqlglot_dialect_identity.rs +0 -0
  137. {polyglot_sql-0.5.2 → polyglot_sql-0.5.4}/crates/polyglot-sql/tests/sqlglot_identity.rs +0 -0
  138. {polyglot_sql-0.5.2 → polyglot_sql-0.5.4}/crates/polyglot-sql/tests/sqlglot_identity_detailed.rs +0 -0
  139. {polyglot_sql-0.5.2 → polyglot_sql-0.5.4}/crates/polyglot-sql/tests/sqlglot_parser.rs +0 -0
  140. {polyglot_sql-0.5.2 → polyglot_sql-0.5.4}/crates/polyglot-sql/tests/sqlglot_pretty.rs +0 -0
  141. {polyglot_sql-0.5.2 → polyglot_sql-0.5.4}/crates/polyglot-sql/tests/sqlglot_transpilation.rs +0 -0
  142. {polyglot_sql-0.5.2 → polyglot_sql-0.5.4}/crates/polyglot-sql/tests/sqlglot_transpile.rs +0 -0
  143. {polyglot_sql-0.5.2 → polyglot_sql-0.5.4}/crates/polyglot-sql/tests/tpch_transpile_stack.rs +0 -0
  144. {polyglot_sql-0.5.2 → polyglot_sql-0.5.4}/crates/polyglot-sql/tests/transform_regression.rs +0 -0
  145. {polyglot_sql-0.5.2 → polyglot_sql-0.5.4}/crates/polyglot-sql-function-catalogs/Cargo.toml +0 -0
  146. {polyglot_sql-0.5.2 → polyglot_sql-0.5.4}/crates/polyglot-sql-function-catalogs/README.md +0 -0
  147. {polyglot_sql-0.5.2 → polyglot_sql-0.5.4}/crates/polyglot-sql-function-catalogs/src/clickhouse.rs +0 -0
  148. {polyglot_sql-0.5.2 → polyglot_sql-0.5.4}/crates/polyglot-sql-function-catalogs/src/duckdb.rs +0 -0
  149. {polyglot_sql-0.5.2 → polyglot_sql-0.5.4}/crates/polyglot-sql-function-catalogs/src/lib.rs +0 -0
  150. {polyglot_sql-0.5.2 → polyglot_sql-0.5.4}/crates/polyglot-sql-function-catalogs/tools/clickhouse/extract_functions.py +0 -0
  151. {polyglot_sql-0.5.2 → polyglot_sql-0.5.4}/crates/polyglot-sql-function-catalogs/tools/duckdb/extract_functions.py +0 -0
  152. {polyglot_sql-0.5.2 → polyglot_sql-0.5.4}/crates/polyglot-sql-python/Cargo.toml +0 -0
  153. {polyglot_sql-0.5.2 → polyglot_sql-0.5.4}/crates/polyglot-sql-python/docs/api.md +0 -0
  154. {polyglot_sql-0.5.2 → polyglot_sql-0.5.4}/crates/polyglot-sql-python/mkdocs.yml +0 -0
  155. {polyglot_sql-0.5.2 → polyglot_sql-0.5.4}/crates/polyglot-sql-python/src/annotate_types.rs +0 -0
  156. {polyglot_sql-0.5.2 → polyglot_sql-0.5.4}/crates/polyglot-sql-python/src/dialects.rs +0 -0
  157. {polyglot_sql-0.5.2 → polyglot_sql-0.5.4}/crates/polyglot-sql-python/src/diff.rs +0 -0
  158. {polyglot_sql-0.5.2 → polyglot_sql-0.5.4}/crates/polyglot-sql-python/src/errors.rs +0 -0
  159. {polyglot_sql-0.5.2 → polyglot_sql-0.5.4}/crates/polyglot-sql-python/src/expr_types.rs +0 -0
  160. {polyglot_sql-0.5.2 → polyglot_sql-0.5.4}/crates/polyglot-sql-python/src/format.rs +0 -0
  161. {polyglot_sql-0.5.2 → polyglot_sql-0.5.4}/crates/polyglot-sql-python/src/generate.rs +0 -0
  162. {polyglot_sql-0.5.2 → polyglot_sql-0.5.4}/crates/polyglot-sql-python/src/lineage.rs +0 -0
  163. {polyglot_sql-0.5.2 → polyglot_sql-0.5.4}/crates/polyglot-sql-python/src/openlineage.rs +0 -0
  164. {polyglot_sql-0.5.2 → polyglot_sql-0.5.4}/crates/polyglot-sql-python/src/optimize.rs +0 -0
  165. {polyglot_sql-0.5.2 → polyglot_sql-0.5.4}/crates/polyglot-sql-python/src/parse.rs +0 -0
  166. {polyglot_sql-0.5.2 → polyglot_sql-0.5.4}/crates/polyglot-sql-python/src/query_analysis.rs +0 -0
  167. {polyglot_sql-0.5.2 → polyglot_sql-0.5.4}/crates/polyglot-sql-python/src/tokenize.rs +0 -0
  168. {polyglot_sql-0.5.2 → polyglot_sql-0.5.4}/crates/polyglot-sql-python/src/transpile.rs +0 -0
  169. {polyglot_sql-0.5.2 → polyglot_sql-0.5.4}/crates/polyglot-sql-python/src/types.rs +0 -0
  170. {polyglot_sql-0.5.2 → polyglot_sql-0.5.4}/crates/polyglot-sql-python/src/validate.rs +0 -0
  171. {polyglot_sql-0.5.2 → polyglot_sql-0.5.4}/crates/polyglot-sql-python/tests/conftest.py +0 -0
  172. {polyglot_sql-0.5.2 → polyglot_sql-0.5.4}/crates/polyglot-sql-python/tests/test_compat.py +0 -0
  173. {polyglot_sql-0.5.2 → polyglot_sql-0.5.4}/crates/polyglot-sql-python/tests/test_dialects.py +0 -0
  174. {polyglot_sql-0.5.2 → polyglot_sql-0.5.4}/crates/polyglot-sql-python/tests/test_diff.py +0 -0
  175. {polyglot_sql-0.5.2 → polyglot_sql-0.5.4}/crates/polyglot-sql-python/tests/test_expression.py +0 -0
  176. {polyglot_sql-0.5.2 → polyglot_sql-0.5.4}/crates/polyglot-sql-python/tests/test_format.py +0 -0
  177. {polyglot_sql-0.5.2 → polyglot_sql-0.5.4}/crates/polyglot-sql-python/tests/test_lineage.py +0 -0
  178. {polyglot_sql-0.5.2 → polyglot_sql-0.5.4}/crates/polyglot-sql-python/tests/test_optimize.py +0 -0
  179. {polyglot_sql-0.5.2 → polyglot_sql-0.5.4}/crates/polyglot-sql-python/tests/test_parse.py +0 -0
  180. {polyglot_sql-0.5.2 → polyglot_sql-0.5.4}/crates/polyglot-sql-python/tests/test_transpile.py +0 -0
  181. {polyglot_sql-0.5.2 → polyglot_sql-0.5.4}/crates/polyglot-sql-python/tests/test_validate.py +0 -0
  182. {polyglot_sql-0.5.2 → polyglot_sql-0.5.4}/crates/polyglot-sql-python/uv.lock +0 -0
  183. {polyglot_sql-0.5.2 → polyglot_sql-0.5.4}/pyproject.toml +0 -0
  184. {polyglot_sql-0.5.2 → polyglot_sql-0.5.4}/python/polyglot_sql/py.typed +0 -0
@@ -605,7 +605,7 @@ dependencies = [
605
605
 
606
606
  [[package]]
607
607
  name = "polyglot-sql"
608
- version = "0.5.2"
608
+ version = "0.5.4"
609
609
  dependencies = [
610
610
  "criterion",
611
611
  "once_cell",
@@ -621,7 +621,7 @@ dependencies = [
621
621
 
622
622
  [[package]]
623
623
  name = "polyglot-sql-ffi"
624
- version = "0.5.2"
624
+ version = "0.5.4"
625
625
  dependencies = [
626
626
  "cbindgen",
627
627
  "polyglot-sql",
@@ -631,11 +631,11 @@ dependencies = [
631
631
 
632
632
  [[package]]
633
633
  name = "polyglot-sql-function-catalogs"
634
- version = "0.5.2"
634
+ version = "0.5.4"
635
635
 
636
636
  [[package]]
637
637
  name = "polyglot-sql-python"
638
- version = "0.5.2"
638
+ version = "0.5.4"
639
639
  dependencies = [
640
640
  "polyglot-sql",
641
641
  "pyo3",
@@ -646,7 +646,7 @@ dependencies = [
646
646
 
647
647
  [[package]]
648
648
  name = "polyglot-sql-wasm"
649
- version = "0.5.2"
649
+ version = "0.5.4"
650
650
  dependencies = [
651
651
  "console_error_panic_hook",
652
652
  "js-sys",
@@ -6,7 +6,7 @@ exclude = [
6
6
  ]
7
7
 
8
8
  [workspace.package]
9
- version = "0.5.2"
9
+ version = "0.5.4"
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.5.2
3
+ Version: 0.5.4
4
4
  Classifier: Development Status :: 4 - Beta
5
5
  Classifier: Intended Audience :: Developers
6
6
  Classifier: License :: OSI Approved :: MIT License
@@ -68,6 +68,16 @@ polyglot_sql.parse_one("VARCHAR(255)", dialect="duckdb", into=polyglot_sql.DataT
68
68
  polyglot_sql.format_sql("SELECT a,b FROM t WHERE x=1", dialect="postgres")
69
69
  ```
70
70
 
71
+ ```python
72
+ ast = polyglot_sql.parse_one("SELECT id FROM a UNION ALL SELECT id FROM b")
73
+ order_expr = polyglot_sql.parse_one("SELECT id").args["expressions"][0]
74
+ ast = polyglot_sql.set_limit(ast, 100)
75
+ ast = polyglot_sql.set_offset(ast, 10)
76
+ ast = polyglot_sql.set_order_by(ast, order_expr)
77
+ polyglot_sql.generate(ast)
78
+ # ["SELECT id FROM a UNION ALL SELECT id FROM b ORDER BY id LIMIT 100 OFFSET 10"]
79
+ ```
80
+
71
81
  ### Format Guard Behavior
72
82
 
73
83
  `format_sql` uses Rust core formatting guards with default limits:
@@ -125,29 +135,72 @@ emission are intentionally out of scope.
125
135
 
126
136
  ```python
127
137
  analysis = polyglot_sql.analyze_query(
128
- "SELECT SUM(o.amount) AS total FROM orders AS o",
138
+ "WITH base AS (SELECT id, amount FROM orders) SELECT * FROM base",
129
139
  {
130
140
  "dialect": "generic",
131
141
  "schema": {
132
142
  "tables": [
133
143
  {
134
144
  "name": "orders",
135
- "columns": [{"name": "amount", "type": "DECIMAL(10,2)"}],
145
+ "columns": [
146
+ {"name": "id", "type": "INT", "nullable": False},
147
+ {"name": "amount", "type": "DECIMAL(10,2)", "nullable": True},
148
+ ],
136
149
  }
137
150
  ]
138
151
  },
139
152
  },
140
153
  )
141
- print(analysis["projections"][0]["transformKind"]) # "aggregation"
142
- print(analysis["projections"][0]["typeHint"]) # "DECIMAL(10, 2)"
154
+ print(analysis["cteFacts"][0]["bodySql"]) # "SELECT id, amount FROM orders"
155
+ print(analysis["starProjections"][0]["expandedColumns"]) # ["id", "amount"]
156
+ print(analysis["projections"][0]["nullability"]) # "non_null"
143
157
  print(analysis["baseTables"][0]["name"]) # "orders"
158
+ print(analysis["baseTables"][0]["table"]) # "orders"
144
159
  ```
145
160
 
146
161
  `analysis["relations"]` reports sources visible in the analyzed scope.
147
162
  `analysis["baseTables"]` reports deduplicated physical table dependencies across
148
- nested CTEs, derived tables, subqueries, and set-operation branches. Validation
163
+ nested CTEs, derived tables, subqueries, and set-operation branches. For
164
+ physical relation facts, `name` remains the qualified display name while
165
+ `catalog`, `schema`, and `table` expose parsed identifier parts. Validation
149
166
  uses broad type families, while query analysis preserves parseable detailed
150
- schema type strings for projection `typeHint` values.
167
+ schema type strings for projection `typeHint` values. `analysis["cteFacts"]`
168
+ reports top-level CTE definitions, `analysis["starProjections"]` records the
169
+ original star projections and schema-expanded columns, and each projection has
170
+ conservative `nullability`: `"non_null"`, `"nullable"`, or `"unknown"`.
171
+ Function-like projections may include `transformFunction` with the function
172
+ name, literal arguments, and column arguments, for example for
173
+ `DATE_TRUNC('month', created_at)`.
174
+
175
+ Validation schema dictionaries use:
176
+
177
+ ```python
178
+ schema = {
179
+ "strict": True,
180
+ "tables": [
181
+ {
182
+ "name": "orders",
183
+ "schema": "analytics",
184
+ "aliases": ["o"],
185
+ "primaryKey": ["id"],
186
+ "uniqueKeys": [["external_id"]],
187
+ "foreignKeys": [
188
+ {
189
+ "columns": ["customer_id"],
190
+ "references": {"table": "customers", "columns": ["id"]},
191
+ }
192
+ ],
193
+ "columns": [
194
+ {"name": "id", "type": "INT", "nullable": False, "primaryKey": True},
195
+ {"name": "amount", "type": "DECIMAL(10,2)", "nullable": True},
196
+ ],
197
+ }
198
+ ],
199
+ }
200
+ ```
201
+
202
+ Use the `type` key for column types. `dataType` / `data_type` are not accepted
203
+ aliases in this payload.
151
204
 
152
205
  ## API Reference
153
206
 
@@ -41,6 +41,16 @@ polyglot_sql.parse_one("VARCHAR(255)", dialect="duckdb", into=polyglot_sql.DataT
41
41
  polyglot_sql.format_sql("SELECT a,b FROM t WHERE x=1", dialect="postgres")
42
42
  ```
43
43
 
44
+ ```python
45
+ ast = polyglot_sql.parse_one("SELECT id FROM a UNION ALL SELECT id FROM b")
46
+ order_expr = polyglot_sql.parse_one("SELECT id").args["expressions"][0]
47
+ ast = polyglot_sql.set_limit(ast, 100)
48
+ ast = polyglot_sql.set_offset(ast, 10)
49
+ ast = polyglot_sql.set_order_by(ast, order_expr)
50
+ polyglot_sql.generate(ast)
51
+ # ["SELECT id FROM a UNION ALL SELECT id FROM b ORDER BY id LIMIT 100 OFFSET 10"]
52
+ ```
53
+
44
54
  ### Format Guard Behavior
45
55
 
46
56
  `format_sql` uses Rust core formatting guards with default limits:
@@ -98,29 +108,72 @@ emission are intentionally out of scope.
98
108
 
99
109
  ```python
100
110
  analysis = polyglot_sql.analyze_query(
101
- "SELECT SUM(o.amount) AS total FROM orders AS o",
111
+ "WITH base AS (SELECT id, amount FROM orders) SELECT * FROM base",
102
112
  {
103
113
  "dialect": "generic",
104
114
  "schema": {
105
115
  "tables": [
106
116
  {
107
117
  "name": "orders",
108
- "columns": [{"name": "amount", "type": "DECIMAL(10,2)"}],
118
+ "columns": [
119
+ {"name": "id", "type": "INT", "nullable": False},
120
+ {"name": "amount", "type": "DECIMAL(10,2)", "nullable": True},
121
+ ],
109
122
  }
110
123
  ]
111
124
  },
112
125
  },
113
126
  )
114
- print(analysis["projections"][0]["transformKind"]) # "aggregation"
115
- print(analysis["projections"][0]["typeHint"]) # "DECIMAL(10, 2)"
127
+ print(analysis["cteFacts"][0]["bodySql"]) # "SELECT id, amount FROM orders"
128
+ print(analysis["starProjections"][0]["expandedColumns"]) # ["id", "amount"]
129
+ print(analysis["projections"][0]["nullability"]) # "non_null"
116
130
  print(analysis["baseTables"][0]["name"]) # "orders"
131
+ print(analysis["baseTables"][0]["table"]) # "orders"
117
132
  ```
118
133
 
119
134
  `analysis["relations"]` reports sources visible in the analyzed scope.
120
135
  `analysis["baseTables"]` reports deduplicated physical table dependencies across
121
- nested CTEs, derived tables, subqueries, and set-operation branches. Validation
136
+ nested CTEs, derived tables, subqueries, and set-operation branches. For
137
+ physical relation facts, `name` remains the qualified display name while
138
+ `catalog`, `schema`, and `table` expose parsed identifier parts. Validation
122
139
  uses broad type families, while query analysis preserves parseable detailed
123
- schema type strings for projection `typeHint` values.
140
+ schema type strings for projection `typeHint` values. `analysis["cteFacts"]`
141
+ reports top-level CTE definitions, `analysis["starProjections"]` records the
142
+ original star projections and schema-expanded columns, and each projection has
143
+ conservative `nullability`: `"non_null"`, `"nullable"`, or `"unknown"`.
144
+ Function-like projections may include `transformFunction` with the function
145
+ name, literal arguments, and column arguments, for example for
146
+ `DATE_TRUNC('month', created_at)`.
147
+
148
+ Validation schema dictionaries use:
149
+
150
+ ```python
151
+ schema = {
152
+ "strict": True,
153
+ "tables": [
154
+ {
155
+ "name": "orders",
156
+ "schema": "analytics",
157
+ "aliases": ["o"],
158
+ "primaryKey": ["id"],
159
+ "uniqueKeys": [["external_id"]],
160
+ "foreignKeys": [
161
+ {
162
+ "columns": ["customer_id"],
163
+ "references": {"table": "customers", "columns": ["id"]},
164
+ }
165
+ ],
166
+ "columns": [
167
+ {"name": "id", "type": "INT", "nullable": False, "primaryKey": True},
168
+ {"name": "amount", "type": "DECIMAL(10,2)", "nullable": True},
169
+ ],
170
+ }
171
+ ],
172
+ }
173
+ ```
174
+
175
+ Use the `type` key for column types. `dataType` / `data_type` are not accepted
176
+ aliases in this payload.
124
177
 
125
178
  ## API Reference
126
179
 
@@ -106,7 +106,7 @@ thiserror = { workspace = true }
106
106
  unicode-segmentation = { workspace = true }
107
107
  stacker = { version = "0.1", optional = true }
108
108
  ts-rs = { version = "12.0", features = ["serde-compat"], optional = true }
109
- polyglot-sql-function-catalogs = { path = "../polyglot-sql-function-catalogs", version = "0.5.2", optional = true, default-features = false }
109
+ polyglot-sql-function-catalogs = { path = "../polyglot-sql-function-catalogs", version = "0.5.4", optional = true, default-features = false }
110
110
 
111
111
  [dev-dependencies]
112
112
  pretty_assertions = "1.4"
@@ -318,19 +318,31 @@ scope, while `base_tables` reports deduplicated physical table dependencies
318
318
  across CTEs, derived tables, subqueries, and set-operation branches. When a
319
319
  `ValidationSchema` is supplied, detailed type strings such as `DECIMAL(10,2)`
320
320
  are preserved in projection `type_hint` values when parseable.
321
+ For physical table relations, `name` remains the qualified display name and
322
+ `catalog`, `schema`, and `table` expose parsed identifier parts.
323
+ `cte_facts` reports top-level CTE names, declared columns, original CTE body SQL,
324
+ and CTE output columns. `star_projections` reports the original top-level star
325
+ projection index, optional table qualifier, and schema-expanded columns when
326
+ known. Each projection also includes conservative `nullability`:
327
+ `non_null`, `nullable`, or `unknown`.
321
328
 
322
329
  ```rust
323
- use polyglot_sql::{analyze_query, AnalyzeQueryOptions, DialectType, QueryShape};
330
+ use polyglot_sql::{
331
+ analyze_query, AnalyzeQueryOptions, DialectType, ProjectionNullability, QueryShape,
332
+ };
324
333
 
325
334
  let schema: polyglot_sql::ValidationSchema = serde_json::from_value(serde_json::json!({
326
335
  "tables": [{
327
336
  "name": "orders",
328
- "columns": [{"name": "amount", "type": "DECIMAL(10,2)"}]
337
+ "columns": [
338
+ {"name": "id", "type": "INT", "nullable": false},
339
+ {"name": "amount", "type": "DECIMAL(10,2)", "nullable": true}
340
+ ]
329
341
  }]
330
342
  })).unwrap();
331
343
 
332
344
  let analysis = analyze_query(
333
- "SELECT SUM(o.amount) AS total FROM orders o",
345
+ "WITH base AS (SELECT id, amount FROM orders) SELECT * FROM base",
334
346
  AnalyzeQueryOptions {
335
347
  dialect: DialectType::Generic,
336
348
  schema: Some(schema),
@@ -338,12 +350,44 @@ let analysis = analyze_query(
338
350
  ).unwrap();
339
351
 
340
352
  assert_eq!(analysis.shape, QueryShape::Select);
341
- assert_eq!(analysis.projections[0].name.as_deref(), Some("total"));
342
- assert_eq!(analysis.projections[0].transform_kind, polyglot_sql::TransformKind::Aggregation);
353
+ assert_eq!(analysis.cte_facts[0].name, "base");
354
+ assert_eq!(analysis.cte_facts[0].body_sql, "SELECT id, amount FROM orders");
355
+ assert_eq!(analysis.star_projections[0].expanded_columns, vec!["id", "amount"]);
356
+ assert_eq!(analysis.projections[0].nullability, ProjectionNullability::NonNull);
343
357
  assert_eq!(analysis.base_tables[0].name, "orders");
344
- assert_eq!(analysis.base_tables[0].alias.as_deref(), Some("o"));
358
+ assert_eq!(analysis.base_tables[0].table.as_deref(), Some("orders"));
359
+ ```
360
+
361
+ External JSON schemas use this shape:
362
+
363
+ ```json
364
+ {
365
+ "strict": true,
366
+ "tables": [
367
+ {
368
+ "name": "orders",
369
+ "schema": "analytics",
370
+ "aliases": ["o"],
371
+ "primaryKey": ["id"],
372
+ "uniqueKeys": [["external_id"]],
373
+ "foreignKeys": [
374
+ {
375
+ "columns": ["customer_id"],
376
+ "references": { "table": "customers", "columns": ["id"] }
377
+ }
378
+ ],
379
+ "columns": [
380
+ { "name": "id", "type": "INT", "nullable": false, "primaryKey": true },
381
+ { "name": "amount", "type": "DECIMAL(10,2)", "nullable": true }
382
+ ]
383
+ }
384
+ ]
385
+ }
345
386
  ```
346
387
 
388
+ Use the `type` key for column types in JSON. `dataType` / `data_type` are not
389
+ accepted aliases.
390
+
347
391
  ### Tokenize
348
392
 
349
393
  Access the raw token stream with full source position spans. Each token carries a `Span` with byte offsets and line/column numbers.
@@ -0,0 +1,278 @@
1
+ //! Compatibility helpers for Expression JSON accepted at public API boundaries.
2
+ //!
3
+ //! The canonical AST representation is the serde JSON form of [`Expression`].
4
+ //! Some wrappers historically exposed JavaScript values that stringify unit
5
+ //! variants such as `Expression::Null` as `{}`. These helpers repair that
6
+ //! narrow compatibility shape while preserving errors for unknown AST forms.
7
+
8
+ use crate::expressions::Expression;
9
+ use serde_json::{json, Value};
10
+
11
+ /// Deserialize a single [`Expression`] from JSON, accepting known compatibility
12
+ /// shapes used by SDK boundaries.
13
+ pub fn expression_from_str(input: &str) -> Result<Expression, String> {
14
+ let value: Value = serde_json::from_str(input).map_err(|error| error.to_string())?;
15
+ expression_from_value(value)
16
+ }
17
+
18
+ /// Deserialize a list of [`Expression`] values from JSON, accepting known
19
+ /// compatibility shapes used by SDK boundaries.
20
+ pub fn expressions_from_str(input: &str) -> Result<Vec<Expression>, String> {
21
+ let value: Value = serde_json::from_str(input).map_err(|error| error.to_string())?;
22
+ expressions_from_value(value)
23
+ }
24
+
25
+ /// Deserialize a single [`Expression`] from a JSON value.
26
+ pub fn expression_from_value(value: Value) -> Result<Expression, String> {
27
+ match serde_json::from_value::<Expression>(value.clone()) {
28
+ Ok(expression) => Ok(expression),
29
+ Err(original_error) => {
30
+ let mut repaired = value;
31
+ normalize_expression_value(&mut repaired);
32
+ serde_json::from_value::<Expression>(repaired)
33
+ .map_err(|error| format!("{error}; original error: {original_error}"))
34
+ }
35
+ }
36
+ }
37
+
38
+ /// Deserialize a list of [`Expression`] values from a JSON value.
39
+ pub fn expressions_from_value(value: Value) -> Result<Vec<Expression>, String> {
40
+ match serde_json::from_value::<Vec<Expression>>(value.clone()) {
41
+ Ok(expressions) => Ok(expressions),
42
+ Err(original_error) => {
43
+ let mut repaired = value;
44
+ if let Value::Array(items) = &mut repaired {
45
+ for item in items {
46
+ normalize_expression_value(item);
47
+ }
48
+ }
49
+ serde_json::from_value::<Vec<Expression>>(repaired)
50
+ .map_err(|error| format!("{error}; original error: {original_error}"))
51
+ }
52
+ }
53
+ }
54
+
55
+ /// Serialize an [`Expression`] through serde JSON first so wrapper layers expose
56
+ /// canonical JSON-compatible values instead of runtime-specific JS shapes.
57
+ pub fn expression_to_value(expression: &Expression) -> Result<Value, String> {
58
+ serde_json::to_value(expression).map_err(|error| error.to_string())
59
+ }
60
+
61
+ fn normalize_expression_value(value: &mut Value) {
62
+ match value {
63
+ Value::Object(map) if map.is_empty() => {
64
+ *value = json!({ "null": null });
65
+ }
66
+ Value::Object(map) if map.len() == 1 => {
67
+ let (kind, payload) = map.iter_mut().next().expect("single entry exists");
68
+ if kind == "null" && matches!(payload, Value::Object(inner) if inner.is_empty()) {
69
+ *payload = Value::Null;
70
+ return;
71
+ }
72
+
73
+ if kind == "is_null" {
74
+ normalize_is_null_payload(payload);
75
+ }
76
+
77
+ match payload {
78
+ Value::Object(payload_map) => normalize_struct_fields(payload_map),
79
+ Value::Array(items) => {
80
+ for item in items {
81
+ normalize_expression_value(item);
82
+ }
83
+ }
84
+ _ => {}
85
+ }
86
+ }
87
+ Value::Object(map) => normalize_struct_fields(map),
88
+ Value::Array(items) => {
89
+ for item in items {
90
+ normalize_expression_value(item);
91
+ }
92
+ }
93
+ _ => {}
94
+ }
95
+ }
96
+
97
+ fn normalize_is_null_payload(payload: &mut Value) {
98
+ let Value::Array(items) = payload else {
99
+ return;
100
+ };
101
+ if items.len() != 1 {
102
+ return;
103
+ }
104
+
105
+ let mut this = items.remove(0);
106
+ normalize_expression_value(&mut this);
107
+ *payload = json!({
108
+ "this": this,
109
+ "not": false,
110
+ "postfix_form": false
111
+ });
112
+ }
113
+
114
+ fn normalize_struct_fields(map: &mut serde_json::Map<String, Value>) {
115
+ for (key, value) in map.iter_mut() {
116
+ if is_expression_array_field(key) {
117
+ if let Value::Array(items) = value {
118
+ for item in items {
119
+ normalize_expression_value(item);
120
+ }
121
+ continue;
122
+ }
123
+ }
124
+
125
+ if is_expression_field(key) {
126
+ normalize_expression_value(value);
127
+ } else {
128
+ normalize_container_value(value);
129
+ }
130
+ }
131
+ }
132
+
133
+ fn normalize_container_value(value: &mut Value) {
134
+ match value {
135
+ Value::Object(map) => normalize_struct_fields(map),
136
+ Value::Array(items) => {
137
+ for item in items {
138
+ normalize_container_value(item);
139
+ }
140
+ }
141
+ _ => {}
142
+ }
143
+ }
144
+
145
+ fn is_expression_field(key: &str) -> bool {
146
+ matches!(
147
+ key,
148
+ "this"
149
+ | "left"
150
+ | "right"
151
+ | "low"
152
+ | "high"
153
+ | "expression"
154
+ | "condition"
155
+ | "query"
156
+ | "unnest"
157
+ | "on"
158
+ | "prewhere"
159
+ | "where"
160
+ | "where_clause"
161
+ | "having"
162
+ | "qualify"
163
+ | "source"
164
+ | "body"
165
+ | "default"
166
+ | "true_value"
167
+ | "false_value"
168
+ | "else_result"
169
+ | "target"
170
+ | "format"
171
+ | "limit"
172
+ | "offset"
173
+ )
174
+ }
175
+
176
+ fn is_expression_array_field(key: &str) -> bool {
177
+ matches!(
178
+ key,
179
+ "expressions"
180
+ | "args"
181
+ | "columns"
182
+ | "values"
183
+ | "partition_by"
184
+ | "order_by"
185
+ | "group_by"
186
+ | "distinct_on"
187
+ | "limit_by"
188
+ | "settings"
189
+ | "for_xml"
190
+ | "for_json"
191
+ | "exclude"
192
+ | "hints"
193
+ | "on_columns"
194
+ )
195
+ }
196
+
197
+ #[cfg(test)]
198
+ mod tests {
199
+ use super::*;
200
+ use crate::expressions::Expression;
201
+
202
+ fn column_json(name: &str) -> Value {
203
+ json!({
204
+ "column": {
205
+ "name": {
206
+ "name": name,
207
+ "quoted": false,
208
+ "trailing_comments": [],
209
+ "span": null
210
+ },
211
+ "table": null,
212
+ "join_mark": false,
213
+ "trailing_comments": [],
214
+ "span": null,
215
+ "inferred_type": null
216
+ }
217
+ })
218
+ }
219
+
220
+ #[test]
221
+ fn expression_from_value_accepts_legacy_empty_object_as_null() {
222
+ assert!(matches!(
223
+ expression_from_value(json!({})).expect("empty object should become NULL"),
224
+ Expression::Null(_)
225
+ ));
226
+ }
227
+
228
+ #[test]
229
+ fn expression_from_value_accepts_empty_null_payload() {
230
+ assert!(matches!(
231
+ expression_from_value(json!({ "null": {} })).expect("empty null payload should work"),
232
+ Expression::Null(_)
233
+ ));
234
+ }
235
+
236
+ #[test]
237
+ fn expression_from_value_repairs_nested_null_expression_fields() {
238
+ let expression = expression_from_value(json!({
239
+ "between": {
240
+ "this": column_json("created_at"),
241
+ "low": { "literal": { "literal_type": "string", "value": "2024-01-01" } },
242
+ "high": {},
243
+ "not": false,
244
+ "symmetric": null
245
+ }
246
+ }))
247
+ .expect("between high bound should be repaired");
248
+
249
+ match expression {
250
+ Expression::Between(between) => assert!(matches!(between.high, Expression::Null(_))),
251
+ other => panic!("expected between expression, got {other:?}"),
252
+ }
253
+ }
254
+
255
+ #[test]
256
+ fn expression_from_value_accepts_is_null_array_shorthand() {
257
+ let expression = expression_from_value(json!({
258
+ "is_null": [column_json("deleted_at")]
259
+ }))
260
+ .expect("is_null shorthand should be repaired");
261
+
262
+ match expression {
263
+ Expression::IsNull(is_null) => {
264
+ assert!(!is_null.not);
265
+ assert!(!is_null.postfix_form);
266
+ assert!(matches!(is_null.this, Expression::Column(_)));
267
+ }
268
+ other => panic!("expected is_null expression, got {other:?}"),
269
+ }
270
+ }
271
+
272
+ #[test]
273
+ fn expression_from_value_keeps_unknown_ast_as_error() {
274
+ let error = expression_from_value(json!({ "not_a_real_expression": {} }))
275
+ .expect_err("unknown expression should stay invalid");
276
+ assert!(error.contains("not_a_real_expression"));
277
+ }
278
+ }