In PostgreSQL architecture, a NamedTupleStore represents an ephemeral named relation rather than a persistent catalog table. Officially referred to within the source code as part of the NamedTuplestoreScan mechanism, these structures exist temporarily to hold transition data during trigger execution.
Implementation Context
This mechanism is primarily utilized within trigger functions that require access to the set of rows modified by a statement. This is achieved through the REFERENCING clause, which binds a relation name to the transition table.
Consider a scenario where one needs to capture the execution plan involving inserted rows. First, define a base table:
CREATE TABLE data_change_tracker (
record_id SERIAL PRIMARY KEY,
payload TEXT
);
Next, create a function that iterates over the explanation of a query targeting the transition relation:
CREATE OR REPLACE FUNCTION log_transition_changes()
RETURNS trigger
LANGUAGE plpgsql
AS $$
DECLARE
output_buffer TEXT := '';
plan_row RECORD;
BEGIN
FOR plan_row IN EXECUTE
$cmd$
EXPLAIN (ANALYZE false, TIMING false, VERBOSE true)
SELECT * FROM inserted_rows
$cmd$
LOOP
output_buffer := concat(output_buffer, plan_row, E'\n');
END LOOP;
RAISE NOTICE 'Plan Output: %', output_buffer;
RETURN NULL;
END;
$$;
Attach this logic using a statement-level trigger. Note that transition tables are generally associated with FOR EACH STATEMENT triggers. For an insert operation, only the new table is available:
CREATE TRIGGER audit_insert_trigger
AFTER INSERT ON data_change_tracker
REFERENCING NEW TABLE AS inserted_rows
FOR EACH STATEMENT
EXECUTE FUNCTION log_transition_changes();
The identifier inserted_rows acts as the handle for the NamedTupleStore. While OLD TABLE is syntacticaly similar, it cannot be referenced during an INSERT event due to engine constraints.
Verificasion and Variations
To inspect the trigger configuration attached to the relation, query the system catalog:
SELECT tgname, tgtype
FROM pg_trigger
WHERE tgrelid = 'data_change_tracker'::regclass;
For update operations, both old and new transition tables can be referenced simultaneously. Ensure previous triggers are removed to avoid conflicts:
DROP TRIGGER IF EXISTS audit_insert_trigger ON data_change_tracker;
CREATE TRIGGER audit_update_trigger
AFTER UPDATE ON data_change_tracker
REFERENCING OLD TABLE AS modified_prev
NEW TABLE AS modified_curr
FOR EACH STATEMENT
EXECUTE FUNCTION log_transition_changes();
Internal Debugging
When investigating issues related to transision table generation within the database kernel, developers can place breakpoints at the make_namedtuplestorescan function. This entry point confirms whether the executor is correctly initializing the scan structure for the ephemeral relation.