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
50 changes: 49 additions & 1 deletion sql/object_reference--0.1.0--stable.sql
Original file line number Diff line number Diff line change
Expand Up @@ -132,6 +132,46 @@ $body$;
SELECT set_config('object_reference.saved_session_replication_role', current_setting('session_replication_role'), true);
SET LOCAL session_replication_role = replica;

/*
* _is_own_object(): true for members of the object_reference extension itself;
* _object_v__for_update() uses it to refuse tracking them.
*/
SELECT __object_reference.create_function(
'_object_reference._is_own_object'
, $args$
classid oid
, objid oid
$args$
, 'boolean LANGUAGE sql STABLE'
, $body$
SELECT
EXISTS(
SELECT 1
FROM pg_catalog.pg_depend d
WHERE d.classid = _is_own_object.classid
AND d.objid = _is_own_object.objid
AND d.deptype = 'e'
AND d.refclassid = 'pg_catalog.pg_extension'::regclass
AND d.refobjid = e.oid
)
/*
* The extension's own declared schema (object_reference) is a special
* case: CREATE EXTENSION records the EXTENSION as depending on it (a
* plain DEPENDENCY_NORMAL row, extension -> schema), not the schema as
* an 'e' member of the extension the way every other object it creates
* is -- so it never matches the pg_depend check above.
*/
OR (_is_own_object.classid = 'pg_catalog.pg_namespace'::regclass AND _is_own_object.objid = e.extnamespace)
/*
* The extension's own pg_extension row has no 'e' row of its own, so match it
* by oid.
*/
OR (_is_own_object.classid = 'pg_catalog.pg_extension'::regclass AND _is_own_object.objid = e.oid)
FROM (SELECT oid, extnamespace FROM pg_catalog.pg_extension WHERE extname = 'object_reference') e
$body$
, 'Is the object a member of the object_reference extension itself? (pg_depend deptype = e membership, not just co-installation.)'
);

/*
* _object_reference.object: no column changes, just a missing
* extension_config_dump marking on its sequence (added alongside the table
Expand Down Expand Up @@ -293,6 +333,14 @@ BEGIN
;
END IF;

-- Refuse to track objects that are themselves members of this extension
IF _object_reference._is_own_object(c_classid, objid) THEN
RAISE 'cannot track an object that is a member of the object_reference extension itself'
USING DETAIL = format('object %s belongs to the object_reference extension', r_identity.identity)
, ERRCODE = 'feature_not_supported'
;
END IF;

-- Ensure the object record exists
SELECT INTO r_object_v
*
Expand Down Expand Up @@ -365,7 +413,7 @@ BEGIN
RETURN r_object_v;
END
$body$
, 'Return details of a object record, creating a new record if one does not exist.'
, 'Return details of a object record, creating a new record if one does not exist. Heavy-weight compared to a plain read of _object_reference._object_v -- use that instead when an existing record is all that''s needed.'
);

SELECT __object_reference.create_function(
Expand Down
46 changes: 45 additions & 1 deletion sql/object_reference.sql
Original file line number Diff line number Diff line change
Expand Up @@ -161,6 +161,42 @@ $body$
, 'Execute arbitrary SQL with logging.'
);

SELECT __object_reference.create_function(
'_object_reference._is_own_object'
, $args$
classid oid
, objid oid
$args$
, 'boolean LANGUAGE sql STABLE'
, $body$
SELECT
EXISTS(
SELECT 1
FROM pg_catalog.pg_depend d
WHERE d.classid = _is_own_object.classid
AND d.objid = _is_own_object.objid
AND d.deptype = 'e'
AND d.refclassid = 'pg_catalog.pg_extension'::regclass
AND d.refobjid = e.oid
)
/*
* The extension's own declared schema (object_reference) is a special
* case: CREATE EXTENSION records the EXTENSION as depending on it (a
* plain DEPENDENCY_NORMAL row, extension -> schema), not the schema as
* an 'e' member of the extension the way every other object it creates
* is -- so it never matches the pg_depend check above.
*/
OR (_is_own_object.classid = 'pg_catalog.pg_namespace'::regclass AND _is_own_object.objid = e.extnamespace)
/*
* The extension's own pg_extension row has no 'e' row of its own, so match it
* by oid.
*/
OR (_is_own_object.classid = 'pg_catalog.pg_extension'::regclass AND _is_own_object.objid = e.oid)
FROM (SELECT oid, extnamespace FROM pg_catalog.pg_extension WHERE extname = 'object_reference') e
$body$
, 'Is the object a member of the object_reference extension itself? (pg_depend deptype = e membership, not just co-installation.)'
);

CREATE TABLE _object_reference.object(
object_id serial PRIMARY KEY
, object_type cat_tools.object_type NOT NULL
Expand Down Expand Up @@ -886,6 +922,14 @@ BEGIN
;
END IF;

-- Refuse to track objects that are themselves members of this extension
IF _object_reference._is_own_object(c_classid, objid) THEN
RAISE 'cannot track an object that is a member of the object_reference extension itself'
USING DETAIL = format('object %s belongs to the object_reference extension', r_identity.identity)
, ERRCODE = 'feature_not_supported'
;
END IF;

-- Ensure the object record exists
SELECT INTO r_object_v
*
Expand Down Expand Up @@ -958,7 +1002,7 @@ BEGIN
RETURN r_object_v;
END
$body$
, 'Return details of a object record, creating a new record if one does not exist.'
, 'Return details of a object record, creating a new record if one does not exist. Heavy-weight compared to a plain read of _object_reference._object_v -- use that instead when an existing record is all that''s needed.'
);

SELECT __object_reference.create_function(
Expand Down
11 changes: 6 additions & 5 deletions test/build/expected/build.out
Original file line number Diff line number Diff line change
Expand Up @@ -2,17 +2,17 @@
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: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: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:212: 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:213: 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:425: 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:461: WARNING: I promise you will be sorry if you try to use this as anything other than an extension!



Expand All @@ -21,9 +21,10 @@ psql:test/temp_load.not_sql:425: WARNING: I promise you will be sorry if you tr



psql:test/temp_load.not_sql:537: 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:544: 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:573: 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:580: WARNING: I promise you will be sorry if you try to use this as anything other than an extension!



Expand Down
13 changes: 9 additions & 4 deletions test/expected/base.out
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
\set ECHO none
1..12
1..17
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;
Expand All @@ -9,7 +9,12 @@ 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
ok 11 - object_reference schema must not be part of the resolved search_path
ok 12 - _object_reference schema must not be part of the resolved search_path
ok 10 - own tracking table is rejected
ok 11 - own event trigger function is rejected
ok 12 - own declared schema is rejected (extension depends on it, not the other way around)
ok 13 - own private schema is rejected (an ordinary 'e' pg_depend member, unlike the declared schema above)
ok 14 - _is_own_object() recognizes its own pg_extension row
ok 15 - CREATE EXTENSION test_factory
ok 16 - object_reference schema must not be part of the resolved search_path
ok 17 - _object_reference schema must not be part of the resolved search_path
# TRANSACTION INTENTIONALLY LEFT OPEN!
42 changes: 41 additions & 1 deletion test/sql/base.sql
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ SELECT plan(
+1 -- schema
+3 -- initial
+2 -- new functions
+3 -- errors (includes temp object test)
+8 -- errors (includes temp object + self-tracking rejection tests)
+1 -- create extensions
+2 -- schema-qualification (search_path)
);
Expand Down Expand Up @@ -74,6 +74,46 @@ SELECT throws_ok(
, 'temp objects are rejected'
);

-- Test rejection of object_reference's own extension-member objects
SELECT throws_ok(
$$SELECT object_reference.object__getsert('table', '_object_reference.object')$$
, '0A000' -- feature_not_supported
, 'cannot track an object that is a member of the object_reference extension itself'
, 'own tracking table is rejected'
);
SELECT throws_ok(
$$SELECT object_reference.object__getsert('function', '_object_reference._etg_drop', '')$$
, '0A000' -- feature_not_supported
, 'cannot track an object that is a member of the object_reference extension itself'
, 'own event trigger function is rejected'
);
SELECT throws_ok(
$$SELECT object_reference.object__getsert('schema', 'object_reference')$$
, '0A000' -- feature_not_supported
, 'cannot track an object that is a member of the object_reference extension itself'
, 'own declared schema is rejected (extension depends on it, not the other way around)'
);
SELECT throws_ok(
$$SELECT object_reference.object__getsert('schema', '_object_reference')$$
, '0A000' -- feature_not_supported
, 'cannot track an object that is a member of the object_reference extension itself'
, 'own private schema is rejected (an ordinary ''e'' pg_depend member, unlike the declared schema above)'
);
/*
* Exercised directly against _is_own_object() rather than through
* object__getsert('extension', ...): the latter's generic by-name OID
* lookup for object types with no reg-type cast (extension included)
* derives the wrong catalog column name and fails before ever reaching
* this check (see object__getsert_w_group_id's v_name_field derivation).
*/
SELECT ok(
_object_reference._is_own_object(
'pg_catalog.pg_extension'::regclass
, (SELECT oid FROM pg_catalog.pg_extension WHERE extname = 'object_reference')
)
, '_is_own_object() recognizes its own pg_extension row'
);

-- Create extensions
SELECT lives_ok(
$$CREATE EXTENSION test_factory$$
Expand Down
Loading