Skip to content

Worktree pgdevkit testdb#1

Merged
aersam merged 16 commits into
mainfrom
worktree-pgdevkit-testdb
Jul 16, 2026
Merged

Worktree pgdevkit testdb#1
aersam merged 16 commits into
mainfrom
worktree-pgdevkit-testdb

Conversation

@aersam

@aersam aersam commented Jul 16, 2026

Copy link
Copy Markdown
Contributor

No description provided.

aersam and others added 16 commits July 15, 2026 15:47
Implements ProjectConfig dataclass and load_config() function for reading
project configuration from pyproject.toml [tool.pgdevkit] section, with
sensible defaults for name, database_dir, env_prefix, extensions, and root.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015bpLodYTShC96vqY6gqsQy
Implement query.execute() to run SQL statements against a PostgreSQL
database via async connection, handling multiple statements and
returning rows from the final statement if it produced any.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code
Ports the database-in-source-layout SQL walker and .test_data.json
seeding from the postgres-test-setup skill's start_postgres.py,
parameterized by database_dir instead of a module constant.
_iter_sql_files checked file.parent.parent.name == "tables" to detect
table files, but for the documented database/{schema}/tables/{name}.sql
layout the parent is "tables" and the grandparent is the schema name --
backwards. This meant the table-dependency-tracking branch never fired,
so delivered_tables/all_declared stayed empty and files were applied in
plain filename order instead of FK dependency order.

Fixing the parent/grandparent check alone exposed a second bug: sqlglot's
exp.Table walk in _get_sql_deps also matches a CREATE TABLE statement's
own target, so every table file's dependency set includes itself. Once
dependency tracking actually activates, that self-reference can never be
satisfied, deadlocking the delayed-resolution loop. Fixed by discarding
the file's own qualified name from its computed deps before checking.

Also extracted the schema-name leading-digit-prefix stripping (already
present, correctly, in the .test_data.json seeding path) into a shared
_strip_layer_prefix() helper used by both the table-dependency branch and
the seeding path.

Added a regression fixture (widget -> widget_part -> widget_part_detail,
a 3-table FK chain whose alphabetical filename order matches its real
dependency order) and confirmed empirically that this specific fixture
raises UndefinedTable pre-fix and applies cleanly post-fix. A simpler
2-table fixture (gadget referencing widget, sorting before it
alphabetically) was tried first per a reviewer's suggestion but was found
to pass even against the unfixed code, due to a coincidental interaction
between the always-empty all_declared set and the delayed-list's
reverse-iteration order -- it does not distinguish correct dependency
resolution from the bug for any chain length where alphabetical order is
simply the reverse of dependency order.
The all-databases prefix pattern used the project's slug directly in a
SQL LIKE pattern without escaping "_", which LIKE treats as a
single-character wildcard. This let clean_testdb(all=True) for a
project like "app" also match and drop a differently-named project's
databases (e.g. "apps"), violating the "never touch another project's
databases" guarantee. Escape LIKE metacharacters in the prefix and add
a regression test with slug-colliding project names.
Replace tests/conftest.py's ad hoc docker-package container/DB bootstrap
with a call to pgdevkit.testdb.ensure_testdb(), so pgdevkit's own tests
use the same shared Podman-backed container as consumers of the testdb
CLI. Removes the now-unused docker dependency and static
PGDB_TEST_POSTGRES_* pytest_env entries. Pin [tool.pgdevkit] name =
"pgdevkit" explicitly so the env_prefix is deterministic regardless of
the checkout directory name (worktrees are named differently from the
main clone). Documents pgdb testdb usage in the README.
CREATE SCHEMA and CREATE FUNCTION parse to AST shapes under the pinned
sqlglot==30.11.0 that the existing name-extraction in parser.py doesn't
handle (Table.name resolves empty when the real name is in .db;
UserDefinedFunction.name doesn't unwrap a nested Table), causing spurious
"" schema and "public." function entries. Fix both by falling back to the
db-arg/nested-Table extraction pattern already used elsewhere in the file.

Also fix two more latent bugs of the same kind surfaced by the same test
run: _parse_table_constraint referenced exp.UniqueKey/exp.Unique, which
don't exist in this sqlglot version (real class is
UniqueColumnConstraint), so any table with a FOREIGN KEY or UNIQUE
constraint raised and was silently dropped entirely by the per-file
exception handler; and _norm_sql didn't strip the trailing ";" that
Postgres's pg_get_viewdef()/pg_get_indexdef() always add but sqlglot's
.sql() rendering never does, so every structurally-identical view compared
as a mismatch.

Finally, simplify the active_users view fixture so it no longer selects
users.email or filters on users.status -- Postgres refuses ALTER COLUMN
TYPE on any column referenced by a view, which blocked two test scenarios.

tests/test_compare.py goes from 5/9 failing to 9/9 passing; full suite
(39 tests) passes with no regressions.
ty check pgdevkit now passes:
- config.py: drop the dead tomli fallback (requires-python >= 3.14
  guarantees tomllib, already flagged as dead code in review)
- api.py: build CREATE/DROP DATABASE via psycopg.sql.SQL/Identifier
  instead of an f-string, matching psycopg's typed-query API
- query.py: cast the caller-provided SQL text to LiteralString with
  an explanatory comment — this function's whole purpose is running
  arbitrary SQL, so it can never itself be a literal

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015bpLodYTShC96vqY6gqsQy
@aersam
aersam merged commit 4922fe7 into main Jul 16, 2026
1 check passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant