Schema verify via information schema
Skill Ed3Design/ed3design-skill-bundles/schema-discipline/skills/schema-verify-via-information-schema
Claude Code skill bundles for software engineering: 56 skills + 5 Python tools + 6 hooks + 4 sub-agents across 6 thematic plugins (token-savers, code-quality, planning-disciplines, async-forensik, schema-discipline, skill-system-meta). Empirically TDD-validated patterns, MIT licensed.
npx -y skills add Ed3Design/ed3design-skill-bundles --skill schema-verify-via-information-schemaAssembled from the repository path, not quoted from the project. Check it against their README if it does not work.
One thing to look at
- 0 stars0 stars. Stars are a popularity signal and not a quality one, but at this level it is likely that nobody has read this closely except its author, and you would be relying on your own review.
What its author says it does
Copied from the file, not written here
Use when about to write or execute SQL against a PostgreSQL/TimescaleDB table whose exact schema (column names, types, deprecated-vs-active-variant) is not 100% certain from current context — especially in forensic debugging where multiple tables have similar names (`ohlcv_1d` vs `ohlcv_1d_hist`, `users` vs `users_v2`) or column names have shifted over time. Iron-Law: `SELECT column_name, data_type FROM information_schema.columns WHERE table_name=$1 ORDER BY ordinal_position` is the cheapest 30-second insurance against UndefinedColumn, false-positive-empty-result, and wrong-table-queried. Trigger on phrases like "writing a query against table X", "UndefinedColumn", "column X does not exist", "verify DB schema", "I think the column is called Y", "before any forensic query". Do NOT load for ORM-mediated queries (SQLAlchemy/Django catch drift at model-load), first-time-schema-design, catalog-queries on information_schema itself, or connection issues.
SKILL.md
7.6 KB, ~1.7k tokens by cl100k_base, as published. Nobody here has run it
Schema-Verify via information_schema
The Iron Law
Before any forensic SELECT/UPDATE/DELETE against a table whose schema is not certain from current context: run one
information_schema.columnsquery first. Cost: <100 ms + ~5 lines of code. Saved: typically 10-60 min of dead-end debugging or false-positive empty-result interpretations.
Why this matters
The naive flow:
- "I need the win rate. Query against
virtual_tradeswithopen_price." asyncpg.exceptions.UndefinedColumnError: column "open_price" does not exist- "Ah, maybe it's called
entry_price?" — retry, errors continue - 2-3 iterations, 10-15 min gone
Or worse:
- Query against
ohlcv_1dfor last 30 days. - 0 Rows returned.
- "The data pipeline is dead, build a new hypothesis!"
- 30 min debugging pipeline code.
- Turns out:
ohlcv_1dis deprecated, the active table isohlcv_1d_hist— it has 21 bars/symbol/30 days perfectly populated.
The real path:
- First query always:
SELECT column_name, data_type FROM information_schema.columns WHERE table_name=$1 ORDER BY ordinal_position - Look at the schema output (read 10-30 columns, 15 seconds)
- Write the real query with verified column names
- On false-empty-result: additionally
SELECT table_name FROM information_schema.tables WHERE table_schema='public' AND table_name LIKE '%<base>%'to see suffix variants
The 3-Step Procedure
Step 1 — Schema lookup as first DB operation
SELECT column_name, data_type
FROM information_schema.columns
WHERE table_name = 'my_table'
ORDER BY ordinal_position;
In Python/asyncpg:
cols = await conn.fetch(
"SELECT column_name, data_type FROM information_schema.columns "
"WHERE table_name=$1 ORDER BY ordinal_position",
table_name,
)
for c in cols:
print(f" {c['column_name']:30s} {c['data_type']}")
Look at the output. THEN build the production query.
Step 2 — On "empty" result: search for sibling tables
If an expected table is empty, before concluding pipeline death:
SELECT table_name
FROM information_schema.tables
WHERE table_schema = 'public'
AND table_name ILIKE '%my_table%'
ORDER BY table_name;
Discovered today: ohlcv_1d (deprecated, empty) vs ohlcv_1d_hist (active, full). Naming suffix _hist suggests "historical" → first intuition "backtest only". Wrong — it is the active daily yfinance source.
Step 3 — On "weird values" / type-error: check data_type
SELECT column_name, data_type, udt_name
FROM information_schema.columns
WHERE table_name=$1 AND column_name=$2;
Common pitfalls this catches:
timestampvstimestamptznumericvsdouble precision(Decimal-vs-float trap fromasyncpg-live-vs-mock-shape)textvsvarchar(N)with length limitjsonbvsjsonvstextwith JSON content- Custom-Enum-Type vs
varchar
Concrete examples (your-app forensic day)
| Symptom | Naive hypothesis | True finding | If skill had run first |
|---|---|---|---|
UndefinedColumn open_price in v3_trades | "wrong table name?" | column is called ko_price_at_signal | 15s schema read → right column immediately |
UndefinedColumn bar_time in ohlcv_1d | "schema bug?" | column is called time | 15s schema read |
ohlcv_1d 0 rows in 30d | "data pipeline dead!" | table is deprecated, active is ohlcv_1d_hist | tables-suffix-search → right table |
Anti-patterns
- ❌ "I remember the columns" — schema drifts over weeks, migrations are invisible
- ❌ "Empty result → pipeline dead" — before pipeline hypothesis: check sibling tables
- ❌ Schema lookup only on error — the bias is reversed: lookup is PREVENTION, not REACTION
- ❌
\din psql without programmatic use — in live debug loop you lose the output on the next statement; keep it programmatically in the fetch result - ❌ Read schema once and keep it for hours — in long sessions a parallel migration may run; for forensics always fresh
Quick template
# Standard header for EVERY new forensic session against unknown table:
async def forensic_query(conn, table, **filters):
# Step 1: Schema verify
cols = await conn.fetch(
"SELECT column_name, data_type FROM information_schema.columns "
"WHERE table_name=$1 ORDER BY ordinal_position", table,
)
if not cols:
# Step 2: Table does not exist — find siblings
siblings = await conn.fetch(
"SELECT table_name FROM information_schema.tables "
"WHERE table_schema='public' AND table_name ILIKE $1",
f"%{table}%",
)
raise ValueError(
f"Table '{table}' not found. Siblings: "
f"{[s['table_name'] for s in siblings]}"
)
schema = {c['column_name']: c['data_type'] for c in cols}
print(f"[{table}] Schema: {list(schema.keys())}")
# ... real query with verified columns
Skill composition
superpowers:systematic-debugging— runs BEFORE this skill for the wider "what is the bug" framingasyncpg-live-vs-mock-shape— runs AFTER this skill for the Mock-vs-Live-Type-Layer (separate concern: asyncpg-type-coercion)schema-use-case-mismatch-detection— runs IF schema is correct but NULL-pattern persists (different problem: semantic mismatch, not schema drift)decision-plan-hypothesis-matrix— runs AROUND this skill: schema-verify is one of the "distinguishing metrics" before hypothesis formulation
Anti-skill — when this is NOT the right tool
| Symptom | Right tool instead |
|---|---|
| Field is consistently NULL despite active writer | schema-use-case-mismatch-detection |
| asyncpg Decimal vs float TypeErrors | asyncpg-live-vs-mock-shape |
| First-time-schema-design (table doesn't exist) | migration design skills (n/a in current catalog) |
| ORM-mediated query (SQLAlchemy / Django) | ORM model introspection (different mechanism) |
When-Built / Why-Built
Built after 3 separate schema-drifts caught in a single forensic day on a production domain:
- Win-rate analysis (morning):
open_pricenot in v3_trades — schema drift since schema migration, correct column isko_price_at_signal - OHLCV-coverage query (afternoon):
bar_timenot in ohlcv_1d/_1h — correct column istime - ohlcv_1d lookup (afternoon): table is deprecated, active table is
ohlcv_1d_hist(suffix_histmisleading)
Each instance would have cost 10-30 min of dead-end if discovered through trial-and-error. With information_schema-First: ~15 seconds total overhead, zero dead-ends.
Promotion trigger: ≥3 further live applications in non-work sessions or in other projects (e.g. TimescaleDB-based monitoring).
What ships with it
Read from the repository
Just SKILL.md. No reference files, no scripts.