SET search_path = core; SELECT set_config('app.tenant_id', '2', true); CREATE OR REPLACE FUNCTION fn_next_project_code_old() RETURNS text LANGUAGE plpgsql SECURITY INVOKER SET search_path = core AS $$ DECLARE v_n integer; v_code text; BEGIN SELECT COALESCE(MAX(substring(code FROM 5)::integer), 0) + 1 INTO v_n FROM projects WHERE code ~ '^PRY-[0-9]{4}' OR code ~ '^OBR-[0-9]{4}'; RETURN 'PRY-' || lpad(v_n::text, 4, '0'); END; $$; CREATE OR REPLACE FUNCTION fn_next_project_code_new() RETURNS text LANGUAGE plpgsql SECURITY DEFINER SET search_path = core AS $$ DECLARE v_n integer; v_code text; BEGIN SELECT COALESCE(MAX(substring(code FROM 5)::integer), 0) + 1 INTO v_n FROM projects WHERE code ~ '^(PRY|OBR)-[0-9]+$'; RETURN 'PRY-' || lpad(v_n::text, 4, '0'); END; $$; DO $$ DECLARE old_code text; new_code text; BEGIN old_code := fn_next_project_code_old(); new_code := fn_next_project_code_new(); IF old_code <> 'PRY-0001' THEN RAISE EXCEPTION 'RLS bug demo: old should propose PRY-0001, got %', old_code; END IF; IF new_code <> 'PRY-0002' THEN RAISE EXCEPTION 'fix should propose PRY-0002, got %', new_code; END IF; RAISE NOTICE 'OK old=% (conflict) new=% (safe)', old_code, new_code; END; $$;