polyglot-sql 0.5.3__tar.gz → 0.5.5__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.3 → polyglot_sql-0.5.5}/Cargo.lock +5 -5
  2. {polyglot_sql-0.5.3 → polyglot_sql-0.5.5}/Cargo.toml +1 -1
  3. {polyglot_sql-0.5.3 → polyglot_sql-0.5.5}/PKG-INFO +18 -2
  4. {polyglot_sql-0.5.3/crates/polyglot-sql-python → polyglot_sql-0.5.5}/README.md +17 -1
  5. {polyglot_sql-0.5.3 → polyglot_sql-0.5.5}/crates/polyglot-sql/Cargo.toml +1 -1
  6. {polyglot_sql-0.5.3 → polyglot_sql-0.5.5}/crates/polyglot-sql/README.md +3 -0
  7. polyglot_sql-0.5.5/crates/polyglot-sql/src/ast_json.rs +278 -0
  8. {polyglot_sql-0.5.3 → polyglot_sql-0.5.5}/crates/polyglot-sql/src/ast_transforms.rs +119 -20
  9. {polyglot_sql-0.5.3 → polyglot_sql-0.5.5}/crates/polyglot-sql/src/builder.rs +27 -0
  10. {polyglot_sql-0.5.3 → polyglot_sql-0.5.5}/crates/polyglot-sql/src/dialects/tsql.rs +2 -0
  11. {polyglot_sql-0.5.3 → polyglot_sql-0.5.5}/crates/polyglot-sql/src/generator.rs +6 -1
  12. {polyglot_sql-0.5.3 → polyglot_sql-0.5.5}/crates/polyglot-sql/src/lib.rs +5 -2
  13. {polyglot_sql-0.5.3 → polyglot_sql-0.5.5}/crates/polyglot-sql/src/query_analysis.rs +227 -14
  14. {polyglot_sql-0.5.3 → polyglot_sql-0.5.5}/crates/polyglot-sql/tests/query_analysis.rs +95 -0
  15. polyglot_sql-0.5.5/crates/polyglot-sql/tests/query_analysis_regression.rs +79 -0
  16. {polyglot_sql-0.5.3 → polyglot_sql-0.5.5}/crates/polyglot-sql/tests/tsql_regression.rs +41 -1
  17. {polyglot_sql-0.5.3 → polyglot_sql-0.5.5/crates/polyglot-sql-python}/README.md +17 -1
  18. {polyglot_sql-0.5.3 → polyglot_sql-0.5.5}/crates/polyglot-sql-python/docs/index.md +3 -0
  19. {polyglot_sql-0.5.3 → polyglot_sql-0.5.5}/crates/polyglot-sql-python/src/expr.rs +4 -3
  20. {polyglot_sql-0.5.3 → polyglot_sql-0.5.5}/crates/polyglot-sql-python/src/helpers.rs +10 -3
  21. {polyglot_sql-0.5.3 → polyglot_sql-0.5.5}/crates/polyglot-sql-python/src/lib.rs +3 -0
  22. {polyglot_sql-0.5.3 → polyglot_sql-0.5.5}/crates/polyglot-sql-python/src/transforms.rs +50 -0
  23. {polyglot_sql-0.5.3 → polyglot_sql-0.5.5}/crates/polyglot-sql-python/tests/test_generate.py +27 -0
  24. {polyglot_sql-0.5.3 → polyglot_sql-0.5.5}/crates/polyglot-sql-python/tests/test_query_analysis.py +35 -0
  25. {polyglot_sql-0.5.3 → polyglot_sql-0.5.5}/crates/polyglot-sql-python/tests/test_transforms.py +13 -0
  26. {polyglot_sql-0.5.3 → polyglot_sql-0.5.5}/python/polyglot_sql/__init__.py +6 -0
  27. {polyglot_sql-0.5.3 → polyglot_sql-0.5.5}/python/polyglot_sql/__init__.pyi +36 -0
  28. {polyglot_sql-0.5.3 → polyglot_sql-0.5.5}/crates/polyglot-sql/benches/in_list.rs +0 -0
  29. {polyglot_sql-0.5.3 → polyglot_sql-0.5.5}/crates/polyglot-sql/benches/parsing.rs +0 -0
  30. {polyglot_sql-0.5.3 → polyglot_sql-0.5.5}/crates/polyglot-sql/benches/rust_parsing.rs +0 -0
  31. {polyglot_sql-0.5.3 → polyglot_sql-0.5.5}/crates/polyglot-sql/benches/transpile.rs +0 -0
  32. {polyglot_sql-0.5.3 → polyglot_sql-0.5.5}/crates/polyglot-sql/examples/basic_usage.rs +0 -0
  33. {polyglot_sql-0.5.3 → polyglot_sql-0.5.5}/crates/polyglot-sql/examples/bench_json.rs +0 -0
  34. {polyglot_sql-0.5.3 → polyglot_sql-0.5.5}/crates/polyglot-sql/src/dialects/athena.rs +0 -0
  35. {polyglot_sql-0.5.3 → polyglot_sql-0.5.5}/crates/polyglot-sql/src/dialects/bigquery.rs +0 -0
  36. {polyglot_sql-0.5.3 → polyglot_sql-0.5.5}/crates/polyglot-sql/src/dialects/clickhouse.rs +0 -0
  37. {polyglot_sql-0.5.3 → polyglot_sql-0.5.5}/crates/polyglot-sql/src/dialects/cockroachdb.rs +0 -0
  38. {polyglot_sql-0.5.3 → polyglot_sql-0.5.5}/crates/polyglot-sql/src/dialects/databricks.rs +0 -0
  39. {polyglot_sql-0.5.3 → polyglot_sql-0.5.5}/crates/polyglot-sql/src/dialects/datafusion.rs +0 -0
  40. {polyglot_sql-0.5.3 → polyglot_sql-0.5.5}/crates/polyglot-sql/src/dialects/doris.rs +0 -0
  41. {polyglot_sql-0.5.3 → polyglot_sql-0.5.5}/crates/polyglot-sql/src/dialects/dremio.rs +0 -0
  42. {polyglot_sql-0.5.3 → polyglot_sql-0.5.5}/crates/polyglot-sql/src/dialects/drill.rs +0 -0
  43. {polyglot_sql-0.5.3 → polyglot_sql-0.5.5}/crates/polyglot-sql/src/dialects/druid.rs +0 -0
  44. {polyglot_sql-0.5.3 → polyglot_sql-0.5.5}/crates/polyglot-sql/src/dialects/duckdb.rs +0 -0
  45. {polyglot_sql-0.5.3 → polyglot_sql-0.5.5}/crates/polyglot-sql/src/dialects/dune.rs +0 -0
  46. {polyglot_sql-0.5.3 → polyglot_sql-0.5.5}/crates/polyglot-sql/src/dialects/exasol.rs +0 -0
  47. {polyglot_sql-0.5.3 → polyglot_sql-0.5.5}/crates/polyglot-sql/src/dialects/fabric.rs +0 -0
  48. {polyglot_sql-0.5.3 → polyglot_sql-0.5.5}/crates/polyglot-sql/src/dialects/generic.rs +0 -0
  49. {polyglot_sql-0.5.3 → polyglot_sql-0.5.5}/crates/polyglot-sql/src/dialects/hive.rs +0 -0
  50. {polyglot_sql-0.5.3 → polyglot_sql-0.5.5}/crates/polyglot-sql/src/dialects/materialize.rs +0 -0
  51. {polyglot_sql-0.5.3 → polyglot_sql-0.5.5}/crates/polyglot-sql/src/dialects/mod.rs +0 -0
  52. {polyglot_sql-0.5.3 → polyglot_sql-0.5.5}/crates/polyglot-sql/src/dialects/mysql.rs +0 -0
  53. {polyglot_sql-0.5.3 → polyglot_sql-0.5.5}/crates/polyglot-sql/src/dialects/oracle.rs +0 -0
  54. {polyglot_sql-0.5.3 → polyglot_sql-0.5.5}/crates/polyglot-sql/src/dialects/postgres.rs +0 -0
  55. {polyglot_sql-0.5.3 → polyglot_sql-0.5.5}/crates/polyglot-sql/src/dialects/presto.rs +0 -0
  56. {polyglot_sql-0.5.3 → polyglot_sql-0.5.5}/crates/polyglot-sql/src/dialects/redshift.rs +0 -0
  57. {polyglot_sql-0.5.3 → polyglot_sql-0.5.5}/crates/polyglot-sql/src/dialects/risingwave.rs +0 -0
  58. {polyglot_sql-0.5.3 → polyglot_sql-0.5.5}/crates/polyglot-sql/src/dialects/singlestore.rs +0 -0
  59. {polyglot_sql-0.5.3 → polyglot_sql-0.5.5}/crates/polyglot-sql/src/dialects/snowflake.rs +0 -0
  60. {polyglot_sql-0.5.3 → polyglot_sql-0.5.5}/crates/polyglot-sql/src/dialects/solr.rs +0 -0
  61. {polyglot_sql-0.5.3 → polyglot_sql-0.5.5}/crates/polyglot-sql/src/dialects/spark.rs +0 -0
  62. {polyglot_sql-0.5.3 → polyglot_sql-0.5.5}/crates/polyglot-sql/src/dialects/sqlite.rs +0 -0
  63. {polyglot_sql-0.5.3 → polyglot_sql-0.5.5}/crates/polyglot-sql/src/dialects/starrocks.rs +0 -0
  64. {polyglot_sql-0.5.3 → polyglot_sql-0.5.5}/crates/polyglot-sql/src/dialects/tableau.rs +0 -0
  65. {polyglot_sql-0.5.3 → polyglot_sql-0.5.5}/crates/polyglot-sql/src/dialects/teradata.rs +0 -0
  66. {polyglot_sql-0.5.3 → polyglot_sql-0.5.5}/crates/polyglot-sql/src/dialects/tidb.rs +0 -0
  67. {polyglot_sql-0.5.3 → polyglot_sql-0.5.5}/crates/polyglot-sql/src/dialects/trino.rs +0 -0
  68. {polyglot_sql-0.5.3 → polyglot_sql-0.5.5}/crates/polyglot-sql/src/diff.rs +0 -0
  69. {polyglot_sql-0.5.3 → polyglot_sql-0.5.5}/crates/polyglot-sql/src/error.rs +0 -0
  70. {polyglot_sql-0.5.3 → polyglot_sql-0.5.5}/crates/polyglot-sql/src/expressions.rs +0 -0
  71. {polyglot_sql-0.5.3 → polyglot_sql-0.5.5}/crates/polyglot-sql/src/function_catalog.rs +0 -0
  72. {polyglot_sql-0.5.3 → polyglot_sql-0.5.5}/crates/polyglot-sql/src/function_registry.rs +0 -0
  73. {polyglot_sql-0.5.3 → polyglot_sql-0.5.5}/crates/polyglot-sql/src/helper.rs +0 -0
  74. {polyglot_sql-0.5.3 → polyglot_sql-0.5.5}/crates/polyglot-sql/src/lineage.rs +0 -0
  75. {polyglot_sql-0.5.3 → polyglot_sql-0.5.5}/crates/polyglot-sql/src/openlineage.rs +0 -0
  76. {polyglot_sql-0.5.3 → polyglot_sql-0.5.5}/crates/polyglot-sql/src/optimizer/annotate_types.rs +0 -0
  77. {polyglot_sql-0.5.3 → polyglot_sql-0.5.5}/crates/polyglot-sql/src/optimizer/canonicalize.rs +0 -0
  78. {polyglot_sql-0.5.3 → polyglot_sql-0.5.5}/crates/polyglot-sql/src/optimizer/eliminate_ctes.rs +0 -0
  79. {polyglot_sql-0.5.3 → polyglot_sql-0.5.5}/crates/polyglot-sql/src/optimizer/eliminate_joins.rs +0 -0
  80. {polyglot_sql-0.5.3 → polyglot_sql-0.5.5}/crates/polyglot-sql/src/optimizer/isolate_table_selects.rs +0 -0
  81. {polyglot_sql-0.5.3 → polyglot_sql-0.5.5}/crates/polyglot-sql/src/optimizer/mod.rs +0 -0
  82. {polyglot_sql-0.5.3 → polyglot_sql-0.5.5}/crates/polyglot-sql/src/optimizer/normalize.rs +0 -0
  83. {polyglot_sql-0.5.3 → polyglot_sql-0.5.5}/crates/polyglot-sql/src/optimizer/normalize_identifiers.rs +0 -0
  84. {polyglot_sql-0.5.3 → polyglot_sql-0.5.5}/crates/polyglot-sql/src/optimizer/optimize_joins.rs +0 -0
  85. {polyglot_sql-0.5.3 → polyglot_sql-0.5.5}/crates/polyglot-sql/src/optimizer/optimizer.rs +0 -0
  86. {polyglot_sql-0.5.3 → polyglot_sql-0.5.5}/crates/polyglot-sql/src/optimizer/pushdown_predicates.rs +0 -0
  87. {polyglot_sql-0.5.3 → polyglot_sql-0.5.5}/crates/polyglot-sql/src/optimizer/pushdown_projections.rs +0 -0
  88. {polyglot_sql-0.5.3 → polyglot_sql-0.5.5}/crates/polyglot-sql/src/optimizer/qualify_columns.rs +0 -0
  89. {polyglot_sql-0.5.3 → polyglot_sql-0.5.5}/crates/polyglot-sql/src/optimizer/qualify_tables.rs +0 -0
  90. {polyglot_sql-0.5.3 → polyglot_sql-0.5.5}/crates/polyglot-sql/src/optimizer/simplify.rs +0 -0
  91. {polyglot_sql-0.5.3 → polyglot_sql-0.5.5}/crates/polyglot-sql/src/optimizer/subquery.rs +0 -0
  92. {polyglot_sql-0.5.3 → polyglot_sql-0.5.5}/crates/polyglot-sql/src/parser.rs +0 -0
  93. {polyglot_sql-0.5.3 → polyglot_sql-0.5.5}/crates/polyglot-sql/src/planner.rs +0 -0
  94. {polyglot_sql-0.5.3 → polyglot_sql-0.5.5}/crates/polyglot-sql/src/resolver.rs +0 -0
  95. {polyglot_sql-0.5.3 → polyglot_sql-0.5.5}/crates/polyglot-sql/src/schema.rs +0 -0
  96. {polyglot_sql-0.5.3 → polyglot_sql-0.5.5}/crates/polyglot-sql/src/scope.rs +0 -0
  97. {polyglot_sql-0.5.3 → polyglot_sql-0.5.5}/crates/polyglot-sql/src/time.rs +0 -0
  98. {polyglot_sql-0.5.3 → polyglot_sql-0.5.5}/crates/polyglot-sql/src/tokens.rs +0 -0
  99. {polyglot_sql-0.5.3 → polyglot_sql-0.5.5}/crates/polyglot-sql/src/transforms.rs +0 -0
  100. {polyglot_sql-0.5.3 → polyglot_sql-0.5.5}/crates/polyglot-sql/src/traversal.rs +0 -0
  101. {polyglot_sql-0.5.3 → polyglot_sql-0.5.5}/crates/polyglot-sql/src/trie.rs +0 -0
  102. {polyglot_sql-0.5.3 → polyglot_sql-0.5.5}/crates/polyglot-sql/src/validation/tests.rs +0 -0
  103. {polyglot_sql-0.5.3 → polyglot_sql-0.5.5}/crates/polyglot-sql/src/validation.rs +0 -0
  104. {polyglot_sql-0.5.3 → polyglot_sql-0.5.5}/crates/polyglot-sql/tests/analyze_failures.rs +0 -0
  105. {polyglot_sql-0.5.3 → polyglot_sql-0.5.5}/crates/polyglot-sql/tests/clickhouse_regression.rs +0 -0
  106. {polyglot_sql-0.5.3 → polyglot_sql-0.5.5}/crates/polyglot-sql/tests/common/known_failures.rs +0 -0
  107. {polyglot_sql-0.5.3 → polyglot_sql-0.5.5}/crates/polyglot-sql/tests/common/mod.rs +0 -0
  108. {polyglot_sql-0.5.3 → polyglot_sql-0.5.5}/crates/polyglot-sql/tests/common/test_data.rs +0 -0
  109. {polyglot_sql-0.5.3 → polyglot_sql-0.5.5}/crates/polyglot-sql/tests/common/test_runner.rs +0 -0
  110. {polyglot_sql-0.5.3 → polyglot_sql-0.5.5}/crates/polyglot-sql/tests/custom_clickhouse_coverage.rs +0 -0
  111. {polyglot_sql-0.5.3 → polyglot_sql-0.5.5}/crates/polyglot-sql/tests/custom_clickhouse_parser.rs +0 -0
  112. {polyglot_sql-0.5.3 → polyglot_sql-0.5.5}/crates/polyglot-sql/tests/custom_dialect.rs +0 -0
  113. {polyglot_sql-0.5.3 → polyglot_sql-0.5.5}/crates/polyglot-sql/tests/custom_dialect_tests.rs +0 -0
  114. {polyglot_sql-0.5.3 → polyglot_sql-0.5.5}/crates/polyglot-sql/tests/custom_fixtures/datafusion/ddl.json +0 -0
  115. {polyglot_sql-0.5.3 → polyglot_sql-0.5.5}/crates/polyglot-sql/tests/custom_fixtures/datafusion/dml.json +0 -0
  116. {polyglot_sql-0.5.3 → polyglot_sql-0.5.5}/crates/polyglot-sql/tests/custom_fixtures/datafusion/functions.json +0 -0
  117. {polyglot_sql-0.5.3 → polyglot_sql-0.5.5}/crates/polyglot-sql/tests/custom_fixtures/datafusion/identity.json +0 -0
  118. {polyglot_sql-0.5.3 → polyglot_sql-0.5.5}/crates/polyglot-sql/tests/custom_fixtures/datafusion/operators.json +0 -0
  119. {polyglot_sql-0.5.3 → polyglot_sql-0.5.5}/crates/polyglot-sql/tests/custom_fixtures/datafusion/select.json +0 -0
  120. {polyglot_sql-0.5.3 → polyglot_sql-0.5.5}/crates/polyglot-sql/tests/custom_fixtures/datafusion/transpilation.json +0 -0
  121. {polyglot_sql-0.5.3 → polyglot_sql-0.5.5}/crates/polyglot-sql/tests/custom_fixtures/datafusion/types.json +0 -0
  122. {polyglot_sql-0.5.3 → polyglot_sql-0.5.5}/crates/polyglot-sql/tests/data_type_api.rs +0 -0
  123. {polyglot_sql-0.5.3 → polyglot_sql-0.5.5}/crates/polyglot-sql/tests/deep_nesting_regression.rs +0 -0
  124. {polyglot_sql-0.5.3 → polyglot_sql-0.5.5}/crates/polyglot-sql/tests/dialect_matrix.rs +0 -0
  125. {polyglot_sql-0.5.3 → polyglot_sql-0.5.5}/crates/polyglot-sql/tests/error_handling.rs +0 -0
  126. {polyglot_sql-0.5.3 → polyglot_sql-0.5.5}/crates/polyglot-sql/tests/fabric_regression.rs +0 -0
  127. {polyglot_sql-0.5.3 → polyglot_sql-0.5.5}/crates/polyglot-sql/tests/fabric_tpch_regression.rs +0 -0
  128. {polyglot_sql-0.5.3 → polyglot_sql-0.5.5}/crates/polyglot-sql/tests/identity_roundtrip.rs +0 -0
  129. {polyglot_sql-0.5.3 → polyglot_sql-0.5.5}/crates/polyglot-sql/tests/issue201_regression_test.rs +0 -0
  130. {polyglot_sql-0.5.3 → polyglot_sql-0.5.5}/crates/polyglot-sql/tests/issue210_regression_test.rs +0 -0
  131. {polyglot_sql-0.5.3 → polyglot_sql-0.5.5}/crates/polyglot-sql/tests/issue226_regression_test.rs +0 -0
  132. {polyglot_sql-0.5.3 → polyglot_sql-0.5.5}/crates/polyglot-sql/tests/issue227_strict_unsupported.rs +0 -0
  133. {polyglot_sql-0.5.3 → polyglot_sql-0.5.5}/crates/polyglot-sql/tests/postgres_sqlite_regression.rs +0 -0
  134. {polyglot_sql-0.5.3 → polyglot_sql-0.5.5}/crates/polyglot-sql/tests/snowflake_regression_test.rs +0 -0
  135. {polyglot_sql-0.5.3 → polyglot_sql-0.5.5}/crates/polyglot-sql/tests/sqlglot_compat.rs +0 -0
  136. {polyglot_sql-0.5.3 → polyglot_sql-0.5.5}/crates/polyglot-sql/tests/sqlglot_dialect_identity.rs +0 -0
  137. {polyglot_sql-0.5.3 → polyglot_sql-0.5.5}/crates/polyglot-sql/tests/sqlglot_identity.rs +0 -0
  138. {polyglot_sql-0.5.3 → polyglot_sql-0.5.5}/crates/polyglot-sql/tests/sqlglot_identity_detailed.rs +0 -0
  139. {polyglot_sql-0.5.3 → polyglot_sql-0.5.5}/crates/polyglot-sql/tests/sqlglot_parser.rs +0 -0
  140. {polyglot_sql-0.5.3 → polyglot_sql-0.5.5}/crates/polyglot-sql/tests/sqlglot_pretty.rs +0 -0
  141. {polyglot_sql-0.5.3 → polyglot_sql-0.5.5}/crates/polyglot-sql/tests/sqlglot_transpilation.rs +0 -0
  142. {polyglot_sql-0.5.3 → polyglot_sql-0.5.5}/crates/polyglot-sql/tests/sqlglot_transpile.rs +0 -0
  143. {polyglot_sql-0.5.3 → polyglot_sql-0.5.5}/crates/polyglot-sql/tests/tpch_transpile_stack.rs +0 -0
  144. {polyglot_sql-0.5.3 → polyglot_sql-0.5.5}/crates/polyglot-sql/tests/transform_regression.rs +0 -0
  145. {polyglot_sql-0.5.3 → polyglot_sql-0.5.5}/crates/polyglot-sql-function-catalogs/Cargo.toml +0 -0
  146. {polyglot_sql-0.5.3 → polyglot_sql-0.5.5}/crates/polyglot-sql-function-catalogs/README.md +0 -0
  147. {polyglot_sql-0.5.3 → polyglot_sql-0.5.5}/crates/polyglot-sql-function-catalogs/src/clickhouse.rs +0 -0
  148. {polyglot_sql-0.5.3 → polyglot_sql-0.5.5}/crates/polyglot-sql-function-catalogs/src/duckdb.rs +0 -0
  149. {polyglot_sql-0.5.3 → polyglot_sql-0.5.5}/crates/polyglot-sql-function-catalogs/src/lib.rs +0 -0
  150. {polyglot_sql-0.5.3 → polyglot_sql-0.5.5}/crates/polyglot-sql-function-catalogs/tools/clickhouse/extract_functions.py +0 -0
  151. {polyglot_sql-0.5.3 → polyglot_sql-0.5.5}/crates/polyglot-sql-function-catalogs/tools/duckdb/extract_functions.py +0 -0
  152. {polyglot_sql-0.5.3 → polyglot_sql-0.5.5}/crates/polyglot-sql-python/Cargo.toml +0 -0
  153. {polyglot_sql-0.5.3 → polyglot_sql-0.5.5}/crates/polyglot-sql-python/docs/api.md +0 -0
  154. {polyglot_sql-0.5.3 → polyglot_sql-0.5.5}/crates/polyglot-sql-python/mkdocs.yml +0 -0
  155. {polyglot_sql-0.5.3 → polyglot_sql-0.5.5}/crates/polyglot-sql-python/src/annotate_types.rs +0 -0
  156. {polyglot_sql-0.5.3 → polyglot_sql-0.5.5}/crates/polyglot-sql-python/src/dialects.rs +0 -0
  157. {polyglot_sql-0.5.3 → polyglot_sql-0.5.5}/crates/polyglot-sql-python/src/diff.rs +0 -0
  158. {polyglot_sql-0.5.3 → polyglot_sql-0.5.5}/crates/polyglot-sql-python/src/errors.rs +0 -0
  159. {polyglot_sql-0.5.3 → polyglot_sql-0.5.5}/crates/polyglot-sql-python/src/expr_types.rs +0 -0
  160. {polyglot_sql-0.5.3 → polyglot_sql-0.5.5}/crates/polyglot-sql-python/src/format.rs +0 -0
  161. {polyglot_sql-0.5.3 → polyglot_sql-0.5.5}/crates/polyglot-sql-python/src/generate.rs +0 -0
  162. {polyglot_sql-0.5.3 → polyglot_sql-0.5.5}/crates/polyglot-sql-python/src/lineage.rs +0 -0
  163. {polyglot_sql-0.5.3 → polyglot_sql-0.5.5}/crates/polyglot-sql-python/src/openlineage.rs +0 -0
  164. {polyglot_sql-0.5.3 → polyglot_sql-0.5.5}/crates/polyglot-sql-python/src/optimize.rs +0 -0
  165. {polyglot_sql-0.5.3 → polyglot_sql-0.5.5}/crates/polyglot-sql-python/src/parse.rs +0 -0
  166. {polyglot_sql-0.5.3 → polyglot_sql-0.5.5}/crates/polyglot-sql-python/src/query_analysis.rs +0 -0
  167. {polyglot_sql-0.5.3 → polyglot_sql-0.5.5}/crates/polyglot-sql-python/src/tokenize.rs +0 -0
  168. {polyglot_sql-0.5.3 → polyglot_sql-0.5.5}/crates/polyglot-sql-python/src/transpile.rs +0 -0
  169. {polyglot_sql-0.5.3 → polyglot_sql-0.5.5}/crates/polyglot-sql-python/src/types.rs +0 -0
  170. {polyglot_sql-0.5.3 → polyglot_sql-0.5.5}/crates/polyglot-sql-python/src/validate.rs +0 -0
  171. {polyglot_sql-0.5.3 → polyglot_sql-0.5.5}/crates/polyglot-sql-python/tests/conftest.py +0 -0
  172. {polyglot_sql-0.5.3 → polyglot_sql-0.5.5}/crates/polyglot-sql-python/tests/test_compat.py +0 -0
  173. {polyglot_sql-0.5.3 → polyglot_sql-0.5.5}/crates/polyglot-sql-python/tests/test_dialects.py +0 -0
  174. {polyglot_sql-0.5.3 → polyglot_sql-0.5.5}/crates/polyglot-sql-python/tests/test_diff.py +0 -0
  175. {polyglot_sql-0.5.3 → polyglot_sql-0.5.5}/crates/polyglot-sql-python/tests/test_expression.py +0 -0
  176. {polyglot_sql-0.5.3 → polyglot_sql-0.5.5}/crates/polyglot-sql-python/tests/test_format.py +0 -0
  177. {polyglot_sql-0.5.3 → polyglot_sql-0.5.5}/crates/polyglot-sql-python/tests/test_lineage.py +0 -0
  178. {polyglot_sql-0.5.3 → polyglot_sql-0.5.5}/crates/polyglot-sql-python/tests/test_optimize.py +0 -0
  179. {polyglot_sql-0.5.3 → polyglot_sql-0.5.5}/crates/polyglot-sql-python/tests/test_parse.py +0 -0
  180. {polyglot_sql-0.5.3 → polyglot_sql-0.5.5}/crates/polyglot-sql-python/tests/test_transpile.py +0 -0
  181. {polyglot_sql-0.5.3 → polyglot_sql-0.5.5}/crates/polyglot-sql-python/tests/test_validate.py +0 -0
  182. {polyglot_sql-0.5.3 → polyglot_sql-0.5.5}/crates/polyglot-sql-python/uv.lock +0 -0
  183. {polyglot_sql-0.5.3 → polyglot_sql-0.5.5}/pyproject.toml +0 -0
  184. {polyglot_sql-0.5.3 → polyglot_sql-0.5.5}/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.3"
608
+ version = "0.5.5"
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.3"
624
+ version = "0.5.5"
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.3"
634
+ version = "0.5.5"
635
635
 
636
636
  [[package]]
637
637
  name = "polyglot-sql-python"
638
- version = "0.5.3"
638
+ version = "0.5.5"
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.3"
649
+ version = "0.5.5"
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.3"
9
+ version = "0.5.5"
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.3
3
+ Version: 0.5.5
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:
@@ -145,16 +155,22 @@ print(analysis["cteFacts"][0]["bodySql"]) # "SELECT id, amount FROM or
145
155
  print(analysis["starProjections"][0]["expandedColumns"]) # ["id", "amount"]
146
156
  print(analysis["projections"][0]["nullability"]) # "non_null"
147
157
  print(analysis["baseTables"][0]["name"]) # "orders"
158
+ print(analysis["baseTables"][0]["table"]) # "orders"
148
159
  ```
149
160
 
150
161
  `analysis["relations"]` reports sources visible in the analyzed scope.
151
162
  `analysis["baseTables"]` reports deduplicated physical table dependencies across
152
- 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
153
166
  uses broad type families, while query analysis preserves parseable detailed
154
167
  schema type strings for projection `typeHint` values. `analysis["cteFacts"]`
155
168
  reports top-level CTE definitions, `analysis["starProjections"]` records the
156
169
  original star projections and schema-expanded columns, and each projection has
157
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)`.
158
174
 
159
175
  Validation schema dictionaries use:
160
176
 
@@ -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:
@@ -118,16 +128,22 @@ print(analysis["cteFacts"][0]["bodySql"]) # "SELECT id, amount FROM or
118
128
  print(analysis["starProjections"][0]["expandedColumns"]) # ["id", "amount"]
119
129
  print(analysis["projections"][0]["nullability"]) # "non_null"
120
130
  print(analysis["baseTables"][0]["name"]) # "orders"
131
+ print(analysis["baseTables"][0]["table"]) # "orders"
121
132
  ```
122
133
 
123
134
  `analysis["relations"]` reports sources visible in the analyzed scope.
124
135
  `analysis["baseTables"]` reports deduplicated physical table dependencies across
125
- 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
126
139
  uses broad type families, while query analysis preserves parseable detailed
127
140
  schema type strings for projection `typeHint` values. `analysis["cteFacts"]`
128
141
  reports top-level CTE definitions, `analysis["starProjections"]` records the
129
142
  original star projections and schema-expanded columns, and each projection has
130
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)`.
131
147
 
132
148
  Validation schema dictionaries use:
133
149
 
@@ -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.3", optional = true, default-features = false }
109
+ polyglot-sql-function-catalogs = { path = "../polyglot-sql-function-catalogs", version = "0.5.5", optional = true, default-features = false }
110
110
 
111
111
  [dev-dependencies]
112
112
  pretty_assertions = "1.4"
@@ -318,6 +318,8 @@ 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.
321
323
  `cte_facts` reports top-level CTE names, declared columns, original CTE body SQL,
322
324
  and CTE output columns. `star_projections` reports the original top-level star
323
325
  projection index, optional table qualifier, and schema-expanded columns when
@@ -353,6 +355,7 @@ assert_eq!(analysis.cte_facts[0].body_sql, "SELECT id, amount FROM orders");
353
355
  assert_eq!(analysis.star_projections[0].expanded_columns, vec!["id", "amount"]);
354
356
  assert_eq!(analysis.projections[0].nullability, ProjectionNullability::NonNull);
355
357
  assert_eq!(analysis.base_tables[0].name, "orders");
358
+ assert_eq!(analysis.base_tables[0].table.as_deref(), Some("orders"));
356
359
  ```
357
360
 
358
361
  External JSON schemas use this shape:
@@ -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
+ }
@@ -98,33 +98,108 @@ pub fn remove_where(expr: Expression) -> Expression {
98
98
  }
99
99
 
100
100
  // ---------------------------------------------------------------------------
101
- // LIMIT / OFFSET
101
+ // LIMIT / OFFSET / ORDER BY
102
102
  // ---------------------------------------------------------------------------
103
103
 
104
- /// Set the LIMIT on a SELECT.
104
+ /// Set the LIMIT on a SELECT or set operation.
105
105
  pub fn set_limit(expr: Expression, limit: usize) -> Expression {
106
- if let Expression::Select(mut sel) = expr {
107
- sel.limit = Some(Limit {
108
- this: Expression::number(limit as i64),
109
- percent: false,
110
- comments: Vec::new(),
111
- });
112
- Expression::Select(sel)
113
- } else {
114
- expr
106
+ set_limit_expr(expr, Expression::number(limit as i64))
107
+ }
108
+
109
+ /// Set the LIMIT on a SELECT or set operation using an expression.
110
+ pub fn set_limit_expr(expr: Expression, limit: Expression) -> Expression {
111
+ match expr {
112
+ Expression::Select(mut sel) => {
113
+ sel.limit = Some(Limit {
114
+ this: limit,
115
+ percent: false,
116
+ comments: Vec::new(),
117
+ });
118
+ Expression::Select(sel)
119
+ }
120
+ Expression::Union(mut union) => {
121
+ union.limit = Some(Box::new(limit));
122
+ Expression::Union(union)
123
+ }
124
+ Expression::Intersect(mut intersect) => {
125
+ intersect.limit = Some(Box::new(limit));
126
+ Expression::Intersect(intersect)
127
+ }
128
+ Expression::Except(mut except) => {
129
+ except.limit = Some(Box::new(limit));
130
+ Expression::Except(except)
131
+ }
132
+ other => other,
115
133
  }
116
134
  }
117
135
 
118
- /// Set the OFFSET on a SELECT.
136
+ /// Set the OFFSET on a SELECT or set operation.
119
137
  pub fn set_offset(expr: Expression, offset: usize) -> Expression {
120
- if let Expression::Select(mut sel) = expr {
121
- sel.offset = Some(Offset {
122
- this: Expression::number(offset as i64),
123
- rows: None,
124
- });
125
- Expression::Select(sel)
126
- } else {
127
- expr
138
+ set_offset_expr(expr, Expression::number(offset as i64))
139
+ }
140
+
141
+ /// Set the OFFSET on a SELECT or set operation using an expression.
142
+ pub fn set_offset_expr(expr: Expression, offset: Expression) -> Expression {
143
+ match expr {
144
+ Expression::Select(mut sel) => {
145
+ sel.offset = Some(Offset {
146
+ this: offset,
147
+ rows: None,
148
+ });
149
+ Expression::Select(sel)
150
+ }
151
+ Expression::Union(mut union) => {
152
+ union.offset = Some(Box::new(offset));
153
+ Expression::Union(union)
154
+ }
155
+ Expression::Intersect(mut intersect) => {
156
+ intersect.offset = Some(Box::new(offset));
157
+ Expression::Intersect(intersect)
158
+ }
159
+ Expression::Except(mut except) => {
160
+ except.offset = Some(Box::new(offset));
161
+ Expression::Except(except)
162
+ }
163
+ other => other,
164
+ }
165
+ }
166
+
167
+ /// Set the ORDER BY clause on a SELECT or set operation.
168
+ ///
169
+ /// Bare expressions are normalized to ascending order expressions. Existing
170
+ /// `Ordered` expressions preserve their direction and null-ordering metadata.
171
+ pub fn set_order_by(expr: Expression, expressions: Vec<Expression>) -> Expression {
172
+ let order_by = OrderBy {
173
+ expressions: expressions.into_iter().map(normalize_ordered).collect(),
174
+ siblings: false,
175
+ comments: Vec::new(),
176
+ };
177
+
178
+ match expr {
179
+ Expression::Select(mut sel) => {
180
+ sel.order_by = Some(order_by);
181
+ Expression::Select(sel)
182
+ }
183
+ Expression::Union(mut union) => {
184
+ union.order_by = Some(order_by);
185
+ Expression::Union(union)
186
+ }
187
+ Expression::Intersect(mut intersect) => {
188
+ intersect.order_by = Some(order_by);
189
+ Expression::Intersect(intersect)
190
+ }
191
+ Expression::Except(mut except) => {
192
+ except.order_by = Some(order_by);
193
+ Expression::Except(except)
194
+ }
195
+ other => other,
196
+ }
197
+ }
198
+
199
+ fn normalize_ordered(expression: Expression) -> Ordered {
200
+ match expression {
201
+ Expression::Ordered(ordered) => *ordered,
202
+ other => Ordered::asc(other),
128
203
  }
129
204
  }
130
205
 
@@ -608,6 +683,14 @@ mod tests {
608
683
  assert!(sql.contains("LIMIT 10"), "Expected LIMIT in: {}", sql);
609
684
  }
610
685
 
686
+ #[test]
687
+ fn test_set_limit_on_set_operation() {
688
+ let expr = parse_one("SELECT a FROM t UNION ALL SELECT a FROM u");
689
+ let result = set_limit(expr, 10);
690
+ let sql = result.sql();
691
+ assert_eq!(sql, "SELECT a FROM t UNION ALL SELECT a FROM u LIMIT 10");
692
+ }
693
+
611
694
  #[test]
612
695
  fn test_set_offset() {
613
696
  let expr = parse_one("SELECT a FROM t");
@@ -616,6 +699,22 @@ mod tests {
616
699
  assert!(sql.contains("OFFSET 5"), "Expected OFFSET in: {}", sql);
617
700
  }
618
701
 
702
+ #[test]
703
+ fn test_set_offset_on_set_operation() {
704
+ let expr = parse_one("SELECT a FROM t UNION ALL SELECT a FROM u");
705
+ let result = set_offset(expr, 5);
706
+ let sql = result.sql();
707
+ assert_eq!(sql, "SELECT a FROM t UNION ALL SELECT a FROM u OFFSET 5");
708
+ }
709
+
710
+ #[test]
711
+ fn test_set_order_by_on_set_operation() {
712
+ let expr = parse_one("SELECT a FROM t UNION ALL SELECT a FROM u");
713
+ let result = set_order_by(expr, vec![Expression::column("a")]);
714
+ let sql = result.sql();
715
+ assert_eq!(sql, "SELECT a FROM t UNION ALL SELECT a FROM u ORDER BY a");
716
+ }
717
+
619
718
  #[test]
620
719
  fn test_remove_limit_offset() {
621
720
  let expr = parse_one("SELECT a FROM t LIMIT 10 OFFSET 5");
@@ -134,6 +134,27 @@ fn builder_table_ref(name: &str) -> TableRef {
134
134
  /// assert_eq!(c.to_sql(), "users.name");
135
135
  /// ```
136
136
  pub fn col(name: &str) -> Expr {
137
+ let parts: Vec<&str> = name.split('.').collect();
138
+ if parts.len() >= 3 && parts.iter().all(|part| !part.is_empty()) {
139
+ let mut expr = Expression::boxed_column(Column {
140
+ name: builder_identifier(parts[1]),
141
+ table: Some(builder_identifier(parts[0])),
142
+ join_mark: false,
143
+ trailing_comments: Vec::new(),
144
+ span: None,
145
+ inferred_type: None,
146
+ });
147
+
148
+ for field in &parts[2..] {
149
+ expr = Expression::Dot(Box::new(DotAccess {
150
+ this: expr,
151
+ field: builder_identifier(field),
152
+ }));
153
+ }
154
+
155
+ return Expr(expr);
156
+ }
157
+
137
158
  if let Some((table, column)) = name.rsplit_once('.') {
138
159
  Expr(Expression::boxed_column(Column {
139
160
  name: builder_identifier(column),
@@ -2891,6 +2912,12 @@ mod tests {
2891
2912
  assert_eq!(sql, "SELECT u.id, u.name FROM users");
2892
2913
  }
2893
2914
 
2915
+ #[test]
2916
+ fn test_nested_dot_column() {
2917
+ let sql = select([col("t.s.f")]).from("users").to_sql();
2918
+ assert_eq!(sql, "SELECT t.s.f FROM users");
2919
+ }
2920
+
2894
2921
  #[test]
2895
2922
  fn test_not_condition() {
2896
2923
  let sql = select(["id"])
@@ -36,6 +36,8 @@ impl DialectImpl for TSQLDialect {
36
36
  config.identifiers.insert('[', ']');
37
37
  // SQL Server also supports double quotes (when QUOTED_IDENTIFIER is ON)
38
38
  config.identifiers.insert('"', '"');
39
+ // SQL Server uses 0x-prefixed binary/varbinary hex literals.
40
+ config.hex_number_strings = true;
39
41
  config
40
42
  }
41
43
 
@@ -14733,8 +14733,13 @@ impl Generator {
14733
14733
  }
14734
14734
  }
14735
14735
  Literal::HexString(h) => {
14736
- // Most dialects use lowercase x'...' for hex literals; Spark/Databricks/Teradata use uppercase X'...'
14736
+ // Most dialects use lowercase x'...' for hex literals.
14737
14737
  match self.config.dialect {
14738
+ Some(DialectType::TSQL) | Some(DialectType::Fabric) => {
14739
+ self.write("0x");
14740
+ self.write(h);
14741
+ return Ok(());
14742
+ }
14738
14743
  Some(DialectType::Spark)
14739
14744
  | Some(DialectType::Databricks)
14740
14745
  | Some(DialectType::Teradata) => self.write("X'"),