Feat/rep 1379 - additional PG-18 container with switch command to go switch between postgresql 14 and 18 (bidirectional switch)#6
Conversation
📝 WalkthroughWalkthroughInfrastructure and documentation for PostgreSQL 18 parallel deployment and migration are added, including Docker compose setup, shell utilities for backup/restore/validation, configuration switching scripts, and database initialization, along with a comprehensive migration guide and DDEV workflow documentation. Changes
Sequence Diagram(s)sequenceDiagram
actor User
participant Config as Config Manager
participant PG14 as PostgreSQL 14
participant Backup as backup-pg14
participant PG18 as PostgreSQL 18
participant Import as import-db18
participant Validate as validate-pg18-migration
participant Switch as switch-pg-version
participant Cache as Cache System
User->>Backup: Request backup
Backup->>PG14: Query DB size
Backup->>Backup: Generate timestamped dump
Backup->>User: Return dump path
User->>Import: Import dump to PG18
Import->>Import: Detect compression (.gz)
Import->>PG18: Stream SQL or decompress & stream
PG18->>User: Confirm import
User->>Validate: Validate migration
Validate->>PG14: Fetch version, schemas, tables, extensions
Validate->>PG18: Fetch version, schemas, tables, extensions
Validate->>Validate: Compare counts & check gin_trgm_ops indexes
Validate->>User: Report differences & reindex needs
User->>Switch: Switch to PostgreSQL 18
Switch->>Config: Verify versioned config files exist
Switch->>Config: Backup current active config (if unversioned)
Switch->>Config: Copy pg18 config to active location
Switch->>User: Confirm config switched
User->>Cache: Clear application cache
Cache->>User: Cache cleared
User->>User: Restart DDEV & verify
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~20 minutes Poem
🚥 Pre-merge checks | ✅ 3✅ Passed checks (3 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Actionable comments posted: 9
🧹 Nitpick comments (1)
pg18-pglogical-build/Dockerfile (1)
39-40: Pin source repos to immutable commits/tags for reproducible builds.
pg_backgroundandpgl_ddl_deployare currently floating refs, so builds can drift over time unexpectedly.Also applies to: 47-47, 56-56
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@pg18-pglogical-build/Dockerfile` around lines 39 - 40, The Dockerfile clones external repos with floating refs; update the RUN steps that clone https://github.com/vibhorkum/pg_background into /tmp/pg_background and the pgl_ddl_deploy clone(s) to pin each git clone to an immutable tag or commit hash (e.g., use git clone ... && cd /tmp/pg_background && git checkout <COMMIT_OR_TAG>) so builds are reproducible; ensure you replace the floating clone commands for both pg_background and pgl_ddl_deploy with clones that checkout a specific commit or tag and document the chosen commit/tag in the Dockerfile comments.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@commands/host/backup-pg14`:
- Around line 23-31: The pipeline currently assigns EXIT_CODE=$? which captures
tee's exit status, not pg_dump's; after running the pg_dump ... 2>&1 | tee -a
"$LOG_FILE" pipeline, set EXIT_CODE from the bash PIPESTATUS array (e.g.
EXIT_CODE=${PIPESTATUS[0]}) so you record pg_dump's exit code (ensure the script
runs under bash where PIPESTATUS exists); reference the pg_dump invocation and
the variables OUTPUT_FILE, LOG_FILE and EXIT_CODE when making this change.
In `@commands/host/import-db18`:
- Around line 22-33: The script can report success despite decompression or SQL
errors because piped commands don't propagate failures and psql isn't set to
stop on SQL errors; enable bash's pipefail (set -o pipefail) and export or pass
ON_ERROR_STOP=1 to psql invocations so any SQL error causes non-zero exit, and
replace the current "$?" check with a direct test of the full pipeline/command
exit status (e.g. check "$?" immediately after the command or use if ...; then)
for both the gunzip | psql branch and the direct psql branch to ensure failures
in gunzip or psql are detected; update references to FILE, gunzip, and psql
invocation to include ON_ERROR_STOP=1 and rely on pipefail for the piped case.
In `@commands/host/switch-pg-version`:
- Around line 43-57: The replacement flow currently deletes $active_file before
confirming the new config was staged, risking a missing active config if cp
fails; instead, stage the selected source ($pg18_file or $pg14_file based on
$VERSION) into a temporary file (e.g., using mktemp), verify the copy succeeded,
then atomically move (mv) the temp into place to replace $active_file and remove
the old symlink/file only as part of the atomic rename; ensure the backup step
for $active_file still runs before staging, and update the logic around [ -f
"$active_file" ] / [ -L "$active_file" ] so they are not removed until after the
temp-to-active mv succeeds.
- Around line 65-77: The success summary is printed unconditionally even if some
switch_config calls fail; update the block that calls switch_config "db_write",
"db_read", "db_read_report", and "common" to check each call's return code,
accumulate a failure flag (or count), and only print the "✓ Successfully
switched to PostgreSQL $VERSION" and the success list when all calls succeed; if
any fail, print a clear error/partial-failure summary and exit with a non-zero
status (e.g., exit 1) instead of the current unconditional echo block.
In `@commands/host/validate-pg18-migration`:
- Around line 11-26: The connection checks reuse $? incorrectly causing the
PG14/PG18 results to be misreported; fix by capturing and testing each command's
exit status immediately after its psql invocation (either run each psql in its
own if statement, e.g. if PGPASSWORD=... psql ...; then echo success/exit on
failure, or store the exit code into distinct variables like PG14_EXIT=$? and
PG18_EXIT=$? right after assigning PG14_CONN and PG18_CONN) and use those
specific statuses when printing the "PostgreSQL 14/18: Connected/Connection
failed" messages; reference the PG14_CONN and PG18_CONN assignments and the two
if blocks to locate where to check $? or store PG14_EXIT/PG18_EXIT so each DB is
validated independently.
In `@config.yaml`:
- Line 6: The config change permanently enabling Xdebug (xdebug_enabled) is
unrelated and can hurt developer performance; revert it by restoring
xdebug_enabled to false or removing the override in config.yaml so developers
use ddev xdebug / ddev xdebug off as intended, or if this was intentional update
the same file to include a clear comment explaining why Xdebug must be enabled
globally and confirm the decision in the PR; locate the xdebug_enabled entry in
config.yaml to apply the change.
In `@postgres18-init/01-init-roles.sql`:
- Around line 37-40: The CREATE COLLATION statement for app.en_natural can fail
if the app schema doesn’t exist; before creating the collation (CREATE COLLATION
app.en_natural ...) add an idempotent step to ensure the schema exists (for
example run CREATE SCHEMA IF NOT EXISTS app) so the collation creation won’t
error on first-run; update the migration around the CREATE COLLATION
app.en_natural to create the app schema first and keep the existing IF NOT
EXISTS/existence checks to remain idempotent.
In `@POSTGRESQL_18_CHANGES.md`:
- Around line 62-72: The query is selecting non-existent columns schemaname and
tablename from pg_namespace/pg_class; update the SELECT and any WHERE/filters to
use n.nspname and c.relname (or alias them, e.g., n.nspname AS schemaname and
c.relname AS tablename) so the query in the block referencing pg_attribute a,
pg_class c and pg_namespace n returns correct schema and table names; keep the
attidentity = 'a' filter and the schemaname IN ('app','rrule') condition but
apply it to n.nspname (or your chosen alias) instead of schemaname.
- Around line 218-222: The query incorrectly calls pg_get_expr(c.reloptions,
c.oid) — reloptions in pg_class is a text[] of keyword=value strings, not a
pg_node_tree, so pg_get_expr will fail; replace that expression by either
selecting c.reloptions directly (or unnesting it) to read options, and if you
need inheritance info join pg_inherits (using inhparent/inhrelid) to retrieve
parent/child relationships; update references to pg_get_expr -> reloptions (or
unnest(reloptions)) and add a join to pg_inherits when querying inheritance.
---
Nitpick comments:
In `@pg18-pglogical-build/Dockerfile`:
- Around line 39-40: The Dockerfile clones external repos with floating refs;
update the RUN steps that clone https://github.com/vibhorkum/pg_background into
/tmp/pg_background and the pgl_ddl_deploy clone(s) to pin each git clone to an
immutable tag or commit hash (e.g., use git clone ... && cd /tmp/pg_background
&& git checkout <COMMIT_OR_TAG>) so builds are reproducible; ensure you replace
the floating clone commands for both pg_background and pgl_ddl_deploy with
clones that checkout a specific commit or tag and document the chosen commit/tag
in the Dockerfile comments.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: b7649b38-4966-4703-86a1-43cf33b9679e
📒 Files selected for processing (10)
POSTGRESQL_18_CHANGES.mdcommands/PG-SWITCH-README.mdcommands/host/backup-pg14commands/host/import-db18commands/host/switch-pg-versioncommands/host/validate-pg18-migrationconfig.yamldocker-compose.postgres18.yamlpg18-pglogical-build/Dockerfilepostgres18-init/01-init-roles.sql
| pg_dump -h localhost -p 5432 -U db -d db \ | ||
| --format=custom \ | ||
| --verbose \ | ||
| --no-owner \ | ||
| --no-acl \ | ||
| --file="$OUTPUT_FILE" \ | ||
| 2>&1 | tee -a "$LOG_FILE" | ||
|
|
||
| EXIT_CODE=$? |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
# Quick static check for pipeline exit-code handling in this script.
rg -n -C2 'pg_dump|PIPESTATUS|set -o pipefail|EXIT_CODE=\$\?' commands/host/backup-pg14Repository: sandeepfd/fdsu_ddev
Length of output: 268
🏁 Script executed:
cat -n commands/host/backup-pg14Repository: sandeepfd/fdsu_ddev
Length of output: 1754
Fix pipeline exit-code capture for pg_dump command.
The current code captures tee's exit code (via EXIT_CODE=$?), not pg_dump's. A failed backup can be silently misreported as successful while the script exits with code 0.
Proposed fix
#!/bin/bash
+set -o pipefail
@@
pg_dump -h localhost -p 5432 -U db -d db \
@@
--file="$OUTPUT_FILE" \
2>&1 | tee -a "$LOG_FILE"
-EXIT_CODE=$?
+EXIT_CODE=${PIPESTATUS[0]}🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@commands/host/backup-pg14` around lines 23 - 31, The pipeline currently
assigns EXIT_CODE=$? which captures tee's exit status, not pg_dump's; after
running the pg_dump ... 2>&1 | tee -a "$LOG_FILE" pipeline, set EXIT_CODE from
the bash PIPESTATUS array (e.g. EXIT_CODE=${PIPESTATUS[0]}) so you record
pg_dump's exit code (ensure the script runs under bash where PIPESTATUS exists);
reference the pg_dump invocation and the variables OUTPUT_FILE, LOG_FILE and
EXIT_CODE when making this change.
| if [[ "$FILE" == *.gz ]]; then | ||
| gunzip -c "$FILE" | PGPASSWORD=db psql -h 127.0.0.1 -p 5433 -U db -d db | ||
| else | ||
| PGPASSWORD=db psql -h 127.0.0.1 -p 5433 -U db -d db < "$FILE" | ||
| fi | ||
|
|
||
| if [ $? -eq 0 ]; then | ||
| echo "✓ Successfully imported into PostgreSQL 18" | ||
| else | ||
| echo "✗ Import failed" | ||
| exit 1 | ||
| fi |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
# Static verification of import error-handling flags/status capture.
rg -n -C2 'ON_ERROR_STOP|pipefail|PIPESTATUS|if \[ \$\? -eq 0 \]' commands/host/import-db18Repository: sandeepfd/fdsu_ddev
Length of output: 162
🏁 Script executed:
cat -n commands/host/import-db18Repository: sandeepfd/fdsu_ddev
Length of output: 1034
Import can report success on partial/failed SQL execution.
The piped gunzip command lacks pipefail error propagation, and both psql invocations lack ON_ERROR_STOP=1, allowing SQL errors and decompression failures to be silently ignored. Line 28 uses $? which only captures the exit code of psql in the pipe, not gunzip.
Proposed fix
#!/bin/bash
+set -o pipefail
## Description: Import database dump into PostgreSQL 18
## Usage: import-db18 [file]
@@ -20,14 +21,18 @@
echo "Importing $FILE into PostgreSQL 18..."
if [[ "$FILE" == *.gz ]]; then
- gunzip -c "$FILE" | PGPASSWORD=db psql -h 127.0.0.1 -p 5433 -U db -d db
+ gunzip -c "$FILE" | PGPASSWORD=db psql -v ON_ERROR_STOP=1 -h 127.0.0.1 -p 5433 -U db -d db
+ IMPORT_EXIT=${PIPESTATUS[1]}
else
- PGPASSWORD=db psql -h 127.0.0.1 -p 5433 -U db -d db < "$FILE"
+ PGPASSWORD=db psql -v ON_ERROR_STOP=1 -h 127.0.0.1 -p 5433 -U db -d db < "$FILE"
+ IMPORT_EXIT=$?
fi
-if [ $? -eq 0 ]; then
+if [ $IMPORT_EXIT -eq 0 ]; then
echo "✓ Successfully imported into PostgreSQL 18"
else
echo "✗ Import failed"
exit 1
fi🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@commands/host/import-db18` around lines 22 - 33, The script can report
success despite decompression or SQL errors because piped commands don't
propagate failures and psql isn't set to stop on SQL errors; enable bash's
pipefail (set -o pipefail) and export or pass ON_ERROR_STOP=1 to psql
invocations so any SQL error causes non-zero exit, and replace the current "$?"
check with a direct test of the full pipeline/command exit status (e.g. check
"$?" immediately after the command or use if ...; then) for both the gunzip |
psql branch and the direct psql branch to ensure failures in gunzip or psql are
detected; update references to FILE, gunzip, and psql invocation to include
ON_ERROR_STOP=1 and rely on pipefail for the piped case.
| mv "$active_file" "${active_file}.backup.$(date +%Y%m%d_%H%M%S)" | ||
| echo " Backed up existing $base_name-local.php" | ||
| fi | ||
| fi | ||
|
|
||
| # Remove existing active file (if it's a symlink or regular file) | ||
| [ -f "$active_file" ] && rm -f "$active_file" | ||
| [ -L "$active_file" ] && rm -f "$active_file" | ||
|
|
||
| # Copy the appropriate versioned file to the active file | ||
| if [ "$VERSION" == "18" ]; then | ||
| cp "$pg18_file" "$active_file" | ||
| else | ||
| cp "$pg14_file" "$active_file" | ||
| fi |
There was a problem hiding this comment.
Non-atomic replacement can leave configs missing on copy failure.
On Line 43 and Lines 49-57, the current flow removes the active file before confirming the new file is safely staged. If cp fails, the app can be left without an active config.
Suggested safer replacement flow
- mv "$active_file" "${active_file}.backup.$(date +%Y%m%d_%H%M%S)"
+ cp "$active_file" "${active_file}.backup.$(date +%Y%m%d_%H%M%S)"
echo " Backed up existing $base_name-local.php"
fi
fi
-
- # Remove existing active file (if it's a symlink or regular file)
- [ -f "$active_file" ] && rm -f "$active_file"
- [ -L "$active_file" ] && rm -f "$active_file"
-
- # Copy the appropriate versioned file to the active file
- if [ "$VERSION" == "18" ]; then
- cp "$pg18_file" "$active_file"
- else
- cp "$pg14_file" "$active_file"
- fi
+
+ local source_file tmp_file
+ if [ "$VERSION" == "18" ]; then
+ source_file="$pg18_file"
+ else
+ source_file="$pg14_file"
+ fi
+ tmp_file="${active_file}.tmp.$$"
+
+ if ! cp "$source_file" "$tmp_file"; then
+ echo "✗ Failed to stage $source_file"
+ rm -f "$tmp_file"
+ return 1
+ fi
+ if ! mv -f "$tmp_file" "$active_file"; then
+ echo "✗ Failed to activate $active_file"
+ rm -f "$tmp_file"
+ return 1
+ fi🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@commands/host/switch-pg-version` around lines 43 - 57, The replacement flow
currently deletes $active_file before confirming the new config was staged,
risking a missing active config if cp fails; instead, stage the selected source
($pg18_file or $pg14_file based on $VERSION) into a temporary file (e.g., using
mktemp), verify the copy succeeded, then atomically move (mv) the temp into
place to replace $active_file and remove the old symlink/file only as part of
the atomic rename; ensure the backup step for $active_file still runs before
staging, and update the logic around [ -f "$active_file" ] / [ -L "$active_file"
] so they are not removed until after the temp-to-active mv succeeds.
| switch_config "db_write" | ||
| switch_config "db_read" | ||
| switch_config "db_read_report" | ||
| switch_config "common" | ||
|
|
||
| echo "" | ||
| echo "✓ Successfully switched to PostgreSQL $VERSION" | ||
| echo "" | ||
| echo "Current active configuration:" | ||
| echo " - db_write-local.php → PostgreSQL $VERSION" | ||
| echo " - db_read-local.php → PostgreSQL $VERSION" | ||
| echo " - db_read_report-local.php → PostgreSQL $VERSION" | ||
| echo " - common-local.php → PostgreSQL $VERSION" |
There was a problem hiding this comment.
Final success summary is printed even when one or more switches fail.
switch_config return codes are ignored on Lines 65-68, then Line 71 reports success unconditionally. This can mislead operators during partial failures.
Track failures and exit non-zero on partial/total failure
-# Switch all config files
-echo ""
-switch_config "db_write"
-switch_config "db_read"
-switch_config "db_read_report"
-switch_config "common"
+echo ""
+failures=0
+for base in db_write db_read db_read_report common; do
+ if ! switch_config "$base"; then
+ failures=$((failures + 1))
+ fi
+done
+
+if [ "$failures" -gt 0 ]; then
+ echo ""
+ echo "✗ Switch completed with $failures failure(s)"
+ exit 1
+fi
echo ""
echo "✓ Successfully switched to PostgreSQL $VERSION"🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@commands/host/switch-pg-version` around lines 65 - 77, The success summary is
printed unconditionally even if some switch_config calls fail; update the block
that calls switch_config "db_write", "db_read", "db_read_report", and "common"
to check each call's return code, accumulate a failure flag (or count), and only
print the "✓ Successfully switched to PostgreSQL $VERSION" and the success list
when all calls succeed; if any fail, print a clear error/partial-failure summary
and exit with a non-zero status (e.g., exit 1) instead of the current
unconditional echo block.
| PG14_CONN=$(PGPASSWORD=db psql -h localhost -p 5432 -U db -d db -t -c "SELECT 1;" 2>&1) | ||
| PG18_CONN=$(PGPASSWORD=db psql -h localhost -p 5433 -U db -d db -t -c "SELECT 1;" 2>&1) | ||
|
|
||
| if [ $? -eq 0 ]; then | ||
| echo " ✓ PostgreSQL 14: Connected" | ||
| else | ||
| echo " ✗ PostgreSQL 14: Connection failed" | ||
| exit 1 | ||
| fi | ||
|
|
||
| if [ $? -eq 0 ]; then | ||
| echo " ✓ PostgreSQL 18: Connected" | ||
| else | ||
| echo " ✗ PostgreSQL 18: Connection failed" | ||
| exit 1 | ||
| fi |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
# Static verification of fragile `$?` usage in connection checks.
rg -n -C2 '\$\?|PG14_CONN|PG18_CONN|SELECT 1' commands/host/validate-pg18-migrationRepository: sandeepfd/fdsu_ddev
Length of output: 473
Connection validation logic is broken due to $? reuse — PostgreSQL 14 is never tested and PostgreSQL 18 status is ignored.
After assigning both variables on lines 11–12, the first if at line 14 checks the exit status of the second assignment (PG18), not the first (PG14). The second if at line 21 then checks the exit status of the first if statement itself, not the PG18 assignment. This means the script reports PG18 connectivity status as "PostgreSQL 14" and never validates PG18 at all.
Proposed fix
-PG14_CONN=$(PGPASSWORD=db psql -h localhost -p 5432 -U db -d db -t -c "SELECT 1;" 2>&1)
-PG18_CONN=$(PGPASSWORD=db psql -h localhost -p 5433 -U db -d db -t -c "SELECT 1;" 2>&1)
-
-if [ $? -eq 0 ]; then
+if PGPASSWORD=db psql -h localhost -p 5432 -U db -d db -t -c "SELECT 1;" >/dev/null 2>&1; then
echo " ✓ PostgreSQL 14: Connected"
else
echo " ✗ PostgreSQL 14: Connection failed"
exit 1
fi
-if [ $? -eq 0 ]; then
+if PGPASSWORD=db psql -h localhost -p 5433 -U db -d db -t -c "SELECT 1;" >/dev/null 2>&1; then
echo " ✓ PostgreSQL 18: Connected"
else
echo " ✗ PostgreSQL 18: Connection failed"
exit 1
fi🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@commands/host/validate-pg18-migration` around lines 11 - 26, The connection
checks reuse $? incorrectly causing the PG14/PG18 results to be misreported; fix
by capturing and testing each command's exit status immediately after its psql
invocation (either run each psql in its own if statement, e.g. if PGPASSWORD=...
psql ...; then echo success/exit on failure, or store the exit code into
distinct variables like PG14_EXIT=$? and PG18_EXIT=$? right after assigning
PG14_CONN and PG18_CONN) and use those specific statuses when printing the
"PostgreSQL 14/18: Connected/Connection failed" messages; reference the
PG14_CONN and PG18_CONN assignments and the two if blocks to locate where to
check $? or store PG14_EXIT/PG18_EXIT so each DB is validated independently.
| php_version: "8.0" | ||
| webserver_type: nginx-fpm | ||
| xdebug_enabled: false | ||
| xdebug_enabled: true |
There was a problem hiding this comment.
Unrelated change: Xdebug permanently enabled may degrade performance.
This change enables Xdebug permanently but appears unrelated to the PR objective (PostgreSQL 18 container). The config file's own comments (lines 57-60) warn that "leaving Xdebug enabled all the time is a big performance hit" and recommend using ddev xdebug / ddev xdebug off commands instead.
If this was intentional, please confirm. Otherwise, consider reverting to avoid impacting all developers using this config.
Suggested revert if unintentional
-xdebug_enabled: true
+xdebug_enabled: false📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| xdebug_enabled: true | |
| xdebug_enabled: false |
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@config.yaml` at line 6, The config change permanently enabling Xdebug
(xdebug_enabled) is unrelated and can hurt developer performance; revert it by
restoring xdebug_enabled to false or removing the override in config.yaml so
developers use ddev xdebug / ddev xdebug off as intended, or if this was
intentional update the same file to include a clear comment explaining why
Xdebug must be enabled globally and confirm the decision in the PR; locate the
xdebug_enabled entry in config.yaml to apply the change.
| CREATE COLLATION app.en_natural ( | ||
| LOCALE = 'en-US-u-kn-true', | ||
| PROVIDER = icu | ||
| ); |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
# Verify whether any init script creates schema `app` before this file runs.
fd -a . postgres18-init
rg -n -C2 'CREATE[[:space:]]+SCHEMA[[:space:]]+app|CREATE[[:space:]]+COLLATION[[:space:]]+app\.en_natural' postgres18-initRepository: sandeepfd/fdsu_ddev
Length of output: 484
🏁 Script executed:
cat -n postgres18-init/01-init-roles.sqlRepository: sandeepfd/fdsu_ddev
Length of output: 1798
🏁 Script executed:
# Check if there are other init scripts and if any create schema app
ls -la postgres18-init/
rg 'CREATE[[:space:]]+SCHEMA[[:space:]]+app' postgres18-init/Repository: sandeepfd/fdsu_ddev
Length of output: 243
🏁 Script executed:
# Search entire repository for CREATE SCHEMA app to see if it's created elsewhere
rg 'CREATE[[:space:]]+SCHEMA[[:space:]]+app' --type sqlRepository: sandeepfd/fdsu_ddev
Length of output: 45
🏁 Script executed:
# Final check: verify app schema doesn't exist in any initialization context
find . -name "*.sql" -o -name "*init*" -o -name "*docker*" | head -20
rg 'CREATE.*SCHEMA' --type sql | grep -v '.git'Repository: sandeepfd/fdsu_ddev
Length of output: 241
Ensure schema app exists before creating app.en_natural collation.
The collation creation at lines 37-40 will fail with "schema app does not exist" if the schema hasn't been created yet. The current error handling only checks for collation existence, not schema existence. This will break container initialization on first boot.
Proposed fix
DO $$
BEGIN
+ IF NOT EXISTS (SELECT 1 FROM pg_namespace WHERE nspname = 'app') THEN
+ EXECUTE 'CREATE SCHEMA app';
+ END IF;
+
-- Check if en_natural collation exists in app schema
IF NOT EXISTS (
SELECT 1 FROM pg_collation c📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| CREATE COLLATION app.en_natural ( | |
| LOCALE = 'en-US-u-kn-true', | |
| PROVIDER = icu | |
| ); | |
| DO $$ | |
| BEGIN | |
| IF NOT EXISTS (SELECT 1 FROM pg_namespace WHERE nspname = 'app') THEN | |
| EXECUTE 'CREATE SCHEMA app'; | |
| END IF; | |
| -- Check if en_natural collation exists in app schema | |
| IF NOT EXISTS ( | |
| SELECT 1 FROM pg_collation c | |
| WHERE c.collname = 'en_natural' | |
| AND n.nspname = 'app' | |
| ) THEN | |
| CREATE COLLATION app.en_natural ( | |
| LOCALE = 'en-US-u-kn-true', | |
| PROVIDER = icu | |
| ); | |
| END IF; | |
| END $$; |
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@postgres18-init/01-init-roles.sql` around lines 37 - 40, The CREATE COLLATION
statement for app.en_natural can fail if the app schema doesn’t exist; before
creating the collation (CREATE COLLATION app.en_natural ...) add an idempotent
step to ensure the schema exists (for example run CREATE SCHEMA IF NOT EXISTS
app) so the collation creation won’t error on first-run; update the migration
around the CREATE COLLATION app.en_natural to create the app schema first and
keep the existing IF NOT EXISTS/existence checks to remain idempotent.
| SELECT | ||
| schemaname, | ||
| tablename, | ||
| attname, | ||
| attidentity | ||
| FROM pg_attribute a | ||
| JOIN pg_class c ON a.attrelid = c.oid | ||
| JOIN pg_namespace n ON c.relnamespace = n.oid | ||
| WHERE attidentity = 'a' -- 'a' = ALWAYS | ||
| AND schemaname IN ('app', 'rrule'); | ||
| ``` |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
# Validate corrected query compiles on PG18.
PGPASSWORD=db psql -h localhost -p 5433 -U db -d db -v ON_ERROR_STOP=1 <<'SQL'
SELECT
n.nspname AS schemaname,
c.relname AS tablename,
a.attname,
a.attidentity
FROM pg_attribute a
JOIN pg_class c ON a.attrelid = c.oid
JOIN pg_namespace n ON c.relnamespace = n.oid
WHERE a.attidentity = 'a'
AND n.nspname IN ('app', 'rrule')
LIMIT 5;
SQLRepository: sandeepfd/fdsu_ddev
Length of output: 107
🏁 Script executed:
git ls-files | head -20Repository: sandeepfd/fdsu_ddev
Length of output: 582
🏁 Script executed:
fd -i postgresqlRepository: sandeepfd/fdsu_ddev
Length of output: 87
🏁 Script executed:
cat -n POSTGRESQL_18_CHANGES.md | sed -n '60,75p'Repository: sandeepfd/fdsu_ddev
Length of output: 568
🌐 Web query:
PostgreSQL 18 pg_attribute pg_class pg_namespace catalog columns nspname relname attname
💡 Result:
In PostgreSQL 18, the system catalogs pg_namespace, pg_class, and pg_attribute store metadata about schemas, tables/relations, and columns/attributes, respectively. The specified columns are: - pg_namespace.nspname (name): Name of the namespace (schema). - pg_class.relname (name): Name of the table, index, view, etc. (relation). - pg_attribute.attname (name): The column name (attribute). These catalogs are linked as follows: - pg_class.relnamespace (oid) references pg_namespace.oid - pg_attribute.attrelid (oid) references pg_class.oid A common query to list schema name, table name, and column name for user tables is: SELECT n.nspname AS schema_name, c.relname AS table_name, a.attname AS column_name FROM pg_namespace n JOIN pg_class c ON c.relnamespace = n.oid JOIN pg_attribute a ON a.attrelid = c.oid WHERE c.relkind = 'r' -- ordinary tables AND a.attnum > 0 -- user columns only AND NOT a.attisdropped ORDER BY n.nspname, c.relname, a.attnum; This joins the catalogs using the specified columns to retrieve schema.table.column information from the PostgreSQL 18 system catalogs.
Citations:
- 1: https://www.postgresql.org/docs/18/catalog-pg-attribute.html
- 2: http://www.postgresql.org/docs/18/catalog-pg-class.html
- 3: https://www.postgresql.org/docs/current/catalog-pg-attribute.html
- 4: https://www.postgresql.org/docs/current/catalog-pg-class.html
- 5: https://www.postgresql.org/docs/current/catalog-pg-namespace.html
- 6: https://www.postgresql.org/docs/18/catalog-pg-namespace.html
- 7: https://stackoverflow.com/questions/37099663/listing-all-relations-with-schema-and-columns-in-postgresql
The catalog query uses non-existent columns (schemaname, tablename).
This snippet will fail as written; use n.nspname and c.relname when reading from pg_namespace/pg_class.
Proposed fix
SELECT
- schemaname,
- tablename,
+ n.nspname AS schemaname,
+ c.relname AS tablename,
attname,
attidentity
FROM pg_attribute a
JOIN pg_class c ON a.attrelid = c.oid
JOIN pg_namespace n ON c.relnamespace = n.oid
WHERE attidentity = 'a' -- 'a' = ALWAYS
-AND schemaname IN ('app', 'rrule');
+AND n.nspname IN ('app', 'rrule');📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| SELECT | |
| schemaname, | |
| tablename, | |
| attname, | |
| attidentity | |
| FROM pg_attribute a | |
| JOIN pg_class c ON a.attrelid = c.oid | |
| JOIN pg_namespace n ON c.relnamespace = n.oid | |
| WHERE attidentity = 'a' -- 'a' = ALWAYS | |
| AND schemaname IN ('app', 'rrule'); | |
| ``` | |
| SELECT | |
| n.nspname AS schemaname, | |
| c.relname AS tablename, | |
| attname, | |
| attidentity | |
| FROM pg_attribute a | |
| JOIN pg_class c ON a.attrelid = c.oid | |
| JOIN pg_namespace n ON c.relnamespace = n.oid | |
| WHERE attidentity = 'a' -- 'a' = ALWAYS | |
| AND n.nspname IN ('app', 'rrule'); |
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@POSTGRESQL_18_CHANGES.md` around lines 62 - 72, The query is selecting
non-existent columns schemaname and tablename from pg_namespace/pg_class; update
the SELECT and any WHERE/filters to use n.nspname and c.relname (or alias them,
e.g., n.nspname AS schemaname and c.relname AS tablename) so the query in the
block referencing pg_attribute a, pg_class c and pg_namespace n returns correct
schema and table names; keep the attidentity = 'a' filter and the schemaname IN
('app','rrule') condition but apply it to n.nspname (or your chosen alias)
instead of schemaname.
| SELECT | ||
| n.nspname as schema, | ||
| c.relname as table_name, | ||
| pg_get_expr(c.reloptions, c.oid) as options | ||
| FROM pg_class c |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
# Validate corrected inheritance query compiles on PG18.
PGPASSWORD=db psql -h localhost -p 5433 -U db -d db -v ON_ERROR_STOP=1 <<'SQL'
SELECT
child_ns.nspname AS child_schema,
child.relname AS child_table,
parent_ns.nspname AS parent_schema,
parent.relname AS parent_table
FROM pg_inherits i
JOIN pg_class child ON child.oid = i.inhrelid
JOIN pg_namespace child_ns ON child_ns.oid = child.relnamespace
JOIN pg_class parent ON parent.oid = i.inhparent
JOIN pg_namespace parent_ns ON parent_ns.oid = parent.relnamespace
LIMIT 5;
SQLRepository: sandeepfd/fdsu_ddev
Length of output: 107
🏁 Script executed:
git ls-files | grep -i postgresql_18_changesRepository: sandeepfd/fdsu_ddev
Length of output: 87
🏁 Script executed:
find . -name "*POSTGRESQL_18_CHANGES*" -o -name "*postgresql_18_changes*"Repository: sandeepfd/fdsu_ddev
Length of output: 89
🌐 Web query:
PostgreSQL pg_get_expr() function behavior with reloptions
💡 Result:
The pg_get_expr function decompiles a pg_node_tree (binary expression representation) into readable SQL text, using the provided relation OID as context for resolving variable references (Vars). It is commonly used with fields like pg_attrdef.adbin for default expressions (pg_get_expr(adbin, adrelid)) or pg_constraint.conbin for check constraints. pg_class.reloptions is a text[] column storing access-method-specific options as "keyword=value" strings (e.g., autovacuum_enabled=false). This is not a pg_node_tree; it is a simple array of text values stored directly as text[]. pg_get_expr is not designed for or used with reloptions. Attempting pg_get_expr(reloptions, oid) would likely error because reloptions lacks the expected pg_node_tree format. To display reloptions, use array_to_string(reloptions, ', ') or similar; no decompilation is needed as it is already human-readable text. No official documentation, source code snippets, or discussions indicate special behavior of pg_get_expr with reloptions. The function's purpose is strictly for node tree expressions, not text arrays.
Citations:
- 1: https://pgpedia.info/p/pg_get_expr.html
- 2: https://www.postgresql.org/docs/9.6/functions-info.html
- 3: https://www.postgresql.org/docs/current/catalog-pg-attrdef.html
- 4: http://www.postgresql.org/docs/16/catalog-pg-class.html
- 5: https://postgrespro.com/docs/postgresql/current/catalog-pg-class
- 6: https://doxygen.postgresql.org/ruleutils_8c_source.html
🌐 Web query:
PostgreSQL reloptions column pg_class what is it
💡 Result:
The reloptions column in PostgreSQL's pg_class system catalog is a text[] (array of text) that stores access-method-specific options for relations (tables, indexes, etc.) as "keyword=value" strings. These options include settings like fillfactor for heap tables, autovacuum parameters, or index-specific configurations such as buffer usage for B-tree indexes. They are set via CREATE TABLE/INDEX or ALTER TABLE SET options and parsed by access methods.
Citations:
- 1: https://www.postgresql.org/docs/current/catalog-pg-class.html
- 2: https://postgrespro.com/docs/postgresql/current/catalog-pg-class
- 3: https://www.postgresql.org/docs/16/catalog-pg-class.html
🏁 Script executed:
head -n 230 POSTGRESQL_18_CHANGES.md | tail -n 20Repository: sandeepfd/fdsu_ddev
Length of output: 660
pg_get_expr(c.reloptions, c.oid) is invalid—reloptions is a text array, not a parse tree expression.
reloptions in pg_class stores access-method-specific options as simple text[] (keyword=value strings), not as pg_node_tree binary expressions. The pg_get_expr() function decompiles only pg_node_tree structures (used for defaults, constraints, etc.) and will error or produce incorrect results when applied to reloptions. For querying table inheritance relationships, directly join pg_inherits instead.
Corrected query
SELECT
- n.nspname as schema,
- c.relname as table_name,
- pg_get_expr(c.reloptions, c.oid) as options
-FROM pg_class c
-JOIN pg_namespace n ON c.relnamespace = n.oid
-WHERE c.relkind = 'r'
- AND c.relispartition = false
- AND EXISTS (
- SELECT 1 FROM pg_inherits
- WHERE inhrelid = c.oid
- );
+ child_ns.nspname AS child_schema,
+ child.relname AS child_table,
+ parent_ns.nspname AS parent_schema,
+ parent.relname AS parent_table
+FROM pg_inherits i
+JOIN pg_class child ON child.oid = i.inhrelid
+JOIN pg_namespace child_ns ON child_ns.oid = child.relnamespace
+JOIN pg_class parent ON parent.oid = i.inhparent
+JOIN pg_namespace parent_ns ON parent_ns.oid = parent.relnamespace;🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@POSTGRESQL_18_CHANGES.md` around lines 218 - 222, The query incorrectly calls
pg_get_expr(c.reloptions, c.oid) — reloptions in pg_class is a text[] of
keyword=value strings, not a pg_node_tree, so pg_get_expr will fail; replace
that expression by either selecting c.reloptions directly (or unnesting it) to
read options, and if you need inheritance info join pg_inherits (using
inhparent/inhrelid) to retrieve parent/child relationships; update references to
pg_get_expr -> reloptions (or unnest(reloptions)) and add a join to pg_inherits
when querying inheritance.
Summary by CodeRabbit
Release Notes
Documentation
New Features