From 738fe0fe2df0accddbd8b4d4a7e0377bd1cb4695 Mon Sep 17 00:00:00 2001 From: peter Date: Tue, 6 Oct 2026 12:18:54 +0100 Subject: [PATCH] find_schema_problems(): report problems in a stable order Several sections had no ORDER BY, so when one section found more than one problem PostgreSQL could return them in any order (found by new break-one-thing tests). Every section is now sorted. Output is otherwise unchanged; no version bump. Co-Authored-By: Claude Opus 5.5 --- sql/09_schema_check.sql | 30 ++++++++++++++++++++---------- 1 file changed, 20 insertions(+), 10 deletions(-) diff --git a/sql/09_schema_check.sql b/sql/09_schema_check.sql index 951d224..73ec36c 100644 --- a/sql/09_schema_check.sql +++ b/sql/09_schema_check.sql @@ -12,7 +12,8 @@ -- meta.find_schema_problems() -- -------------------------------------------------------------------------- -- --- Returns one row per problem: (object, problem). Read-only. +-- Returns one row per problem: (object, problem), in a stable order +-- (section by section, each sorted). Read-only. -- -- The expectations below mirror 00..08. When any of those files changes, -- update this file in the same commit. The release tests (a freshly @@ -42,12 +43,14 @@ BEGIN RETURN QUERY SELECT 'schema ' || s, 'missing' FROM unnest(ARRAY['meta', 'series', 'log']) AS s - WHERE NOT EXISTS (SELECT 1 FROM pg_namespace WHERE nspname = s); + WHERE NOT EXISTS (SELECT 1 FROM pg_namespace WHERE nspname = s) + ORDER BY 1; RETURN QUERY SELECT 'extension ' || e, 'missing' FROM unnest(ARRAY['pg_trgm']) AS e - WHERE NOT EXISTS (SELECT 1 FROM pg_extension WHERE extname = e); + WHERE NOT EXISTS (SELECT 1 FROM pg_extension WHERE extname = e) + ORDER BY 1; RETURN QUERY SELECT 'sequence ' || s, 'missing' @@ -57,7 +60,8 @@ BEGIN 'log.pending_changes_id_seq', 'log.script_log_id_seq' ]) AS s - WHERE to_regclass(s) IS NULL; + WHERE to_regclass(s) IS NULL + ORDER BY 1; -- Tables and columns ---------------------------------------------------- RETURN QUERY @@ -231,7 +235,8 @@ BEGIN ) AS c(tbl, name) WHERE to_regclass(c.tbl) IS NOT NULL AND NOT EXISTS (SELECT 1 FROM pg_constraint k - WHERE k.conrelid = to_regclass(c.tbl) AND k.conname = c.name); + WHERE k.conrelid = to_regclass(c.tbl) AND k.conname = c.name) + ORDER BY 1; RETURN QUERY SELECT 'index ' || i.idx, 'missing' @@ -253,12 +258,14 @@ BEGIN ('log.idx_script_log_series', 'log.script_log'), ('log.idx_script_log_level', 'log.script_log') ) AS i(idx, tbl) - WHERE to_regclass(i.tbl) IS NOT NULL AND to_regclass(i.idx) IS NULL; + WHERE to_regclass(i.tbl) IS NOT NULL AND to_regclass(i.idx) IS NULL + ORDER BY 1; RETURN QUERY SELECT 'view ' || v, 'missing' FROM unnest(ARRAY['log.script_log_recent']) AS v - WHERE to_regclass(v) IS NULL; + WHERE to_regclass(v) IS NULL + ORDER BY 1; -- Functions (compared by whitespace-insensitive body fingerprint) ------- RETURN QUERY @@ -285,7 +292,8 @@ BEGIN WHERE NOT EXISTS (SELECT 1 FROM pg_proc p JOIN pg_namespace n ON n.oid = p.pronamespace WHERE n.nspname || '.' || p.proname = f.name - AND md5(btrim(regexp_replace(p.prosrc, '\s+', ' ', 'g'))) = f.fingerprint); + AND md5(btrim(regexp_replace(p.prosrc, '\s+', ' ', 'g'))) = f.fingerprint) + ORDER BY 1; -- Drop-protection event trigger ----------------------------------------- RETURN QUERY @@ -302,7 +310,8 @@ BEGIN (CASE WHEN t.evtfoid::regproc::text <> 'meta.prevent_direct_drop' THEN 'calls ' || t.evtfoid::regproc::text || ', expected meta.prevent_direct_drop' END) ) AS x(p) - WHERE t.evtname = 'prevent_series_drops' AND p IS NOT NULL; + WHERE t.evtname = 'prevent_series_drops' AND p IS NOT NULL + ORDER BY 2; -- Reference data and version row ---------------------------------------- RETURN QUERY @@ -321,7 +330,8 @@ BEGIN ) AS e(tbl, expected) WHERE to_regclass(e.tbl) IS NOT NULL ) AS r - WHERE r.actual < r.expected; + WHERE r.actual < r.expected + ORDER BY 1; -- Series schema registry ------------------------------------------------ -- Only REGISTERED schemas are examined; user schemas are never looked at.