Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 10 additions & 3 deletions internal/diff/function.go
Original file line number Diff line number Diff line change
Expand Up @@ -243,10 +243,17 @@ func generateFunctionSQL(function *ir.Function, targetSchema string) string {
// Note: Don't output PARALLEL UNSAFE (it's the default)

// Add SET search_path if specified
// Note: Output without outer quotes to handle multi-schema paths correctly
// e.g., "SET search_path = pg_catalog, public" not "SET search_path = 'pg_catalog, public'"
// Note: Multi-schema paths are output unquoted (e.g., "SET search_path = pg_catalog, public"),
// except for the empty search_path case which requires single quotes: SET search_path = ''
if function.SearchPath != "" {
stmt.WriteString(fmt.Sprintf("\nSET search_path = %s", function.SearchPath))
// PostgreSQL stores SET search_path = '' as search_path="" in proconfig.
// The extracted value is "" (two double-quote chars). Render as '' (single-quoted empty string).
// Only the whole-value empty case is handled; mixed paths (e.g. pg_catalog, "") are not expected.
if function.SearchPath == `""` {
stmt.WriteString("\nSET search_path = ''")
} else {
stmt.WriteString(fmt.Sprintf("\nSET search_path = %s", function.SearchPath))
}
}
Comment on lines 248 to 257
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Consider also covering partial "" in a composite path

The current check only fires when the entire SearchPath value equals "" (i.e., search_path="" was the only proconfig entry, representing a fully empty path). If PostgreSQL ever stores a composite value such as pg_catalog, "" — where the empty element appears alongside real schemas — the literal "" in that string would fall through to the else branch and be rendered verbatim as a double-quoted identifier, rather than ''.

In practice this is very unlikely, but since the fix already has a comment explaining the proconfig format, it might be worth a brief note here that only the whole-value case is handled:

Suggested change
if function.SearchPath != "" {
stmt.WriteString(fmt.Sprintf("\nSET search_path = %s", function.SearchPath))
// PostgreSQL stores SET search_path = '' as search_path="" in proconfig.
// The extracted value is "" (two double-quote chars). Render as '' (single-quoted empty string).
if function.SearchPath == `""` {
stmt.WriteString("\nSET search_path = ''")
} else {
stmt.WriteString(fmt.Sprintf("\nSET search_path = %s", function.SearchPath))
}
}
if function.SearchPath != "" {
// PostgreSQL stores SET search_path = '' as search_path="" in proconfig.
// The extracted value is "" (two double-quote chars). Render as '' (single-quoted empty string).
// Note: only the whole-value empty case is handled; mixed paths (e.g. pg_catalog, "") are not expected.
if function.SearchPath == `""` {
stmt.WriteString("\nSET search_path = ''")
} else {
stmt.WriteString(fmt.Sprintf("\nSET search_path = %s", function.SearchPath))
}
}


// Add the function body
Expand Down
12 changes: 12 additions & 0 deletions testdata/diff/create_function/issue_354_empty_search_path/diff.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
CREATE OR REPLACE FUNCTION create_hello(
p_title text
)
RETURNS void
LANGUAGE plpgsql
VOLATILE
SET search_path = ''
AS $$
BEGIN
INSERT INTO test (title) VALUES (p_title);
END;
$$;
17 changes: 17 additions & 0 deletions testdata/diff/create_function/issue_354_empty_search_path/new.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
CREATE TABLE public.test (
id bigint GENERATED BY DEFAULT AS IDENTITY NOT NULL,
title text,
created_at timestamp with time zone DEFAULT now() NOT NULL,
CONSTRAINT test_pkey PRIMARY KEY (id)
);

CREATE OR REPLACE FUNCTION create_hello(p_title text)
RETURNS void
LANGUAGE plpgsql
SECURITY INVOKER
SET search_path = ''
AS $$
BEGIN
INSERT INTO public.test (title) VALUES (p_title);
END;
$$;
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
CREATE TABLE public.test (
id bigint GENERATED BY DEFAULT AS IDENTITY NOT NULL,
title text,
created_at timestamp with time zone DEFAULT now() NOT NULL,
CONSTRAINT test_pkey PRIMARY KEY (id)
);
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
{
"version": "1.0.0",
"pgschema_version": "1.7.4",
"created_at": "1970-01-01T00:00:00Z",
"source_fingerprint": {
"hash": "742d612cedabf2d80d87df6c7d9fac8911e008b29ef8e15d16a32d2edf43ddd9"
},
"groups": [
{
"steps": [
{
"sql": "CREATE OR REPLACE FUNCTION create_hello(\n p_title text\n)\nRETURNS void\nLANGUAGE plpgsql\nVOLATILE\nSET search_path = ''\nAS $$\nBEGIN\n INSERT INTO test (title) VALUES (p_title);\nEND;\n$$;",
"type": "function",
"operation": "create",
"path": "public.create_hello"
}
]
}
]
}
12 changes: 12 additions & 0 deletions testdata/diff/create_function/issue_354_empty_search_path/plan.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
CREATE OR REPLACE FUNCTION create_hello(
p_title text
)
RETURNS void
LANGUAGE plpgsql
VOLATILE
SET search_path = ''
AS $$
BEGIN
INSERT INTO test (title) VALUES (p_title);
END;
$$;
23 changes: 23 additions & 0 deletions testdata/diff/create_function/issue_354_empty_search_path/plan.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
Plan: 1 to add.

Summary by type:
functions: 1 to add

Functions:
+ create_hello

DDL to be executed:
--------------------------------------------------

CREATE OR REPLACE FUNCTION create_hello(
p_title text
)
RETURNS void
LANGUAGE plpgsql
VOLATILE
SET search_path = ''
AS $$
BEGIN
INSERT INTO test (title) VALUES (p_title);
END;
$$;
Loading