Skip to content

trigger_is fails (have: NULL) for triggers defined on temporary tables #350

@olawale-york

Description

@olawale-york

Description:
The trigger_is function fails to correctly identify the function executed by a trigger when that trigger is defined on a temporary table. The test fails with have: NULL, indicating trigger_is could not find the linked function in the catalogs. This happens despite direct catalog queries (pg_trigger, pg_proc) showing the trigger is correctly linked within the database session. has_trigger correctly finds the trigger on the temp table, and trigger_is works correctly for identical triggers on persistent tables.
Environment:
pgTAP Version: 1.2.0
PostgreSQL Version: 15
Operating System: Docker
Steps to Reproduce:

-- Minimal Reproducible Example for trigger_is on temp table issue

BEGIN;

SELECT plan(7);

-- 1. Create a simple trigger function in public schema
CREATE OR REPLACE FUNCTION public.my_temp_trigger_func()
RETURNS TRIGGER AS $$ BEGIN RETURN NEW; END; $$ LANGUAGE plpgsql;
SELECT has_function('public', 'my_temp_trigger_func', ARRAY[]::text[], 'Trigger function should exist');

-- 2. Create a dummy persistent table
CREATE TABLE public.persistent_table (id serial primary key, data text);
SELECT has_table('public', 'persistent_table', 'Persistent table should exist');

-- 3. Create a trigger on the persistent table (using the function)
CREATE TRIGGER persistent_trigger
BEFORE UPDATE ON public.persistent_table
FOR EACH ROW EXECUTE FUNCTION public.my_temp_trigger_func();
SELECT has_trigger('public', 'persistent_table', 'persistent_trigger', 'Trigger on persistent table should exist');

-- 4. Create a dummy temporary table
CREATE TEMPORARY TABLE temp_table (id serial primary key, data text) ON COMMIT PRESERVE ROWS;
SELECT has_table('temp_table', 'Temporary table should exist'); -- Note: has_table checks search_path

-- 5. Create an identical trigger on the temporary table
CREATE TRIGGER temp_trigger
BEFORE UPDATE ON temp_table
FOR EACH ROW EXECUTE FUNCTION public.my_temp_trigger_func();
SELECT has_trigger('temp_table', 'temp_trigger', 'Trigger on temporary table should exist');

-- 6. Test trigger_is on the PERSISTENT table (EXPECTED TO PASS)
SELECT trigger_is(
'public', -- table schema
'persistent_table', -- table name
'persistent_trigger', -- trigger name
'public', -- function schema
'my_temp_trigger_func', -- function name
'trigger_is should PASS for persistent table trigger'
);

-- 7. Test trigger_is on the TEMPORARY table (EXPECTED TO FAIL with have: NULL)
-- Diagnostic query (optional, uncomment to show DB state is correct):
/*
SELECT diag((
SELECT format('Trigger info from pg_trigger for temp_trigger: tgfoid=%s, resolved=%s',
tg.tgfoid, tg.tgfoid::regproc)
FROM pg_trigger tg
JOIN pg_class tbl ON tg.tgrelid = tbl.oid
JOIN pg_namespace ns ON tbl.relnamespace = ns.oid
WHERE tbl.relname = 'temp_table' AND ns.nspname LIKE 'pg_temp%' AND tg.tgname = 'temp_trigger'
));
*/
SELECT trigger_is(
'temp_table', -- table name (pgTAP should search temp schemas)
'temp_trigger', -- trigger name
'public', -- expected function schema
'my_temp_trigger_func', -- expected function name
'trigger_is should FAIL for temporary table trigger (bug)'
);

SELECT * FROM finish();
ROLLBACK;

Expected Behavior:
The final trigger_is test (Test 7 in the example script) should pass, correctly identifying public.my_temp_trigger_func as the function executed by temp_trigger on the temporary table temp_table.
Actual Behavior:
The final trigger_is test fails.
For example:

Failed test 7: "trigger_is should FAIL for temporary table trigger (bug)"

have: NULL

want: my_temp_trigger_func

Workaround:
There is no known workaround using standard pgTAP functions to reliably assert the trigger-function linkage on temporary tables. Users must rely on has_trigger and behavioral tests, or potentially skip this specific assertion for temporary objects.
Additional Context:
Direct inspection of pg_trigger.tgfoid confirms the database linkage is correct at the time of the test, strongly suggesting the issue lies within pgTAP's lookup mechanism for temporary relations in this context.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions