summaryrefslogtreecommitdiff
path: root/src/backend
diff options
context:
space:
mode:
Diffstat (limited to 'src/backend')
-rw-r--r--src/backend/catalog/objectaddress.c440
-rw-r--r--src/backend/commands/event_trigger.c1
-rw-r--r--src/backend/parser/parse_type.c46
3 files changed, 471 insertions, 16 deletions
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:
@@ -1097,6 +1206,88 @@ get_object_address_attribute(ObjectType objtype, List *objname,
}
/*
+ * 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
*/
static ObjectAddress
@@ -1177,6 +1368,225 @@ get_object_address_opcf(ObjectType objtype,
}
/*
+ * 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.
*/
void
@@ -1388,6 +1798,34 @@ get_object_namespace(const ObjectAddress *address)
}
/*
+ * 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
*/
Oid
@@ -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)));
}