From 51c49e2b8dc4f90fbd2955d1032a03ed32f65432 Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Thu, 21 May 2026 18:05:22 +0000 Subject: [PATCH 1/4] Add read-only PL/pgSQL function to fetch party description Introduces get_party_description(party_id) as a STABLE plpgsql function that selects description from public.party without modifying data. Co-authored-by: Denis Lussier --- cli/scripts/sql/get_party_description.sql | 31 +++++++++++++++++++++++ 1 file changed, 31 insertions(+) create mode 100644 cli/scripts/sql/get_party_description.sql diff --git a/cli/scripts/sql/get_party_description.sql b/cli/scripts/sql/get_party_description.sql new file mode 100644 index 0000000..25c8db3 --- /dev/null +++ b/cli/scripts/sql/get_party_description.sql @@ -0,0 +1,31 @@ +-- Read-only PL/pgSQL helper for party.description. +-- +-- Expected table (adjust names/types to match your schema): +-- CREATE TABLE party ( +-- party_id bigint PRIMARY KEY, +-- description text +-- ); + +CREATE OR REPLACE FUNCTION get_party_description(p_party_id bigint) +RETURNS text +LANGUAGE plpgsql +STABLE +STRICT +PARALLEL SAFE +SECURITY INVOKER +SET search_path = '' +AS $function$ +DECLARE + v_description text; +BEGIN + SELECT p.description + INTO v_description + FROM public.party AS p + WHERE p.party_id = p_party_id; + + RETURN v_description; +END; +$function$; + +COMMENT ON FUNCTION get_party_description(bigint) IS + 'Returns description for the given party_id; NULL if the row does not exist.'; From b802573c8cfce2d3afbbdb5c768e5aa37144ec6c Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Thu, 21 May 2026 18:08:13 +0000 Subject: [PATCH 2/4] Add Liquibase project with changeset for get_party_description Introduces liquibase/ with master changelog, changeset 001 that deploys the read-only PL/pgSQL function via sqlFile (splitStatements=false), COMMENT ON FUNCTION, and rollback DROP FUNCTION. Co-authored-by: Denis Lussier --- .gitignore | 2 + liquibase/README.md | 56 +++++++++++++++++++ .../changes/001-get-party-description.sql | 26 +++++++++ .../changes/001-get-party-description.xml | 27 +++++++++ liquibase/changelog/db.changelog-master.xml | 10 ++++ liquibase/liquibase.properties | 13 +++++ liquibase/liquibase.properties.example | 7 +++ 7 files changed, 141 insertions(+) create mode 100644 liquibase/README.md create mode 100644 liquibase/changelog/changes/001-get-party-description.sql create mode 100644 liquibase/changelog/changes/001-get-party-description.xml create mode 100644 liquibase/changelog/db.changelog-master.xml create mode 100644 liquibase/liquibase.properties create mode 100644 liquibase/liquibase.properties.example diff --git a/.gitignore b/.gitignore index c603208..6ccf7dd 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,6 @@ out/ +liquibase/liquibase.local.properties +liquibase/lib/ devel/pg/src devel/pg/1* devel/pg/data* diff --git a/liquibase/README.md b/liquibase/README.md new file mode 100644 index 0000000..fd46826 --- /dev/null +++ b/liquibase/README.md @@ -0,0 +1,56 @@ +# Liquibase + +Database migrations for PostgreSQL. + +## Layout + +``` +liquibase/ + liquibase.properties # default connection (override locally) + changelog/ + db.changelog-master.xml # root changelog + changes/ + 001-get-party-description.xml + 001-get-party-description.sql +``` + +## Prerequisites + +- [Liquibase CLI](https://docs.liquibase.com/start/install/home.html) +- PostgreSQL JDBC driver on the classpath, or Liquibase 4.29+ with bundled drivers + +## Configure + +```bash +cp liquibase.properties.example liquibase.local.properties +# edit liquibase.local.properties with your JDBC URL and credentials +``` + +`liquibase.local.properties` is gitignored. + +## Run + +From the repository root: + +```bash +cd liquibase +liquibase update +``` + +Dry-run SQL: + +```bash +liquibase update-sql +``` + +Rollback the function changeset: + +```bash +liquibase rollback-count 1 +``` + +## Changesets + +| Id | Description | +|----|-------------| +| `001-get-party-description` | Creates `public.get_party_description(bigint)` | diff --git a/liquibase/changelog/changes/001-get-party-description.sql b/liquibase/changelog/changes/001-get-party-description.sql new file mode 100644 index 0000000..5cf00e9 --- /dev/null +++ b/liquibase/changelog/changes/001-get-party-description.sql @@ -0,0 +1,26 @@ +-- Expected table (create separately if needed): +-- CREATE TABLE public.party ( +-- party_id bigint PRIMARY KEY, +-- description text +-- ); + +CREATE OR REPLACE FUNCTION public.get_party_description(p_party_id bigint) +RETURNS text +LANGUAGE plpgsql +STABLE +STRICT +PARALLEL SAFE +SECURITY INVOKER +SET search_path = '' +AS $function$ +DECLARE + v_description text; +BEGIN + SELECT p.description + INTO v_description + FROM public.party AS p + WHERE p.party_id = p_party_id; + + RETURN v_description; +END; +$function$; diff --git a/liquibase/changelog/changes/001-get-party-description.xml b/liquibase/changelog/changes/001-get-party-description.xml new file mode 100644 index 0000000..f927ad0 --- /dev/null +++ b/liquibase/changelog/changes/001-get-party-description.xml @@ -0,0 +1,27 @@ + + + + + Read-only PL/pgSQL function get_party_description(party_id) + + + + + COMMENT ON FUNCTION public.get_party_description(bigint) IS + 'Returns description for the given party_id; NULL if the row does not exist.'; + + + + DROP FUNCTION IF EXISTS public.get_party_description(bigint); + + + + diff --git a/liquibase/changelog/db.changelog-master.xml b/liquibase/changelog/db.changelog-master.xml new file mode 100644 index 0000000..eeea4ee --- /dev/null +++ b/liquibase/changelog/db.changelog-master.xml @@ -0,0 +1,10 @@ + + + + + + diff --git a/liquibase/liquibase.properties b/liquibase/liquibase.properties new file mode 100644 index 0000000..28ef77e --- /dev/null +++ b/liquibase/liquibase.properties @@ -0,0 +1,13 @@ +# Copy to liquibase.local.properties and override for your environment. +# Liquibase loads liquibase.local.properties automatically when present. + +changeLogFile=changelog/db.changelog-master.xml + +url=jdbc:postgresql://localhost:5432/ctl +username=postgres +password= + +driver=org.postgresql.Driver +classpath=lib/postgresql.jar + +logLevel=info diff --git a/liquibase/liquibase.properties.example b/liquibase/liquibase.properties.example new file mode 100644 index 0000000..1938c68 --- /dev/null +++ b/liquibase/liquibase.properties.example @@ -0,0 +1,7 @@ +changeLogFile=changelog/db.changelog-master.xml + +url=jdbc:postgresql://localhost:5432/your_database +username=your_user +password=your_password + +driver=org.postgresql.Driver From 02f1271575ab8c1fa54822cc7c988aa0c6d32b73 Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Thu, 21 May 2026 18:14:53 +0000 Subject: [PATCH 3/4] Convert Liquibase changesets to formatted SQL changelogs Replace XML changeset and sqlFile indirection with direct formatted SQL (--liquibase formatted sql, --changeset, --rollback). Master changelog is now db.changelog-master.sql. Co-authored-by: Denis Lussier --- liquibase/README.md | 21 ++++++++------- .../changes/001-get-party-description.sql | 13 +++++---- .../changes/001-get-party-description.xml | 27 ------------------- liquibase/changelog/db.changelog-master.sql | 3 +++ liquibase/changelog/db.changelog-master.xml | 10 ------- liquibase/liquibase.properties | 2 +- liquibase/liquibase.properties.example | 2 +- 7 files changed, 24 insertions(+), 54 deletions(-) delete mode 100644 liquibase/changelog/changes/001-get-party-description.xml create mode 100644 liquibase/changelog/db.changelog-master.sql delete mode 100644 liquibase/changelog/db.changelog-master.xml diff --git a/liquibase/README.md b/liquibase/README.md index fd46826..b61f14b 100644 --- a/liquibase/README.md +++ b/liquibase/README.md @@ -1,23 +1,22 @@ # Liquibase -Database migrations for PostgreSQL. +Database migrations for PostgreSQL using [formatted SQL](https://docs.liquibase.com/concepts/changelogs/sql-format.html) changelogs (no XML changesets). ## Layout ``` liquibase/ - liquibase.properties # default connection (override locally) + liquibase.properties changelog/ - db.changelog-master.xml # root changelog + db.changelog-master.sql changes/ - 001-get-party-description.xml - 001-get-party-description.sql + 001-get-party-description.sql # formatted SQL changeset ``` ## Prerequisites - [Liquibase CLI](https://docs.liquibase.com/start/install/home.html) -- PostgreSQL JDBC driver on the classpath, or Liquibase 4.29+ with bundled drivers +- PostgreSQL JDBC driver on the classpath (or Liquibase 4.29+ with bundled drivers) ## Configure @@ -37,7 +36,7 @@ cd liquibase liquibase update ``` -Dry-run SQL: +Preview SQL: ```bash liquibase update-sql @@ -51,6 +50,8 @@ liquibase rollback-count 1 ## Changesets -| Id | Description | -|----|-------------| -| `001-get-party-description` | Creates `public.get_party_description(bigint)` | +| Id | File | Description | +|----|------|-------------| +| `001-get-party-description` | `changes/001-get-party-description.sql` | Creates `public.get_party_description(bigint)` with `splitStatements:false` for `$function$` | + +The changeset uses inline SQL (not `sqlFile`). Rollback is declared with `--rollback` in the same file. diff --git a/liquibase/changelog/changes/001-get-party-description.sql b/liquibase/changelog/changes/001-get-party-description.sql index 5cf00e9..0b9f714 100644 --- a/liquibase/changelog/changes/001-get-party-description.sql +++ b/liquibase/changelog/changes/001-get-party-description.sql @@ -1,9 +1,7 @@ --- Expected table (create separately if needed): --- CREATE TABLE public.party ( --- party_id bigint PRIMARY KEY, --- description text --- ); +--liquibase formatted sql +--changeset ctl:001-get-party-description splitStatements:false +--comment: Read-only PL/pgSQL function get_party_description(party_id) CREATE OR REPLACE FUNCTION public.get_party_description(p_party_id bigint) RETURNS text LANGUAGE plpgsql @@ -24,3 +22,8 @@ BEGIN RETURN v_description; END; $function$; + +COMMENT ON FUNCTION public.get_party_description(bigint) IS + 'Returns description for the given party_id; NULL if the row does not exist.'; + +--rollback DROP FUNCTION IF EXISTS public.get_party_description(bigint); diff --git a/liquibase/changelog/changes/001-get-party-description.xml b/liquibase/changelog/changes/001-get-party-description.xml deleted file mode 100644 index f927ad0..0000000 --- a/liquibase/changelog/changes/001-get-party-description.xml +++ /dev/null @@ -1,27 +0,0 @@ - - - - - Read-only PL/pgSQL function get_party_description(party_id) - - - - - COMMENT ON FUNCTION public.get_party_description(bigint) IS - 'Returns description for the given party_id; NULL if the row does not exist.'; - - - - DROP FUNCTION IF EXISTS public.get_party_description(bigint); - - - - diff --git a/liquibase/changelog/db.changelog-master.sql b/liquibase/changelog/db.changelog-master.sql new file mode 100644 index 0000000..ad55b01 --- /dev/null +++ b/liquibase/changelog/db.changelog-master.sql @@ -0,0 +1,3 @@ +--liquibase formatted sql + +--include file:changes/001-get-party-description.sql diff --git a/liquibase/changelog/db.changelog-master.xml b/liquibase/changelog/db.changelog-master.xml deleted file mode 100644 index eeea4ee..0000000 --- a/liquibase/changelog/db.changelog-master.xml +++ /dev/null @@ -1,10 +0,0 @@ - - - - - - diff --git a/liquibase/liquibase.properties b/liquibase/liquibase.properties index 28ef77e..5889d0b 100644 --- a/liquibase/liquibase.properties +++ b/liquibase/liquibase.properties @@ -1,7 +1,7 @@ # Copy to liquibase.local.properties and override for your environment. # Liquibase loads liquibase.local.properties automatically when present. -changeLogFile=changelog/db.changelog-master.xml +changeLogFile=changelog/db.changelog-master.sql url=jdbc:postgresql://localhost:5432/ctl username=postgres diff --git a/liquibase/liquibase.properties.example b/liquibase/liquibase.properties.example index 1938c68..d4a1b08 100644 --- a/liquibase/liquibase.properties.example +++ b/liquibase/liquibase.properties.example @@ -1,4 +1,4 @@ -changeLogFile=changelog/db.changelog-master.xml +changeLogFile=changelog/db.changelog-master.sql url=jdbc:postgresql://localhost:5432/your_database username=your_user From 6438d9fc811e27957ce91a68e18a9332362d46bc Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Thu, 21 May 2026 18:22:09 +0000 Subject: [PATCH 4/4] Use native SQL files without Liquibase directives in SQL Plain PostgreSQL in .sql files; changeset metadata and sqlFile refs stay in minimal XML. Add separate rollback SQL file. Co-authored-by: Denis Lussier --- liquibase/README.md | 22 +++++++++-------- .../001-get-party-description-rollback.sql | 1 + .../changes/001-get-party-description.sql | 6 ----- .../changes/001-get-party-description.xml | 24 +++++++++++++++++++ liquibase/changelog/db.changelog-master.sql | 3 --- liquibase/changelog/db.changelog-master.xml | 10 ++++++++ liquibase/liquibase.properties | 2 +- liquibase/liquibase.properties.example | 2 +- 8 files changed, 49 insertions(+), 21 deletions(-) create mode 100644 liquibase/changelog/changes/001-get-party-description-rollback.sql create mode 100644 liquibase/changelog/changes/001-get-party-description.xml delete mode 100644 liquibase/changelog/db.changelog-master.sql create mode 100644 liquibase/changelog/db.changelog-master.xml diff --git a/liquibase/README.md b/liquibase/README.md index b61f14b..3558889 100644 --- a/liquibase/README.md +++ b/liquibase/README.md @@ -1,6 +1,6 @@ # Liquibase -Database migrations for PostgreSQL using [formatted SQL](https://docs.liquibase.com/concepts/changelogs/sql-format.html) changelogs (no XML changesets). +Database migrations for PostgreSQL. **SQL files are plain PostgreSQL** — no Liquibase directives inside them. Liquibase metadata lives only in the small XML changelogs that reference those files via `sqlFile`. ## Layout @@ -8,9 +8,11 @@ Database migrations for PostgreSQL using [formatted SQL](https://docs.liquibase. liquibase/ liquibase.properties changelog/ - db.changelog-master.sql + db.changelog-master.xml changes/ - 001-get-party-description.sql # formatted SQL changeset + 001-get-party-description.xml # changeset (sqlFile refs only) + 001-get-party-description.sql # native SQL + 001-get-party-description-rollback.sql # native rollback SQL ``` ## Prerequisites @@ -29,8 +31,6 @@ cp liquibase.properties.example liquibase.local.properties ## Run -From the repository root: - ```bash cd liquibase liquibase update @@ -42,16 +42,18 @@ Preview SQL: liquibase update-sql ``` -Rollback the function changeset: +Rollback: ```bash liquibase rollback-count 1 ``` +You can also run the native `.sql` files directly with `psql` if you are not using Liquibase. + ## Changesets -| Id | File | Description | -|----|------|-------------| -| `001-get-party-description` | `changes/001-get-party-description.sql` | Creates `public.get_party_description(bigint)` with `splitStatements:false` for `$function$` | +| Id | Apply SQL | Rollback SQL | +|----|-----------|--------------| +| `001-get-party-description` | `changes/001-get-party-description.sql` | `changes/001-get-party-description-rollback.sql` | -The changeset uses inline SQL (not `sqlFile`). Rollback is declared with `--rollback` in the same file. +`splitStatements="false"` is set on `sqlFile` so PostgreSQL `$function$` delimiters are not split. diff --git a/liquibase/changelog/changes/001-get-party-description-rollback.sql b/liquibase/changelog/changes/001-get-party-description-rollback.sql new file mode 100644 index 0000000..fb36d3a --- /dev/null +++ b/liquibase/changelog/changes/001-get-party-description-rollback.sql @@ -0,0 +1 @@ +DROP FUNCTION IF EXISTS public.get_party_description(bigint); diff --git a/liquibase/changelog/changes/001-get-party-description.sql b/liquibase/changelog/changes/001-get-party-description.sql index 0b9f714..4fd6a9c 100644 --- a/liquibase/changelog/changes/001-get-party-description.sql +++ b/liquibase/changelog/changes/001-get-party-description.sql @@ -1,7 +1,3 @@ ---liquibase formatted sql - ---changeset ctl:001-get-party-description splitStatements:false ---comment: Read-only PL/pgSQL function get_party_description(party_id) CREATE OR REPLACE FUNCTION public.get_party_description(p_party_id bigint) RETURNS text LANGUAGE plpgsql @@ -25,5 +21,3 @@ $function$; COMMENT ON FUNCTION public.get_party_description(bigint) IS 'Returns description for the given party_id; NULL if the row does not exist.'; - ---rollback DROP FUNCTION IF EXISTS public.get_party_description(bigint); diff --git a/liquibase/changelog/changes/001-get-party-description.xml b/liquibase/changelog/changes/001-get-party-description.xml new file mode 100644 index 0000000..ca15a28 --- /dev/null +++ b/liquibase/changelog/changes/001-get-party-description.xml @@ -0,0 +1,24 @@ + + + + + Read-only PL/pgSQL function get_party_description(party_id) + + + + + + + + + diff --git a/liquibase/changelog/db.changelog-master.sql b/liquibase/changelog/db.changelog-master.sql deleted file mode 100644 index ad55b01..0000000 --- a/liquibase/changelog/db.changelog-master.sql +++ /dev/null @@ -1,3 +0,0 @@ ---liquibase formatted sql - ---include file:changes/001-get-party-description.sql diff --git a/liquibase/changelog/db.changelog-master.xml b/liquibase/changelog/db.changelog-master.xml new file mode 100644 index 0000000..eeea4ee --- /dev/null +++ b/liquibase/changelog/db.changelog-master.xml @@ -0,0 +1,10 @@ + + + + + + diff --git a/liquibase/liquibase.properties b/liquibase/liquibase.properties index 5889d0b..28ef77e 100644 --- a/liquibase/liquibase.properties +++ b/liquibase/liquibase.properties @@ -1,7 +1,7 @@ # Copy to liquibase.local.properties and override for your environment. # Liquibase loads liquibase.local.properties automatically when present. -changeLogFile=changelog/db.changelog-master.sql +changeLogFile=changelog/db.changelog-master.xml url=jdbc:postgresql://localhost:5432/ctl username=postgres diff --git a/liquibase/liquibase.properties.example b/liquibase/liquibase.properties.example index d4a1b08..1938c68 100644 --- a/liquibase/liquibase.properties.example +++ b/liquibase/liquibase.properties.example @@ -1,4 +1,4 @@ -changeLogFile=changelog/db.changelog-master.sql +changeLogFile=changelog/db.changelog-master.xml url=jdbc:postgresql://localhost:5432/your_database username=your_user