sqlassert 0.1.0__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.
- sqlassert-0.1.2/.gitignore +4 -0
- {sqlassert-0.1.0 → sqlassert-0.1.2}/PKG-INFO +1 -1
- {sqlassert-0.1.0 → sqlassert-0.1.2}/pyproject.toml +4 -2
- sqlassert-0.1.2/scripts/publish-pypi.sh +34 -0
- {sqlassert-0.1.0 → sqlassert-0.1.2}/sqlassert.egg-info/PKG-INFO +1 -1
- {sqlassert-0.1.0 → sqlassert-0.1.2}/sqlassert.egg-info/SOURCES.txt +3 -0
- sqlassert-0.1.2/unique-join-dbt-package-handoff.md +119 -0
- {sqlassert-0.1.0 → sqlassert-0.1.2}/README.md +0 -0
- {sqlassert-0.1.0 → sqlassert-0.1.2}/setup.cfg +0 -0
- {sqlassert-0.1.0 → sqlassert-0.1.2}/sqlassert/__init__.py +0 -0
- {sqlassert-0.1.0 → sqlassert-0.1.2}/sqlassert/unique.py +0 -0
- {sqlassert-0.1.0 → sqlassert-0.1.2}/sqlassert.egg-info/dependency_links.txt +0 -0
- {sqlassert-0.1.0 → sqlassert-0.1.2}/sqlassert.egg-info/requires.txt +0 -0
- {sqlassert-0.1.0 → sqlassert-0.1.2}/sqlassert.egg-info/top_level.txt +0 -0
- {sqlassert-0.1.0 → sqlassert-0.1.2}/tests/test_unique_join.py +0 -0
|
@@ -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
|
-
|
|
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
|
|
@@ -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
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|