sqlassert 0.1.1__tar.gz → 0.1.2__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.
@@ -0,0 +1,4 @@
1
+ .vscode
2
+ *egg-info
3
+ build/
4
+ dist/
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: sqlassert
3
- Version: 0.1.1
3
+ Version: 0.1.2
4
4
  Summary: Generate SQL assertions from unique join markers.
5
5
  Requires-Python: >=3.10
6
6
  Description-Content-Type: text/markdown
@@ -1,10 +1,10 @@
1
1
  [build-system]
2
- requires = ["setuptools>=68"]
2
+ requires = ["setuptools>=68", "setuptools-scm>=8"]
3
3
  build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "sqlassert"
7
- version = "0.1.1"
7
+ dynamic = ["version"]
8
8
  description = "Generate SQL assertions from unique join markers."
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.10"
@@ -20,3 +20,5 @@ test = [
20
20
 
21
21
  [tool.pytest.ini_options]
22
22
  testpaths = ["tests"]
23
+
24
+ [tool.setuptools_scm]
@@ -0,0 +1,34 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+
4
+ cd "$(dirname "$0")/.."
5
+
6
+ python3 -m pip install --upgrade ".[test]" build twine
7
+ python3 -m pytest
8
+
9
+ if [[ -n "$(git status --porcelain)" ]]; then
10
+ echo "Refusing to publish a dirty worktree. Commit or stash changes first." >&2
11
+ exit 1
12
+ fi
13
+
14
+ if git describe --exact-match --tags --match 'v[0-9]*.[0-9]*.[0-9]*' HEAD >/dev/null 2>&1; then
15
+ VERSION_TAG="$(git describe --exact-match --tags --match 'v[0-9]*.[0-9]*.[0-9]*' HEAD)"
16
+ echo "Publishing $VERSION_TAG"
17
+ else
18
+ LATEST_TAG="$(git tag --list 'v[0-9]*.[0-9]*.[0-9]*' --sort=-v:refname | head -n 1)"
19
+ if [[ -z "$LATEST_TAG" ]]; then
20
+ VERSION_TAG="v0.1.1"
21
+ else
22
+ VERSION="${LATEST_TAG#v}"
23
+ IFS=. read -r MAJOR MINOR PATCH <<< "$VERSION"
24
+ VERSION_TAG="v$MAJOR.$MINOR.$((PATCH + 1))"
25
+ fi
26
+
27
+ git tag "$VERSION_TAG"
28
+ echo "Tagged $VERSION_TAG"
29
+ fi
30
+
31
+ rm -rf dist
32
+ python3 -m build
33
+ python3 -m twine check dist/*
34
+ python3 -m twine upload --repository pypi dist/* --verbose
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: sqlassert
3
- Version: 0.1.1
3
+ Version: 0.1.2
4
4
  Summary: Generate SQL assertions from unique join markers.
5
5
  Requires-Python: >=3.10
6
6
  Description-Content-Type: text/markdown
@@ -1,5 +1,8 @@
1
+ .gitignore
1
2
  README.md
2
3
  pyproject.toml
4
+ unique-join-dbt-package-handoff.md
5
+ scripts/publish-pypi.sh
3
6
  sqlassert/__init__.py
4
7
  sqlassert/unique.py
5
8
  sqlassert.egg-info/PKG-INFO
@@ -0,0 +1,119 @@
1
+ # Handoff: dbt Unique Join Assertion Package
2
+
3
+ ## Next Session Focus
4
+
5
+ Start a brand new package that lets dbt users mark joins as uniqueness assertions without changing production SQL. The marker should compile to a SQL comment, and an external parser/test runner should inspect compiled SQL, find marked joins, generate assertion queries, and execute them against DuckDB.
6
+
7
+ ## Conversation Summary
8
+
9
+ The user is working on a DuckDB `UNIQUE JOIN` idea. In core DuckDB, the current branch already has a core implementation shape involving:
10
+
11
+ - `JoinRef::is_unique`
12
+ - libpg_query/Bison grammar support for `UNIQUE JOIN`
13
+ - binder checks that prove RHS uniqueness
14
+ - tests under `/Users/fred/src/duckdb/test/sql/join/unique/test_unique_join.test`
15
+
16
+ We investigated whether DuckDB's new experimental PEG parser could make this distributable as an extension. The conclusion was no, not cleanly. The local DuckDB checkout has PEG parser code in `extension/autocomplete`, including `enable_peg_parser`, but it registers a full parser override rather than exposing a small public API for extensions to inject one join modifier and continue through normal binding. Parser extensibility alone also does not provide the binder semantics needed for `UNIQUE JOIN`.
17
+
18
+ The user rejected distributing a DuckDB fork because adoption would be poor. We pivoted to a dbt-package strategy.
19
+
20
+ ## Agreed Direction
21
+
22
+ Use dbt templating only as a marker mechanism. The marker should always render a valid SQL comment, not special SQL syntax.
23
+
24
+ Example model SQL:
25
+
26
+ ```sql
27
+ from sessions s
28
+ {{ u }} left join users u
29
+ on s.user_id = u.id
30
+ ```
31
+
32
+ Compiled SQL:
33
+
34
+ ```sql
35
+ /* unique_join */
36
+ left join users u
37
+ on s.user_id = u.id
38
+ ```
39
+
40
+ Then a separate custom parser/test runner:
41
+
42
+ 1. Reads dbt compiled SQL from `target/compiled`.
43
+ 2. Finds `/* unique_join */` comments.
44
+ 3. Parses the following valid DuckDB join.
45
+ 4. Infers the RHS and key columns from the join condition.
46
+ 5. Generates assertion queries, e.g. group by RHS key and fail if duplicates exist.
47
+ 6. Runs those queries against DuckDB.
48
+ 7. Reports failures back as package/test output.
49
+
50
+ Important: DuckDB itself should never see custom syntax in the package path. The SQL remains valid DuckDB because the marker is only a comment.
51
+
52
+ ## Design Preferences From User
53
+
54
+ - Keep `u` literally just a marker.
55
+ - Do not make dbt parse or solve anything.
56
+ - Avoid `u()` or `u(...)` unless there is a compelling reason; the user explicitly pushed back on extra macro metadata.
57
+ - Heavy work belongs in the external parser/test runner.
58
+ - The package should be adoptable and not require a DuckDB fork or custom DuckDB extension.
59
+
60
+ ## Parser Recommendation
61
+
62
+ Start with SQLGlot in Python:
63
+
64
+ - It has a DuckDB dialect.
65
+ - It exposes an AST.
66
+ - It is easier to distribute in a dbt/Python package context.
67
+
68
+ Alternatives discussed:
69
+
70
+ - `sqlparser-rs`: viable in Rust, has `DuckDbDialect`, but may be more work for source-span/comment-adjacent logic.
71
+ - SQLFluff: supports DuckDB and templating workflows, but likely too heavy and lint-oriented for the semantic rewrite engine.
72
+ - pglast: not recommended because DuckDB syntax differs enough from PostgreSQL.
73
+
74
+ ## Suggested V1 Scope
75
+
76
+ Be strict:
77
+
78
+ - Only support joins immediately preceded by the marker comment.
79
+ - Only support equality predicates in `ON`, joined by `AND`.
80
+ - Only infer RHS key columns from simple predicates like `lhs.col = rhs.col`.
81
+ - Fail loudly on `OR`, non-equality joins, functions, arbitrary expressions, anti/semi joins, or ambiguous aliases.
82
+ - Generate simple duplicate checks against the RHS relation/subquery.
83
+
84
+ For example:
85
+
86
+ ```sql
87
+ select id, count(*) as n
88
+ from users
89
+ group by id
90
+ having count(*) > 1
91
+ limit 1
92
+ ```
93
+
94
+ For RHS subqueries, wrap the RHS in a CTE before checking duplicates.
95
+
96
+ ## Files/Refs Mentioned
97
+
98
+ - DuckDB branch/workspace: `/Users/fred/src/duckdb`
99
+ - Current branch: `ujoin`
100
+ - User test file with current local changes: `/Users/fred/src/duckdb/test/sql/join/unique/test_unique_join.test`
101
+ - Core AST flag observed at: `/Users/fred/src/duckdb/src/include/duckdb/parser/tableref/joinref.hpp`
102
+ - DuckDB binder uniqueness checks observed at: `/Users/fred/src/duckdb/src/planner/binder/tableref/bind_joinref.cpp`
103
+ - DuckDB PEG/autocomplete parser override observed at: `/Users/fred/src/duckdb/extension/autocomplete/autocomplete_extension.cpp`
104
+ - SQLGlot docs: https://sqlglot.com/sqlglot.html
105
+ - SQLGlot DuckDB dialect docs: https://sqlglot.com/sqlglot/dialects/duckdb.html
106
+
107
+ ## Suggested Skills
108
+
109
+ - `prototype`: use if the next session wants to quickly build a runnable parser/test-runner proof of concept before committing to package structure.
110
+ - `design-an-interface`: use if the next session wants to compare package APIs, CLI shapes, or marker/comment conventions.
111
+ - `spreadsheets`/`documents` are not relevant unless the user asks for package docs or planning artifacts.
112
+
113
+ ## Open Questions For Next Session
114
+
115
+ - Package language: Python/dbt package only, or Python CLI plus dbt macros?
116
+ - Marker spelling: `/* unique_join */`, `/* ujoin */`, or something namespaced like `/* dbt_unique_join */`.
117
+ - How the tool discovers database connection settings: reuse dbt profiles, accept a DuckDB path, or run through dbt invocation.
118
+ - Whether V1 should support CTE/RHS subqueries or only base relations.
119
+ - How failures should be surfaced: stdout only, dbt-compatible test result artifacts, or generated dbt generic tests.
File without changes
File without changes
File without changes