CockroachDB 25.4.3 keeps mutations when PL/pgSQL results are unused
A PL/pgSQL statement could disappear when its INTO variables were unused. The January patch also fixes prepared statements whose NULL parameters could produce incorrect rows.
Discarding an unused result must not discard the mutation that produced it. CockroachDB 25.4.3 fixes a case where a SQL statement with side effects inside a PL/pgSQL routine could be dropped when it had an INTO clause and none of its target variables were referenced. The January 9 release notes date the bug to version 23.2. An INSERT is one example of the affected statement type.
The interesting boundary is between a statement's returned value and its effect on the database. A routine can assign a result that later becomes unnecessary while retaining an essential write. Treating the unused assignment as evidence that the entire statement is unnecessary changes the routine's meaning. This patch corrects that behavior without requiring the application to make artificial use of a result it does not need.
I have three years with CockroachDB, and its default serializable isolation is a substantial part of its appeal to me. The PostgreSQL wire protocol also reduces adoption work. Those properties make execution semantics particularly important. Consensus and transaction isolation do not make an application correct if a requested mutation is omitted before it can become part of the database's state.
An INTO target is only one result of a write
CockroachDB's version 25.4 PL/pgSQL documentation describes routines that execute SQL and assign returned values into local variables. Its stored-procedure example uses DELETE ... RETURNING ... INTO to capture information about deleted rows. The deletion and the assignment are distinct observable parts of that operation. The row changes remain meaningful even when later code does not inspect the captured values.
The defect requires all target variables to be unused. That makes apparently harmless routine cleanup relevant to its behavior. Removing a message that references a returned identifier, for example, can leave the variable unused without removing the author's intention to perform the write. This is a consequence of the documented trigger, not a claim that a particular application has experienced it.
The release notes do not identify a storage or replication failure as the cause. They say the statement could be dropped. That distinction matters when investigating missing rows. A transaction that completes without the intended statement is a different problem from one whose executed mutation fails to commit. Checking only whether the routine returned successfully would not distinguish them.
A useful regression test therefore examines the resulting table contents after calling the routine. The local variable should remain unused in the test, because making it part of a return value could remove the stated trigger. Test design has to preserve the property responsible for the bug while observing the mutation through a separate query.
NULL parameters exposed another execution shortcut
The same patch corrects prepared statements that could return rows containing NULL when the result should have been empty. The notes trace this second bug to version 21.2. Through version 25.3, it required a prepared statement with an equality filter between a placeholder and a unique column containing NULL values, with the placeholder bound to NULL at execution.
Version 25.4 broadened the conditions. The column no longer had to be unique, and inclusion in any index was enough. That historical detail explains why a routine maintenance release can matter to an application whose SQL has not changed. A newer execution implementation can make an older correctness problem reachable under more ordinary schema conditions.
The notes explicitly describe a violation of SQL NULL-equality semantics. Binding NULL to an equality parameter must not turn that predicate into a request for rows whose column is NULL. An application that intends a NULL test needs to express that operation, but this bug concerned an equality predicate producing rows contrary to its own semantics.
Prepared execution is part of the trigger. A reproduction using a literal in place of the parameter may no longer test the same case. The notes include both explicit and implicit prepared statements, so an application need not issue a visible PREPARE command in its own source for the condition to be relevant. Its actual execution path determines whether it uses prepared statements.
Routine dependencies also need accurate accounting
Another fix prevents dropping a column referenced only as an update target in a routine's SET clause. The old dependency accounting could miss that reference and allow the drop. This problem concerns a different meaning of use. A column can be required because a statement writes it, even when an expression does not read its previous value.
The fix applies only to newly created routines. On this branch, it must also be enabled with the prevent_update_set_column_drop session variable. Those qualifications make the correction different from the unused-INTO fix. Installing 25.4.3 does not establish that existing routine dependencies have all been reconstructed under the corrected logic.
The release also fixes a query predicate ignored under a specific lookup-join plan involving an index with preceding enum columns. Together, these changes make the patch relevant to applications relying on ordinary SQL semantics, beyond installations adopting a new feature. The resulting rows and mutations are the evidence that matters for those tests.
My preference for CockroachDB rests on making consistency requirements explicit, while accepting the latency cost of distributed agreement. This patch concerns what the SQL execution layer asks that distributed system to do. For the unused-result case, the required behavior is that the mutation remains part of the routine even though its returned value has no later reader.