diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c index 3a4245d5dc..128863744c 100644 --- a/src/backend/catalog/objectaddress.c +++ b/src/backend/catalog/objectaddress.c @@ -431,6 +431,106 @@ static const ObjectPropertyType ObjectProperty[] = } }; +/* + * This struct maps the string object types as returned by + * getObjectTypeDescription into ObjType enum values. Note that some enum + * values can be obtained by different names, and that some string object types + * do not have corresponding values in the output enum. The user of this map + * must be careful to test for invalid values being returned. + * + * To ease maintenance, this follows the order of getObjectTypeDescription. + */ +static const struct object_type_map +{ + const char *tm_name; + ObjectType tm_type; +} +ObjectTypeMap[] = +{ + /* OCLASS_CLASS, all kinds of relations */ + { "table", OBJECT_TABLE }, + { "index", OBJECT_INDEX }, + { "sequence", OBJECT_SEQUENCE }, + { "toast table", -1 }, /* unmapped */ + { "view", OBJECT_VIEW }, + { "materialized view", OBJECT_MATVIEW }, + { "composite type", -1 }, /* unmapped */ + { "foreign table", OBJECT_FOREIGN_TABLE }, + { "table column", OBJECT_COLUMN }, + { "index column", -1 }, /* unmapped */ + { "sequence column", -1 }, /* unmapped */ + { "toast table column", -1 }, /* unmapped */ + { "view column", -1 }, /* unmapped */ + { "materialized view column", -1 }, /* unmapped */ + { "composite type column", -1 }, /* unmapped */ + { "foreign table column", OBJECT_COLUMN }, + /* OCLASS_PROC */ + { "aggregate", OBJECT_AGGREGATE }, + { "function", OBJECT_FUNCTION }, + /* OCLASS_TYPE */ + { "type", OBJECT_TYPE }, + /* OCLASS_CAST */ + { "cast", OBJECT_CAST }, + /* OCLASS_COLLATION */ + { "collation", OBJECT_COLLATION }, + /* OCLASS_CONSTRAINT */ + { "table constraint", OBJECT_TABCONSTRAINT }, + { "domain constraint", OBJECT_DOMCONSTRAINT }, + /* OCLASS_CONVERSION */ + { "conversion", OBJECT_CONVERSION }, + /* OCLASS_DEFAULT */ + { "default value", OBJECT_DEFAULT }, + /* OCLASS_LANGUAGE */ + { "language", OBJECT_LANGUAGE }, + /* OCLASS_LARGEOBJECT */ + { "large object", OBJECT_LARGEOBJECT }, + /* OCLASS_OPERATOR */ + { "operator", OBJECT_OPERATOR }, + /* OCLASS_OPCLASS */ + { "operator class", OBJECT_OPCLASS }, + /* OCLASS_OPFAMILY */ + { "operator family", OBJECT_OPFAMILY }, + /* OCLASS_AMOP */ + { "operator of access method", -1 }, /* unmapped */ + /* OCLASS_AMPROC */ + { "function of access method", -1 }, /* unmapped */ + /* OCLASS_REWRITE */ + { "rule", OBJECT_RULE }, + /* OCLASS_TRIGGER */ + { "trigger", OBJECT_TRIGGER }, + /* OCLASS_SCHEMA */ + { "schema", OBJECT_SCHEMA }, + /* OCLASS_TSPARSER */ + { "text search parser", OBJECT_TSPARSER }, + /* OCLASS_TSDICT */ + { "text search dictionary", OBJECT_TSDICTIONARY }, + /* OCLASS_TSTEMPLATE */ + { "text search template", OBJECT_TSTEMPLATE }, + /* OCLASS_TSCONFIG */ + { "text search configuration", OBJECT_TSCONFIGURATION }, + /* OCLASS_ROLE */ + { "role", OBJECT_ROLE }, + /* OCLASS_DATABASE */ + { "database", OBJECT_DATABASE }, + /* OCLASS_TBLSPACE */ + { "tablespace", OBJECT_TABLESPACE }, + /* OCLASS_FDW */ + { "foreign-data wrapper", OBJECT_FDW }, + /* OCLASS_FOREIGN_SERVER */ + { "server", OBJECT_FOREIGN_SERVER }, + /* OCLASS_USER_MAPPING */ + { "user mapping", -1 }, /* unmapped */ + /* OCLASS_DEFACL */ + { "default acl", -1 }, /* unmapped */ + /* OCLASS_EXTENSION */ + { "extension", OBJECT_EXTENSION }, + /* OCLASS_EVENT_TRIGGER */ + { "event trigger", OBJECT_EVENT_TRIGGER }, + /* OCLASS_POLICY */ + { "policy", OBJECT_POLICY } +}; + + static ObjectAddress get_object_address_unqualified(ObjectType objtype, List *qualname, bool missing_ok); static ObjectAddress get_relation_by_qualified_name(ObjectType objtype, @@ -441,6 +541,9 @@ static ObjectAddress get_object_address_relobject(ObjectType objtype, static ObjectAddress get_object_address_attribute(ObjectType objtype, List *objname, Relation *relp, LOCKMODE lockmode, bool missing_ok); +static ObjectAddress get_object_address_attrdef(ObjectType objtype, + List *objname, Relation *relp, LOCKMODE lockmode, + bool missing_ok); static ObjectAddress get_object_address_type(ObjectType objtype, List *objname, bool missing_ok); static ObjectAddress get_object_address_opcf(ObjectType objtype, List *objname, @@ -528,6 +631,12 @@ get_object_address(ObjectType objtype, List *objname, List *objargs, &relation, lockmode, missing_ok); break; + case OBJECT_DEFAULT: + address = + get_object_address_attrdef(objtype, objname, + &relation, lockmode, + missing_ok); + break; case OBJECT_RULE: case OBJECT_TRIGGER: case OBJECT_TABCONSTRAINT: @@ -1096,6 +1205,88 @@ get_object_address_attribute(ObjectType objtype, List *objname, return address; } +/* + * Find the ObjectAddress for an attribute's default value. + */ +static ObjectAddress +get_object_address_attrdef(ObjectType objtype, List *objname, + Relation *relp, LOCKMODE lockmode, + bool missing_ok) +{ + ObjectAddress address; + List *relname; + Oid reloid; + Relation relation; + const char *attname; + AttrNumber attnum; + TupleDesc tupdesc; + Oid defoid; + + /* Extract relation name and open relation. */ + if (list_length(objname) < 2) + ereport(ERROR, + (errcode(ERRCODE_SYNTAX_ERROR), + errmsg("column name must be qualified"))); + attname = strVal(llast(objname)); + relname = list_truncate(list_copy(objname), list_length(objname) - 1); + /* XXX no missing_ok support here */ + relation = relation_openrv(makeRangeVarFromNameList(relname), lockmode); + reloid = RelationGetRelid(relation); + + tupdesc = RelationGetDescr(relation); + + /* Look up attribute number and scan pg_attrdef to find its tuple */ + attnum = get_attnum(reloid, attname); + defoid = InvalidOid; + if (attnum != InvalidAttrNumber && tupdesc->constr != NULL) + { + Relation attrdef; + ScanKeyData keys[2]; + SysScanDesc scan; + HeapTuple tup; + + attrdef = relation_open(AttrDefaultRelationId, AccessShareLock); + ScanKeyInit(&keys[0], + Anum_pg_attrdef_adrelid, + BTEqualStrategyNumber, + F_OIDEQ, + ObjectIdGetDatum(reloid)); + ScanKeyInit(&keys[1], + Anum_pg_attrdef_adnum, + BTEqualStrategyNumber, + F_INT2EQ, + Int16GetDatum(attnum)); + scan = systable_beginscan(attrdef, AttrDefaultIndexId, true, + NULL, 2, keys); + if (HeapTupleIsValid(tup = systable_getnext(scan))) + defoid = HeapTupleGetOid(tup); + + systable_endscan(scan); + relation_close(attrdef, AccessShareLock); + } + if (!OidIsValid(defoid)) + { + if (!missing_ok) + ereport(ERROR, + (errcode(ERRCODE_UNDEFINED_COLUMN), + errmsg("default value for column \"%s\" of relation \"%s\" does not exist", + attname, NameListToString(relname)))); + + address.classId = AttrDefaultRelationId; + address.objectId = InvalidOid; + address.objectSubId = InvalidAttrNumber; + relation_close(relation, lockmode); + return address; + } + + address.classId = AttrDefaultRelationId; + address.objectId = defoid; + address.objectSubId = 0; + + *relp = relation; + return address; +} + /* * Find the ObjectAddress for a type or domain */ @@ -1176,6 +1367,225 @@ get_object_address_opcf(ObjectType objtype, return address; } +/* + * Convert an array of TEXT into a List of string Values, as emitted by the + * parser, which is what get_object_address uses as input. + */ +static List * +textarray_to_strvaluelist(ArrayType *arr) +{ + Datum *elems; + bool *nulls; + int nelems; + List *list = NIL; + int i; + + deconstruct_array(arr, TEXTOID, -1, false, 'i', + &elems, &nulls, &nelems); + + for (i = 0; i < nelems; i++) + { + if (nulls[i]) + ereport(ERROR, + (errcode(ERRCODE_INVALID_PARAMETER_VALUE), + errmsg("name or argument lists may not contain nulls"))); + list = lappend(list, makeString(TextDatumGetCString(elems[i]))); + } + + return list; +} + +/* + * SQL-callable version of get_object_address + */ +Datum +pg_get_object_address(PG_FUNCTION_ARGS) +{ + char *ttype = TextDatumGetCString(PG_GETARG_TEXT_P(0)); + ArrayType *namearr = PG_GETARG_ARRAYTYPE_P(1); + ArrayType *argsarr = PG_GETARG_ARRAYTYPE_P(2); + int itype; + ObjectType type; + List *name; + List *args; + ObjectAddress addr; + TupleDesc tupdesc; + Datum values[3]; + bool nulls[3]; + HeapTuple htup; + Relation relation; + + /* Decode object type, raise error if unknown */ + ttype = TextDatumGetCString(PG_GETARG_TEXT_P(0)); + itype = read_objtype_from_string(ttype); + if (itype < 0) + ereport(ERROR, + (errcode(ERRCODE_INVALID_PARAMETER_VALUE), + errmsg("unsupported object type \"%s\"", ttype))); + type = (ObjectType) itype; + + /* + * Convert the text array to the representation appropriate for the + * given object type. Most use a simple string Values list, but there + * are some exceptions. + */ + if (type == OBJECT_TYPE || type == OBJECT_DOMAIN) + { + Datum *elems; + bool *nulls; + int nelems; + TypeName *typname; + + deconstruct_array(namearr, TEXTOID, -1, false, 'i', + &elems, &nulls, &nelems); + if (nelems != 1) + ereport(ERROR, + (errcode(ERRCODE_INVALID_PARAMETER_VALUE), + errmsg("name list length must be exactly %d", 1))); + if (nulls[0]) + ereport(ERROR, + (errcode(ERRCODE_INVALID_PARAMETER_VALUE), + errmsg("name or argument lists may not contain nulls"))); + typname = typeStringToTypeName(TextDatumGetCString(elems[0])); + name = typname->names; + } + else if (type == OBJECT_CAST) + { + Datum *elems; + bool *nulls; + int nelems; + + deconstruct_array(namearr, TEXTOID, -1, false, 'i', + &elems, &nulls, &nelems); + if (nelems != 1) + ereport(ERROR, + (errcode(ERRCODE_INVALID_PARAMETER_VALUE), + errmsg("name list length must be exactly %d", 1))); + if (nulls[0]) + ereport(ERROR, + (errcode(ERRCODE_INVALID_PARAMETER_VALUE), + errmsg("name or argument lists may not contain nulls"))); + name = list_make1(typeStringToTypeName(TextDatumGetCString(elems[0]))); + } + else if (type == OBJECT_LARGEOBJECT) + { + Datum *elems; + bool *nulls; + int nelems; + + deconstruct_array(namearr, TEXTOID, -1, false, 'i', + &elems, &nulls, &nelems); + if (nelems != 1) + ereport(ERROR, + (errcode(ERRCODE_INVALID_PARAMETER_VALUE), + errmsg("name list length must be exactly %d", 1))); + if (nulls[0]) + ereport(ERROR, + (errcode(ERRCODE_INVALID_PARAMETER_VALUE), + errmsg("large object OID may not be null"))); + name = list_make1(makeFloat(TextDatumGetCString(elems[0]))); + } + else + { + name = textarray_to_strvaluelist(namearr); + if (list_length(name) < 1) + ereport(ERROR, + (errcode(ERRCODE_INVALID_PARAMETER_VALUE), + errmsg("name list must be of length at least %d", 1))); + } + + /* + * If args are given, decode them according to the object type. + */ + if (type == OBJECT_AGGREGATE || + type == OBJECT_FUNCTION || + type == OBJECT_OPERATOR || + type == OBJECT_CAST) + { + /* in these cases, the args list must be of TypeName */ + Datum *elems; + bool *nulls; + int nelems; + int i; + + deconstruct_array(argsarr, TEXTOID, -1, false, 'i', + &elems, &nulls, &nelems); + + args = NIL; + for (i = 0; i < nelems; i++) + { + if (nulls[i]) + ereport(ERROR, + (errcode(ERRCODE_INVALID_PARAMETER_VALUE), + errmsg("name or argument lists may not contain nulls"))); + args = lappend(args, + typeStringToTypeName(TextDatumGetCString(elems[i]))); + } + } + else + { + /* For all other object types, use string Values */ + args = textarray_to_strvaluelist(argsarr); + } + + /* + * get_object_name is pretty sensitive to the length its input lists; + * check that they're what it wants. + */ + switch (type) + { + case OBJECT_LARGEOBJECT: + if (list_length(name) != 1) + ereport(ERROR, + (errcode(ERRCODE_INVALID_PARAMETER_VALUE), + errmsg("name list length must be %d", 1))); + break; + case OBJECT_OPCLASS: + case OBJECT_OPFAMILY: + case OBJECT_CAST: + if (list_length(args) != 1) + ereport(ERROR, + (errcode(ERRCODE_INVALID_PARAMETER_VALUE), + errmsg("argument list length must be exactly %d", 1))); + break; + case OBJECT_OPERATOR: + if (list_length(args) != 2) + ereport(ERROR, + (errcode(ERRCODE_INVALID_PARAMETER_VALUE), + errmsg("argument list length must be exactly %d", 2))); + break; + default: + break; + } + + addr = get_object_address(type, name, args, + &relation, AccessShareLock, false); + + /* We don't need the relcache entry, thank you very much */ + if (relation) + relation_close(relation, AccessShareLock); + + tupdesc = CreateTemplateTupleDesc(3, false); + TupleDescInitEntry(tupdesc, (AttrNumber) 1, "classid", + OIDOID, -1, 0); + TupleDescInitEntry(tupdesc, (AttrNumber) 2, "objid", + OIDOID, -1, 0); + TupleDescInitEntry(tupdesc, (AttrNumber) 3, "objsubid", + INT4OID, -1, 0); + tupdesc = BlessTupleDesc(tupdesc); + + values[0] = ObjectIdGetDatum(addr.classId); + values[1] = ObjectIdGetDatum(addr.objectId); + values[2] = Int32GetDatum(addr.objectSubId); + nulls[0] = false; + nulls[1] = false; + nulls[2] = false; + + htup = heap_form_tuple(tupdesc, values, nulls); + + PG_RETURN_DATUM(HeapTupleGetDatum(htup)); +} + /* * Check ownership of an object previously identified by get_object_address. */ @@ -1387,6 +1797,34 @@ get_object_namespace(const ObjectAddress *address) return oid; } +/* + * Return ObjectType for the given object type as given by + * getObjectTypeDescription; if no valid ObjectType code exists, but it's a + * possible output type from getObjectTypeDescription, return -1. + * Otherwise, an error is thrown. + */ +int +read_objtype_from_string(const char *objtype) +{ + ObjectType type; + int i; + + for (i = 0; i < lengthof(ObjectTypeMap); i++) + { + if (strcmp(ObjectTypeMap[i].tm_name, objtype) == 0) + { + type = ObjectTypeMap[i].tm_type; + break; + } + } + if (i >= lengthof(ObjectTypeMap)) + ereport(ERROR, + (errcode(ERRCODE_INVALID_PARAMETER_VALUE), + errmsg("unrecognized object type \"%s\"", objtype))); + + return type; +} + /* * Interfaces to reference fields of ObjectPropertyType */ @@ -2518,6 +2956,8 @@ pg_identify_object(PG_FUNCTION_ARGS) /* * Return a palloc'ed string that describes the type of object that the * passed address is for. + * + * Keep ObjectTypeMap in sync with this. */ char * getObjectTypeDescription(const ObjectAddress *object) diff --git a/src/backend/commands/event_trigger.c b/src/backend/commands/event_trigger.c index 6bdb774987..34dd3c01ad 100644 --- a/src/backend/commands/event_trigger.c +++ b/src/backend/commands/event_trigger.c @@ -1055,6 +1055,7 @@ EventTriggerSupportsObjectType(ObjectType obtype) case OBJECT_COLUMN: case OBJECT_COLLATION: case OBJECT_CONVERSION: + case OBJECT_DEFAULT: case OBJECT_DOMAIN: case OBJECT_DOMCONSTRAINT: case OBJECT_EXTENSION: diff --git a/src/backend/parser/parse_type.c b/src/backend/parser/parse_type.c index d0803dfafd..299cc5359f 100644 --- a/src/backend/parser/parse_type.c +++ b/src/backend/parser/parse_type.c @@ -705,13 +705,11 @@ pts_error_callback(void *arg) /* * Given a string that is supposed to be a SQL-compatible type declaration, * such as "int4" or "integer" or "character varying(32)", parse - * the string and convert it to a type OID and type modifier. - * If missing_ok is true, InvalidOid is returned rather than raising an error - * when the type name is not found. + * the string and return the result as a TypeName. + * If the string cannot be parsed as a type, an error is raised. */ -void -parseTypeString(const char *str, Oid *typeid_p, int32 *typmod_p, - bool missing_ok) +TypeName * +typeStringToTypeName(const char *str) { StringInfoData buf; List *raw_parsetree_list; @@ -720,7 +718,6 @@ parseTypeString(const char *str, Oid *typeid_p, int32 *typmod_p, TypeCast *typecast; TypeName *typeName; ErrorContextCallback ptserrcontext; - Type tup; /* make sure we give useful error for empty input */ if (strspn(str, " \t\n\r\f") == strlen(str)) @@ -779,6 +776,7 @@ parseTypeString(const char *str, Oid *typeid_p, int32 *typmod_p, typecast->arg == NULL || !IsA(typecast->arg, A_Const)) goto fail; + typeName = typecast->typeName; if (typeName == NULL || !IsA(typeName, TypeName)) @@ -786,6 +784,31 @@ parseTypeString(const char *str, Oid *typeid_p, int32 *typmod_p, if (typeName->setof) goto fail; + pfree(buf.data); + + return typeName; + +fail: + ereport(ERROR, + (errcode(ERRCODE_SYNTAX_ERROR), + errmsg("invalid type name \"%s\"", str))); +} + +/* + * Given a string that is supposed to be a SQL-compatible type declaration, + * such as "int4" or "integer" or "character varying(32)", parse + * the string and convert it to a type OID and type modifier. + * If missing_ok is true, InvalidOid is returned rather than raising an error + * when the type name is not found. + */ +void +parseTypeString(const char *str, Oid *typeid_p, int32 *typmod_p, bool missing_ok) +{ + TypeName *typeName; + Type tup; + + typeName = typeStringToTypeName(str); + tup = LookupTypeName(NULL, typeName, typmod_p, missing_ok); if (tup == NULL) { @@ -808,13 +831,4 @@ parseTypeString(const char *str, Oid *typeid_p, int32 *typmod_p, *typeid_p = HeapTupleGetOid(tup); ReleaseSysCache(tup); } - - pfree(buf.data); - - return; - -fail: - ereport(ERROR, - (errcode(ERRCODE_SYNTAX_ERROR), - errmsg("invalid type name \"%s\"", str))); } diff --git a/src/include/catalog/catversion.h b/src/include/catalog/catversion.h index e56f7ded9d..73c4b9858d 100644 --- a/src/include/catalog/catversion.h +++ b/src/include/catalog/catversion.h @@ -53,6 +53,6 @@ */ /* yyyymmddN */ -#define CATALOG_VERSION_NO 201412232 +#define CATALOG_VERSION_NO 201412233 #endif diff --git a/src/include/catalog/objectaddress.h b/src/include/catalog/objectaddress.h index 2a9431d0d3..d885692a43 100644 --- a/src/include/catalog/objectaddress.h +++ b/src/include/catalog/objectaddress.h @@ -55,6 +55,7 @@ extern HeapTuple get_catalog_object_by_oid(Relation catalog, extern char *getObjectDescription(const ObjectAddress *object); extern char *getObjectDescriptionOids(Oid classid, Oid objid); +extern int read_objtype_from_string(const char *objtype); extern char *getObjectTypeDescription(const ObjectAddress *object); extern char *getObjectIdentity(const ObjectAddress *address); diff --git a/src/include/catalog/pg_proc.h b/src/include/catalog/pg_proc.h index 7f64aaa964..61a03e08a6 100644 --- a/src/include/catalog/pg_proc.h +++ b/src/include/catalog/pg_proc.h @@ -3036,6 +3036,9 @@ DESCR("get identification of SQL object"); DATA(insert OID = 3839 ( pg_identify_object PGNSP PGUID 12 1 0 0 0 f f f f t f s 3 0 2249 "26 26 23" "{26,26,23,25,25,25,25}" "{i,i,i,o,o,o,o}" "{classid,objid,subobjid,type,schema,name,identity}" _null_ pg_identify_object _null_ _null_ _null_ )); DESCR("get machine-parseable identification of SQL object"); +DATA(insert OID = 3954 ( pg_get_object_address PGNSP PGUID 12 1 0 0 0 f f f f t f s 3 0 2249 "25 1009 1009" "{25,1009,1009,26,26,23}" "{i,i,i,o,o,o}" "{type,name,args,classid,objid,subobjid}" _null_ pg_get_object_address _null_ _null_ _null_ )); +DESCR("get OID-based object address from name/args arrays"); + DATA(insert OID = 2079 ( pg_table_is_visible PGNSP PGUID 12 10 0 0 0 f f f f t f s 1 0 16 "26" _null_ _null_ _null_ _null_ pg_table_is_visible _null_ _null_ _null_ )); DESCR("is table visible in search path?"); DATA(insert OID = 2080 ( pg_type_is_visible PGNSP PGUID 12 10 0 0 0 f f f f t f s 1 0 16 "26" _null_ _null_ _null_ _null_ pg_type_is_visible _null_ _null_ _null_ )); diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h index f027a37535..48ca5f1bb8 100644 --- a/src/include/nodes/parsenodes.h +++ b/src/include/nodes/parsenodes.h @@ -1185,6 +1185,7 @@ typedef enum ObjectType OBJECT_COLLATION, OBJECT_CONVERSION, OBJECT_DATABASE, + OBJECT_DEFAULT, OBJECT_DOMAIN, OBJECT_DOMCONSTRAINT, OBJECT_EVENT_TRIGGER, diff --git a/src/include/parser/parse_type.h b/src/include/parser/parse_type.h index fa9cc5989b..152bdbb01e 100644 --- a/src/include/parser/parse_type.h +++ b/src/include/parser/parse_type.h @@ -47,6 +47,7 @@ extern Datum stringTypeDatum(Type tp, char *string, int32 atttypmod); extern Oid typeidTypeRelid(Oid type_id); +extern TypeName *typeStringToTypeName(const char *str); extern void parseTypeString(const char *str, Oid *typeid_p, int32 *typmod_p, bool missing_ok); #define ISCOMPLEX(typeid) (typeidTypeRelid(typeid) != InvalidOid) diff --git a/src/include/utils/builtins.h b/src/include/utils/builtins.h index c8e0e3a434..50915a4197 100644 --- a/src/include/utils/builtins.h +++ b/src/include/utils/builtins.h @@ -1201,6 +1201,9 @@ extern Datum pg_last_committed_xact(PG_FUNCTION_ARGS); extern Datum pg_describe_object(PG_FUNCTION_ARGS); extern Datum pg_identify_object(PG_FUNCTION_ARGS); +/* catalog/objectaddress.c */ +extern Datum pg_get_object_address(PG_FUNCTION_ARGS); + /* commands/constraint.c */ extern Datum unique_key_recheck(PG_FUNCTION_ARGS); diff --git a/src/test/regress/expected/object_address.out b/src/test/regress/expected/object_address.out new file mode 100644 index 0000000000..67eadffc6b --- /dev/null +++ b/src/test/regress/expected/object_address.out @@ -0,0 +1,399 @@ +-- +-- Test for pg_get_object_address +-- +-- Clean up in case a prior regression run failed +SET client_min_messages TO 'warning'; +DROP ROLE IF EXISTS regtest_addr_user; +CREATE USER regtest_addr_user; +-- Test generic object addressing/identification functions +CREATE SCHEMA addr_nsp; +SET search_path TO 'addr_nsp'; +CREATE FOREIGN DATA WRAPPER addr_fdw; +CREATE SERVER addr_fserv FOREIGN DATA WRAPPER addr_fdw; +CREATE TEXT SEARCH DICTIONARY addr_ts_dict (template=simple); +CREATE TEXT SEARCH CONFIGURATION addr_ts_conf (copy=english); +CREATE TEXT SEARCH TEMPLATE addr_ts_temp (lexize=dsimple_lexize); +CREATE TEXT SEARCH PARSER addr_ts_prs + (start = prsd_start, gettoken = prsd_nexttoken, end = prsd_end, lextypes = prsd_lextype); +CREATE TABLE addr_nsp.gentable ( + a serial primary key CONSTRAINT a_chk CHECK (a > 0), + b text DEFAULT 'hello'); +CREATE VIEW addr_nsp.genview AS SELECT * from addr_nsp.gentable; +CREATE MATERIALIZED VIEW addr_nsp.genmatview AS SELECT * FROM addr_nsp.gentable; +CREATE TYPE addr_nsp.gencomptype AS (a int); +CREATE TYPE addr_nsp.genenum AS ENUM ('one', 'two'); +CREATE FOREIGN TABLE addr_nsp.genftable (a int) SERVER addr_fserv; +CREATE AGGREGATE addr_nsp.genaggr(int4) (sfunc = int4pl, stype = int4); +CREATE DOMAIN addr_nsp.gendomain AS int4 CONSTRAINT domconstr CHECK (value > 0); +CREATE FUNCTION addr_nsp.trig() RETURNS TRIGGER LANGUAGE plpgsql AS $$ BEGIN END; $$; +CREATE TRIGGER t BEFORE INSERT ON addr_nsp.gentable FOR EACH ROW EXECUTE PROCEDURE addr_nsp.trig(); +CREATE POLICY genpol ON addr_nsp.gentable; +CREATE FUNCTION addr_nsp.etrig() RETURNS EVENT_TRIGGER LANGUAGE plpgsql AS $$ BEGIN END; $$; +CREATE EVENT TRIGGER evttrig ON ddl_command_end EXECUTE PROCEDURE addr_nsp.etrig(); +-- test some error cases +SELECT pg_get_object_address('stone', '{}', '{}'); +ERROR: unrecognized object type "stone" +SELECT pg_get_object_address('table', '{}', '{}'); +ERROR: name list must be of length at least 1 +SELECT pg_get_object_address('table', '{NULL}', '{}'); +ERROR: name or argument lists may not contain nulls +-- unrecognized object types +DO $$ +DECLARE + objtype text; +BEGIN + FOR objtype IN VALUES ('toast table'), ('index column'), ('sequence column'), + ('toast table column'), ('view column'), ('materialized view column'), + ('operator of access method'), ('function of access method'), + ('user mapping') + LOOP + BEGIN + PERFORM pg_get_object_address(objtype, '{one}', '{}'); + EXCEPTION WHEN invalid_parameter_value THEN + RAISE WARNING 'error for %: %', objtype, sqlerrm; + END; + END LOOP; +END; +$$; +WARNING: error for toast table: unsupported object type "toast table" +WARNING: error for index column: unsupported object type "index column" +WARNING: error for sequence column: unsupported object type "sequence column" +WARNING: error for toast table column: unsupported object type "toast table column" +WARNING: error for view column: unsupported object type "view column" +WARNING: error for materialized view column: unsupported object type "materialized view column" +WARNING: error for operator of access method: unsupported object type "operator of access method" +WARNING: error for function of access method: unsupported object type "function of access method" +WARNING: error for user mapping: unsupported object type "user mapping" +DO $$ +DECLARE + objtype text; + names text[]; + args text[]; +BEGIN + FOR objtype IN VALUES + ('table'), ('index'), ('sequence'), ('view'), + ('materialized view'), ('foreign table'), + ('table column'), ('foreign table column'), + ('aggregate'), ('function'), ('type'), ('cast'), + ('collation'), + ('table constraint'), ('domain constraint'), ('conversion'), ('default value'), + ('operator'), ('operator class'), ('operator family'), ('rule'), ('trigger'), + ('text search parser'), ('text search dictionary'), + ('text search template'), ('text search configuration'), + ('policy') + LOOP + FOR names IN VALUES ('{eins}'), ('{addr_nsp, zwei}'), ('{eins, zwei, drei}') + LOOP + FOR args IN VALUES ('{}'), ('{integer}') + LOOP + BEGIN + PERFORM pg_get_object_address(objtype, names, args); + EXCEPTION WHEN OTHERS THEN + RAISE WARNING 'error for %,%,%: %', objtype, names, args, sqlerrm; + END; + END LOOP; + END LOOP; + END LOOP; +END; +$$; +WARNING: error for table,{eins},{}: relation "eins" does not exist +WARNING: error for table,{eins},{integer}: relation "eins" does not exist +WARNING: error for table,{addr_nsp,zwei},{}: relation "addr_nsp.zwei" does not exist +WARNING: error for table,{addr_nsp,zwei},{integer}: relation "addr_nsp.zwei" does not exist +WARNING: error for table,{eins,zwei,drei},{}: cross-database references are not implemented: "eins.zwei.drei" +WARNING: error for table,{eins,zwei,drei},{integer}: cross-database references are not implemented: "eins.zwei.drei" +WARNING: error for index,{eins},{}: relation "eins" does not exist +WARNING: error for index,{eins},{integer}: relation "eins" does not exist +WARNING: error for index,{addr_nsp,zwei},{}: relation "addr_nsp.zwei" does not exist +WARNING: error for index,{addr_nsp,zwei},{integer}: relation "addr_nsp.zwei" does not exist +WARNING: error for index,{eins,zwei,drei},{}: cross-database references are not implemented: "eins.zwei.drei" +WARNING: error for index,{eins,zwei,drei},{integer}: cross-database references are not implemented: "eins.zwei.drei" +WARNING: error for sequence,{eins},{}: relation "eins" does not exist +WARNING: error for sequence,{eins},{integer}: relation "eins" does not exist +WARNING: error for sequence,{addr_nsp,zwei},{}: relation "addr_nsp.zwei" does not exist +WARNING: error for sequence,{addr_nsp,zwei},{integer}: relation "addr_nsp.zwei" does not exist +WARNING: error for sequence,{eins,zwei,drei},{}: cross-database references are not implemented: "eins.zwei.drei" +WARNING: error for sequence,{eins,zwei,drei},{integer}: cross-database references are not implemented: "eins.zwei.drei" +WARNING: error for view,{eins},{}: relation "eins" does not exist +WARNING: error for view,{eins},{integer}: relation "eins" does not exist +WARNING: error for view,{addr_nsp,zwei},{}: relation "addr_nsp.zwei" does not exist +WARNING: error for view,{addr_nsp,zwei},{integer}: relation "addr_nsp.zwei" does not exist +WARNING: error for view,{eins,zwei,drei},{}: cross-database references are not implemented: "eins.zwei.drei" +WARNING: error for view,{eins,zwei,drei},{integer}: cross-database references are not implemented: "eins.zwei.drei" +WARNING: error for materialized view,{eins},{}: relation "eins" does not exist +WARNING: error for materialized view,{eins},{integer}: relation "eins" does not exist +WARNING: error for materialized view,{addr_nsp,zwei},{}: relation "addr_nsp.zwei" does not exist +WARNING: error for materialized view,{addr_nsp,zwei},{integer}: relation "addr_nsp.zwei" does not exist +WARNING: error for materialized view,{eins,zwei,drei},{}: cross-database references are not implemented: "eins.zwei.drei" +WARNING: error for materialized view,{eins,zwei,drei},{integer}: cross-database references are not implemented: "eins.zwei.drei" +WARNING: error for foreign table,{eins},{}: relation "eins" does not exist +WARNING: error for foreign table,{eins},{integer}: relation "eins" does not exist +WARNING: error for foreign table,{addr_nsp,zwei},{}: relation "addr_nsp.zwei" does not exist +WARNING: error for foreign table,{addr_nsp,zwei},{integer}: relation "addr_nsp.zwei" does not exist +WARNING: error for foreign table,{eins,zwei,drei},{}: cross-database references are not implemented: "eins.zwei.drei" +WARNING: error for foreign table,{eins,zwei,drei},{integer}: cross-database references are not implemented: "eins.zwei.drei" +WARNING: error for table column,{eins},{}: column name must be qualified +WARNING: error for table column,{eins},{integer}: column name must be qualified +WARNING: error for table column,{addr_nsp,zwei},{}: relation "addr_nsp" does not exist +WARNING: error for table column,{addr_nsp,zwei},{integer}: relation "addr_nsp" does not exist +WARNING: error for table column,{eins,zwei,drei},{}: schema "eins" does not exist +WARNING: error for table column,{eins,zwei,drei},{integer}: schema "eins" does not exist +WARNING: error for foreign table column,{eins},{}: column name must be qualified +WARNING: error for foreign table column,{eins},{integer}: column name must be qualified +WARNING: error for foreign table column,{addr_nsp,zwei},{}: relation "addr_nsp" does not exist +WARNING: error for foreign table column,{addr_nsp,zwei},{integer}: relation "addr_nsp" does not exist +WARNING: error for foreign table column,{eins,zwei,drei},{}: schema "eins" does not exist +WARNING: error for foreign table column,{eins,zwei,drei},{integer}: schema "eins" does not exist +WARNING: error for aggregate,{eins},{}: aggregate eins(*) does not exist +WARNING: error for aggregate,{eins},{integer}: aggregate eins(integer) does not exist +WARNING: error for aggregate,{addr_nsp,zwei},{}: aggregate addr_nsp.zwei(*) does not exist +WARNING: error for aggregate,{addr_nsp,zwei},{integer}: aggregate addr_nsp.zwei(integer) does not exist +WARNING: error for aggregate,{eins,zwei,drei},{}: cross-database references are not implemented: eins.zwei.drei +WARNING: error for aggregate,{eins,zwei,drei},{integer}: cross-database references are not implemented: eins.zwei.drei +WARNING: error for function,{eins},{}: function eins() does not exist +WARNING: error for function,{eins},{integer}: function eins(integer) does not exist +WARNING: error for function,{addr_nsp,zwei},{}: function addr_nsp.zwei() does not exist +WARNING: error for function,{addr_nsp,zwei},{integer}: function addr_nsp.zwei(integer) does not exist +WARNING: error for function,{eins,zwei,drei},{}: cross-database references are not implemented: eins.zwei.drei +WARNING: error for function,{eins,zwei,drei},{integer}: cross-database references are not implemented: eins.zwei.drei +WARNING: error for type,{eins},{}: type "eins" does not exist +WARNING: error for type,{eins},{integer}: type "eins" does not exist +WARNING: error for type,{addr_nsp,zwei},{}: name list length must be exactly 1 +WARNING: error for type,{addr_nsp,zwei},{integer}: name list length must be exactly 1 +WARNING: error for type,{eins,zwei,drei},{}: name list length must be exactly 1 +WARNING: error for type,{eins,zwei,drei},{integer}: name list length must be exactly 1 +WARNING: error for cast,{eins},{}: argument list length must be exactly 1 +WARNING: error for cast,{eins},{integer}: type "eins" does not exist +WARNING: error for cast,{addr_nsp,zwei},{}: name list length must be exactly 1 +WARNING: error for cast,{addr_nsp,zwei},{integer}: name list length must be exactly 1 +WARNING: error for cast,{eins,zwei,drei},{}: name list length must be exactly 1 +WARNING: error for cast,{eins,zwei,drei},{integer}: name list length must be exactly 1 +WARNING: error for collation,{eins},{}: collation "eins" for encoding "UTF8" does not exist +WARNING: error for collation,{eins},{integer}: collation "eins" for encoding "UTF8" does not exist +WARNING: error for collation,{addr_nsp,zwei},{}: collation "addr_nsp.zwei" for encoding "UTF8" does not exist +WARNING: error for collation,{addr_nsp,zwei},{integer}: collation "addr_nsp.zwei" for encoding "UTF8" does not exist +WARNING: error for collation,{eins,zwei,drei},{}: cross-database references are not implemented: eins.zwei.drei +WARNING: error for collation,{eins,zwei,drei},{integer}: cross-database references are not implemented: eins.zwei.drei +WARNING: error for table constraint,{eins},{}: must specify relation and object name +WARNING: error for table constraint,{eins},{integer}: must specify relation and object name +WARNING: error for table constraint,{addr_nsp,zwei},{}: relation "addr_nsp" does not exist +WARNING: error for table constraint,{addr_nsp,zwei},{integer}: relation "addr_nsp" does not exist +WARNING: error for table constraint,{eins,zwei,drei},{}: schema "eins" does not exist +WARNING: error for table constraint,{eins,zwei,drei},{integer}: schema "eins" does not exist +WARNING: error for domain constraint,{eins},{}: cache lookup failed for type 0 +WARNING: error for domain constraint,{eins},{integer}: cache lookup failed for type 0 +WARNING: error for domain constraint,{addr_nsp,zwei},{}: type "addr_nsp" does not exist +WARNING: error for domain constraint,{addr_nsp,zwei},{integer}: type "addr_nsp" does not exist +WARNING: error for domain constraint,{eins,zwei,drei},{}: schema "eins" does not exist +WARNING: error for domain constraint,{eins,zwei,drei},{integer}: schema "eins" does not exist +WARNING: error for conversion,{eins},{}: conversion "eins" does not exist +WARNING: error for conversion,{eins},{integer}: conversion "eins" does not exist +WARNING: error for conversion,{addr_nsp,zwei},{}: conversion "addr_nsp.zwei" does not exist +WARNING: error for conversion,{addr_nsp,zwei},{integer}: conversion "addr_nsp.zwei" does not exist +WARNING: error for conversion,{eins,zwei,drei},{}: cross-database references are not implemented: eins.zwei.drei +WARNING: error for conversion,{eins,zwei,drei},{integer}: cross-database references are not implemented: eins.zwei.drei +WARNING: error for default value,{eins},{}: column name must be qualified +WARNING: error for default value,{eins},{integer}: column name must be qualified +WARNING: error for default value,{addr_nsp,zwei},{}: relation "addr_nsp" does not exist +WARNING: error for default value,{addr_nsp,zwei},{integer}: relation "addr_nsp" does not exist +WARNING: error for default value,{eins,zwei,drei},{}: schema "eins" does not exist +WARNING: error for default value,{eins,zwei,drei},{integer}: schema "eins" does not exist +WARNING: error for operator,{eins},{}: argument list length must be exactly 2 +WARNING: error for operator,{eins},{integer}: argument list length must be exactly 2 +WARNING: error for operator,{addr_nsp,zwei},{}: argument list length must be exactly 2 +WARNING: error for operator,{addr_nsp,zwei},{integer}: argument list length must be exactly 2 +WARNING: error for operator,{eins,zwei,drei},{}: argument list length must be exactly 2 +WARNING: error for operator,{eins,zwei,drei},{integer}: argument list length must be exactly 2 +WARNING: error for operator class,{eins},{}: argument list length must be exactly 1 +WARNING: error for operator class,{eins},{integer}: access method "integer" does not exist +WARNING: error for operator class,{addr_nsp,zwei},{}: argument list length must be exactly 1 +WARNING: error for operator class,{addr_nsp,zwei},{integer}: access method "integer" does not exist +WARNING: error for operator class,{eins,zwei,drei},{}: argument list length must be exactly 1 +WARNING: error for operator class,{eins,zwei,drei},{integer}: access method "integer" does not exist +WARNING: error for operator family,{eins},{}: argument list length must be exactly 1 +WARNING: error for operator family,{eins},{integer}: access method "integer" does not exist +WARNING: error for operator family,{addr_nsp,zwei},{}: argument list length must be exactly 1 +WARNING: error for operator family,{addr_nsp,zwei},{integer}: access method "integer" does not exist +WARNING: error for operator family,{eins,zwei,drei},{}: argument list length must be exactly 1 +WARNING: error for operator family,{eins,zwei,drei},{integer}: access method "integer" does not exist +WARNING: error for rule,{eins},{}: rule "eins" does not exist +WARNING: error for rule,{eins},{integer}: rule "eins" does not exist +WARNING: error for rule,{addr_nsp,zwei},{}: relation "addr_nsp" does not exist +WARNING: error for rule,{addr_nsp,zwei},{integer}: relation "addr_nsp" does not exist +WARNING: error for rule,{eins,zwei,drei},{}: schema "eins" does not exist +WARNING: error for rule,{eins,zwei,drei},{integer}: schema "eins" does not exist +WARNING: error for trigger,{eins},{}: must specify relation and object name +WARNING: error for trigger,{eins},{integer}: must specify relation and object name +WARNING: error for trigger,{addr_nsp,zwei},{}: relation "addr_nsp" does not exist +WARNING: error for trigger,{addr_nsp,zwei},{integer}: relation "addr_nsp" does not exist +WARNING: error for trigger,{eins,zwei,drei},{}: schema "eins" does not exist +WARNING: error for trigger,{eins,zwei,drei},{integer}: schema "eins" does not exist +WARNING: error for text search parser,{eins},{}: text search parser "eins" does not exist +WARNING: error for text search parser,{eins},{integer}: text search parser "eins" does not exist +WARNING: error for text search parser,{addr_nsp,zwei},{}: text search parser "addr_nsp.zwei" does not exist +WARNING: error for text search parser,{addr_nsp,zwei},{integer}: text search parser "addr_nsp.zwei" does not exist +WARNING: error for text search parser,{eins,zwei,drei},{}: cross-database references are not implemented: eins.zwei.drei +WARNING: error for text search parser,{eins,zwei,drei},{integer}: cross-database references are not implemented: eins.zwei.drei +WARNING: error for text search dictionary,{eins},{}: text search dictionary "eins" does not exist +WARNING: error for text search dictionary,{eins},{integer}: text search dictionary "eins" does not exist +WARNING: error for text search dictionary,{addr_nsp,zwei},{}: text search dictionary "addr_nsp.zwei" does not exist +WARNING: error for text search dictionary,{addr_nsp,zwei},{integer}: text search dictionary "addr_nsp.zwei" does not exist +WARNING: error for text search dictionary,{eins,zwei,drei},{}: cross-database references are not implemented: eins.zwei.drei +WARNING: error for text search dictionary,{eins,zwei,drei},{integer}: cross-database references are not implemented: eins.zwei.drei +WARNING: error for text search template,{eins},{}: text search template "eins" does not exist +WARNING: error for text search template,{eins},{integer}: text search template "eins" does not exist +WARNING: error for text search template,{addr_nsp,zwei},{}: text search template "addr_nsp.zwei" does not exist +WARNING: error for text search template,{addr_nsp,zwei},{integer}: text search template "addr_nsp.zwei" does not exist +WARNING: error for text search template,{eins,zwei,drei},{}: cross-database references are not implemented: eins.zwei.drei +WARNING: error for text search template,{eins,zwei,drei},{integer}: cross-database references are not implemented: eins.zwei.drei +WARNING: error for text search configuration,{eins},{}: text search configuration "eins" does not exist +WARNING: error for text search configuration,{eins},{integer}: text search configuration "eins" does not exist +WARNING: error for text search configuration,{addr_nsp,zwei},{}: text search configuration "addr_nsp.zwei" does not exist +WARNING: error for text search configuration,{addr_nsp,zwei},{integer}: text search configuration "addr_nsp.zwei" does not exist +WARNING: error for text search configuration,{eins,zwei,drei},{}: cross-database references are not implemented: eins.zwei.drei +WARNING: error for text search configuration,{eins,zwei,drei},{integer}: cross-database references are not implemented: eins.zwei.drei +WARNING: error for policy,{eins},{}: must specify relation and object name +WARNING: error for policy,{eins},{integer}: must specify relation and object name +WARNING: error for policy,{addr_nsp,zwei},{}: relation "addr_nsp" does not exist +WARNING: error for policy,{addr_nsp,zwei},{integer}: relation "addr_nsp" does not exist +WARNING: error for policy,{eins,zwei,drei},{}: schema "eins" does not exist +WARNING: error for policy,{eins,zwei,drei},{integer}: schema "eins" does not exist +-- these object types cannot be qualified names +SELECT pg_get_object_address('language', '{one}', '{}'); +ERROR: language "one" does not exist +SELECT pg_get_object_address('language', '{one,two}', '{}'); +ERROR: language name cannot be qualified +SELECT pg_get_object_address('large object', '{123}', '{}'); +ERROR: large object 123 does not exist +SELECT pg_get_object_address('large object', '{123,456}', '{}'); +ERROR: name list length must be exactly 1 +SELECT pg_get_object_address('large object', '{blargh}', '{}'); +ERROR: invalid input syntax for type oid: "blargh" +SELECT pg_get_object_address('schema', '{one}', '{}'); +ERROR: schema "one" does not exist +SELECT pg_get_object_address('schema', '{one,two}', '{}'); +ERROR: schema name cannot be qualified +SELECT pg_get_object_address('role', '{one}', '{}'); +ERROR: role "one" does not exist +SELECT pg_get_object_address('role', '{one,two}', '{}'); +ERROR: role name cannot be qualified +SELECT pg_get_object_address('database', '{one}', '{}'); +ERROR: database "one" does not exist +SELECT pg_get_object_address('database', '{one,two}', '{}'); +ERROR: database name cannot be qualified +SELECT pg_get_object_address('tablespace', '{one}', '{}'); +ERROR: tablespace "one" does not exist +SELECT pg_get_object_address('tablespace', '{one,two}', '{}'); +ERROR: tablespace name cannot be qualified +SELECT pg_get_object_address('foreign-data wrapper', '{one}', '{}'); +ERROR: foreign-data wrapper "one" does not exist +SELECT pg_get_object_address('foreign-data wrapper', '{one,two}', '{}'); +ERROR: foreign-data wrapper name cannot be qualified +SELECT pg_get_object_address('server', '{one}', '{}'); +ERROR: server "one" does not exist +SELECT pg_get_object_address('server', '{one,two}', '{}'); +ERROR: server name cannot be qualified +SELECT pg_get_object_address('extension', '{one}', '{}'); +ERROR: extension "one" does not exist +SELECT pg_get_object_address('extension', '{one,two}', '{}'); +ERROR: extension name cannot be qualified +SELECT pg_get_object_address('event trigger', '{one}', '{}'); +ERROR: event trigger "one" does not exist +SELECT pg_get_object_address('event trigger', '{one,two}', '{}'); +ERROR: event trigger name cannot be qualified +-- test successful cases +WITH objects (type, name, args) AS (VALUES + ('table', '{addr_nsp, gentable}'::text[], '{}'::text[]), + ('index', '{addr_nsp, gentable_pkey}', '{}'), + ('sequence', '{addr_nsp, gentable_a_seq}', '{}'), + -- toast table + ('view', '{addr_nsp, genview}', '{}'), + ('materialized view', '{addr_nsp, genmatview}', '{}'), + ('foreign table', '{addr_nsp, genftable}', '{}'), + ('table column', '{addr_nsp, gentable, b}', '{}'), + ('foreign table column', '{addr_nsp, genftable, a}', '{}'), + ('aggregate', '{addr_nsp, genaggr}', '{int4}'), + ('function', '{pg_catalog, pg_identify_object}', '{pg_catalog.oid, pg_catalog.oid, int4}'), + ('type', '{pg_catalog._int4}', '{}'), + ('type', '{addr_nsp.gendomain}', '{}'), + ('type', '{addr_nsp.gencomptype}', '{}'), + ('type', '{addr_nsp.genenum}', '{}'), + ('cast', '{int8}', '{int4}'), + ('collation', '{default}', '{}'), + ('table constraint', '{addr_nsp, gentable, a_chk}', '{}'), + ('domain constraint', '{addr_nsp, gendomain, domconstr}', '{}'), + ('conversion', '{pg_catalog, ascii_to_mic}', '{}'), + ('default value', '{addr_nsp, gentable, b}', '{}'), + ('language', '{plpgsql}', '{}'), + -- large object + ('operator', '{+}', '{int4, int4}'), + ('operator class', '{int4_ops}', '{btree}'), + ('operator family', '{integer_ops}', '{btree}'), + -- operator of access method + -- function of access method + ('rule', '{addr_nsp, genview, _RETURN}', '{}'), + ('trigger', '{addr_nsp, gentable, t}', '{}'), + ('schema', '{addr_nsp}', '{}'), + ('text search parser', '{addr_ts_prs}', '{}'), + ('text search dictionary', '{addr_ts_dict}', '{}'), + ('text search template', '{addr_ts_temp}', '{}'), + ('text search configuration', '{addr_ts_conf}', '{}'), + ('role', '{regtest_addr_user}', '{}'), + -- database + -- tablespace + ('foreign-data wrapper', '{addr_fdw}', '{}'), + ('server', '{addr_fserv}', '{}'), + -- user mapping + -- extension + ('event trigger', '{evttrig}', '{}'), + ('policy', '{addr_nsp, gentable, genpol}', '{}') + ) +SELECT (pg_identify_object(classid, objid, subobjid)).* + FROM objects, pg_get_object_address(type, name, args) +ORDER BY classid, objid; + type | schema | name | identity +---------------------------+------------+-------------------+---------------------------------------------------------------------- + type | pg_catalog | _int4 | integer[] + type | addr_nsp | gencomptype | addr_nsp.gencomptype + type | addr_nsp | genenum | addr_nsp.genenum + type | addr_nsp | gendomain | addr_nsp.gendomain + function | pg_catalog | | pg_catalog.pg_identify_object(pg_catalog.oid,pg_catalog.oid,integer) + aggregate | addr_nsp | | addr_nsp.genaggr(integer) + sequence | addr_nsp | gentable_a_seq | addr_nsp.gentable_a_seq + table | addr_nsp | gentable | addr_nsp.gentable + table column | addr_nsp | gentable | addr_nsp.gentable.b + index | addr_nsp | gentable_pkey | addr_nsp.gentable_pkey + view | addr_nsp | genview | addr_nsp.genview + materialized view | addr_nsp | genmatview | addr_nsp.genmatview + foreign table column | addr_nsp | genftable | addr_nsp.genftable.a + foreign table | addr_nsp | genftable | addr_nsp.genftable + role | | regtest_addr_user | regtest_addr_user + server | | addr_fserv | addr_fserv + foreign-data wrapper | | addr_fdw | addr_fdw + default value | | | for addr_nsp.gentable.b + cast | | | (bigint AS integer) + table constraint | addr_nsp | | a_chk on addr_nsp.gentable + domain constraint | addr_nsp | | domconstr on addr_nsp.gendomain + conversion | pg_catalog | ascii_to_mic | ascii_to_mic + language | | plpgsql | plpgsql + schema | | addr_nsp | addr_nsp + operator class | pg_catalog | int4_ops | pg_catalog.int4_ops for btree + operator | pg_catalog | | pg_catalog.+(integer,integer) + rule | | | "_RETURN" on addr_nsp.genview + trigger | | | t on addr_nsp.gentable + operator family | pg_catalog | integer_ops | pg_catalog.integer_ops for btree + policy | | | genpol on addr_nsp.gentable + collation | pg_catalog | "default" | pg_catalog."default" + event trigger | | evttrig | evttrig + text search dictionary | addr_nsp | addr_ts_dict | addr_nsp.addr_ts_dict + text search parser | addr_nsp | addr_ts_prs | addr_nsp.addr_ts_prs + text search configuration | addr_nsp | addr_ts_conf | addr_nsp.addr_ts_conf + text search template | addr_nsp | addr_ts_temp | addr_nsp.addr_ts_temp +(36 rows) + +--- +--- Cleanup resources +--- +DROP FOREIGN DATA WRAPPER addr_fdw CASCADE; +DROP SCHEMA addr_nsp CASCADE; +DROP USER regtest_addr_user; diff --git a/src/test/regress/parallel_schedule b/src/test/regress/parallel_schedule index 62cc198564..e0ae2f2b01 100644 --- a/src/test/regress/parallel_schedule +++ b/src/test/regress/parallel_schedule @@ -83,7 +83,7 @@ test: select_into select_distinct select_distinct_on select_implicit select_havi # ---------- # Another group of parallel tests # ---------- -test: brin gin gist spgist privileges security_label collate matview lock replica_identity rowsecurity +test: brin gin gist spgist privileges security_label collate matview lock replica_identity rowsecurity object_address # ---------- # Another group of parallel tests diff --git a/src/test/regress/serial_schedule b/src/test/regress/serial_schedule index 07fc827329..7f762bd08f 100644 --- a/src/test/regress/serial_schedule +++ b/src/test/regress/serial_schedule @@ -106,6 +106,7 @@ test: matview test: lock test: replica_identity test: rowsecurity +test: object_address test: alter_generic test: misc test: psql diff --git a/src/test/regress/sql/object_address.sql b/src/test/regress/sql/object_address.sql new file mode 100644 index 0000000000..bd4c7c5016 --- /dev/null +++ b/src/test/regress/sql/object_address.sql @@ -0,0 +1,177 @@ +-- +-- Test for pg_get_object_address +-- + +-- Clean up in case a prior regression run failed +SET client_min_messages TO 'warning'; + +DROP ROLE IF EXISTS regtest_addr_user; + +CREATE USER regtest_addr_user; + +-- Test generic object addressing/identification functions +CREATE SCHEMA addr_nsp; +SET search_path TO 'addr_nsp'; +CREATE FOREIGN DATA WRAPPER addr_fdw; +CREATE SERVER addr_fserv FOREIGN DATA WRAPPER addr_fdw; +CREATE TEXT SEARCH DICTIONARY addr_ts_dict (template=simple); +CREATE TEXT SEARCH CONFIGURATION addr_ts_conf (copy=english); +CREATE TEXT SEARCH TEMPLATE addr_ts_temp (lexize=dsimple_lexize); +CREATE TEXT SEARCH PARSER addr_ts_prs + (start = prsd_start, gettoken = prsd_nexttoken, end = prsd_end, lextypes = prsd_lextype); +CREATE TABLE addr_nsp.gentable ( + a serial primary key CONSTRAINT a_chk CHECK (a > 0), + b text DEFAULT 'hello'); +CREATE VIEW addr_nsp.genview AS SELECT * from addr_nsp.gentable; +CREATE MATERIALIZED VIEW addr_nsp.genmatview AS SELECT * FROM addr_nsp.gentable; +CREATE TYPE addr_nsp.gencomptype AS (a int); +CREATE TYPE addr_nsp.genenum AS ENUM ('one', 'two'); +CREATE FOREIGN TABLE addr_nsp.genftable (a int) SERVER addr_fserv; +CREATE AGGREGATE addr_nsp.genaggr(int4) (sfunc = int4pl, stype = int4); +CREATE DOMAIN addr_nsp.gendomain AS int4 CONSTRAINT domconstr CHECK (value > 0); +CREATE FUNCTION addr_nsp.trig() RETURNS TRIGGER LANGUAGE plpgsql AS $$ BEGIN END; $$; +CREATE TRIGGER t BEFORE INSERT ON addr_nsp.gentable FOR EACH ROW EXECUTE PROCEDURE addr_nsp.trig(); +CREATE POLICY genpol ON addr_nsp.gentable; + +CREATE FUNCTION addr_nsp.etrig() RETURNS EVENT_TRIGGER LANGUAGE plpgsql AS $$ BEGIN END; $$; +CREATE EVENT TRIGGER evttrig ON ddl_command_end EXECUTE PROCEDURE addr_nsp.etrig(); + +-- test some error cases +SELECT pg_get_object_address('stone', '{}', '{}'); +SELECT pg_get_object_address('table', '{}', '{}'); +SELECT pg_get_object_address('table', '{NULL}', '{}'); + +-- unrecognized object types +DO $$ +DECLARE + objtype text; +BEGIN + FOR objtype IN VALUES ('toast table'), ('index column'), ('sequence column'), + ('toast table column'), ('view column'), ('materialized view column'), + ('operator of access method'), ('function of access method'), + ('user mapping') + LOOP + BEGIN + PERFORM pg_get_object_address(objtype, '{one}', '{}'); + EXCEPTION WHEN invalid_parameter_value THEN + RAISE WARNING 'error for %: %', objtype, sqlerrm; + END; + END LOOP; +END; +$$; + +DO $$ +DECLARE + objtype text; + names text[]; + args text[]; +BEGIN + FOR objtype IN VALUES + ('table'), ('index'), ('sequence'), ('view'), + ('materialized view'), ('foreign table'), + ('table column'), ('foreign table column'), + ('aggregate'), ('function'), ('type'), ('cast'), + ('collation'), + ('table constraint'), ('domain constraint'), ('conversion'), ('default value'), + ('operator'), ('operator class'), ('operator family'), ('rule'), ('trigger'), + ('text search parser'), ('text search dictionary'), + ('text search template'), ('text search configuration'), + ('policy') + LOOP + FOR names IN VALUES ('{eins}'), ('{addr_nsp, zwei}'), ('{eins, zwei, drei}') + LOOP + FOR args IN VALUES ('{}'), ('{integer}') + LOOP + BEGIN + PERFORM pg_get_object_address(objtype, names, args); + EXCEPTION WHEN OTHERS THEN + RAISE WARNING 'error for %,%,%: %', objtype, names, args, sqlerrm; + END; + END LOOP; + END LOOP; + END LOOP; +END; +$$; + +-- these object types cannot be qualified names +SELECT pg_get_object_address('language', '{one}', '{}'); +SELECT pg_get_object_address('language', '{one,two}', '{}'); +SELECT pg_get_object_address('large object', '{123}', '{}'); +SELECT pg_get_object_address('large object', '{123,456}', '{}'); +SELECT pg_get_object_address('large object', '{blargh}', '{}'); +SELECT pg_get_object_address('schema', '{one}', '{}'); +SELECT pg_get_object_address('schema', '{one,two}', '{}'); +SELECT pg_get_object_address('role', '{one}', '{}'); +SELECT pg_get_object_address('role', '{one,two}', '{}'); +SELECT pg_get_object_address('database', '{one}', '{}'); +SELECT pg_get_object_address('database', '{one,two}', '{}'); +SELECT pg_get_object_address('tablespace', '{one}', '{}'); +SELECT pg_get_object_address('tablespace', '{one,two}', '{}'); +SELECT pg_get_object_address('foreign-data wrapper', '{one}', '{}'); +SELECT pg_get_object_address('foreign-data wrapper', '{one,two}', '{}'); +SELECT pg_get_object_address('server', '{one}', '{}'); +SELECT pg_get_object_address('server', '{one,two}', '{}'); +SELECT pg_get_object_address('extension', '{one}', '{}'); +SELECT pg_get_object_address('extension', '{one,two}', '{}'); +SELECT pg_get_object_address('event trigger', '{one}', '{}'); +SELECT pg_get_object_address('event trigger', '{one,two}', '{}'); + +-- test successful cases +WITH objects (type, name, args) AS (VALUES + ('table', '{addr_nsp, gentable}'::text[], '{}'::text[]), + ('index', '{addr_nsp, gentable_pkey}', '{}'), + ('sequence', '{addr_nsp, gentable_a_seq}', '{}'), + -- toast table + ('view', '{addr_nsp, genview}', '{}'), + ('materialized view', '{addr_nsp, genmatview}', '{}'), + ('foreign table', '{addr_nsp, genftable}', '{}'), + ('table column', '{addr_nsp, gentable, b}', '{}'), + ('foreign table column', '{addr_nsp, genftable, a}', '{}'), + ('aggregate', '{addr_nsp, genaggr}', '{int4}'), + ('function', '{pg_catalog, pg_identify_object}', '{pg_catalog.oid, pg_catalog.oid, int4}'), + ('type', '{pg_catalog._int4}', '{}'), + ('type', '{addr_nsp.gendomain}', '{}'), + ('type', '{addr_nsp.gencomptype}', '{}'), + ('type', '{addr_nsp.genenum}', '{}'), + ('cast', '{int8}', '{int4}'), + ('collation', '{default}', '{}'), + ('table constraint', '{addr_nsp, gentable, a_chk}', '{}'), + ('domain constraint', '{addr_nsp, gendomain, domconstr}', '{}'), + ('conversion', '{pg_catalog, ascii_to_mic}', '{}'), + ('default value', '{addr_nsp, gentable, b}', '{}'), + ('language', '{plpgsql}', '{}'), + -- large object + ('operator', '{+}', '{int4, int4}'), + ('operator class', '{int4_ops}', '{btree}'), + ('operator family', '{integer_ops}', '{btree}'), + -- operator of access method + -- function of access method + ('rule', '{addr_nsp, genview, _RETURN}', '{}'), + ('trigger', '{addr_nsp, gentable, t}', '{}'), + ('schema', '{addr_nsp}', '{}'), + ('text search parser', '{addr_ts_prs}', '{}'), + ('text search dictionary', '{addr_ts_dict}', '{}'), + ('text search template', '{addr_ts_temp}', '{}'), + ('text search configuration', '{addr_ts_conf}', '{}'), + ('role', '{regtest_addr_user}', '{}'), + -- database + -- tablespace + ('foreign-data wrapper', '{addr_fdw}', '{}'), + ('server', '{addr_fserv}', '{}'), + -- user mapping + -- extension + ('event trigger', '{evttrig}', '{}'), + ('policy', '{addr_nsp, gentable, genpol}', '{}') + ) +SELECT (pg_identify_object(classid, objid, subobjid)).* + FROM objects, pg_get_object_address(type, name, args) +ORDER BY classid, objid; + +--- +--- Cleanup resources +--- +DROP FOREIGN DATA WRAPPER addr_fdw CASCADE; + +DROP SCHEMA addr_nsp CASCADE; + +DROP USER regtest_addr_user;