diff --git a/cmd/dump/dump_integration_test.go b/cmd/dump/dump_integration_test.go index 9517c9ff..4941c3d4 100644 --- a/cmd/dump/dump_integration_test.go +++ b/cmd/dump/dump_integration_test.go @@ -130,6 +130,13 @@ func TestDumpCommand_Issue345ArrayCast(t *testing.T) { runExactMatchTest(t, "issue_345_array_cast") } +func TestDumpCommand_Issue191FunctionProcedureOverload(t *testing.T) { + if testing.Short() { + t.Skip("Skipping integration test in short mode") + } + runExactMatchTest(t, "issue_191_function_procedure_overload") +} + func TestDumpCommand_Issue318CrossSchemaComment(t *testing.T) { if testing.Short() { t.Skip("Skipping integration test in short mode") diff --git a/ir/queries/queries.sql b/ir/queries/queries.sql index da9cf138..78bcf0ce 100644 --- a/ir/queries/queries.sql +++ b/ir/queries/queries.sql @@ -542,11 +542,12 @@ SELECT p.proisstrict AS is_strict, p.prosecdef AS is_security_definer FROM information_schema.routines r -LEFT JOIN pg_proc p ON p.proname = r.routine_name +LEFT JOIN pg_proc p ON p.proname = r.routine_name AND p.pronamespace = (SELECT oid FROM pg_namespace WHERE nspname = r.routine_schema) + AND p.oid = (regexp_match(r.specific_name, '_(\d+)$'))[1]::oid LEFT JOIN pg_depend d ON d.objid = p.oid AND d.deptype = 'e' LEFT JOIN pg_description desc_func ON desc_func.objoid = p.oid AND desc_func.classoid = 'pg_proc'::regclass -WHERE +WHERE r.routine_schema NOT IN ('information_schema', 'pg_catalog', 'pg_toast') AND r.routine_schema NOT LIKE 'pg_temp_%' AND r.routine_schema NOT LIKE 'pg_toast_temp_%' @@ -566,11 +567,12 @@ SELECT oidvectortypes(p.proargtypes) AS procedure_arguments, pg_get_function_arguments(p.oid) AS procedure_signature FROM information_schema.routines r -LEFT JOIN pg_proc p ON p.proname = r.routine_name +LEFT JOIN pg_proc p ON p.proname = r.routine_name AND p.pronamespace = (SELECT oid FROM pg_namespace WHERE nspname = r.routine_schema) + AND p.oid = (regexp_match(r.specific_name, '_(\d+)$'))[1]::oid LEFT JOIN pg_depend d ON d.objid = p.oid AND d.deptype = 'e' LEFT JOIN pg_description desc_proc ON desc_proc.objoid = p.oid AND desc_proc.classoid = 'pg_proc'::regclass -WHERE +WHERE r.routine_schema NOT IN ('information_schema', 'pg_catalog', 'pg_toast') AND r.routine_schema NOT LIKE 'pg_temp_%' AND r.routine_schema NOT LIKE 'pg_toast_temp_%' @@ -994,6 +996,7 @@ SELECT FROM information_schema.routines r LEFT JOIN pg_proc p ON p.proname = r.routine_name AND p.pronamespace = (SELECT oid FROM pg_namespace WHERE nspname = r.routine_schema) + AND p.oid = (regexp_match(r.specific_name, '_(\d+)$'))[1]::oid LEFT JOIN pg_depend d ON d.objid = p.oid AND d.deptype = 'e' LEFT JOIN pg_description desc_func ON desc_func.objoid = p.oid AND desc_func.classoid = 'pg_proc'::regclass WHERE r.routine_schema = $1 @@ -1020,6 +1023,7 @@ SELECT FROM information_schema.routines r LEFT JOIN pg_proc p ON p.proname = r.routine_name AND p.pronamespace = (SELECT oid FROM pg_namespace WHERE nspname = r.routine_schema) + AND p.oid = (regexp_match(r.specific_name, '_(\d+)$'))[1]::oid LEFT JOIN pg_depend d ON d.objid = p.oid AND d.deptype = 'e' LEFT JOIN pg_description desc_proc ON desc_proc.objoid = p.oid AND desc_proc.classoid = 'pg_proc'::regclass WHERE r.routine_schema = $1 diff --git a/ir/queries/queries.sql.go b/ir/queries/queries.sql.go index 1a71f83c..28efd3e9 100644 --- a/ir/queries/queries.sql.go +++ b/ir/queries/queries.sql.go @@ -1400,11 +1400,12 @@ SELECT p.proisstrict AS is_strict, p.prosecdef AS is_security_definer FROM information_schema.routines r -LEFT JOIN pg_proc p ON p.proname = r.routine_name +LEFT JOIN pg_proc p ON p.proname = r.routine_name AND p.pronamespace = (SELECT oid FROM pg_namespace WHERE nspname = r.routine_schema) + AND p.oid = (regexp_match(r.specific_name, '_(\d+)$'))[1]::oid LEFT JOIN pg_depend d ON d.objid = p.oid AND d.deptype = 'e' LEFT JOIN pg_description desc_func ON desc_func.objoid = p.oid AND desc_func.classoid = 'pg_proc'::regclass -WHERE +WHERE r.routine_schema NOT IN ('information_schema', 'pg_catalog', 'pg_toast') AND r.routine_schema NOT LIKE 'pg_temp_%' AND r.routine_schema NOT LIKE 'pg_toast_temp_%' @@ -1495,6 +1496,7 @@ SELECT FROM information_schema.routines r LEFT JOIN pg_proc p ON p.proname = r.routine_name AND p.pronamespace = (SELECT oid FROM pg_namespace WHERE nspname = r.routine_schema) + AND p.oid = (regexp_match(r.specific_name, '_(\d+)$'))[1]::oid LEFT JOIN pg_depend d ON d.objid = p.oid AND d.deptype = 'e' LEFT JOIN pg_description desc_func ON desc_func.objoid = p.oid AND desc_func.classoid = 'pg_proc'::regclass WHERE r.routine_schema = $1 @@ -2068,11 +2070,12 @@ SELECT oidvectortypes(p.proargtypes) AS procedure_arguments, pg_get_function_arguments(p.oid) AS procedure_signature FROM information_schema.routines r -LEFT JOIN pg_proc p ON p.proname = r.routine_name +LEFT JOIN pg_proc p ON p.proname = r.routine_name AND p.pronamespace = (SELECT oid FROM pg_namespace WHERE nspname = r.routine_schema) + AND p.oid = (regexp_match(r.specific_name, '_(\d+)$'))[1]::oid LEFT JOIN pg_depend d ON d.objid = p.oid AND d.deptype = 'e' LEFT JOIN pg_description desc_proc ON desc_proc.objoid = p.oid AND desc_proc.classoid = 'pg_proc'::regclass -WHERE +WHERE r.routine_schema NOT IN ('information_schema', 'pg_catalog', 'pg_toast') AND r.routine_schema NOT LIKE 'pg_temp_%' AND r.routine_schema NOT LIKE 'pg_toast_temp_%' @@ -2143,6 +2146,7 @@ SELECT FROM information_schema.routines r LEFT JOIN pg_proc p ON p.proname = r.routine_name AND p.pronamespace = (SELECT oid FROM pg_namespace WHERE nspname = r.routine_schema) + AND p.oid = (regexp_match(r.specific_name, '_(\d+)$'))[1]::oid LEFT JOIN pg_depend d ON d.objid = p.oid AND d.deptype = 'e' LEFT JOIN pg_description desc_proc ON desc_proc.objoid = p.oid AND desc_proc.classoid = 'pg_proc'::regclass WHERE r.routine_schema = $1 diff --git a/testdata/dump/issue_191_function_procedure_overload/manifest.json b/testdata/dump/issue_191_function_procedure_overload/manifest.json index 7ebfedf2..15c06883 100644 --- a/testdata/dump/issue_191_function_procedure_overload/manifest.json +++ b/testdata/dump/issue_191_function_procedure_overload/manifest.json @@ -1,11 +1,12 @@ { "name": "issue_191_function_procedure_overload", - "description": "Test case for overloaded functions and procedures not being fully dumped (GitHub issue #191)", + "description": "Test case for overloaded functions and procedures (GitHub issues #191, #368)", "source": "https://github.com/pgplex/pgschema/issues/191", "notes": [ - "Tests that all overloaded functions are preserved when dumping (not just the last one)", - "Tests that all overloaded procedures are preserved when dumping", - "Covers overloads with different parameter counts: test_func(integer) vs test_func(integer, integer)", - "Covers overloads with different parameter types: test_func(integer) vs test_func(text)" + "#191: Tests that all overloaded functions are preserved when dumping (not just the last one)", + "#191: Tests that all overloaded procedures are preserved when dumping", + "#191: Covers overloads with different parameter counts: test_func(integer) vs test_func(integer, integer)", + "#191: Covers overloads with different parameter types: test_func(integer) vs test_func(text)", + "#368: Tests that overloaded functions with different languages (sql vs plpgsql) each get the correct LANGUAGE" ] } diff --git a/testdata/dump/issue_191_function_procedure_overload/pgdump.sql b/testdata/dump/issue_191_function_procedure_overload/pgdump.sql index 1f0204c0..7f1bf59b 100644 --- a/testdata/dump/issue_191_function_procedure_overload/pgdump.sql +++ b/testdata/dump/issue_191_function_procedure_overload/pgdump.sql @@ -95,6 +95,32 @@ END; $$; +-- +-- Name: provide_tx(text[]); Type: FUNCTION; Schema: public; Owner: - +-- + +CREATE FUNCTION public.provide_tx(VARIADIC p_txs text[]) + RETURNS void + LANGUAGE sql + AS $$ +SELECT 1; +$$; + + +-- +-- Name: provide_tx(uuid); Type: FUNCTION; Schema: public; Owner: - +-- + +CREATE FUNCTION public.provide_tx(p_id uuid) + RETURNS void + LANGUAGE plpgsql + AS $$ +BEGIN + RAISE NOTICE '%', p_id; +END; +$$; + + -- -- PostgreSQL database dump complete -- diff --git a/testdata/dump/issue_191_function_procedure_overload/pgschema.sql b/testdata/dump/issue_191_function_procedure_overload/pgschema.sql index 307fb63f..a32a7c9d 100644 --- a/testdata/dump/issue_191_function_procedure_overload/pgschema.sql +++ b/testdata/dump/issue_191_function_procedure_overload/pgschema.sql @@ -2,10 +2,40 @@ -- pgschema database dump -- --- Dumped from database version PostgreSQL 17.5 --- Dumped by pgschema version 1.5.0 +-- Dumped from database version PostgreSQL 18.0 +-- Dumped by pgschema version 1.7.4 +-- +-- Name: provide_tx(text[]); Type: FUNCTION; Schema: -; Owner: - +-- + +CREATE OR REPLACE FUNCTION provide_tx( + VARIADIC p_txs text[] +) +RETURNS void +LANGUAGE sql +VOLATILE +AS $$ +SELECT 1; +$$; + +-- +-- Name: provide_tx(uuid); Type: FUNCTION; Schema: -; Owner: - +-- + +CREATE OR REPLACE FUNCTION provide_tx( + p_id uuid +) +RETURNS void +LANGUAGE plpgsql +VOLATILE +AS $$ +BEGIN + RAISE NOTICE '%', p_id; +END; +$$; + -- -- Name: test_func(integer); Type: FUNCTION; Schema: -; Owner: - -- @@ -97,3 +127,4 @@ BEGIN RAISE NOTICE 'Text: %', a; END; $$; + diff --git a/testdata/dump/issue_191_function_procedure_overload/raw.sql b/testdata/dump/issue_191_function_procedure_overload/raw.sql index e089fe2d..b074d684 100644 --- a/testdata/dump/issue_191_function_procedure_overload/raw.sql +++ b/testdata/dump/issue_191_function_procedure_overload/raw.sql @@ -1,9 +1,13 @@ -- -- Test case for GitHub issue #191: Overloaded functions and procedures not fully dumped +-- Also covers #368: dump emits wrong LANGUAGE for overloaded functions with mixed languages -- --- This test case reproduces a bug where only the last overloaded function/procedure --- is included in the dump output. Functions and procedures are stored by name only, --- causing overloads with different signatures to overwrite each other. +-- #191: Only the last overloaded function/procedure was included in the dump output. +-- Functions and procedures were stored by name only, causing overloads with +-- different signatures to overwrite each other. +-- #368: When overloaded functions have different languages (e.g., sql vs plpgsql), +-- the dump may assign the wrong language due to a cross-join in the query +-- between information_schema.routines and pg_proc. -- -- @@ -70,3 +74,25 @@ BEGIN RAISE NOTICE 'Text: %', a; END; $$; + +-- +-- Mixed-language function overloads (#368): same name, different languages +-- + +-- Overload 1: SQL language +CREATE OR REPLACE FUNCTION provide_tx(VARIADIC p_txs text[]) +RETURNS void +LANGUAGE sql +AS $$ +SELECT 1; +$$; + +-- Overload 2: plpgsql language +CREATE OR REPLACE FUNCTION provide_tx(p_id uuid) +RETURNS void +LANGUAGE plpgsql +AS $$ +BEGIN + RAISE NOTICE '%', p_id; +END; +$$;