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
7 changes: 7 additions & 0 deletions cmd/dump/dump_integration_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,13 @@ func TestDumpCommand_Issue320PlpgsqlReservedKeywordType(t *testing.T) {
runExactMatchTest(t, "issue_320_plpgsql_reserved_keyword_type")
}

func TestDumpCommand_Issue345ArrayCast(t *testing.T) {
if testing.Short() {
t.Skip("Skipping integration test in short mode")
}
runExactMatchTest(t, "issue_345_array_cast")
}

func TestDumpCommand_Issue318CrossSchemaComment(t *testing.T) {
if testing.Short() {
t.Skip("Skipping integration test in short mode")
Expand Down
9 changes: 7 additions & 2 deletions ir/normalize.go
Original file line number Diff line number Diff line change
Expand Up @@ -175,6 +175,7 @@ func normalizeDefaultValue(value string, tableSchema string) string {
// - '2024-01-01'::date (date literals need the cast in expressions)
//
// Pattern matches redundant text/varchar/char/json casts (including arrays)
// For column defaults, these casts are redundant because the column type provides context
// Note: jsonb must come before json to avoid partial match
// Note: (?:\[\])* handles multi-dimensional arrays like text[][]
re = regexp.MustCompile(`('(?:[^']|'')*')::(text|character varying|character|bpchar|varchar|jsonb|json)(?:\[\])*`)
Expand Down Expand Up @@ -743,8 +744,12 @@ func normalizeExpressionParentheses(expr string) string {

// Step 3: Normalize redundant type casts in function arguments
// Pattern: 'text'::text -> 'text' (removing redundant text cast from literals)
redundantTextCastRegex := regexp.MustCompile(`'([^']+)'::text`)
expr = redundantTextCastRegex.ReplaceAllString(expr, "'$1'")
// IMPORTANT: Do NOT match when followed by [] (array cast is semantically significant)
// e.g., '{nested,key}'::text[] must be preserved as-is
// Since Go regex doesn't support lookahead, we use [^[\w] which excludes both '['
// and word characters (letters/digits/_), correctly preventing matches like ::text[] or ::textual
redundantTextCastRegex := regexp.MustCompile(`'([^']+)'::text([^[\w]|$)`)
expr = redundantTextCastRegex.ReplaceAllString(expr, "'$1'$2")

return expr
}
Expand Down
9 changes: 9 additions & 0 deletions testdata/dump/issue_345_array_cast/manifest.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
{
"name": "issue_345_array_cast",
"description": "pgschema dump strips type name from array literal casts (e.g., '{nested,key}'::text[] becomes '{nested,key}'[])",
"source": "https://github.com/pgplex/pgschema/issues/345",
"notes": [
"Verifies that explicit array type casts are preserved in policy expressions",
"Reproduces the bug where '{nested,key}'::text[] becomes '{nested,key}'[] in dump output"
]
}
43 changes: 43 additions & 0 deletions testdata/dump/issue_345_array_cast/pgdump.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
--
-- PostgreSQL database dump
--

SET statement_timeout = 0;
SET lock_timeout = 0;
SET client_encoding = 'UTF8';
SET standard_conforming_strings = on;
SET check_function_bodies = false;
SET client_min_messages = warning;
SET row_security = off;

--
-- Name: repro; Type: TABLE; Schema: public; Owner: -
--

CREATE TABLE public.repro (
id uuid DEFAULT gen_random_uuid() NOT NULL,
data jsonb DEFAULT '{}'::jsonb
);

--
-- Name: repro repro_pkey; Type: CONSTRAINT; Schema: public; Owner: -
--

ALTER TABLE ONLY public.repro
ADD CONSTRAINT repro_pkey PRIMARY KEY (id);

--
-- Name: repro; Type: ROW SECURITY; Schema: public; Owner: -
--

ALTER TABLE public.repro ENABLE ROW LEVEL SECURITY;

--
-- Name: repro p; Type: POLICY; Schema: public; Owner: -
--

CREATE POLICY p ON public.repro USING (((data #>> ('{nested,key}'::text[])) = 'x'::text));

--
-- PostgreSQL database dump complete
--
30 changes: 30 additions & 0 deletions testdata/dump/issue_345_array_cast/pgschema.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
--
-- pgschema database dump
--

-- Dumped from database version PostgreSQL 18.0
-- Dumped by pgschema version 1.7.3


--
-- Name: repro; Type: TABLE; Schema: -; Owner: -
--

CREATE TABLE IF NOT EXISTS repro (
id uuid DEFAULT gen_random_uuid(),
data jsonb DEFAULT '{}',
CONSTRAINT repro_pkey PRIMARY KEY (id)
);

--
-- Name: repro; Type: RLS; Schema: -; Owner: -
--

ALTER TABLE repro ENABLE ROW LEVEL SECURITY;

--
-- Name: p; Type: POLICY; Schema: -; Owner: -
--

CREATE POLICY p ON repro TO PUBLIC USING ((data #>> '{nested,key}'::text[]) = 'x');

13 changes: 13 additions & 0 deletions testdata/dump/issue_345_array_cast/raw.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
--
-- Test case for GitHub issue #345: pgschema dump strips type name from array literal casts
--
-- This reproduces the bug where explicit array type casts like ::text[] are
-- stripped, leaving invalid SQL like '{nested,key}'[]
--

CREATE TABLE repro (
id uuid DEFAULT gen_random_uuid() PRIMARY KEY,
data jsonb DEFAULT '{}'
);

CREATE POLICY p ON repro USING ((data #>> '{nested,key}'::text[]) = 'x');