summaryrefslogtreecommitdiffstats
path: root/src/backend/catalog/objectaddress.c
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-05-04 12:17:33 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-05-04 12:17:33 +0000
commit5e45211a64149b3c659b90ff2de6fa982a5a93ed (patch)
tree739caf8c461053357daa9f162bef34516c7bf452 /src/backend/catalog/objectaddress.c
parentInitial commit. (diff)
downloadpostgresql-15-5e45211a64149b3c659b90ff2de6fa982a5a93ed.tar.xz
postgresql-15-5e45211a64149b3c659b90ff2de6fa982a5a93ed.zip
Adding upstream version 15.5.upstream/15.5
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'src/backend/catalog/objectaddress.c')
-rw-r--r--src/backend/catalog/objectaddress.c6102
1 files changed, 6102 insertions, 0 deletions
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
new file mode 100644
index 0000000..27616ac
--- /dev/null
+++ b/src/backend/catalog/objectaddress.c
@@ -0,0 +1,6102 @@
+/*-------------------------------------------------------------------------
+ *
+ * objectaddress.c
+ * functions for working with ObjectAddresses
+ *
+ * Portions Copyright (c) 1996-2022, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ * src/backend/catalog/objectaddress.c
+ *
+ *-------------------------------------------------------------------------
+ */
+
+#include "postgres.h"
+
+#include "access/genam.h"
+#include "access/htup_details.h"
+#include "access/relation.h"
+#include "access/sysattr.h"
+#include "access/table.h"
+#include "catalog/catalog.h"
+#include "catalog/objectaddress.h"
+#include "catalog/pg_am.h"
+#include "catalog/pg_amop.h"
+#include "catalog/pg_amproc.h"
+#include "catalog/pg_attrdef.h"
+#include "catalog/pg_authid.h"
+#include "catalog/pg_cast.h"
+#include "catalog/pg_collation.h"
+#include "catalog/pg_constraint.h"
+#include "catalog/pg_conversion.h"
+#include "catalog/pg_database.h"
+#include "catalog/pg_default_acl.h"
+#include "catalog/pg_enum.h"
+#include "catalog/pg_event_trigger.h"
+#include "catalog/pg_extension.h"
+#include "catalog/pg_foreign_data_wrapper.h"
+#include "catalog/pg_foreign_server.h"
+#include "catalog/pg_language.h"
+#include "catalog/pg_largeobject.h"
+#include "catalog/pg_largeobject_metadata.h"
+#include "catalog/pg_namespace.h"
+#include "catalog/pg_opclass.h"
+#include "catalog/pg_operator.h"
+#include "catalog/pg_opfamily.h"
+#include "catalog/pg_parameter_acl.h"
+#include "catalog/pg_policy.h"
+#include "catalog/pg_proc.h"
+#include "catalog/pg_publication.h"
+#include "catalog/pg_publication_namespace.h"
+#include "catalog/pg_publication_rel.h"
+#include "catalog/pg_rewrite.h"
+#include "catalog/pg_statistic_ext.h"
+#include "catalog/pg_subscription.h"
+#include "catalog/pg_tablespace.h"
+#include "catalog/pg_transform.h"
+#include "catalog/pg_trigger.h"
+#include "catalog/pg_ts_config.h"
+#include "catalog/pg_ts_dict.h"
+#include "catalog/pg_ts_parser.h"
+#include "catalog/pg_ts_template.h"
+#include "catalog/pg_type.h"
+#include "catalog/pg_user_mapping.h"
+#include "commands/dbcommands.h"
+#include "commands/defrem.h"
+#include "commands/event_trigger.h"
+#include "commands/extension.h"
+#include "commands/policy.h"
+#include "commands/proclang.h"
+#include "commands/tablespace.h"
+#include "commands/trigger.h"
+#include "foreign/foreign.h"
+#include "funcapi.h"
+#include "miscadmin.h"
+#include "nodes/makefuncs.h"
+#include "parser/parse_func.h"
+#include "parser/parse_oper.h"
+#include "parser/parse_type.h"
+#include "rewrite/rewriteSupport.h"
+#include "storage/large_object.h"
+#include "storage/lmgr.h"
+#include "storage/sinval.h"
+#include "utils/acl.h"
+#include "utils/builtins.h"
+#include "utils/fmgroids.h"
+#include "utils/lsyscache.h"
+#include "utils/memutils.h"
+#include "utils/regproc.h"
+#include "utils/syscache.h"
+
+/*
+ * ObjectProperty
+ *
+ * This array provides a common part of system object structure; to help
+ * consolidate routines to handle various kind of object classes.
+ */
+typedef struct
+{
+ const char *class_descr; /* string describing the catalog, for internal
+ * error messages */
+ Oid class_oid; /* oid of catalog */
+ Oid oid_index_oid; /* oid of index on system oid column */
+ int oid_catcache_id; /* id of catcache on system oid column */
+ int name_catcache_id; /* id of catcache on (name,namespace), or
+ * (name) if the object does not live in a
+ * namespace */
+ AttrNumber attnum_oid; /* attribute number of oid column */
+ AttrNumber attnum_name; /* attnum of name field */
+ AttrNumber attnum_namespace; /* attnum of namespace field */
+ AttrNumber attnum_owner; /* attnum of owner field */
+ AttrNumber attnum_acl; /* attnum of acl field */
+ ObjectType objtype; /* OBJECT_* of this object type */
+ bool is_nsp_name_unique; /* can the nsp/name combination (or name
+ * alone, if there's no namespace) be
+ * considered a unique identifier for an
+ * object of this class? */
+} ObjectPropertyType;
+
+static const ObjectPropertyType ObjectProperty[] =
+{
+ {
+ "access method",
+ AccessMethodRelationId,
+ AmOidIndexId,
+ AMOID,
+ AMNAME,
+ Anum_pg_am_oid,
+ Anum_pg_am_amname,
+ InvalidAttrNumber,
+ InvalidAttrNumber,
+ InvalidAttrNumber,
+ -1,
+ true
+ },
+ {
+ "access method operator",
+ AccessMethodOperatorRelationId,
+ AccessMethodOperatorOidIndexId,
+ -1,
+ -1,
+ Anum_pg_amop_oid,
+ InvalidAttrNumber,
+ InvalidAttrNumber,
+ InvalidAttrNumber,
+ InvalidAttrNumber,
+ OBJECT_AMOP,
+ false
+ },
+ {
+ "access method procedure",
+ AccessMethodProcedureRelationId,
+ AccessMethodProcedureOidIndexId,
+ -1,
+ -1,
+ Anum_pg_amproc_oid,
+ InvalidAttrNumber,
+ InvalidAttrNumber,
+ InvalidAttrNumber,
+ InvalidAttrNumber,
+ OBJECT_AMPROC,
+ false
+ },
+ {
+ "cast",
+ CastRelationId,
+ CastOidIndexId,
+ -1,
+ -1,
+ Anum_pg_cast_oid,
+ InvalidAttrNumber,
+ InvalidAttrNumber,
+ InvalidAttrNumber,
+ InvalidAttrNumber,
+ -1,
+ false
+ },
+ {
+ "collation",
+ CollationRelationId,
+ CollationOidIndexId,
+ COLLOID,
+ -1, /* COLLNAMEENCNSP also takes encoding */
+ Anum_pg_collation_oid,
+ Anum_pg_collation_collname,
+ Anum_pg_collation_collnamespace,
+ Anum_pg_collation_collowner,
+ InvalidAttrNumber,
+ OBJECT_COLLATION,
+ true
+ },
+ {
+ "constraint",
+ ConstraintRelationId,
+ ConstraintOidIndexId,
+ CONSTROID,
+ -1,
+ Anum_pg_constraint_oid,
+ Anum_pg_constraint_conname,
+ Anum_pg_constraint_connamespace,
+ InvalidAttrNumber,
+ InvalidAttrNumber,
+ -1,
+ false
+ },
+ {
+ "conversion",
+ ConversionRelationId,
+ ConversionOidIndexId,
+ CONVOID,
+ CONNAMENSP,
+ Anum_pg_conversion_oid,
+ Anum_pg_conversion_conname,
+ Anum_pg_conversion_connamespace,
+ Anum_pg_conversion_conowner,
+ InvalidAttrNumber,
+ OBJECT_CONVERSION,
+ true
+ },
+ {
+ "database",
+ DatabaseRelationId,
+ DatabaseOidIndexId,
+ DATABASEOID,
+ -1,
+ Anum_pg_database_oid,
+ Anum_pg_database_datname,
+ InvalidAttrNumber,
+ Anum_pg_database_datdba,
+ Anum_pg_database_datacl,
+ OBJECT_DATABASE,
+ true
+ },
+ {
+ "default ACL",
+ DefaultAclRelationId,
+ DefaultAclOidIndexId,
+ -1,
+ -1,
+ Anum_pg_default_acl_oid,
+ InvalidAttrNumber,
+ InvalidAttrNumber,
+ InvalidAttrNumber,
+ InvalidAttrNumber,
+ OBJECT_DEFACL,
+ false
+ },
+ {
+ "extension",
+ ExtensionRelationId,
+ ExtensionOidIndexId,
+ -1,
+ -1,
+ Anum_pg_extension_oid,
+ Anum_pg_extension_extname,
+ InvalidAttrNumber, /* extension doesn't belong to extnamespace */
+ Anum_pg_extension_extowner,
+ InvalidAttrNumber,
+ OBJECT_EXTENSION,
+ true
+ },
+ {
+ "foreign-data wrapper",
+ ForeignDataWrapperRelationId,
+ ForeignDataWrapperOidIndexId,
+ FOREIGNDATAWRAPPEROID,
+ FOREIGNDATAWRAPPERNAME,
+ Anum_pg_foreign_data_wrapper_oid,
+ Anum_pg_foreign_data_wrapper_fdwname,
+ InvalidAttrNumber,
+ Anum_pg_foreign_data_wrapper_fdwowner,
+ Anum_pg_foreign_data_wrapper_fdwacl,
+ OBJECT_FDW,
+ true
+ },
+ {
+ "foreign server",
+ ForeignServerRelationId,
+ ForeignServerOidIndexId,
+ FOREIGNSERVEROID,
+ FOREIGNSERVERNAME,
+ Anum_pg_foreign_server_oid,
+ Anum_pg_foreign_server_srvname,
+ InvalidAttrNumber,
+ Anum_pg_foreign_server_srvowner,
+ Anum_pg_foreign_server_srvacl,
+ OBJECT_FOREIGN_SERVER,
+ true
+ },
+ {
+ "function",
+ ProcedureRelationId,
+ ProcedureOidIndexId,
+ PROCOID,
+ -1, /* PROCNAMEARGSNSP also takes argument types */
+ Anum_pg_proc_oid,
+ Anum_pg_proc_proname,
+ Anum_pg_proc_pronamespace,
+ Anum_pg_proc_proowner,
+ Anum_pg_proc_proacl,
+ OBJECT_FUNCTION,
+ false
+ },
+ {
+ "language",
+ LanguageRelationId,
+ LanguageOidIndexId,
+ LANGOID,
+ LANGNAME,
+ Anum_pg_language_oid,
+ Anum_pg_language_lanname,
+ InvalidAttrNumber,
+ Anum_pg_language_lanowner,
+ Anum_pg_language_lanacl,
+ OBJECT_LANGUAGE,
+ true
+ },
+ {
+ "large object metadata",
+ LargeObjectMetadataRelationId,
+ LargeObjectMetadataOidIndexId,
+ -1,
+ -1,
+ Anum_pg_largeobject_metadata_oid,
+ InvalidAttrNumber,
+ InvalidAttrNumber,
+ Anum_pg_largeobject_metadata_lomowner,
+ Anum_pg_largeobject_metadata_lomacl,
+ OBJECT_LARGEOBJECT,
+ false
+ },
+ {
+ "operator class",
+ OperatorClassRelationId,
+ OpclassOidIndexId,
+ CLAOID,
+ -1, /* CLAAMNAMENSP also takes opcmethod */
+ Anum_pg_opclass_oid,
+ Anum_pg_opclass_opcname,
+ Anum_pg_opclass_opcnamespace,
+ Anum_pg_opclass_opcowner,
+ InvalidAttrNumber,
+ OBJECT_OPCLASS,
+ true
+ },
+ {
+ "operator",
+ OperatorRelationId,
+ OperatorOidIndexId,
+ OPEROID,
+ -1, /* OPERNAMENSP also takes left and right type */
+ Anum_pg_operator_oid,
+ Anum_pg_operator_oprname,
+ Anum_pg_operator_oprnamespace,
+ Anum_pg_operator_oprowner,
+ InvalidAttrNumber,
+ OBJECT_OPERATOR,
+ false
+ },
+ {
+ "operator family",
+ OperatorFamilyRelationId,
+ OpfamilyOidIndexId,
+ OPFAMILYOID,
+ -1, /* OPFAMILYAMNAMENSP also takes opfmethod */
+ Anum_pg_opfamily_oid,
+ Anum_pg_opfamily_opfname,
+ Anum_pg_opfamily_opfnamespace,
+ Anum_pg_opfamily_opfowner,
+ InvalidAttrNumber,
+ OBJECT_OPFAMILY,
+ true
+ },
+ {
+ "role",
+ AuthIdRelationId,
+ AuthIdOidIndexId,
+ AUTHOID,
+ AUTHNAME,
+ Anum_pg_authid_oid,
+ Anum_pg_authid_rolname,
+ InvalidAttrNumber,
+ InvalidAttrNumber,
+ InvalidAttrNumber,
+ -1,
+ true
+ },
+ {
+ "rule",
+ RewriteRelationId,
+ RewriteOidIndexId,
+ -1,
+ -1,
+ Anum_pg_rewrite_oid,
+ Anum_pg_rewrite_rulename,
+ InvalidAttrNumber,
+ InvalidAttrNumber,
+ InvalidAttrNumber,
+ -1,
+ false
+ },
+ {
+ "schema",
+ NamespaceRelationId,
+ NamespaceOidIndexId,
+ NAMESPACEOID,
+ NAMESPACENAME,
+ Anum_pg_namespace_oid,
+ Anum_pg_namespace_nspname,
+ InvalidAttrNumber,
+ Anum_pg_namespace_nspowner,
+ Anum_pg_namespace_nspacl,
+ OBJECT_SCHEMA,
+ true
+ },
+ {
+ "relation",
+ RelationRelationId,
+ ClassOidIndexId,
+ RELOID,
+ RELNAMENSP,
+ Anum_pg_class_oid,
+ Anum_pg_class_relname,
+ Anum_pg_class_relnamespace,
+ Anum_pg_class_relowner,
+ Anum_pg_class_relacl,
+ OBJECT_TABLE,
+ true
+ },
+ {
+ "tablespace",
+ TableSpaceRelationId,
+ TablespaceOidIndexId,
+ TABLESPACEOID,
+ -1,
+ Anum_pg_tablespace_oid,
+ Anum_pg_tablespace_spcname,
+ InvalidAttrNumber,
+ Anum_pg_tablespace_spcowner,
+ Anum_pg_tablespace_spcacl,
+ OBJECT_TABLESPACE,
+ true
+ },
+ {
+ "transform",
+ TransformRelationId,
+ TransformOidIndexId,
+ TRFOID,
+ InvalidAttrNumber,
+ Anum_pg_transform_oid
+ },
+ {
+ "trigger",
+ TriggerRelationId,
+ TriggerOidIndexId,
+ -1,
+ -1,
+ Anum_pg_trigger_oid,
+ Anum_pg_trigger_tgname,
+ InvalidAttrNumber,
+ InvalidAttrNumber,
+ InvalidAttrNumber,
+ -1,
+ false
+ },
+ {
+ "policy",
+ PolicyRelationId,
+ PolicyOidIndexId,
+ -1,
+ -1,
+ Anum_pg_policy_oid,
+ Anum_pg_policy_polname,
+ InvalidAttrNumber,
+ InvalidAttrNumber,
+ InvalidAttrNumber,
+ -1,
+ false
+ },
+ {
+ "event trigger",
+ EventTriggerRelationId,
+ EventTriggerOidIndexId,
+ EVENTTRIGGEROID,
+ EVENTTRIGGERNAME,
+ Anum_pg_event_trigger_oid,
+ Anum_pg_event_trigger_evtname,
+ InvalidAttrNumber,
+ Anum_pg_event_trigger_evtowner,
+ InvalidAttrNumber,
+ OBJECT_EVENT_TRIGGER,
+ true
+ },
+ {
+ "text search configuration",
+ TSConfigRelationId,
+ TSConfigOidIndexId,
+ TSCONFIGOID,
+ TSCONFIGNAMENSP,
+ Anum_pg_ts_config_oid,
+ Anum_pg_ts_config_cfgname,
+ Anum_pg_ts_config_cfgnamespace,
+ Anum_pg_ts_config_cfgowner,
+ InvalidAttrNumber,
+ OBJECT_TSCONFIGURATION,
+ true
+ },
+ {
+ "text search dictionary",
+ TSDictionaryRelationId,
+ TSDictionaryOidIndexId,
+ TSDICTOID,
+ TSDICTNAMENSP,
+ Anum_pg_ts_dict_oid,
+ Anum_pg_ts_dict_dictname,
+ Anum_pg_ts_dict_dictnamespace,
+ Anum_pg_ts_dict_dictowner,
+ InvalidAttrNumber,
+ OBJECT_TSDICTIONARY,
+ true
+ },
+ {
+ "text search parser",
+ TSParserRelationId,
+ TSParserOidIndexId,
+ TSPARSEROID,
+ TSPARSERNAMENSP,
+ Anum_pg_ts_parser_oid,
+ Anum_pg_ts_parser_prsname,
+ Anum_pg_ts_parser_prsnamespace,
+ InvalidAttrNumber,
+ InvalidAttrNumber,
+ -1,
+ true
+ },
+ {
+ "text search template",
+ TSTemplateRelationId,
+ TSTemplateOidIndexId,
+ TSTEMPLATEOID,
+ TSTEMPLATENAMENSP,
+ Anum_pg_ts_template_oid,
+ Anum_pg_ts_template_tmplname,
+ Anum_pg_ts_template_tmplnamespace,
+ InvalidAttrNumber,
+ InvalidAttrNumber,
+ -1,
+ true,
+ },
+ {
+ "type",
+ TypeRelationId,
+ TypeOidIndexId,
+ TYPEOID,
+ TYPENAMENSP,
+ Anum_pg_type_oid,
+ Anum_pg_type_typname,
+ Anum_pg_type_typnamespace,
+ Anum_pg_type_typowner,
+ Anum_pg_type_typacl,
+ OBJECT_TYPE,
+ true
+ },
+ {
+ "publication",
+ PublicationRelationId,
+ PublicationObjectIndexId,
+ PUBLICATIONOID,
+ PUBLICATIONNAME,
+ Anum_pg_publication_oid,
+ Anum_pg_publication_pubname,
+ InvalidAttrNumber,
+ Anum_pg_publication_pubowner,
+ InvalidAttrNumber,
+ OBJECT_PUBLICATION,
+ true
+ },
+ {
+ "subscription",
+ SubscriptionRelationId,
+ SubscriptionObjectIndexId,
+ SUBSCRIPTIONOID,
+ SUBSCRIPTIONNAME,
+ Anum_pg_subscription_oid,
+ Anum_pg_subscription_subname,
+ InvalidAttrNumber,
+ Anum_pg_subscription_subowner,
+ InvalidAttrNumber,
+ OBJECT_SUBSCRIPTION,
+ true
+ },
+ {
+ "extended statistics",
+ StatisticExtRelationId,
+ StatisticExtOidIndexId,
+ STATEXTOID,
+ STATEXTNAMENSP,
+ Anum_pg_statistic_ext_oid,
+ Anum_pg_statistic_ext_stxname,
+ Anum_pg_statistic_ext_stxnamespace,
+ Anum_pg_statistic_ext_stxowner,
+ InvalidAttrNumber, /* no ACL (same as relation) */
+ OBJECT_STATISTIC_EXT,
+ true
+ },
+ {
+ "user mapping",
+ UserMappingRelationId,
+ UserMappingOidIndexId,
+ USERMAPPINGOID,
+ -1,
+ Anum_pg_user_mapping_oid,
+ InvalidAttrNumber,
+ InvalidAttrNumber,
+ InvalidAttrNumber,
+ InvalidAttrNumber,
+ OBJECT_USER_MAPPING,
+ false
+ },
+};
+
+/*
+ * This struct maps the string object types as returned by
+ * getObjectTypeDescription into ObjectType 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
+ },
+ {
+ "procedure", OBJECT_PROCEDURE
+ },
+ /* 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_AM */
+ {
+ "access method", OBJECT_ACCESS_METHOD
+ },
+ /* OCLASS_AMOP */
+ {
+ "operator of access method", OBJECT_AMOP
+ },
+ /* OCLASS_AMPROC */
+ {
+ "function of access method", OBJECT_AMPROC
+ },
+ /* 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", OBJECT_USER_MAPPING
+ },
+ /* OCLASS_DEFACL */
+ {
+ "default acl", OBJECT_DEFACL
+ },
+ /* OCLASS_EXTENSION */
+ {
+ "extension", OBJECT_EXTENSION
+ },
+ /* OCLASS_EVENT_TRIGGER */
+ {
+ "event trigger", OBJECT_EVENT_TRIGGER
+ },
+ /* OCLASS_PARAMETER_ACL */
+ {
+ "parameter ACL", OBJECT_PARAMETER_ACL
+ },
+ /* OCLASS_POLICY */
+ {
+ "policy", OBJECT_POLICY
+ },
+ /* OCLASS_PUBLICATION */
+ {
+ "publication", OBJECT_PUBLICATION
+ },
+ /* OCLASS_PUBLICATION_NAMESPACE */
+ {
+ "publication namespace", OBJECT_PUBLICATION_NAMESPACE
+ },
+ /* OCLASS_PUBLICATION_REL */
+ {
+ "publication relation", OBJECT_PUBLICATION_REL
+ },
+ /* OCLASS_SUBSCRIPTION */
+ {
+ "subscription", OBJECT_SUBSCRIPTION
+ },
+ /* OCLASS_TRANSFORM */
+ {
+ "transform", OBJECT_TRANSFORM
+ },
+ /* OCLASS_STATISTIC_EXT */
+ {
+ "statistics object", OBJECT_STATISTIC_EXT
+ }
+};
+
+const ObjectAddress InvalidObjectAddress =
+{
+ InvalidOid,
+ InvalidOid,
+ 0
+};
+
+static ObjectAddress get_object_address_unqualified(ObjectType objtype,
+ String *strval, bool missing_ok);
+static ObjectAddress get_relation_by_qualified_name(ObjectType objtype,
+ List *object, Relation *relp,
+ LOCKMODE lockmode, bool missing_ok);
+static ObjectAddress get_object_address_relobject(ObjectType objtype,
+ List *object, Relation *relp, bool missing_ok);
+static ObjectAddress get_object_address_attribute(ObjectType objtype,
+ List *object, Relation *relp,
+ LOCKMODE lockmode, bool missing_ok);
+static ObjectAddress get_object_address_attrdef(ObjectType objtype,
+ List *object, Relation *relp, LOCKMODE lockmode,
+ bool missing_ok);
+static ObjectAddress get_object_address_type(ObjectType objtype,
+ TypeName *typename, bool missing_ok);
+static ObjectAddress get_object_address_opcf(ObjectType objtype, List *object,
+ bool missing_ok);
+static ObjectAddress get_object_address_opf_member(ObjectType objtype,
+ List *object, bool missing_ok);
+
+static ObjectAddress get_object_address_usermapping(List *object,
+ bool missing_ok);
+static ObjectAddress get_object_address_publication_rel(List *object,
+ Relation *relp,
+ bool missing_ok);
+static ObjectAddress get_object_address_publication_schema(List *object,
+ bool missing_ok);
+static ObjectAddress get_object_address_defacl(List *object,
+ bool missing_ok);
+static const ObjectPropertyType *get_object_property_data(Oid class_id);
+
+static void getRelationDescription(StringInfo buffer, Oid relid,
+ bool missing_ok);
+static void getOpFamilyDescription(StringInfo buffer, Oid opfid,
+ bool missing_ok);
+static void getRelationTypeDescription(StringInfo buffer, Oid relid,
+ int32 objectSubId, bool missing_ok);
+static void getProcedureTypeDescription(StringInfo buffer, Oid procid,
+ bool missing_ok);
+static void getConstraintTypeDescription(StringInfo buffer, Oid constroid,
+ bool missing_ok);
+static void getOpFamilyIdentity(StringInfo buffer, Oid opfid, List **object,
+ bool missing_ok);
+static void getRelationIdentity(StringInfo buffer, Oid relid, List **object,
+ bool missing_ok);
+
+/*
+ * Translate an object name and arguments (as passed by the parser) to an
+ * ObjectAddress.
+ *
+ * The returned object will be locked using the specified lockmode. If a
+ * sub-object is looked up, the parent object will be locked instead.
+ *
+ * If the object is a relation or a child object of a relation (e.g. an
+ * attribute or constraint), the relation is also opened and *relp receives
+ * the open relcache entry pointer; otherwise, *relp is set to NULL. This
+ * is a bit grotty but it makes life simpler, since the caller will
+ * typically need the relcache entry too. Caller must close the relcache
+ * entry when done with it. The relation is locked with the specified lockmode
+ * if the target object is the relation itself or an attribute, but for other
+ * child objects, only AccessShareLock is acquired on the relation.
+ *
+ * If the object is not found, an error is thrown, unless missing_ok is
+ * true. In this case, no lock is acquired, relp is set to NULL, and the
+ * returned address has objectId set to InvalidOid.
+ *
+ * We don't currently provide a function to release the locks acquired here;
+ * typically, the lock must be held until commit to guard against a concurrent
+ * drop operation.
+ *
+ * Note: If the object is not found, we don't give any indication of the
+ * reason. (It might have been a missing schema if the name was qualified, or
+ * a nonexistent type name in case of a cast, function or operator; etc).
+ * Currently there is only one caller that might be interested in such info, so
+ * we don't spend much effort here. If more callers start to care, it might be
+ * better to add some support for that in this function.
+ */
+ObjectAddress
+get_object_address(ObjectType objtype, Node *object,
+ Relation *relp, LOCKMODE lockmode, bool missing_ok)
+{
+ ObjectAddress address;
+ ObjectAddress old_address = {InvalidOid, InvalidOid, 0};
+ Relation relation = NULL;
+ uint64 inval_count;
+
+ /* Some kind of lock must be taken. */
+ Assert(lockmode != NoLock);
+
+ for (;;)
+ {
+ /*
+ * Remember this value, so that, after looking up the object name and
+ * locking it, we can check whether any invalidation messages have
+ * been processed that might require a do-over.
+ */
+ inval_count = SharedInvalidMessageCounter;
+
+ /* Look up object address. */
+ switch (objtype)
+ {
+ case OBJECT_INDEX:
+ case OBJECT_SEQUENCE:
+ case OBJECT_TABLE:
+ case OBJECT_VIEW:
+ case OBJECT_MATVIEW:
+ case OBJECT_FOREIGN_TABLE:
+ address =
+ get_relation_by_qualified_name(objtype, castNode(List, object),
+ &relation, lockmode,
+ missing_ok);
+ break;
+ case OBJECT_COLUMN:
+ address =
+ get_object_address_attribute(objtype, castNode(List, object),
+ &relation, lockmode,
+ missing_ok);
+ break;
+ case OBJECT_DEFAULT:
+ address =
+ get_object_address_attrdef(objtype, castNode(List, object),
+ &relation, lockmode,
+ missing_ok);
+ break;
+ case OBJECT_RULE:
+ case OBJECT_TRIGGER:
+ case OBJECT_TABCONSTRAINT:
+ case OBJECT_POLICY:
+ address = get_object_address_relobject(objtype, castNode(List, object),
+ &relation, missing_ok);
+ break;
+ case OBJECT_DOMCONSTRAINT:
+ {
+ List *objlist;
+ ObjectAddress domaddr;
+ char *constrname;
+
+ objlist = castNode(List, object);
+ domaddr = get_object_address_type(OBJECT_DOMAIN,
+ linitial_node(TypeName, objlist),
+ missing_ok);
+ constrname = strVal(lsecond(objlist));
+
+ address.classId = ConstraintRelationId;
+ address.objectId = get_domain_constraint_oid(domaddr.objectId,
+ constrname, missing_ok);
+ address.objectSubId = 0;
+ }
+ break;
+ case OBJECT_DATABASE:
+ case OBJECT_EXTENSION:
+ case OBJECT_TABLESPACE:
+ case OBJECT_ROLE:
+ case OBJECT_SCHEMA:
+ case OBJECT_LANGUAGE:
+ case OBJECT_FDW:
+ case OBJECT_FOREIGN_SERVER:
+ case OBJECT_EVENT_TRIGGER:
+ case OBJECT_PARAMETER_ACL:
+ case OBJECT_ACCESS_METHOD:
+ case OBJECT_PUBLICATION:
+ case OBJECT_SUBSCRIPTION:
+ address = get_object_address_unqualified(objtype,
+ castNode(String, object), missing_ok);
+ break;
+ case OBJECT_TYPE:
+ case OBJECT_DOMAIN:
+ address = get_object_address_type(objtype, castNode(TypeName, object), missing_ok);
+ break;
+ case OBJECT_AGGREGATE:
+ case OBJECT_FUNCTION:
+ case OBJECT_PROCEDURE:
+ case OBJECT_ROUTINE:
+ address.classId = ProcedureRelationId;
+ address.objectId = LookupFuncWithArgs(objtype, castNode(ObjectWithArgs, object), missing_ok);
+ address.objectSubId = 0;
+ break;
+ case OBJECT_OPERATOR:
+ address.classId = OperatorRelationId;
+ address.objectId = LookupOperWithArgs(castNode(ObjectWithArgs, object), missing_ok);
+ address.objectSubId = 0;
+ break;
+ case OBJECT_COLLATION:
+ address.classId = CollationRelationId;
+ address.objectId = get_collation_oid(castNode(List, object), missing_ok);
+ address.objectSubId = 0;
+ break;
+ case OBJECT_CONVERSION:
+ address.classId = ConversionRelationId;
+ address.objectId = get_conversion_oid(castNode(List, object), missing_ok);
+ address.objectSubId = 0;
+ break;
+ case OBJECT_OPCLASS:
+ case OBJECT_OPFAMILY:
+ address = get_object_address_opcf(objtype, castNode(List, object), missing_ok);
+ break;
+ case OBJECT_AMOP:
+ case OBJECT_AMPROC:
+ address = get_object_address_opf_member(objtype, castNode(List, object), missing_ok);
+ break;
+ case OBJECT_LARGEOBJECT:
+ address.classId = LargeObjectRelationId;
+ address.objectId = oidparse(object);
+ address.objectSubId = 0;
+ if (!LargeObjectExists(address.objectId))
+ {
+ if (!missing_ok)
+ ereport(ERROR,
+ (errcode(ERRCODE_UNDEFINED_OBJECT),
+ errmsg("large object %u does not exist",
+ address.objectId)));
+ }
+ break;
+ case OBJECT_CAST:
+ {
+ TypeName *sourcetype = linitial_node(TypeName, castNode(List, object));
+ TypeName *targettype = lsecond_node(TypeName, castNode(List, object));
+ Oid sourcetypeid;
+ Oid targettypeid;
+
+ sourcetypeid = LookupTypeNameOid(NULL, sourcetype, missing_ok);
+ targettypeid = LookupTypeNameOid(NULL, targettype, missing_ok);
+ address.classId = CastRelationId;
+ address.objectId =
+ get_cast_oid(sourcetypeid, targettypeid, missing_ok);
+ address.objectSubId = 0;
+ }
+ break;
+ case OBJECT_TRANSFORM:
+ {
+ TypeName *typename = linitial_node(TypeName, castNode(List, object));
+ char *langname = strVal(lsecond(castNode(List, object)));
+ Oid type_id = LookupTypeNameOid(NULL, typename, missing_ok);
+ Oid lang_id = get_language_oid(langname, missing_ok);
+
+ address.classId = TransformRelationId;
+ address.objectId =
+ get_transform_oid(type_id, lang_id, missing_ok);
+ address.objectSubId = 0;
+ }
+ break;
+ case OBJECT_TSPARSER:
+ address.classId = TSParserRelationId;
+ address.objectId = get_ts_parser_oid(castNode(List, object), missing_ok);
+ address.objectSubId = 0;
+ break;
+ case OBJECT_TSDICTIONARY:
+ address.classId = TSDictionaryRelationId;
+ address.objectId = get_ts_dict_oid(castNode(List, object), missing_ok);
+ address.objectSubId = 0;
+ break;
+ case OBJECT_TSTEMPLATE:
+ address.classId = TSTemplateRelationId;
+ address.objectId = get_ts_template_oid(castNode(List, object), missing_ok);
+ address.objectSubId = 0;
+ break;
+ case OBJECT_TSCONFIGURATION:
+ address.classId = TSConfigRelationId;
+ address.objectId = get_ts_config_oid(castNode(List, object), missing_ok);
+ address.objectSubId = 0;
+ break;
+ case OBJECT_USER_MAPPING:
+ address = get_object_address_usermapping(castNode(List, object),
+ missing_ok);
+ break;
+ case OBJECT_PUBLICATION_NAMESPACE:
+ address = get_object_address_publication_schema(castNode(List, object),
+ missing_ok);
+ break;
+ case OBJECT_PUBLICATION_REL:
+ address = get_object_address_publication_rel(castNode(List, object),
+ &relation,
+ missing_ok);
+ break;
+ case OBJECT_DEFACL:
+ address = get_object_address_defacl(castNode(List, object),
+ missing_ok);
+ break;
+ case OBJECT_STATISTIC_EXT:
+ address.classId = StatisticExtRelationId;
+ address.objectId = get_statistics_object_oid(castNode(List, object),
+ missing_ok);
+ address.objectSubId = 0;
+ break;
+ default:
+ elog(ERROR, "unrecognized objtype: %d", (int) objtype);
+ /* placate compiler, in case it thinks elog might return */
+ address.classId = InvalidOid;
+ address.objectId = InvalidOid;
+ address.objectSubId = 0;
+ }
+
+ /*
+ * If we could not find the supplied object, return without locking.
+ */
+ if (!OidIsValid(address.objectId))
+ {
+ Assert(missing_ok);
+ return address;
+ }
+
+ /*
+ * If we're retrying, see if we got the same answer as last time. If
+ * so, we're done; if not, we locked the wrong thing, so give up our
+ * lock.
+ */
+ if (OidIsValid(old_address.classId))
+ {
+ if (old_address.classId == address.classId
+ && old_address.objectId == address.objectId
+ && old_address.objectSubId == address.objectSubId)
+ break;
+ if (old_address.classId != RelationRelationId)
+ {
+ if (IsSharedRelation(old_address.classId))
+ UnlockSharedObject(old_address.classId,
+ old_address.objectId,
+ 0, lockmode);
+ else
+ UnlockDatabaseObject(old_address.classId,
+ old_address.objectId,
+ 0, lockmode);
+ }
+ }
+
+ /*
+ * If we're dealing with a relation or attribute, then the relation is
+ * already locked. Otherwise, we lock it now.
+ */
+ if (address.classId != RelationRelationId)
+ {
+ if (IsSharedRelation(address.classId))
+ LockSharedObject(address.classId, address.objectId, 0,
+ lockmode);
+ else
+ LockDatabaseObject(address.classId, address.objectId, 0,
+ lockmode);
+ }
+
+ /*
+ * At this point, we've resolved the name to an OID and locked the
+ * corresponding database object. However, it's possible that by the
+ * time we acquire the lock on the object, concurrent DDL has modified
+ * the database in such a way that the name we originally looked up no
+ * longer resolves to that OID.
+ *
+ * We can be certain that this isn't an issue if (a) no shared
+ * invalidation messages have been processed or (b) we've locked a
+ * relation somewhere along the line. All the relation name lookups
+ * in this module ultimately use RangeVarGetRelid() to acquire a
+ * relation lock, and that function protects against the same kinds of
+ * races we're worried about here. Even when operating on a
+ * constraint, rule, or trigger, we still acquire AccessShareLock on
+ * the relation, which is enough to freeze out any concurrent DDL.
+ *
+ * In all other cases, however, it's possible that the name we looked
+ * up no longer refers to the object we locked, so we retry the lookup
+ * and see whether we get the same answer.
+ */
+ if (inval_count == SharedInvalidMessageCounter || relation != NULL)
+ break;
+ old_address = address;
+ }
+
+ /* Return the object address and the relation. */
+ *relp = relation;
+ return address;
+}
+
+/*
+ * Return an ObjectAddress based on a RangeVar and an object name. The
+ * name of the relation identified by the RangeVar is prepended to the
+ * (possibly empty) list passed in as object. This is useful to find
+ * the ObjectAddress of objects that depend on a relation. All other
+ * considerations are exactly as for get_object_address above.
+ */
+ObjectAddress
+get_object_address_rv(ObjectType objtype, RangeVar *rel, List *object,
+ Relation *relp, LOCKMODE lockmode,
+ bool missing_ok)
+{
+ if (rel)
+ {
+ object = lcons(makeString(rel->relname), object);
+ if (rel->schemaname)
+ object = lcons(makeString(rel->schemaname), object);
+ if (rel->catalogname)
+ object = lcons(makeString(rel->catalogname), object);
+ }
+
+ return get_object_address(objtype, (Node *) object,
+ relp, lockmode, missing_ok);
+}
+
+/*
+ * Find an ObjectAddress for a type of object that is identified by an
+ * unqualified name.
+ */
+static ObjectAddress
+get_object_address_unqualified(ObjectType objtype,
+ String *strval, bool missing_ok)
+{
+ const char *name;
+ ObjectAddress address;
+
+ name = strVal(strval);
+
+ /* Translate name to OID. */
+ switch (objtype)
+ {
+ case OBJECT_ACCESS_METHOD:
+ address.classId = AccessMethodRelationId;
+ address.objectId = get_am_oid(name, missing_ok);
+ address.objectSubId = 0;
+ break;
+ case OBJECT_DATABASE:
+ address.classId = DatabaseRelationId;
+ address.objectId = get_database_oid(name, missing_ok);
+ address.objectSubId = 0;
+ break;
+ case OBJECT_EXTENSION:
+ address.classId = ExtensionRelationId;
+ address.objectId = get_extension_oid(name, missing_ok);
+ address.objectSubId = 0;
+ break;
+ case OBJECT_TABLESPACE:
+ address.classId = TableSpaceRelationId;
+ address.objectId = get_tablespace_oid(name, missing_ok);
+ address.objectSubId = 0;
+ break;
+ case OBJECT_ROLE:
+ address.classId = AuthIdRelationId;
+ address.objectId = get_role_oid(name, missing_ok);
+ address.objectSubId = 0;
+ break;
+ case OBJECT_SCHEMA:
+ address.classId = NamespaceRelationId;
+ address.objectId = get_namespace_oid(name, missing_ok);
+ address.objectSubId = 0;
+ break;
+ case OBJECT_LANGUAGE:
+ address.classId = LanguageRelationId;
+ address.objectId = get_language_oid(name, missing_ok);
+ address.objectSubId = 0;
+ break;
+ case OBJECT_FDW:
+ address.classId = ForeignDataWrapperRelationId;
+ address.objectId = get_foreign_data_wrapper_oid(name, missing_ok);
+ address.objectSubId = 0;
+ break;
+ case OBJECT_FOREIGN_SERVER:
+ address.classId = ForeignServerRelationId;
+ address.objectId = get_foreign_server_oid(name, missing_ok);
+ address.objectSubId = 0;
+ break;
+ case OBJECT_EVENT_TRIGGER:
+ address.classId = EventTriggerRelationId;
+ address.objectId = get_event_trigger_oid(name, missing_ok);
+ address.objectSubId = 0;
+ break;
+ case OBJECT_PARAMETER_ACL:
+ address.classId = ParameterAclRelationId;
+ address.objectId = ParameterAclLookup(name, missing_ok);
+ address.objectSubId = 0;
+ break;
+ case OBJECT_PUBLICATION:
+ address.classId = PublicationRelationId;
+ address.objectId = get_publication_oid(name, missing_ok);
+ address.objectSubId = 0;
+ break;
+ case OBJECT_SUBSCRIPTION:
+ address.classId = SubscriptionRelationId;
+ address.objectId = get_subscription_oid(name, missing_ok);
+ address.objectSubId = 0;
+ break;
+ default:
+ elog(ERROR, "unrecognized objtype: %d", (int) objtype);
+ /* placate compiler, which doesn't know elog won't return */
+ address.classId = InvalidOid;
+ address.objectId = InvalidOid;
+ address.objectSubId = 0;
+ }
+
+ return address;
+}
+
+/*
+ * Locate a relation by qualified name.
+ */
+static ObjectAddress
+get_relation_by_qualified_name(ObjectType objtype, List *object,
+ Relation *relp, LOCKMODE lockmode,
+ bool missing_ok)
+{
+ Relation relation;
+ ObjectAddress address;
+
+ address.classId = RelationRelationId;
+ address.objectId = InvalidOid;
+ address.objectSubId = 0;
+
+ relation = relation_openrv_extended(makeRangeVarFromNameList(object),
+ lockmode, missing_ok);
+ if (!relation)
+ return address;
+
+ switch (objtype)
+ {
+ case OBJECT_INDEX:
+ if (relation->rd_rel->relkind != RELKIND_INDEX &&
+ relation->rd_rel->relkind != RELKIND_PARTITIONED_INDEX)
+ ereport(ERROR,
+ (errcode(ERRCODE_WRONG_OBJECT_TYPE),
+ errmsg("\"%s\" is not an index",
+ RelationGetRelationName(relation))));
+ break;
+ case OBJECT_SEQUENCE:
+ if (relation->rd_rel->relkind != RELKIND_SEQUENCE)
+ ereport(ERROR,
+ (errcode(ERRCODE_WRONG_OBJECT_TYPE),
+ errmsg("\"%s\" is not a sequence",
+ RelationGetRelationName(relation))));
+ break;
+ case OBJECT_TABLE:
+ if (relation->rd_rel->relkind != RELKIND_RELATION &&
+ relation->rd_rel->relkind != RELKIND_PARTITIONED_TABLE)
+ ereport(ERROR,
+ (errcode(ERRCODE_WRONG_OBJECT_TYPE),
+ errmsg("\"%s\" is not a table",
+ RelationGetRelationName(relation))));
+ break;
+ case OBJECT_VIEW:
+ if (relation->rd_rel->relkind != RELKIND_VIEW)
+ ereport(ERROR,
+ (errcode(ERRCODE_WRONG_OBJECT_TYPE),
+ errmsg("\"%s\" is not a view",
+ RelationGetRelationName(relation))));
+ break;
+ case OBJECT_MATVIEW:
+ if (relation->rd_rel->relkind != RELKIND_MATVIEW)
+ ereport(ERROR,
+ (errcode(ERRCODE_WRONG_OBJECT_TYPE),
+ errmsg("\"%s\" is not a materialized view",
+ RelationGetRelationName(relation))));
+ break;
+ case OBJECT_FOREIGN_TABLE:
+ if (relation->rd_rel->relkind != RELKIND_FOREIGN_TABLE)
+ ereport(ERROR,
+ (errcode(ERRCODE_WRONG_OBJECT_TYPE),
+ errmsg("\"%s\" is not a foreign table",
+ RelationGetRelationName(relation))));
+ break;
+ default:
+ elog(ERROR, "unrecognized objtype: %d", (int) objtype);
+ break;
+ }
+
+ /* Done. */
+ address.objectId = RelationGetRelid(relation);
+ *relp = relation;
+
+ return address;
+}
+
+/*
+ * Find object address for an object that is attached to a relation.
+ *
+ * Note that we take only an AccessShareLock on the relation. We need not
+ * pass down the LOCKMODE from get_object_address(), because that is the lock
+ * mode for the object itself, not the relation to which it is attached.
+ */
+static ObjectAddress
+get_object_address_relobject(ObjectType objtype, List *object,
+ Relation *relp, bool missing_ok)
+{
+ ObjectAddress address;
+ Relation relation = NULL;
+ int nnames;
+ const char *depname;
+ List *relname;
+ Oid reloid;
+
+ /* Extract name of dependent object. */
+ depname = strVal(llast(object));
+
+ /* Separate relation name from dependent object name. */
+ nnames = list_length(object);
+ if (nnames < 2)
+ ereport(ERROR,
+ (errcode(ERRCODE_SYNTAX_ERROR),
+ errmsg("must specify relation and object name")));
+
+ /* Extract relation name and open relation. */
+ relname = list_truncate(list_copy(object), nnames - 1);
+ relation = table_openrv_extended(makeRangeVarFromNameList(relname),
+ AccessShareLock,
+ missing_ok);
+
+ reloid = relation ? RelationGetRelid(relation) : InvalidOid;
+
+ switch (objtype)
+ {
+ case OBJECT_RULE:
+ address.classId = RewriteRelationId;
+ address.objectId = relation ?
+ get_rewrite_oid(reloid, depname, missing_ok) : InvalidOid;
+ address.objectSubId = 0;
+ break;
+ case OBJECT_TRIGGER:
+ address.classId = TriggerRelationId;
+ address.objectId = relation ?
+ get_trigger_oid(reloid, depname, missing_ok) : InvalidOid;
+ address.objectSubId = 0;
+ break;
+ case OBJECT_TABCONSTRAINT:
+ address.classId = ConstraintRelationId;
+ address.objectId = relation ?
+ get_relation_constraint_oid(reloid, depname, missing_ok) :
+ InvalidOid;
+ address.objectSubId = 0;
+ break;
+ case OBJECT_POLICY:
+ address.classId = PolicyRelationId;
+ address.objectId = relation ?
+ get_relation_policy_oid(reloid, depname, missing_ok) :
+ InvalidOid;
+ address.objectSubId = 0;
+ break;
+ default:
+ elog(ERROR, "unrecognized objtype: %d", (int) objtype);
+ }
+
+ /* Avoid relcache leak when object not found. */
+ if (!OidIsValid(address.objectId))
+ {
+ if (relation != NULL)
+ table_close(relation, AccessShareLock);
+
+ relation = NULL; /* department of accident prevention */
+ return address;
+ }
+
+ /* Done. */
+ *relp = relation;
+ return address;
+}
+
+/*
+ * Find the ObjectAddress for an attribute.
+ */
+static ObjectAddress
+get_object_address_attribute(ObjectType objtype, List *object,
+ Relation *relp, LOCKMODE lockmode,
+ bool missing_ok)
+{
+ ObjectAddress address;
+ List *relname;
+ Oid reloid;
+ Relation relation;
+ const char *attname;
+ AttrNumber attnum;
+
+ /* Extract relation name and open relation. */
+ if (list_length(object) < 2)
+ ereport(ERROR,
+ (errcode(ERRCODE_SYNTAX_ERROR),
+ errmsg("column name must be qualified")));
+ attname = strVal(llast(object));
+ relname = list_truncate(list_copy(object), list_length(object) - 1);
+ /* XXX no missing_ok support here */
+ relation = relation_openrv(makeRangeVarFromNameList(relname), lockmode);
+ reloid = RelationGetRelid(relation);
+
+ /* Look up attribute and construct return value. */
+ attnum = get_attnum(reloid, attname);
+ if (attnum == InvalidAttrNumber)
+ {
+ if (!missing_ok)
+ ereport(ERROR,
+ (errcode(ERRCODE_UNDEFINED_COLUMN),
+ errmsg("column \"%s\" of relation \"%s\" does not exist",
+ attname, NameListToString(relname))));
+
+ address.classId = RelationRelationId;
+ address.objectId = InvalidOid;
+ address.objectSubId = InvalidAttrNumber;
+ relation_close(relation, lockmode);
+ return address;
+ }
+
+ address.classId = RelationRelationId;
+ address.objectId = reloid;
+ address.objectSubId = attnum;
+
+ *relp = relation;
+ return address;
+}
+
+/*
+ * Find the ObjectAddress for an attribute's default value.
+ */
+static ObjectAddress
+get_object_address_attrdef(ObjectType objtype, List *object,
+ 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(object) < 2)
+ ereport(ERROR,
+ (errcode(ERRCODE_SYNTAX_ERROR),
+ errmsg("column name must be qualified")));
+ attname = strVal(llast(object));
+ relname = list_truncate(list_copy(object), list_length(object) - 1);
+ /* XXX no missing_ok support here */
+ relation = relation_openrv(makeRangeVarFromNameList(relname), lockmode);
+ reloid = RelationGetRelid(relation);
+
+ tupdesc = RelationGetDescr(relation);
+
+ /* Look up attribute number and fetch the pg_attrdef OID */
+ attnum = get_attnum(reloid, attname);
+ defoid = InvalidOid;
+ if (attnum != InvalidAttrNumber && tupdesc->constr != NULL)
+ defoid = GetAttrDefaultOid(reloid, attnum);
+ 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
+get_object_address_type(ObjectType objtype, TypeName *typename, bool missing_ok)
+{
+ ObjectAddress address;
+ Type tup;
+
+ address.classId = TypeRelationId;
+ address.objectId = InvalidOid;
+ address.objectSubId = 0;
+
+ tup = LookupTypeName(NULL, typename, NULL, missing_ok);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ ereport(ERROR,
+ (errcode(ERRCODE_UNDEFINED_OBJECT),
+ errmsg("type \"%s\" does not exist",
+ TypeNameToString(typename))));
+ return address;
+ }
+ address.objectId = typeTypeId(tup);
+
+ if (objtype == OBJECT_DOMAIN)
+ {
+ if (((Form_pg_type) GETSTRUCT(tup))->typtype != TYPTYPE_DOMAIN)
+ ereport(ERROR,
+ (errcode(ERRCODE_WRONG_OBJECT_TYPE),
+ errmsg("\"%s\" is not a domain",
+ TypeNameToString(typename))));
+ }
+
+ ReleaseSysCache(tup);
+
+ return address;
+}
+
+/*
+ * Find the ObjectAddress for an opclass or opfamily.
+ */
+static ObjectAddress
+get_object_address_opcf(ObjectType objtype, List *object, bool missing_ok)
+{
+ Oid amoid;
+ ObjectAddress address;
+
+ /* XXX no missing_ok support here */
+ amoid = get_index_am_oid(strVal(linitial(object)), false);
+ object = list_copy_tail(object, 1);
+
+ switch (objtype)
+ {
+ case OBJECT_OPCLASS:
+ address.classId = OperatorClassRelationId;
+ address.objectId = get_opclass_oid(amoid, object, missing_ok);
+ address.objectSubId = 0;
+ break;
+ case OBJECT_OPFAMILY:
+ address.classId = OperatorFamilyRelationId;
+ address.objectId = get_opfamily_oid(amoid, object, missing_ok);
+ address.objectSubId = 0;
+ break;
+ default:
+ elog(ERROR, "unrecognized objtype: %d", (int) objtype);
+ /* placate compiler, which doesn't know elog won't return */
+ address.classId = InvalidOid;
+ address.objectId = InvalidOid;
+ address.objectSubId = 0;
+ }
+
+ return address;
+}
+
+/*
+ * Find the ObjectAddress for an opclass/opfamily member.
+ *
+ * (The returned address corresponds to a pg_amop/pg_amproc object).
+ */
+static ObjectAddress
+get_object_address_opf_member(ObjectType objtype,
+ List *object, bool missing_ok)
+{
+ ObjectAddress famaddr;
+ ObjectAddress address;
+ ListCell *cell;
+ List *copy;
+ TypeName *typenames[2];
+ Oid typeoids[2];
+ int membernum;
+ int i;
+
+ /*
+ * The last element of the object list contains the strategy or procedure
+ * number. We need to strip that out before getting the opclass/family
+ * address. The rest can be used directly by get_object_address_opcf().
+ */
+ membernum = atoi(strVal(llast(linitial(object))));
+ copy = list_truncate(list_copy(linitial(object)), list_length(linitial(object)) - 1);
+
+ /* no missing_ok support here */
+ famaddr = get_object_address_opcf(OBJECT_OPFAMILY, copy, false);
+
+ /* find out left/right type names and OIDs */
+ typenames[0] = typenames[1] = NULL;
+ typeoids[0] = typeoids[1] = InvalidOid;
+ i = 0;
+ foreach(cell, lsecond(object))
+ {
+ ObjectAddress typaddr;
+
+ typenames[i] = lfirst_node(TypeName, cell);
+ typaddr = get_object_address_type(OBJECT_TYPE, typenames[i], missing_ok);
+ typeoids[i] = typaddr.objectId;
+ if (++i >= 2)
+ break;
+ }
+
+ switch (objtype)
+ {
+ case OBJECT_AMOP:
+ {
+ HeapTuple tp;
+
+ ObjectAddressSet(address, AccessMethodOperatorRelationId,
+ InvalidOid);
+
+ tp = SearchSysCache4(AMOPSTRATEGY,
+ ObjectIdGetDatum(famaddr.objectId),
+ ObjectIdGetDatum(typeoids[0]),
+ ObjectIdGetDatum(typeoids[1]),
+ Int16GetDatum(membernum));
+ if (!HeapTupleIsValid(tp))
+ {
+ if (!missing_ok)
+ ereport(ERROR,
+ (errcode(ERRCODE_UNDEFINED_OBJECT),
+ errmsg("operator %d (%s, %s) of %s does not exist",
+ membernum,
+ TypeNameToString(typenames[0]),
+ TypeNameToString(typenames[1]),
+ getObjectDescription(&famaddr, false))));
+ }
+ else
+ {
+ address.objectId = ((Form_pg_amop) GETSTRUCT(tp))->oid;
+ ReleaseSysCache(tp);
+ }
+ }
+ break;
+
+ case OBJECT_AMPROC:
+ {
+ HeapTuple tp;
+
+ ObjectAddressSet(address, AccessMethodProcedureRelationId,
+ InvalidOid);
+
+ tp = SearchSysCache4(AMPROCNUM,
+ ObjectIdGetDatum(famaddr.objectId),
+ ObjectIdGetDatum(typeoids[0]),
+ ObjectIdGetDatum(typeoids[1]),
+ Int16GetDatum(membernum));
+ if (!HeapTupleIsValid(tp))
+ {
+ if (!missing_ok)
+ ereport(ERROR,
+ (errcode(ERRCODE_UNDEFINED_OBJECT),
+ errmsg("function %d (%s, %s) of %s does not exist",
+ membernum,
+ TypeNameToString(typenames[0]),
+ TypeNameToString(typenames[1]),
+ getObjectDescription(&famaddr, false))));
+ }
+ else
+ {
+ address.objectId = ((Form_pg_amproc) GETSTRUCT(tp))->oid;
+ ReleaseSysCache(tp);
+ }
+ }
+ break;
+ default:
+ elog(ERROR, "unrecognized objtype: %d", (int) objtype);
+ }
+
+ return address;
+}
+
+/*
+ * Find the ObjectAddress for a user mapping.
+ */
+static ObjectAddress
+get_object_address_usermapping(List *object, bool missing_ok)
+{
+ ObjectAddress address;
+ Oid userid;
+ char *username;
+ char *servername;
+ ForeignServer *server;
+ HeapTuple tp;
+
+ ObjectAddressSet(address, UserMappingRelationId, InvalidOid);
+
+ /* fetch string names from input lists, for error messages */
+ username = strVal(linitial(object));
+ servername = strVal(lsecond(object));
+
+ /* look up pg_authid OID of mapped user; InvalidOid if PUBLIC */
+ if (strcmp(username, "public") == 0)
+ userid = InvalidOid;
+ else
+ {
+ tp = SearchSysCache1(AUTHNAME,
+ CStringGetDatum(username));
+ if (!HeapTupleIsValid(tp))
+ {
+ if (!missing_ok)
+ ereport(ERROR,
+ (errcode(ERRCODE_UNDEFINED_OBJECT),
+ errmsg("user mapping for user \"%s\" on server \"%s\" does not exist",
+ username, servername)));
+ return address;
+ }
+ userid = ((Form_pg_authid) GETSTRUCT(tp))->oid;
+ ReleaseSysCache(tp);
+ }
+
+ /* Now look up the pg_user_mapping tuple */
+ server = GetForeignServerByName(servername, true);
+ if (!server)
+ {
+ if (!missing_ok)
+ ereport(ERROR,
+ (errcode(ERRCODE_UNDEFINED_OBJECT),
+ errmsg("server \"%s\" does not exist", servername)));
+ return address;
+ }
+ tp = SearchSysCache2(USERMAPPINGUSERSERVER,
+ ObjectIdGetDatum(userid),
+ ObjectIdGetDatum(server->serverid));
+ if (!HeapTupleIsValid(tp))
+ {
+ if (!missing_ok)
+ ereport(ERROR,
+ (errcode(ERRCODE_UNDEFINED_OBJECT),
+ errmsg("user mapping for user \"%s\" on server \"%s\" does not exist",
+ username, servername)));
+ return address;
+ }
+
+ address.objectId = ((Form_pg_user_mapping) GETSTRUCT(tp))->oid;
+
+ ReleaseSysCache(tp);
+
+ return address;
+}
+
+/*
+ * Find the ObjectAddress for a publication relation. The first element of
+ * the object parameter is the relation name, the second is the
+ * publication name.
+ */
+static ObjectAddress
+get_object_address_publication_rel(List *object,
+ Relation *relp, bool missing_ok)
+{
+ ObjectAddress address;
+ Relation relation;
+ List *relname;
+ char *pubname;
+ Publication *pub;
+
+ ObjectAddressSet(address, PublicationRelRelationId, InvalidOid);
+
+ relname = linitial(object);
+ relation = relation_openrv_extended(makeRangeVarFromNameList(relname),
+ AccessShareLock, missing_ok);
+ if (!relation)
+ return address;
+
+ /* fetch publication name from input list */
+ pubname = strVal(lsecond(object));
+
+ /* Now look up the pg_publication tuple */
+ pub = GetPublicationByName(pubname, missing_ok);
+ if (!pub)
+ {
+ relation_close(relation, AccessShareLock);
+ return address;
+ }
+
+ /* Find the publication relation mapping in syscache. */
+ address.objectId =
+ GetSysCacheOid2(PUBLICATIONRELMAP, Anum_pg_publication_rel_oid,
+ ObjectIdGetDatum(RelationGetRelid(relation)),
+ ObjectIdGetDatum(pub->oid));
+ if (!OidIsValid(address.objectId))
+ {
+ if (!missing_ok)
+ ereport(ERROR,
+ (errcode(ERRCODE_UNDEFINED_OBJECT),
+ errmsg("publication relation \"%s\" in publication \"%s\" does not exist",
+ RelationGetRelationName(relation), pubname)));
+ relation_close(relation, AccessShareLock);
+ return address;
+ }
+
+ *relp = relation;
+ return address;
+}
+
+/*
+ * Find the ObjectAddress for a publication schema. The first element of the
+ * object parameter is the schema name, the second is the publication name.
+ */
+static ObjectAddress
+get_object_address_publication_schema(List *object, bool missing_ok)
+{
+ ObjectAddress address;
+ Publication *pub;
+ char *pubname;
+ char *schemaname;
+ Oid schemaid;
+
+ ObjectAddressSet(address, PublicationNamespaceRelationId, InvalidOid);
+
+ /* Fetch schema name and publication name from input list */
+ schemaname = strVal(linitial(object));
+ pubname = strVal(lsecond(object));
+
+ schemaid = get_namespace_oid(schemaname, missing_ok);
+ if (!OidIsValid(schemaid))
+ return address;
+
+ /* Now look up the pg_publication tuple */
+ pub = GetPublicationByName(pubname, missing_ok);
+ if (!pub)
+ return address;
+
+ /* Find the publication schema mapping in syscache */
+ address.objectId =
+ GetSysCacheOid2(PUBLICATIONNAMESPACEMAP,
+ Anum_pg_publication_namespace_oid,
+ ObjectIdGetDatum(schemaid),
+ ObjectIdGetDatum(pub->oid));
+ if (!OidIsValid(address.objectId) && !missing_ok)
+ ereport(ERROR,
+ (errcode(ERRCODE_UNDEFINED_OBJECT),
+ errmsg("publication schema \"%s\" in publication \"%s\" does not exist",
+ schemaname, pubname)));
+
+ return address;
+}
+
+/*
+ * Find the ObjectAddress for a default ACL.
+ */
+static ObjectAddress
+get_object_address_defacl(List *object, bool missing_ok)
+{
+ HeapTuple tp;
+ Oid userid;
+ Oid schemaid;
+ char *username;
+ char *schema;
+ char objtype;
+ char *objtype_str;
+ ObjectAddress address;
+
+ ObjectAddressSet(address, DefaultAclRelationId, InvalidOid);
+
+ /*
+ * First figure out the textual attributes so that they can be used for
+ * error reporting.
+ */
+ username = strVal(lsecond(object));
+ if (list_length(object) >= 3)
+ schema = (char *) strVal(lthird(object));
+ else
+ schema = NULL;
+
+ /*
+ * Decode defaclobjtype. Only first char is considered; the rest of the
+ * string, if any, is blissfully ignored.
+ */
+ objtype = ((char *) strVal(linitial(object)))[0];
+ switch (objtype)
+ {
+ case DEFACLOBJ_RELATION:
+ objtype_str = "tables";
+ break;
+ case DEFACLOBJ_SEQUENCE:
+ objtype_str = "sequences";
+ break;
+ case DEFACLOBJ_FUNCTION:
+ objtype_str = "functions";
+ break;
+ case DEFACLOBJ_TYPE:
+ objtype_str = "types";
+ break;
+ case DEFACLOBJ_NAMESPACE:
+ objtype_str = "schemas";
+ break;
+ default:
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+ errmsg("unrecognized default ACL object type \"%c\"", objtype),
+ errhint("Valid object types are \"%c\", \"%c\", \"%c\", \"%c\", \"%c\".",
+ DEFACLOBJ_RELATION,
+ DEFACLOBJ_SEQUENCE,
+ DEFACLOBJ_FUNCTION,
+ DEFACLOBJ_TYPE,
+ DEFACLOBJ_NAMESPACE)));
+ }
+
+ /*
+ * Look up user ID. Behave as "default ACL not found" if the user doesn't
+ * exist.
+ */
+ tp = SearchSysCache1(AUTHNAME,
+ CStringGetDatum(username));
+ if (!HeapTupleIsValid(tp))
+ goto not_found;
+ userid = ((Form_pg_authid) GETSTRUCT(tp))->oid;
+ ReleaseSysCache(tp);
+
+ /*
+ * If a schema name was given, look up its OID. If it doesn't exist,
+ * behave as "default ACL not found".
+ */
+ if (schema)
+ {
+ schemaid = get_namespace_oid(schema, true);
+ if (schemaid == InvalidOid)
+ goto not_found;
+ }
+ else
+ schemaid = InvalidOid;
+
+ /* Finally, look up the pg_default_acl object */
+ tp = SearchSysCache3(DEFACLROLENSPOBJ,
+ ObjectIdGetDatum(userid),
+ ObjectIdGetDatum(schemaid),
+ CharGetDatum(objtype));
+ if (!HeapTupleIsValid(tp))
+ goto not_found;
+
+ address.objectId = ((Form_pg_default_acl) GETSTRUCT(tp))->oid;
+ ReleaseSysCache(tp);
+
+ return address;
+
+not_found:
+ if (!missing_ok)
+ {
+ if (schema)
+ ereport(ERROR,
+ (errcode(ERRCODE_UNDEFINED_OBJECT),
+ errmsg("default ACL for user \"%s\" in schema \"%s\" on %s does not exist",
+ username, schema, objtype_str)));
+ else
+ ereport(ERROR,
+ (errcode(ERRCODE_UNDEFINED_OBJECT),
+ errmsg("default ACL for user \"%s\" on %s does not exist",
+ username, objtype_str)));
+ }
+ 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, TYPALIGN_INT,
+ &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_DATUM(0));
+ ArrayType *namearr = PG_GETARG_ARRAYTYPE_P(1);
+ ArrayType *argsarr = PG_GETARG_ARRAYTYPE_P(2);
+ int itype;
+ ObjectType type;
+ List *name = NIL;
+ TypeName *typename = NULL;
+ List *args = NIL;
+ Node *objnode = NULL;
+ ObjectAddress addr;
+ TupleDesc tupdesc;
+ Datum values[3];
+ bool nulls[3];
+ HeapTuple htup;
+ Relation relation;
+
+ /* Decode object type, raise error if unknown */
+ 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 || type == OBJECT_CAST ||
+ type == OBJECT_TRANSFORM || type == OBJECT_DOMCONSTRAINT)
+ {
+ Datum *elems;
+ bool *nulls;
+ int nelems;
+
+ deconstruct_array(namearr, TEXTOID, -1, false, TYPALIGN_INT,
+ &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")));
+ typename = typeStringToTypeName(TextDatumGetCString(elems[0]));
+ }
+ else if (type == OBJECT_LARGEOBJECT)
+ {
+ Datum *elems;
+ bool *nulls;
+ int nelems;
+
+ deconstruct_array(namearr, TEXTOID, -1, false, TYPALIGN_INT,
+ &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")));
+ objnode = (Node *) 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 length must be at least %d", 1)));
+ }
+
+ /*
+ * If args are given, decode them according to the object type.
+ */
+ if (type == OBJECT_AGGREGATE ||
+ type == OBJECT_FUNCTION ||
+ type == OBJECT_PROCEDURE ||
+ type == OBJECT_ROUTINE ||
+ type == OBJECT_OPERATOR ||
+ type == OBJECT_CAST ||
+ type == OBJECT_AMOP ||
+ type == OBJECT_AMPROC)
+ {
+ /* in these cases, the args list must be of TypeName */
+ Datum *elems;
+ bool *nulls;
+ int nelems;
+ int i;
+
+ deconstruct_array(argsarr, TEXTOID, -1, false, TYPALIGN_INT,
+ &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_address is pretty sensitive to the length of its input
+ * lists; check that they're what it wants.
+ */
+ switch (type)
+ {
+ case OBJECT_PUBLICATION_NAMESPACE:
+ case OBJECT_USER_MAPPING:
+ if (list_length(name) != 1)
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+ errmsg("name list length must be exactly %d", 1)));
+ /* fall through to check args length */
+ /* FALLTHROUGH */
+ case OBJECT_DOMCONSTRAINT:
+ case OBJECT_CAST:
+ case OBJECT_PUBLICATION_REL:
+ case OBJECT_DEFACL:
+ case OBJECT_TRANSFORM:
+ if (list_length(args) != 1)
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+ errmsg("argument list length must be exactly %d", 1)));
+ break;
+ case OBJECT_OPFAMILY:
+ case OBJECT_OPCLASS:
+ if (list_length(name) < 2)
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+ errmsg("name list length must be at least %d", 2)));
+ break;
+ case OBJECT_AMOP:
+ case OBJECT_AMPROC:
+ if (list_length(name) < 3)
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+ errmsg("name list length must be at least %d", 3)));
+ /* fall through to check args length */
+ /* FALLTHROUGH */
+ 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;
+ }
+
+ /*
+ * Now build the Node type that get_object_address() expects for the given
+ * type.
+ */
+ switch (type)
+ {
+ case OBJECT_TABLE:
+ case OBJECT_SEQUENCE:
+ case OBJECT_VIEW:
+ case OBJECT_MATVIEW:
+ case OBJECT_INDEX:
+ case OBJECT_FOREIGN_TABLE:
+ case OBJECT_COLUMN:
+ case OBJECT_ATTRIBUTE:
+ case OBJECT_COLLATION:
+ case OBJECT_CONVERSION:
+ case OBJECT_STATISTIC_EXT:
+ case OBJECT_TSPARSER:
+ case OBJECT_TSDICTIONARY:
+ case OBJECT_TSTEMPLATE:
+ case OBJECT_TSCONFIGURATION:
+ case OBJECT_DEFAULT:
+ case OBJECT_POLICY:
+ case OBJECT_RULE:
+ case OBJECT_TRIGGER:
+ case OBJECT_TABCONSTRAINT:
+ case OBJECT_OPCLASS:
+ case OBJECT_OPFAMILY:
+ objnode = (Node *) name;
+ break;
+ case OBJECT_ACCESS_METHOD:
+ case OBJECT_DATABASE:
+ case OBJECT_EVENT_TRIGGER:
+ case OBJECT_EXTENSION:
+ case OBJECT_FDW:
+ case OBJECT_FOREIGN_SERVER:
+ case OBJECT_LANGUAGE:
+ case OBJECT_PARAMETER_ACL:
+ case OBJECT_PUBLICATION:
+ case OBJECT_ROLE:
+ case OBJECT_SCHEMA:
+ case OBJECT_SUBSCRIPTION:
+ case OBJECT_TABLESPACE:
+ if (list_length(name) != 1)
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+ errmsg("name list length must be exactly %d", 1)));
+ objnode = linitial(name);
+ break;
+ case OBJECT_TYPE:
+ case OBJECT_DOMAIN:
+ objnode = (Node *) typename;
+ break;
+ case OBJECT_CAST:
+ case OBJECT_DOMCONSTRAINT:
+ case OBJECT_TRANSFORM:
+ objnode = (Node *) list_make2(typename, linitial(args));
+ break;
+ case OBJECT_PUBLICATION_REL:
+ objnode = (Node *) list_make2(name, linitial(args));
+ break;
+ case OBJECT_PUBLICATION_NAMESPACE:
+ case OBJECT_USER_MAPPING:
+ objnode = (Node *) list_make2(linitial(name), linitial(args));
+ break;
+ case OBJECT_DEFACL:
+ objnode = (Node *) lcons(linitial(args), name);
+ break;
+ case OBJECT_AMOP:
+ case OBJECT_AMPROC:
+ objnode = (Node *) list_make2(name, args);
+ break;
+ case OBJECT_FUNCTION:
+ case OBJECT_PROCEDURE:
+ case OBJECT_ROUTINE:
+ case OBJECT_AGGREGATE:
+ case OBJECT_OPERATOR:
+ {
+ ObjectWithArgs *owa = makeNode(ObjectWithArgs);
+
+ owa->objname = name;
+ owa->objargs = args;
+ objnode = (Node *) owa;
+ break;
+ }
+ case OBJECT_LARGEOBJECT:
+ /* already handled above */
+ break;
+ /* no default, to let compiler warn about missing case */
+ }
+
+ if (objnode == NULL)
+ elog(ERROR, "unrecognized object type: %d", type);
+
+ addr = get_object_address(type, objnode,
+ &relation, AccessShareLock, false);
+
+ /* We don't need the relcache entry, thank you very much */
+ if (relation)
+ relation_close(relation, AccessShareLock);
+
+ tupdesc = CreateTemplateTupleDesc(3);
+ 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
+check_object_ownership(Oid roleid, ObjectType objtype, ObjectAddress address,
+ Node *object, Relation relation)
+{
+ switch (objtype)
+ {
+ case OBJECT_INDEX:
+ case OBJECT_SEQUENCE:
+ case OBJECT_TABLE:
+ case OBJECT_VIEW:
+ case OBJECT_MATVIEW:
+ case OBJECT_FOREIGN_TABLE:
+ case OBJECT_COLUMN:
+ case OBJECT_RULE:
+ case OBJECT_TRIGGER:
+ case OBJECT_POLICY:
+ case OBJECT_TABCONSTRAINT:
+ if (!pg_class_ownercheck(RelationGetRelid(relation), roleid))
+ aclcheck_error(ACLCHECK_NOT_OWNER, objtype,
+ RelationGetRelationName(relation));
+ break;
+ case OBJECT_DATABASE:
+ if (!pg_database_ownercheck(address.objectId, roleid))
+ aclcheck_error(ACLCHECK_NOT_OWNER, objtype,
+ strVal(object));
+ break;
+ case OBJECT_TYPE:
+ case OBJECT_DOMAIN:
+ case OBJECT_ATTRIBUTE:
+ if (!pg_type_ownercheck(address.objectId, roleid))
+ aclcheck_error_type(ACLCHECK_NOT_OWNER, address.objectId);
+ break;
+ case OBJECT_DOMCONSTRAINT:
+ {
+ HeapTuple tuple;
+ Oid contypid;
+
+ tuple = SearchSysCache1(CONSTROID,
+ ObjectIdGetDatum(address.objectId));
+ if (!HeapTupleIsValid(tuple))
+ elog(ERROR, "constraint with OID %u does not exist",
+ address.objectId);
+
+ contypid = ((Form_pg_constraint) GETSTRUCT(tuple))->contypid;
+
+ ReleaseSysCache(tuple);
+
+ /*
+ * Fallback to type ownership check in this case as this is
+ * what domain constraints rely on.
+ */
+ if (!pg_type_ownercheck(contypid, roleid))
+ aclcheck_error_type(ACLCHECK_NOT_OWNER, contypid);
+ }
+ break;
+ case OBJECT_AGGREGATE:
+ case OBJECT_FUNCTION:
+ case OBJECT_PROCEDURE:
+ case OBJECT_ROUTINE:
+ if (!pg_proc_ownercheck(address.objectId, roleid))
+ aclcheck_error(ACLCHECK_NOT_OWNER, objtype,
+ NameListToString((castNode(ObjectWithArgs, object))->objname));
+ break;
+ case OBJECT_OPERATOR:
+ if (!pg_oper_ownercheck(address.objectId, roleid))
+ aclcheck_error(ACLCHECK_NOT_OWNER, objtype,
+ NameListToString((castNode(ObjectWithArgs, object))->objname));
+ break;
+ case OBJECT_SCHEMA:
+ if (!pg_namespace_ownercheck(address.objectId, roleid))
+ aclcheck_error(ACLCHECK_NOT_OWNER, objtype,
+ strVal(object));
+ break;
+ case OBJECT_COLLATION:
+ if (!pg_collation_ownercheck(address.objectId, roleid))
+ aclcheck_error(ACLCHECK_NOT_OWNER, objtype,
+ NameListToString(castNode(List, object)));
+ break;
+ case OBJECT_CONVERSION:
+ if (!pg_conversion_ownercheck(address.objectId, roleid))
+ aclcheck_error(ACLCHECK_NOT_OWNER, objtype,
+ NameListToString(castNode(List, object)));
+ break;
+ case OBJECT_EXTENSION:
+ if (!pg_extension_ownercheck(address.objectId, roleid))
+ aclcheck_error(ACLCHECK_NOT_OWNER, objtype,
+ strVal(object));
+ break;
+ case OBJECT_FDW:
+ if (!pg_foreign_data_wrapper_ownercheck(address.objectId, roleid))
+ aclcheck_error(ACLCHECK_NOT_OWNER, objtype,
+ strVal(object));
+ break;
+ case OBJECT_FOREIGN_SERVER:
+ if (!pg_foreign_server_ownercheck(address.objectId, roleid))
+ aclcheck_error(ACLCHECK_NOT_OWNER, objtype,
+ strVal(object));
+ break;
+ case OBJECT_EVENT_TRIGGER:
+ if (!pg_event_trigger_ownercheck(address.objectId, roleid))
+ aclcheck_error(ACLCHECK_NOT_OWNER, objtype,
+ strVal(object));
+ break;
+ case OBJECT_LANGUAGE:
+ if (!pg_language_ownercheck(address.objectId, roleid))
+ aclcheck_error(ACLCHECK_NOT_OWNER, objtype,
+ strVal(object));
+ break;
+ case OBJECT_OPCLASS:
+ if (!pg_opclass_ownercheck(address.objectId, roleid))
+ aclcheck_error(ACLCHECK_NOT_OWNER, objtype,
+ NameListToString(castNode(List, object)));
+ break;
+ case OBJECT_OPFAMILY:
+ if (!pg_opfamily_ownercheck(address.objectId, roleid))
+ aclcheck_error(ACLCHECK_NOT_OWNER, objtype,
+ NameListToString(castNode(List, object)));
+ break;
+ case OBJECT_LARGEOBJECT:
+ if (!lo_compat_privileges &&
+ !pg_largeobject_ownercheck(address.objectId, roleid))
+ ereport(ERROR,
+ (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
+ errmsg("must be owner of large object %u",
+ address.objectId)));
+ break;
+ case OBJECT_CAST:
+ {
+ /* We can only check permissions on the source/target types */
+ TypeName *sourcetype = linitial_node(TypeName, castNode(List, object));
+ TypeName *targettype = lsecond_node(TypeName, castNode(List, object));
+ Oid sourcetypeid = typenameTypeId(NULL, sourcetype);
+ Oid targettypeid = typenameTypeId(NULL, targettype);
+
+ if (!pg_type_ownercheck(sourcetypeid, roleid)
+ && !pg_type_ownercheck(targettypeid, roleid))
+ ereport(ERROR,
+ (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
+ errmsg("must be owner of type %s or type %s",
+ format_type_be(sourcetypeid),
+ format_type_be(targettypeid))));
+ }
+ break;
+ case OBJECT_PUBLICATION:
+ if (!pg_publication_ownercheck(address.objectId, roleid))
+ aclcheck_error(ACLCHECK_NOT_OWNER, objtype,
+ strVal(object));
+ break;
+ case OBJECT_SUBSCRIPTION:
+ if (!pg_subscription_ownercheck(address.objectId, roleid))
+ aclcheck_error(ACLCHECK_NOT_OWNER, objtype,
+ strVal(object));
+ break;
+ case OBJECT_TRANSFORM:
+ {
+ TypeName *typename = linitial_node(TypeName, castNode(List, object));
+ Oid typeid = typenameTypeId(NULL, typename);
+
+ if (!pg_type_ownercheck(typeid, roleid))
+ aclcheck_error_type(ACLCHECK_NOT_OWNER, typeid);
+ }
+ break;
+ case OBJECT_TABLESPACE:
+ if (!pg_tablespace_ownercheck(address.objectId, roleid))
+ aclcheck_error(ACLCHECK_NOT_OWNER, objtype,
+ strVal(object));
+ break;
+ case OBJECT_TSDICTIONARY:
+ if (!pg_ts_dict_ownercheck(address.objectId, roleid))
+ aclcheck_error(ACLCHECK_NOT_OWNER, objtype,
+ NameListToString(castNode(List, object)));
+ break;
+ case OBJECT_TSCONFIGURATION:
+ if (!pg_ts_config_ownercheck(address.objectId, roleid))
+ aclcheck_error(ACLCHECK_NOT_OWNER, objtype,
+ NameListToString(castNode(List, object)));
+ break;
+ case OBJECT_ROLE:
+
+ /*
+ * We treat roles as being "owned" by those with CREATEROLE priv,
+ * except that superusers are only owned by superusers.
+ */
+ if (superuser_arg(address.objectId))
+ {
+ if (!superuser_arg(roleid))
+ ereport(ERROR,
+ (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
+ errmsg("must be superuser")));
+ }
+ else
+ {
+ if (!has_createrole_privilege(roleid))
+ ereport(ERROR,
+ (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
+ errmsg("must have CREATEROLE privilege")));
+ }
+ break;
+ case OBJECT_TSPARSER:
+ case OBJECT_TSTEMPLATE:
+ case OBJECT_ACCESS_METHOD:
+ case OBJECT_PARAMETER_ACL:
+ /* We treat these object types as being owned by superusers */
+ if (!superuser_arg(roleid))
+ ereport(ERROR,
+ (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
+ errmsg("must be superuser")));
+ break;
+ case OBJECT_STATISTIC_EXT:
+ if (!pg_statistics_object_ownercheck(address.objectId, roleid))
+ aclcheck_error(ACLCHECK_NOT_OWNER, objtype,
+ NameListToString(castNode(List, object)));
+ break;
+ default:
+ elog(ERROR, "unrecognized object type: %d",
+ (int) objtype);
+ }
+}
+
+/*
+ * get_object_namespace
+ *
+ * Find the schema containing the specified object. For non-schema objects,
+ * this function returns InvalidOid.
+ */
+Oid
+get_object_namespace(const ObjectAddress *address)
+{
+ int cache;
+ HeapTuple tuple;
+ bool isnull;
+ Oid oid;
+ const ObjectPropertyType *property;
+
+ /* If not owned by a namespace, just return InvalidOid. */
+ property = get_object_property_data(address->classId);
+ if (property->attnum_namespace == InvalidAttrNumber)
+ return InvalidOid;
+
+ /* Currently, we can only handle object types with system caches. */
+ cache = property->oid_catcache_id;
+ Assert(cache != -1);
+
+ /* Fetch tuple from syscache and extract namespace attribute. */
+ tuple = SearchSysCache1(cache, ObjectIdGetDatum(address->objectId));
+ if (!HeapTupleIsValid(tuple))
+ elog(ERROR, "cache lookup failed for cache %d oid %u",
+ cache, address->objectId);
+ oid = DatumGetObjectId(SysCacheGetAttr(cache,
+ tuple,
+ property->attnum_namespace,
+ &isnull));
+ Assert(!isnull);
+ ReleaseSysCache(tuple);
+
+ 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)
+{
+ int i;
+
+ for (i = 0; i < lengthof(ObjectTypeMap); i++)
+ {
+ if (strcmp(ObjectTypeMap[i].tm_name, objtype) == 0)
+ return ObjectTypeMap[i].tm_type;
+ }
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+ errmsg("unrecognized object type \"%s\"", objtype)));
+
+ return -1; /* keep compiler quiet */
+}
+
+/*
+ * Interfaces to reference fields of ObjectPropertyType
+ */
+const char *
+get_object_class_descr(Oid class_id)
+{
+ const ObjectPropertyType *prop = get_object_property_data(class_id);
+
+ return prop->class_descr;
+}
+
+Oid
+get_object_oid_index(Oid class_id)
+{
+ const ObjectPropertyType *prop = get_object_property_data(class_id);
+
+ return prop->oid_index_oid;
+}
+
+int
+get_object_catcache_oid(Oid class_id)
+{
+ const ObjectPropertyType *prop = get_object_property_data(class_id);
+
+ return prop->oid_catcache_id;
+}
+
+int
+get_object_catcache_name(Oid class_id)
+{
+ const ObjectPropertyType *prop = get_object_property_data(class_id);
+
+ return prop->name_catcache_id;
+}
+
+AttrNumber
+get_object_attnum_oid(Oid class_id)
+{
+ const ObjectPropertyType *prop = get_object_property_data(class_id);
+
+ return prop->attnum_oid;
+}
+
+AttrNumber
+get_object_attnum_name(Oid class_id)
+{
+ const ObjectPropertyType *prop = get_object_property_data(class_id);
+
+ return prop->attnum_name;
+}
+
+AttrNumber
+get_object_attnum_namespace(Oid class_id)
+{
+ const ObjectPropertyType *prop = get_object_property_data(class_id);
+
+ return prop->attnum_namespace;
+}
+
+AttrNumber
+get_object_attnum_owner(Oid class_id)
+{
+ const ObjectPropertyType *prop = get_object_property_data(class_id);
+
+ return prop->attnum_owner;
+}
+
+AttrNumber
+get_object_attnum_acl(Oid class_id)
+{
+ const ObjectPropertyType *prop = get_object_property_data(class_id);
+
+ return prop->attnum_acl;
+}
+
+/*
+ * get_object_type
+ *
+ * Return the object type associated with a given object. This routine
+ * is primarily used to determine the object type to mention in ACL check
+ * error messages, so it's desirable for it to avoid failing.
+ */
+ObjectType
+get_object_type(Oid class_id, Oid object_id)
+{
+ const ObjectPropertyType *prop = get_object_property_data(class_id);
+
+ if (prop->objtype == OBJECT_TABLE)
+ {
+ /*
+ * If the property data says it's a table, dig a little deeper to get
+ * the real relation kind, so that callers can produce more precise
+ * error messages.
+ */
+ return get_relkind_objtype(get_rel_relkind(object_id));
+ }
+ else
+ return prop->objtype;
+}
+
+bool
+get_object_namensp_unique(Oid class_id)
+{
+ const ObjectPropertyType *prop = get_object_property_data(class_id);
+
+ return prop->is_nsp_name_unique;
+}
+
+/*
+ * Return whether we have useful data for the given object class in the
+ * ObjectProperty table.
+ */
+bool
+is_objectclass_supported(Oid class_id)
+{
+ int index;
+
+ for (index = 0; index < lengthof(ObjectProperty); index++)
+ {
+ if (ObjectProperty[index].class_oid == class_id)
+ return true;
+ }
+
+ return false;
+}
+
+/*
+ * Find ObjectProperty structure by class_id.
+ */
+static const ObjectPropertyType *
+get_object_property_data(Oid class_id)
+{
+ static const ObjectPropertyType *prop_last = NULL;
+ int index;
+
+ /*
+ * A shortcut to speed up multiple consecutive lookups of a particular
+ * object class.
+ */
+ if (prop_last && prop_last->class_oid == class_id)
+ return prop_last;
+
+ for (index = 0; index < lengthof(ObjectProperty); index++)
+ {
+ if (ObjectProperty[index].class_oid == class_id)
+ {
+ prop_last = &ObjectProperty[index];
+ return &ObjectProperty[index];
+ }
+ }
+
+ ereport(ERROR,
+ (errmsg_internal("unrecognized class ID: %u", class_id)));
+
+ return NULL; /* keep MSC compiler happy */
+}
+
+/*
+ * Return a copy of the tuple for the object with the given object OID, from
+ * the given catalog (which must have been opened by the caller and suitably
+ * locked). NULL is returned if the OID is not found.
+ *
+ * We try a syscache first, if available.
+ */
+HeapTuple
+get_catalog_object_by_oid(Relation catalog, AttrNumber oidcol, Oid objectId)
+{
+ HeapTuple tuple;
+ Oid classId = RelationGetRelid(catalog);
+ int oidCacheId = get_object_catcache_oid(classId);
+
+ if (oidCacheId > 0)
+ {
+ tuple = SearchSysCacheCopy1(oidCacheId, ObjectIdGetDatum(objectId));
+ if (!HeapTupleIsValid(tuple)) /* should not happen */
+ return NULL;
+ }
+ else
+ {
+ Oid oidIndexId = get_object_oid_index(classId);
+ SysScanDesc scan;
+ ScanKeyData skey;
+
+ Assert(OidIsValid(oidIndexId));
+
+ ScanKeyInit(&skey,
+ oidcol,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(objectId));
+
+ scan = systable_beginscan(catalog, oidIndexId, true,
+ NULL, 1, &skey);
+ tuple = systable_getnext(scan);
+ if (!HeapTupleIsValid(tuple))
+ {
+ systable_endscan(scan);
+ return NULL;
+ }
+ tuple = heap_copytuple(tuple);
+
+ systable_endscan(scan);
+ }
+
+ return tuple;
+}
+
+/*
+ * getPublicationSchemaInfo
+ *
+ * Get publication name and schema name from the object address into pubname and
+ * nspname. Both pubname and nspname are palloc'd strings which will be freed by
+ * the caller.
+ */
+static bool
+getPublicationSchemaInfo(const ObjectAddress *object, bool missing_ok,
+ char **pubname, char **nspname)
+{
+ HeapTuple tup;
+ Form_pg_publication_namespace pnform;
+
+ tup = SearchSysCache1(PUBLICATIONNAMESPACE,
+ ObjectIdGetDatum(object->objectId));
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "cache lookup failed for publication schema %u",
+ object->objectId);
+ return false;
+ }
+
+ pnform = (Form_pg_publication_namespace) GETSTRUCT(tup);
+ *pubname = get_publication_name(pnform->pnpubid, missing_ok);
+ if (!(*pubname))
+ {
+ ReleaseSysCache(tup);
+ return false;
+ }
+
+ *nspname = get_namespace_name(pnform->pnnspid);
+ if (!(*nspname))
+ {
+ Oid schemaid = pnform->pnnspid;
+
+ pfree(*pubname);
+ ReleaseSysCache(tup);
+ if (!missing_ok)
+ elog(ERROR, "cache lookup failed for schema %u",
+ schemaid);
+ return false;
+ }
+
+ ReleaseSysCache(tup);
+ return true;
+}
+
+/*
+ * getObjectDescription: build an object description for messages
+ *
+ * The result is a palloc'd string. NULL is returned for an undefined
+ * object if missing_ok is true, else an error is generated.
+ */
+char *
+getObjectDescription(const ObjectAddress *object, bool missing_ok)
+{
+ StringInfoData buffer;
+
+ initStringInfo(&buffer);
+
+ switch (getObjectClass(object))
+ {
+ case OCLASS_CLASS:
+ if (object->objectSubId == 0)
+ getRelationDescription(&buffer, object->objectId, missing_ok);
+ else
+ {
+ /* column, not whole relation */
+ StringInfoData rel;
+ char *attname = get_attname(object->objectId,
+ object->objectSubId,
+ missing_ok);
+
+ if (!attname)
+ break;
+
+ initStringInfo(&rel);
+ getRelationDescription(&rel, object->objectId, missing_ok);
+ /* translator: second %s is, e.g., "table %s" */
+ appendStringInfo(&buffer, _("column %s of %s"),
+ attname, rel.data);
+ pfree(rel.data);
+ }
+ break;
+
+ case OCLASS_PROC:
+ {
+ bits16 flags = FORMAT_PROC_INVALID_AS_NULL;
+ char *proname = format_procedure_extended(object->objectId,
+ flags);
+
+ if (proname == NULL)
+ break;
+
+ appendStringInfo(&buffer, _("function %s"), proname);
+ break;
+ }
+
+ case OCLASS_TYPE:
+ {
+ bits16 flags = FORMAT_TYPE_INVALID_AS_NULL;
+ char *typname = format_type_extended(object->objectId, -1,
+ flags);
+
+ if (typname == NULL)
+ break;
+
+ appendStringInfo(&buffer, _("type %s"), typname);
+ break;
+ }
+
+ case OCLASS_CAST:
+ {
+ Relation castDesc;
+ ScanKeyData skey[1];
+ SysScanDesc rcscan;
+ HeapTuple tup;
+ Form_pg_cast castForm;
+
+ castDesc = table_open(CastRelationId, AccessShareLock);
+
+ ScanKeyInit(&skey[0],
+ Anum_pg_cast_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ rcscan = systable_beginscan(castDesc, CastOidIndexId, true,
+ NULL, 1, skey);
+
+ tup = systable_getnext(rcscan);
+
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for cast %u",
+ object->objectId);
+
+ systable_endscan(rcscan);
+ table_close(castDesc, AccessShareLock);
+ break;
+ }
+
+ castForm = (Form_pg_cast) GETSTRUCT(tup);
+
+ appendStringInfo(&buffer, _("cast from %s to %s"),
+ format_type_be(castForm->castsource),
+ format_type_be(castForm->casttarget));
+
+ systable_endscan(rcscan);
+ table_close(castDesc, AccessShareLock);
+ break;
+ }
+
+ case OCLASS_COLLATION:
+ {
+ HeapTuple collTup;
+ Form_pg_collation coll;
+ char *nspname;
+
+ collTup = SearchSysCache1(COLLOID,
+ ObjectIdGetDatum(object->objectId));
+ if (!HeapTupleIsValid(collTup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "cache lookup failed for collation %u",
+ object->objectId);
+ break;
+ }
+
+ coll = (Form_pg_collation) GETSTRUCT(collTup);
+
+ /* Qualify the name if not visible in search path */
+ if (CollationIsVisible(object->objectId))
+ nspname = NULL;
+ else
+ nspname = get_namespace_name(coll->collnamespace);
+
+ appendStringInfo(&buffer, _("collation %s"),
+ quote_qualified_identifier(nspname,
+ NameStr(coll->collname)));
+ ReleaseSysCache(collTup);
+ break;
+ }
+
+ case OCLASS_CONSTRAINT:
+ {
+ HeapTuple conTup;
+ Form_pg_constraint con;
+
+ conTup = SearchSysCache1(CONSTROID,
+ ObjectIdGetDatum(object->objectId));
+ if (!HeapTupleIsValid(conTup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "cache lookup failed for constraint %u",
+ object->objectId);
+ break;
+ }
+
+ con = (Form_pg_constraint) GETSTRUCT(conTup);
+
+ if (OidIsValid(con->conrelid))
+ {
+ StringInfoData rel;
+
+ initStringInfo(&rel);
+ getRelationDescription(&rel, con->conrelid, false);
+ /* translator: second %s is, e.g., "table %s" */
+ appendStringInfo(&buffer, _("constraint %s on %s"),
+ NameStr(con->conname), rel.data);
+ pfree(rel.data);
+ }
+ else
+ {
+ appendStringInfo(&buffer, _("constraint %s"),
+ NameStr(con->conname));
+ }
+
+ ReleaseSysCache(conTup);
+ break;
+ }
+
+ case OCLASS_CONVERSION:
+ {
+ HeapTuple conTup;
+ Form_pg_conversion conv;
+ char *nspname;
+
+ conTup = SearchSysCache1(CONVOID,
+ ObjectIdGetDatum(object->objectId));
+ if (!HeapTupleIsValid(conTup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "cache lookup failed for conversion %u",
+ object->objectId);
+ break;
+ }
+
+ conv = (Form_pg_conversion) GETSTRUCT(conTup);
+
+ /* Qualify the name if not visible in search path */
+ if (ConversionIsVisible(object->objectId))
+ nspname = NULL;
+ else
+ nspname = get_namespace_name(conv->connamespace);
+
+ appendStringInfo(&buffer, _("conversion %s"),
+ quote_qualified_identifier(nspname,
+ NameStr(conv->conname)));
+ ReleaseSysCache(conTup);
+ break;
+ }
+
+ case OCLASS_DEFAULT:
+ {
+ ObjectAddress colobject;
+
+ colobject = GetAttrDefaultColumnAddress(object->objectId);
+
+ if (!OidIsValid(colobject.objectId))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for attrdef %u",
+ object->objectId);
+ break;
+ }
+
+ /* translator: %s is typically "column %s of table %s" */
+ appendStringInfo(&buffer, _("default value for %s"),
+ getObjectDescription(&colobject, false));
+ break;
+ }
+
+ case OCLASS_LANGUAGE:
+ {
+ char *langname = get_language_name(object->objectId,
+ missing_ok);
+
+ if (langname)
+ appendStringInfo(&buffer, _("language %s"),
+ get_language_name(object->objectId, false));
+ break;
+ }
+
+ case OCLASS_LARGEOBJECT:
+ if (!LargeObjectExists(object->objectId))
+ break;
+ appendStringInfo(&buffer, _("large object %u"),
+ object->objectId);
+ break;
+
+ case OCLASS_OPERATOR:
+ {
+ bits16 flags = FORMAT_OPERATOR_INVALID_AS_NULL;
+ char *oprname = format_operator_extended(object->objectId,
+ flags);
+
+ if (oprname == NULL)
+ break;
+
+ appendStringInfo(&buffer, _("operator %s"), oprname);
+ break;
+ }
+
+ case OCLASS_OPCLASS:
+ {
+ HeapTuple opcTup;
+ Form_pg_opclass opcForm;
+ HeapTuple amTup;
+ Form_pg_am amForm;
+ char *nspname;
+
+ opcTup = SearchSysCache1(CLAOID,
+ ObjectIdGetDatum(object->objectId));
+ if (!HeapTupleIsValid(opcTup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "cache lookup failed for opclass %u",
+ object->objectId);
+ break;
+ }
+
+ opcForm = (Form_pg_opclass) GETSTRUCT(opcTup);
+
+ amTup = SearchSysCache1(AMOID,
+ ObjectIdGetDatum(opcForm->opcmethod));
+ if (!HeapTupleIsValid(amTup))
+ elog(ERROR, "cache lookup failed for access method %u",
+ opcForm->opcmethod);
+ amForm = (Form_pg_am) GETSTRUCT(amTup);
+
+ /* Qualify the name if not visible in search path */
+ if (OpclassIsVisible(object->objectId))
+ nspname = NULL;
+ else
+ nspname = get_namespace_name(opcForm->opcnamespace);
+
+ appendStringInfo(&buffer, _("operator class %s for access method %s"),
+ quote_qualified_identifier(nspname,
+ NameStr(opcForm->opcname)),
+ NameStr(amForm->amname));
+
+ ReleaseSysCache(amTup);
+ ReleaseSysCache(opcTup);
+ break;
+ }
+
+ case OCLASS_OPFAMILY:
+ getOpFamilyDescription(&buffer, object->objectId, missing_ok);
+ break;
+
+ case OCLASS_AM:
+ {
+ HeapTuple tup;
+
+ tup = SearchSysCache1(AMOID,
+ ObjectIdGetDatum(object->objectId));
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "cache lookup failed for access method %u",
+ object->objectId);
+ break;
+ }
+
+ appendStringInfo(&buffer, _("access method %s"),
+ NameStr(((Form_pg_am) GETSTRUCT(tup))->amname));
+ ReleaseSysCache(tup);
+ break;
+ }
+
+ case OCLASS_AMOP:
+ {
+ Relation amopDesc;
+ HeapTuple tup;
+ ScanKeyData skey[1];
+ SysScanDesc amscan;
+ Form_pg_amop amopForm;
+ StringInfoData opfam;
+
+ amopDesc = table_open(AccessMethodOperatorRelationId,
+ AccessShareLock);
+
+ ScanKeyInit(&skey[0],
+ Anum_pg_amop_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ amscan = systable_beginscan(amopDesc, AccessMethodOperatorOidIndexId, true,
+ NULL, 1, skey);
+
+ tup = systable_getnext(amscan);
+
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for amop entry %u",
+ object->objectId);
+
+ systable_endscan(amscan);
+ table_close(amopDesc, AccessShareLock);
+ break;
+ }
+
+ amopForm = (Form_pg_amop) GETSTRUCT(tup);
+
+ initStringInfo(&opfam);
+ getOpFamilyDescription(&opfam, amopForm->amopfamily, false);
+
+ /*------
+ translator: %d is the operator strategy (a number), the
+ first two %s's are data type names, the third %s is the
+ description of the operator family, and the last %s is the
+ textual form of the operator with arguments. */
+ appendStringInfo(&buffer, _("operator %d (%s, %s) of %s: %s"),
+ amopForm->amopstrategy,
+ format_type_be(amopForm->amoplefttype),
+ format_type_be(amopForm->amoprighttype),
+ opfam.data,
+ format_operator(amopForm->amopopr));
+
+ pfree(opfam.data);
+
+ systable_endscan(amscan);
+ table_close(amopDesc, AccessShareLock);
+ break;
+ }
+
+ case OCLASS_AMPROC:
+ {
+ Relation amprocDesc;
+ ScanKeyData skey[1];
+ SysScanDesc amscan;
+ HeapTuple tup;
+ Form_pg_amproc amprocForm;
+ StringInfoData opfam;
+
+ amprocDesc = table_open(AccessMethodProcedureRelationId,
+ AccessShareLock);
+
+ ScanKeyInit(&skey[0],
+ Anum_pg_amproc_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ amscan = systable_beginscan(amprocDesc, AccessMethodProcedureOidIndexId, true,
+ NULL, 1, skey);
+
+ tup = systable_getnext(amscan);
+
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for amproc entry %u",
+ object->objectId);
+
+ systable_endscan(amscan);
+ table_close(amprocDesc, AccessShareLock);
+ break;
+ }
+
+ amprocForm = (Form_pg_amproc) GETSTRUCT(tup);
+
+ initStringInfo(&opfam);
+ getOpFamilyDescription(&opfam, amprocForm->amprocfamily, false);
+
+ /*------
+ translator: %d is the function number, the first two %s's
+ are data type names, the third %s is the description of the
+ operator family, and the last %s is the textual form of the
+ function with arguments. */
+ appendStringInfo(&buffer, _("function %d (%s, %s) of %s: %s"),
+ amprocForm->amprocnum,
+ format_type_be(amprocForm->amproclefttype),
+ format_type_be(amprocForm->amprocrighttype),
+ opfam.data,
+ format_procedure(amprocForm->amproc));
+
+ pfree(opfam.data);
+
+ systable_endscan(amscan);
+ table_close(amprocDesc, AccessShareLock);
+ break;
+ }
+
+ case OCLASS_REWRITE:
+ {
+ Relation ruleDesc;
+ ScanKeyData skey[1];
+ SysScanDesc rcscan;
+ HeapTuple tup;
+ Form_pg_rewrite rule;
+ StringInfoData rel;
+
+ ruleDesc = table_open(RewriteRelationId, AccessShareLock);
+
+ ScanKeyInit(&skey[0],
+ Anum_pg_rewrite_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ rcscan = systable_beginscan(ruleDesc, RewriteOidIndexId, true,
+ NULL, 1, skey);
+
+ tup = systable_getnext(rcscan);
+
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for rule %u",
+ object->objectId);
+
+ systable_endscan(rcscan);
+ table_close(ruleDesc, AccessShareLock);
+ break;
+ }
+
+ rule = (Form_pg_rewrite) GETSTRUCT(tup);
+
+ initStringInfo(&rel);
+ getRelationDescription(&rel, rule->ev_class, false);
+
+ /* translator: second %s is, e.g., "table %s" */
+ appendStringInfo(&buffer, _("rule %s on %s"),
+ NameStr(rule->rulename), rel.data);
+ pfree(rel.data);
+ systable_endscan(rcscan);
+ table_close(ruleDesc, AccessShareLock);
+ break;
+ }
+
+ case OCLASS_TRIGGER:
+ {
+ Relation trigDesc;
+ ScanKeyData skey[1];
+ SysScanDesc tgscan;
+ HeapTuple tup;
+ Form_pg_trigger trig;
+ StringInfoData rel;
+
+ trigDesc = table_open(TriggerRelationId, AccessShareLock);
+
+ ScanKeyInit(&skey[0],
+ Anum_pg_trigger_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ tgscan = systable_beginscan(trigDesc, TriggerOidIndexId, true,
+ NULL, 1, skey);
+
+ tup = systable_getnext(tgscan);
+
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for trigger %u",
+ object->objectId);
+
+ systable_endscan(tgscan);
+ table_close(trigDesc, AccessShareLock);
+ break;
+ }
+
+ trig = (Form_pg_trigger) GETSTRUCT(tup);
+
+ initStringInfo(&rel);
+ getRelationDescription(&rel, trig->tgrelid, false);
+
+ /* translator: second %s is, e.g., "table %s" */
+ appendStringInfo(&buffer, _("trigger %s on %s"),
+ NameStr(trig->tgname), rel.data);
+ pfree(rel.data);
+ systable_endscan(tgscan);
+ table_close(trigDesc, AccessShareLock);
+ break;
+ }
+
+ case OCLASS_SCHEMA:
+ {
+ char *nspname;
+
+ nspname = get_namespace_name(object->objectId);
+ if (!nspname)
+ {
+ if (!missing_ok)
+ elog(ERROR, "cache lookup failed for namespace %u",
+ object->objectId);
+ break;
+ }
+ appendStringInfo(&buffer, _("schema %s"), nspname);
+ break;
+ }
+
+ case OCLASS_STATISTIC_EXT:
+ {
+ HeapTuple stxTup;
+ Form_pg_statistic_ext stxForm;
+ char *nspname;
+
+ stxTup = SearchSysCache1(STATEXTOID,
+ ObjectIdGetDatum(object->objectId));
+ if (!HeapTupleIsValid(stxTup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for statistics object %u",
+ object->objectId);
+ break;
+ }
+
+ stxForm = (Form_pg_statistic_ext) GETSTRUCT(stxTup);
+
+ /* Qualify the name if not visible in search path */
+ if (StatisticsObjIsVisible(object->objectId))
+ nspname = NULL;
+ else
+ nspname = get_namespace_name(stxForm->stxnamespace);
+
+ appendStringInfo(&buffer, _("statistics object %s"),
+ quote_qualified_identifier(nspname,
+ NameStr(stxForm->stxname)));
+
+ ReleaseSysCache(stxTup);
+ break;
+ }
+
+ case OCLASS_TSPARSER:
+ {
+ HeapTuple tup;
+ Form_pg_ts_parser prsForm;
+ char *nspname;
+
+ tup = SearchSysCache1(TSPARSEROID,
+ ObjectIdGetDatum(object->objectId));
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "cache lookup failed for text search parser %u",
+ object->objectId);
+ break;
+ }
+ prsForm = (Form_pg_ts_parser) GETSTRUCT(tup);
+
+ /* Qualify the name if not visible in search path */
+ if (TSParserIsVisible(object->objectId))
+ nspname = NULL;
+ else
+ nspname = get_namespace_name(prsForm->prsnamespace);
+
+ appendStringInfo(&buffer, _("text search parser %s"),
+ quote_qualified_identifier(nspname,
+ NameStr(prsForm->prsname)));
+ ReleaseSysCache(tup);
+ break;
+ }
+
+ case OCLASS_TSDICT:
+ {
+ HeapTuple tup;
+ Form_pg_ts_dict dictForm;
+ char *nspname;
+
+ tup = SearchSysCache1(TSDICTOID,
+ ObjectIdGetDatum(object->objectId));
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "cache lookup failed for text search dictionary %u",
+ object->objectId);
+ break;
+ }
+
+ dictForm = (Form_pg_ts_dict) GETSTRUCT(tup);
+
+ /* Qualify the name if not visible in search path */
+ if (TSDictionaryIsVisible(object->objectId))
+ nspname = NULL;
+ else
+ nspname = get_namespace_name(dictForm->dictnamespace);
+
+ appendStringInfo(&buffer, _("text search dictionary %s"),
+ quote_qualified_identifier(nspname,
+ NameStr(dictForm->dictname)));
+ ReleaseSysCache(tup);
+ break;
+ }
+
+ case OCLASS_TSTEMPLATE:
+ {
+ HeapTuple tup;
+ Form_pg_ts_template tmplForm;
+ char *nspname;
+
+ tup = SearchSysCache1(TSTEMPLATEOID,
+ ObjectIdGetDatum(object->objectId));
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "cache lookup failed for text search template %u",
+ object->objectId);
+ break;
+ }
+
+ tmplForm = (Form_pg_ts_template) GETSTRUCT(tup);
+
+ /* Qualify the name if not visible in search path */
+ if (TSTemplateIsVisible(object->objectId))
+ nspname = NULL;
+ else
+ nspname = get_namespace_name(tmplForm->tmplnamespace);
+
+ appendStringInfo(&buffer, _("text search template %s"),
+ quote_qualified_identifier(nspname,
+ NameStr(tmplForm->tmplname)));
+ ReleaseSysCache(tup);
+ break;
+ }
+
+ case OCLASS_TSCONFIG:
+ {
+ HeapTuple tup;
+ Form_pg_ts_config cfgForm;
+ char *nspname;
+
+ tup = SearchSysCache1(TSCONFIGOID,
+ ObjectIdGetDatum(object->objectId));
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "cache lookup failed for text search configuration %u",
+ object->objectId);
+ break;
+ }
+
+ cfgForm = (Form_pg_ts_config) GETSTRUCT(tup);
+
+ /* Qualify the name if not visible in search path */
+ if (TSConfigIsVisible(object->objectId))
+ nspname = NULL;
+ else
+ nspname = get_namespace_name(cfgForm->cfgnamespace);
+
+ appendStringInfo(&buffer, _("text search configuration %s"),
+ quote_qualified_identifier(nspname,
+ NameStr(cfgForm->cfgname)));
+ ReleaseSysCache(tup);
+ break;
+ }
+
+ case OCLASS_ROLE:
+ {
+ char *username = GetUserNameFromId(object->objectId,
+ missing_ok);
+
+ if (username)
+ appendStringInfo(&buffer, _("role %s"), username);
+ break;
+ }
+
+ case OCLASS_DATABASE:
+ {
+ char *datname;
+
+ datname = get_database_name(object->objectId);
+ if (!datname)
+ {
+ if (!missing_ok)
+ elog(ERROR, "cache lookup failed for database %u",
+ object->objectId);
+ break;
+ }
+ appendStringInfo(&buffer, _("database %s"), datname);
+ break;
+ }
+
+ case OCLASS_TBLSPACE:
+ {
+ char *tblspace;
+
+ tblspace = get_tablespace_name(object->objectId);
+ if (!tblspace)
+ {
+ if (!missing_ok)
+ elog(ERROR, "cache lookup failed for tablespace %u",
+ object->objectId);
+ break;
+ }
+ appendStringInfo(&buffer, _("tablespace %s"), tblspace);
+ break;
+ }
+
+ case OCLASS_FDW:
+ {
+ ForeignDataWrapper *fdw;
+
+ fdw = GetForeignDataWrapperExtended(object->objectId,
+ missing_ok);
+ if (fdw)
+ appendStringInfo(&buffer, _("foreign-data wrapper %s"), fdw->fdwname);
+ break;
+ }
+
+ case OCLASS_FOREIGN_SERVER:
+ {
+ ForeignServer *srv;
+
+ srv = GetForeignServerExtended(object->objectId, missing_ok);
+ if (srv)
+ appendStringInfo(&buffer, _("server %s"), srv->servername);
+ break;
+ }
+
+ case OCLASS_USER_MAPPING:
+ {
+ HeapTuple tup;
+ Oid useid;
+ char *usename;
+ Form_pg_user_mapping umform;
+ ForeignServer *srv;
+
+ tup = SearchSysCache1(USERMAPPINGOID,
+ ObjectIdGetDatum(object->objectId));
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "cache lookup failed for user mapping %u",
+ object->objectId);
+ break;
+ }
+
+ umform = (Form_pg_user_mapping) GETSTRUCT(tup);
+ useid = umform->umuser;
+ srv = GetForeignServer(umform->umserver);
+
+ ReleaseSysCache(tup);
+
+ if (OidIsValid(useid))
+ usename = GetUserNameFromId(useid, false);
+ else
+ usename = "public";
+
+ appendStringInfo(&buffer, _("user mapping for %s on server %s"), usename,
+ srv->servername);
+ break;
+ }
+
+ case OCLASS_DEFACL:
+ {
+ Relation defaclrel;
+ ScanKeyData skey[1];
+ SysScanDesc rcscan;
+ HeapTuple tup;
+ Form_pg_default_acl defacl;
+ char *rolename;
+ char *nspname;
+
+ defaclrel = table_open(DefaultAclRelationId, AccessShareLock);
+
+ ScanKeyInit(&skey[0],
+ Anum_pg_default_acl_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ rcscan = systable_beginscan(defaclrel, DefaultAclOidIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(rcscan);
+
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for default ACL %u",
+ object->objectId);
+
+ systable_endscan(rcscan);
+ table_close(defaclrel, AccessShareLock);
+ break;
+ }
+
+ defacl = (Form_pg_default_acl) GETSTRUCT(tup);
+
+ rolename = GetUserNameFromId(defacl->defaclrole, false);
+
+ if (OidIsValid(defacl->defaclnamespace))
+ nspname = get_namespace_name(defacl->defaclnamespace);
+ else
+ nspname = NULL;
+
+ switch (defacl->defaclobjtype)
+ {
+ case DEFACLOBJ_RELATION:
+ if (nspname)
+ appendStringInfo(&buffer,
+ _("default privileges on new relations belonging to role %s in schema %s"),
+ rolename, nspname);
+ else
+ appendStringInfo(&buffer,
+ _("default privileges on new relations belonging to role %s"),
+ rolename);
+ break;
+ case DEFACLOBJ_SEQUENCE:
+ if (nspname)
+ appendStringInfo(&buffer,
+ _("default privileges on new sequences belonging to role %s in schema %s"),
+ rolename, nspname);
+ else
+ appendStringInfo(&buffer,
+ _("default privileges on new sequences belonging to role %s"),
+ rolename);
+ break;
+ case DEFACLOBJ_FUNCTION:
+ if (nspname)
+ appendStringInfo(&buffer,
+ _("default privileges on new functions belonging to role %s in schema %s"),
+ rolename, nspname);
+ else
+ appendStringInfo(&buffer,
+ _("default privileges on new functions belonging to role %s"),
+ rolename);
+ break;
+ case DEFACLOBJ_TYPE:
+ if (nspname)
+ appendStringInfo(&buffer,
+ _("default privileges on new types belonging to role %s in schema %s"),
+ rolename, nspname);
+ else
+ appendStringInfo(&buffer,
+ _("default privileges on new types belonging to role %s"),
+ rolename);
+ break;
+ case DEFACLOBJ_NAMESPACE:
+ Assert(!nspname);
+ appendStringInfo(&buffer,
+ _("default privileges on new schemas belonging to role %s"),
+ rolename);
+ break;
+ default:
+ /* shouldn't get here */
+ if (nspname)
+ appendStringInfo(&buffer,
+ _("default privileges belonging to role %s in schema %s"),
+ rolename, nspname);
+ else
+ appendStringInfo(&buffer,
+ _("default privileges belonging to role %s"),
+ rolename);
+ break;
+ }
+
+ systable_endscan(rcscan);
+ table_close(defaclrel, AccessShareLock);
+ break;
+ }
+
+ case OCLASS_EXTENSION:
+ {
+ char *extname;
+
+ extname = get_extension_name(object->objectId);
+ if (!extname)
+ {
+ if (!missing_ok)
+ elog(ERROR, "cache lookup failed for extension %u",
+ object->objectId);
+ break;
+ }
+ appendStringInfo(&buffer, _("extension %s"), extname);
+ break;
+ }
+
+ case OCLASS_EVENT_TRIGGER:
+ {
+ HeapTuple tup;
+
+ tup = SearchSysCache1(EVENTTRIGGEROID,
+ ObjectIdGetDatum(object->objectId));
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "cache lookup failed for event trigger %u",
+ object->objectId);
+ break;
+ }
+ appendStringInfo(&buffer, _("event trigger %s"),
+ NameStr(((Form_pg_event_trigger) GETSTRUCT(tup))->evtname));
+ ReleaseSysCache(tup);
+ break;
+ }
+
+ case OCLASS_PARAMETER_ACL:
+ {
+ HeapTuple tup;
+ Datum nameDatum;
+ bool isNull;
+ char *parname;
+
+ tup = SearchSysCache1(PARAMETERACLOID,
+ ObjectIdGetDatum(object->objectId));
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "cache lookup failed for parameter ACL %u",
+ object->objectId);
+ break;
+ }
+ nameDatum = SysCacheGetAttr(PARAMETERACLOID, tup,
+ Anum_pg_parameter_acl_parname,
+ &isNull);
+ Assert(!isNull);
+ parname = TextDatumGetCString(nameDatum);
+ appendStringInfo(&buffer, _("parameter %s"), parname);
+ ReleaseSysCache(tup);
+ break;
+ }
+
+ case OCLASS_POLICY:
+ {
+ Relation policy_rel;
+ ScanKeyData skey[1];
+ SysScanDesc sscan;
+ HeapTuple tuple;
+ Form_pg_policy form_policy;
+ StringInfoData rel;
+
+ policy_rel = table_open(PolicyRelationId, AccessShareLock);
+
+ ScanKeyInit(&skey[0],
+ Anum_pg_policy_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ sscan = systable_beginscan(policy_rel, PolicyOidIndexId,
+ true, NULL, 1, skey);
+
+ tuple = systable_getnext(sscan);
+
+ if (!HeapTupleIsValid(tuple))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for policy %u",
+ object->objectId);
+
+ systable_endscan(sscan);
+ table_close(policy_rel, AccessShareLock);
+ break;
+ }
+
+ form_policy = (Form_pg_policy) GETSTRUCT(tuple);
+
+ initStringInfo(&rel);
+ getRelationDescription(&rel, form_policy->polrelid, false);
+
+ /* translator: second %s is, e.g., "table %s" */
+ appendStringInfo(&buffer, _("policy %s on %s"),
+ NameStr(form_policy->polname), rel.data);
+ pfree(rel.data);
+ systable_endscan(sscan);
+ table_close(policy_rel, AccessShareLock);
+ break;
+ }
+
+ case OCLASS_PUBLICATION:
+ {
+ char *pubname = get_publication_name(object->objectId,
+ missing_ok);
+
+ if (pubname)
+ appendStringInfo(&buffer, _("publication %s"), pubname);
+ break;
+ }
+
+ case OCLASS_PUBLICATION_NAMESPACE:
+ {
+ char *pubname;
+ char *nspname;
+
+ if (!getPublicationSchemaInfo(object, missing_ok,
+ &pubname, &nspname))
+ break;
+
+ appendStringInfo(&buffer, _("publication of schema %s in publication %s"),
+ nspname, pubname);
+ pfree(pubname);
+ pfree(nspname);
+ break;
+ }
+
+ case OCLASS_PUBLICATION_REL:
+ {
+ HeapTuple tup;
+ char *pubname;
+ Form_pg_publication_rel prform;
+ StringInfoData rel;
+
+ tup = SearchSysCache1(PUBLICATIONREL,
+ ObjectIdGetDatum(object->objectId));
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "cache lookup failed for publication table %u",
+ object->objectId);
+ break;
+ }
+
+ prform = (Form_pg_publication_rel) GETSTRUCT(tup);
+ pubname = get_publication_name(prform->prpubid, false);
+
+ initStringInfo(&rel);
+ getRelationDescription(&rel, prform->prrelid, false);
+
+ /* translator: first %s is, e.g., "table %s" */
+ appendStringInfo(&buffer, _("publication of %s in publication %s"),
+ rel.data, pubname);
+ pfree(rel.data);
+ ReleaseSysCache(tup);
+ break;
+ }
+
+ case OCLASS_SUBSCRIPTION:
+ {
+ char *subname = get_subscription_name(object->objectId,
+ missing_ok);
+
+ if (subname)
+ appendStringInfo(&buffer, _("subscription %s"), subname);
+ break;
+ }
+
+ case OCLASS_TRANSFORM:
+ {
+ HeapTuple trfTup;
+ Form_pg_transform trfForm;
+
+ trfTup = SearchSysCache1(TRFOID,
+ ObjectIdGetDatum(object->objectId));
+ if (!HeapTupleIsValid(trfTup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for transform %u",
+ object->objectId);
+ break;
+ }
+
+ trfForm = (Form_pg_transform) GETSTRUCT(trfTup);
+
+ appendStringInfo(&buffer, _("transform for %s language %s"),
+ format_type_be(trfForm->trftype),
+ get_language_name(trfForm->trflang, false));
+
+ ReleaseSysCache(trfTup);
+ break;
+ }
+
+ /*
+ * There's intentionally no default: case here; we want the
+ * compiler to warn if a new OCLASS hasn't been handled above.
+ */
+ }
+
+ /* an empty buffer is equivalent to no object found */
+ if (buffer.len == 0)
+ return NULL;
+
+ return buffer.data;
+}
+
+/*
+ * getObjectDescriptionOids: as above, except the object is specified by Oids
+ */
+char *
+getObjectDescriptionOids(Oid classid, Oid objid)
+{
+ ObjectAddress address;
+
+ address.classId = classid;
+ address.objectId = objid;
+ address.objectSubId = 0;
+
+ return getObjectDescription(&address, false);
+}
+
+/*
+ * subroutine for getObjectDescription: describe a relation
+ *
+ * The result is appended to "buffer".
+ */
+static void
+getRelationDescription(StringInfo buffer, Oid relid, bool missing_ok)
+{
+ HeapTuple relTup;
+ Form_pg_class relForm;
+ char *nspname;
+ char *relname;
+
+ relTup = SearchSysCache1(RELOID,
+ ObjectIdGetDatum(relid));
+ if (!HeapTupleIsValid(relTup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "cache lookup failed for relation %u", relid);
+ return;
+ }
+ relForm = (Form_pg_class) GETSTRUCT(relTup);
+
+ /* Qualify the name if not visible in search path */
+ if (RelationIsVisible(relid))
+ nspname = NULL;
+ else
+ nspname = get_namespace_name(relForm->relnamespace);
+
+ relname = quote_qualified_identifier(nspname, NameStr(relForm->relname));
+
+ switch (relForm->relkind)
+ {
+ case RELKIND_RELATION:
+ case RELKIND_PARTITIONED_TABLE:
+ appendStringInfo(buffer, _("table %s"),
+ relname);
+ break;
+ case RELKIND_INDEX:
+ case RELKIND_PARTITIONED_INDEX:
+ appendStringInfo(buffer, _("index %s"),
+ relname);
+ break;
+ case RELKIND_SEQUENCE:
+ appendStringInfo(buffer, _("sequence %s"),
+ relname);
+ break;
+ case RELKIND_TOASTVALUE:
+ appendStringInfo(buffer, _("toast table %s"),
+ relname);
+ break;
+ case RELKIND_VIEW:
+ appendStringInfo(buffer, _("view %s"),
+ relname);
+ break;
+ case RELKIND_MATVIEW:
+ appendStringInfo(buffer, _("materialized view %s"),
+ relname);
+ break;
+ case RELKIND_COMPOSITE_TYPE:
+ appendStringInfo(buffer, _("composite type %s"),
+ relname);
+ break;
+ case RELKIND_FOREIGN_TABLE:
+ appendStringInfo(buffer, _("foreign table %s"),
+ relname);
+ break;
+ default:
+ /* shouldn't get here */
+ appendStringInfo(buffer, _("relation %s"),
+ relname);
+ break;
+ }
+
+ ReleaseSysCache(relTup);
+}
+
+/*
+ * subroutine for getObjectDescription: describe an operator family
+ */
+static void
+getOpFamilyDescription(StringInfo buffer, Oid opfid, bool missing_ok)
+{
+ HeapTuple opfTup;
+ Form_pg_opfamily opfForm;
+ HeapTuple amTup;
+ Form_pg_am amForm;
+ char *nspname;
+
+ opfTup = SearchSysCache1(OPFAMILYOID, ObjectIdGetDatum(opfid));
+ if (!HeapTupleIsValid(opfTup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "cache lookup failed for opfamily %u", opfid);
+ return;
+ }
+ opfForm = (Form_pg_opfamily) GETSTRUCT(opfTup);
+
+ amTup = SearchSysCache1(AMOID, ObjectIdGetDatum(opfForm->opfmethod));
+ if (!HeapTupleIsValid(amTup))
+ elog(ERROR, "cache lookup failed for access method %u",
+ opfForm->opfmethod);
+ amForm = (Form_pg_am) GETSTRUCT(amTup);
+
+ /* Qualify the name if not visible in search path */
+ if (OpfamilyIsVisible(opfid))
+ nspname = NULL;
+ else
+ nspname = get_namespace_name(opfForm->opfnamespace);
+
+ appendStringInfo(buffer, _("operator family %s for access method %s"),
+ quote_qualified_identifier(nspname,
+ NameStr(opfForm->opfname)),
+ NameStr(amForm->amname));
+
+ ReleaseSysCache(amTup);
+ ReleaseSysCache(opfTup);
+}
+
+/*
+ * SQL-level callable version of getObjectDescription
+ */
+Datum
+pg_describe_object(PG_FUNCTION_ARGS)
+{
+ Oid classid = PG_GETARG_OID(0);
+ Oid objid = PG_GETARG_OID(1);
+ int32 objsubid = PG_GETARG_INT32(2);
+ char *description;
+ ObjectAddress address;
+
+ /* for "pinned" items in pg_depend, return null */
+ if (!OidIsValid(classid) && !OidIsValid(objid))
+ PG_RETURN_NULL();
+
+ address.classId = classid;
+ address.objectId = objid;
+ address.objectSubId = objsubid;
+
+ description = getObjectDescription(&address, true);
+
+ if (description == NULL)
+ PG_RETURN_NULL();
+
+ PG_RETURN_TEXT_P(cstring_to_text(description));
+}
+
+/*
+ * SQL-level callable function to obtain object type + identity
+ */
+Datum
+pg_identify_object(PG_FUNCTION_ARGS)
+{
+ Oid classid = PG_GETARG_OID(0);
+ Oid objid = PG_GETARG_OID(1);
+ int32 objsubid = PG_GETARG_INT32(2);
+ Oid schema_oid = InvalidOid;
+ const char *objname = NULL;
+ char *objidentity;
+ ObjectAddress address;
+ Datum values[4];
+ bool nulls[4];
+ TupleDesc tupdesc;
+ HeapTuple htup;
+
+ address.classId = classid;
+ address.objectId = objid;
+ address.objectSubId = objsubid;
+
+ /*
+ * Construct a tuple descriptor for the result row. This must match this
+ * function's pg_proc entry!
+ */
+ tupdesc = CreateTemplateTupleDesc(4);
+ TupleDescInitEntry(tupdesc, (AttrNumber) 1, "type",
+ TEXTOID, -1, 0);
+ TupleDescInitEntry(tupdesc, (AttrNumber) 2, "schema",
+ TEXTOID, -1, 0);
+ TupleDescInitEntry(tupdesc, (AttrNumber) 3, "name",
+ TEXTOID, -1, 0);
+ TupleDescInitEntry(tupdesc, (AttrNumber) 4, "identity",
+ TEXTOID, -1, 0);
+
+ tupdesc = BlessTupleDesc(tupdesc);
+
+ if (is_objectclass_supported(address.classId))
+ {
+ HeapTuple objtup;
+ Relation catalog = table_open(address.classId, AccessShareLock);
+
+ objtup = get_catalog_object_by_oid(catalog,
+ get_object_attnum_oid(address.classId),
+ address.objectId);
+ if (objtup != NULL)
+ {
+ bool isnull;
+ AttrNumber nspAttnum;
+ AttrNumber nameAttnum;
+
+ nspAttnum = get_object_attnum_namespace(address.classId);
+ if (nspAttnum != InvalidAttrNumber)
+ {
+ schema_oid = heap_getattr(objtup, nspAttnum,
+ RelationGetDescr(catalog), &isnull);
+ if (isnull)
+ elog(ERROR, "invalid null namespace in object %u/%u/%d",
+ address.classId, address.objectId, address.objectSubId);
+ }
+
+ /*
+ * We only return the object name if it can be used (together with
+ * the schema name, if any) as a unique identifier.
+ */
+ if (get_object_namensp_unique(address.classId))
+ {
+ nameAttnum = get_object_attnum_name(address.classId);
+ if (nameAttnum != InvalidAttrNumber)
+ {
+ Datum nameDatum;
+
+ nameDatum = heap_getattr(objtup, nameAttnum,
+ RelationGetDescr(catalog), &isnull);
+ if (isnull)
+ elog(ERROR, "invalid null name in object %u/%u/%d",
+ address.classId, address.objectId, address.objectSubId);
+ objname = quote_identifier(NameStr(*(DatumGetName(nameDatum))));
+ }
+ }
+ }
+
+ table_close(catalog, AccessShareLock);
+ }
+
+ /* object type, which can never be NULL */
+ values[0] = CStringGetTextDatum(getObjectTypeDescription(&address, true));
+ nulls[0] = false;
+
+ /*
+ * Before doing anything, extract the object identity. If the identity
+ * could not be found, set all the fields except the object type to NULL.
+ */
+ objidentity = getObjectIdentity(&address, true);
+
+ /* schema name */
+ if (OidIsValid(schema_oid) && objidentity)
+ {
+ const char *schema = quote_identifier(get_namespace_name(schema_oid));
+
+ values[1] = CStringGetTextDatum(schema);
+ nulls[1] = false;
+ }
+ else
+ nulls[1] = true;
+
+ /* object name */
+ if (objname && objidentity)
+ {
+ values[2] = CStringGetTextDatum(objname);
+ nulls[2] = false;
+ }
+ else
+ nulls[2] = true;
+
+ /* object identity */
+ if (objidentity)
+ {
+ values[3] = CStringGetTextDatum(objidentity);
+ nulls[3] = false;
+ }
+ else
+ nulls[3] = true;
+
+ htup = heap_form_tuple(tupdesc, values, nulls);
+
+ PG_RETURN_DATUM(HeapTupleGetDatum(htup));
+}
+
+/*
+ * SQL-level callable function to obtain object type + identity
+ */
+Datum
+pg_identify_object_as_address(PG_FUNCTION_ARGS)
+{
+ Oid classid = PG_GETARG_OID(0);
+ Oid objid = PG_GETARG_OID(1);
+ int32 objsubid = PG_GETARG_INT32(2);
+ ObjectAddress address;
+ char *identity;
+ List *names;
+ List *args;
+ Datum values[3];
+ bool nulls[3];
+ TupleDesc tupdesc;
+ HeapTuple htup;
+
+ address.classId = classid;
+ address.objectId = objid;
+ address.objectSubId = objsubid;
+
+ /*
+ * Construct a tuple descriptor for the result row. This must match this
+ * function's pg_proc entry!
+ */
+ tupdesc = CreateTemplateTupleDesc(3);
+ TupleDescInitEntry(tupdesc, (AttrNumber) 1, "type",
+ TEXTOID, -1, 0);
+ TupleDescInitEntry(tupdesc, (AttrNumber) 2, "object_names",
+ TEXTARRAYOID, -1, 0);
+ TupleDescInitEntry(tupdesc, (AttrNumber) 3, "object_args",
+ TEXTARRAYOID, -1, 0);
+
+ tupdesc = BlessTupleDesc(tupdesc);
+
+ /* object type, which can never be NULL */
+ values[0] = CStringGetTextDatum(getObjectTypeDescription(&address, true));
+ nulls[0] = false;
+
+ /* object identity */
+ identity = getObjectIdentityParts(&address, &names, &args, true);
+ if (identity == NULL)
+ {
+ nulls[1] = true;
+ nulls[2] = true;
+ }
+ else
+ {
+ pfree(identity);
+
+ /* object_names */
+ if (names != NIL)
+ values[1] = PointerGetDatum(strlist_to_textarray(names));
+ else
+ values[1] = PointerGetDatum(construct_empty_array(TEXTOID));
+ nulls[1] = false;
+
+ /* object_args */
+ if (args)
+ values[2] = PointerGetDatum(strlist_to_textarray(args));
+ else
+ values[2] = PointerGetDatum(construct_empty_array(TEXTOID));
+ nulls[2] = false;
+ }
+
+ htup = heap_form_tuple(tupdesc, values, nulls);
+
+ PG_RETURN_DATUM(HeapTupleGetDatum(htup));
+}
+
+/*
+ * 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, bool missing_ok)
+{
+ StringInfoData buffer;
+
+ initStringInfo(&buffer);
+
+ switch (getObjectClass(object))
+ {
+ case OCLASS_CLASS:
+ getRelationTypeDescription(&buffer, object->objectId,
+ object->objectSubId,
+ missing_ok);
+ break;
+
+ case OCLASS_PROC:
+ getProcedureTypeDescription(&buffer, object->objectId,
+ missing_ok);
+ break;
+
+ case OCLASS_TYPE:
+ appendStringInfoString(&buffer, "type");
+ break;
+
+ case OCLASS_CAST:
+ appendStringInfoString(&buffer, "cast");
+ break;
+
+ case OCLASS_COLLATION:
+ appendStringInfoString(&buffer, "collation");
+ break;
+
+ case OCLASS_CONSTRAINT:
+ getConstraintTypeDescription(&buffer, object->objectId,
+ missing_ok);
+ break;
+
+ case OCLASS_CONVERSION:
+ appendStringInfoString(&buffer, "conversion");
+ break;
+
+ case OCLASS_DEFAULT:
+ appendStringInfoString(&buffer, "default value");
+ break;
+
+ case OCLASS_LANGUAGE:
+ appendStringInfoString(&buffer, "language");
+ break;
+
+ case OCLASS_LARGEOBJECT:
+ appendStringInfoString(&buffer, "large object");
+ break;
+
+ case OCLASS_OPERATOR:
+ appendStringInfoString(&buffer, "operator");
+ break;
+
+ case OCLASS_OPCLASS:
+ appendStringInfoString(&buffer, "operator class");
+ break;
+
+ case OCLASS_OPFAMILY:
+ appendStringInfoString(&buffer, "operator family");
+ break;
+
+ case OCLASS_AM:
+ appendStringInfoString(&buffer, "access method");
+ break;
+
+ case OCLASS_AMOP:
+ appendStringInfoString(&buffer, "operator of access method");
+ break;
+
+ case OCLASS_AMPROC:
+ appendStringInfoString(&buffer, "function of access method");
+ break;
+
+ case OCLASS_REWRITE:
+ appendStringInfoString(&buffer, "rule");
+ break;
+
+ case OCLASS_TRIGGER:
+ appendStringInfoString(&buffer, "trigger");
+ break;
+
+ case OCLASS_SCHEMA:
+ appendStringInfoString(&buffer, "schema");
+ break;
+
+ case OCLASS_STATISTIC_EXT:
+ appendStringInfoString(&buffer, "statistics object");
+ break;
+
+ case OCLASS_TSPARSER:
+ appendStringInfoString(&buffer, "text search parser");
+ break;
+
+ case OCLASS_TSDICT:
+ appendStringInfoString(&buffer, "text search dictionary");
+ break;
+
+ case OCLASS_TSTEMPLATE:
+ appendStringInfoString(&buffer, "text search template");
+ break;
+
+ case OCLASS_TSCONFIG:
+ appendStringInfoString(&buffer, "text search configuration");
+ break;
+
+ case OCLASS_ROLE:
+ appendStringInfoString(&buffer, "role");
+ break;
+
+ case OCLASS_DATABASE:
+ appendStringInfoString(&buffer, "database");
+ break;
+
+ case OCLASS_TBLSPACE:
+ appendStringInfoString(&buffer, "tablespace");
+ break;
+
+ case OCLASS_FDW:
+ appendStringInfoString(&buffer, "foreign-data wrapper");
+ break;
+
+ case OCLASS_FOREIGN_SERVER:
+ appendStringInfoString(&buffer, "server");
+ break;
+
+ case OCLASS_USER_MAPPING:
+ appendStringInfoString(&buffer, "user mapping");
+ break;
+
+ case OCLASS_DEFACL:
+ appendStringInfoString(&buffer, "default acl");
+ break;
+
+ case OCLASS_EXTENSION:
+ appendStringInfoString(&buffer, "extension");
+ break;
+
+ case OCLASS_EVENT_TRIGGER:
+ appendStringInfoString(&buffer, "event trigger");
+ break;
+
+ case OCLASS_PARAMETER_ACL:
+ appendStringInfoString(&buffer, "parameter ACL");
+ break;
+
+ case OCLASS_POLICY:
+ appendStringInfoString(&buffer, "policy");
+ break;
+
+ case OCLASS_PUBLICATION:
+ appendStringInfoString(&buffer, "publication");
+ break;
+
+ case OCLASS_PUBLICATION_NAMESPACE:
+ appendStringInfoString(&buffer, "publication namespace");
+ break;
+
+ case OCLASS_PUBLICATION_REL:
+ appendStringInfoString(&buffer, "publication relation");
+ break;
+
+ case OCLASS_SUBSCRIPTION:
+ appendStringInfoString(&buffer, "subscription");
+ break;
+
+ case OCLASS_TRANSFORM:
+ appendStringInfoString(&buffer, "transform");
+ break;
+
+ /*
+ * There's intentionally no default: case here; we want the
+ * compiler to warn if a new OCLASS hasn't been handled above.
+ */
+ }
+
+ /* the result can never be empty */
+ Assert(buffer.len > 0);
+
+ return buffer.data;
+}
+
+/*
+ * subroutine for getObjectTypeDescription: describe a relation type
+ */
+static void
+getRelationTypeDescription(StringInfo buffer, Oid relid, int32 objectSubId,
+ bool missing_ok)
+{
+ HeapTuple relTup;
+ Form_pg_class relForm;
+
+ relTup = SearchSysCache1(RELOID,
+ ObjectIdGetDatum(relid));
+ if (!HeapTupleIsValid(relTup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "cache lookup failed for relation %u", relid);
+
+ /* fallback to "relation" for an undefined object */
+ appendStringInfoString(buffer, "relation");
+ return;
+ }
+ relForm = (Form_pg_class) GETSTRUCT(relTup);
+
+ switch (relForm->relkind)
+ {
+ case RELKIND_RELATION:
+ case RELKIND_PARTITIONED_TABLE:
+ appendStringInfoString(buffer, "table");
+ break;
+ case RELKIND_INDEX:
+ case RELKIND_PARTITIONED_INDEX:
+ appendStringInfoString(buffer, "index");
+ break;
+ case RELKIND_SEQUENCE:
+ appendStringInfoString(buffer, "sequence");
+ break;
+ case RELKIND_TOASTVALUE:
+ appendStringInfoString(buffer, "toast table");
+ break;
+ case RELKIND_VIEW:
+ appendStringInfoString(buffer, "view");
+ break;
+ case RELKIND_MATVIEW:
+ appendStringInfoString(buffer, "materialized view");
+ break;
+ case RELKIND_COMPOSITE_TYPE:
+ appendStringInfoString(buffer, "composite type");
+ break;
+ case RELKIND_FOREIGN_TABLE:
+ appendStringInfoString(buffer, "foreign table");
+ break;
+ default:
+ /* shouldn't get here */
+ appendStringInfoString(buffer, "relation");
+ break;
+ }
+
+ if (objectSubId != 0)
+ appendStringInfoString(buffer, " column");
+
+ ReleaseSysCache(relTup);
+}
+
+/*
+ * subroutine for getObjectTypeDescription: describe a constraint type
+ */
+static void
+getConstraintTypeDescription(StringInfo buffer, Oid constroid, bool missing_ok)
+{
+ Relation constrRel;
+ HeapTuple constrTup;
+ Form_pg_constraint constrForm;
+
+ constrRel = table_open(ConstraintRelationId, AccessShareLock);
+ constrTup = get_catalog_object_by_oid(constrRel, Anum_pg_constraint_oid,
+ constroid);
+ if (!HeapTupleIsValid(constrTup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "cache lookup failed for constraint %u", constroid);
+
+ table_close(constrRel, AccessShareLock);
+
+ /* fallback to "constraint" for an undefined object */
+ appendStringInfoString(buffer, "constraint");
+ return;
+ }
+
+ constrForm = (Form_pg_constraint) GETSTRUCT(constrTup);
+
+ if (OidIsValid(constrForm->conrelid))
+ appendStringInfoString(buffer, "table constraint");
+ else if (OidIsValid(constrForm->contypid))
+ appendStringInfoString(buffer, "domain constraint");
+ else
+ elog(ERROR, "invalid constraint %u", constrForm->oid);
+
+ table_close(constrRel, AccessShareLock);
+}
+
+/*
+ * subroutine for getObjectTypeDescription: describe a procedure type
+ */
+static void
+getProcedureTypeDescription(StringInfo buffer, Oid procid,
+ bool missing_ok)
+{
+ HeapTuple procTup;
+ Form_pg_proc procForm;
+
+ procTup = SearchSysCache1(PROCOID,
+ ObjectIdGetDatum(procid));
+ if (!HeapTupleIsValid(procTup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "cache lookup failed for procedure %u", procid);
+
+ /* fallback to "procedure" for an undefined object */
+ appendStringInfoString(buffer, "routine");
+ return;
+ }
+ procForm = (Form_pg_proc) GETSTRUCT(procTup);
+
+ if (procForm->prokind == PROKIND_AGGREGATE)
+ appendStringInfoString(buffer, "aggregate");
+ else if (procForm->prokind == PROKIND_PROCEDURE)
+ appendStringInfoString(buffer, "procedure");
+ else /* function or window function */
+ appendStringInfoString(buffer, "function");
+
+ ReleaseSysCache(procTup);
+}
+
+/*
+ * Obtain a given object's identity, as a palloc'ed string.
+ *
+ * This is for machine consumption, so it's not translated. All elements are
+ * schema-qualified when appropriate. Returns NULL if the object could not
+ * be found.
+ */
+char *
+getObjectIdentity(const ObjectAddress *object, bool missing_ok)
+{
+ return getObjectIdentityParts(object, NULL, NULL, missing_ok);
+}
+
+/*
+ * As above, but more detailed.
+ *
+ * There are two sets of return values: the identity itself as a palloc'd
+ * string is returned. objname and objargs, if not NULL, are output parameters
+ * that receive lists of C-strings that are useful to give back to
+ * get_object_address() to reconstruct the ObjectAddress. Returns NULL if
+ * the object could not be found.
+ */
+char *
+getObjectIdentityParts(const ObjectAddress *object,
+ List **objname, List **objargs,
+ bool missing_ok)
+{
+ StringInfoData buffer;
+
+ initStringInfo(&buffer);
+
+ /*
+ * Make sure that both objname and objargs were passed, or none was; and
+ * initialize them to empty lists. For objname this is useless because it
+ * will be initialized in all cases inside the switch; but we do it anyway
+ * so that we can test below that no branch leaves it unset.
+ */
+ Assert(PointerIsValid(objname) == PointerIsValid(objargs));
+ if (objname)
+ {
+ *objname = NIL;
+ *objargs = NIL;
+ }
+
+ switch (getObjectClass(object))
+ {
+ case OCLASS_CLASS:
+ {
+ char *attr = NULL;
+
+ /*
+ * Check for the attribute first, so as if it is missing we
+ * can skip the entire relation description.
+ */
+ if (object->objectSubId != 0)
+ {
+ attr = get_attname(object->objectId,
+ object->objectSubId,
+ missing_ok);
+
+ if (missing_ok && attr == NULL)
+ break;
+ }
+
+ getRelationIdentity(&buffer, object->objectId, objname,
+ missing_ok);
+ if (objname && *objname == NIL)
+ break;
+
+ if (attr)
+ {
+ appendStringInfo(&buffer, ".%s",
+ quote_identifier(attr));
+ if (objname)
+ *objname = lappend(*objname, attr);
+ }
+ }
+ break;
+
+ case OCLASS_PROC:
+ {
+ bits16 flags = FORMAT_PROC_FORCE_QUALIFY | FORMAT_PROC_INVALID_AS_NULL;
+ char *proname = format_procedure_extended(object->objectId,
+ flags);
+
+ if (proname == NULL)
+ break;
+
+ appendStringInfoString(&buffer, proname);
+ if (objname)
+ format_procedure_parts(object->objectId, objname, objargs,
+ missing_ok);
+ break;
+ }
+
+ case OCLASS_TYPE:
+ {
+ bits16 flags = FORMAT_TYPE_INVALID_AS_NULL | FORMAT_TYPE_FORCE_QUALIFY;
+ char *typeout;
+
+ typeout = format_type_extended(object->objectId, -1, flags);
+
+ if (typeout == NULL)
+ break;
+
+ appendStringInfoString(&buffer, typeout);
+ if (objname)
+ *objname = list_make1(typeout);
+ }
+ break;
+
+ case OCLASS_CAST:
+ {
+ Relation castRel;
+ HeapTuple tup;
+ Form_pg_cast castForm;
+
+ castRel = table_open(CastRelationId, AccessShareLock);
+
+ tup = get_catalog_object_by_oid(castRel, Anum_pg_cast_oid,
+ object->objectId);
+
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for cast %u",
+ object->objectId);
+
+ table_close(castRel, AccessShareLock);
+ break;
+ }
+
+ castForm = (Form_pg_cast) GETSTRUCT(tup);
+
+ appendStringInfo(&buffer, "(%s AS %s)",
+ format_type_be_qualified(castForm->castsource),
+ format_type_be_qualified(castForm->casttarget));
+
+ if (objname)
+ {
+ *objname = list_make1(format_type_be_qualified(castForm->castsource));
+ *objargs = list_make1(format_type_be_qualified(castForm->casttarget));
+ }
+
+ table_close(castRel, AccessShareLock);
+ break;
+ }
+
+ case OCLASS_COLLATION:
+ {
+ HeapTuple collTup;
+ Form_pg_collation coll;
+ char *schema;
+
+ collTup = SearchSysCache1(COLLOID,
+ ObjectIdGetDatum(object->objectId));
+ if (!HeapTupleIsValid(collTup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "cache lookup failed for collation %u",
+ object->objectId);
+ break;
+ }
+ coll = (Form_pg_collation) GETSTRUCT(collTup);
+ schema = get_namespace_name_or_temp(coll->collnamespace);
+ appendStringInfoString(&buffer,
+ quote_qualified_identifier(schema,
+ NameStr(coll->collname)));
+ if (objname)
+ *objname = list_make2(schema,
+ pstrdup(NameStr(coll->collname)));
+ ReleaseSysCache(collTup);
+ break;
+ }
+
+ case OCLASS_CONSTRAINT:
+ {
+ HeapTuple conTup;
+ Form_pg_constraint con;
+
+ conTup = SearchSysCache1(CONSTROID,
+ ObjectIdGetDatum(object->objectId));
+ if (!HeapTupleIsValid(conTup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "cache lookup failed for constraint %u",
+ object->objectId);
+ break;
+ }
+ con = (Form_pg_constraint) GETSTRUCT(conTup);
+
+ if (OidIsValid(con->conrelid))
+ {
+ appendStringInfo(&buffer, "%s on ",
+ quote_identifier(NameStr(con->conname)));
+ getRelationIdentity(&buffer, con->conrelid, objname,
+ false);
+ if (objname)
+ *objname = lappend(*objname, pstrdup(NameStr(con->conname)));
+ }
+ else
+ {
+ ObjectAddress domain;
+
+ Assert(OidIsValid(con->contypid));
+ domain.classId = TypeRelationId;
+ domain.objectId = con->contypid;
+ domain.objectSubId = 0;
+
+ appendStringInfo(&buffer, "%s on %s",
+ quote_identifier(NameStr(con->conname)),
+ getObjectIdentityParts(&domain, objname,
+ objargs, false));
+
+ if (objname)
+ *objargs = lappend(*objargs, pstrdup(NameStr(con->conname)));
+ }
+
+ ReleaseSysCache(conTup);
+ break;
+ }
+
+ case OCLASS_CONVERSION:
+ {
+ HeapTuple conTup;
+ Form_pg_conversion conForm;
+ char *schema;
+
+ conTup = SearchSysCache1(CONVOID,
+ ObjectIdGetDatum(object->objectId));
+ if (!HeapTupleIsValid(conTup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "cache lookup failed for conversion %u",
+ object->objectId);
+ break;
+ }
+ conForm = (Form_pg_conversion) GETSTRUCT(conTup);
+ schema = get_namespace_name_or_temp(conForm->connamespace);
+ appendStringInfoString(&buffer,
+ quote_qualified_identifier(schema,
+ NameStr(conForm->conname)));
+ if (objname)
+ *objname = list_make2(schema,
+ pstrdup(NameStr(conForm->conname)));
+ ReleaseSysCache(conTup);
+ break;
+ }
+
+ case OCLASS_DEFAULT:
+ {
+ ObjectAddress colobject;
+
+ colobject = GetAttrDefaultColumnAddress(object->objectId);
+
+ if (!OidIsValid(colobject.objectId))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for attrdef %u",
+ object->objectId);
+ break;
+ }
+
+ appendStringInfo(&buffer, "for %s",
+ getObjectIdentityParts(&colobject,
+ objname, objargs,
+ false));
+ break;
+ }
+
+ case OCLASS_LANGUAGE:
+ {
+ HeapTuple langTup;
+ Form_pg_language langForm;
+
+ langTup = SearchSysCache1(LANGOID,
+ ObjectIdGetDatum(object->objectId));
+ if (!HeapTupleIsValid(langTup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "cache lookup failed for language %u",
+ object->objectId);
+ break;
+ }
+ langForm = (Form_pg_language) GETSTRUCT(langTup);
+ appendStringInfoString(&buffer,
+ quote_identifier(NameStr(langForm->lanname)));
+ if (objname)
+ *objname = list_make1(pstrdup(NameStr(langForm->lanname)));
+ ReleaseSysCache(langTup);
+ break;
+ }
+ case OCLASS_LARGEOBJECT:
+ if (!LargeObjectExists(object->objectId))
+ break;
+ appendStringInfo(&buffer, "%u",
+ object->objectId);
+ if (objname)
+ *objname = list_make1(psprintf("%u", object->objectId));
+ break;
+
+ case OCLASS_OPERATOR:
+ {
+ bits16 flags = FORMAT_OPERATOR_FORCE_QUALIFY | FORMAT_OPERATOR_INVALID_AS_NULL;
+ char *oprname = format_operator_extended(object->objectId,
+ flags);
+
+ if (oprname == NULL)
+ break;
+
+ appendStringInfoString(&buffer, oprname);
+ if (objname)
+ format_operator_parts(object->objectId, objname, objargs, missing_ok);
+ break;
+ }
+
+ case OCLASS_OPCLASS:
+ {
+ HeapTuple opcTup;
+ Form_pg_opclass opcForm;
+ HeapTuple amTup;
+ Form_pg_am amForm;
+ char *schema;
+
+ opcTup = SearchSysCache1(CLAOID,
+ ObjectIdGetDatum(object->objectId));
+ if (!HeapTupleIsValid(opcTup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "cache lookup failed for opclass %u",
+ object->objectId);
+ break;
+ }
+ opcForm = (Form_pg_opclass) GETSTRUCT(opcTup);
+ schema = get_namespace_name_or_temp(opcForm->opcnamespace);
+
+ amTup = SearchSysCache1(AMOID,
+ ObjectIdGetDatum(opcForm->opcmethod));
+ if (!HeapTupleIsValid(amTup))
+ elog(ERROR, "cache lookup failed for access method %u",
+ opcForm->opcmethod);
+ amForm = (Form_pg_am) GETSTRUCT(amTup);
+
+ appendStringInfo(&buffer, "%s USING %s",
+ quote_qualified_identifier(schema,
+ NameStr(opcForm->opcname)),
+ quote_identifier(NameStr(amForm->amname)));
+ if (objname)
+ *objname = list_make3(pstrdup(NameStr(amForm->amname)),
+ schema,
+ pstrdup(NameStr(opcForm->opcname)));
+
+ ReleaseSysCache(amTup);
+ ReleaseSysCache(opcTup);
+ break;
+ }
+
+ case OCLASS_OPFAMILY:
+ getOpFamilyIdentity(&buffer, object->objectId, objname,
+ missing_ok);
+ break;
+
+ case OCLASS_AM:
+ {
+ char *amname;
+
+ amname = get_am_name(object->objectId);
+ if (!amname)
+ {
+ if (!missing_ok)
+ elog(ERROR, "cache lookup failed for access method %u",
+ object->objectId);
+ break;
+ }
+ appendStringInfoString(&buffer, quote_identifier(amname));
+ if (objname)
+ *objname = list_make1(amname);
+ }
+ break;
+
+ case OCLASS_AMOP:
+ {
+ Relation amopDesc;
+ HeapTuple tup;
+ ScanKeyData skey[1];
+ SysScanDesc amscan;
+ Form_pg_amop amopForm;
+ StringInfoData opfam;
+ char *ltype;
+ char *rtype;
+
+ amopDesc = table_open(AccessMethodOperatorRelationId,
+ AccessShareLock);
+
+ ScanKeyInit(&skey[0],
+ Anum_pg_amop_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ amscan = systable_beginscan(amopDesc, AccessMethodOperatorOidIndexId, true,
+ NULL, 1, skey);
+
+ tup = systable_getnext(amscan);
+
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for amop entry %u",
+ object->objectId);
+
+ systable_endscan(amscan);
+ table_close(amopDesc, AccessShareLock);
+ break;
+ }
+
+ amopForm = (Form_pg_amop) GETSTRUCT(tup);
+
+ initStringInfo(&opfam);
+ getOpFamilyIdentity(&opfam, amopForm->amopfamily, objname,
+ false);
+
+ ltype = format_type_be_qualified(amopForm->amoplefttype);
+ rtype = format_type_be_qualified(amopForm->amoprighttype);
+
+ if (objname)
+ {
+ *objname = lappend(*objname,
+ psprintf("%d", amopForm->amopstrategy));
+ *objargs = list_make2(ltype, rtype);
+ }
+
+ appendStringInfo(&buffer, "operator %d (%s, %s) of %s",
+ amopForm->amopstrategy,
+ ltype, rtype, opfam.data);
+
+ pfree(opfam.data);
+
+ systable_endscan(amscan);
+ table_close(amopDesc, AccessShareLock);
+ break;
+ }
+
+ case OCLASS_AMPROC:
+ {
+ Relation amprocDesc;
+ ScanKeyData skey[1];
+ SysScanDesc amscan;
+ HeapTuple tup;
+ Form_pg_amproc amprocForm;
+ StringInfoData opfam;
+ char *ltype;
+ char *rtype;
+
+ amprocDesc = table_open(AccessMethodProcedureRelationId,
+ AccessShareLock);
+
+ ScanKeyInit(&skey[0],
+ Anum_pg_amproc_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ amscan = systable_beginscan(amprocDesc, AccessMethodProcedureOidIndexId, true,
+ NULL, 1, skey);
+
+ tup = systable_getnext(amscan);
+
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for amproc entry %u",
+ object->objectId);
+
+ systable_endscan(amscan);
+ table_close(amprocDesc, AccessShareLock);
+ break;
+ }
+
+ amprocForm = (Form_pg_amproc) GETSTRUCT(tup);
+
+ initStringInfo(&opfam);
+ getOpFamilyIdentity(&opfam, amprocForm->amprocfamily, objname,
+ false);
+
+ ltype = format_type_be_qualified(amprocForm->amproclefttype);
+ rtype = format_type_be_qualified(amprocForm->amprocrighttype);
+
+ if (objname)
+ {
+ *objname = lappend(*objname,
+ psprintf("%d", amprocForm->amprocnum));
+ *objargs = list_make2(ltype, rtype);
+ }
+
+ appendStringInfo(&buffer, "function %d (%s, %s) of %s",
+ amprocForm->amprocnum,
+ ltype, rtype, opfam.data);
+
+ pfree(opfam.data);
+
+ systable_endscan(amscan);
+ table_close(amprocDesc, AccessShareLock);
+ break;
+ }
+
+ case OCLASS_REWRITE:
+ {
+ Relation ruleDesc;
+ HeapTuple tup;
+ Form_pg_rewrite rule;
+
+ ruleDesc = table_open(RewriteRelationId, AccessShareLock);
+
+ tup = get_catalog_object_by_oid(ruleDesc, Anum_pg_rewrite_oid,
+ object->objectId);
+
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for rule %u",
+ object->objectId);
+
+ table_close(ruleDesc, AccessShareLock);
+ break;
+ }
+
+ rule = (Form_pg_rewrite) GETSTRUCT(tup);
+
+ appendStringInfo(&buffer, "%s on ",
+ quote_identifier(NameStr(rule->rulename)));
+ getRelationIdentity(&buffer, rule->ev_class, objname, false);
+ if (objname)
+ *objname = lappend(*objname, pstrdup(NameStr(rule->rulename)));
+
+ table_close(ruleDesc, AccessShareLock);
+ break;
+ }
+
+ case OCLASS_TRIGGER:
+ {
+ Relation trigDesc;
+ HeapTuple tup;
+ Form_pg_trigger trig;
+
+ trigDesc = table_open(TriggerRelationId, AccessShareLock);
+
+ tup = get_catalog_object_by_oid(trigDesc, Anum_pg_trigger_oid,
+ object->objectId);
+
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for trigger %u",
+ object->objectId);
+
+ table_close(trigDesc, AccessShareLock);
+ break;
+ }
+
+ trig = (Form_pg_trigger) GETSTRUCT(tup);
+
+ appendStringInfo(&buffer, "%s on ",
+ quote_identifier(NameStr(trig->tgname)));
+ getRelationIdentity(&buffer, trig->tgrelid, objname, false);
+ if (objname)
+ *objname = lappend(*objname, pstrdup(NameStr(trig->tgname)));
+
+ table_close(trigDesc, AccessShareLock);
+ break;
+ }
+
+ case OCLASS_SCHEMA:
+ {
+ char *nspname;
+
+ nspname = get_namespace_name_or_temp(object->objectId);
+ if (!nspname)
+ {
+ if (!missing_ok)
+ elog(ERROR, "cache lookup failed for namespace %u",
+ object->objectId);
+ break;
+ }
+ appendStringInfoString(&buffer,
+ quote_identifier(nspname));
+ if (objname)
+ *objname = list_make1(nspname);
+ break;
+ }
+
+ case OCLASS_STATISTIC_EXT:
+ {
+ HeapTuple tup;
+ Form_pg_statistic_ext formStatistic;
+ char *schema;
+
+ tup = SearchSysCache1(STATEXTOID,
+ ObjectIdGetDatum(object->objectId));
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "cache lookup failed for statistics object %u",
+ object->objectId);
+ break;
+ }
+ formStatistic = (Form_pg_statistic_ext) GETSTRUCT(tup);
+ schema = get_namespace_name_or_temp(formStatistic->stxnamespace);
+ appendStringInfoString(&buffer,
+ quote_qualified_identifier(schema,
+ NameStr(formStatistic->stxname)));
+ if (objname)
+ *objname = list_make2(schema,
+ pstrdup(NameStr(formStatistic->stxname)));
+ ReleaseSysCache(tup);
+ }
+ break;
+
+ case OCLASS_TSPARSER:
+ {
+ HeapTuple tup;
+ Form_pg_ts_parser formParser;
+ char *schema;
+
+ tup = SearchSysCache1(TSPARSEROID,
+ ObjectIdGetDatum(object->objectId));
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "cache lookup failed for text search parser %u",
+ object->objectId);
+ break;
+ }
+ formParser = (Form_pg_ts_parser) GETSTRUCT(tup);
+ schema = get_namespace_name_or_temp(formParser->prsnamespace);
+ appendStringInfoString(&buffer,
+ quote_qualified_identifier(schema,
+ NameStr(formParser->prsname)));
+ if (objname)
+ *objname = list_make2(schema,
+ pstrdup(NameStr(formParser->prsname)));
+ ReleaseSysCache(tup);
+ break;
+ }
+
+ case OCLASS_TSDICT:
+ {
+ HeapTuple tup;
+ Form_pg_ts_dict formDict;
+ char *schema;
+
+ tup = SearchSysCache1(TSDICTOID,
+ ObjectIdGetDatum(object->objectId));
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "cache lookup failed for text search dictionary %u",
+ object->objectId);
+ break;
+ }
+ formDict = (Form_pg_ts_dict) GETSTRUCT(tup);
+ schema = get_namespace_name_or_temp(formDict->dictnamespace);
+ appendStringInfoString(&buffer,
+ quote_qualified_identifier(schema,
+ NameStr(formDict->dictname)));
+ if (objname)
+ *objname = list_make2(schema,
+ pstrdup(NameStr(formDict->dictname)));
+ ReleaseSysCache(tup);
+ break;
+ }
+
+ case OCLASS_TSTEMPLATE:
+ {
+ HeapTuple tup;
+ Form_pg_ts_template formTmpl;
+ char *schema;
+
+ tup = SearchSysCache1(TSTEMPLATEOID,
+ ObjectIdGetDatum(object->objectId));
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "cache lookup failed for text search template %u",
+ object->objectId);
+ break;
+ }
+ formTmpl = (Form_pg_ts_template) GETSTRUCT(tup);
+ schema = get_namespace_name_or_temp(formTmpl->tmplnamespace);
+ appendStringInfoString(&buffer,
+ quote_qualified_identifier(schema,
+ NameStr(formTmpl->tmplname)));
+ if (objname)
+ *objname = list_make2(schema,
+ pstrdup(NameStr(formTmpl->tmplname)));
+ ReleaseSysCache(tup);
+ break;
+ }
+
+ case OCLASS_TSCONFIG:
+ {
+ HeapTuple tup;
+ Form_pg_ts_config formCfg;
+ char *schema;
+
+ tup = SearchSysCache1(TSCONFIGOID,
+ ObjectIdGetDatum(object->objectId));
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "cache lookup failed for text search configuration %u",
+ object->objectId);
+ break;
+ }
+ formCfg = (Form_pg_ts_config) GETSTRUCT(tup);
+ schema = get_namespace_name_or_temp(formCfg->cfgnamespace);
+ appendStringInfoString(&buffer,
+ quote_qualified_identifier(schema,
+ NameStr(formCfg->cfgname)));
+ if (objname)
+ *objname = list_make2(schema,
+ pstrdup(NameStr(formCfg->cfgname)));
+ ReleaseSysCache(tup);
+ break;
+ }
+
+ case OCLASS_ROLE:
+ {
+ char *username;
+
+ username = GetUserNameFromId(object->objectId, missing_ok);
+ if (!username)
+ break;
+ if (objname)
+ *objname = list_make1(username);
+ appendStringInfoString(&buffer,
+ quote_identifier(username));
+ break;
+ }
+
+ case OCLASS_DATABASE:
+ {
+ char *datname;
+
+ datname = get_database_name(object->objectId);
+ if (!datname)
+ {
+ if (!missing_ok)
+ elog(ERROR, "cache lookup failed for database %u",
+ object->objectId);
+ break;
+ }
+ if (objname)
+ *objname = list_make1(datname);
+ appendStringInfoString(&buffer,
+ quote_identifier(datname));
+ break;
+ }
+
+ case OCLASS_TBLSPACE:
+ {
+ char *tblspace;
+
+ tblspace = get_tablespace_name(object->objectId);
+ if (!tblspace)
+ {
+ if (!missing_ok)
+ elog(ERROR, "cache lookup failed for tablespace %u",
+ object->objectId);
+ break;
+ }
+ if (objname)
+ *objname = list_make1(tblspace);
+ appendStringInfoString(&buffer,
+ quote_identifier(tblspace));
+ break;
+ }
+
+ case OCLASS_FDW:
+ {
+ ForeignDataWrapper *fdw;
+
+ fdw = GetForeignDataWrapperExtended(object->objectId,
+ missing_ok);
+ if (fdw)
+ {
+ appendStringInfoString(&buffer, quote_identifier(fdw->fdwname));
+ if (objname)
+ *objname = list_make1(pstrdup(fdw->fdwname));
+ }
+ break;
+ }
+
+ case OCLASS_FOREIGN_SERVER:
+ {
+ ForeignServer *srv;
+
+ srv = GetForeignServerExtended(object->objectId,
+ missing_ok);
+ if (srv)
+ {
+ appendStringInfoString(&buffer,
+ quote_identifier(srv->servername));
+ if (objname)
+ *objname = list_make1(pstrdup(srv->servername));
+ }
+ break;
+ }
+
+ case OCLASS_USER_MAPPING:
+ {
+ HeapTuple tup;
+ Oid useid;
+ Form_pg_user_mapping umform;
+ ForeignServer *srv;
+ const char *usename;
+
+ tup = SearchSysCache1(USERMAPPINGOID,
+ ObjectIdGetDatum(object->objectId));
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "cache lookup failed for user mapping %u",
+ object->objectId);
+ break;
+ }
+ umform = (Form_pg_user_mapping) GETSTRUCT(tup);
+ useid = umform->umuser;
+ srv = GetForeignServer(umform->umserver);
+
+ ReleaseSysCache(tup);
+
+ if (OidIsValid(useid))
+ usename = GetUserNameFromId(useid, false);
+ else
+ usename = "public";
+
+ if (objname)
+ {
+ *objname = list_make1(pstrdup(usename));
+ *objargs = list_make1(pstrdup(srv->servername));
+ }
+
+ appendStringInfo(&buffer, "%s on server %s",
+ quote_identifier(usename),
+ srv->servername);
+ break;
+ }
+
+ case OCLASS_DEFACL:
+ {
+ Relation defaclrel;
+ ScanKeyData skey[1];
+ SysScanDesc rcscan;
+ HeapTuple tup;
+ Form_pg_default_acl defacl;
+ char *schema;
+ char *username;
+
+ defaclrel = table_open(DefaultAclRelationId, AccessShareLock);
+
+ ScanKeyInit(&skey[0],
+ Anum_pg_default_acl_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ rcscan = systable_beginscan(defaclrel, DefaultAclOidIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(rcscan);
+
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for default ACL %u",
+ object->objectId);
+
+ systable_endscan(rcscan);
+ table_close(defaclrel, AccessShareLock);
+ break;
+ }
+
+ defacl = (Form_pg_default_acl) GETSTRUCT(tup);
+
+ username = GetUserNameFromId(defacl->defaclrole, false);
+ appendStringInfo(&buffer,
+ "for role %s",
+ quote_identifier(username));
+
+ if (OidIsValid(defacl->defaclnamespace))
+ {
+ schema = get_namespace_name_or_temp(defacl->defaclnamespace);
+ appendStringInfo(&buffer,
+ " in schema %s",
+ quote_identifier(schema));
+ }
+ else
+ schema = NULL;
+
+ switch (defacl->defaclobjtype)
+ {
+ case DEFACLOBJ_RELATION:
+ appendStringInfoString(&buffer,
+ " on tables");
+ break;
+ case DEFACLOBJ_SEQUENCE:
+ appendStringInfoString(&buffer,
+ " on sequences");
+ break;
+ case DEFACLOBJ_FUNCTION:
+ appendStringInfoString(&buffer,
+ " on functions");
+ break;
+ case DEFACLOBJ_TYPE:
+ appendStringInfoString(&buffer,
+ " on types");
+ break;
+ case DEFACLOBJ_NAMESPACE:
+ appendStringInfoString(&buffer,
+ " on schemas");
+ break;
+ }
+
+ if (objname)
+ {
+ *objname = list_make1(username);
+ if (schema)
+ *objname = lappend(*objname, schema);
+ *objargs = list_make1(psprintf("%c", defacl->defaclobjtype));
+ }
+
+ systable_endscan(rcscan);
+ table_close(defaclrel, AccessShareLock);
+ break;
+ }
+
+ case OCLASS_EXTENSION:
+ {
+ char *extname;
+
+ extname = get_extension_name(object->objectId);
+ if (!extname)
+ {
+ if (!missing_ok)
+ elog(ERROR, "cache lookup failed for extension %u",
+ object->objectId);
+ break;
+ }
+ appendStringInfoString(&buffer, quote_identifier(extname));
+ if (objname)
+ *objname = list_make1(extname);
+ break;
+ }
+
+ case OCLASS_EVENT_TRIGGER:
+ {
+ HeapTuple tup;
+ Form_pg_event_trigger trigForm;
+ char *evtname;
+
+ tup = SearchSysCache1(EVENTTRIGGEROID,
+ ObjectIdGetDatum(object->objectId));
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "cache lookup failed for event trigger %u",
+ object->objectId);
+ break;
+ }
+ trigForm = (Form_pg_event_trigger) GETSTRUCT(tup);
+ evtname = pstrdup(NameStr(trigForm->evtname));
+ appendStringInfoString(&buffer, quote_identifier(evtname));
+ if (objname)
+ *objname = list_make1(evtname);
+ ReleaseSysCache(tup);
+ break;
+ }
+
+ case OCLASS_PARAMETER_ACL:
+ {
+ HeapTuple tup;
+ Datum nameDatum;
+ bool isNull;
+ char *parname;
+
+ tup = SearchSysCache1(PARAMETERACLOID,
+ ObjectIdGetDatum(object->objectId));
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "cache lookup failed for parameter ACL %u",
+ object->objectId);
+ break;
+ }
+ nameDatum = SysCacheGetAttr(PARAMETERACLOID, tup,
+ Anum_pg_parameter_acl_parname,
+ &isNull);
+ Assert(!isNull);
+ parname = TextDatumGetCString(nameDatum);
+ appendStringInfoString(&buffer, parname);
+ if (objname)
+ *objname = list_make1(parname);
+ ReleaseSysCache(tup);
+ break;
+ }
+
+ case OCLASS_POLICY:
+ {
+ Relation polDesc;
+ HeapTuple tup;
+ Form_pg_policy policy;
+
+ polDesc = table_open(PolicyRelationId, AccessShareLock);
+
+ tup = get_catalog_object_by_oid(polDesc, Anum_pg_policy_oid,
+ object->objectId);
+
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for policy %u",
+ object->objectId);
+
+ table_close(polDesc, AccessShareLock);
+ break;
+ }
+
+ policy = (Form_pg_policy) GETSTRUCT(tup);
+
+ appendStringInfo(&buffer, "%s on ",
+ quote_identifier(NameStr(policy->polname)));
+ getRelationIdentity(&buffer, policy->polrelid, objname, false);
+ if (objname)
+ *objname = lappend(*objname, pstrdup(NameStr(policy->polname)));
+
+ table_close(polDesc, AccessShareLock);
+ break;
+ }
+
+ case OCLASS_PUBLICATION:
+ {
+ char *pubname;
+
+ pubname = get_publication_name(object->objectId, missing_ok);
+ if (pubname)
+ {
+ appendStringInfoString(&buffer,
+ quote_identifier(pubname));
+ if (objname)
+ *objname = list_make1(pubname);
+ }
+ break;
+ }
+
+ case OCLASS_PUBLICATION_NAMESPACE:
+ {
+ char *pubname;
+ char *nspname;
+
+ if (!getPublicationSchemaInfo(object, missing_ok, &pubname,
+ &nspname))
+ break;
+ appendStringInfo(&buffer, "%s in publication %s",
+ nspname, pubname);
+
+ if (objargs)
+ *objargs = list_make1(pubname);
+ else
+ pfree(pubname);
+
+ if (objname)
+ *objname = list_make1(nspname);
+ else
+ pfree(nspname);
+
+ break;
+ }
+
+ case OCLASS_PUBLICATION_REL:
+ {
+ HeapTuple tup;
+ char *pubname;
+ Form_pg_publication_rel prform;
+
+ tup = SearchSysCache1(PUBLICATIONREL,
+ ObjectIdGetDatum(object->objectId));
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "cache lookup failed for publication table %u",
+ object->objectId);
+ break;
+ }
+
+ prform = (Form_pg_publication_rel) GETSTRUCT(tup);
+ pubname = get_publication_name(prform->prpubid, false);
+
+ getRelationIdentity(&buffer, prform->prrelid, objname, false);
+ appendStringInfo(&buffer, " in publication %s", pubname);
+
+ if (objargs)
+ *objargs = list_make1(pubname);
+
+ ReleaseSysCache(tup);
+ break;
+ }
+
+ case OCLASS_SUBSCRIPTION:
+ {
+ char *subname;
+
+ subname = get_subscription_name(object->objectId, missing_ok);
+ if (subname)
+ {
+ appendStringInfoString(&buffer,
+ quote_identifier(subname));
+ if (objname)
+ *objname = list_make1(subname);
+ }
+ break;
+ }
+
+ case OCLASS_TRANSFORM:
+ {
+ Relation transformDesc;
+ HeapTuple tup;
+ Form_pg_transform transform;
+ char *transformLang;
+ char *transformType;
+
+ transformDesc = table_open(TransformRelationId, AccessShareLock);
+
+ tup = get_catalog_object_by_oid(transformDesc,
+ Anum_pg_transform_oid,
+ object->objectId);
+
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for transform %u",
+ object->objectId);
+
+ table_close(transformDesc, AccessShareLock);
+ break;
+ }
+
+ transform = (Form_pg_transform) GETSTRUCT(tup);
+
+ transformType = format_type_be_qualified(transform->trftype);
+ transformLang = get_language_name(transform->trflang, false);
+
+ appendStringInfo(&buffer, "for %s on language %s",
+ transformType,
+ transformLang);
+ if (objname)
+ {
+ *objname = list_make1(transformType);
+ *objargs = list_make1(pstrdup(transformLang));
+ }
+
+ table_close(transformDesc, AccessShareLock);
+ }
+ break;
+
+ /*
+ * There's intentionally no default: case here; we want the
+ * compiler to warn if a new OCLASS hasn't been handled above.
+ */
+ }
+
+ if (!missing_ok)
+ {
+ /*
+ * If a get_object_address() representation was requested, make sure
+ * we are providing one. We don't check objargs, because many of the
+ * cases above leave it as NIL.
+ */
+ if (objname && *objname == NIL)
+ elog(ERROR, "requested object address for unsupported object class %d: text result \"%s\"",
+ (int) getObjectClass(object), buffer.data);
+ }
+ else
+ {
+ /* an empty buffer is equivalent to no object found */
+ if (buffer.len == 0)
+ {
+ Assert((objname == NULL || *objname == NIL) &&
+ (objargs == NULL || *objargs == NIL));
+ return NULL;
+ }
+ }
+
+ return buffer.data;
+}
+
+static void
+getOpFamilyIdentity(StringInfo buffer, Oid opfid, List **object,
+ bool missing_ok)
+{
+ HeapTuple opfTup;
+ Form_pg_opfamily opfForm;
+ HeapTuple amTup;
+ Form_pg_am amForm;
+ char *schema;
+
+ opfTup = SearchSysCache1(OPFAMILYOID, ObjectIdGetDatum(opfid));
+ if (!HeapTupleIsValid(opfTup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "cache lookup failed for opfamily %u", opfid);
+ return;
+ }
+ opfForm = (Form_pg_opfamily) GETSTRUCT(opfTup);
+
+ amTup = SearchSysCache1(AMOID, ObjectIdGetDatum(opfForm->opfmethod));
+ if (!HeapTupleIsValid(amTup))
+ elog(ERROR, "cache lookup failed for access method %u",
+ opfForm->opfmethod);
+ amForm = (Form_pg_am) GETSTRUCT(amTup);
+
+ schema = get_namespace_name_or_temp(opfForm->opfnamespace);
+ appendStringInfo(buffer, "%s USING %s",
+ quote_qualified_identifier(schema,
+ NameStr(opfForm->opfname)),
+ NameStr(amForm->amname));
+
+ if (object)
+ *object = list_make3(pstrdup(NameStr(amForm->amname)),
+ pstrdup(schema),
+ pstrdup(NameStr(opfForm->opfname)));
+
+ ReleaseSysCache(amTup);
+ ReleaseSysCache(opfTup);
+}
+
+/*
+ * Append the relation identity (quoted qualified name) to the given
+ * StringInfo.
+ */
+static void
+getRelationIdentity(StringInfo buffer, Oid relid, List **object,
+ bool missing_ok)
+{
+ HeapTuple relTup;
+ Form_pg_class relForm;
+ char *schema;
+
+ relTup = SearchSysCache1(RELOID,
+ ObjectIdGetDatum(relid));
+ if (!HeapTupleIsValid(relTup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "cache lookup failed for relation %u", relid);
+
+ if (object)
+ *object = NIL;
+ return;
+ }
+ relForm = (Form_pg_class) GETSTRUCT(relTup);
+
+ schema = get_namespace_name_or_temp(relForm->relnamespace);
+ appendStringInfoString(buffer,
+ quote_qualified_identifier(schema,
+ NameStr(relForm->relname)));
+ if (object)
+ *object = list_make2(schema, pstrdup(NameStr(relForm->relname)));
+
+ ReleaseSysCache(relTup);
+}
+
+/*
+ * Auxiliary function to build a TEXT array out of a list of C-strings.
+ */
+ArrayType *
+strlist_to_textarray(List *list)
+{
+ ArrayType *arr;
+ Datum *datums;
+ bool *nulls;
+ int j = 0;
+ ListCell *cell;
+ MemoryContext memcxt;
+ MemoryContext oldcxt;
+ int lb[1];
+
+ /* Work in a temp context; easier than individually pfree'ing the Datums */
+ memcxt = AllocSetContextCreate(CurrentMemoryContext,
+ "strlist to array",
+ ALLOCSET_DEFAULT_SIZES);
+ oldcxt = MemoryContextSwitchTo(memcxt);
+
+ datums = (Datum *) palloc(sizeof(Datum) * list_length(list));
+ nulls = palloc(sizeof(bool) * list_length(list));
+
+ foreach(cell, list)
+ {
+ char *name = lfirst(cell);
+
+ if (name)
+ {
+ nulls[j] = false;
+ datums[j++] = CStringGetTextDatum(name);
+ }
+ else
+ nulls[j] = true;
+ }
+
+ MemoryContextSwitchTo(oldcxt);
+
+ lb[0] = 1;
+ arr = construct_md_array(datums, nulls, 1, &j,
+ lb, TEXTOID, -1, false, TYPALIGN_INT);
+
+ MemoryContextDelete(memcxt);
+
+ return arr;
+}
+
+/*
+ * get_relkind_objtype
+ *
+ * Return the object type for the relkind given by the caller.
+ *
+ * If an unexpected relkind is passed, we say OBJECT_TABLE rather than
+ * failing. That's because this is mostly used for generating error messages
+ * for failed ACL checks on relations, and we'd rather produce a generic
+ * message saying "table" than fail entirely.
+ */
+ObjectType
+get_relkind_objtype(char relkind)
+{
+ switch (relkind)
+ {
+ case RELKIND_RELATION:
+ case RELKIND_PARTITIONED_TABLE:
+ return OBJECT_TABLE;
+ case RELKIND_INDEX:
+ case RELKIND_PARTITIONED_INDEX:
+ return OBJECT_INDEX;
+ case RELKIND_SEQUENCE:
+ return OBJECT_SEQUENCE;
+ case RELKIND_VIEW:
+ return OBJECT_VIEW;
+ case RELKIND_MATVIEW:
+ return OBJECT_MATVIEW;
+ case RELKIND_FOREIGN_TABLE:
+ return OBJECT_FOREIGN_TABLE;
+ case RELKIND_TOASTVALUE:
+ return OBJECT_TABLE;
+ default:
+ /* Per above, don't raise an error */
+ return OBJECT_TABLE;
+ }
+}