Skip to content

sql: reject volatility weakening when IMMUTABLE-required dependent exists#171491

Open
virajchogle wants to merge 2 commits into
cockroachdb:masterfrom
virajchogle:ctor-volatility-check-171486
Open

sql: reject volatility weakening when IMMUTABLE-required dependent exists#171491
virajchogle wants to merge 2 commits into
cockroachdb:masterfrom
virajchogle:ctor-volatility-check-171486

Conversation

@virajchogle

Copy link
Copy Markdown
Contributor

Problem

CREATE OR REPLACE FUNCTION silently accepted weakening a function's volatility (e.g. IMMUTABLEVOLATILE) even when an existing dependent still required IMMUTABLE. Reading the dependent rows afterward crashed in the optimizer with a nil-pointer dereference at logical_props_builder.go:1837.

Reproducer:

CREATE FUNCTION pwn(x INT) RETURNS INT LANGUAGE SQL IMMUTABLE AS $$ SELECT x $$;
CREATE TABLE t (pk INT PRIMARY KEY, x INT, c INT AS (pwn(x)) VIRTUAL);
INSERT INTO t VALUES (1, 10);
CREATE OR REPLACE FUNCTION pwn(x INT) RETURNS INT LANGUAGE SQL VOLATILE AS $$ SELECT x $$;
SELECT c FROM t;
-- pq: internal error: runtime error: invalid memory address or nil pointer dereference

Fix

Walk the function's dependents at CREATE OR REPLACE FUNCTION time. If any reference uses the function in an IMMUTABLE-required context (computed columns, expression indexes, CHECK constraints) and the new volatility is anything other than IMMUTABLE, reject the replacement with an error naming the dependent table.

Implemented in both schema changers (legacy and declarative). A defensive nil-check at the optbuilder's virtual-column projection turns the pre-existing nil-deref into a clear error for clusters that already reached the corrupt state.

PG comparison

Tested against PG 17.10 and PG 18.4 in real containers (not just docs):

  • PG 17.10 (STORED only): silently accepts the volatility flip; the SELECT returns the value computed at INSERT time.
  • PG 18.4 (VIRTUAL added): blocks the prerequisite — ERROR: generation expression uses user-defined function. DETAIL: Virtual generated columns that make use of user-defined functions are not yet supported.

PG sidesteps this scenario entirely. The error wording in this PR is CRDB-original. (More empirical detail in the issue thread.)

Test coverage

Tests live in pkg/sql/logictest/testdata/logic_test/udf_in_table and run across all default logictest configurations (legacy + declarative schema changers, multi-region, mixed-version 25.4/26.1/26.2, etc.). Cases covered:

  • Virtual computed column dependent → reject VOLATILE / STABLE / omitted-volatility
  • Same dependent → allow IMMUTABLE (strengthening is fine)
  • Stored computed column dependent → reject VOLATILE
  • CHECK constraint dependent → reject VOLATILE
  • Expression index dependent → reject VOLATILE
  • Function with no dependents → allow any volatility

Out of scope

Views and RLS policies. Their volatility ceilings are looser (STABLE-or-better) and folding them into the same check would balloon the diff. Can be addressed in a follow-up.


Fixes #171486

Epic: none

Release note (bug fix): CREATE OR REPLACE FUNCTION now rejects weakening a function's volatility when it is referenced by a computed column, expression index, or CHECK constraint.

…ists

CREATE OR REPLACE FUNCTION silently accepted weakening a function's volatility while computed columns, expression indexes, or CHECK constraints still required IMMUTABLE, crashing subsequent reads in the optimizer. Reject the replacement in both schema changers and add a nil-check at the virtual-column projection for already-corrupt clusters.

Fixes cockroachdb#171486
Epic: none

Release note (bug fix): CREATE OR REPLACE FUNCTION now rejects weakening a function's volatility when it is referenced by a computed column,expression index, or CHECK constraint.
@virajchogle virajchogle requested review from a team as code owners June 8, 2026 16:51
@virajchogle virajchogle requested review from ZhouXing19 and spilchen and removed request for a team June 8, 2026 16:51
@trunk-io

trunk-io Bot commented Jun 8, 2026

Copy link
Copy Markdown
Contributor

Merging to master in this repository is managed by Trunk.

  • To merge this pull request, check the box to the left or comment /trunk merge below.

After your PR is submitted to the merge queue, this comment will be automatically updated with its status. If the PR fails, failure details will also be posted here

@blathers-crl

blathers-crl Bot commented Jun 8, 2026

Copy link
Copy Markdown

Thank you for contributing to CockroachDB. Please ensure you have followed the guidelines for creating a PR.

My owl senses detect your PR is good for review. Please keep an eye out for any test failures in CI.

🦉 Hoot! I am a Blathers, a bot for CockroachDB. My owner is dev-inf.

@blathers-crl blathers-crl Bot added the O-community Originated from the community label Jun 8, 2026
@cockroach-teamcity

Copy link
Copy Markdown
Member

This change is Reviewable

@bghal bghal left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@bghal made 2 comments.
Reviewable status: :shipit: complete! 0 of 0 LGTMs obtained (waiting on spilchen, virajchogle, and ZhouXing19).


pkg/sql/create_function.go line 272 at r1 (raw file):

		return err
	}
	if err := validateVolatilityForDependents(params.ctx, params.p, udfDesc); err != nil {

Would column defaults going volatile be rejected?


pkg/sql/schemachanger/scbuild/internal/scbuildstmt/create_function.go line 441 at r1 (raw file):

}

// updateDependentTriggers finds all triggers that reference the given function

This is hanging.

@bghal

bghal commented Jun 10, 2026

Copy link
Copy Markdown
Contributor

Thanks for looking into just a couple comments.

…function

Move the updateDependentTriggers doc comment so it sits adjacent to its function declaration. The validateVolatilityForDependents function was inserted between the comment and its function, causing godoc to attach the comment to the wrong declaration.

Release note: None
@blathers-crl

blathers-crl Bot commented Jun 10, 2026

Copy link
Copy Markdown

Thank you for updating your pull request.

Before a member of our team reviews your PR, I have some potential action items for you:

  • We notice you have more than one commit in your PR. We try break logical changes into separate commits, but commits such as "fix typo" or "address review commits" should be squashed into one commit and pushed with --force
  • When CI has completed, please ensure no errors have appeared.

🦉 Hoot! I am a Blathers, a bot for CockroachDB. My owner is dev-inf.

@virajchogle

Copy link
Copy Markdown
Contributor Author

@bghal Defaults are intentionally out of scope. Column DEFAULTs don't require IMMUTABLE in PG (DEFAULT now(), DEFAULT nextval() are valid). The function runs once at insert/update-with-DEFAULT time and the result is
stored, so volatility doesn't matter. The check targets only IMMUTABLE-required contexts: computed columns, expression indexes, CHECK constraints.

If you have a specific case in mind where a default does require IMMUTABLE, would extend the check to cover it.

@virajchogle

Copy link
Copy Markdown
Contributor Author

Fixed the other hanging issue with another commit, moved the updateDependentTriggers doc comment back next to its function. My bad, completely missed the comment goofup.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

O-community Originated from the community

Projects

None yet

Development

Successfully merging this pull request may close these issues.

sql/opt: nil pointer dereference projecting virtual computed column after CREATE OR REPLACE FUNCTION flips referenced UDF to VOLATILE

3 participants