sqlassert 0.1.2__tar.gz → 0.3.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 (271) hide show
  1. sqlassert-0.3.1/.github/workflows/ci.yml +126 -0
  2. sqlassert-0.3.1/.gitignore +9 -0
  3. sqlassert-0.3.1/AGENTS.md +33 -0
  4. sqlassert-0.3.1/CLAUDE.md +3 -0
  5. sqlassert-0.3.1/CONTEXT.md +254 -0
  6. sqlassert-0.3.1/LICENSE +21 -0
  7. sqlassert-0.3.1/PKG-INFO +725 -0
  8. sqlassert-0.3.1/README.md +695 -0
  9. sqlassert-0.3.1/case.sql +8 -0
  10. sqlassert-0.3.1/docs/adr/0001-isolate-clingo-as-the-property-engine.md +7 -0
  11. sqlassert-0.3.1/docs/adr/0002-use-a-framework-independent-ir.md +3 -0
  12. sqlassert-0.3.1/docs/adr/0003-encode-the-ir-in-clingo.md +251 -0
  13. sqlassert-0.3.1/docs/adr/0004-use-python-analysis-functions-for-property-inference.md +3 -0
  14. sqlassert-0.3.1/docs/adr/0005-analyze-sql-without-a-connection.md +3 -0
  15. sqlassert-0.3.1/docs/calcite-audit.md +243 -0
  16. sqlassert-0.3.1/docs/calcite-notes.md +72 -0
  17. sqlassert-0.3.1/docs/dialect-provenance.json +685 -0
  18. sqlassert-0.3.1/docs/dialects.md +308 -0
  19. sqlassert-0.3.1/docs/functional-dependencies.md +617 -0
  20. sqlassert-0.3.1/docs/implementation-conventions.md +277 -0
  21. sqlassert-0.3.1/docs/issues/0011-fuzz-test-analyze-against-a-real-world-sql-corpus.md +40 -0
  22. sqlassert-0.3.1/docs/issues/0015-suggest-provable-properties-for-unannotated-sql.md +278 -0
  23. sqlassert-0.3.1/docs/issues/0023-close-ir-lowering-gaps.md +200 -0
  24. sqlassert-0.3.1/docs/issues/0027-soundness-oracle-against-duckdb.md +168 -0
  25. sqlassert-0.3.1/docs/issues/0037-close-the-paulley-audit-missing-rules.md +44 -0
  26. sqlassert-0.3.1/docs/issues/0039-smaller-lowering-gaps.md +25 -0
  27. sqlassert-0.3.1/docs/issues/0042-lowering-support-v2.md +38 -0
  28. sqlassert-0.3.1/docs/issues/0043-external-integrations.md +91 -0
  29. sqlassert-0.3.1/docs/issues/0045-close-the-calcite-audit-missing-proofs.md +119 -0
  30. sqlassert-0.3.1/docs/issues/0046-certify-dialect-facts.md +70 -0
  31. sqlassert-0.3.1/docs/issues/0049-collation-aware-properties.md +42 -0
  32. sqlassert-0.3.1/docs/issues/0050-dbt-catalog-json.md +32 -0
  33. sqlassert-0.3.1/docs/issues/0051-validate-snowflake-against-a-real-engine.md +30 -0
  34. sqlassert-0.3.1/docs/issues/0052-validate-bigquery-against-a-real-engine.md +31 -0
  35. sqlassert-0.3.1/docs/issues/0053-validate-databricks-against-a-real-engine.md +31 -0
  36. sqlassert-0.3.1/docs/issues/0054-validate-redshift-against-a-real-engine.md +30 -0
  37. sqlassert-0.3.1/docs/issues/README.md +80 -0
  38. sqlassert-0.3.1/docs/issues/done/0001-spec-rewrite-the-property-engine-around-a-relational-ir-and.md +156 -0
  39. sqlassert-0.3.1/docs/issues/done/0002-prove-one-declared-unique-join-through-clingo.md +39 -0
  40. sqlassert-0.3.1/docs/issues/done/0003-prove-composite-and-nullable-unique-sets.md +37 -0
  41. sqlassert-0.3.1/docs/issues/done/0004-propagate-uniqueness-through-relation-instances-filters-and.md +36 -0
  42. sqlassert-0.3.1/docs/issues/done/0005-analyze-cte-and-from-subquery-plans.md +40 -0
  43. sqlassert-0.3.1/docs/issues/done/0006-expand-create-views-with-provenance-and-cycle-detection.md +36 -0
  44. sqlassert-0.3.1/docs/issues/done/0007-derive-uniqueness-from-aggregate-and-distinct.md +37 -0
  45. sqlassert-0.3.1/docs/issues/done/0008-derive-uniqueness-from-qualifybypartition.md +36 -0
  46. sqlassert-0.3.1/docs/issues/done/0009-return-conservative-diagnostics-for-the-complete-mvp-boundar.md +66 -0
  47. sqlassert-0.3.1/docs/issues/done/0010-cut-over-to-analyze-and-retire-the-legacy-analyzer.md +38 -0
  48. sqlassert-0.3.1/docs/issues/done/0012-add-unique-set-assertions-for-select-expressions-unique-prim.md +80 -0
  49. sqlassert-0.3.1/docs/issues/done/0013-refactoring.md +160 -0
  50. sqlassert-0.3.1/docs/issues/done/0014-hydrate-analysis-from-database-catalogs-with-recursive-view.md +106 -0
  51. sqlassert-0.3.1/docs/issues/done/0016-assert-and-declare-string-enum-domains.md +43 -0
  52. sqlassert-0.3.1/docs/issues/done/0017-complete-typed-enum-support.md +37 -0
  53. sqlassert-0.3.1/docs/issues/done/0018-assert-exhaustive-simple-case-expressions.md +32 -0
  54. sqlassert-0.3.1/docs/issues/done/0019-assert-and-declare-direct-foreign-keys.md +34 -0
  55. sqlassert-0.3.1/docs/issues/done/0020-propagate-foreign-keys-through-queries.md +31 -0
  56. sqlassert-0.3.1/docs/issues/done/0021-assert-simple-exactly-one-joins.md +34 -0
  57. sqlassert-0.3.1/docs/issues/done/0022-complete-exactly-one-reasoning.md +34 -0
  58. sqlassert-0.3.1/docs/issues/done/0024-functional-dependency-reasoning.md +105 -0
  59. sqlassert-0.3.1/docs/issues/done/0025-check-rule-consistency-invariants.md +21 -0
  60. sqlassert-0.3.1/docs/issues/done/0026-equality-groups.md +47 -0
  61. sqlassert-0.3.1/docs/issues/done/0028-analyze-join-markers-inside-cte-bodies.md +43 -0
  62. sqlassert-0.3.1/docs/issues/done/0029-enum-string-literals-may-coincide.md +40 -0
  63. sqlassert-0.3.1/docs/issues/done/0030-read-enum-types-and-collations-from-the-catalog.md +43 -0
  64. sqlassert-0.3.1/docs/issues/done/0031-enumerated-domains-through-set-operations.md +38 -0
  65. sqlassert-0.3.1/docs/issues/done/0032-infer-simple-case-nullability.md +33 -0
  66. sqlassert-0.3.1/docs/issues/done/0033-inner-join-suggestion-across-join-chains-and-ctes.md +42 -0
  67. sqlassert-0.3.1/docs/issues/done/0034-close-soundness-oracle-blind-spots.md +51 -0
  68. sqlassert-0.3.1/docs/issues/done/0035-audit-fd-propagation-against-paulley.md +50 -0
  69. sqlassert-0.3.1/docs/issues/done/0036-refactor-tests-for-corpus-and-oracle-scale.md +193 -0
  70. sqlassert-0.3.1/docs/issues/done/0038-lower-order-by-and-select-list-subqueries.md +125 -0
  71. sqlassert-0.3.1/docs/issues/done/0040-audit-inference-against-calcite.md +118 -0
  72. sqlassert-0.3.1/docs/issues/done/0041-join-cardinality-markers-and-fan-trap-smell.md +161 -0
  73. sqlassert-0.3.1/docs/issues/done/0044-v1-external-integrations.md +108 -0
  74. sqlassert-0.3.1/docs/issues/done/0047-discovery-base-class.md +52 -0
  75. sqlassert-0.3.1/docs/issues/done/0048-dbt-plugin-package.md +43 -0
  76. sqlassert-0.3.1/docs/literature-notes.md +127 -0
  77. sqlassert-0.3.1/docs/mvp-scope.md +38 -0
  78. sqlassert-0.3.1/docs/references.md +67 -0
  79. sqlassert-0.3.1/integrations/dbt/LICENSE +21 -0
  80. sqlassert-0.3.1/integrations/dbt/README.md +129 -0
  81. sqlassert-0.3.1/integrations/dbt/pyproject.toml +58 -0
  82. sqlassert-0.3.1/integrations/dbt/sqlassert_dbt/__init__.py +19 -0
  83. sqlassert-0.3.1/integrations/dbt/sqlassert_dbt/__main__.py +115 -0
  84. sqlassert-0.3.1/integrations/dbt/sqlassert_dbt/expectations.py +336 -0
  85. sqlassert-0.3.1/integrations/dbt/sqlassert_dbt/jinja.py +183 -0
  86. sqlassert-0.3.1/integrations/dbt/sqlassert_dbt/manifest.py +254 -0
  87. sqlassert-0.3.1/integrations/dbt/sqlassert_dbt/program.py +686 -0
  88. sqlassert-0.3.1/integrations/dbt/sqlassert_dbt/project.py +340 -0
  89. sqlassert-0.3.1/integrations/dbt/sqlassert_dbt/report.py +158 -0
  90. sqlassert-0.3.1/integrations/dbt/tests/conftest.py +16 -0
  91. sqlassert-0.3.1/integrations/dbt/tests/dbt_project/dbt_project.yml +5 -0
  92. sqlassert-0.3.1/integrations/dbt/tests/dbt_project/models/active_customers.sql +4 -0
  93. sqlassert-0.3.1/integrations/dbt/tests/dbt_project/models/customer_keys.sql +4 -0
  94. sqlassert-0.3.1/integrations/dbt/tests/dbt_project/models/customer_orders.sql +6 -0
  95. sqlassert-0.3.1/integrations/dbt/tests/dbt_project/models/customer_signups.sql +2 -0
  96. sqlassert-0.3.1/integrations/dbt/tests/dbt_project/models/customers.sql +12 -0
  97. sqlassert-0.3.1/integrations/dbt/tests/dbt_project/models/orders_appended.sql +7 -0
  98. sqlassert-0.3.1/integrations/dbt/tests/dbt_project/models/orders_incremental.sql +8 -0
  99. sqlassert-0.3.1/integrations/dbt/tests/dbt_project/models/schema.yml +85 -0
  100. sqlassert-0.3.1/integrations/dbt/tests/dbt_project/models/stg_customers.sql +3 -0
  101. sqlassert-0.3.1/integrations/dbt/tests/dbt_project/models/stg_orders.sql +2 -0
  102. sqlassert-0.3.1/integrations/dbt/tests/dbt_project/profiles.yml +7 -0
  103. sqlassert-0.3.1/integrations/dbt/tests/dbt_project/seeds/raw_payments.csv +2 -0
  104. sqlassert-0.3.1/integrations/dbt/tests/dbt_runs.py +26 -0
  105. sqlassert-0.3.1/integrations/dbt/tests/test_contracts.py +56 -0
  106. sqlassert-0.3.1/integrations/dbt/tests/test_dbt.py +561 -0
  107. sqlassert-0.3.1/join.sql +6 -0
  108. sqlassert-0.3.1/left.sql +10 -0
  109. sqlassert-0.3.1/poetry.lock +359 -0
  110. sqlassert-0.3.1/pyproject.toml +62 -0
  111. sqlassert-0.3.1/scripts/certify_dialects.py +661 -0
  112. {sqlassert-0.1.2 → sqlassert-0.3.1}/scripts/publish-pypi.sh +1 -0
  113. sqlassert-0.3.1/sqlassert/__init__.py +38 -0
  114. sqlassert-0.3.1/sqlassert/__main__.py +138 -0
  115. sqlassert-0.3.1/sqlassert/check.py +444 -0
  116. sqlassert-0.3.1/sqlassert/diagnostics.py +57 -0
  117. sqlassert-0.3.1/sqlassert/dialects.py +211 -0
  118. sqlassert-0.3.1/sqlassert/discovery/__init__.py +30 -0
  119. sqlassert-0.3.1/sqlassert/discovery/bigquery.py +71 -0
  120. sqlassert-0.3.1/sqlassert/discovery/databricks.py +18 -0
  121. sqlassert-0.3.1/sqlassert/discovery/duckdb.py +163 -0
  122. sqlassert-0.3.1/sqlassert/discovery/fetcher.py +402 -0
  123. sqlassert-0.3.1/sqlassert/discovery/information_schema.py +326 -0
  124. sqlassert-0.3.1/sqlassert/discovery/mysql.py +70 -0
  125. sqlassert-0.3.1/sqlassert/discovery/postgres.py +143 -0
  126. sqlassert-0.3.1/sqlassert/discovery/redshift.py +36 -0
  127. sqlassert-0.3.1/sqlassert/discovery/snowflake.py +63 -0
  128. sqlassert-0.3.1/sqlassert/discovery/trino.py +37 -0
  129. sqlassert-0.3.1/sqlassert/improvements.py +528 -0
  130. sqlassert-0.3.1/sqlassert/ir/__init__.py +56 -0
  131. sqlassert-0.3.1/sqlassert/ir/convert.py +2377 -0
  132. sqlassert-0.3.1/sqlassert/ir/model.py +676 -0
  133. sqlassert-0.3.1/sqlassert/main.py +57 -0
  134. sqlassert-0.3.1/sqlassert/properties.py +121 -0
  135. sqlassert-0.3.1/sqlassert/provenance.py +73 -0
  136. sqlassert-0.3.1/sqlassert/python_engine.py +299 -0
  137. sqlassert-0.3.1/sqlassert/reporting.py +102 -0
  138. sqlassert-0.3.1/sqlassert/rules/STYLEGUIDE.md +19 -0
  139. sqlassert-0.3.1/sqlassert/rules/__init__.py +11 -0
  140. sqlassert-0.3.1/sqlassert/rules/column_set.py +34 -0
  141. sqlassert-0.3.1/sqlassert/rules/domain.py +376 -0
  142. sqlassert-0.3.1/sqlassert/rules/equality.py +233 -0
  143. sqlassert-0.3.1/sqlassert/rules/exactly_one.py +75 -0
  144. sqlassert-0.3.1/sqlassert/rules/foreign_key.py +316 -0
  145. sqlassert-0.3.1/sqlassert/rules/join_cardinality.py +94 -0
  146. sqlassert-0.3.1/sqlassert/rules/nullability.py +418 -0
  147. sqlassert-0.3.1/sqlassert/rules/uniqueness.py +601 -0
  148. sqlassert-0.3.1/sqlassert/smells.py +306 -0
  149. sqlassert-0.3.1/sqlassert/sql_parse.py +974 -0
  150. sqlassert-0.3.1/sqlassert/suggest.py +339 -0
  151. sqlassert-0.3.1/sqlassert.egg-info/PKG-INFO +725 -0
  152. sqlassert-0.3.1/sqlassert.egg-info/SOURCES.txt +258 -0
  153. sqlassert-0.3.1/sqlassert.egg-info/entry_points.txt +2 -0
  154. sqlassert-0.3.1/sqlassert.egg-info/requires.txt +11 -0
  155. sqlassert-0.3.1/sqlassert.egg-info/scm_file_list.json +254 -0
  156. sqlassert-0.3.1/sqlassert.egg-info/scm_version.json +8 -0
  157. sqlassert-0.3.1/tests/aggregate_and_distinct.sqltest +100 -0
  158. sqlassert-0.3.1/tests/calcite_audit.sqltest +72 -0
  159. sqlassert-0.3.1/tests/calcite_audit_iq.sqltest +262 -0
  160. sqlassert-0.3.1/tests/calcite_audit_keys.sqltest +448 -0
  161. sqlassert-0.3.1/tests/calcite_audit_nulls.sqltest +379 -0
  162. sqlassert-0.3.1/tests/composite_unique_sets.sqltest +138 -0
  163. sqlassert-0.3.1/tests/conftest.py +98 -0
  164. sqlassert-0.3.1/tests/connection_warnings.sqltest +9 -0
  165. sqlassert-0.3.1/tests/cte_and_subquery.sqltest +25 -0
  166. sqlassert-0.3.1/tests/database_hydration.sqltest +13 -0
  167. sqlassert-0.3.1/tests/dialect_duckdb.sqltest +57 -0
  168. sqlassert-0.3.1/tests/dialect_mysql.sqltest +91 -0
  169. sqlassert-0.3.1/tests/dialect_postgres.sqltest +81 -0
  170. sqlassert-0.3.1/tests/dialect_trino.sqltest +137 -0
  171. sqlassert-0.3.1/tests/dialect_warehouses.sqltest +169 -0
  172. sqlassert-0.3.1/tests/duckdb_hydration.sqltest +8 -0
  173. sqlassert-0.3.1/tests/enumerated_domain.sqltest +547 -0
  174. sqlassert-0.3.1/tests/enumerated_domain_standalone.sqltest +13 -0
  175. sqlassert-0.3.1/tests/exactly_one_join.sqltest +675 -0
  176. sqlassert-0.3.1/tests/exhaustive_case.sqltest +158 -0
  177. sqlassert-0.3.1/tests/foreign_keys.sqltest +419 -0
  178. sqlassert-0.3.1/tests/foreign_keys_standalone.sqltest +55 -0
  179. sqlassert-0.3.1/tests/functional_dependencies.sqltest +151 -0
  180. sqlassert-0.3.1/tests/functional_dependencies_of_functions.sqltest +188 -0
  181. sqlassert-0.3.1/tests/functional_dependencies_standalone.sqltest +115 -0
  182. sqlassert-0.3.1/tests/join_cardinality.sqltest +48 -0
  183. sqlassert-0.3.1/tests/join_kinds.sqltest +215 -0
  184. sqlassert-0.3.1/tests/join_kinds_standalone.sqltest +17 -0
  185. sqlassert-0.3.1/tests/joins_in_bodies.sqltest +138 -0
  186. sqlassert-0.3.1/tests/limit.sqltest +37 -0
  187. sqlassert-0.3.1/tests/marker_accounting.sqltest +58 -0
  188. sqlassert-0.3.1/tests/not_null.sqltest +303 -0
  189. sqlassert-0.3.1/tests/not_null_case_expressions.sqltest +44 -0
  190. sqlassert-0.3.1/tests/not_null_rejection.sqltest +225 -0
  191. sqlassert-0.3.1/tests/not_null_standalone.sqltest +16 -0
  192. sqlassert-0.3.1/tests/not_null_values.sqltest +83 -0
  193. sqlassert-0.3.1/tests/paulley_audit.sqltest +332 -0
  194. sqlassert-0.3.1/tests/propagation.sqltest +99 -0
  195. sqlassert-0.3.1/tests/qualify_by_partition.sqltest +37 -0
  196. sqlassert-0.3.1/tests/root_select_body.sqltest +79 -0
  197. sqlassert-0.3.1/tests/scalar_aggregates.sqltest +26 -0
  198. sqlassert-0.3.1/tests/scalar_subqueries.sqltest +108 -0
  199. sqlassert-0.3.1/tests/schema_amendments.sqltest +147 -0
  200. sqlassert-0.3.1/tests/schema_amendments_standalone.sqltest +33 -0
  201. sqlassert-0.3.1/tests/set_operations.sqltest +56 -0
  202. sqlassert-0.3.1/tests/soundness.sqltest +209 -0
  203. sqlassert-0.3.1/tests/soundness_oracle.py +816 -0
  204. sqlassert-0.3.1/tests/sqltest.py +395 -0
  205. sqlassert-0.3.1/tests/subqueries.sqltest +115 -0
  206. sqlassert-0.3.1/tests/test_analyze_aggregate_and_distinct.py +336 -0
  207. sqlassert-0.3.1/tests/test_analyze_cte_and_subquery.py +241 -0
  208. sqlassert-0.3.1/tests/test_analyze_enumerated_domain.py +53 -0
  209. sqlassert-0.3.1/tests/test_analyze_exactly_one_join.py +42 -0
  210. sqlassert-0.3.1/tests/test_analyze_exhaustive_case.py +104 -0
  211. sqlassert-0.3.1/tests/test_analyze_join_cardinality.py +66 -0
  212. sqlassert-0.3.1/tests/test_analyze_join_kinds.py +39 -0
  213. sqlassert-0.3.1/tests/test_analyze_joins_in_bodies.py +25 -0
  214. sqlassert-0.3.1/tests/test_analyze_marker_accounting.py +88 -0
  215. sqlassert-0.3.1/tests/test_analyze_not_null.py +128 -0
  216. sqlassert-0.3.1/tests/test_analyze_qualify_by_partition.py +193 -0
  217. sqlassert-0.3.1/tests/test_analyze_relation_facts.py +173 -0
  218. sqlassert-0.3.1/tests/test_analyze_scalar_subqueries.py +47 -0
  219. sqlassert-0.3.1/tests/test_analyze_set_operations.py +140 -0
  220. sqlassert-0.3.1/tests/test_analyze_soundness.py +76 -0
  221. sqlassert-0.3.1/tests/test_analyze_unique_join.py +22 -0
  222. sqlassert-0.3.1/tests/test_analyze_unique_set_assertion.py +62 -0
  223. sqlassert-0.3.1/tests/test_analyze_views.py +147 -0
  224. sqlassert-0.3.1/tests/test_check.py +376 -0
  225. sqlassert-0.3.1/tests/test_connection_warnings.py +102 -0
  226. sqlassert-0.3.1/tests/test_database_hydration.py +303 -0
  227. sqlassert-0.3.1/tests/test_dialect_certification.py +129 -0
  228. sqlassert-0.3.1/tests/test_dialects.py +67 -0
  229. sqlassert-0.3.1/tests/test_duckdb_hydration.py +193 -0
  230. sqlassert-0.3.1/tests/test_equality_groups.py +183 -0
  231. sqlassert-0.3.1/tests/test_ir_model.py +125 -0
  232. sqlassert-0.3.1/tests/test_ir_unions.py +24 -0
  233. sqlassert-0.3.1/tests/test_main.py +8 -0
  234. sqlassert-0.3.1/tests/test_module_boundaries.py +59 -0
  235. sqlassert-0.3.1/tests/test_mysql_catalog.py +127 -0
  236. sqlassert-0.3.1/tests/test_oracle_plugin.py +92 -0
  237. sqlassert-0.3.1/tests/test_postgres_catalog.py +234 -0
  238. sqlassert-0.3.1/tests/test_property_functions.py +18 -0
  239. sqlassert-0.3.1/tests/test_python_engine.py +229 -0
  240. sqlassert-0.3.1/tests/test_smells.py +170 -0
  241. sqlassert-0.3.1/tests/test_soundness_oracle.py +633 -0
  242. sqlassert-0.3.1/tests/test_sql_parse.py +342 -0
  243. sqlassert-0.3.1/tests/test_sqltest.py +155 -0
  244. sqlassert-0.3.1/tests/test_suggest.py +330 -0
  245. sqlassert-0.3.1/tests/test_suggest_improvements.py +611 -0
  246. sqlassert-0.3.1/tests/test_trino_catalog.py +74 -0
  247. sqlassert-0.3.1/tests/test_warehouse_catalogs.py +279 -0
  248. sqlassert-0.3.1/tests/unique_join.sqltest +76 -0
  249. sqlassert-0.3.1/tests/unique_set_assertion.sqltest +133 -0
  250. sqlassert-0.3.1/tests/unique_set_declaration.sqltest +41 -0
  251. sqlassert-0.3.1/tests/unique_set_declaration_standalone.sqltest +10 -0
  252. sqlassert-0.3.1/tests/unsupported_statements.sqltest +32 -0
  253. sqlassert-0.3.1/tests/views.sqltest +40 -0
  254. sqlassert-0.3.1/tmp/clingo-rules-rebuild-assessment.md +577 -0
  255. sqlassert-0.3.1/tmp/sqlassert-clingo-grilling-handoff.md +335 -0
  256. sqlassert-0.3.1/tmp/try.py +14 -0
  257. sqlassert-0.3.1/uv.lock +253 -0
  258. sqlassert-0.1.2/.gitignore +0 -4
  259. sqlassert-0.1.2/PKG-INFO +0 -173
  260. sqlassert-0.1.2/README.md +0 -162
  261. sqlassert-0.1.2/pyproject.toml +0 -24
  262. sqlassert-0.1.2/sqlassert/__init__.py +0 -13
  263. sqlassert-0.1.2/sqlassert/unique.py +0 -605
  264. sqlassert-0.1.2/sqlassert.egg-info/PKG-INFO +0 -173
  265. sqlassert-0.1.2/sqlassert.egg-info/SOURCES.txt +0 -13
  266. sqlassert-0.1.2/sqlassert.egg-info/requires.txt +0 -5
  267. sqlassert-0.1.2/tests/test_unique_join.py +0 -310
  268. sqlassert-0.1.2/unique-join-dbt-package-handoff.md +0 -119
  269. {sqlassert-0.1.2 → sqlassert-0.3.1}/setup.cfg +0 -0
  270. {sqlassert-0.1.2 → sqlassert-0.3.1}/sqlassert.egg-info/dependency_links.txt +0 -0
  271. {sqlassert-0.1.2 → sqlassert-0.3.1}/sqlassert.egg-info/top_level.txt +0 -0
@@ -0,0 +1,126 @@
1
+ name: CI
2
+
3
+ # Tests on every push and pull request. Publishing (see the jobs at the end)
4
+ # happens only on a push to this repository, never on a pull request:
5
+ # - a push to main publishes a dev build (X.Y.Z.devN) to TestPyPI;
6
+ # - a tag vX.Y.Z publishes to PyPI (vX.Y.ZrcN: a pre-release, which pip
7
+ # installs only with --pre) and creates a GitHub release.
8
+ # No token is stored: PyPI trusts this workflow through OIDC (Trusted
9
+ # Publishing), configured on pypi.org and test.pypi.org for both projects.
10
+ on:
11
+ push:
12
+ branches: [main]
13
+ tags: ["v*"]
14
+ pull_request:
15
+
16
+ permissions:
17
+ contents: read
18
+
19
+ jobs:
20
+ # The sqlassert package alone: dbt is never installed here.
21
+ sqlassert:
22
+ runs-on: ubuntu-latest
23
+ steps:
24
+ - uses: actions/checkout@v4
25
+ with:
26
+ fetch-depth: 0 # setuptools-scm reads the version from git
27
+ - uses: actions/setup-python@v5
28
+ with:
29
+ python-version: "3.13"
30
+ - run: pip install -e ".[test,dev]"
31
+ - run: python -m pytest -q
32
+ - run: ty check sqlassert tests
33
+ - run: python -m pytest --soundness-oracle -q
34
+
35
+ # The dbt plugin (integrations/dbt) against this checkout's sqlassert, with
36
+ # dbt-duckdb to compile its test project.
37
+ dbt-plugin:
38
+ runs-on: ubuntu-latest
39
+ defaults:
40
+ run:
41
+ working-directory: integrations/dbt
42
+ steps:
43
+ - uses: actions/checkout@v4
44
+ with:
45
+ fetch-depth: 0
46
+ - uses: actions/setup-python@v5
47
+ with:
48
+ python-version: "3.13"
49
+ - run: pip install -e ../.. -e ".[test]" ty
50
+ - run: python -m pytest -q
51
+ - run: ty check
52
+
53
+ # Both distributions, versioned by setuptools-scm from the git tag (or
54
+ # X.Y.Z.devN, the commits since the last tag, on main).
55
+ build:
56
+ if: github.event_name == 'push'
57
+ needs: [sqlassert, dbt-plugin]
58
+ runs-on: ubuntu-latest
59
+ steps:
60
+ - uses: actions/checkout@v4
61
+ with:
62
+ fetch-depth: 0
63
+ - uses: actions/setup-python@v5
64
+ with:
65
+ python-version: "3.13"
66
+ - run: pip install build twine
67
+ - run: python -m build --outdir dist
68
+ - run: python -m build --outdir dist integrations/dbt
69
+ - run: twine check --strict dist/*
70
+ - uses: actions/upload-artifact@v4
71
+ with:
72
+ name: dist
73
+ path: dist/
74
+
75
+ publish-testpypi:
76
+ if: github.event_name == 'push' && github.ref == 'refs/heads/main'
77
+ needs: build
78
+ runs-on: ubuntu-latest
79
+ environment:
80
+ name: testpypi
81
+ url: https://test.pypi.org/p/sqlassert
82
+ permissions:
83
+ id-token: write # OIDC token for Trusted Publishing; nothing else
84
+ steps:
85
+ - uses: actions/download-artifact@v4
86
+ with:
87
+ name: dist
88
+ path: dist/
89
+ - uses: pypa/gh-action-pypi-publish@release/v1
90
+ with:
91
+ repository-url: https://test.pypi.org/legacy/
92
+
93
+ publish-pypi:
94
+ if: github.event_name == 'push' && startsWith(github.ref, 'refs/tags/v')
95
+ needs: build
96
+ runs-on: ubuntu-latest
97
+ environment:
98
+ name: pypi
99
+ url: https://pypi.org/p/sqlassert
100
+ permissions:
101
+ id-token: write
102
+ steps:
103
+ - uses: actions/download-artifact@v4
104
+ with:
105
+ name: dist
106
+ path: dist/
107
+ - uses: pypa/gh-action-pypi-publish@release/v1
108
+
109
+ github-release:
110
+ needs: publish-pypi
111
+ runs-on: ubuntu-latest
112
+ permissions:
113
+ contents: write
114
+ steps:
115
+ - uses: actions/download-artifact@v4
116
+ with:
117
+ name: dist
118
+ path: dist/
119
+ - env:
120
+ GH_TOKEN: ${{ github.token }}
121
+ TAG: ${{ github.ref_name }}
122
+ run: |
123
+ prerelease=""
124
+ if [[ "$TAG" =~ (a|b|rc)[0-9]+$ ]]; then prerelease="--prerelease"; fi
125
+ gh release create "$TAG" dist/* --repo "$GITHUB_REPOSITORY" \
126
+ --title "$TAG" --generate-notes $prerelease
@@ -0,0 +1,9 @@
1
+ .vscode
2
+ *egg-info
3
+ build/
4
+ dist/
5
+ tmp
6
+ papers/
7
+ vendor/
8
+ __pycache__/
9
+ docs/issues/mkt/
@@ -0,0 +1,33 @@
1
+ # Agent Guidelines
2
+
3
+ ## No backwards compatibility while in alpha
4
+
5
+ sqlassert is alpha. Code simplicity wins over compatibility:
6
+
7
+ - Replace mechanisms outright; do not keep old and new side by side.
8
+ - No feature flags, shims, deprecation paths, or fallback branches for old
9
+ behavior. They are an anchor on every later change.
10
+ - Delete superseded code, tests, and docs in the same change.
11
+
12
+ The one contract to keep stable is the user-facing one: the Property Markers
13
+ (`/**ASSERT ...**/`, `/**DECLARE ...**/`) and the Properties they introduce.
14
+ Internal representation (IR, analysis functions, rules) may change freely.
15
+
16
+ ## Analysis works without a connection
17
+
18
+ `analyze(sql)` must work from the SQL alone ([ADR 0005](docs/adr/0005-analyze-sql-without-a-connection.md)).
19
+ Whenever a connection would have helped (a table with no `CREATE`, an unknown
20
+ column type, an ambiguous column), emit a warning Diagnostic saying what is
21
+ missing and how to supply it, and treat the affected facts as unknown; never
22
+ guess. With a connection (`analyze_database`), resolve it from the catalog. A
23
+ connection may only add proofs, never change what the SQL alone proves.
24
+
25
+ ## Where things are
26
+
27
+ - Domain language: `CONTEXT.md`. Use its terms; it lists terms to avoid.
28
+ - Implementation conventions: `docs/implementation-conventions.md`.
29
+ - Rule style: `sqlassert/rules/STYLEGUIDE.md`.
30
+ - Test style: SQL cases in `tests/*.sqltest` (format in `tests/sqltest.py`;
31
+ see "Tests" in `docs/implementation-conventions.md`).
32
+ - Open work: `docs/issues/` (index in `docs/issues/README.md`).
33
+ - Architecture decisions: `docs/adr/`.
@@ -0,0 +1,3 @@
1
+ @AGENTS.md
2
+
3
+ Never add Claude attribution to commits or PRs: no `Co-Authored-By: Claude` trailer, no "Generated with Claude Code" line.
@@ -0,0 +1,254 @@
1
+ # SQL Assertion Analysis
2
+
3
+ This context describes static claims about SQL queries and the evidence used to establish them without inspecting table data.
4
+
5
+ ## Language
6
+
7
+ **Assertion**:
8
+ A request from a SQL author to validate that a Property holds, like an
9
+ assertion in a programming language. An Assertion is a proof obligation: it
10
+ does not make the Property true, and remains Unknown when no proof is available.
11
+ _Avoid_: Property Declaration, assumption, annotation
12
+
13
+ **Property Declaration**:
14
+ A trusted statement from a SQL author that a Property is true. A Property
15
+ Declaration places that Property directly among the program's declarations,
16
+ contributes Knowledge without requiring proof, and does not produce an
17
+ assertion result.
18
+ _Avoid_: Assertion, Proof, Create Statement
19
+
20
+ **Join Cardinality**:
21
+ Which inputs' rows a join can repeat, read in FROM order (the left input is
22
+ everything before the join, the right its joined source). A row of one input
23
+ appears once per matching row of the other, or once padded.
24
+ - **Many To One Join**: no left row is repeated: each matches at most one right row.
25
+ - **One To Many Join**: no right row is repeated.
26
+ - **One To One Join**: neither.
27
+ - **Unique Join** (`UNIQUE`, `NOT MANY TO MANY`): rows of at most one input
28
+ are repeated; the join is not many to many.
29
+ A SEMI or ANTI join has no Join Cardinality: it outputs no right rows.
30
+ _Avoid_: Unique join marker, duplicate check, 1:N
31
+
32
+ **Fan Trap**, **Chasm Trap**:
33
+ An aggregate whose argument's rows a join may repeat (a Smell). A Fan Trap
34
+ is through a join that is not proved to be a Unique Join; a Chasm Trap through
35
+ a One To Many (or Many To One) Join whose argument is on the side with one row
36
+ per key.
37
+
38
+ **Exactly One Join Assertion**:
39
+ An assertion that every row of a join's left-hand input matches exactly one
40
+ right-hand row, so the join neither multiplies nor removes left rows and a left
41
+ join null-extends none of them. It combines a Many To One Join (at most one match)
42
+ with guaranteed matching (at least one): an ON made only of equalities between
43
+ left and right columns, non-null left columns, and Referential Coverage of the
44
+ left columns, as one tuple, onto the right columns they are paired with.
45
+ _Avoid_: Lookup join, mandatory join, 1:1 join
46
+
47
+ **Exhaustive Case Assertion**:
48
+ An assertion that a simple `CASE` has an explicit `WHEN` arm for every value
49
+ of its operand's Enumerated Domain, so no non-null operand value reaches
50
+ `ELSE`. `ELSE` never counts as an arm; a NULL operand is left to `NOT NULL`.
51
+ _Avoid_: Complete case, total match
52
+
53
+ **Unique Set Assertion**:
54
+ An assertion that a named set of a Select Expression's output columns forms a Unique Set, or, when asserted as a key, a Candidate Key.
55
+ _Avoid_: Unique constraint, primary key marker, uniqueness check
56
+
57
+ **Property Marker**:
58
+ The comment syntax an author writes to introduce an Assertion or Property
59
+ Declaration. An Assertion Marker means “please validate that this is true”; a
60
+ Property Declaration Marker means “I state that this is true.” The marker is
61
+ syntax, while the Assertion or Property Declaration determines how its Property
62
+ enters reasoning. A comment shaped like a marker but not recognized as one is
63
+ reported rather than ignored, because a marker its author believed in is worse
64
+ than no marker at all.
65
+ _Avoid_: Assertion, Property Declaration, annotation, hint
66
+
67
+ **Property**:
68
+ A structural semantic statement about a relation or query construct. The same
69
+ Property types are used in two ways: an Assertion wraps one as a query, while a
70
+ Property Declaration lists one as accepted Knowledge. A public Analysis
71
+ Function evaluates a Property.
72
+ _Avoid_: Rule, flag
73
+
74
+ **Analysis Function**:
75
+ A computation over the semantic program that returns an Analysis Result. A
76
+ public Analysis Function evaluates a Property; an internal one derives a value
77
+ needed by another Analysis Function.
78
+ _Avoid_: Rule, property
79
+
80
+ **Analysis Result**:
81
+ The value computed by one Analysis Function together with the evidence for that
82
+ value. Its evidence is represented directly by its reason, subjects, premises,
83
+ and members rather than by a separate evidence object.
84
+ _Avoid_: Proof, property
85
+
86
+ **Result Value**:
87
+ The computed value inside an Analysis Result. It can be a Boolean property
88
+ answer or a derived value used only during analysis.
89
+ _Avoid_: Property, proof
90
+
91
+ **Evidence**:
92
+ The reasons and prior Analysis Results that justify an Analysis Result's value.
93
+ A Proof is Evidence for a Boolean Result Value that is true.
94
+ _Avoid_: Result Value, assertion
95
+
96
+ **Proof**:
97
+ A true Boolean Analysis Result whose Evidence establishes that a Property
98
+ follows from known facts and inference rules.
99
+ _Avoid_: Successful check, guess
100
+
101
+ **Unknown**:
102
+ An outcome meaning that available knowledge is insufficient to prove or disprove an assertion.
103
+ _Avoid_: Invalid, false
104
+
105
+ **Disproof**:
106
+ Evidence establishing that an assertion is false, rather than merely lacking a proof.
107
+ _Avoid_: Unknown, validation failure
108
+
109
+ **Diagnostic**:
110
+ An explicit report that part of a SQL program could not be analyzed, such as an unsupported statement, a duplicate declaration, or an assertion the analysis never reached. A diagnostic describes a limit of the analysis rather than a property of the query, and is never a Disproof.
111
+ _Avoid_: Error, warning, validation failure, unknown
112
+
113
+ **Connection Warning**:
114
+ A Diagnostic reported apart, in `Report.warnings`, when the SQL alone lacks a fact a database connection would supply: a table with no `CREATE`, or an unqualified column more than one undeclared source may own. The affected facts are unknown; the warning names the fix. It never fails a Report, and `analyze_database` emits none ([ADR 0005](docs/adr/0005-analyze-sql-without-a-connection.md)). `Report.warnings` also carries the other facts the SQL leaves open: a key or foreign key the dialect does not enforce (`unenforced-constraint`; `RELY` or `trust_unenforced_keys`, on by default, makes it a Property Declaration, never Knowledge, and then no warning is reported), an operator whose meaning a setting decides (`ambiguous-operator`: MySQL's `||`), a comparison of a `CAST` of a timestamp that keeps its zone (`zoned-timestamp-cast`: Trino's), and a comparison of a composite column or `CAST` whose elements may be NULL (`composite-comparison`). The last three are about the SQL itself, so `analyze_database` keeps them.
115
+
116
+ **Non-Null Elements**:
117
+ A Property of composite columns (ARRAY, ROW, MAP): no element of a listed
118
+ column's value is NULL, nested ones included (`NOT NULL ELEMENTS`). The value
119
+ itself may be NULL. No catalog states it; a comparison of composites is
120
+ non-null only when it holds for each composite operand.
121
+ _Avoid_: Non-null array, element constraint
122
+
123
+ **Grouping Key**:
124
+ A scalar expression whose value identifies a group produced by aggregation. The complete set of grouping keys uniquely identifies rows produced by an ordinary aggregation.
125
+ _Avoid_: Aggregate predicate
126
+
127
+ **Aggregate Expression**:
128
+ An expression that computes one value from the rows in a group, such as `sum(amount)`.
129
+ _Avoid_: Aggregate predicate, grouping key
130
+
131
+ **QualifyByPartition**:
132
+ A relational operation that retains rows according to a window-derived position within each partition. It establishes uniqueness only when its condition guarantees at most one retained row per partition.
133
+ _Avoid_: Deduplicate
134
+
135
+ **Relation Expression**:
136
+ A semantic operation that produces a relation with its own output columns.
137
+ **Relation Operation** is a synonym used when focusing specifically on the
138
+ topmost operation of the expression.
139
+ _Avoid_: Relation definition, relation instance, plan
140
+
141
+ **Named Relation**:
142
+ The reusable meaning declared by a table, view, or CTE, shared by every reference to that declaration.
143
+ _Avoid_: Relation definition, alias
144
+
145
+ **Alias**:
146
+ One occurrence of a relation expression in a query scope, including an occurrence whose name is implicit.
147
+ _Avoid_: Relation instance, named relation
148
+
149
+ **Output Column**:
150
+ One named value produced by one relation expression. A pass-through operation produces a fresh Output Column referring to the upstream one.
151
+ _Avoid_: Column instance
152
+
153
+ **Column Reference**:
154
+ A scalar expression that reads an upstream Output Column.
155
+ _Avoid_: Input column, column identifier
156
+
157
+ **Origin**:
158
+ The SQL location or catalog object from which a semantic element or piece of knowledge arose.
159
+ _Avoid_: SQLGlot node
160
+
161
+ **Finding**:
162
+ What `sqlassert check` reports for one SQL Program: an `UNKNOWN` assertion
163
+ (coded `unproved-` and its marker, e.g. `unproved-unique`) or a Diagnostic (error level, failing the check), or a Connection Warning (warning
164
+ level, never failing it). A Finding is located at its Origin's span, when it
165
+ has one: the Property Marker as written.
166
+ _Avoid_: Violation, failure, lint
167
+
168
+ **Knowledge**:
169
+ Properties accepted as true for inference. Knowledge may come from a catalog,
170
+ an explicit Property Declaration, query structure, or a proved Assertion. A
171
+ Property Declaration is therefore a source of Knowledge; an Assertion is a
172
+ query whose Property is not accepted as Knowledge unless proved. Knowledge is
173
+ the status and collection of accepted Properties, not a separate Property base
174
+ type. It contains no SQL definitions or parser structures.
175
+ _Avoid_: View SQL, database connection, SQLGlot AST
176
+
177
+ **Unique Set**:
178
+ A set of relation columns whose fully non-null value combinations cannot repeat. Rows containing null in the set are not guaranteed to be distinct from one another.
179
+ _Avoid_: Candidate key
180
+
181
+ **Derived Unique Set**:
182
+ A Unique Set established for one Relation Expression by functional dependencies rather than stated directly: its columns determine the relation's Row Identity, possibly through Unique Sets and dependencies of upstream Relation Expressions.
183
+ _Avoid_: Propagated key
184
+
185
+ **Non-Null Unique Set**:
186
+ A Unique Set whose columns are all non-null, so every row has a distinct, complete value combination.
187
+ _Avoid_: Candidate key, nullable unique set
188
+
189
+ **Referential Coverage**:
190
+ A Property between ordered referencing columns of one relation and equally
191
+ many ordered referenced columns of another, paired by position: every row whose
192
+ referencing columns are all non-null has a referenced row equal to it on the
193
+ paired columns. A row with any null referencing component is exempt (SQL
194
+ `MATCH SIMPLE`). Knowledge -- a `REFERENCES` constraint, a catalog foreign key,
195
+ or a Foreign Key Property Declaration -- supplies Referential Coverage only.
196
+ _Avoid_: Inclusion dependency, reference check
197
+
198
+ **Foreign Key**:
199
+ Referential Coverage onto a Unique Set of the referenced Named Relation. Each
200
+ referencing row with no null component therefore matches exactly one referenced
201
+ row. The Unique Set must be proved or declared on its own; declaring a Foreign
202
+ Key never vouches for it.
203
+ _Avoid_: Reference, relationship, join key
204
+
205
+ **Functional Dependency**:
206
+ A Property of a relation, written `X → Y` (`DETERMINES`): any two rows that agree on the columns X also agree on the columns Y, for every database the schema allows. Strict when NULL counts as agreeing with NULL; lax when it holds only on rows whose X columns are all non-null.
207
+ _Avoid_: Correlation, derived column
208
+
209
+ **Enumerated Domain**:
210
+ A Property of one column: every non-null value it takes belongs to a finite set
211
+ of literals, for every database the schema allows. It says nothing about which
212
+ values occur (occurrence), and NULL is outside it, governed by `NOT NULL`
213
+ (nullability). It is a structural claim sqlassert proves or is told, not a
214
+ database enum type, though a catalog's enum type could supply it as Knowledge.
215
+ _Avoid_: Enum type, value set, check constraint
216
+
217
+ **Equality Group**:
218
+ An analysis-only set of one relation's output columns that carry the same value on every row: merged by strict equalities (`WHERE` and inner-join `ON` `a = b`) between known matching comparison types, never by an outer join's `ON`. Implicit casts and collations do not establish value identity. Stronger than a pair of Functional Dependencies, which an injective `a = f(b)` also satisfies. Value facts such as Enumerated Domains and Foreign Keys read it; uniqueness does not.
219
+ _Avoid_: Equivalence class, alias set
220
+
221
+ **Null Source**:
222
+ A reason an output column may be NULL: organically (a nullable source column, an expression whose nullability is not modelled, a NULL literal) or because an outer join padded its row. A column is non-null when it has no Null Source; a null-rejecting condition on a padded column proves its join matched and removes that padding from every column it padded.
223
+ _Avoid_: Nullable flag, null-extension
224
+
225
+ **Row Identity**:
226
+ An analysis-only value that tells apart every row of one Relation Expression, duplicates included. A column set is a Unique Set when it determines the relation's Row Identity. Never appears in the IR, in SQL, or to authors.
227
+ _Avoid_: rowid, ι, row number
228
+
229
+ **Select Expression**:
230
+ The `SELECT` query body common to a Root Select, a view or CTE definition, and a subquery, independent of how it is introduced into the program.
231
+ _Avoid_: Select-like expression, query, statement
232
+
233
+ **Attachment Site**:
234
+ A place in the SQL text where a Property Marker can be written: right before a
235
+ JOIN, right after the last token of a Select Expression, or right before a
236
+ simple `CASE`. Every marker attaches at one; `suggest` names each site by its
237
+ line and what it marks.
238
+ _Avoid_: Marker position, anchor
239
+
240
+ **Improvement**:
241
+ A rewrite of the SQL itself, at one Attachment Site, that a Proof shows leaves
242
+ the program's result unchanged as a multiset: an outer join that cannot keep a
243
+ padded row written as an inner join, a join that is redundant or only filters,
244
+ a redundant `DISTINCT` or grouping column, an unused CTE. `suggest` ranks
245
+ Improvements (High level) before markers to paste (Mid level).
246
+ _Avoid_: Optimization, lint, hint
247
+
248
+ **Create Statement**:
249
+ A top-level SQL declaration that introduces a Named Relation or related schema Knowledge into the analyzed program.
250
+ _Avoid_: Root select
251
+
252
+ **Root Select**:
253
+ The optional, single top-level select expression analyzed by a SQL program. It uses the same select-expression form as the body of a view definition but is not introduced by a create statement. A program of views alone has none: each view is analyzed as a query of its own.
254
+ _Avoid_: Main view, implicit view
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Fred Israel
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.