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
208 changes: 188 additions & 20 deletions regress/expected/age_upgrade.out
Original file line number Diff line number Diff line change
Expand Up @@ -27,16 +27,30 @@
-- upgrade template.
--
-- Compared catalogs:
-- pg_proc — functions, aggregates, procedures (name, args, properties)
-- pg_proc — functions, aggregates, procedures (name, args, properties
-- including probin/prosrc to catch C-symbol renames and
-- SQL-body changes)
-- pg_class — tables, views, sequences, indexes (name, kind)
-- pg_type — types (name, type category)
-- pg_operator — operators (name, left/right types)
-- pg_cast — casts involving AGE types (source, target, context)
-- pg_opclass — operator classes (name, access method)
-- pg_constraint — constraints (name, type, table, referenced table)
-- pg_depend — extension membership (every AGE-owned object must be
-- linked back to the extension via deptype='e'; catches
-- a missing ALTER EXTENSION age ADD ... in the template)
--
-- All comparison queries should return 0 rows.
--
-- Note on synthetic-initial install (step 10): the synthetic '*_initial'
-- snapshot is built from a fixed historical commit, so its CREATE FUNCTION
-- statements may reference C symbols that have since been removed from the
-- current age.so. Step 10 disables check_function_bodies so that dlsym is
-- deferred to call time; the immediately-following ALTER EXTENSION UPDATE
-- (step 11) DROPs any such retired functions before any plan can call them.
-- This lets developers cleanly remove deprecated C entry points without
-- needing to keep error-raising stubs in age.so.
--
LOAD 'age';
SET search_path TO ag_catalog;
-- Step 1: Clean up any graphs left by prior tests (deterministic, no output).
Expand All @@ -56,15 +70,23 @@ BEGIN
END
$$;
-- =====================================================================
-- FRESH INSTALL SNAPSHOTS (Steps 2-7)
-- FRESH INSTALL SNAPSHOTS (Steps 2-7b)
-- Capture the catalog state from the default CREATE EXTENSION install.
-- =====================================================================
-- Step 2: Snapshot functions (includes aggregates via prokind).
-- probin/prosrc capture the binding to the implementation:
-- * LANGUAGE c : probin = '$libdir/age', prosrc = C symbol name
-- (a renamed/retargeted symbol shows up here)
-- * LANGUAGE sql/plpgsql: probin = NULL, prosrc = function body text
-- (a body change in the upgrade template shows up here)
-- * LANGUAGE internal : probin = NULL, prosrc = builtin name
CREATE TEMP TABLE _fresh_funcs AS
SELECT proname::text,
pg_get_function_identity_arguments(oid) AS args,
provolatile::text, proisstrict::text, prokind::text,
prorettype::regtype::text AS rettype, proretset::text
prorettype::regtype::text AS rettype, proretset::text,
COALESCE(probin, '') AS probin,
COALESCE(prosrc, '') AS prosrc
FROM pg_proc
WHERE pronamespace = 'ag_catalog'::regnamespace
ORDER BY proname, args;
Expand Down Expand Up @@ -113,6 +135,57 @@ SELECT conname::text, contype::text,
FROM pg_constraint
WHERE connamespace = 'ag_catalog'::regnamespace
ORDER BY conname;
-- Step 7b: Snapshot extension membership (pg_depend deptype='e').
-- Every object that CREATE EXTENSION owns has a row in pg_depend linking
-- it to the extension. The upgrade template must produce the same set:
-- if it CREATEs an object but forgets to ALTER EXTENSION ADD it, the
-- catalog row exists (so funcs_match/rels_match would pass) but the
-- pg_depend link is absent and pg_dump --extension would diverge.
CREATE TEMP TABLE _fresh_extmembers AS
SELECT
CASE d.classid
WHEN 'pg_proc'::regclass
THEN 'function: ' || d.objid::regprocedure::text
WHEN 'pg_type'::regclass
THEN 'type: ' || d.objid::regtype::text
WHEN 'pg_class'::regclass
THEN 'relation: ' || d.objid::regclass::text
|| ' (' || (SELECT relkind::text FROM pg_class WHERE oid = d.objid) || ')'
WHEN 'pg_operator'::regclass
THEN 'operator: ' || d.objid::regoperator::text
WHEN 'pg_cast'::regclass
THEN 'cast: ' || (SELECT castsource::regtype::text || ' -> ' || casttarget::regtype::text
FROM pg_cast WHERE oid = d.objid)
WHEN 'pg_opclass'::regclass
THEN 'opclass: ' || (SELECT opcname || ' (' || (SELECT amname FROM pg_am WHERE oid = opcmethod) || ')'
FROM pg_opclass WHERE oid = d.objid)
WHEN 'pg_constraint'::regclass
THEN 'constraint: ' || (SELECT conname || ' on ' || conrelid::regclass::text
FROM pg_constraint WHERE oid = d.objid)
WHEN 'pg_opfamily'::regclass
THEN 'opfamily: ' || (SELECT opfname || ' (' || (SELECT amname FROM pg_am WHERE oid = opfmethod) || ')'
FROM pg_opfamily WHERE oid = d.objid)
WHEN 'pg_amop'::regclass
THEN 'amop: ' || (SELECT (SELECT opfname FROM pg_opfamily WHERE oid = a.amopfamily)
|| ' [strategy ' || a.amopstrategy || '] '
|| a.amoplefttype::regtype::text || ','
|| a.amoprighttype::regtype::text || ' op '
|| a.amopopr::regoperator::text
FROM pg_amop a WHERE a.oid = d.objid)
WHEN 'pg_amproc'::regclass
THEN 'amproc: ' || (SELECT (SELECT opfname FROM pg_opfamily WHERE oid = p.amprocfamily)
|| ' [proc ' || p.amprocnum || '] '
|| p.amproclefttype::regtype::text || ','
|| p.amprocrighttype::regtype::text || ' fn '
|| p.amproc::regprocedure::text
FROM pg_amproc p WHERE p.oid = d.objid)
ELSE 'unhandled[' || d.classid::regclass::text || ']'
END AS member
FROM pg_depend d
WHERE d.deptype = 'e'
AND d.refclassid = 'pg_extension'::regclass
AND d.refobjid = (SELECT oid FROM pg_extension WHERE extname = 'age')
ORDER BY 1;
-- Step 8: Drop AGE entirely.
DROP EXTENSION age;
-- Step 9: Verify we have an upgrade path available.
Expand All @@ -124,6 +197,19 @@ FROM pg_available_extension_versions WHERE name = 'age';
(1 row)

-- Step 10: Install AGE at the synthetic initial version.
--
-- Disable check_function_bodies for this CREATE only. The synthetic
-- '*_initial' SQL is pulled from a fixed historical commit and may
-- declare C functions whose symbols have since been removed from the
-- current age.so. With check_function_bodies=on, PostgreSQL would dlsym
-- each such symbol at CREATE FUNCTION time and abort. Deferring the
-- symbol probe to call time is safe because step 11 (ALTER EXTENSION
-- UPDATE) immediately runs the upgrade template, which DROPs any
-- removed-in-HEAD functions before the test (or any user) can call them.
-- The fresh CREATE EXTENSION at step 35 keeps the GUC at its default,
-- so any inconsistency between HEAD's SQL and HEAD's age.so is still
-- caught at install time on the production code path.
SET check_function_bodies = off;
DO $$
DECLARE init_ver text;
BEGIN
Expand All @@ -138,6 +224,7 @@ BEGIN
EXECUTE format('CREATE EXTENSION age VERSION %L', init_ver);
END;
$$;
RESET check_function_bodies;
-- Step 11: Upgrade to the current (default) version via the stamped template.
DO $$
DECLARE curr_ver text;
Expand All @@ -160,15 +247,17 @@ FROM pg_available_extensions WHERE name = 'age';
(1 row)

-- =====================================================================
-- UPGRADED INSTALL SNAPSHOTS (Steps 13-18)
-- UPGRADED INSTALL SNAPSHOTS (Steps 13-18b)
-- Capture the catalog state after upgrade from initial to current.
-- =====================================================================
-- Step 13: Snapshot functions.
-- Step 13: Snapshot functions (probin/prosrc included; see step 2).
CREATE TEMP TABLE _upgraded_funcs AS
SELECT proname::text,
pg_get_function_identity_arguments(oid) AS args,
provolatile::text, proisstrict::text, prokind::text,
prorettype::regtype::text AS rettype, proretset::text
prorettype::regtype::text AS rettype, proretset::text,
COALESCE(probin, '') AS probin,
COALESCE(prosrc, '') AS prosrc
FROM pg_proc
WHERE pronamespace = 'ag_catalog'::regnamespace
ORDER BY proname, args;
Expand Down Expand Up @@ -217,8 +306,54 @@ SELECT conname::text, contype::text,
FROM pg_constraint
WHERE connamespace = 'ag_catalog'::regnamespace
ORDER BY conname;
-- Step 18b: Snapshot extension membership after upgrade (see step 7b).
CREATE TEMP TABLE _upgraded_extmembers AS
SELECT
CASE d.classid
WHEN 'pg_proc'::regclass
THEN 'function: ' || d.objid::regprocedure::text
WHEN 'pg_type'::regclass
THEN 'type: ' || d.objid::regtype::text
WHEN 'pg_class'::regclass
THEN 'relation: ' || d.objid::regclass::text
|| ' (' || (SELECT relkind::text FROM pg_class WHERE oid = d.objid) || ')'
WHEN 'pg_operator'::regclass
THEN 'operator: ' || d.objid::regoperator::text
WHEN 'pg_cast'::regclass
THEN 'cast: ' || (SELECT castsource::regtype::text || ' -> ' || casttarget::regtype::text
FROM pg_cast WHERE oid = d.objid)
WHEN 'pg_opclass'::regclass
THEN 'opclass: ' || (SELECT opcname || ' (' || (SELECT amname FROM pg_am WHERE oid = opcmethod) || ')'
FROM pg_opclass WHERE oid = d.objid)
WHEN 'pg_constraint'::regclass
THEN 'constraint: ' || (SELECT conname || ' on ' || conrelid::regclass::text
FROM pg_constraint WHERE oid = d.objid)
WHEN 'pg_opfamily'::regclass
THEN 'opfamily: ' || (SELECT opfname || ' (' || (SELECT amname FROM pg_am WHERE oid = opfmethod) || ')'
FROM pg_opfamily WHERE oid = d.objid)
WHEN 'pg_amop'::regclass
THEN 'amop: ' || (SELECT (SELECT opfname FROM pg_opfamily WHERE oid = a.amopfamily)
|| ' [strategy ' || a.amopstrategy || '] '
|| a.amoplefttype::regtype::text || ','
|| a.amoprighttype::regtype::text || ' op '
|| a.amopopr::regoperator::text
FROM pg_amop a WHERE a.oid = d.objid)
WHEN 'pg_amproc'::regclass
THEN 'amproc: ' || (SELECT (SELECT opfname FROM pg_opfamily WHERE oid = p.amprocfamily)
|| ' [proc ' || p.amprocnum || '] '
|| p.amproclefttype::regtype::text || ','
|| p.amprocrighttype::regtype::text || ' fn '
|| p.amproc::regprocedure::text
FROM pg_amproc p WHERE p.oid = d.objid)
ELSE 'unhandled[' || d.classid::regclass::text || ']'
END AS member
FROM pg_depend d
WHERE d.deptype = 'e'
AND d.refclassid = 'pg_extension'::regclass
AND d.refobjid = (SELECT oid FROM pg_extension WHERE extname = 'age')
ORDER BY 1;
-- =====================================================================
-- COMPARISON: Missing or extra objects (Steps 19-33)
-- COMPARISON: Missing or extra objects (Steps 19-33b)
-- Any rows returned indicate a template deficiency.
-- =====================================================================
-- Step 19: Functions MISSING after upgrade.
Expand All @@ -241,23 +376,32 @@ ORDER BY 1;
----------------
(0 rows)

-- Step 21: Function PROPERTY changes (volatility, strictness, kind, return type).
-- Step 21: Function PROPERTY changes
-- (kind, volatility, strictness, return type, return-set, binding,
-- body/symbol). The probin/prosrc check catches:
-- * a C function whose symbol was renamed in the upgrade template
-- * a SQL/plpgsql function whose body was changed in either path
-- * a language change between the fresh and upgrade installs.
SELECT f.proname || '(' || f.args || ')' AS function_name,
CASE WHEN f.prokind <> u.prokind THEN 'prokind: ' || f.prokind || '->' || u.prokind END AS kind_change,
CASE WHEN f.provolatile<> u.provolatile THEN 'volatile: ' || f.provolatile|| '->' || u.provolatile END AS volatility_change,
CASE WHEN f.proisstrict<> u.proisstrict THEN 'strict: ' || f.proisstrict|| '->' || u.proisstrict END AS strict_change,
CASE WHEN f.rettype <> u.rettype THEN 'rettype: ' || f.rettype || '->' || u.rettype END AS rettype_change,
CASE WHEN f.proretset <> u.proretset THEN 'retset: ' || f.proretset || '->' || u.proretset END AS retset_change
CASE WHEN f.prokind <> u.prokind THEN 'prokind: ' || f.prokind || '->' || u.prokind END AS kind_change,
CASE WHEN f.provolatile <> u.provolatile THEN 'volatile: ' || f.provolatile || '->' || u.provolatile END AS volatility_change,
CASE WHEN f.proisstrict <> u.proisstrict THEN 'strict: ' || f.proisstrict || '->' || u.proisstrict END AS strict_change,
CASE WHEN f.rettype <> u.rettype THEN 'rettype: ' || f.rettype || '->' || u.rettype END AS rettype_change,
CASE WHEN f.proretset <> u.proretset THEN 'retset: ' || f.proretset || '->' || u.proretset END AS retset_change,
CASE WHEN f.probin <> u.probin THEN 'probin: ' || f.probin || '->' || u.probin END AS probin_change,
CASE WHEN f.prosrc <> u.prosrc THEN 'prosrc changed' END AS prosrc_change
FROM _fresh_funcs f
JOIN _upgraded_funcs u USING (proname, args)
WHERE f.provolatile <> u.provolatile
OR f.proisstrict <> u.proisstrict
OR f.prokind <> u.prokind
OR f.rettype <> u.rettype
OR f.proretset <> u.proretset
OR f.probin <> u.probin
OR f.prosrc <> u.prosrc
ORDER BY 1;
function_name | kind_change | volatility_change | strict_change | rettype_change | retset_change
---------------+-------------+-------------------+---------------+----------------+---------------
function_name | kind_change | volatility_change | strict_change | rettype_change | retset_change | probin_change | prosrc_change
---------------+-------------+-------------------+---------------+----------------+---------------+---------------+---------------
(0 rows)

-- Step 22: Relations MISSING after upgrade.
Expand Down Expand Up @@ -380,6 +524,28 @@ ORDER BY 1;
------------------
(0 rows)

-- Step 33a: Extension members MISSING after upgrade
-- (object exists in pg_proc/pg_class/etc. but is not linked to the AGE
-- extension via pg_depend, i.e. ALTER EXTENSION age ADD ... was forgotten).
SELECT f.member AS missing_extension_member
FROM _fresh_extmembers f
LEFT JOIN _upgraded_extmembers u USING (member)
WHERE u.member IS NULL
ORDER BY 1;
missing_extension_member
--------------------------
(0 rows)

-- Step 33b: Extension members EXTRA after upgrade.
SELECT u.member AS extra_extension_member
FROM _upgraded_extmembers u
LEFT JOIN _fresh_extmembers f USING (member)
WHERE f.member IS NULL
ORDER BY 1;
extra_extension_member
------------------------
(0 rows)

-- =====================================================================
-- SUMMARY (Step 34)
-- =====================================================================
Expand All @@ -391,10 +557,11 @@ SELECT
(SELECT count(*) FROM _fresh_ops) = (SELECT count(*) FROM _upgraded_ops) AS ops_match,
(SELECT count(*) FROM _fresh_casts) = (SELECT count(*) FROM _upgraded_casts) AS casts_match,
(SELECT count(*) FROM _fresh_opclass) = (SELECT count(*) FROM _upgraded_opclass) AS opclass_match,
(SELECT count(*) FROM _fresh_constraints) = (SELECT count(*) FROM _upgraded_constraints) AS constraints_match;
funcs_match | rels_match | types_match | ops_match | casts_match | opclass_match | constraints_match
-------------+------------+-------------+-----------+-------------+---------------+-------------------
t | t | t | t | t | t | t
(SELECT count(*) FROM _fresh_constraints) = (SELECT count(*) FROM _upgraded_constraints) AS constraints_match,
(SELECT count(*) FROM _fresh_extmembers) = (SELECT count(*) FROM _upgraded_extmembers) AS extmembers_match;
funcs_match | rels_match | types_match | ops_match | casts_match | opclass_match | constraints_match | extmembers_match
-------------+------------+-------------+-----------+-------------+---------------+-------------------+------------------
t | t | t | t | t | t | t | t
(1 row)

-- =====================================================================
Expand All @@ -404,7 +571,8 @@ SELECT
DROP TABLE _fresh_funcs, _upgraded_funcs, _fresh_rels, _upgraded_rels,
_fresh_types, _upgraded_types, _fresh_ops, _upgraded_ops,
_fresh_casts, _upgraded_casts, _fresh_opclass, _upgraded_opclass,
_fresh_constraints, _upgraded_constraints;
_fresh_constraints, _upgraded_constraints,
_fresh_extmembers, _upgraded_extmembers;
DROP EXTENSION age;
CREATE EXTENSION age;
-- Step 36: Remove synthetic upgrade test files from the extension directory.
Expand Down
Loading
Loading