sqlty 0.2.0__tar.gz → 0.2.1__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 (250) hide show
  1. {sqlty-0.2.0 → sqlty-0.2.1}/PKG-INFO +1 -1
  2. {sqlty-0.2.0 → sqlty-0.2.1}/rust/Cargo.lock +7 -4
  3. {sqlty-0.2.0 → sqlty-0.2.1}/rust/Cargo.toml +2 -1
  4. {sqlty-0.2.0 → sqlty-0.2.1}/rust/crates/sqlty-ext/Cargo.toml +2 -0
  5. {sqlty-0.2.0 → sqlty-0.2.1}/rust/crates/sqlty-ext/src/lib.rs +39 -20
  6. {sqlty-0.2.0 → sqlty-0.2.1}/rust/crates/sqlty-sql/Cargo.toml +1 -0
  7. {sqlty-0.2.0 → sqlty-0.2.1}/rust/crates/sqlty-sql/src/analyzer/api.rs +4 -1
  8. {sqlty-0.2.0 → sqlty-0.2.1}/rust/crates/sqlty-sql/src/analyzer/dml.rs +66 -14
  9. {sqlty-0.2.0 → sqlty-0.2.1}/rust/crates/sqlty-sql/src/analyzer/expr.rs +35 -19
  10. {sqlty-0.2.0 → sqlty-0.2.1}/rust/crates/sqlty-sql/src/analyzer/functions.rs +16 -3
  11. {sqlty-0.2.0 → sqlty-0.2.1}/rust/crates/sqlty-sql/src/analyzer/grouping.rs +79 -23
  12. {sqlty-0.2.0 → sqlty-0.2.1}/rust/crates/sqlty-sql/src/analyzer/json.rs +1 -0
  13. {sqlty-0.2.0 → sqlty-0.2.1}/rust/crates/sqlty-sql/src/analyzer/narrowing.rs +2 -0
  14. {sqlty-0.2.0 → sqlty-0.2.1}/rust/crates/sqlty-sql/src/analyzer/nullability.rs +12 -5
  15. {sqlty-0.2.0 → sqlty-0.2.1}/rust/crates/sqlty-sql/src/analyzer/operators.rs +23 -9
  16. {sqlty-0.2.0 → sqlty-0.2.1}/rust/crates/sqlty-sql/src/analyzer/preprocess.rs +33 -2
  17. {sqlty-0.2.0 → sqlty-0.2.1}/rust/crates/sqlty-sql/src/analyzer/query.rs +59 -3
  18. {sqlty-0.2.0 → sqlty-0.2.1}/rust/crates/sqlty-sql/src/analyzer/resolve.rs +9 -1
  19. {sqlty-0.2.0 → sqlty-0.2.1}/rust/crates/sqlty-sql/src/analyzer/respell.rs +13 -1
  20. {sqlty-0.2.0 → sqlty-0.2.1}/rust/crates/sqlty-sql/src/catalog/alter.rs +13 -5
  21. {sqlty-0.2.0 → sqlty-0.2.1}/rust/crates/sqlty-sql/src/catalog/columns.rs +21 -0
  22. {sqlty-0.2.0 → sqlty-0.2.1}/rust/crates/sqlty-sql/src/catalog/mod.rs +6 -1
  23. {sqlty-0.2.0 → sqlty-0.2.1}/rust/crates/sqlty-sql/src/catalog/read.rs +5 -0
  24. {sqlty-0.2.0 → sqlty-0.2.1}/rust/crates/sqlty-sql/src/catalog/resolve.rs +27 -2
  25. {sqlty-0.2.0 → sqlty-0.2.1}/skills/sqlty-ci/SKILL.md +1 -1
  26. {sqlty-0.2.0 → sqlty-0.2.1}/src/sqlty/_check.py +2 -3
  27. {sqlty-0.2.0 → sqlty-0.2.1}/src/sqlty/_drivers.py +63 -0
  28. {sqlty-0.2.0 → sqlty-0.2.1}/src/sqlty/_source.py +12 -0
  29. {sqlty-0.2.0 → sqlty-0.2.1}/src/sqlty/core.py +2 -1
  30. {sqlty-0.2.0 → sqlty-0.2.1}/src/sqlty/rules/param-type-mismatch.md +4 -0
  31. {sqlty-0.2.0 → sqlty-0.2.1}/src/sqlty/rules/ungrouped-column.md +3 -0
  32. {sqlty-0.2.0 → sqlty-0.2.1}/src/sqlty/rules/unparsed-query.md +3 -1
  33. {sqlty-0.2.0 → sqlty-0.2.1}/LICENSE +0 -0
  34. {sqlty-0.2.0 → sqlty-0.2.1}/README.md +0 -0
  35. {sqlty-0.2.0 → sqlty-0.2.1}/pyproject.toml +0 -0
  36. {sqlty-0.2.0 → sqlty-0.2.1}/rust/crates/sqlty-ext/build.rs +0 -0
  37. {sqlty-0.2.0 → sqlty-0.2.1}/rust/crates/sqlty-python/Cargo.toml +0 -0
  38. {sqlty-0.2.0 → sqlty-0.2.1}/rust/crates/sqlty-python/src/checker.rs +0 -0
  39. {sqlty-0.2.0 → sqlty-0.2.1}/rust/crates/sqlty-python/src/edit.rs +0 -0
  40. {sqlty-0.2.0 → sqlty-0.2.1}/rust/crates/sqlty-python/src/fixer.rs +0 -0
  41. {sqlty-0.2.0 → sqlty-0.2.1}/rust/crates/sqlty-python/src/lib.rs +0 -0
  42. {sqlty-0.2.0 → sqlty-0.2.1}/rust/crates/sqlty-python/src/options.rs +0 -0
  43. {sqlty-0.2.0 → sqlty-0.2.1}/rust/crates/sqlty-python/src/rewriter.rs +0 -0
  44. {sqlty-0.2.0 → sqlty-0.2.1}/rust/crates/sqlty-python/src/rules.rs +0 -0
  45. {sqlty-0.2.0 → sqlty-0.2.1}/rust/crates/sqlty-python/src/scan.rs +0 -0
  46. {sqlty-0.2.0 → sqlty-0.2.1}/rust/crates/sqlty-python/src/template.rs +0 -0
  47. {sqlty-0.2.0 → sqlty-0.2.1}/rust/crates/sqlty-python/src/tokens.rs +0 -0
  48. {sqlty-0.2.0 → sqlty-0.2.1}/rust/crates/sqlty-sql/build.rs +0 -0
  49. {sqlty-0.2.0 → sqlty-0.2.1}/rust/crates/sqlty-sql/rules/README.md +0 -0
  50. {sqlty-0.2.0 → sqlty-0.2.1}/rust/crates/sqlty-sql/rules/common/aggregates.inference.toml +0 -0
  51. {sqlty-0.2.0 → sqlty-0.2.1}/rust/crates/sqlty-sql/rules/common/arithmetic.inference.toml +0 -0
  52. {sqlty-0.2.0 → sqlty-0.2.1}/rust/crates/sqlty-sql/rules/common/comparison.inference.toml +0 -0
  53. {sqlty-0.2.0 → sqlty-0.2.1}/rust/crates/sqlty-sql/rules/common/concat.inference.toml +0 -0
  54. {sqlty-0.2.0 → sqlty-0.2.1}/rust/crates/sqlty-sql/rules/common/conditional.inference.toml +0 -0
  55. {sqlty-0.2.0 → sqlty-0.2.1}/rust/crates/sqlty-sql/rules/common/datetime.inference.toml +0 -0
  56. {sqlty-0.2.0 → sqlty-0.2.1}/rust/crates/sqlty-sql/rules/common/json.inference.toml +0 -0
  57. {sqlty-0.2.0 → sqlty-0.2.1}/rust/crates/sqlty-sql/rules/common/math.inference.toml +0 -0
  58. {sqlty-0.2.0 → sqlty-0.2.1}/rust/crates/sqlty-sql/rules/common/strings.inference.toml +0 -0
  59. {sqlty-0.2.0 → sqlty-0.2.1}/rust/crates/sqlty-sql/rules/common/window.inference.toml +0 -0
  60. {sqlty-0.2.0 → sqlty-0.2.1}/rust/crates/sqlty-sql/rules/compile.rs +0 -0
  61. {sqlty-0.2.0 → sqlty-0.2.1}/rust/crates/sqlty-sql/rules/dialects.toml +0 -0
  62. {sqlty-0.2.0 → sqlty-0.2.1}/rust/crates/sqlty-sql/rules/mysql/aggregates.inference.toml +0 -0
  63. {sqlty-0.2.0 → sqlty-0.2.1}/rust/crates/sqlty-sql/rules/mysql/arithmetic.inference.toml +0 -0
  64. {sqlty-0.2.0 → sqlty-0.2.1}/rust/crates/sqlty-sql/rules/mysql/bits.inference.toml +0 -0
  65. {sqlty-0.2.0 → sqlty-0.2.1}/rust/crates/sqlty-sql/rules/mysql/comparison.inference.toml +0 -0
  66. {sqlty-0.2.0 → sqlty-0.2.1}/rust/crates/sqlty-sql/rules/mysql/concat.inference.toml +0 -0
  67. {sqlty-0.2.0 → sqlty-0.2.1}/rust/crates/sqlty-sql/rules/mysql/conditional.inference.toml +0 -0
  68. {sqlty-0.2.0 → sqlty-0.2.1}/rust/crates/sqlty-sql/rules/mysql/datetime.inference.toml +0 -0
  69. {sqlty-0.2.0 → sqlty-0.2.1}/rust/crates/sqlty-sql/rules/mysql/encryption.inference.toml +0 -0
  70. {sqlty-0.2.0 → sqlty-0.2.1}/rust/crates/sqlty-sql/rules/mysql/json.inference.toml +0 -0
  71. {sqlty-0.2.0 → sqlty-0.2.1}/rust/crates/sqlty-sql/rules/mysql/math.inference.toml +0 -0
  72. {sqlty-0.2.0 → sqlty-0.2.1}/rust/crates/sqlty-sql/rules/mysql/misc.inference.toml +0 -0
  73. {sqlty-0.2.0 → sqlty-0.2.1}/rust/crates/sqlty-sql/rules/mysql/spatial.inference.toml +0 -0
  74. {sqlty-0.2.0 → sqlty-0.2.1}/rust/crates/sqlty-sql/rules/mysql/strings.inference.toml +0 -0
  75. {sqlty-0.2.0 → sqlty-0.2.1}/rust/crates/sqlty-sql/rules/mysql/system.inference.toml +0 -0
  76. {sqlty-0.2.0 → sqlty-0.2.1}/rust/crates/sqlty-sql/rules/postgres/admin.inference.toml +0 -0
  77. {sqlty-0.2.0 → sqlty-0.2.1}/rust/crates/sqlty-sql/rules/postgres/aggregates.inference.toml +0 -0
  78. {sqlty-0.2.0 → sqlty-0.2.1}/rust/crates/sqlty-sql/rules/postgres/arithmetic.inference.toml +0 -0
  79. {sqlty-0.2.0 → sqlty-0.2.1}/rust/crates/sqlty-sql/rules/postgres/arrays.inference.toml +0 -0
  80. {sqlty-0.2.0 → sqlty-0.2.1}/rust/crates/sqlty-sql/rules/postgres/binary.inference.toml +0 -0
  81. {sqlty-0.2.0 → sqlty-0.2.1}/rust/crates/sqlty-sql/rules/postgres/comparison.inference.toml +0 -0
  82. {sqlty-0.2.0 → sqlty-0.2.1}/rust/crates/sqlty-sql/rules/postgres/concat.inference.toml +0 -0
  83. {sqlty-0.2.0 → sqlty-0.2.1}/rust/crates/sqlty-sql/rules/postgres/conditional.inference.toml +0 -0
  84. {sqlty-0.2.0 → sqlty-0.2.1}/rust/crates/sqlty-sql/rules/postgres/datetime.inference.toml +0 -0
  85. {sqlty-0.2.0 → sqlty-0.2.1}/rust/crates/sqlty-sql/rules/postgres/enums.inference.toml +0 -0
  86. {sqlty-0.2.0 → sqlty-0.2.1}/rust/crates/sqlty-sql/rules/postgres/events.inference.toml +0 -0
  87. {sqlty-0.2.0 → sqlty-0.2.1}/rust/crates/sqlty-sql/rules/postgres/extensions.inference.toml +0 -0
  88. {sqlty-0.2.0 → sqlty-0.2.1}/rust/crates/sqlty-sql/rules/postgres/formatting.inference.toml +0 -0
  89. {sqlty-0.2.0 → sqlty-0.2.1}/rust/crates/sqlty-sql/rules/postgres/geometry.inference.toml +0 -0
  90. {sqlty-0.2.0 → sqlty-0.2.1}/rust/crates/sqlty-sql/rules/postgres/json.inference.toml +0 -0
  91. {sqlty-0.2.0 → sqlty-0.2.1}/rust/crates/sqlty-sql/rules/postgres/math.inference.toml +0 -0
  92. {sqlty-0.2.0 → sqlty-0.2.1}/rust/crates/sqlty-sql/rules/postgres/merge.inference.toml +0 -0
  93. {sqlty-0.2.0 → sqlty-0.2.1}/rust/crates/sqlty-sql/rules/postgres/network.inference.toml +0 -0
  94. {sqlty-0.2.0 → sqlty-0.2.1}/rust/crates/sqlty-sql/rules/postgres/ranges.inference.toml +0 -0
  95. {sqlty-0.2.0 → sqlty-0.2.1}/rust/crates/sqlty-sql/rules/postgres/rows.inference.toml +0 -0
  96. {sqlty-0.2.0 → sqlty-0.2.1}/rust/crates/sqlty-sql/rules/postgres/sequences.inference.toml +0 -0
  97. {sqlty-0.2.0 → sqlty-0.2.1}/rust/crates/sqlty-sql/rules/postgres/series.inference.toml +0 -0
  98. {sqlty-0.2.0 → sqlty-0.2.1}/rust/crates/sqlty-sql/rules/postgres/strings.inference.toml +0 -0
  99. {sqlty-0.2.0 → sqlty-0.2.1}/rust/crates/sqlty-sql/rules/postgres/system.inference.toml +0 -0
  100. {sqlty-0.2.0 → sqlty-0.2.1}/rust/crates/sqlty-sql/rules/postgres/textsearch.inference.toml +0 -0
  101. {sqlty-0.2.0 → sqlty-0.2.1}/rust/crates/sqlty-sql/rules/postgres/uuid.inference.toml +0 -0
  102. {sqlty-0.2.0 → sqlty-0.2.1}/rust/crates/sqlty-sql/rules/postgres/xml.inference.toml +0 -0
  103. {sqlty-0.2.0 → sqlty-0.2.1}/rust/crates/sqlty-sql/rules/sqlite/aggregates.inference.toml +0 -0
  104. {sqlty-0.2.0 → sqlty-0.2.1}/rust/crates/sqlty-sql/rules/sqlite/arithmetic.inference.toml +0 -0
  105. {sqlty-0.2.0 → sqlty-0.2.1}/rust/crates/sqlty-sql/rules/sqlite/bitwise.inference.toml +0 -0
  106. {sqlty-0.2.0 → sqlty-0.2.1}/rust/crates/sqlty-sql/rules/sqlite/datetime.inference.toml +0 -0
  107. {sqlty-0.2.0 → sqlty-0.2.1}/rust/crates/sqlty-sql/rules/sqlite/fts.inference.toml +0 -0
  108. {sqlty-0.2.0 → sqlty-0.2.1}/rust/crates/sqlty-sql/rules/sqlite/json.inference.toml +0 -0
  109. {sqlty-0.2.0 → sqlty-0.2.1}/rust/crates/sqlty-sql/rules/sqlite/math.inference.toml +0 -0
  110. {sqlty-0.2.0 → sqlty-0.2.1}/rust/crates/sqlty-sql/rules/sqlite/scalar.inference.toml +0 -0
  111. {sqlty-0.2.0 → sqlty-0.2.1}/rust/crates/sqlty-sql/rules/sqlite/strings.inference.toml +0 -0
  112. {sqlty-0.2.0 → sqlty-0.2.1}/rust/crates/sqlty-sql/src/analyzer/dialect.rs +0 -0
  113. {sqlty-0.2.0 → sqlty-0.2.1}/rust/crates/sqlty-sql/src/analyzer/explain.rs +0 -0
  114. {sqlty-0.2.0 → sqlty-0.2.1}/rust/crates/sqlty-sql/src/analyzer/hashing.rs +0 -0
  115. {sqlty-0.2.0 → sqlty-0.2.1}/rust/crates/sqlty-sql/src/analyzer/inference.rs +0 -0
  116. {sqlty-0.2.0 → sqlty-0.2.1}/rust/crates/sqlty-sql/src/analyzer/literals.rs +0 -0
  117. {sqlty-0.2.0 → sqlty-0.2.1}/rust/crates/sqlty-sql/src/analyzer/memo.rs +0 -0
  118. {sqlty-0.2.0 → sqlty-0.2.1}/rust/crates/sqlty-sql/src/analyzer/merged_star.rs +0 -0
  119. {sqlty-0.2.0 → sqlty-0.2.1}/rust/crates/sqlty-sql/src/analyzer/mod.rs +0 -0
  120. {sqlty-0.2.0 → sqlty-0.2.1}/rust/crates/sqlty-sql/src/analyzer/names.rs +0 -0
  121. {sqlty-0.2.0 → sqlty-0.2.1}/rust/crates/sqlty-sql/src/analyzer/params.rs +0 -0
  122. {sqlty-0.2.0 → sqlty-0.2.1}/rust/crates/sqlty-sql/src/analyzer/pg_record_functions.rs +0 -0
  123. {sqlty-0.2.0 → sqlty-0.2.1}/rust/crates/sqlty-sql/src/analyzer/references.rs +0 -0
  124. {sqlty-0.2.0 → sqlty-0.2.1}/rust/crates/sqlty-sql/src/analyzer/registry.rs +0 -0
  125. {sqlty-0.2.0 → sqlty-0.2.1}/rust/crates/sqlty-sql/src/analyzer/scope.rs +0 -0
  126. {sqlty-0.2.0 → sqlty-0.2.1}/rust/crates/sqlty-sql/src/analyzer/snapshots/sqlty_sql__analyzer__dml__tests__returning_columns_in_queries.snap +0 -0
  127. {sqlty-0.2.0 → sqlty-0.2.1}/rust/crates/sqlty-sql/src/analyzer/snapshots/sqlty_sql__analyzer__expr__tests__arrays_and_subscripts.snap +0 -0
  128. {sqlty-0.2.0 → sqlty-0.2.1}/rust/crates/sqlty-sql/src/analyzer/snapshots/sqlty_sql__analyzer__expr__tests__case_expressions.snap +0 -0
  129. {sqlty-0.2.0 → sqlty-0.2.1}/rust/crates/sqlty-sql/src/analyzer/snapshots/sqlty_sql__analyzer__expr__tests__casts.snap +0 -0
  130. {sqlty-0.2.0 → sqlty-0.2.1}/rust/crates/sqlty-sql/src/analyzer/snapshots/sqlty_sql__analyzer__expr__tests__casts_without_a_schema_do_not_complain.snap +0 -0
  131. {sqlty-0.2.0 → sqlty-0.2.1}/rust/crates/sqlty-sql/src/analyzer/snapshots/sqlty_sql__analyzer__expr__tests__composite_fields_and_json_access.snap +0 -0
  132. {sqlty-0.2.0 → sqlty-0.2.1}/rust/crates/sqlty-sql/src/analyzer/snapshots/sqlty_sql__analyzer__expr__tests__literals_and_unary_operators.snap +0 -0
  133. {sqlty-0.2.0 → sqlty-0.2.1}/rust/crates/sqlty-sql/src/analyzer/snapshots/sqlty_sql__analyzer__expr__tests__misc_expressions.snap +0 -0
  134. {sqlty-0.2.0 → sqlty-0.2.1}/rust/crates/sqlty-sql/src/analyzer/snapshots/sqlty_sql__analyzer__expr__tests__mysql_and_sqlite_expressions.snap +0 -0
  135. {sqlty-0.2.0 → sqlty-0.2.1}/rust/crates/sqlty-sql/src/analyzer/snapshots/sqlty_sql__analyzer__functions__tests__aggregates_in_postgres.snap +0 -0
  136. {sqlty-0.2.0 → sqlty-0.2.1}/rust/crates/sqlty-sql/src/analyzer/snapshots/sqlty_sql__analyzer__functions__tests__function_availability_differs_by_dialect.snap +0 -0
  137. {sqlty-0.2.0 → sqlty-0.2.1}/rust/crates/sqlty-sql/src/analyzer/snapshots/sqlty_sql__analyzer__functions__tests__return_types_follow_the_argument.snap +0 -0
  138. {sqlty-0.2.0 → sqlty-0.2.1}/rust/crates/sqlty-sql/src/analyzer/snapshots/sqlty_sql__analyzer__functions__tests__unknown_functions_are_untyped_and_explained.snap +0 -0
  139. {sqlty-0.2.0 → sqlty-0.2.1}/rust/crates/sqlty-sql/src/analyzer/snapshots/sqlty_sql__analyzer__literals__tests__value_sets_flow_through_expressions.snap +0 -0
  140. {sqlty-0.2.0 → sqlty-0.2.1}/rust/crates/sqlty-sql/src/analyzer/snapshots/sqlty_sql__analyzer__names__tests__column_names_follow_postgres_figure_colname.snap +0 -0
  141. {sqlty-0.2.0 → sqlty-0.2.1}/rust/crates/sqlty-sql/src/analyzer/snapshots/sqlty_sql__analyzer__operators__tests__arithmetic_promotes_numbers_and_types_date_math.snap +0 -0
  142. {sqlty-0.2.0 → sqlty-0.2.1}/rust/crates/sqlty-sql/src/analyzer/snapshots/sqlty_sql__analyzer__operators__tests__binary_operators_in_queries.snap +0 -0
  143. {sqlty-0.2.0 → sqlty-0.2.1}/rust/crates/sqlty-sql/src/analyzer/snapshots/sqlty_sql__analyzer__operators__tests__common_type_follows_postgres_union_rules.snap +0 -0
  144. {sqlty-0.2.0 → sqlty-0.2.1}/rust/crates/sqlty-sql/src/analyzer/snapshots/sqlty_sql__analyzer__operators__tests__division_differs_between_postgres_and_mysql.snap +0 -0
  145. {sqlty-0.2.0 → sqlty-0.2.1}/rust/crates/sqlty-sql/src/analyzer/snapshots/sqlty_sql__analyzer__query__tests__column_names_come_from_the_first_branch.snap +0 -0
  146. {sqlty-0.2.0 → sqlty-0.2.1}/rust/crates/sqlty-sql/src/analyzer/snapshots/sqlty_sql__analyzer__query__tests__ctes.snap +0 -0
  147. {sqlty-0.2.0 → sqlty-0.2.1}/rust/crates/sqlty-sql/src/analyzer/snapshots/sqlty_sql__analyzer__query__tests__dialects_name_and_type_the_same_query_differently.snap +0 -0
  148. {sqlty-0.2.0 → sqlty-0.2.1}/rust/crates/sqlty-sql/src/analyzer/snapshots/sqlty_sql__analyzer__query__tests__projection_forms.snap +0 -0
  149. {sqlty-0.2.0 → sqlty-0.2.1}/rust/crates/sqlty-sql/src/analyzer/snapshots/sqlty_sql__analyzer__query__tests__recursive_ctes_settle_nullability.snap +0 -0
  150. {sqlty-0.2.0 → sqlty-0.2.1}/rust/crates/sqlty-sql/src/analyzer/snapshots/sqlty_sql__analyzer__query__tests__set_operations_combine_nullability.snap +0 -0
  151. {sqlty-0.2.0 → sqlty-0.2.1}/rust/crates/sqlty-sql/src/analyzer/snapshots/sqlty_sql__analyzer__query__tests__set_operations_unify_types.snap +0 -0
  152. {sqlty-0.2.0 → sqlty-0.2.1}/rust/crates/sqlty-sql/src/analyzer/snapshots/sqlty_sql__analyzer__query__tests__statements_that_are_not_queries_have_no_columns.snap +0 -0
  153. {sqlty-0.2.0 → sqlty-0.2.1}/rust/crates/sqlty-sql/src/analyzer/snapshots/sqlty_sql__analyzer__query__tests__unsupported_query_forms_are_explained.snap +0 -0
  154. {sqlty-0.2.0 → sqlty-0.2.1}/rust/crates/sqlty-sql/src/analyzer/snapshots/sqlty_sql__analyzer__query__tests__values_lists.snap +0 -0
  155. {sqlty-0.2.0 → sqlty-0.2.1}/rust/crates/sqlty-sql/src/analyzer/snapshots/sqlty_sql__analyzer__resolve__tests__ambiguous_names_are_reported_once_per_query.snap +0 -0
  156. {sqlty-0.2.0 → sqlty-0.2.1}/rust/crates/sqlty-sql/src/analyzer/snapshots/sqlty_sql__analyzer__resolve__tests__cte_columns_are_resolved_like_tables.snap +0 -0
  157. {sqlty-0.2.0 → sqlty-0.2.1}/rust/crates/sqlty-sql/src/analyzer/snapshots/sqlty_sql__analyzer__resolve__tests__names_are_not_judged_without_a_schema.snap +0 -0
  158. {sqlty-0.2.0 → sqlty-0.2.1}/rust/crates/sqlty-sql/src/analyzer/snapshots/sqlty_sql__analyzer__resolve__tests__nullability_comes_from_the_declaration.snap +0 -0
  159. {sqlty-0.2.0 → sqlty-0.2.1}/rust/crates/sqlty-sql/src/analyzer/snapshots/sqlty_sql__analyzer__resolve__tests__quoted_names_keep_their_case_in_postgres_only.snap +0 -0
  160. {sqlty-0.2.0 → sqlty-0.2.1}/rust/crates/sqlty-sql/src/analyzer/snapshots/sqlty_sql__analyzer__resolve__tests__star_over_unreadable_sources_names_the_reason.snap +0 -0
  161. {sqlty-0.2.0 → sqlty-0.2.1}/rust/crates/sqlty-sql/src/analyzer/snapshots/sqlty_sql__analyzer__resolve__tests__system_columns_exist_without_being_declared.snap +0 -0
  162. {sqlty-0.2.0 → sqlty-0.2.1}/rust/crates/sqlty-sql/src/analyzer/snapshots/sqlty_sql__analyzer__resolve__tests__unknown_names_say_what_is_missing.snap +0 -0
  163. {sqlty-0.2.0 → sqlty-0.2.1}/rust/crates/sqlty-sql/src/analyzer/snapshots/sqlty_sql__analyzer__resolve__tests__unqualified_names_resolve_to_the_first_source_that_has_them.snap +0 -0
  164. {sqlty-0.2.0 → sqlty-0.2.1}/rust/crates/sqlty-sql/src/analyzer/snapshots/sqlty_sql__analyzer__scope__tests__correlated_subqueries_see_the_outer_scope.snap +0 -0
  165. {sqlty-0.2.0 → sqlty-0.2.1}/rust/crates/sqlty-sql/src/analyzer/snapshots/sqlty_sql__analyzer__scope__tests__derived_tables_ctes_and_views.snap +0 -0
  166. {sqlty-0.2.0 → sqlty-0.2.1}/rust/crates/sqlty-sql/src/analyzer/snapshots/sqlty_sql__analyzer__scope__tests__nullability_follows_the_join_kind.snap +0 -0
  167. {sqlty-0.2.0 → sqlty-0.2.1}/rust/crates/sqlty-sql/src/analyzer/snapshots/sqlty_sql__analyzer__scope__tests__opaque_sources_explain_themselves.snap +0 -0
  168. {sqlty-0.2.0 → sqlty-0.2.1}/rust/crates/sqlty-sql/src/analyzer/snapshots/sqlty_sql__analyzer__scope__tests__star_expansion_over_joins.snap +0 -0
  169. {sqlty-0.2.0 → sqlty-0.2.1}/rust/crates/sqlty-sql/src/analyzer/snapshots/sqlty_sql__analyzer__scope__tests__table_names_fold_by_dialect.snap +0 -0
  170. {sqlty-0.2.0 → sqlty-0.2.1}/rust/crates/sqlty-sql/src/analyzer/sql_type.rs +0 -0
  171. {sqlty-0.2.0 → sqlty-0.2.1}/rust/crates/sqlty-sql/src/analyzer/star_expand.rs +0 -0
  172. {sqlty-0.2.0 → sqlty-0.2.1}/rust/crates/sqlty-sql/src/analyzer/table_functions.rs +0 -0
  173. {sqlty-0.2.0 → sqlty-0.2.1}/rust/crates/sqlty-sql/src/analyzer/test_support.rs +0 -0
  174. {sqlty-0.2.0 → sqlty-0.2.1}/rust/crates/sqlty-sql/src/analyzer/tests.rs +0 -0
  175. {sqlty-0.2.0 → sqlty-0.2.1}/rust/crates/sqlty-sql/src/analyzer/text_names.rs +0 -0
  176. {sqlty-0.2.0 → sqlty-0.2.1}/rust/crates/sqlty-sql/src/analyzer/typed.rs +0 -0
  177. {sqlty-0.2.0 → sqlty-0.2.1}/rust/crates/sqlty-sql/src/catalog/builtin_catalog.sql +0 -0
  178. {sqlty-0.2.0 → sqlty-0.2.1}/rust/crates/sqlty-sql/src/catalog/loader.rs +0 -0
  179. {sqlty-0.2.0 → sqlty-0.2.1}/rust/crates/sqlty-sql/src/catalog/names.rs +0 -0
  180. {sqlty-0.2.0 → sqlty-0.2.1}/rust/crates/sqlty-sql/src/ddl.rs +0 -0
  181. {sqlty-0.2.0 → sqlty-0.2.1}/rust/crates/sqlty-sql/src/diagnostics.rs +0 -0
  182. {sqlty-0.2.0 → sqlty-0.2.1}/rust/crates/sqlty-sql/src/json.rs +0 -0
  183. {sqlty-0.2.0 → sqlty-0.2.1}/rust/crates/sqlty-sql/src/lib.rs +0 -0
  184. {sqlty-0.2.0 → sqlty-0.2.1}/rust/crates/sqlty-sql/src/placeholders.rs +0 -0
  185. {sqlty-0.2.0 → sqlty-0.2.1}/rust/crates/sqlty-sql/src/values.rs +0 -0
  186. {sqlty-0.2.0 → sqlty-0.2.1}/rust/crates/sqlty-sql/tests/inference_compiler.rs +0 -0
  187. {sqlty-0.2.0 → sqlty-0.2.1}/rust/crates/sqlty-sql/tests/timing.rs +0 -0
  188. {sqlty-0.2.0 → sqlty-0.2.1}/skills/sqlty/SKILL.md +0 -0
  189. {sqlty-0.2.0 → sqlty-0.2.1}/skills/sqlty-adopt/SKILL.md +0 -0
  190. {sqlty-0.2.0 → sqlty-0.2.1}/skills/sqlty-breaking/SKILL.md +0 -0
  191. {sqlty-0.2.0 → sqlty-0.2.1}/skills/sqlty-check/SKILL.md +0 -0
  192. {sqlty-0.2.0 → sqlty-0.2.1}/skills/sqlty-rewrite/SKILL.md +0 -0
  193. {sqlty-0.2.0 → sqlty-0.2.1}/src/sqlty/__init__.py +0 -0
  194. {sqlty-0.2.0 → sqlty-0.2.1}/src/sqlty/__main__.py +0 -0
  195. {sqlty-0.2.0 → sqlty-0.2.1}/src/sqlty/_breaking.py +0 -0
  196. {sqlty-0.2.0 → sqlty-0.2.1}/src/sqlty/_cli.py +0 -0
  197. {sqlty-0.2.0 → sqlty-0.2.1}/src/sqlty/_config.py +0 -0
  198. {sqlty-0.2.0 → sqlty-0.2.1}/src/sqlty/_core.pyi +0 -0
  199. {sqlty-0.2.0 → sqlty-0.2.1}/src/sqlty/_decode.py +0 -0
  200. {sqlty-0.2.0 → sqlty-0.2.1}/src/sqlty/_gitignore.py +0 -0
  201. {sqlty-0.2.0 → sqlty-0.2.1}/src/sqlty/_helpers.py +0 -0
  202. {sqlty-0.2.0 → sqlty-0.2.1}/src/sqlty/_json_shape.py +0 -0
  203. {sqlty-0.2.0 → sqlty-0.2.1}/src/sqlty/_json_types.py +0 -0
  204. {sqlty-0.2.0 → sqlty-0.2.1}/src/sqlty/_lint.py +0 -0
  205. {sqlty-0.2.0 → sqlty-0.2.1}/src/sqlty/_params.py +0 -0
  206. {sqlty-0.2.0 → sqlty-0.2.1}/src/sqlty/_psycopg.py +0 -0
  207. {sqlty-0.2.0 → sqlty-0.2.1}/src/sqlty/_snapshot.py +0 -0
  208. {sqlty-0.2.0 → sqlty-0.2.1}/src/sqlty/_type_extractor/__init__.py +0 -0
  209. {sqlty-0.2.0 → sqlty-0.2.1}/src/sqlty/_type_extractor/ast_fields.py +0 -0
  210. {sqlty-0.2.0 → sqlty-0.2.1}/src/sqlty/_type_extractor/compat.py +0 -0
  211. {sqlty-0.2.0 → sqlty-0.2.1}/src/sqlty/_type_extractor/extract.py +0 -0
  212. {sqlty-0.2.0 → sqlty-0.2.1}/src/sqlty/_type_extractor/generic.py +0 -0
  213. {sqlty-0.2.0 → sqlty-0.2.1}/src/sqlty/_type_extractor/info.py +0 -0
  214. {sqlty-0.2.0 → sqlty-0.2.1}/src/sqlty/_type_extractor/sandbox.py +0 -0
  215. {sqlty-0.2.0 → sqlty-0.2.1}/src/sqlty/_type_extractor/subtype.py +0 -0
  216. {sqlty-0.2.0 → sqlty-0.2.1}/src/sqlty/drivers/psycopg.py +0 -0
  217. {sqlty-0.2.0 → sqlty-0.2.1}/src/sqlty/magic.py +0 -0
  218. {sqlty-0.2.0 → sqlty-0.2.1}/src/sqlty/py.typed +0 -0
  219. {sqlty-0.2.0 → sqlty-0.2.1}/src/sqlty/rules/ambiguous-column.md +0 -0
  220. {sqlty-0.2.0 → sqlty-0.2.1}/src/sqlty/rules/column-count-mismatch.md +0 -0
  221. {sqlty-0.2.0 → sqlty-0.2.1}/src/sqlty/rules/column-name-mismatch.md +0 -0
  222. {sqlty-0.2.0 → sqlty-0.2.1}/src/sqlty/rules/column-type-mismatch.md +0 -0
  223. {sqlty-0.2.0 → sqlty-0.2.1}/src/sqlty/rules/fstring-skipped.md +0 -0
  224. {sqlty-0.2.0 → sqlty-0.2.1}/src/sqlty/rules/inline-sql-comment.md +0 -0
  225. {sqlty-0.2.0 → sqlty-0.2.1}/src/sqlty/rules/insert-missing-column.md +0 -0
  226. {sqlty-0.2.0 → sqlty-0.2.1}/src/sqlty/rules/insert-value-count.md +0 -0
  227. {sqlty-0.2.0 → sqlty-0.2.1}/src/sqlty/rules/json-shape-mismatch.md +0 -0
  228. {sqlty-0.2.0 → sqlty-0.2.1}/src/sqlty/rules/literal-mismatch.md +0 -0
  229. {sqlty-0.2.0 → sqlty-0.2.1}/src/sqlty/rules/mysql-sql-marker.md +0 -0
  230. {sqlty-0.2.0 → sqlty-0.2.1}/src/sqlty/rules/narrow-type.md +0 -0
  231. {sqlty-0.2.0 → sqlty-0.2.1}/src/sqlty/rules/nullable-mismatch.md +0 -0
  232. {sqlty-0.2.0 → sqlty-0.2.1}/src/sqlty/rules/param-count-mismatch.md +0 -0
  233. {sqlty-0.2.0 → sqlty-0.2.1}/src/sqlty/rules/redundant-literal.md +0 -0
  234. {sqlty-0.2.0 → sqlty-0.2.1}/src/sqlty/rules/redundant-optional.md +0 -0
  235. {sqlty-0.2.0 → sqlty-0.2.1}/src/sqlty/rules/select-star-no-schema.md +0 -0
  236. {sqlty-0.2.0 → sqlty-0.2.1}/src/sqlty/rules/select-star.md +0 -0
  237. {sqlty-0.2.0 → sqlty-0.2.1}/src/sqlty/rules/stale-rewrite.md +0 -0
  238. {sqlty-0.2.0 → sqlty-0.2.1}/src/sqlty/rules/stale-type.md +0 -0
  239. {sqlty-0.2.0 → sqlty-0.2.1}/src/sqlty/rules/tstring-skipped.md +0 -0
  240. {sqlty-0.2.0 → sqlty-0.2.1}/src/sqlty/rules/typeddict-inheritance.md +0 -0
  241. {sqlty-0.2.0 → sqlty-0.2.1}/src/sqlty/rules/typeddict-key-mismatch.md +0 -0
  242. {sqlty-0.2.0 → sqlty-0.2.1}/src/sqlty/rules/unchecked-query.md +0 -0
  243. {sqlty-0.2.0 → sqlty-0.2.1}/src/sqlty/rules/unknown-column.md +0 -0
  244. {sqlty-0.2.0 → sqlty-0.2.1}/src/sqlty/rules/unknown-function.md +0 -0
  245. {sqlty-0.2.0 → sqlty-0.2.1}/src/sqlty/rules/unknown-table.md +0 -0
  246. {sqlty-0.2.0 → sqlty-0.2.1}/src/sqlty/rules/unknown-type.md +0 -0
  247. {sqlty-0.2.0 → sqlty-0.2.1}/src/sqlty/rules/unsupported-syntax.md +0 -0
  248. {sqlty-0.2.0 → sqlty-0.2.1}/src/sqlty/rules/untyped-query.md +0 -0
  249. {sqlty-0.2.0 → sqlty-0.2.1}/src/sqlty/rules/unused-ignore.md +0 -0
  250. {sqlty-0.2.0 → sqlty-0.2.1}/src/sqlty_magic.pth +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: sqlty
3
- Version: 0.2.0
3
+ Version: 0.2.1
4
4
  Classifier: Development Status :: 4 - Beta
5
5
  Classifier: Environment :: Console
6
6
  Classifier: Intended Audience :: Developers
@@ -614,7 +614,7 @@ dependencies = [
614
614
 
615
615
  [[package]]
616
616
  name = "sqlty-bench"
617
- version = "0.2.0"
617
+ version = "0.2.1"
618
618
  dependencies = [
619
619
  "criterion",
620
620
  "sqlty-python",
@@ -623,17 +623,19 @@ dependencies = [
623
623
 
624
624
  [[package]]
625
625
  name = "sqlty-core"
626
- version = "0.2.0"
626
+ version = "0.2.1"
627
627
  dependencies = [
628
628
  "pyo3",
629
+ "recursive",
629
630
  "sqlty-python",
630
631
  "sqlty-sql",
632
+ "stacker",
631
633
  "thiserror",
632
634
  ]
633
635
 
634
636
  [[package]]
635
637
  name = "sqlty-python"
636
- version = "0.2.0"
638
+ version = "0.2.1"
637
639
  dependencies = [
638
640
  "regex",
639
641
  "sqlty-sql",
@@ -642,9 +644,10 @@ dependencies = [
642
644
 
643
645
  [[package]]
644
646
  name = "sqlty-sql"
645
- version = "0.2.0"
647
+ version = "0.2.1"
646
648
  dependencies = [
647
649
  "insta",
650
+ "recursive",
648
651
  "regex",
649
652
  "rustc-hash",
650
653
  "serde",
@@ -3,7 +3,7 @@ resolver = "2"
3
3
  members = ["crates/*"]
4
4
 
5
5
  [workspace.package]
6
- version = "0.2.0"
6
+ version = "0.2.1"
7
7
  edition = "2021"
8
8
  description = "Rust engine of sqlty: SQL type inference and Python source rewriting"
9
9
  license = "MIT"
@@ -15,6 +15,7 @@ rust-version = "1.88"
15
15
  pyo3 = { version = "0.29", features = ["extension-module"] }
16
16
  sqlparser = { version = "0.63", features = ["visitor"] }
17
17
  regex = "1"
18
+ recursive = "0.1"
18
19
  rustc-hash = "2"
19
20
  thiserror = "2"
20
21
  insta = "1.49"
@@ -19,4 +19,6 @@ crate-type = ["cdylib"]
19
19
  pyo3.workspace = true
20
20
  sqlty-sql.workspace = true
21
21
  sqlty-python.workspace = true
22
+ recursive.workspace = true
23
+ stacker = "0.1"
22
24
  thiserror.workspace = true
@@ -187,7 +187,7 @@ fn load_schema(
187
187
  let dialect = dialect.0;
188
188
  let overrides = analyzer::TypeOverrides::new(&types.unwrap_or_default(), dialect)
189
189
  .map_err(PyValueError::new_err)?;
190
- let loaded = py.detach(|| {
190
+ let loaded = detach(py, || {
191
191
  let (schema, skipped) = if overrides.is_empty() {
192
192
  analyzer::load_schema_with_skipped(schema_sql, dialect)
193
193
  } else {
@@ -294,7 +294,20 @@ fn infer_type(
294
294
  schema_sql: Option<SchemaArg>,
295
295
  ) -> Result<Option<String>, Error> {
296
296
  let dialect = dialect.0;
297
- Ok(py.detach(|| analyzer::infer_type(sql, dialect, schema(schema_sql, dialect).as_deref()))?)
297
+ Ok(detach(py, || {
298
+ analyzer::infer_type(sql, dialect, schema(schema_sql, dialect).as_deref())
299
+ })?)
300
+ }
301
+
302
+ /// The stack the analyzer's recursion keeps free: it moves to a new stack of
303
+ /// [`STACK_SEGMENT`] when less is left, so an expression nested `MAX_DEPTH` (1000) levels
304
+ /// deep fits whatever thread calls it.
305
+ const STACK_RED_ZONE: usize = 2 << 20;
306
+ const STACK_SEGMENT: usize = 16 << 20;
307
+
308
+ /// Runs `f` without the GIL, with at least [`STACK_RED_ZONE`] of stack.
309
+ fn detach<T: Send, F: Send + FnOnce() -> T>(py: Python<'_>, f: F) -> T {
310
+ py.detach(|| stacker::maybe_grow(STACK_RED_ZONE, STACK_SEGMENT, f))
298
311
  }
299
312
 
300
313
  include!(concat!(env!("OUT_DIR"), "/skills.rs"));
@@ -320,7 +333,7 @@ fn analyze_select(
320
333
  schema_sql: Option<SchemaArg>,
321
334
  ) -> Result<Option<Vec<ColumnTuple>>, Error> {
322
335
  let dialect = dialect.0;
323
- let columns = py.detach(|| {
336
+ let columns = detach(py, || {
324
337
  analyzer::analyze_select(sql, dialect, schema(schema_sql, dialect).as_deref())
325
338
  })?;
326
339
  Ok(columns.map(|columns| {
@@ -349,7 +362,7 @@ fn explain_select(
349
362
  schema_sql: Option<SchemaArg>,
350
363
  ) -> Option<Vec<ColumnRulesTuple>> {
351
364
  let dialect = dialect.0;
352
- let columns = py.detach(|| {
365
+ let columns = detach(py, || {
353
366
  analyzer::explain_select(sql, dialect, schema(schema_sql, dialect).as_deref())
354
367
  })?;
355
368
  let tuple = |rule: analyzer::RuleRef| (rule.id, rule.reference, rule.reasoning);
@@ -385,7 +398,7 @@ fn analyze_columns(
385
398
  schema_sql: Option<SchemaArg>,
386
399
  ) -> Result<Option<Vec<NullableColumnTuple>>, Error> {
387
400
  let dialect = dialect.0;
388
- let columns = py.detach(|| {
401
+ let columns = detach(py, || {
389
402
  analyzer::analyze_select(sql, dialect, schema(schema_sql, dialect).as_deref())
390
403
  })?;
391
404
  Ok(columns.map(|columns| {
@@ -448,7 +461,7 @@ fn analyze_params(
448
461
  schema_sql: Option<SchemaArg>,
449
462
  ) -> Option<Vec<ParamTuple>> {
450
463
  let dialect = dialect.0;
451
- let params = py.detach(|| {
464
+ let params = detach(py, || {
452
465
  analyzer::analyze_params(sql, dialect, schema(schema_sql, dialect).as_deref())
453
466
  })?;
454
467
  Some(
@@ -509,7 +522,7 @@ fn query_diagnostics(
509
522
  schema_sql: Option<SchemaArg>,
510
523
  ) -> Vec<(String, String)> {
511
524
  let dialect = dialect.0;
512
- py.detach(|| {
525
+ detach(py, || {
513
526
  analyzer::analyze_with_diagnostics(sql, dialect, schema(schema_sql, dialect).as_deref())
514
527
  .1
515
528
  .into_iter()
@@ -528,7 +541,7 @@ fn schema_skipped_tables(
528
541
  dialect: DialectArg,
529
542
  ) -> Vec<String> {
530
543
  let dialect = dialect.0;
531
- py.detach(|| skipped_objects(schema_sql, dialect, Some("table")))
544
+ detach(py, || skipped_objects(schema_sql, dialect, Some("table")))
532
545
  .into_iter()
533
546
  .map(|(_, name)| name)
534
547
  .collect()
@@ -544,7 +557,7 @@ fn schema_skipped_objects(
544
557
  dialect: DialectArg,
545
558
  ) -> Vec<(String, String)> {
546
559
  let dialect = dialect.0;
547
- py.detach(|| skipped_objects(schema_sql, dialect, None))
560
+ detach(py, || skipped_objects(schema_sql, dialect, None))
548
561
  }
549
562
 
550
563
  fn skipped_objects<S: AsRef<str>>(
@@ -569,7 +582,7 @@ fn skipped_objects<S: AsRef<str>>(
569
582
  #[pyo3(signature = (sql, *, dialect = DialectArg::POSTGRES))]
570
583
  fn parse_error(py: Python<'_>, sql: &str, dialect: DialectArg) -> Option<String> {
571
584
  let dialect = dialect.0;
572
- py.detach(|| analyzer::parse_error(sql, dialect))
585
+ detach(py, || analyzer::parse_error(sql, dialect))
573
586
  }
574
587
 
575
588
  /// Whether a query yields rows: a query, DML with RETURNING, or unparseable SQL.
@@ -579,7 +592,7 @@ fn parse_error(py: Python<'_>, sql: &str, dialect: DialectArg) -> Option<String>
579
592
  #[pyo3(signature = (sql, *, dialect = DialectArg::POSTGRES))]
580
593
  fn returns_rows(py: Python<'_>, sql: &str, dialect: DialectArg) -> bool {
581
594
  let dialect = dialect.0;
582
- py.detach(|| analyzer::returns_rows(sql, dialect))
595
+ detach(py, || analyzer::returns_rows(sql, dialect))
583
596
  }
584
597
 
585
598
  /// Rewrite Python source: "--sql\n..." -> SQL[T]("--sql\n...").
@@ -603,7 +616,7 @@ fn rewrite_source(
603
616
  keep_lines: bool,
604
617
  ) -> Result<String, Error> {
605
618
  let dialect = dialect.0;
606
- py.detach(|| {
619
+ detach(py, || {
607
620
  let schema = schema_for(source, schema_sql, dialect);
608
621
  let options = Options::new(dialect, schema.as_deref(), import_style, untyped)?;
609
622
  Ok(rewriter::rewrite_source_with(source, &options, keep_lines))
@@ -619,7 +632,7 @@ fn rewrite_source(
619
632
  #[pyfunction]
620
633
  #[pyo3(signature = (source, *, import_style = None))]
621
634
  fn strip_source(py: Python<'_>, source: &str, import_style: Option<&str>) -> Result<String, Error> {
622
- py.detach(|| {
635
+ detach(py, || {
623
636
  let style = ImportStyle::parse(import_style)?;
624
637
  Ok(rewriter::strip_source(source, &style))
625
638
  })
@@ -649,7 +662,7 @@ fn fix_source(
649
662
  suppressed: Option<rewriter::Suppressed>,
650
663
  ) -> Result<String, Error> {
651
664
  let dialect = dialect.0;
652
- py.detach(|| {
665
+ detach(py, || {
653
666
  let schema = schema_for(source, schema_sql, dialect);
654
667
  let options = Options::new(dialect, schema.as_deref(), import_style, untyped)?;
655
668
  let rules = RuleConfig::from_overrides(rules).map_err(ConfigError::from)?;
@@ -675,7 +688,9 @@ fn expand_star(
675
688
  schema_sql: Option<SchemaArg>,
676
689
  ) -> Option<String> {
677
690
  let dialect = dialect.0;
678
- py.detach(|| analyzer::expand_star(sql, dialect, schema(schema_sql, dialect).as_deref()))
691
+ detach(py, || {
692
+ analyzer::expand_star(sql, dialect, schema(schema_sql, dialect).as_deref())
693
+ })
679
694
  }
680
695
 
681
696
  /// `(rule, message)` of `select-star` or `select-star-no-schema` for a query whose
@@ -693,8 +708,10 @@ fn select_star_finding(
693
708
  has_schema: bool,
694
709
  ) -> Option<(&'static str, String)> {
695
710
  let dialect = dialect.0;
696
- py.detach(|| checker::select_star_finding(sql, dialect, has_schema))
697
- .map(|(rule, message)| (rule.name(), message))
711
+ detach(py, || {
712
+ checker::select_star_finding(sql, dialect, has_schema)
713
+ })
714
+ .map(|(rule, message)| (rule.name(), message))
698
715
  }
699
716
 
700
717
  /// A finding of `check_source`, which Python reads as a dict.
@@ -737,7 +754,7 @@ fn check_source(
737
754
  any_calls: Vec<usize>,
738
755
  ) -> Result<Vec<DiagnosticOut>, Error> {
739
756
  let dialect = dialect.0;
740
- py.detach(|| {
757
+ detach(py, || {
741
758
  let schema = schema_for(source, schema_sql, dialect);
742
759
  let options = Options::new(dialect, schema.as_deref(), import_style, untyped)?;
743
760
  let rules = RuleConfig::from_overrides(rules).map_err(ConfigError::from)?;
@@ -767,7 +784,7 @@ fn wrapped_queries(
767
784
  source: &str,
768
785
  import_style: Option<&str>,
769
786
  ) -> Result<Vec<(usize, String, Option<String>)>, Error> {
770
- py.detach(|| {
787
+ detach(py, || {
771
788
  let style = ImportStyle::parse(import_style)?;
772
789
  Ok(checker::wrapped_queries(source, &style))
773
790
  })
@@ -795,7 +812,7 @@ fn query_sites(
795
812
  import_style: Option<&str>,
796
813
  any_calls: Vec<usize>,
797
814
  ) -> Result<Vec<(usize, String, String)>, Error> {
798
- py.detach(|| {
815
+ detach(py, || {
799
816
  let style = ImportStyle::parse(import_style)?;
800
817
  Ok(checker::query_sites(source, &style, &any_calls)
801
818
  .into_iter()
@@ -808,6 +825,8 @@ fn query_sites(
808
825
  /// module runs without the GIL on free-threaded builds.
809
826
  #[pymodule(gil_used = false)]
810
827
  fn _core(m: &Bound<'_, PyModule>) -> PyResult<()> {
828
+ recursive::set_minimum_stack_size(STACK_RED_ZONE);
829
+ recursive::set_stack_allocation_size(STACK_SEGMENT);
811
830
  m.add_function(wrap_pyfunction!(infer_type, m)?)?;
812
831
  m.add_function(wrap_pyfunction!(load_schema, m)?)?;
813
832
  m.add_class::<SchemaHandle>()?;
@@ -13,6 +13,7 @@ workspace = true
13
13
 
14
14
  [dependencies]
15
15
  sqlparser.workspace = true
16
+ recursive.workspace = true
16
17
  regex.workspace = true
17
18
  rustc-hash.workspace = true
18
19
  thiserror.workspace = true
@@ -10,7 +10,7 @@ use super::dialect::SqlDialect;
10
10
  use super::dml::statement_returns_rows;
11
11
  use super::expr::from_sql_type;
12
12
  use super::names::alias_name;
13
- use super::preprocess::{parse_first, parse_query, parser_input};
13
+ use super::preprocess::{parse_first, parse_query, parser_input, too_deep, MAX_DEPTH};
14
14
  use super::query::{Analyzer, Column};
15
15
  use super::{Inferred, Schema, SchemaData, SkippedObject, ANY};
16
16
 
@@ -166,6 +166,9 @@ pub fn parse_error(sql: &str, dialect: &str) -> Option<String> {
166
166
  let input = parser_input(sql, dialect);
167
167
  Some(
168
168
  match Parser::parse_sql(dialect.parser_dialect().as_ref(), &input) {
169
+ Ok(stmts) if too_deep(&stmts) => {
170
+ format!("expressions nest more than {MAX_DEPTH} levels deep")
171
+ }
169
172
  Ok(_) => "unsupported in this dialect".to_string(),
170
173
  Err(err) => {
171
174
  let text = err.to_string();
@@ -24,12 +24,24 @@ pub(super) fn statement_returns_rows(stmt: &Statement) -> bool {
24
24
  | SetExpr::Update(inner)
25
25
  | SetExpr::Delete(inner)
26
26
  | SetExpr::Merge(inner) => statement_returns_rows(inner),
27
- _ => true,
27
+ body => !selects_into(body),
28
28
  },
29
29
  _ => returning(stmt).is_some(),
30
30
  }
31
31
  }
32
32
 
33
+ /// Whether a query stores its rows instead of returning them: PostgreSQL's
34
+ /// `SELECT ... INTO table`, MySQL's `SELECT ... INTO @variable`. Only the first `SELECT` of
35
+ /// a set operation may have `INTO`.
36
+ pub(super) fn selects_into(body: &SetExpr) -> bool {
37
+ match body {
38
+ SetExpr::Select(select) => select.into.is_some(),
39
+ SetExpr::SetOperation { left, .. } => selects_into(left),
40
+ SetExpr::Query(query) => selects_into(&query.body),
41
+ _ => false,
42
+ }
43
+ }
44
+
33
45
  /// The non-empty top-level RETURNING list of INSERT/UPDATE/DELETE/MERGE.
34
46
  pub(super) fn returning(stmt: &Statement) -> Option<&[SelectItem]> {
35
47
  let items = match stmt {
@@ -218,17 +230,16 @@ impl Analyzer<'_> {
218
230
  let TableObject::TableName(name) = &insert.table else {
219
231
  return self.facts.clone();
220
232
  };
233
+ let table = self
234
+ .schema
235
+ .table(&table_key(name, self.dialect))
236
+ .unwrap_or_default();
221
237
  let columns: Vec<String> = if insert.columns.is_empty() {
222
- self.schema
223
- .table(&table_key(name, self.dialect))
224
- .map(|columns| {
225
- columns
226
- .iter()
227
- .filter(|c| !c.invisible)
228
- .map(|c| c.ident.clone())
229
- .collect()
230
- })
231
- .unwrap_or_default()
238
+ table
239
+ .iter()
240
+ .filter(|c| !c.invisible)
241
+ .map(|c| c.ident.clone())
242
+ .collect()
232
243
  } else {
233
244
  insert
234
245
  .columns
@@ -237,16 +248,57 @@ impl Analyzer<'_> {
237
248
  .map(|ident| ident.value.clone())
238
249
  .collect()
239
250
  };
251
+ // `None` for `DEFAULT VALUES`, which writes no column.
240
252
  let values = insert
241
253
  .source
242
254
  .as_deref()
243
- .and_then(|source| quiet.query_columns(source).ok().flatten())
244
- .unwrap_or_default();
255
+ .map(|source| quiet.query_columns(source).ok().flatten());
256
+ let written = match &values {
257
+ None => Some(0),
258
+ Some(values) => values.as_ref().map(Vec::len),
259
+ };
260
+ // A column the INSERT leaves out gets its DEFAULT; `VALUES` shorter than the
261
+ // table leaves out the last columns.
262
+ let listed: Option<Vec<String>> = if insert.columns.is_empty() {
263
+ written.map(|count| {
264
+ table
265
+ .iter()
266
+ .filter(|c| !c.invisible)
267
+ .take(count)
268
+ .map(|c| c.name.clone())
269
+ .collect()
270
+ })
271
+ } else {
272
+ Some(
273
+ insert
274
+ .columns
275
+ .iter()
276
+ .filter_map(|column| column.0.last().and_then(|part| part.as_ident()))
277
+ .map(|ident| fold(ident, self.dialect))
278
+ .collect(),
279
+ )
280
+ };
281
+ let defaulted = listed.into_iter().flat_map(|listed| {
282
+ table
283
+ .iter()
284
+ .filter(move |c| !listed.contains(&c.name))
285
+ .filter(|c| {
286
+ c.default.as_ref().is_some_and(|default| {
287
+ non_null(quiet.nullability(
288
+ default,
289
+ &Scope::new(),
290
+ &Facts::default(),
291
+ ))
292
+ })
293
+ })
294
+ .map(|c| c.name.clone())
295
+ });
245
296
  let inserted: Vec<String> = columns
246
297
  .into_iter()
247
- .zip(&values)
298
+ .zip(values.flatten().unwrap_or_default())
248
299
  .filter(|(_, value)| non_null(value.nullability))
249
300
  .map(|(column, _)| column)
301
+ .chain(defaulted)
250
302
  .collect();
251
303
  match &insert.on {
252
304
  None
@@ -5,7 +5,7 @@ use sqlparser::ast::{
5
5
  FunctionArguments, Ident, ObjectName, Subscript, UnaryOperator, Value,
6
6
  };
7
7
 
8
- use crate::catalog::{fold, FunctionReturn};
8
+ use crate::catalog::{fold, FunctionReturn, SchemaColumn};
9
9
  use crate::diagnostics::NameRule;
10
10
 
11
11
  use super::dialect::SqlDialect;
@@ -102,16 +102,12 @@ impl<'a> Analyzer<'a> {
102
102
  /// the composite's name when it declares no such field. In PostgreSQL each table is
103
103
  /// also the composite type of its rows.
104
104
  fn field_typed(&self, composite: Option<&str>, field: &Ident) -> Result<Typed, String> {
105
- let postgres = self.dialect == SqlDialect::Postgres;
106
- let Some((name, fields)) = composite.map(str::to_lowercase).and_then(|name| {
107
- let fields = self
108
- .schema
109
- .composite(&name)
110
- .or_else(|| postgres.then(|| self.schema.table(&name)).flatten())?;
111
- Some((name, fields))
112
- }) else {
105
+ let Some((name, fields)) = composite.and_then(|name| self.composite_fields(name)) else {
113
106
  return Ok(Typed::UNKNOWN);
114
107
  };
108
+ if is_field_star(field) {
109
+ return Ok(Typed::UNKNOWN);
110
+ }
115
111
  let field = fold(field, self.dialect);
116
112
  match fields.iter().find(|col| col.name == field) {
117
113
  Some(col) => Ok(col.typed(self.dialect)),
@@ -119,6 +115,18 @@ impl<'a> Analyzer<'a> {
119
115
  }
120
116
  }
121
117
 
118
+ /// The lower-case name and the fields of the composite type `composite`, when the schema
119
+ /// declares it. In PostgreSQL each table is also the composite type of its rows.
120
+ pub(super) fn composite_fields(&self, composite: &str) -> Option<(String, &[SchemaColumn])> {
121
+ let name = composite.to_lowercase();
122
+ let fields = self.schema.composite(&name).or_else(|| {
123
+ (self.dialect == SqlDialect::Postgres)
124
+ .then(|| self.schema.table(&name))
125
+ .flatten()
126
+ })?;
127
+ Some((name, fields))
128
+ }
129
+
122
130
  /// Whether `data_type` is, or is an array of, a domain, enum or composite type the
123
131
  /// schema defines.
124
132
  fn defines_type(&self, data_type: &DataType) -> bool {
@@ -196,6 +204,7 @@ impl<'a> Analyzer<'a> {
196
204
  }
197
205
  }
198
206
 
207
+ #[recursive::recursive]
199
208
  pub(super) fn expr(&self, expr: &Expr, scope: &Scope) -> Inferred<Typed> {
200
209
  match expr {
201
210
  Expr::Cast { data_type, .. } => Ok(self.cast_typed(data_type)),
@@ -235,10 +244,13 @@ impl<'a> Analyzer<'a> {
235
244
  expr: inner,
236
245
  } => match crate::values::constant(expr) {
237
246
  Some(negative) => Ok(constant(negative)),
238
- None if self.prefix_rule(op, inner, scope)?.is_some() => {
239
- self.prefix_op(op, inner, scope)
247
+ None => {
248
+ let typed = self.expr(inner, scope)?;
249
+ Ok(match self.prefix_rule_of(op, &typed) {
250
+ Some(rule) => self.prefixed(rule, typed),
251
+ None => strip(typed, self.dialect),
252
+ })
240
253
  }
241
- None => Ok(strip(self.expr(inner, scope)?, self.dialect)),
242
254
  },
243
255
  Expr::AtTimeZone { timestamp, .. } if self.dialect == SqlDialect::Postgres => {
244
256
  // `timestamp AT TIME ZONE zone` is a `timestamptz` and vice versa; a time
@@ -252,17 +264,14 @@ impl<'a> Analyzer<'a> {
252
264
  })
253
265
  }
254
266
  Expr::Ceil { expr: inner, .. } | Expr::Floor { expr: inner, .. } => {
267
+ let typed = self.expr(inner, scope)?;
255
268
  let returns = match self.dialect {
256
269
  SqlDialect::MySql => Returns::Integral,
257
270
  // SQLite returns an integer argument as it is.
258
- SqlDialect::Sqlite
259
- if self.expr(inner, scope)?.python.as_deref() == Some("int") =>
260
- {
261
- Returns::Arg(0)
262
- }
271
+ SqlDialect::Sqlite if typed.python.as_deref() == Some("int") => Returns::Arg(0),
263
272
  _ => Returns::Real,
264
273
  };
265
- self.function_type(returns, &[inner], scope)
274
+ self.function_type_of(returns, &[inner], &[Some(typed), None], scope)
266
275
  }
267
276
  Expr::Extract { .. } if self.dialect == SqlDialect::Postgres => {
268
277
  Ok(from_sql_type("DECIMAL", self.dialect))
@@ -593,7 +602,9 @@ impl<'a> Analyzer<'a> {
593
602
  )
594
603
  }
595
604
  FunctionType::Code => self.sum(argument()?, arg, scope)?,
596
- FunctionType::Returns(returns) => self.function_type(returns, &args, scope)?,
605
+ FunctionType::Returns(returns) => {
606
+ self.function_type_of(returns, &args, &typed, scope)?
607
+ }
597
608
  });
598
609
  }
599
610
  match name.as_str() {
@@ -648,6 +659,11 @@ pub(super) fn is_binary_charset(charset: &ObjectName) -> bool {
648
659
  charset.to_string().eq_ignore_ascii_case("binary")
649
660
  }
650
661
 
662
+ /// Whether `field` is the `"*"` that respell writes for PostgreSQL's `(composite).*`.
663
+ pub(super) fn is_field_star(field: &Ident) -> bool {
664
+ field.value == "*" && field.quote_style == Some('"')
665
+ }
666
+
651
667
  #[cfg(test)]
652
668
  mod tests {
653
669
  use super::super::test_support::reports;
@@ -125,9 +125,22 @@ impl<'a> Analyzer<'a> {
125
125
  args: &[&Expr],
126
126
  scope: &Scope,
127
127
  ) -> Inferred<Typed> {
128
- let arg = |i: usize| match args.get(i) {
129
- Some(arg) => self.expr(arg, scope),
130
- None => Ok(Typed::UNKNOWN),
128
+ self.function_type_of(returns, args, &[None, None], scope)
129
+ }
130
+
131
+ /// [`Self::function_type`] given the types of the first two arguments already inferred,
132
+ /// which are not inferred again.
133
+ pub(super) fn function_type_of(
134
+ &self,
135
+ returns: Returns,
136
+ args: &[&Expr],
137
+ known: &RuleArguments,
138
+ scope: &Scope,
139
+ ) -> Inferred<Typed> {
140
+ let arg = |i: usize| match (known.get(i), args.get(i)) {
141
+ (Some(Some(typed)), _) => Ok(typed.clone()),
142
+ (_, Some(arg)) => self.expr(arg, scope),
143
+ (_, None) => Ok(Typed::UNKNOWN),
131
144
  };
132
145
  let sql = |name: &str| Ok(from_sql_type(name, self.dialect));
133
146
  match returns {