cockroachdb / cockroachdb/cockroach
v26.3: pg_dump aborts with "definition of view appears to be empty" on tables whose PL/pgSQL trigger body contains DML
- Dominant language
- Go
- Stars
- 32.5k
- Forks
- 4.1k
- PR merge metrics
- PR metrics pending
Description
## Summary
On CockroachDB v26.3.1, creating a trigger whose PL/pgSQL function body contains DML (e.g. `INSERT ... ON CONFLICT`) causes a bogus `_RETURN` entry to appear in `pg_rewrite` for the underlying **table**. PostgreSQL uses `_RETURN` rewrite rules to mark views, so PostgreSQL 18 `pg_dump` (with the new v26.3 `pg_dump_compatibility` support) misclassifies the table as a view and aborts:
```
pg_dump: error: definition of view "pos" appears to be empty (length zero)
```
This aborts the entire dump, not just the affected table.
## Reproduction
Server: `cockroachdb/cockroach:v26.3.1` (single-node, `--insecure`)
Client: `pg_dump (PostgreSQL) 18.6`
```sql
CREATE DATABASE repro;
CREATE TABLE repro.public.bal (id STRING PRIMARY KEY, n INT);
CREATE TABLE repro.public.pos (id STRING PRIMARY KEY, n INT);
CREATE FUNCTION repro.public.f_dml() RETURNS TRIGGER AS $$
BEGIN
INSERT INTO bal (id, n) VALUES ((NEW).id, 1)
ON CONFLICT (id) DO UPDATE SET n = bal.n + 1;
RETURN NEW;
END
$$ LANGUAGE PLpgSQL;
CREATE TRIGGER pos_ai AFTER INSERT ON repro.public.pos
FOR EACH ROW EXECUTE FUNCTION repro.public.f_dml();
```
A `_RETURN` rewrite rule now exists for a plain table:
```sql
SELECT oid, rulename, ev_class::regclass::string AS ev_class, ev_type
FROM repro.public.pg_rewrite;
-- oid | rulename | ev_class | ev_type
-- 2920291571 | _RETURN | pos | 1
```
Then:
```bash
pg_dump "postgresql://root@localhost:26257/repro?sslmode=disable" --table=pos --schema-only
# NOTICE: setting pg_dump_compatibility = "cockroachdb" because application_name is "pg_dump"
# pg_dump: error: definition of view "pos" appears to be empty (length zero)
```
`pg_get_viewdef('pos'::regclass)` correctly returns NULL for the table, which is exactly what trips pg_dump's fatal check once the `_RETURN` rule makes it look like a view.
## Expected behavior
`pg_rewrite` should only expose `_RETURN` rules for actual views, and pg_dump should dump the table normally.
## Additional observations
- A trigger function whose body contains **no DML** (`BEGIN RETURN NEW; END`) does not produce the spurious `pg_rewrite` row, and pg_dump succeeds.
- The failure is identical with `pg_dump_compatibility=postgres`. With `=off`, pg_dump fails earlier with `schema with OID 101 does not exist` (possibly a separate catalog fixup gap).
- Related restore-side friction found in the same workflow: stock pg_dump plain output sets `check_function_bodies = false`, but CockroachDB still validates PL/pgSQL bodies at `CREATE FUNCTION` time. Since pg_dump emits `CREATE FUNCTION` before the tables the body references, restoring such a dump fails with `ERROR: relation "..." does not exist` unless the dump is reordered. Honoring deferred body validation would make stock pg_dump output restorable.
## Impact
Any schema using PL/pgSQL trigger functions with DML bodies (a common pattern for maintaining derived/balance tables) cannot be dumped at all with the documented v26.3 pg_dump support.
## Environment
- CockroachDB CCL v26.3.1 (aarch64, single-node local container)
- pg_dump (PostgreSQL) 18.6
Jira issue: CRDB-67763
Epic CRDB-65516
Contributor guide
Research direction
Start by running the supplied SQL reproduction and inspecting pg_rewrite for the spurious _RETURN rule on the plain table. Trace the pg_dump_compatibility path involved in PostgreSQL 18 pg_dump, then verify that only actual views expose _RETURN rules and that schema-only pg_dump completes successfully.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- go, postgresql, sql
- Domain
- databases
- Issue type
- Bug
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Active
- Clarity
- Mostly clear
- Newbie friendliness
- 45/100