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.
- sqlassert-0.3.1/.github/workflows/ci.yml +126 -0
- sqlassert-0.3.1/.gitignore +9 -0
- sqlassert-0.3.1/AGENTS.md +33 -0
- sqlassert-0.3.1/CLAUDE.md +3 -0
- sqlassert-0.3.1/CONTEXT.md +254 -0
- sqlassert-0.3.1/LICENSE +21 -0
- sqlassert-0.3.1/PKG-INFO +725 -0
- sqlassert-0.3.1/README.md +695 -0
- sqlassert-0.3.1/case.sql +8 -0
- sqlassert-0.3.1/docs/adr/0001-isolate-clingo-as-the-property-engine.md +7 -0
- sqlassert-0.3.1/docs/adr/0002-use-a-framework-independent-ir.md +3 -0
- sqlassert-0.3.1/docs/adr/0003-encode-the-ir-in-clingo.md +251 -0
- sqlassert-0.3.1/docs/adr/0004-use-python-analysis-functions-for-property-inference.md +3 -0
- sqlassert-0.3.1/docs/adr/0005-analyze-sql-without-a-connection.md +3 -0
- sqlassert-0.3.1/docs/calcite-audit.md +243 -0
- sqlassert-0.3.1/docs/calcite-notes.md +72 -0
- sqlassert-0.3.1/docs/dialect-provenance.json +685 -0
- sqlassert-0.3.1/docs/dialects.md +308 -0
- sqlassert-0.3.1/docs/functional-dependencies.md +617 -0
- sqlassert-0.3.1/docs/implementation-conventions.md +277 -0
- sqlassert-0.3.1/docs/issues/0011-fuzz-test-analyze-against-a-real-world-sql-corpus.md +40 -0
- sqlassert-0.3.1/docs/issues/0015-suggest-provable-properties-for-unannotated-sql.md +278 -0
- sqlassert-0.3.1/docs/issues/0023-close-ir-lowering-gaps.md +200 -0
- sqlassert-0.3.1/docs/issues/0027-soundness-oracle-against-duckdb.md +168 -0
- sqlassert-0.3.1/docs/issues/0037-close-the-paulley-audit-missing-rules.md +44 -0
- sqlassert-0.3.1/docs/issues/0039-smaller-lowering-gaps.md +25 -0
- sqlassert-0.3.1/docs/issues/0042-lowering-support-v2.md +38 -0
- sqlassert-0.3.1/docs/issues/0043-external-integrations.md +91 -0
- sqlassert-0.3.1/docs/issues/0045-close-the-calcite-audit-missing-proofs.md +119 -0
- sqlassert-0.3.1/docs/issues/0046-certify-dialect-facts.md +70 -0
- sqlassert-0.3.1/docs/issues/0049-collation-aware-properties.md +42 -0
- sqlassert-0.3.1/docs/issues/0050-dbt-catalog-json.md +32 -0
- sqlassert-0.3.1/docs/issues/0051-validate-snowflake-against-a-real-engine.md +30 -0
- sqlassert-0.3.1/docs/issues/0052-validate-bigquery-against-a-real-engine.md +31 -0
- sqlassert-0.3.1/docs/issues/0053-validate-databricks-against-a-real-engine.md +31 -0
- sqlassert-0.3.1/docs/issues/0054-validate-redshift-against-a-real-engine.md +30 -0
- sqlassert-0.3.1/docs/issues/README.md +80 -0
- sqlassert-0.3.1/docs/issues/done/0001-spec-rewrite-the-property-engine-around-a-relational-ir-and.md +156 -0
- sqlassert-0.3.1/docs/issues/done/0002-prove-one-declared-unique-join-through-clingo.md +39 -0
- sqlassert-0.3.1/docs/issues/done/0003-prove-composite-and-nullable-unique-sets.md +37 -0
- sqlassert-0.3.1/docs/issues/done/0004-propagate-uniqueness-through-relation-instances-filters-and.md +36 -0
- sqlassert-0.3.1/docs/issues/done/0005-analyze-cte-and-from-subquery-plans.md +40 -0
- sqlassert-0.3.1/docs/issues/done/0006-expand-create-views-with-provenance-and-cycle-detection.md +36 -0
- sqlassert-0.3.1/docs/issues/done/0007-derive-uniqueness-from-aggregate-and-distinct.md +37 -0
- sqlassert-0.3.1/docs/issues/done/0008-derive-uniqueness-from-qualifybypartition.md +36 -0
- sqlassert-0.3.1/docs/issues/done/0009-return-conservative-diagnostics-for-the-complete-mvp-boundar.md +66 -0
- sqlassert-0.3.1/docs/issues/done/0010-cut-over-to-analyze-and-retire-the-legacy-analyzer.md +38 -0
- sqlassert-0.3.1/docs/issues/done/0012-add-unique-set-assertions-for-select-expressions-unique-prim.md +80 -0
- sqlassert-0.3.1/docs/issues/done/0013-refactoring.md +160 -0
- sqlassert-0.3.1/docs/issues/done/0014-hydrate-analysis-from-database-catalogs-with-recursive-view.md +106 -0
- sqlassert-0.3.1/docs/issues/done/0016-assert-and-declare-string-enum-domains.md +43 -0
- sqlassert-0.3.1/docs/issues/done/0017-complete-typed-enum-support.md +37 -0
- sqlassert-0.3.1/docs/issues/done/0018-assert-exhaustive-simple-case-expressions.md +32 -0
- sqlassert-0.3.1/docs/issues/done/0019-assert-and-declare-direct-foreign-keys.md +34 -0
- sqlassert-0.3.1/docs/issues/done/0020-propagate-foreign-keys-through-queries.md +31 -0
- sqlassert-0.3.1/docs/issues/done/0021-assert-simple-exactly-one-joins.md +34 -0
- sqlassert-0.3.1/docs/issues/done/0022-complete-exactly-one-reasoning.md +34 -0
- sqlassert-0.3.1/docs/issues/done/0024-functional-dependency-reasoning.md +105 -0
- sqlassert-0.3.1/docs/issues/done/0025-check-rule-consistency-invariants.md +21 -0
- sqlassert-0.3.1/docs/issues/done/0026-equality-groups.md +47 -0
- sqlassert-0.3.1/docs/issues/done/0028-analyze-join-markers-inside-cte-bodies.md +43 -0
- sqlassert-0.3.1/docs/issues/done/0029-enum-string-literals-may-coincide.md +40 -0
- sqlassert-0.3.1/docs/issues/done/0030-read-enum-types-and-collations-from-the-catalog.md +43 -0
- sqlassert-0.3.1/docs/issues/done/0031-enumerated-domains-through-set-operations.md +38 -0
- sqlassert-0.3.1/docs/issues/done/0032-infer-simple-case-nullability.md +33 -0
- sqlassert-0.3.1/docs/issues/done/0033-inner-join-suggestion-across-join-chains-and-ctes.md +42 -0
- sqlassert-0.3.1/docs/issues/done/0034-close-soundness-oracle-blind-spots.md +51 -0
- sqlassert-0.3.1/docs/issues/done/0035-audit-fd-propagation-against-paulley.md +50 -0
- sqlassert-0.3.1/docs/issues/done/0036-refactor-tests-for-corpus-and-oracle-scale.md +193 -0
- sqlassert-0.3.1/docs/issues/done/0038-lower-order-by-and-select-list-subqueries.md +125 -0
- sqlassert-0.3.1/docs/issues/done/0040-audit-inference-against-calcite.md +118 -0
- sqlassert-0.3.1/docs/issues/done/0041-join-cardinality-markers-and-fan-trap-smell.md +161 -0
- sqlassert-0.3.1/docs/issues/done/0044-v1-external-integrations.md +108 -0
- sqlassert-0.3.1/docs/issues/done/0047-discovery-base-class.md +52 -0
- sqlassert-0.3.1/docs/issues/done/0048-dbt-plugin-package.md +43 -0
- sqlassert-0.3.1/docs/literature-notes.md +127 -0
- sqlassert-0.3.1/docs/mvp-scope.md +38 -0
- sqlassert-0.3.1/docs/references.md +67 -0
- sqlassert-0.3.1/integrations/dbt/LICENSE +21 -0
- sqlassert-0.3.1/integrations/dbt/README.md +129 -0
- sqlassert-0.3.1/integrations/dbt/pyproject.toml +58 -0
- sqlassert-0.3.1/integrations/dbt/sqlassert_dbt/__init__.py +19 -0
- sqlassert-0.3.1/integrations/dbt/sqlassert_dbt/__main__.py +115 -0
- sqlassert-0.3.1/integrations/dbt/sqlassert_dbt/expectations.py +336 -0
- sqlassert-0.3.1/integrations/dbt/sqlassert_dbt/jinja.py +183 -0
- sqlassert-0.3.1/integrations/dbt/sqlassert_dbt/manifest.py +254 -0
- sqlassert-0.3.1/integrations/dbt/sqlassert_dbt/program.py +686 -0
- sqlassert-0.3.1/integrations/dbt/sqlassert_dbt/project.py +340 -0
- sqlassert-0.3.1/integrations/dbt/sqlassert_dbt/report.py +158 -0
- sqlassert-0.3.1/integrations/dbt/tests/conftest.py +16 -0
- sqlassert-0.3.1/integrations/dbt/tests/dbt_project/dbt_project.yml +5 -0
- sqlassert-0.3.1/integrations/dbt/tests/dbt_project/models/active_customers.sql +4 -0
- sqlassert-0.3.1/integrations/dbt/tests/dbt_project/models/customer_keys.sql +4 -0
- sqlassert-0.3.1/integrations/dbt/tests/dbt_project/models/customer_orders.sql +6 -0
- sqlassert-0.3.1/integrations/dbt/tests/dbt_project/models/customer_signups.sql +2 -0
- sqlassert-0.3.1/integrations/dbt/tests/dbt_project/models/customers.sql +12 -0
- sqlassert-0.3.1/integrations/dbt/tests/dbt_project/models/orders_appended.sql +7 -0
- sqlassert-0.3.1/integrations/dbt/tests/dbt_project/models/orders_incremental.sql +8 -0
- sqlassert-0.3.1/integrations/dbt/tests/dbt_project/models/schema.yml +85 -0
- sqlassert-0.3.1/integrations/dbt/tests/dbt_project/models/stg_customers.sql +3 -0
- sqlassert-0.3.1/integrations/dbt/tests/dbt_project/models/stg_orders.sql +2 -0
- sqlassert-0.3.1/integrations/dbt/tests/dbt_project/profiles.yml +7 -0
- sqlassert-0.3.1/integrations/dbt/tests/dbt_project/seeds/raw_payments.csv +2 -0
- sqlassert-0.3.1/integrations/dbt/tests/dbt_runs.py +26 -0
- sqlassert-0.3.1/integrations/dbt/tests/test_contracts.py +56 -0
- sqlassert-0.3.1/integrations/dbt/tests/test_dbt.py +561 -0
- sqlassert-0.3.1/join.sql +6 -0
- sqlassert-0.3.1/left.sql +10 -0
- sqlassert-0.3.1/poetry.lock +359 -0
- sqlassert-0.3.1/pyproject.toml +62 -0
- sqlassert-0.3.1/scripts/certify_dialects.py +661 -0
- {sqlassert-0.1.2 → sqlassert-0.3.1}/scripts/publish-pypi.sh +1 -0
- sqlassert-0.3.1/sqlassert/__init__.py +38 -0
- sqlassert-0.3.1/sqlassert/__main__.py +138 -0
- sqlassert-0.3.1/sqlassert/check.py +444 -0
- sqlassert-0.3.1/sqlassert/diagnostics.py +57 -0
- sqlassert-0.3.1/sqlassert/dialects.py +211 -0
- sqlassert-0.3.1/sqlassert/discovery/__init__.py +30 -0
- sqlassert-0.3.1/sqlassert/discovery/bigquery.py +71 -0
- sqlassert-0.3.1/sqlassert/discovery/databricks.py +18 -0
- sqlassert-0.3.1/sqlassert/discovery/duckdb.py +163 -0
- sqlassert-0.3.1/sqlassert/discovery/fetcher.py +402 -0
- sqlassert-0.3.1/sqlassert/discovery/information_schema.py +326 -0
- sqlassert-0.3.1/sqlassert/discovery/mysql.py +70 -0
- sqlassert-0.3.1/sqlassert/discovery/postgres.py +143 -0
- sqlassert-0.3.1/sqlassert/discovery/redshift.py +36 -0
- sqlassert-0.3.1/sqlassert/discovery/snowflake.py +63 -0
- sqlassert-0.3.1/sqlassert/discovery/trino.py +37 -0
- sqlassert-0.3.1/sqlassert/improvements.py +528 -0
- sqlassert-0.3.1/sqlassert/ir/__init__.py +56 -0
- sqlassert-0.3.1/sqlassert/ir/convert.py +2377 -0
- sqlassert-0.3.1/sqlassert/ir/model.py +676 -0
- sqlassert-0.3.1/sqlassert/main.py +57 -0
- sqlassert-0.3.1/sqlassert/properties.py +121 -0
- sqlassert-0.3.1/sqlassert/provenance.py +73 -0
- sqlassert-0.3.1/sqlassert/python_engine.py +299 -0
- sqlassert-0.3.1/sqlassert/reporting.py +102 -0
- sqlassert-0.3.1/sqlassert/rules/STYLEGUIDE.md +19 -0
- sqlassert-0.3.1/sqlassert/rules/__init__.py +11 -0
- sqlassert-0.3.1/sqlassert/rules/column_set.py +34 -0
- sqlassert-0.3.1/sqlassert/rules/domain.py +376 -0
- sqlassert-0.3.1/sqlassert/rules/equality.py +233 -0
- sqlassert-0.3.1/sqlassert/rules/exactly_one.py +75 -0
- sqlassert-0.3.1/sqlassert/rules/foreign_key.py +316 -0
- sqlassert-0.3.1/sqlassert/rules/join_cardinality.py +94 -0
- sqlassert-0.3.1/sqlassert/rules/nullability.py +418 -0
- sqlassert-0.3.1/sqlassert/rules/uniqueness.py +601 -0
- sqlassert-0.3.1/sqlassert/smells.py +306 -0
- sqlassert-0.3.1/sqlassert/sql_parse.py +974 -0
- sqlassert-0.3.1/sqlassert/suggest.py +339 -0
- sqlassert-0.3.1/sqlassert.egg-info/PKG-INFO +725 -0
- sqlassert-0.3.1/sqlassert.egg-info/SOURCES.txt +258 -0
- sqlassert-0.3.1/sqlassert.egg-info/entry_points.txt +2 -0
- sqlassert-0.3.1/sqlassert.egg-info/requires.txt +11 -0
- sqlassert-0.3.1/sqlassert.egg-info/scm_file_list.json +254 -0
- sqlassert-0.3.1/sqlassert.egg-info/scm_version.json +8 -0
- sqlassert-0.3.1/tests/aggregate_and_distinct.sqltest +100 -0
- sqlassert-0.3.1/tests/calcite_audit.sqltest +72 -0
- sqlassert-0.3.1/tests/calcite_audit_iq.sqltest +262 -0
- sqlassert-0.3.1/tests/calcite_audit_keys.sqltest +448 -0
- sqlassert-0.3.1/tests/calcite_audit_nulls.sqltest +379 -0
- sqlassert-0.3.1/tests/composite_unique_sets.sqltest +138 -0
- sqlassert-0.3.1/tests/conftest.py +98 -0
- sqlassert-0.3.1/tests/connection_warnings.sqltest +9 -0
- sqlassert-0.3.1/tests/cte_and_subquery.sqltest +25 -0
- sqlassert-0.3.1/tests/database_hydration.sqltest +13 -0
- sqlassert-0.3.1/tests/dialect_duckdb.sqltest +57 -0
- sqlassert-0.3.1/tests/dialect_mysql.sqltest +91 -0
- sqlassert-0.3.1/tests/dialect_postgres.sqltest +81 -0
- sqlassert-0.3.1/tests/dialect_trino.sqltest +137 -0
- sqlassert-0.3.1/tests/dialect_warehouses.sqltest +169 -0
- sqlassert-0.3.1/tests/duckdb_hydration.sqltest +8 -0
- sqlassert-0.3.1/tests/enumerated_domain.sqltest +547 -0
- sqlassert-0.3.1/tests/enumerated_domain_standalone.sqltest +13 -0
- sqlassert-0.3.1/tests/exactly_one_join.sqltest +675 -0
- sqlassert-0.3.1/tests/exhaustive_case.sqltest +158 -0
- sqlassert-0.3.1/tests/foreign_keys.sqltest +419 -0
- sqlassert-0.3.1/tests/foreign_keys_standalone.sqltest +55 -0
- sqlassert-0.3.1/tests/functional_dependencies.sqltest +151 -0
- sqlassert-0.3.1/tests/functional_dependencies_of_functions.sqltest +188 -0
- sqlassert-0.3.1/tests/functional_dependencies_standalone.sqltest +115 -0
- sqlassert-0.3.1/tests/join_cardinality.sqltest +48 -0
- sqlassert-0.3.1/tests/join_kinds.sqltest +215 -0
- sqlassert-0.3.1/tests/join_kinds_standalone.sqltest +17 -0
- sqlassert-0.3.1/tests/joins_in_bodies.sqltest +138 -0
- sqlassert-0.3.1/tests/limit.sqltest +37 -0
- sqlassert-0.3.1/tests/marker_accounting.sqltest +58 -0
- sqlassert-0.3.1/tests/not_null.sqltest +303 -0
- sqlassert-0.3.1/tests/not_null_case_expressions.sqltest +44 -0
- sqlassert-0.3.1/tests/not_null_rejection.sqltest +225 -0
- sqlassert-0.3.1/tests/not_null_standalone.sqltest +16 -0
- sqlassert-0.3.1/tests/not_null_values.sqltest +83 -0
- sqlassert-0.3.1/tests/paulley_audit.sqltest +332 -0
- sqlassert-0.3.1/tests/propagation.sqltest +99 -0
- sqlassert-0.3.1/tests/qualify_by_partition.sqltest +37 -0
- sqlassert-0.3.1/tests/root_select_body.sqltest +79 -0
- sqlassert-0.3.1/tests/scalar_aggregates.sqltest +26 -0
- sqlassert-0.3.1/tests/scalar_subqueries.sqltest +108 -0
- sqlassert-0.3.1/tests/schema_amendments.sqltest +147 -0
- sqlassert-0.3.1/tests/schema_amendments_standalone.sqltest +33 -0
- sqlassert-0.3.1/tests/set_operations.sqltest +56 -0
- sqlassert-0.3.1/tests/soundness.sqltest +209 -0
- sqlassert-0.3.1/tests/soundness_oracle.py +816 -0
- sqlassert-0.3.1/tests/sqltest.py +395 -0
- sqlassert-0.3.1/tests/subqueries.sqltest +115 -0
- sqlassert-0.3.1/tests/test_analyze_aggregate_and_distinct.py +336 -0
- sqlassert-0.3.1/tests/test_analyze_cte_and_subquery.py +241 -0
- sqlassert-0.3.1/tests/test_analyze_enumerated_domain.py +53 -0
- sqlassert-0.3.1/tests/test_analyze_exactly_one_join.py +42 -0
- sqlassert-0.3.1/tests/test_analyze_exhaustive_case.py +104 -0
- sqlassert-0.3.1/tests/test_analyze_join_cardinality.py +66 -0
- sqlassert-0.3.1/tests/test_analyze_join_kinds.py +39 -0
- sqlassert-0.3.1/tests/test_analyze_joins_in_bodies.py +25 -0
- sqlassert-0.3.1/tests/test_analyze_marker_accounting.py +88 -0
- sqlassert-0.3.1/tests/test_analyze_not_null.py +128 -0
- sqlassert-0.3.1/tests/test_analyze_qualify_by_partition.py +193 -0
- sqlassert-0.3.1/tests/test_analyze_relation_facts.py +173 -0
- sqlassert-0.3.1/tests/test_analyze_scalar_subqueries.py +47 -0
- sqlassert-0.3.1/tests/test_analyze_set_operations.py +140 -0
- sqlassert-0.3.1/tests/test_analyze_soundness.py +76 -0
- sqlassert-0.3.1/tests/test_analyze_unique_join.py +22 -0
- sqlassert-0.3.1/tests/test_analyze_unique_set_assertion.py +62 -0
- sqlassert-0.3.1/tests/test_analyze_views.py +147 -0
- sqlassert-0.3.1/tests/test_check.py +376 -0
- sqlassert-0.3.1/tests/test_connection_warnings.py +102 -0
- sqlassert-0.3.1/tests/test_database_hydration.py +303 -0
- sqlassert-0.3.1/tests/test_dialect_certification.py +129 -0
- sqlassert-0.3.1/tests/test_dialects.py +67 -0
- sqlassert-0.3.1/tests/test_duckdb_hydration.py +193 -0
- sqlassert-0.3.1/tests/test_equality_groups.py +183 -0
- sqlassert-0.3.1/tests/test_ir_model.py +125 -0
- sqlassert-0.3.1/tests/test_ir_unions.py +24 -0
- sqlassert-0.3.1/tests/test_main.py +8 -0
- sqlassert-0.3.1/tests/test_module_boundaries.py +59 -0
- sqlassert-0.3.1/tests/test_mysql_catalog.py +127 -0
- sqlassert-0.3.1/tests/test_oracle_plugin.py +92 -0
- sqlassert-0.3.1/tests/test_postgres_catalog.py +234 -0
- sqlassert-0.3.1/tests/test_property_functions.py +18 -0
- sqlassert-0.3.1/tests/test_python_engine.py +229 -0
- sqlassert-0.3.1/tests/test_smells.py +170 -0
- sqlassert-0.3.1/tests/test_soundness_oracle.py +633 -0
- sqlassert-0.3.1/tests/test_sql_parse.py +342 -0
- sqlassert-0.3.1/tests/test_sqltest.py +155 -0
- sqlassert-0.3.1/tests/test_suggest.py +330 -0
- sqlassert-0.3.1/tests/test_suggest_improvements.py +611 -0
- sqlassert-0.3.1/tests/test_trino_catalog.py +74 -0
- sqlassert-0.3.1/tests/test_warehouse_catalogs.py +279 -0
- sqlassert-0.3.1/tests/unique_join.sqltest +76 -0
- sqlassert-0.3.1/tests/unique_set_assertion.sqltest +133 -0
- sqlassert-0.3.1/tests/unique_set_declaration.sqltest +41 -0
- sqlassert-0.3.1/tests/unique_set_declaration_standalone.sqltest +10 -0
- sqlassert-0.3.1/tests/unsupported_statements.sqltest +32 -0
- sqlassert-0.3.1/tests/views.sqltest +40 -0
- sqlassert-0.3.1/tmp/clingo-rules-rebuild-assessment.md +577 -0
- sqlassert-0.3.1/tmp/sqlassert-clingo-grilling-handoff.md +335 -0
- sqlassert-0.3.1/tmp/try.py +14 -0
- sqlassert-0.3.1/uv.lock +253 -0
- sqlassert-0.1.2/.gitignore +0 -4
- sqlassert-0.1.2/PKG-INFO +0 -173
- sqlassert-0.1.2/README.md +0 -162
- sqlassert-0.1.2/pyproject.toml +0 -24
- sqlassert-0.1.2/sqlassert/__init__.py +0 -13
- sqlassert-0.1.2/sqlassert/unique.py +0 -605
- sqlassert-0.1.2/sqlassert.egg-info/PKG-INFO +0 -173
- sqlassert-0.1.2/sqlassert.egg-info/SOURCES.txt +0 -13
- sqlassert-0.1.2/sqlassert.egg-info/requires.txt +0 -5
- sqlassert-0.1.2/tests/test_unique_join.py +0 -310
- sqlassert-0.1.2/unique-join-dbt-package-handoff.md +0 -119
- {sqlassert-0.1.2 → sqlassert-0.3.1}/setup.cfg +0 -0
- {sqlassert-0.1.2 → sqlassert-0.3.1}/sqlassert.egg-info/dependency_links.txt +0 -0
- {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,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,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
|
sqlassert-0.3.1/LICENSE
ADDED
|
@@ -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.
|