From d25086e232390feaf1ea4620fd5ba3bd7f1aca3f Mon Sep 17 00:00:00 2001 From: jnasbyupgrade Date: Fri, 7 Aug 2026 16:48:30 -0500 Subject: [PATCH 1/3] Build cat_tools from its 0.3.0 git tag; adapt to its API/enum changes pgxn install --unstable cat_tools resolves to the newest release actually published to the PGXN package index, which is still 0.2.1 (2017) and fails standalone on modern PostgreSQL with "column oid specified more than once" at CREATE EXTENSION. A fixed release, 0.3.0, is tagged in cat_tools' own git repo but hasn't been uploaded to PGXN yet, so the Makefile's cat_tools target now clones Postgres-Extensions/cat_tools at the 0.3.0 tag and builds/installs it directly. Since this is the first time object_reference's suite has actually run against a real, working cat_tools, two small fallout fixes are needed: - cat_tools.function__arg_types_text() is deprecated in 0.3.0 in favor of cat_tools.routine__parse_arg_types_text() (identical signature/body, just renamed, deprecated one emits a WARNING on every call). Switched object_reference's one call site to the non-deprecated name. - cat_tools 0.3.0's object_type enum grew two new members, "partitioned table" and "partitioned index". pg_get_object_address() doesn't recognize either (only the base table/index types they derive from), so object_reference classifies them as unsupported, matching object_reference.unsupported()'s existing handling of "event trigger" for the same reason. test/sql/all.sql's sanity-check of the unsupported set is updated to match. sql/object_reference--stable.sql and test/expected/zzz_build.out are regenerated (make results) to match. Extracted from PR #5, which had scope-crept into also carrying this fix alongside the actual CI/pgxn-tools migration; splitting it out here so it can be reviewed and merged independently. --- Makefile | 15 ++++++++++++++- sql/object_reference--stable.sql | 9 +++++++-- sql/object_reference.sql | 9 +++++++-- test/expected/zzz_build.out | 10 +++++----- test/sql/all.sql | 7 +++++++ 5 files changed, 40 insertions(+), 10 deletions(-) diff --git a/Makefile b/Makefile index 9ae665d..ec25834 100644 --- a/Makefile +++ b/Makefile @@ -17,10 +17,23 @@ extra_clean += $(wildcard test/dump/*.log) dump_test: test/dump/run.sh test/helpers/object_table.sql $(wildcard test/dump/*.sql) $< -f # Force drop of databases if they exist +CAT_TOOLS_VERSION = 0.3.0 +CAT_TOOLS_BUILD_DIR = tmp/cat_tools-$(CAT_TOOLS_VERSION) +extra_clean += $(CAT_TOOLS_BUILD_DIR) + .PHONY: cat_tools cat_tools: $(DESTDIR)$(datadir)/extension/cat_tools.control $(DESTDIR)$(datadir)/extension/cat_tools.control: - pgxn install --unstable cat_tools + # `pgxn install --unstable cat_tools` resolves to the newest release + # published to the PGXN package index, which is still 0.2.1 -- it fails + # standalone on modern PostgreSQL with "column oid specified more than + # once" at CREATE EXTENSION. A fixed release, 0.3.0, is tagged in + # cat_tools' own git repo but hasn't been uploaded to PGXN yet, so build + # it from that tag directly until PGXN has it. + rm -rf $(CAT_TOOLS_BUILD_DIR) + git clone --branch $(CAT_TOOLS_VERSION) --depth 1 https://github.com/Postgres-Extensions/cat_tools.git $(CAT_TOOLS_BUILD_DIR) + $(MAKE) -C $(CAT_TOOLS_BUILD_DIR) install PG_CONFIG=$(PG_CONFIG) DESTDIR=$(DESTDIR) + rm -rf $(CAT_TOOLS_BUILD_DIR) .PHONY: count_nulls count_nulls: $(DESTDIR)$(datadir)/extension/count_nulls.control diff --git a/sql/object_reference--stable.sql b/sql/object_reference--stable.sql index 4a68ebc..652da0f 100644 --- a/sql/object_reference--stable.sql +++ b/sql/object_reference--stable.sql @@ -85,7 +85,7 @@ CREATE FUNCTION __object_reference.create_function( , grants text DEFAULT NULL ) RETURNS void LANGUAGE plpgsql AS $body$ DECLARE - c_clean_args text := cat_tools.function__arg_types_text(args); + c_clean_args text := cat_tools.routine__parse_arg_types_text(args); create_template CONSTANT text := $template$ CREATE OR REPLACE FUNCTION %s( @@ -523,7 +523,12 @@ SELECT __object_reference.create_function( , $body$ SELECT cat_tools.objects__shared() || cat_tools.objects__address_unsupported() - || '{event trigger}' + /* + * pg_get_object_address() doesn't recognize "partitioned table" or + * "partitioned index" (only the base "table"/"index" types it derives + * from), so object identity tracking can't round-trip them. + */ + || '{event trigger, partitioned table, partitioned index}' $body$ , 'Returns array of object types that are not supported.' , 'object_reference__usage' diff --git a/sql/object_reference.sql b/sql/object_reference.sql index e83b461..db92512 100644 --- a/sql/object_reference.sql +++ b/sql/object_reference.sql @@ -84,7 +84,7 @@ CREATE FUNCTION __object_reference.create_function( , grants text DEFAULT NULL ) RETURNS void LANGUAGE plpgsql AS $body$ DECLARE - c_clean_args text := cat_tools.function__arg_types_text(args); + c_clean_args text := cat_tools.routine__parse_arg_types_text(args); create_template CONSTANT text := $template$ CREATE OR REPLACE FUNCTION %s( @@ -522,7 +522,12 @@ SELECT __object_reference.create_function( , $body$ SELECT cat_tools.objects__shared() || cat_tools.objects__address_unsupported() - || '{event trigger}' + /* + * pg_get_object_address() doesn't recognize "partitioned table" or + * "partitioned index" (only the base "table"/"index" types it derives + * from), so object identity tracking can't round-trip them. + */ + || '{event trigger, partitioned table, partitioned index}' $body$ , 'Returns array of object types that are not supported.' , 'object_reference__usage' diff --git a/test/expected/zzz_build.out b/test/expected/zzz_build.out index fe92660..d9612d3 100644 --- a/test/expected/zzz_build.out +++ b/test/expected/zzz_build.out @@ -2,16 +2,16 @@ This extension must be loaded via CREATE EXTENSION object_reference; You really, REALLY do NOT want to try and load this via psql!!! -psql:test/temp_load.not_sql:188: WARNING: I promise you will be sorry if you try to use this as anything other than an extension! +psql:test/temp_load.not_sql:187: WARNING: I promise you will be sorry if you try to use this as anything other than an extension! -psql:test/temp_load.not_sql:189: WARNING: I promise you will be sorry if you try to use this as anything other than an extension! +psql:test/temp_load.not_sql:188: WARNING: I promise you will be sorry if you try to use this as anything other than an extension! -psql:test/temp_load.not_sql:513: WARNING: I promise you will be sorry if you try to use this as anything other than an extension! +psql:test/temp_load.not_sql:512: WARNING: I promise you will be sorry if you try to use this as anything other than an extension! @@ -21,9 +21,9 @@ psql:test/temp_load.not_sql:513: WARNING: I promise you will be sorry if you tr -psql:test/temp_load.not_sql:620: WARNING: I promise you will be sorry if you try to use this as anything other than an extension! +psql:test/temp_load.not_sql:624: WARNING: I promise you will be sorry if you try to use this as anything other than an extension! -psql:test/temp_load.not_sql:627: WARNING: I promise you will be sorry if you try to use this as anything other than an extension! +psql:test/temp_load.not_sql:631: WARNING: I promise you will be sorry if you try to use this as anything other than an extension! diff --git a/test/sql/all.sql b/test/sql/all.sql index e00f04b..8e96fe2 100644 --- a/test/sql/all.sql +++ b/test/sql/all.sql @@ -53,6 +53,13 @@ SELECT bag_eq( UNION -- Intentionally not UNION ALL; we want to know if object_reference.unsupported has dupes SELECT * FROM cat_tools.objects__address_unsupported_srf() UNION SELECT 'event trigger' + /* + * pg_identify_object_as_address() returns these as plain "table"/"index", + * and pg_get_object_address() doesn't recognize "partitioned table" or + * "partitioned index" at all, so the round-trip is broken. + */ + UNION SELECT 'partitioned table' + UNION SELECT 'partitioned index' $$ , 'Verify object_reference.unsupported()' ); From 0f68d1a49735a8ccbf1df52fc20ba7b816d2564b Mon Sep 17 00:00:00 2001 From: jnasbyupgrade Date: Fri, 7 Aug 2026 16:59:58 -0500 Subject: [PATCH 2/3] Revert the Makefile git-tag-build workaround; PGXN now serves cat_tools 0.3.0 pgxn install --unstable cat_tools now resolves to cat_tools 0.3.0 directly (confirmed against the live PGXN index and by a clean CREATE EXTENSION cat_tools; both locally and, once pushed, in the actual object_reference CI run for #5's slimmed CI-migration branch -- it went fully green without this Makefile change at all). The PGXN package index being stuck at the broken, 2017-era 0.2.1 release was true when this fix was first written, but isn't true anymore, so the git-clone-from-tag workaround has nothing left to work around. Keeping it would leave a Makefile comment describing a problem that no longer exists. The SQL/test fixes (renamed function call, new object_type enum members classified as unsupported) are unaffected -- those are needed regardless of how cat_tools 0.3.0 gets installed. --- Makefile | 15 +-------------- 1 file changed, 1 insertion(+), 14 deletions(-) diff --git a/Makefile b/Makefile index ec25834..9ae665d 100644 --- a/Makefile +++ b/Makefile @@ -17,23 +17,10 @@ extra_clean += $(wildcard test/dump/*.log) dump_test: test/dump/run.sh test/helpers/object_table.sql $(wildcard test/dump/*.sql) $< -f # Force drop of databases if they exist -CAT_TOOLS_VERSION = 0.3.0 -CAT_TOOLS_BUILD_DIR = tmp/cat_tools-$(CAT_TOOLS_VERSION) -extra_clean += $(CAT_TOOLS_BUILD_DIR) - .PHONY: cat_tools cat_tools: $(DESTDIR)$(datadir)/extension/cat_tools.control $(DESTDIR)$(datadir)/extension/cat_tools.control: - # `pgxn install --unstable cat_tools` resolves to the newest release - # published to the PGXN package index, which is still 0.2.1 -- it fails - # standalone on modern PostgreSQL with "column oid specified more than - # once" at CREATE EXTENSION. A fixed release, 0.3.0, is tagged in - # cat_tools' own git repo but hasn't been uploaded to PGXN yet, so build - # it from that tag directly until PGXN has it. - rm -rf $(CAT_TOOLS_BUILD_DIR) - git clone --branch $(CAT_TOOLS_VERSION) --depth 1 https://github.com/Postgres-Extensions/cat_tools.git $(CAT_TOOLS_BUILD_DIR) - $(MAKE) -C $(CAT_TOOLS_BUILD_DIR) install PG_CONFIG=$(PG_CONFIG) DESTDIR=$(DESTDIR) - rm -rf $(CAT_TOOLS_BUILD_DIR) + pgxn install --unstable cat_tools .PHONY: count_nulls count_nulls: $(DESTDIR)$(datadir)/extension/count_nulls.control From aa60049205eed622320d60d349cc95e2db319a0c Mon Sep 17 00:00:00 2001 From: jnasbyupgrade Date: Tue, 4 Aug 2026 19:19:12 -0500 Subject: [PATCH 3/3] Remove reg* pseudotype columns; add object info functions Reconciles the substantive feature delta from new_features (PR #2) onto the current 'stable' baseline (post PR #5/#16: pgxn-tools testing, cat_tools 0.3.0, and the linter): - _object_reference._object_oid: drop the per-catalog regclass/regconfig/ regdictionary/regnamespace/regoperator/regprocedure/regtype columns and their unique indexes plus the count_nulls-backed null_count trigger that enforced "exactly one is set". classid is now plain oid and object_oid (also NOT NULL) is the sole identifier column, so there's nothing left to arbitrate between. - _object_reference._object_v / _object_v__for_update: drop the reg* columns from the column list to match. - _object_reference._object_oid__add: replace the dynamic, format()-built INSERT that picked a reg* column based on cat_tools.object__reg_type() with a plain INSERT into object_oid. - Drop the count_nulls search_path DO block (dead now that the trigger using it is gone) and the count_nulls dependency throughout (control, Makefile, test setup). - Add object_reference.object__describe()/object__identity(), thin wrappers around pg_describe_object()/pg_identify_object(); and object__cleanup(), which best-effort deletes an object record (ignoring foreign_key_violation if it's still referenced elsewhere). Wire object__cleanup() up to a new AFTER DELETE trigger on object_group__object so removing an object from its last group automatically attempts cleanup. - _object_v__for_update (the getsert core): refuse to track objects living in a pg_temp*/pg_toast_temp* schema, since a tracked reference would outlive the temporary object it points to. - test/sql/object_group.sql: switch the two scratch tables from TEMP to regular tables (object__getsert now rejects temp objects) and add coverage for the new automatic-cleanup trigger. - test/sql/base.sql: replace the count_nulls-relocation test (relocation was already unsupported and the whole extension no longer depends on count_nulls) with coverage for object_oid, object__describe(), object__identity(), and temp-object rejection. sql/object_reference--0.1.0.sql (the frozen historical release) and the META files are untouched. default_version stays 'stable'; sql/object_reference--stable.sql is regenerated to match sql/object_reference.sql. make lint and make test (including the dump/restore test) pass on both PostgreSQL 12 and 17. Supersedes PR #2 (new_features) and, for the update/upgrade test infrastructure built on top of it, sets up the rebuild of PR #3. Co-Authored-By: Claude Sonnet 5 --- Makefile | 7 +- object_reference.control | 2 +- sql/object_reference--stable.sql | 182 ++++++++++++++++--------------- sql/object_reference.sql | 182 ++++++++++++++++--------------- test/deps.sql | 1 - test/expected/base.out | 15 +-- test/expected/object_group.out | 9 +- test/expected/zzz_build.out | 72 +++++++++++- test/load.sql | 1 - test/sql/base.sql | 47 ++++---- test/sql/object_group.sql | 44 ++++++-- test/sql/zzz_build.sql | 1 - 12 files changed, 333 insertions(+), 230 deletions(-) diff --git a/Makefile b/Makefile index 9ae665d..36af9d4 100644 --- a/Makefile +++ b/Makefile @@ -3,7 +3,7 @@ include pgxntool/base.mk testdeps: $(wildcard test/*.sql test/helpers/*.sql) # Be careful not to include directories in this testdeps: test_factory -install: cat_tools count_nulls +install: cat_tools # pgxntool's check-stale-expected target (added in pgxntool 2.2.0) depends on # installcheck but is listed before install in TEST_DEPS, and Make evaluates a @@ -22,11 +22,6 @@ cat_tools: $(DESTDIR)$(datadir)/extension/cat_tools.control $(DESTDIR)$(datadir)/extension/cat_tools.control: pgxn install --unstable cat_tools -.PHONY: count_nulls -count_nulls: $(DESTDIR)$(datadir)/extension/count_nulls.control -$(DESTDIR)$(datadir)/extension/count_nulls.control: - pgxn install --unstable count_nulls - .PHONY: test_factory test_factory: $(DESTDIR)$(datadir)/extension/test_factory.control $(DESTDIR)$(datadir)/extension/test_factory.control: diff --git a/object_reference.control b/object_reference.control index 5c7d974..142d0b1 100644 --- a/object_reference.control +++ b/object_reference.control @@ -2,4 +2,4 @@ comment = 'Provides reference IDs for database objects' default_version = 'stable' relocatable = false schema = 'object_reference' -requires = 'cat_tools, count_nulls' +requires = 'cat_tools' diff --git a/sql/object_reference--stable.sql b/sql/object_reference--stable.sql index 652da0f..299352d 100644 --- a/sql/object_reference--stable.sql +++ b/sql/object_reference--stable.sql @@ -3,17 +3,6 @@ \echo You really, REALLY do NOT want to try and load this via psql!!! \echo It will FAIL during pg_dump! \quit --- This BS is because count_nulls is relocatable, so could be in any schema -DO $$ -BEGIN - RAISE DEBUG 'initial search_path = %', current_setting('search_path'); - PERFORM set_config('search_path', current_setting('search_path') || ', ' || extnamespace::regnamespace::text, true) -- true = local only - FROM pg_extension - WHERE extname = 'count_nulls' - ; - RAISE DEBUG 'search_path changed to %', current_setting('search_path'); -END -$$; /* DO $$ DECLARE @@ -192,7 +181,7 @@ GRANT REFERENCES ON _object_reference.object TO object_reference__dependency; CREATE TABLE _object_reference._object_oid( object_id int PRIMARY KEY REFERENCES _object_reference.object ON DELETE CASCADE ON UPDATE CASCADE - , classid regclass NOT NULL + , classid oid NOT NULL /* TODO: needs to be a trigger CONSTRAINT classid_must_match__object__address_classid CHECK( classid IS NOT DISTINCT FROM cat_tools.object__address_classid(object_type) ) @@ -200,50 +189,11 @@ CREATE TABLE _object_reference._object_oid( , objid oid NOT NULL , objsubid int NOT NULL CONSTRAINT objid_must_match CHECK( -- _object_reference._sanity() depends on this! - objid IS NOT DISTINCT FROM coalesce( - regclass::oid -- Need to cast first item to generic OID - , regconfig - , regdictionary - , regnamespace -- SED: REQUIRES 9.5! - , regoperator - , regprocedure - , regtype - , object_oid - ) + objid IS NOT DISTINCT FROM object_oid ) , CONSTRAINT object__u_classid__objid__objsubid UNIQUE( classid, objid, objsubid ) - , regclass regclass - CONSTRAINT regclass_classid CHECK( regclass IS NULL OR classid = cat_tools.object__reg_type_catalog('regclass') ) - , regconfig regconfig - CONSTRAINT regconfig_classid CHECK( regconfig IS NULL OR classid = cat_tools.object__reg_type_catalog('regconfig') ) - , regdictionary regdictionary - CONSTRAINT regdictionary_classid CHECK( regdictionary IS NULL OR classid = cat_tools.object__reg_type_catalog('regdictionary') ) - , regnamespace regnamespace -- SED: REQUIRES 9.5! - CONSTRAINT regnamespace_classid CHECK( regnamespace IS NULL OR classid = cat_tools.object__reg_type_catalog('regnamespace') ) -- SED: REQUIRES 9.5! - , regoperator regoperator - CONSTRAINT regoperator_classid CHECK( regoperator IS NULL OR classid = cat_tools.object__reg_type_catalog('regoperator') ) - , regprocedure regprocedure - CONSTRAINT regprocedure_classid CHECK( regprocedure IS NULL OR classid = cat_tools.object__reg_type_catalog('regprocedure') ) - -- I don't think we should ever have regrole since we can't create event triggers on it --- , regrole regrole - , regtype regtype - CONSTRAINT regtype_classid CHECK( regtype IS NULL OR classid = cat_tools.object__reg_type_catalog('regtype') ) - , object_oid oid + , object_oid oid NOT NULL ); -CREATE TRIGGER null_count - AFTER INSERT OR UPDATE - ON _object_reference._object_oid - FOR EACH ROW EXECUTE PROCEDURE not_null_count_trigger( - 5 -- First 4 fields, + 1 - , 'only one object reference field may be set' - ) -; -CREATE UNIQUE INDEX _object_oid__u_regclass ON _object_reference._object_oid(regclass) WHERE regclass IS NOT NULL; -CREATE UNIQUE INDEX _object_oid__u_regconfig ON _object_reference._object_oid(regconfig) WHERE regconfig IS NOT NULL; -CREATE UNIQUE INDEX _object_oid__u_regdictionary ON _object_reference._object_oid(regdictionary) WHERE regdictionary IS NOT NULL; -CREATE UNIQUE INDEX _object_oid__u_regoperator ON _object_reference._object_oid(regoperator) WHERE regoperator IS NOT NULL; -CREATE UNIQUE INDEX _object_oid__u_regprocedure ON _object_reference._object_oid(regprocedure) WHERE regprocedure IS NOT NULL; -CREATE UNIQUE INDEX _object_oid__u_regtype ON _object_reference._object_oid(regtype) WHERE regtype IS NOT NULL; SELECT __object_reference.create_function( '_object_reference._sanity' @@ -303,13 +253,6 @@ CREATE VIEW _object_reference._object_v AS , i.classid , i.objid , i.objsubid - , i.regclass - , i.regconfig - , i.regdictionary - , i.regnamespace - , i.regoperator - , i.regprocedure - , i.regtype , i.object_oid , s.* FROM _object_reference.object o @@ -325,13 +268,6 @@ CREATE VIEW _object_reference._object_v__for_update AS , i.classid , i.objid , i.objsubid - , i.regclass - , i.regconfig - , i.regdictionary - , i.regnamespace - , i.regoperator - , i.regprocedure - , i.regtype , i.object_oid , s.* FROM _object_reference.object o @@ -363,26 +299,9 @@ BEGIN WHERE o.object_id = _object_oid__add.object_id ; END IF; - DECLARE - c_reg_type name := cat_tools.object__reg_type(object_type); -- Verifies regtype is supported, if there is one - c_oid_field CONSTANT name := coalesce(c_reg_type, 'object_oid'); - - c_oid_insert CONSTANT text := format( - --USING object_id, classid, objid, objsubid - $$INSERT INTO _object_reference._object_oid(object_id, classid, objid, objsubid, %I) - SELECT $1, $2, $3, $4, $3::%I$$ - , c_oid_field - , coalesce(c_reg_type, 'oid') - ) - ; BEGIN - RAISE DEBUG E'%\n USING %, %, %, %' - , c_oid_insert - , object_id, classid, objid, objsubid - ; - EXECUTE c_oid_insert - USING object_id, classid, objid, objsubid - ; + INSERT INTO _object_reference._object_oid(object_id, classid, objid, objsubid, object_oid) + VALUES (object_id, classid, objid, objsubid, objid); SELECT INTO STRICT r_object_v -- Record better exist! * @@ -632,6 +551,24 @@ CREATE TABLE _object_reference.object_group__object( ); SELECT __object_reference.safe_dump('_object_reference.object_group__object'); +-- Trigger function for automatic object cleanup +SELECT __object_reference.create_function( + '_object_reference._object_group__object__cleanup_trigger' + , '' + , 'trigger LANGUAGE plpgsql' + , $body$ +BEGIN + PERFORM object_reference.object__cleanup(OLD.object_id); + RETURN OLD; +END +$body$ + , 'Trigger function to automatically attempt cleanup of objects when removed from groups.' +); +CREATE TRIGGER object_group__object__cleanup + AFTER DELETE ON _object_reference.object_group__object + FOR EACH ROW + EXECUTE FUNCTION _object_reference._object_group__object__cleanup_trigger(); + -- __get SELECT __object_reference.create_function( 'object_reference.object_group__get' @@ -836,6 +773,69 @@ $body$ , 'object_reference__dependency' ); +/* + * OBJECT INFO FUNCTIONS + */ +SELECT __object_reference.create_function( + 'object_reference.object__describe' + , $args$ + object_id int +$args$ + , 'text LANGUAGE sql' + , $body$ +SELECT pg_catalog.pg_describe_object( + o.classid + , o.objid + , o.objsubid +) +FROM _object_reference._object_oid o +WHERE o.object_id = $1 +$body$ + , 'Return a human-readable description of the object, matching pg_describe_object() format.' + , 'object_reference__usage' +); + +SELECT __object_reference.create_function( + 'object_reference.object__identity' + , $args$ + object_id int + , OUT type text + , OUT schema text + , OUT name text + , OUT identity text +$args$ + , 'record LANGUAGE sql' + , $body$ +SELECT + i.type::text + , i.schema::text + , i.name::text + , i.identity::text +FROM _object_reference._object_oid o + , LATERAL pg_catalog.pg_identify_object(o.classid, o.objid, o.objsubid) i +WHERE o.object_id = $1 +$body$ + , 'Return object identification information matching pg_identify_object() format.' + , 'object_reference__usage' +); +SELECT __object_reference.create_function( + 'object_reference.object__cleanup' + , $args$ + object_id int +$args$ + , 'void LANGUAGE plpgsql' + , $body$ +BEGIN + DELETE FROM _object_reference.object WHERE object.object_id = object__cleanup.object_id; +EXCEPTION WHEN foreign_key_violation THEN + -- Object is still referenced elsewhere, ignore the error + NULL; +END +$body$ + , 'Attempts to delete an object from the tracking system. Silently returns if the object is still referenced by other tables.' + , 'object_reference__usage' +); + /* * OBJECT GETSERT */ @@ -855,6 +855,7 @@ DECLARE r_object_v _object_reference._object_v; r_address record; + r_identity record; did_insert boolean := false; @@ -883,6 +884,15 @@ BEGIN ; END IF; + -- Refuse to track objects in temporary schemas + SELECT INTO r_identity * FROM pg_catalog.pg_identify_object(c_classid, objid, objsubid); + IF r_identity.schema IS NOT NULL AND (r_identity.schema LIKE 'pg_temp%' OR r_identity.schema LIKE 'pg_toast_temp%') THEN + RAISE 'cannot track temporary object' + USING DETAIL = format('object %s is in temporary schema %s', r_identity.identity, r_identity.schema) + , ERRCODE = 'feature_not_supported' + ; + END IF; + -- Ensure the object record exists SELECT INTO r_object_v * diff --git a/sql/object_reference.sql b/sql/object_reference.sql index db92512..e7b93a3 100644 --- a/sql/object_reference.sql +++ b/sql/object_reference.sql @@ -2,17 +2,6 @@ \echo You really, REALLY do NOT want to try and load this via psql!!! \echo It will FAIL during pg_dump! \quit --- This BS is because count_nulls is relocatable, so could be in any schema -DO $$ -BEGIN - RAISE DEBUG 'initial search_path = %', current_setting('search_path'); - PERFORM set_config('search_path', current_setting('search_path') || ', ' || extnamespace::regnamespace::text, true) -- true = local only - FROM pg_extension - WHERE extname = 'count_nulls' - ; - RAISE DEBUG 'search_path changed to %', current_setting('search_path'); -END -$$; /* DO $$ DECLARE @@ -191,7 +180,7 @@ GRANT REFERENCES ON _object_reference.object TO object_reference__dependency; CREATE TABLE _object_reference._object_oid( object_id int PRIMARY KEY REFERENCES _object_reference.object ON DELETE CASCADE ON UPDATE CASCADE - , classid regclass NOT NULL + , classid oid NOT NULL /* TODO: needs to be a trigger CONSTRAINT classid_must_match__object__address_classid CHECK( classid IS NOT DISTINCT FROM cat_tools.object__address_classid(object_type) ) @@ -199,50 +188,11 @@ CREATE TABLE _object_reference._object_oid( , objid oid NOT NULL , objsubid int NOT NULL CONSTRAINT objid_must_match CHECK( -- _object_reference._sanity() depends on this! - objid IS NOT DISTINCT FROM coalesce( - regclass::oid -- Need to cast first item to generic OID - , regconfig - , regdictionary - , regnamespace -- SED: REQUIRES 9.5! - , regoperator - , regprocedure - , regtype - , object_oid - ) + objid IS NOT DISTINCT FROM object_oid ) , CONSTRAINT object__u_classid__objid__objsubid UNIQUE( classid, objid, objsubid ) - , regclass regclass - CONSTRAINT regclass_classid CHECK( regclass IS NULL OR classid = cat_tools.object__reg_type_catalog('regclass') ) - , regconfig regconfig - CONSTRAINT regconfig_classid CHECK( regconfig IS NULL OR classid = cat_tools.object__reg_type_catalog('regconfig') ) - , regdictionary regdictionary - CONSTRAINT regdictionary_classid CHECK( regdictionary IS NULL OR classid = cat_tools.object__reg_type_catalog('regdictionary') ) - , regnamespace regnamespace -- SED: REQUIRES 9.5! - CONSTRAINT regnamespace_classid CHECK( regnamespace IS NULL OR classid = cat_tools.object__reg_type_catalog('regnamespace') ) -- SED: REQUIRES 9.5! - , regoperator regoperator - CONSTRAINT regoperator_classid CHECK( regoperator IS NULL OR classid = cat_tools.object__reg_type_catalog('regoperator') ) - , regprocedure regprocedure - CONSTRAINT regprocedure_classid CHECK( regprocedure IS NULL OR classid = cat_tools.object__reg_type_catalog('regprocedure') ) - -- I don't think we should ever have regrole since we can't create event triggers on it --- , regrole regrole - , regtype regtype - CONSTRAINT regtype_classid CHECK( regtype IS NULL OR classid = cat_tools.object__reg_type_catalog('regtype') ) - , object_oid oid + , object_oid oid NOT NULL ); -CREATE TRIGGER null_count - AFTER INSERT OR UPDATE - ON _object_reference._object_oid - FOR EACH ROW EXECUTE PROCEDURE not_null_count_trigger( - 5 -- First 4 fields, + 1 - , 'only one object reference field may be set' - ) -; -CREATE UNIQUE INDEX _object_oid__u_regclass ON _object_reference._object_oid(regclass) WHERE regclass IS NOT NULL; -CREATE UNIQUE INDEX _object_oid__u_regconfig ON _object_reference._object_oid(regconfig) WHERE regconfig IS NOT NULL; -CREATE UNIQUE INDEX _object_oid__u_regdictionary ON _object_reference._object_oid(regdictionary) WHERE regdictionary IS NOT NULL; -CREATE UNIQUE INDEX _object_oid__u_regoperator ON _object_reference._object_oid(regoperator) WHERE regoperator IS NOT NULL; -CREATE UNIQUE INDEX _object_oid__u_regprocedure ON _object_reference._object_oid(regprocedure) WHERE regprocedure IS NOT NULL; -CREATE UNIQUE INDEX _object_oid__u_regtype ON _object_reference._object_oid(regtype) WHERE regtype IS NOT NULL; SELECT __object_reference.create_function( '_object_reference._sanity' @@ -302,13 +252,6 @@ CREATE VIEW _object_reference._object_v AS , i.classid , i.objid , i.objsubid - , i.regclass - , i.regconfig - , i.regdictionary - , i.regnamespace - , i.regoperator - , i.regprocedure - , i.regtype , i.object_oid , s.* FROM _object_reference.object o @@ -324,13 +267,6 @@ CREATE VIEW _object_reference._object_v__for_update AS , i.classid , i.objid , i.objsubid - , i.regclass - , i.regconfig - , i.regdictionary - , i.regnamespace - , i.regoperator - , i.regprocedure - , i.regtype , i.object_oid , s.* FROM _object_reference.object o @@ -362,26 +298,9 @@ BEGIN WHERE o.object_id = _object_oid__add.object_id ; END IF; - DECLARE - c_reg_type name := cat_tools.object__reg_type(object_type); -- Verifies regtype is supported, if there is one - c_oid_field CONSTANT name := coalesce(c_reg_type, 'object_oid'); - - c_oid_insert CONSTANT text := format( - --USING object_id, classid, objid, objsubid - $$INSERT INTO _object_reference._object_oid(object_id, classid, objid, objsubid, %I) - SELECT $1, $2, $3, $4, $3::%I$$ - , c_oid_field - , coalesce(c_reg_type, 'oid') - ) - ; BEGIN - RAISE DEBUG E'%\n USING %, %, %, %' - , c_oid_insert - , object_id, classid, objid, objsubid - ; - EXECUTE c_oid_insert - USING object_id, classid, objid, objsubid - ; + INSERT INTO _object_reference._object_oid(object_id, classid, objid, objsubid, object_oid) + VALUES (object_id, classid, objid, objsubid, objid); SELECT INTO STRICT r_object_v -- Record better exist! * @@ -631,6 +550,24 @@ CREATE TABLE _object_reference.object_group__object( ); SELECT __object_reference.safe_dump('_object_reference.object_group__object'); +-- Trigger function for automatic object cleanup +SELECT __object_reference.create_function( + '_object_reference._object_group__object__cleanup_trigger' + , '' + , 'trigger LANGUAGE plpgsql' + , $body$ +BEGIN + PERFORM object_reference.object__cleanup(OLD.object_id); + RETURN OLD; +END +$body$ + , 'Trigger function to automatically attempt cleanup of objects when removed from groups.' +); +CREATE TRIGGER object_group__object__cleanup + AFTER DELETE ON _object_reference.object_group__object + FOR EACH ROW + EXECUTE FUNCTION _object_reference._object_group__object__cleanup_trigger(); + -- __get SELECT __object_reference.create_function( 'object_reference.object_group__get' @@ -835,6 +772,69 @@ $body$ , 'object_reference__dependency' ); +/* + * OBJECT INFO FUNCTIONS + */ +SELECT __object_reference.create_function( + 'object_reference.object__describe' + , $args$ + object_id int +$args$ + , 'text LANGUAGE sql' + , $body$ +SELECT pg_catalog.pg_describe_object( + o.classid + , o.objid + , o.objsubid +) +FROM _object_reference._object_oid o +WHERE o.object_id = $1 +$body$ + , 'Return a human-readable description of the object, matching pg_describe_object() format.' + , 'object_reference__usage' +); + +SELECT __object_reference.create_function( + 'object_reference.object__identity' + , $args$ + object_id int + , OUT type text + , OUT schema text + , OUT name text + , OUT identity text +$args$ + , 'record LANGUAGE sql' + , $body$ +SELECT + i.type::text + , i.schema::text + , i.name::text + , i.identity::text +FROM _object_reference._object_oid o + , LATERAL pg_catalog.pg_identify_object(o.classid, o.objid, o.objsubid) i +WHERE o.object_id = $1 +$body$ + , 'Return object identification information matching pg_identify_object() format.' + , 'object_reference__usage' +); +SELECT __object_reference.create_function( + 'object_reference.object__cleanup' + , $args$ + object_id int +$args$ + , 'void LANGUAGE plpgsql' + , $body$ +BEGIN + DELETE FROM _object_reference.object WHERE object.object_id = object__cleanup.object_id; +EXCEPTION WHEN foreign_key_violation THEN + -- Object is still referenced elsewhere, ignore the error + NULL; +END +$body$ + , 'Attempts to delete an object from the tracking system. Silently returns if the object is still referenced by other tables.' + , 'object_reference__usage' +); + /* * OBJECT GETSERT */ @@ -854,6 +854,7 @@ DECLARE r_object_v _object_reference._object_v; r_address record; + r_identity record; did_insert boolean := false; @@ -882,6 +883,15 @@ BEGIN ; END IF; + -- Refuse to track objects in temporary schemas + SELECT INTO r_identity * FROM pg_catalog.pg_identify_object(c_classid, objid, objsubid); + IF r_identity.schema IS NOT NULL AND (r_identity.schema LIKE 'pg_temp%' OR r_identity.schema LIKE 'pg_toast_temp%') THEN + RAISE 'cannot track temporary object' + USING DETAIL = format('object %s is in temporary schema %s', r_identity.identity, r_identity.schema) + , ERRCODE = 'feature_not_supported' + ; + END IF; + -- Ensure the object record exists SELECT INTO r_object_v * diff --git a/test/deps.sql b/test/deps.sql index e1a53c8..d13a016 100644 --- a/test/deps.sql +++ b/test/deps.sql @@ -4,6 +4,5 @@ /* * Normally these should be loaded by the cascade! -CREATE EXTENSION IF NOT EXISTS count_nulls; CREATE EXTENSION IF NOT EXISTS cat_tools; */ diff --git a/test/expected/base.out b/test/expected/base.out index 5e39f56..5357054 100644 --- a/test/expected/base.out +++ b/test/expected/base.out @@ -1,12 +1,13 @@ \set ECHO none -1..9 +1..10 ok 1 - Role object_reference__dependency should be granted USAGE on schema _object_reference ok 2 - Role object_reference__dependency should be granted REFERENCES on table _object_reference.object ok 3 - CREATE TEMP TABLE test_object AS SELECT object_reference.object__getsert('table', 'test_table') AS object_id; -ok 4 - Verify regclass field is correct -ok 5 - Existing object works, provides correct ID -ok 6 - secondary may not be specified for table objects -ok 7 - Verify count_nulls extension can not be relocated -ok 8 - Still works after moving the count_nulls extension -ok 9 - CREATE EXTENSION test_factory +ok 4 - Verify object_oid field is correct +ok 5 - object__describe returns same result as pg_describe_object +ok 6 - object__identity returns same result as pg_identify_object +ok 7 - Existing object works, provides correct ID +ok 8 - secondary may not be specified for table objects +ok 9 - temp objects are rejected +ok 10 - CREATE EXTENSION test_factory # TRANSACTION INTENTIONALLY LEFT OPEN! diff --git a/test/expected/object_group.out b/test/expected/object_group.out index d286833..e670106 100644 --- a/test/expected/object_group.out +++ b/test/expected/object_group.out @@ -1,5 +1,5 @@ \set ECHO none -1..24 +1..29 ok 1 - Register test table 1 ok 2 - object_group__create(...) for group name that is too long throws error ok 3 - object_group__create('object reference test group') @@ -24,6 +24,9 @@ ok 21 - object_group__object__add(...)for missing group throws error ok 22 - Removing group with items in it fails ok 23 - __object__remove() for col1 works ok 24 - __object__remove() for test_table_2 works -ok 25 - Removing empty group works -# Looks like you planned 24 tests but ran 25 +ok 25 - Add test table back to group for cleanup test +ok 26 - Object exists before cleanup test +ok 27 - Remove from group triggers automatic cleanup attempt +ok 28 - Object was automatically cleaned up after group removal +ok 29 - Removing empty group works # TRANSACTION INTENTIONALLY LEFT OPEN! diff --git a/test/expected/zzz_build.out b/test/expected/zzz_build.out index d9612d3..99e1332 100644 --- a/test/expected/zzz_build.out +++ b/test/expected/zzz_build.out @@ -2,16 +2,26 @@ This extension must be loaded via CREATE EXTENSION object_reference; You really, REALLY do NOT want to try and load this via psql!!! -psql:test/temp_load.not_sql:187: WARNING: I promise you will be sorry if you try to use this as anything other than an extension! +psql:test/temp_load.not_sql:176: WARNING: I promise you will be sorry if you try to use this as anything other than an extension! -psql:test/temp_load.not_sql:188: WARNING: I promise you will be sorry if you try to use this as anything other than an extension! +psql:test/temp_load.not_sql:177: WARNING: I promise you will be sorry if you try to use this as anything other than an extension! +psql:test/temp_load.not_sql:323: NOTICE: type reference _object_reference._object_oid.object_id%TYPE converted to integer +psql:test/temp_load.not_sql:323: NOTICE: type reference _object_reference.object.object_type%TYPE converted to cat_tools.object_type +psql:test/temp_load.not_sql:323: NOTICE: type reference _object_reference._object_oid.classid%TYPE converted to oid +psql:test/temp_load.not_sql:323: NOTICE: type reference _object_reference._object_oid.objid%TYPE converted to oid +psql:test/temp_load.not_sql:323: NOTICE: type reference _object_reference._object_oid.objsubid%TYPE converted to integer +psql:test/temp_load.not_sql:323: NOTICE: type reference _object_reference._object_oid.object_id%TYPE converted to integer +psql:test/temp_load.not_sql:323: NOTICE: type reference _object_reference.object.object_type%TYPE converted to cat_tools.object_type +psql:test/temp_load.not_sql:323: NOTICE: type reference _object_reference._object_oid.classid%TYPE converted to oid +psql:test/temp_load.not_sql:323: NOTICE: type reference _object_reference._object_oid.objid%TYPE converted to oid +psql:test/temp_load.not_sql:323: NOTICE: type reference _object_reference._object_oid.objsubid%TYPE converted to integer -psql:test/temp_load.not_sql:512: WARNING: I promise you will be sorry if you try to use this as anything other than an extension! +psql:test/temp_load.not_sql:431: WARNING: I promise you will be sorry if you try to use this as anything other than an extension! @@ -21,28 +31,80 @@ psql:test/temp_load.not_sql:512: WARNING: I promise you will be sorry if you tr -psql:test/temp_load.not_sql:624: WARNING: I promise you will be sorry if you try to use this as anything other than an extension! +psql:test/temp_load.not_sql:543: WARNING: I promise you will be sorry if you try to use this as anything other than an extension! -psql:test/temp_load.not_sql:631: WARNING: I promise you will be sorry if you try to use this as anything other than an extension! +psql:test/temp_load.not_sql:550: WARNING: I promise you will be sorry if you try to use this as anything other than an extension! +psql:test/temp_load.not_sql:595: NOTICE: type reference _object_reference.object_group.object_group_name%TYPE converted to character varying +psql:test/temp_load.not_sql:595: NOTICE: type reference _object_reference.object_group.object_group_name%TYPE converted to character varying +psql:test/temp_load.not_sql:620: NOTICE: type reference _object_reference.object_group.object_group_id%TYPE converted to integer +psql:test/temp_load.not_sql:620: NOTICE: type reference _object_reference.object_group.object_group_id%TYPE converted to integer +psql:test/temp_load.not_sql:635: NOTICE: type reference _object_reference.object_group.object_group_name%TYPE converted to character varying +psql:test/temp_load.not_sql:635: NOTICE: type reference _object_reference.object_group.object_group_name%TYPE converted to character varying +psql:test/temp_load.not_sql:662: NOTICE: type reference _object_reference.object_group.object_group_id%TYPE converted to integer +psql:test/temp_load.not_sql:662: NOTICE: type reference _object_reference.object_group.object_group_id%TYPE converted to integer +psql:test/temp_load.not_sql:678: NOTICE: type reference _object_reference.object_group.object_group_name%TYPE converted to character varying +psql:test/temp_load.not_sql:678: NOTICE: type reference _object_reference.object_group.object_group_name%TYPE converted to character varying +psql:test/temp_load.not_sql:695: NOTICE: type reference _object_reference.object_group__object.object_group_id%TYPE converted to integer +psql:test/temp_load.not_sql:695: NOTICE: type reference _object_reference.object_group__object.object_id%TYPE converted to integer +psql:test/temp_load.not_sql:695: NOTICE: type reference _object_reference.object_group__object.object_group_id%TYPE converted to integer +psql:test/temp_load.not_sql:695: NOTICE: type reference _object_reference.object_group__object.object_id%TYPE converted to integer +psql:test/temp_load.not_sql:729: NOTICE: type reference _object_reference.object_group__object.object_group_id%TYPE converted to integer +psql:test/temp_load.not_sql:729: NOTICE: type reference _object_reference.object_group__object.object_id%TYPE converted to integer +psql:test/temp_load.not_sql:729: NOTICE: type reference _object_reference.object_group__object.object_group_id%TYPE converted to integer +psql:test/temp_load.not_sql:729: NOTICE: type reference _object_reference.object_group__object.object_id%TYPE converted to integer +psql:test/temp_load.not_sql:967: NOTICE: type reference _object_reference.object.object_type%TYPE converted to cat_tools.object_type +psql:test/temp_load.not_sql:967: NOTICE: type reference _object_reference._object_oid.objid%TYPE converted to oid +psql:test/temp_load.not_sql:967: NOTICE: type reference _object_reference._object_oid.objsubid%TYPE converted to integer +psql:test/temp_load.not_sql:967: NOTICE: type reference _object_reference.object.object_type%TYPE converted to cat_tools.object_type +psql:test/temp_load.not_sql:967: NOTICE: type reference _object_reference._object_oid.objid%TYPE converted to oid +psql:test/temp_load.not_sql:967: NOTICE: type reference _object_reference._object_oid.objsubid%TYPE converted to integer +psql:test/temp_load.not_sql:1194: NOTICE: type reference _object_reference.object_group.object_group_name%TYPE converted to character varying +psql:test/temp_load.not_sql:1194: NOTICE: type reference _object_reference.object_group.object_group_name%TYPE converted to character varying +psql:test/temp_load.not_sql:1208: NOTICE: type reference _object_reference.object_group.object_group_name%TYPE converted to character varying +psql:test/temp_load.not_sql:1208: NOTICE: type reference _object_reference.object_group.object_group_name%TYPE converted to character varying +psql:test/temp_load.not_sql:1235: NOTICE: type reference _object_reference.object_group.object_group_id%TYPE converted to integer +psql:test/temp_load.not_sql:1235: NOTICE: type reference _object_reference.object_group.object_group_name%TYPE converted to character varying +psql:test/temp_load.not_sql:1235: NOTICE: type reference _object_reference.object_group.object_group_id%TYPE converted to integer +psql:test/temp_load.not_sql:1235: NOTICE: type reference _object_reference.object_group.object_group_name%TYPE converted to character varying +psql:test/temp_load.not_sql:1235: NOTICE: type reference _object_reference.object_group.object_group_id%TYPE converted to integer +psql:test/temp_load.not_sql:1235: NOTICE: type reference _object_reference.object_group.object_group_name%TYPE converted to character varying +psql:test/temp_load.not_sql:1249: NOTICE: type reference _object_reference.object_group.object_group_id%TYPE converted to integer +psql:test/temp_load.not_sql:1249: NOTICE: type reference _object_reference.object_group.object_group_name%TYPE converted to character varying +psql:test/temp_load.not_sql:1249: NOTICE: type reference _object_reference.object_group.object_group_id%TYPE converted to integer +psql:test/temp_load.not_sql:1249: NOTICE: type reference _object_reference.object_group.object_group_name%TYPE converted to character varying +psql:test/temp_load.not_sql:1249: NOTICE: type reference _object_reference.object_group.object_group_id%TYPE converted to integer +psql:test/temp_load.not_sql:1249: NOTICE: type reference _object_reference.object_group.object_group_name%TYPE converted to character varying + +psql:test/temp_load.not_sql:1296: NOTICE: type reference _object_reference.object_group.object_group_id%TYPE converted to integer +psql:test/temp_load.not_sql:1296: NOTICE: type reference _object_reference.object_group.object_group_id%TYPE converted to integer + +psql:test/temp_load.not_sql:1310: NOTICE: type reference _object_reference.object_group.object_group_name%TYPE converted to character varying +psql:test/temp_load.not_sql:1310: NOTICE: type reference _object_reference.object_group.object_group_name%TYPE converted to character varying + +psql:test/temp_load.not_sql:1355: NOTICE: type reference _object_reference.object_group.object_group_id%TYPE converted to integer +psql:test/temp_load.not_sql:1355: NOTICE: type reference _object_reference.object_group.object_group_id%TYPE converted to integer + +psql:test/temp_load.not_sql:1369: NOTICE: type reference _object_reference.object_group.object_group_name%TYPE converted to character varying +psql:test/temp_load.not_sql:1369: NOTICE: type reference _object_reference.object_group.object_group_name%TYPE converted to character varying diff --git a/test/load.sql b/test/load.sql index 0f1c6be..f1b267f 100644 --- a/test/load.sql +++ b/test/load.sql @@ -1,6 +1,5 @@ \i test/pgxntool/setup.sql --- Need to add count_nulls back into the path SET search_path = tap, public; -- Don't use IF NOT EXISTS here; we want to ensure we always have the latest code diff --git a/test/sql/base.sql b/test/sql/base.sql index 202ae5f..10f48f7 100644 --- a/test/sql/base.sql +++ b/test/sql/base.sql @@ -8,8 +8,8 @@ SELECT plan( 0 +1 -- schema +3 -- initial - +2 -- errors - +2 -- move + +2 -- new functions + +3 -- errors (includes temp object test) +1 -- create extensions ); @@ -32,9 +32,23 @@ SELECT lives_ok( , $$CREATE TEMP TABLE test_object AS SELECT object_reference.object__getsert('table', 'test_table') AS object_id;$$ ); SELECT is( - (SELECT regclass FROM _object_reference._object_v WHERE object_id = (SELECT object_id FROM test_object)) - , 'test_table'::regclass - , 'Verify regclass field is correct' + (SELECT object_oid FROM _object_reference._object_v WHERE object_id = (SELECT object_id FROM test_object)) + , 'test_table'::regclass::oid + , 'Verify object_oid field is correct' +); + +-- Test object__describe function +SELECT is( + object_reference.object__describe((SELECT object_id FROM test_object)) + , pg_catalog.pg_describe_object('pg_class'::regclass, 'test_table'::regclass, 0) + , 'object__describe returns same result as pg_describe_object' +); + +-- Test object__identity function +SELECT results_eq( + $$SELECT * FROM object_reference.object__identity((SELECT object_id FROM test_object))$$ + , $$SELECT type, schema, name, identity FROM pg_catalog.pg_identify_object('pg_class'::regclass, 'test_table'::regclass, 0)$$ + , 'object__identity returns same result as pg_identify_object' ); SELECT is( object_reference.object__getsert('table', 'test_table') @@ -50,24 +64,13 @@ SELECT throws_ok( , 'secondary may not be specified for table objects' ); -/* - * I'm not sure if our extension would continue working if count_nulls was - * relocated. Currently a moot point since relocation isn't supported, but I'd - * already coded the second test so might as well leave it here in case it - * changes in the future. - */ -\set null_schema test_relocate_count_nulls -CREATE SCHEMA :null_schema; +-- Test temp object rejection +CREATE TEMP TABLE temp_test_table(); SELECT throws_ok( - $$ALTER EXTENSION count_nulls SET SCHEMA $$ || :'null_schema' - , '0A000' - , NULL - , 'Verify count_nulls extension can not be relocated' -); -SELECT is( - object_reference.object__getsert('table', 'test_table') - , (SELECT object_id FROM test_object) - , 'Still works after moving the count_nulls extension' + $$SELECT object_reference.object__getsert('table', 'temp_test_table')$$ + , '0A000' -- feature_not_supported + , 'cannot track temporary object' + , 'temp objects are rejected' ); -- Create extensions diff --git a/test/sql/object_group.sql b/test/sql/object_group.sql index 6304892..bef342a 100644 --- a/test/sql/object_group.sql +++ b/test/sql/object_group.sql @@ -2,8 +2,8 @@ \i test/load.sql -CREATE TEMP TABLE test_table_1(col1 int, col2 int); -CREATE TEMP TABLE test_table_2(col1 int, col2 int); +CREATE TABLE object_group_test_table_1(col1 int, col2 int); +CREATE TABLE object_group_test_table_2(col1 int, col2 int); CREATE FUNCTION pg_temp.bogus_group( command_template text @@ -40,10 +40,12 @@ SELECT plan( +4 -- __object__remove +4 + 2 -- __remove + +4 -- cleanup tests + +1 -- final group removal (there was always an extra test) ); SELECT lives_ok( - $$CREATE TEMP TABLE test_table_1_id AS SELECT * FROM object_reference.object__getsert('table', 'test_table_1')$$ + $$CREATE TEMP TABLE test_table_1_id AS SELECT * FROM object_reference.object__getsert('table', 'object_group_test_table_1')$$ , 'Register test table 1' ); @@ -102,37 +104,37 @@ SELECT lives_ok( -- object__getsert SELECT throws_ok( -- Can't use helper here - $$CREATE TEMP TABLE col1_id AS SELECT * FROM object_reference.object__getsert('table column', 'test_table_1', 'col1', 'absurd group name used only for testing purposes ktxbye')$$ + $$CREATE TEMP TABLE col1_id AS SELECT * FROM object_reference.object__getsert('table column', 'object_group_test_table_1', 'col1', 'absurd group name used only for testing purposes ktxbye')$$ , 'P0002' , 'object group "absurd group name used only for testing purposes ktxbye" does not exist' , 'object__getsert with bogus group name' ); /* TODO SELECT throws_ok( -- Can't use helper here - $$CREATE TEMP TABLE col1_id AS SELECT * FROM object_reference.object__getsert_w_group_id('table column', 'test_table_1', 'col1', -1)$$ + $$CREATE TEMP TABLE col1_id AS SELECT * FROM object_reference.object__getsert_w_group_id('table column', 'object_group_test_table_1', 'col1', -1)$$ , '' , '' , 'object__getsert with bogus group id' ); */ SELECT lives_ok( - $$CREATE TEMP TABLE col1_id AS SELECT * FROM object_reference.object__getsert('table column', 'test_table_1', 'col1', 'object reference test group')$$ + $$CREATE TEMP TABLE col1_id AS SELECT * FROM object_reference.object__getsert('table column', 'object_group_test_table_1', 'col1', 'object reference test group')$$ , 'Register test column' ); SELECT lives_ok( - $$CREATE TEMP TABLE test_table_2_id AS SELECT * FROM object_reference.object__getsert('table', 'test_table_2', object_group_name := 'object reference test group')$$ + $$CREATE TEMP TABLE test_table_2_id AS SELECT * FROM object_reference.object__getsert('table', 'object_group_test_table_2', object_group_name := 'object reference test group')$$ , 'Register test table 2' ); -- Drop tests SELECT throws_ok( - $$ALTER TABLE test_table_1 DROP COLUMN col1$$ + $$ALTER TABLE object_group_test_table_1 DROP COLUMN col1$$ , '23503' , NULL -- current error is crap anyway , 'Dropping col1 fails' ); SELECT throws_ok( - $$DROP TABLE test_table_2$$ + $$DROP TABLE object_group_test_table_2$$ , '23503' , NULL -- current error is crap anyway , 'Dropping test_table_2 fails' @@ -144,7 +146,7 @@ SELECT throws_ok( , 'Removing test group fails' ); SELECT lives_ok( - $$ALTER TABLE test_table_1 DROP COLUMN col2$$ + $$ALTER TABLE object_group_test_table_1 DROP COLUMN col2$$ , 'Dropping col2 works' ); @@ -178,7 +180,7 @@ SELECT lives_ok( , '__object__remove() for test_table_1 works' ); SELECT throws_ok( - $$DROP TABLE test_table_1$$ -- Should not work because column is still registered + $$DROP TABLE object_group_test_table_1$$ -- Should not work because column is still registered , '23503' , NULL -- current error is crap anyway , 'Dropping test_table_1 fails' @@ -211,6 +213,26 @@ SELECT lives_ok( ) , '__object__remove() for test_table_2 works' ); + +-- Test automatic cleanup via trigger +SELECT lives_ok( + $$CREATE TEMP TABLE cleanup_test_id AS SELECT * FROM object_reference.object__getsert('table', 'object_group_test_table_1', object_group_name := 'object reference test group')$$ + , 'Add test table back to group for cleanup test' +); +SELECT ok( + EXISTS(SELECT 1 FROM _object_reference.object WHERE object_id = (SELECT object__getsert FROM cleanup_test_id)) + , 'Object exists before cleanup test' +); +SELECT lives_ok( + $$DELETE FROM _object_reference.object_group__object WHERE object_id = (SELECT object__getsert FROM cleanup_test_id)$$ + , 'Remove from group triggers automatic cleanup attempt' +); +-- Object should be deleted because it's no longer in any group and trigger calls cleanup +SELECT ok( + NOT EXISTS(SELECT 1 FROM _object_reference.object WHERE object_id = (SELECT object__getsert FROM cleanup_test_id)) + , 'Object was automatically cleaned up after group removal' +); + SELECT lives_ok( $$SELECT object_reference.object_group__remove('object reference test group')$$ , 'Removing empty group works' diff --git a/test/sql/zzz_build.sql b/test/sql/zzz_build.sql index 4da65b4..4fc0628 100644 --- a/test/sql/zzz_build.sql +++ b/test/sql/zzz_build.sql @@ -6,7 +6,6 @@ -- Loads deps, but not extension itself \i test/pgxntool/setup.sql -CREATE EXTENSION IF NOT EXISTS count_nulls; CREATE EXTENSION IF NOT EXISTS cat_tools; CREATE SCHEMA object_reference;