Skip to content

feat: disable check_function_bodies when running a plan - #312

Draft
dilame wants to merge 1 commit into
stripe:mainfrom
dilame:fix/disable-check-function-bodies
Draft

dilame wants to merge 1 commit into
stripe:mainfrom
dilame:fix/disable-check-function-bodies

Conversation

@dilame

@dilame dilame commented Sep 26, 2026

Copy link
Copy Markdown

What changed

A plan now runs with check_function_bodies disabled, in all three places a plan executes:

  • plan validation against the temporary database (executeStatementsIgnoreTimeouts, which also seeds the temporary database);
  • the apply command;
  • the runnable SQL script the plan command prints, which starts with SET check_function_bodies = false;.

Why

PostgreSQL validates a routine's body at CREATE time: the body validator resolves the relations, composite types, domains and so on that the body names. None of those body references are recorded in pg_depend — verified directly:

CREATE TABLE s.t(id int);
CREATE FUNCTION s.f() RETURNS int LANGUAGE plpgsql AS
  $$ DECLARE v s.t; BEGIN RETURN 1; END $$;
-- pg_depend for s.f has only pg_namespace and pg_language rows: no pg_type edge

So a plan cannot order them, and one that creates a relation and a routine whose body names that relation failed validation:

CREATE OR REPLACE FUNCTION ... RETURNS SETOF ...
DECLARE v_answer deposit.event__qualifier_answered__bank;
: ERROR: type "deposit.event__qualifier_answered__bank" does not exist

This is exactly why pg_dump emits SET check_function_bodies = false at the start of a dump, and the same reasoning applies here. Ordering the routine after every relation instead is not an option: it closes a cycle whenever a domain's CHECK calls the routine while a table column is typed with that domain (routine → table → domain → routine).

The declarative DDL schema source keeps the check on: there the file order is the author's, so a bad body reference is theirs to see.

Validation

go test ./...

New acceptance case: a plpgsql function whose body declares a variable of a table row type, with the table created in the same plan. The plan orders the function first, as it must — the body reference is untrackable — and applies because the session has the check disabled. The plan command's golden SQL output asserts the new preamble.

PostgreSQL validates a routine's body at CREATE time, resolving the relations,
composite types, domains and so on that the body names. It records none of those
body references in pg_depend, so a plan cannot order them: a plan that creates a
relation and a routine whose body names that relation's row type failed
validation.

Disable the check for the plan's session, exactly as pg_dump does when it emits
`SET check_function_bodies = false` at the start of a dump. Covered in the three
places a plan runs: plan validation against the temporary database, the `apply`
command, and the SQL script the `plan` command prints.
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