1 | /*------------------------------------------------------------------------- |
2 | * |
3 | * objectaddress.c |
4 | * functions for working with ObjectAddresses |
5 | * |
6 | * Portions Copyright (c) 1996-2019, PostgreSQL Global Development Group |
7 | * Portions Copyright (c) 1994, Regents of the University of California |
8 | * |
9 | * |
10 | * IDENTIFICATION |
11 | * src/backend/catalog/objectaddress.c |
12 | * |
13 | *------------------------------------------------------------------------- |
14 | */ |
15 | |
16 | #include "postgres.h" |
17 | |
18 | #include "access/genam.h" |
19 | #include "access/htup_details.h" |
20 | #include "access/relation.h" |
21 | #include "access/sysattr.h" |
22 | #include "access/table.h" |
23 | #include "catalog/catalog.h" |
24 | #include "catalog/indexing.h" |
25 | #include "catalog/objectaddress.h" |
26 | #include "catalog/pg_am.h" |
27 | #include "catalog/pg_amop.h" |
28 | #include "catalog/pg_amproc.h" |
29 | #include "catalog/pg_attrdef.h" |
30 | #include "catalog/pg_authid.h" |
31 | #include "catalog/pg_cast.h" |
32 | #include "catalog/pg_default_acl.h" |
33 | #include "catalog/pg_enum.h" |
34 | #include "catalog/pg_event_trigger.h" |
35 | #include "catalog/pg_collation.h" |
36 | #include "catalog/pg_constraint.h" |
37 | #include "catalog/pg_conversion.h" |
38 | #include "catalog/pg_database.h" |
39 | #include "catalog/pg_extension.h" |
40 | #include "catalog/pg_foreign_data_wrapper.h" |
41 | #include "catalog/pg_foreign_server.h" |
42 | #include "catalog/pg_language.h" |
43 | #include "catalog/pg_largeobject.h" |
44 | #include "catalog/pg_largeobject_metadata.h" |
45 | #include "catalog/pg_namespace.h" |
46 | #include "catalog/pg_opclass.h" |
47 | #include "catalog/pg_opfamily.h" |
48 | #include "catalog/pg_operator.h" |
49 | #include "catalog/pg_proc.h" |
50 | #include "catalog/pg_policy.h" |
51 | #include "catalog/pg_publication.h" |
52 | #include "catalog/pg_publication_rel.h" |
53 | #include "catalog/pg_rewrite.h" |
54 | #include "catalog/pg_statistic_ext.h" |
55 | #include "catalog/pg_subscription.h" |
56 | #include "catalog/pg_tablespace.h" |
57 | #include "catalog/pg_transform.h" |
58 | #include "catalog/pg_trigger.h" |
59 | #include "catalog/pg_ts_config.h" |
60 | #include "catalog/pg_ts_dict.h" |
61 | #include "catalog/pg_ts_parser.h" |
62 | #include "catalog/pg_ts_template.h" |
63 | #include "catalog/pg_type.h" |
64 | #include "catalog/pg_user_mapping.h" |
65 | #include "commands/dbcommands.h" |
66 | #include "commands/defrem.h" |
67 | #include "commands/event_trigger.h" |
68 | #include "commands/extension.h" |
69 | #include "commands/policy.h" |
70 | #include "commands/proclang.h" |
71 | #include "commands/tablespace.h" |
72 | #include "commands/trigger.h" |
73 | #include "foreign/foreign.h" |
74 | #include "funcapi.h" |
75 | #include "miscadmin.h" |
76 | #include "nodes/makefuncs.h" |
77 | #include "parser/parse_func.h" |
78 | #include "parser/parse_oper.h" |
79 | #include "parser/parse_type.h" |
80 | #include "rewrite/rewriteSupport.h" |
81 | #include "storage/large_object.h" |
82 | #include "storage/lmgr.h" |
83 | #include "storage/sinval.h" |
84 | #include "utils/builtins.h" |
85 | #include "utils/fmgroids.h" |
86 | #include "utils/lsyscache.h" |
87 | #include "utils/memutils.h" |
88 | #include "utils/regproc.h" |
89 | #include "utils/syscache.h" |
90 | |
91 | /* |
92 | * ObjectProperty |
93 | * |
94 | * This array provides a common part of system object structure; to help |
95 | * consolidate routines to handle various kind of object classes. |
96 | */ |
97 | typedef struct |
98 | { |
99 | Oid class_oid; /* oid of catalog */ |
100 | Oid oid_index_oid; /* oid of index on system oid column */ |
101 | int oid_catcache_id; /* id of catcache on system oid column */ |
102 | int name_catcache_id; /* id of catcache on (name,namespace), or |
103 | * (name) if the object does not live in a |
104 | * namespace */ |
105 | AttrNumber attnum_oid; /* attribute number of oid column */ |
106 | AttrNumber attnum_name; /* attnum of name field */ |
107 | AttrNumber attnum_namespace; /* attnum of namespace field */ |
108 | AttrNumber attnum_owner; /* attnum of owner field */ |
109 | AttrNumber attnum_acl; /* attnum of acl field */ |
110 | ObjectType objtype; /* OBJECT_* of this object type */ |
111 | bool is_nsp_name_unique; /* can the nsp/name combination (or name |
112 | * alone, if there's no namespace) be |
113 | * considered a unique identifier for an |
114 | * object of this class? */ |
115 | } ObjectPropertyType; |
116 | |
117 | static const ObjectPropertyType ObjectProperty[] = |
118 | { |
119 | { |
120 | AccessMethodRelationId, |
121 | AmOidIndexId, |
122 | AMOID, |
123 | AMNAME, |
124 | Anum_pg_am_oid, |
125 | Anum_pg_am_amname, |
126 | InvalidAttrNumber, |
127 | InvalidAttrNumber, |
128 | InvalidAttrNumber, |
129 | -1, |
130 | true |
131 | }, |
132 | { |
133 | CastRelationId, |
134 | CastOidIndexId, |
135 | -1, |
136 | -1, |
137 | Anum_pg_cast_oid, |
138 | InvalidAttrNumber, |
139 | InvalidAttrNumber, |
140 | InvalidAttrNumber, |
141 | InvalidAttrNumber, |
142 | -1, |
143 | false |
144 | }, |
145 | { |
146 | CollationRelationId, |
147 | CollationOidIndexId, |
148 | COLLOID, |
149 | -1, /* COLLNAMEENCNSP also takes encoding */ |
150 | Anum_pg_collation_oid, |
151 | Anum_pg_collation_collname, |
152 | Anum_pg_collation_collnamespace, |
153 | Anum_pg_collation_collowner, |
154 | InvalidAttrNumber, |
155 | OBJECT_COLLATION, |
156 | true |
157 | }, |
158 | { |
159 | ConstraintRelationId, |
160 | ConstraintOidIndexId, |
161 | CONSTROID, |
162 | -1, |
163 | Anum_pg_constraint_oid, |
164 | Anum_pg_constraint_conname, |
165 | Anum_pg_constraint_connamespace, |
166 | InvalidAttrNumber, |
167 | InvalidAttrNumber, |
168 | -1, |
169 | false |
170 | }, |
171 | { |
172 | ConversionRelationId, |
173 | ConversionOidIndexId, |
174 | CONVOID, |
175 | CONNAMENSP, |
176 | Anum_pg_conversion_oid, |
177 | Anum_pg_conversion_conname, |
178 | Anum_pg_conversion_connamespace, |
179 | Anum_pg_conversion_conowner, |
180 | InvalidAttrNumber, |
181 | OBJECT_CONVERSION, |
182 | true |
183 | }, |
184 | { |
185 | DatabaseRelationId, |
186 | DatabaseOidIndexId, |
187 | DATABASEOID, |
188 | -1, |
189 | Anum_pg_database_oid, |
190 | Anum_pg_database_datname, |
191 | InvalidAttrNumber, |
192 | Anum_pg_database_datdba, |
193 | Anum_pg_database_datacl, |
194 | OBJECT_DATABASE, |
195 | true |
196 | }, |
197 | { |
198 | ExtensionRelationId, |
199 | ExtensionOidIndexId, |
200 | -1, |
201 | -1, |
202 | Anum_pg_extension_oid, |
203 | Anum_pg_extension_extname, |
204 | InvalidAttrNumber, /* extension doesn't belong to extnamespace */ |
205 | Anum_pg_extension_extowner, |
206 | InvalidAttrNumber, |
207 | OBJECT_EXTENSION, |
208 | true |
209 | }, |
210 | { |
211 | ForeignDataWrapperRelationId, |
212 | ForeignDataWrapperOidIndexId, |
213 | FOREIGNDATAWRAPPEROID, |
214 | FOREIGNDATAWRAPPERNAME, |
215 | Anum_pg_foreign_data_wrapper_oid, |
216 | Anum_pg_foreign_data_wrapper_fdwname, |
217 | InvalidAttrNumber, |
218 | Anum_pg_foreign_data_wrapper_fdwowner, |
219 | Anum_pg_foreign_data_wrapper_fdwacl, |
220 | OBJECT_FDW, |
221 | true |
222 | }, |
223 | { |
224 | ForeignServerRelationId, |
225 | ForeignServerOidIndexId, |
226 | FOREIGNSERVEROID, |
227 | FOREIGNSERVERNAME, |
228 | Anum_pg_foreign_server_oid, |
229 | Anum_pg_foreign_server_srvname, |
230 | InvalidAttrNumber, |
231 | Anum_pg_foreign_server_srvowner, |
232 | Anum_pg_foreign_server_srvacl, |
233 | OBJECT_FOREIGN_SERVER, |
234 | true |
235 | }, |
236 | { |
237 | ProcedureRelationId, |
238 | ProcedureOidIndexId, |
239 | PROCOID, |
240 | -1, /* PROCNAMEARGSNSP also takes argument types */ |
241 | Anum_pg_proc_oid, |
242 | Anum_pg_proc_proname, |
243 | Anum_pg_proc_pronamespace, |
244 | Anum_pg_proc_proowner, |
245 | Anum_pg_proc_proacl, |
246 | OBJECT_FUNCTION, |
247 | false |
248 | }, |
249 | { |
250 | LanguageRelationId, |
251 | LanguageOidIndexId, |
252 | LANGOID, |
253 | LANGNAME, |
254 | Anum_pg_language_oid, |
255 | Anum_pg_language_lanname, |
256 | InvalidAttrNumber, |
257 | Anum_pg_language_lanowner, |
258 | Anum_pg_language_lanacl, |
259 | OBJECT_LANGUAGE, |
260 | true |
261 | }, |
262 | { |
263 | LargeObjectMetadataRelationId, |
264 | LargeObjectMetadataOidIndexId, |
265 | -1, |
266 | -1, |
267 | Anum_pg_largeobject_metadata_oid, |
268 | InvalidAttrNumber, |
269 | InvalidAttrNumber, |
270 | Anum_pg_largeobject_metadata_lomowner, |
271 | Anum_pg_largeobject_metadata_lomacl, |
272 | OBJECT_LARGEOBJECT, |
273 | false |
274 | }, |
275 | { |
276 | OperatorClassRelationId, |
277 | OpclassOidIndexId, |
278 | CLAOID, |
279 | -1, /* CLAAMNAMENSP also takes opcmethod */ |
280 | Anum_pg_opclass_oid, |
281 | Anum_pg_opclass_opcname, |
282 | Anum_pg_opclass_opcnamespace, |
283 | Anum_pg_opclass_opcowner, |
284 | InvalidAttrNumber, |
285 | OBJECT_OPCLASS, |
286 | true |
287 | }, |
288 | { |
289 | OperatorRelationId, |
290 | OperatorOidIndexId, |
291 | OPEROID, |
292 | -1, /* OPERNAMENSP also takes left and right type */ |
293 | Anum_pg_operator_oid, |
294 | Anum_pg_operator_oprname, |
295 | Anum_pg_operator_oprnamespace, |
296 | Anum_pg_operator_oprowner, |
297 | InvalidAttrNumber, |
298 | OBJECT_OPERATOR, |
299 | false |
300 | }, |
301 | { |
302 | OperatorFamilyRelationId, |
303 | OpfamilyOidIndexId, |
304 | OPFAMILYOID, |
305 | -1, /* OPFAMILYAMNAMENSP also takes opfmethod */ |
306 | Anum_pg_opfamily_oid, |
307 | Anum_pg_opfamily_opfname, |
308 | Anum_pg_opfamily_opfnamespace, |
309 | Anum_pg_opfamily_opfowner, |
310 | InvalidAttrNumber, |
311 | OBJECT_OPFAMILY, |
312 | true |
313 | }, |
314 | { |
315 | AuthIdRelationId, |
316 | AuthIdOidIndexId, |
317 | AUTHOID, |
318 | AUTHNAME, |
319 | Anum_pg_authid_oid, |
320 | Anum_pg_authid_rolname, |
321 | InvalidAttrNumber, |
322 | InvalidAttrNumber, |
323 | InvalidAttrNumber, |
324 | -1, |
325 | true |
326 | }, |
327 | { |
328 | RewriteRelationId, |
329 | RewriteOidIndexId, |
330 | -1, |
331 | -1, |
332 | Anum_pg_rewrite_oid, |
333 | Anum_pg_rewrite_rulename, |
334 | InvalidAttrNumber, |
335 | InvalidAttrNumber, |
336 | InvalidAttrNumber, |
337 | -1, |
338 | false |
339 | }, |
340 | { |
341 | NamespaceRelationId, |
342 | NamespaceOidIndexId, |
343 | NAMESPACEOID, |
344 | NAMESPACENAME, |
345 | Anum_pg_namespace_oid, |
346 | Anum_pg_namespace_nspname, |
347 | InvalidAttrNumber, |
348 | Anum_pg_namespace_nspowner, |
349 | Anum_pg_namespace_nspacl, |
350 | OBJECT_SCHEMA, |
351 | true |
352 | }, |
353 | { |
354 | RelationRelationId, |
355 | ClassOidIndexId, |
356 | RELOID, |
357 | RELNAMENSP, |
358 | Anum_pg_class_oid, |
359 | Anum_pg_class_relname, |
360 | Anum_pg_class_relnamespace, |
361 | Anum_pg_class_relowner, |
362 | Anum_pg_class_relacl, |
363 | OBJECT_TABLE, |
364 | true |
365 | }, |
366 | { |
367 | TableSpaceRelationId, |
368 | TablespaceOidIndexId, |
369 | TABLESPACEOID, |
370 | -1, |
371 | Anum_pg_tablespace_oid, |
372 | Anum_pg_tablespace_spcname, |
373 | InvalidAttrNumber, |
374 | Anum_pg_tablespace_spcowner, |
375 | Anum_pg_tablespace_spcacl, |
376 | OBJECT_TABLESPACE, |
377 | true |
378 | }, |
379 | { |
380 | TransformRelationId, |
381 | TransformOidIndexId, |
382 | TRFOID, |
383 | InvalidAttrNumber, |
384 | Anum_pg_transform_oid |
385 | }, |
386 | { |
387 | TriggerRelationId, |
388 | TriggerOidIndexId, |
389 | -1, |
390 | -1, |
391 | Anum_pg_trigger_oid, |
392 | Anum_pg_trigger_tgname, |
393 | InvalidAttrNumber, |
394 | InvalidAttrNumber, |
395 | InvalidAttrNumber, |
396 | -1, |
397 | false |
398 | }, |
399 | { |
400 | PolicyRelationId, |
401 | PolicyOidIndexId, |
402 | -1, |
403 | -1, |
404 | Anum_pg_policy_oid, |
405 | Anum_pg_policy_polname, |
406 | InvalidAttrNumber, |
407 | InvalidAttrNumber, |
408 | InvalidAttrNumber, |
409 | -1, |
410 | false |
411 | }, |
412 | { |
413 | EventTriggerRelationId, |
414 | EventTriggerOidIndexId, |
415 | EVENTTRIGGEROID, |
416 | EVENTTRIGGERNAME, |
417 | Anum_pg_event_trigger_oid, |
418 | Anum_pg_event_trigger_evtname, |
419 | InvalidAttrNumber, |
420 | Anum_pg_event_trigger_evtowner, |
421 | InvalidAttrNumber, |
422 | OBJECT_EVENT_TRIGGER, |
423 | true |
424 | }, |
425 | { |
426 | TSConfigRelationId, |
427 | TSConfigOidIndexId, |
428 | TSCONFIGOID, |
429 | TSCONFIGNAMENSP, |
430 | Anum_pg_ts_config_oid, |
431 | Anum_pg_ts_config_cfgname, |
432 | Anum_pg_ts_config_cfgnamespace, |
433 | Anum_pg_ts_config_cfgowner, |
434 | InvalidAttrNumber, |
435 | OBJECT_TSCONFIGURATION, |
436 | true |
437 | }, |
438 | { |
439 | TSDictionaryRelationId, |
440 | TSDictionaryOidIndexId, |
441 | TSDICTOID, |
442 | TSDICTNAMENSP, |
443 | Anum_pg_ts_dict_oid, |
444 | Anum_pg_ts_dict_dictname, |
445 | Anum_pg_ts_dict_dictnamespace, |
446 | Anum_pg_ts_dict_dictowner, |
447 | InvalidAttrNumber, |
448 | OBJECT_TSDICTIONARY, |
449 | true |
450 | }, |
451 | { |
452 | TSParserRelationId, |
453 | TSParserOidIndexId, |
454 | TSPARSEROID, |
455 | TSPARSERNAMENSP, |
456 | Anum_pg_ts_parser_oid, |
457 | Anum_pg_ts_parser_prsname, |
458 | Anum_pg_ts_parser_prsnamespace, |
459 | InvalidAttrNumber, |
460 | InvalidAttrNumber, |
461 | -1, |
462 | true |
463 | }, |
464 | { |
465 | TSTemplateRelationId, |
466 | TSTemplateOidIndexId, |
467 | TSTEMPLATEOID, |
468 | TSTEMPLATENAMENSP, |
469 | Anum_pg_ts_template_oid, |
470 | Anum_pg_ts_template_tmplname, |
471 | Anum_pg_ts_template_tmplnamespace, |
472 | InvalidAttrNumber, |
473 | InvalidAttrNumber, |
474 | -1, |
475 | true, |
476 | }, |
477 | { |
478 | TypeRelationId, |
479 | TypeOidIndexId, |
480 | TYPEOID, |
481 | TYPENAMENSP, |
482 | Anum_pg_type_oid, |
483 | Anum_pg_type_typname, |
484 | Anum_pg_type_typnamespace, |
485 | Anum_pg_type_typowner, |
486 | Anum_pg_type_typacl, |
487 | OBJECT_TYPE, |
488 | true |
489 | }, |
490 | { |
491 | PublicationRelationId, |
492 | PublicationObjectIndexId, |
493 | PUBLICATIONOID, |
494 | PUBLICATIONNAME, |
495 | Anum_pg_publication_oid, |
496 | Anum_pg_publication_pubname, |
497 | InvalidAttrNumber, |
498 | Anum_pg_publication_pubowner, |
499 | InvalidAttrNumber, |
500 | OBJECT_PUBLICATION, |
501 | true |
502 | }, |
503 | { |
504 | SubscriptionRelationId, |
505 | SubscriptionObjectIndexId, |
506 | SUBSCRIPTIONOID, |
507 | SUBSCRIPTIONNAME, |
508 | Anum_pg_subscription_oid, |
509 | Anum_pg_subscription_subname, |
510 | InvalidAttrNumber, |
511 | Anum_pg_subscription_subowner, |
512 | InvalidAttrNumber, |
513 | OBJECT_SUBSCRIPTION, |
514 | true |
515 | }, |
516 | { |
517 | StatisticExtRelationId, |
518 | StatisticExtOidIndexId, |
519 | STATEXTOID, |
520 | STATEXTNAMENSP, |
521 | Anum_pg_statistic_ext_oid, |
522 | Anum_pg_statistic_ext_stxname, |
523 | Anum_pg_statistic_ext_stxnamespace, |
524 | Anum_pg_statistic_ext_stxowner, |
525 | InvalidAttrNumber, /* no ACL (same as relation) */ |
526 | OBJECT_STATISTIC_EXT, |
527 | true |
528 | } |
529 | }; |
530 | |
531 | /* |
532 | * This struct maps the string object types as returned by |
533 | * getObjectTypeDescription into ObjType enum values. Note that some enum |
534 | * values can be obtained by different names, and that some string object types |
535 | * do not have corresponding values in the output enum. The user of this map |
536 | * must be careful to test for invalid values being returned. |
537 | * |
538 | * To ease maintenance, this follows the order of getObjectTypeDescription. |
539 | */ |
540 | static const struct object_type_map |
541 | { |
542 | const char *tm_name; |
543 | ObjectType tm_type; |
544 | } |
545 | |
546 | ObjectTypeMap[] = |
547 | { |
548 | /* OCLASS_CLASS, all kinds of relations */ |
549 | { |
550 | "table" , OBJECT_TABLE |
551 | }, |
552 | { |
553 | "index" , OBJECT_INDEX |
554 | }, |
555 | { |
556 | "sequence" , OBJECT_SEQUENCE |
557 | }, |
558 | { |
559 | "toast table" , -1 |
560 | }, /* unmapped */ |
561 | { |
562 | "view" , OBJECT_VIEW |
563 | }, |
564 | { |
565 | "materialized view" , OBJECT_MATVIEW |
566 | }, |
567 | { |
568 | "composite type" , -1 |
569 | }, /* unmapped */ |
570 | { |
571 | "foreign table" , OBJECT_FOREIGN_TABLE |
572 | }, |
573 | { |
574 | "table column" , OBJECT_COLUMN |
575 | }, |
576 | { |
577 | "index column" , -1 |
578 | }, /* unmapped */ |
579 | { |
580 | "sequence column" , -1 |
581 | }, /* unmapped */ |
582 | { |
583 | "toast table column" , -1 |
584 | }, /* unmapped */ |
585 | { |
586 | "view column" , -1 |
587 | }, /* unmapped */ |
588 | { |
589 | "materialized view column" , -1 |
590 | }, /* unmapped */ |
591 | { |
592 | "composite type column" , -1 |
593 | }, /* unmapped */ |
594 | { |
595 | "foreign table column" , OBJECT_COLUMN |
596 | }, |
597 | /* OCLASS_PROC */ |
598 | { |
599 | "aggregate" , OBJECT_AGGREGATE |
600 | }, |
601 | { |
602 | "function" , OBJECT_FUNCTION |
603 | }, |
604 | { |
605 | "procedure" , OBJECT_PROCEDURE |
606 | }, |
607 | /* OCLASS_TYPE */ |
608 | { |
609 | "type" , OBJECT_TYPE |
610 | }, |
611 | /* OCLASS_CAST */ |
612 | { |
613 | "cast" , OBJECT_CAST |
614 | }, |
615 | /* OCLASS_COLLATION */ |
616 | { |
617 | "collation" , OBJECT_COLLATION |
618 | }, |
619 | /* OCLASS_CONSTRAINT */ |
620 | { |
621 | "table constraint" , OBJECT_TABCONSTRAINT |
622 | }, |
623 | { |
624 | "domain constraint" , OBJECT_DOMCONSTRAINT |
625 | }, |
626 | /* OCLASS_CONVERSION */ |
627 | { |
628 | "conversion" , OBJECT_CONVERSION |
629 | }, |
630 | /* OCLASS_DEFAULT */ |
631 | { |
632 | "default value" , OBJECT_DEFAULT |
633 | }, |
634 | /* OCLASS_LANGUAGE */ |
635 | { |
636 | "language" , OBJECT_LANGUAGE |
637 | }, |
638 | /* OCLASS_LARGEOBJECT */ |
639 | { |
640 | "large object" , OBJECT_LARGEOBJECT |
641 | }, |
642 | /* OCLASS_OPERATOR */ |
643 | { |
644 | "operator" , OBJECT_OPERATOR |
645 | }, |
646 | /* OCLASS_OPCLASS */ |
647 | { |
648 | "operator class" , OBJECT_OPCLASS |
649 | }, |
650 | /* OCLASS_OPFAMILY */ |
651 | { |
652 | "operator family" , OBJECT_OPFAMILY |
653 | }, |
654 | /* OCLASS_AM */ |
655 | { |
656 | "access method" , OBJECT_ACCESS_METHOD |
657 | }, |
658 | /* OCLASS_AMOP */ |
659 | { |
660 | "operator of access method" , OBJECT_AMOP |
661 | }, |
662 | /* OCLASS_AMPROC */ |
663 | { |
664 | "function of access method" , OBJECT_AMPROC |
665 | }, |
666 | /* OCLASS_REWRITE */ |
667 | { |
668 | "rule" , OBJECT_RULE |
669 | }, |
670 | /* OCLASS_TRIGGER */ |
671 | { |
672 | "trigger" , OBJECT_TRIGGER |
673 | }, |
674 | /* OCLASS_SCHEMA */ |
675 | { |
676 | "schema" , OBJECT_SCHEMA |
677 | }, |
678 | /* OCLASS_TSPARSER */ |
679 | { |
680 | "text search parser" , OBJECT_TSPARSER |
681 | }, |
682 | /* OCLASS_TSDICT */ |
683 | { |
684 | "text search dictionary" , OBJECT_TSDICTIONARY |
685 | }, |
686 | /* OCLASS_TSTEMPLATE */ |
687 | { |
688 | "text search template" , OBJECT_TSTEMPLATE |
689 | }, |
690 | /* OCLASS_TSCONFIG */ |
691 | { |
692 | "text search configuration" , OBJECT_TSCONFIGURATION |
693 | }, |
694 | /* OCLASS_ROLE */ |
695 | { |
696 | "role" , OBJECT_ROLE |
697 | }, |
698 | /* OCLASS_DATABASE */ |
699 | { |
700 | "database" , OBJECT_DATABASE |
701 | }, |
702 | /* OCLASS_TBLSPACE */ |
703 | { |
704 | "tablespace" , OBJECT_TABLESPACE |
705 | }, |
706 | /* OCLASS_FDW */ |
707 | { |
708 | "foreign-data wrapper" , OBJECT_FDW |
709 | }, |
710 | /* OCLASS_FOREIGN_SERVER */ |
711 | { |
712 | "server" , OBJECT_FOREIGN_SERVER |
713 | }, |
714 | /* OCLASS_USER_MAPPING */ |
715 | { |
716 | "user mapping" , OBJECT_USER_MAPPING |
717 | }, |
718 | /* OCLASS_DEFACL */ |
719 | { |
720 | "default acl" , OBJECT_DEFACL |
721 | }, |
722 | /* OCLASS_EXTENSION */ |
723 | { |
724 | "extension" , OBJECT_EXTENSION |
725 | }, |
726 | /* OCLASS_EVENT_TRIGGER */ |
727 | { |
728 | "event trigger" , OBJECT_EVENT_TRIGGER |
729 | }, |
730 | /* OCLASS_POLICY */ |
731 | { |
732 | "policy" , OBJECT_POLICY |
733 | }, |
734 | /* OCLASS_PUBLICATION */ |
735 | { |
736 | "publication" , OBJECT_PUBLICATION |
737 | }, |
738 | /* OCLASS_PUBLICATION_REL */ |
739 | { |
740 | "publication relation" , OBJECT_PUBLICATION_REL |
741 | }, |
742 | /* OCLASS_SUBSCRIPTION */ |
743 | { |
744 | "subscription" , OBJECT_SUBSCRIPTION |
745 | }, |
746 | /* OCLASS_TRANSFORM */ |
747 | { |
748 | "transform" , OBJECT_TRANSFORM |
749 | }, |
750 | /* OBJECT_STATISTIC_EXT */ |
751 | { |
752 | "statistics object" , OBJECT_STATISTIC_EXT |
753 | } |
754 | }; |
755 | |
756 | const ObjectAddress InvalidObjectAddress = |
757 | { |
758 | InvalidOid, |
759 | InvalidOid, |
760 | 0 |
761 | }; |
762 | |
763 | static ObjectAddress get_object_address_unqualified(ObjectType objtype, |
764 | Value *strval, bool missing_ok); |
765 | static ObjectAddress get_relation_by_qualified_name(ObjectType objtype, |
766 | List *object, Relation *relp, |
767 | LOCKMODE lockmode, bool missing_ok); |
768 | static ObjectAddress get_object_address_relobject(ObjectType objtype, |
769 | List *object, Relation *relp, bool missing_ok); |
770 | static ObjectAddress get_object_address_attribute(ObjectType objtype, |
771 | List *object, Relation *relp, |
772 | LOCKMODE lockmode, bool missing_ok); |
773 | static ObjectAddress get_object_address_attrdef(ObjectType objtype, |
774 | List *object, Relation *relp, LOCKMODE lockmode, |
775 | bool missing_ok); |
776 | static ObjectAddress get_object_address_type(ObjectType objtype, |
777 | TypeName *typename, bool missing_ok); |
778 | static ObjectAddress get_object_address_opcf(ObjectType objtype, List *object, |
779 | bool missing_ok); |
780 | static ObjectAddress get_object_address_opf_member(ObjectType objtype, |
781 | List *object, bool missing_ok); |
782 | |
783 | static ObjectAddress get_object_address_usermapping(List *object, |
784 | bool missing_ok); |
785 | static ObjectAddress get_object_address_publication_rel(List *object, |
786 | Relation *relp, |
787 | bool missing_ok); |
788 | static ObjectAddress get_object_address_defacl(List *object, |
789 | bool missing_ok); |
790 | static const ObjectPropertyType *get_object_property_data(Oid class_id); |
791 | |
792 | static void getRelationDescription(StringInfo buffer, Oid relid); |
793 | static void getOpFamilyDescription(StringInfo buffer, Oid opfid); |
794 | static void getRelationTypeDescription(StringInfo buffer, Oid relid, |
795 | int32 objectSubId); |
796 | static void getProcedureTypeDescription(StringInfo buffer, Oid procid); |
797 | static void getConstraintTypeDescription(StringInfo buffer, Oid constroid); |
798 | static void getOpFamilyIdentity(StringInfo buffer, Oid opfid, List **object); |
799 | static void getRelationIdentity(StringInfo buffer, Oid relid, List **object); |
800 | |
801 | /* |
802 | * Translate an object name and arguments (as passed by the parser) to an |
803 | * ObjectAddress. |
804 | * |
805 | * The returned object will be locked using the specified lockmode. If a |
806 | * sub-object is looked up, the parent object will be locked instead. |
807 | * |
808 | * If the object is a relation or a child object of a relation (e.g. an |
809 | * attribute or constraint), the relation is also opened and *relp receives |
810 | * the open relcache entry pointer; otherwise, *relp is set to NULL. This |
811 | * is a bit grotty but it makes life simpler, since the caller will |
812 | * typically need the relcache entry too. Caller must close the relcache |
813 | * entry when done with it. The relation is locked with the specified lockmode |
814 | * if the target object is the relation itself or an attribute, but for other |
815 | * child objects, only AccessShareLock is acquired on the relation. |
816 | * |
817 | * If the object is not found, an error is thrown, unless missing_ok is |
818 | * true. In this case, no lock is acquired, relp is set to NULL, and the |
819 | * returned address has objectId set to InvalidOid. |
820 | * |
821 | * We don't currently provide a function to release the locks acquired here; |
822 | * typically, the lock must be held until commit to guard against a concurrent |
823 | * drop operation. |
824 | * |
825 | * Note: If the object is not found, we don't give any indication of the |
826 | * reason. (It might have been a missing schema if the name was qualified, or |
827 | * a nonexistent type name in case of a cast, function or operator; etc). |
828 | * Currently there is only one caller that might be interested in such info, so |
829 | * we don't spend much effort here. If more callers start to care, it might be |
830 | * better to add some support for that in this function. |
831 | */ |
832 | ObjectAddress |
833 | get_object_address(ObjectType objtype, Node *object, |
834 | Relation *relp, LOCKMODE lockmode, bool missing_ok) |
835 | { |
836 | ObjectAddress address; |
837 | ObjectAddress old_address = {InvalidOid, InvalidOid, 0}; |
838 | Relation relation = NULL; |
839 | uint64 inval_count; |
840 | |
841 | /* Some kind of lock must be taken. */ |
842 | Assert(lockmode != NoLock); |
843 | |
844 | for (;;) |
845 | { |
846 | /* |
847 | * Remember this value, so that, after looking up the object name and |
848 | * locking it, we can check whether any invalidation messages have |
849 | * been processed that might require a do-over. |
850 | */ |
851 | inval_count = SharedInvalidMessageCounter; |
852 | |
853 | /* Look up object address. */ |
854 | switch (objtype) |
855 | { |
856 | case OBJECT_INDEX: |
857 | case OBJECT_SEQUENCE: |
858 | case OBJECT_TABLE: |
859 | case OBJECT_VIEW: |
860 | case OBJECT_MATVIEW: |
861 | case OBJECT_FOREIGN_TABLE: |
862 | address = |
863 | get_relation_by_qualified_name(objtype, castNode(List, object), |
864 | &relation, lockmode, |
865 | missing_ok); |
866 | break; |
867 | case OBJECT_COLUMN: |
868 | address = |
869 | get_object_address_attribute(objtype, castNode(List, object), |
870 | &relation, lockmode, |
871 | missing_ok); |
872 | break; |
873 | case OBJECT_DEFAULT: |
874 | address = |
875 | get_object_address_attrdef(objtype, castNode(List, object), |
876 | &relation, lockmode, |
877 | missing_ok); |
878 | break; |
879 | case OBJECT_RULE: |
880 | case OBJECT_TRIGGER: |
881 | case OBJECT_TABCONSTRAINT: |
882 | case OBJECT_POLICY: |
883 | address = get_object_address_relobject(objtype, castNode(List, object), |
884 | &relation, missing_ok); |
885 | break; |
886 | case OBJECT_DOMCONSTRAINT: |
887 | { |
888 | List *objlist; |
889 | ObjectAddress domaddr; |
890 | char *constrname; |
891 | |
892 | objlist = castNode(List, object); |
893 | domaddr = get_object_address_type(OBJECT_DOMAIN, |
894 | linitial_node(TypeName, objlist), |
895 | missing_ok); |
896 | constrname = strVal(lsecond(objlist)); |
897 | |
898 | address.classId = ConstraintRelationId; |
899 | address.objectId = get_domain_constraint_oid(domaddr.objectId, |
900 | constrname, missing_ok); |
901 | address.objectSubId = 0; |
902 | |
903 | } |
904 | break; |
905 | case OBJECT_DATABASE: |
906 | case OBJECT_EXTENSION: |
907 | case OBJECT_TABLESPACE: |
908 | case OBJECT_ROLE: |
909 | case OBJECT_SCHEMA: |
910 | case OBJECT_LANGUAGE: |
911 | case OBJECT_FDW: |
912 | case OBJECT_FOREIGN_SERVER: |
913 | case OBJECT_EVENT_TRIGGER: |
914 | case OBJECT_ACCESS_METHOD: |
915 | case OBJECT_PUBLICATION: |
916 | case OBJECT_SUBSCRIPTION: |
917 | address = get_object_address_unqualified(objtype, |
918 | (Value *) object, missing_ok); |
919 | break; |
920 | case OBJECT_TYPE: |
921 | case OBJECT_DOMAIN: |
922 | address = get_object_address_type(objtype, castNode(TypeName, object), missing_ok); |
923 | break; |
924 | case OBJECT_AGGREGATE: |
925 | case OBJECT_FUNCTION: |
926 | case OBJECT_PROCEDURE: |
927 | case OBJECT_ROUTINE: |
928 | address.classId = ProcedureRelationId; |
929 | address.objectId = LookupFuncWithArgs(objtype, castNode(ObjectWithArgs, object), missing_ok); |
930 | address.objectSubId = 0; |
931 | break; |
932 | case OBJECT_OPERATOR: |
933 | address.classId = OperatorRelationId; |
934 | address.objectId = LookupOperWithArgs(castNode(ObjectWithArgs, object), missing_ok); |
935 | address.objectSubId = 0; |
936 | break; |
937 | case OBJECT_COLLATION: |
938 | address.classId = CollationRelationId; |
939 | address.objectId = get_collation_oid(castNode(List, object), missing_ok); |
940 | address.objectSubId = 0; |
941 | break; |
942 | case OBJECT_CONVERSION: |
943 | address.classId = ConversionRelationId; |
944 | address.objectId = get_conversion_oid(castNode(List, object), missing_ok); |
945 | address.objectSubId = 0; |
946 | break; |
947 | case OBJECT_OPCLASS: |
948 | case OBJECT_OPFAMILY: |
949 | address = get_object_address_opcf(objtype, castNode(List, object), missing_ok); |
950 | break; |
951 | case OBJECT_AMOP: |
952 | case OBJECT_AMPROC: |
953 | address = get_object_address_opf_member(objtype, castNode(List, object), missing_ok); |
954 | break; |
955 | case OBJECT_LARGEOBJECT: |
956 | address.classId = LargeObjectRelationId; |
957 | address.objectId = oidparse(object); |
958 | address.objectSubId = 0; |
959 | if (!LargeObjectExists(address.objectId)) |
960 | { |
961 | if (!missing_ok) |
962 | ereport(ERROR, |
963 | (errcode(ERRCODE_UNDEFINED_OBJECT), |
964 | errmsg("large object %u does not exist" , |
965 | address.objectId))); |
966 | } |
967 | break; |
968 | case OBJECT_CAST: |
969 | { |
970 | TypeName *sourcetype = linitial_node(TypeName, castNode(List, object)); |
971 | TypeName *targettype = lsecond_node(TypeName, castNode(List, object)); |
972 | Oid sourcetypeid; |
973 | Oid targettypeid; |
974 | |
975 | sourcetypeid = LookupTypeNameOid(NULL, sourcetype, missing_ok); |
976 | targettypeid = LookupTypeNameOid(NULL, targettype, missing_ok); |
977 | address.classId = CastRelationId; |
978 | address.objectId = |
979 | get_cast_oid(sourcetypeid, targettypeid, missing_ok); |
980 | address.objectSubId = 0; |
981 | } |
982 | break; |
983 | case OBJECT_TRANSFORM: |
984 | { |
985 | TypeName *typename = linitial_node(TypeName, castNode(List, object)); |
986 | char *langname = strVal(lsecond(castNode(List, object))); |
987 | Oid type_id = LookupTypeNameOid(NULL, typename, missing_ok); |
988 | Oid lang_id = get_language_oid(langname, missing_ok); |
989 | |
990 | address.classId = TransformRelationId; |
991 | address.objectId = |
992 | get_transform_oid(type_id, lang_id, missing_ok); |
993 | address.objectSubId = 0; |
994 | } |
995 | break; |
996 | case OBJECT_TSPARSER: |
997 | address.classId = TSParserRelationId; |
998 | address.objectId = get_ts_parser_oid(castNode(List, object), missing_ok); |
999 | address.objectSubId = 0; |
1000 | break; |
1001 | case OBJECT_TSDICTIONARY: |
1002 | address.classId = TSDictionaryRelationId; |
1003 | address.objectId = get_ts_dict_oid(castNode(List, object), missing_ok); |
1004 | address.objectSubId = 0; |
1005 | break; |
1006 | case OBJECT_TSTEMPLATE: |
1007 | address.classId = TSTemplateRelationId; |
1008 | address.objectId = get_ts_template_oid(castNode(List, object), missing_ok); |
1009 | address.objectSubId = 0; |
1010 | break; |
1011 | case OBJECT_TSCONFIGURATION: |
1012 | address.classId = TSConfigRelationId; |
1013 | address.objectId = get_ts_config_oid(castNode(List, object), missing_ok); |
1014 | address.objectSubId = 0; |
1015 | break; |
1016 | case OBJECT_USER_MAPPING: |
1017 | address = get_object_address_usermapping(castNode(List, object), |
1018 | missing_ok); |
1019 | break; |
1020 | case OBJECT_PUBLICATION_REL: |
1021 | address = get_object_address_publication_rel(castNode(List, object), |
1022 | &relation, |
1023 | missing_ok); |
1024 | break; |
1025 | case OBJECT_DEFACL: |
1026 | address = get_object_address_defacl(castNode(List, object), |
1027 | missing_ok); |
1028 | break; |
1029 | case OBJECT_STATISTIC_EXT: |
1030 | address.classId = StatisticExtRelationId; |
1031 | address.objectId = get_statistics_object_oid(castNode(List, object), |
1032 | missing_ok); |
1033 | address.objectSubId = 0; |
1034 | break; |
1035 | default: |
1036 | elog(ERROR, "unrecognized objtype: %d" , (int) objtype); |
1037 | /* placate compiler, in case it thinks elog might return */ |
1038 | address.classId = InvalidOid; |
1039 | address.objectId = InvalidOid; |
1040 | address.objectSubId = 0; |
1041 | } |
1042 | |
1043 | /* |
1044 | * If we could not find the supplied object, return without locking. |
1045 | */ |
1046 | if (!OidIsValid(address.objectId)) |
1047 | { |
1048 | Assert(missing_ok); |
1049 | return address; |
1050 | } |
1051 | |
1052 | /* |
1053 | * If we're retrying, see if we got the same answer as last time. If |
1054 | * so, we're done; if not, we locked the wrong thing, so give up our |
1055 | * lock. |
1056 | */ |
1057 | if (OidIsValid(old_address.classId)) |
1058 | { |
1059 | if (old_address.classId == address.classId |
1060 | && old_address.objectId == address.objectId |
1061 | && old_address.objectSubId == address.objectSubId) |
1062 | break; |
1063 | if (old_address.classId != RelationRelationId) |
1064 | { |
1065 | if (IsSharedRelation(old_address.classId)) |
1066 | UnlockSharedObject(old_address.classId, |
1067 | old_address.objectId, |
1068 | 0, lockmode); |
1069 | else |
1070 | UnlockDatabaseObject(old_address.classId, |
1071 | old_address.objectId, |
1072 | 0, lockmode); |
1073 | } |
1074 | } |
1075 | |
1076 | /* |
1077 | * If we're dealing with a relation or attribute, then the relation is |
1078 | * already locked. Otherwise, we lock it now. |
1079 | */ |
1080 | if (address.classId != RelationRelationId) |
1081 | { |
1082 | if (IsSharedRelation(address.classId)) |
1083 | LockSharedObject(address.classId, address.objectId, 0, |
1084 | lockmode); |
1085 | else |
1086 | LockDatabaseObject(address.classId, address.objectId, 0, |
1087 | lockmode); |
1088 | } |
1089 | |
1090 | /* |
1091 | * At this point, we've resolved the name to an OID and locked the |
1092 | * corresponding database object. However, it's possible that by the |
1093 | * time we acquire the lock on the object, concurrent DDL has modified |
1094 | * the database in such a way that the name we originally looked up no |
1095 | * longer resolves to that OID. |
1096 | * |
1097 | * We can be certain that this isn't an issue if (a) no shared |
1098 | * invalidation messages have been processed or (b) we've locked a |
1099 | * relation somewhere along the line. All the relation name lookups |
1100 | * in this module ultimately use RangeVarGetRelid() to acquire a |
1101 | * relation lock, and that function protects against the same kinds of |
1102 | * races we're worried about here. Even when operating on a |
1103 | * constraint, rule, or trigger, we still acquire AccessShareLock on |
1104 | * the relation, which is enough to freeze out any concurrent DDL. |
1105 | * |
1106 | * In all other cases, however, it's possible that the name we looked |
1107 | * up no longer refers to the object we locked, so we retry the lookup |
1108 | * and see whether we get the same answer. |
1109 | */ |
1110 | if (inval_count == SharedInvalidMessageCounter || relation != NULL) |
1111 | break; |
1112 | old_address = address; |
1113 | } |
1114 | |
1115 | /* Return the object address and the relation. */ |
1116 | *relp = relation; |
1117 | return address; |
1118 | } |
1119 | |
1120 | /* |
1121 | * Return an ObjectAddress based on a RangeVar and an object name. The |
1122 | * name of the relation identified by the RangeVar is prepended to the |
1123 | * (possibly empty) list passed in as object. This is useful to find |
1124 | * the ObjectAddress of objects that depend on a relation. All other |
1125 | * considerations are exactly as for get_object_address above. |
1126 | */ |
1127 | ObjectAddress |
1128 | get_object_address_rv(ObjectType objtype, RangeVar *rel, List *object, |
1129 | Relation *relp, LOCKMODE lockmode, |
1130 | bool missing_ok) |
1131 | { |
1132 | if (rel) |
1133 | { |
1134 | object = lcons(makeString(rel->relname), object); |
1135 | if (rel->schemaname) |
1136 | object = lcons(makeString(rel->schemaname), object); |
1137 | if (rel->catalogname) |
1138 | object = lcons(makeString(rel->catalogname), object); |
1139 | } |
1140 | |
1141 | return get_object_address(objtype, (Node *) object, |
1142 | relp, lockmode, missing_ok); |
1143 | } |
1144 | |
1145 | /* |
1146 | * Find an ObjectAddress for a type of object that is identified by an |
1147 | * unqualified name. |
1148 | */ |
1149 | static ObjectAddress |
1150 | get_object_address_unqualified(ObjectType objtype, |
1151 | Value *strval, bool missing_ok) |
1152 | { |
1153 | const char *name; |
1154 | ObjectAddress address; |
1155 | |
1156 | name = strVal(strval); |
1157 | |
1158 | /* Translate name to OID. */ |
1159 | switch (objtype) |
1160 | { |
1161 | case OBJECT_ACCESS_METHOD: |
1162 | address.classId = AccessMethodRelationId; |
1163 | address.objectId = get_am_oid(name, missing_ok); |
1164 | address.objectSubId = 0; |
1165 | break; |
1166 | case OBJECT_DATABASE: |
1167 | address.classId = DatabaseRelationId; |
1168 | address.objectId = get_database_oid(name, missing_ok); |
1169 | address.objectSubId = 0; |
1170 | break; |
1171 | case OBJECT_EXTENSION: |
1172 | address.classId = ExtensionRelationId; |
1173 | address.objectId = get_extension_oid(name, missing_ok); |
1174 | address.objectSubId = 0; |
1175 | break; |
1176 | case OBJECT_TABLESPACE: |
1177 | address.classId = TableSpaceRelationId; |
1178 | address.objectId = get_tablespace_oid(name, missing_ok); |
1179 | address.objectSubId = 0; |
1180 | break; |
1181 | case OBJECT_ROLE: |
1182 | address.classId = AuthIdRelationId; |
1183 | address.objectId = get_role_oid(name, missing_ok); |
1184 | address.objectSubId = 0; |
1185 | break; |
1186 | case OBJECT_SCHEMA: |
1187 | address.classId = NamespaceRelationId; |
1188 | address.objectId = get_namespace_oid(name, missing_ok); |
1189 | address.objectSubId = 0; |
1190 | break; |
1191 | case OBJECT_LANGUAGE: |
1192 | address.classId = LanguageRelationId; |
1193 | address.objectId = get_language_oid(name, missing_ok); |
1194 | address.objectSubId = 0; |
1195 | break; |
1196 | case OBJECT_FDW: |
1197 | address.classId = ForeignDataWrapperRelationId; |
1198 | address.objectId = get_foreign_data_wrapper_oid(name, missing_ok); |
1199 | address.objectSubId = 0; |
1200 | break; |
1201 | case OBJECT_FOREIGN_SERVER: |
1202 | address.classId = ForeignServerRelationId; |
1203 | address.objectId = get_foreign_server_oid(name, missing_ok); |
1204 | address.objectSubId = 0; |
1205 | break; |
1206 | case OBJECT_EVENT_TRIGGER: |
1207 | address.classId = EventTriggerRelationId; |
1208 | address.objectId = get_event_trigger_oid(name, missing_ok); |
1209 | address.objectSubId = 0; |
1210 | break; |
1211 | case OBJECT_PUBLICATION: |
1212 | address.classId = PublicationRelationId; |
1213 | address.objectId = get_publication_oid(name, missing_ok); |
1214 | address.objectSubId = 0; |
1215 | break; |
1216 | case OBJECT_SUBSCRIPTION: |
1217 | address.classId = SubscriptionRelationId; |
1218 | address.objectId = get_subscription_oid(name, missing_ok); |
1219 | address.objectSubId = 0; |
1220 | break; |
1221 | default: |
1222 | elog(ERROR, "unrecognized objtype: %d" , (int) objtype); |
1223 | /* placate compiler, which doesn't know elog won't return */ |
1224 | address.classId = InvalidOid; |
1225 | address.objectId = InvalidOid; |
1226 | address.objectSubId = 0; |
1227 | } |
1228 | |
1229 | return address; |
1230 | } |
1231 | |
1232 | /* |
1233 | * Locate a relation by qualified name. |
1234 | */ |
1235 | static ObjectAddress |
1236 | get_relation_by_qualified_name(ObjectType objtype, List *object, |
1237 | Relation *relp, LOCKMODE lockmode, |
1238 | bool missing_ok) |
1239 | { |
1240 | Relation relation; |
1241 | ObjectAddress address; |
1242 | |
1243 | address.classId = RelationRelationId; |
1244 | address.objectId = InvalidOid; |
1245 | address.objectSubId = 0; |
1246 | |
1247 | relation = relation_openrv_extended(makeRangeVarFromNameList(object), |
1248 | lockmode, missing_ok); |
1249 | if (!relation) |
1250 | return address; |
1251 | |
1252 | switch (objtype) |
1253 | { |
1254 | case OBJECT_INDEX: |
1255 | if (relation->rd_rel->relkind != RELKIND_INDEX && |
1256 | relation->rd_rel->relkind != RELKIND_PARTITIONED_INDEX) |
1257 | ereport(ERROR, |
1258 | (errcode(ERRCODE_WRONG_OBJECT_TYPE), |
1259 | errmsg("\"%s\" is not an index" , |
1260 | RelationGetRelationName(relation)))); |
1261 | break; |
1262 | case OBJECT_SEQUENCE: |
1263 | if (relation->rd_rel->relkind != RELKIND_SEQUENCE) |
1264 | ereport(ERROR, |
1265 | (errcode(ERRCODE_WRONG_OBJECT_TYPE), |
1266 | errmsg("\"%s\" is not a sequence" , |
1267 | RelationGetRelationName(relation)))); |
1268 | break; |
1269 | case OBJECT_TABLE: |
1270 | if (relation->rd_rel->relkind != RELKIND_RELATION && |
1271 | relation->rd_rel->relkind != RELKIND_PARTITIONED_TABLE) |
1272 | ereport(ERROR, |
1273 | (errcode(ERRCODE_WRONG_OBJECT_TYPE), |
1274 | errmsg("\"%s\" is not a table" , |
1275 | RelationGetRelationName(relation)))); |
1276 | break; |
1277 | case OBJECT_VIEW: |
1278 | if (relation->rd_rel->relkind != RELKIND_VIEW) |
1279 | ereport(ERROR, |
1280 | (errcode(ERRCODE_WRONG_OBJECT_TYPE), |
1281 | errmsg("\"%s\" is not a view" , |
1282 | RelationGetRelationName(relation)))); |
1283 | break; |
1284 | case OBJECT_MATVIEW: |
1285 | if (relation->rd_rel->relkind != RELKIND_MATVIEW) |
1286 | ereport(ERROR, |
1287 | (errcode(ERRCODE_WRONG_OBJECT_TYPE), |
1288 | errmsg("\"%s\" is not a materialized view" , |
1289 | RelationGetRelationName(relation)))); |
1290 | break; |
1291 | case OBJECT_FOREIGN_TABLE: |
1292 | if (relation->rd_rel->relkind != RELKIND_FOREIGN_TABLE) |
1293 | ereport(ERROR, |
1294 | (errcode(ERRCODE_WRONG_OBJECT_TYPE), |
1295 | errmsg("\"%s\" is not a foreign table" , |
1296 | RelationGetRelationName(relation)))); |
1297 | break; |
1298 | default: |
1299 | elog(ERROR, "unrecognized objtype: %d" , (int) objtype); |
1300 | break; |
1301 | } |
1302 | |
1303 | /* Done. */ |
1304 | address.objectId = RelationGetRelid(relation); |
1305 | *relp = relation; |
1306 | |
1307 | return address; |
1308 | } |
1309 | |
1310 | /* |
1311 | * Find object address for an object that is attached to a relation. |
1312 | * |
1313 | * Note that we take only an AccessShareLock on the relation. We need not |
1314 | * pass down the LOCKMODE from get_object_address(), because that is the lock |
1315 | * mode for the object itself, not the relation to which it is attached. |
1316 | */ |
1317 | static ObjectAddress |
1318 | get_object_address_relobject(ObjectType objtype, List *object, |
1319 | Relation *relp, bool missing_ok) |
1320 | { |
1321 | ObjectAddress address; |
1322 | Relation relation = NULL; |
1323 | int nnames; |
1324 | const char *depname; |
1325 | List *relname; |
1326 | Oid reloid; |
1327 | |
1328 | /* Extract name of dependent object. */ |
1329 | depname = strVal(llast(object)); |
1330 | |
1331 | /* Separate relation name from dependent object name. */ |
1332 | nnames = list_length(object); |
1333 | if (nnames < 2) |
1334 | ereport(ERROR, |
1335 | (errcode(ERRCODE_SYNTAX_ERROR), |
1336 | errmsg("must specify relation and object name" ))); |
1337 | |
1338 | /* Extract relation name and open relation. */ |
1339 | relname = list_truncate(list_copy(object), nnames - 1); |
1340 | relation = table_openrv_extended(makeRangeVarFromNameList(relname), |
1341 | AccessShareLock, |
1342 | missing_ok); |
1343 | |
1344 | reloid = relation ? RelationGetRelid(relation) : InvalidOid; |
1345 | |
1346 | switch (objtype) |
1347 | { |
1348 | case OBJECT_RULE: |
1349 | address.classId = RewriteRelationId; |
1350 | address.objectId = relation ? |
1351 | get_rewrite_oid(reloid, depname, missing_ok) : InvalidOid; |
1352 | address.objectSubId = 0; |
1353 | break; |
1354 | case OBJECT_TRIGGER: |
1355 | address.classId = TriggerRelationId; |
1356 | address.objectId = relation ? |
1357 | get_trigger_oid(reloid, depname, missing_ok) : InvalidOid; |
1358 | address.objectSubId = 0; |
1359 | break; |
1360 | case OBJECT_TABCONSTRAINT: |
1361 | address.classId = ConstraintRelationId; |
1362 | address.objectId = relation ? |
1363 | get_relation_constraint_oid(reloid, depname, missing_ok) : |
1364 | InvalidOid; |
1365 | address.objectSubId = 0; |
1366 | break; |
1367 | case OBJECT_POLICY: |
1368 | address.classId = PolicyRelationId; |
1369 | address.objectId = relation ? |
1370 | get_relation_policy_oid(reloid, depname, missing_ok) : |
1371 | InvalidOid; |
1372 | address.objectSubId = 0; |
1373 | break; |
1374 | default: |
1375 | elog(ERROR, "unrecognized objtype: %d" , (int) objtype); |
1376 | } |
1377 | |
1378 | /* Avoid relcache leak when object not found. */ |
1379 | if (!OidIsValid(address.objectId)) |
1380 | { |
1381 | if (relation != NULL) |
1382 | table_close(relation, AccessShareLock); |
1383 | |
1384 | relation = NULL; /* department of accident prevention */ |
1385 | return address; |
1386 | } |
1387 | |
1388 | /* Done. */ |
1389 | *relp = relation; |
1390 | return address; |
1391 | } |
1392 | |
1393 | /* |
1394 | * Find the ObjectAddress for an attribute. |
1395 | */ |
1396 | static ObjectAddress |
1397 | get_object_address_attribute(ObjectType objtype, List *object, |
1398 | Relation *relp, LOCKMODE lockmode, |
1399 | bool missing_ok) |
1400 | { |
1401 | ObjectAddress address; |
1402 | List *relname; |
1403 | Oid reloid; |
1404 | Relation relation; |
1405 | const char *attname; |
1406 | AttrNumber attnum; |
1407 | |
1408 | /* Extract relation name and open relation. */ |
1409 | if (list_length(object) < 2) |
1410 | ereport(ERROR, |
1411 | (errcode(ERRCODE_SYNTAX_ERROR), |
1412 | errmsg("column name must be qualified" ))); |
1413 | attname = strVal(lfirst(list_tail(object))); |
1414 | relname = list_truncate(list_copy(object), list_length(object) - 1); |
1415 | /* XXX no missing_ok support here */ |
1416 | relation = relation_openrv(makeRangeVarFromNameList(relname), lockmode); |
1417 | reloid = RelationGetRelid(relation); |
1418 | |
1419 | /* Look up attribute and construct return value. */ |
1420 | attnum = get_attnum(reloid, attname); |
1421 | if (attnum == InvalidAttrNumber) |
1422 | { |
1423 | if (!missing_ok) |
1424 | ereport(ERROR, |
1425 | (errcode(ERRCODE_UNDEFINED_COLUMN), |
1426 | errmsg("column \"%s\" of relation \"%s\" does not exist" , |
1427 | attname, NameListToString(relname)))); |
1428 | |
1429 | address.classId = RelationRelationId; |
1430 | address.objectId = InvalidOid; |
1431 | address.objectSubId = InvalidAttrNumber; |
1432 | relation_close(relation, lockmode); |
1433 | return address; |
1434 | } |
1435 | |
1436 | address.classId = RelationRelationId; |
1437 | address.objectId = reloid; |
1438 | address.objectSubId = attnum; |
1439 | |
1440 | *relp = relation; |
1441 | return address; |
1442 | } |
1443 | |
1444 | /* |
1445 | * Find the ObjectAddress for an attribute's default value. |
1446 | */ |
1447 | static ObjectAddress |
1448 | get_object_address_attrdef(ObjectType objtype, List *object, |
1449 | Relation *relp, LOCKMODE lockmode, |
1450 | bool missing_ok) |
1451 | { |
1452 | ObjectAddress address; |
1453 | List *relname; |
1454 | Oid reloid; |
1455 | Relation relation; |
1456 | const char *attname; |
1457 | AttrNumber attnum; |
1458 | TupleDesc tupdesc; |
1459 | Oid defoid; |
1460 | |
1461 | /* Extract relation name and open relation. */ |
1462 | if (list_length(object) < 2) |
1463 | ereport(ERROR, |
1464 | (errcode(ERRCODE_SYNTAX_ERROR), |
1465 | errmsg("column name must be qualified" ))); |
1466 | attname = strVal(llast(object)); |
1467 | relname = list_truncate(list_copy(object), list_length(object) - 1); |
1468 | /* XXX no missing_ok support here */ |
1469 | relation = relation_openrv(makeRangeVarFromNameList(relname), lockmode); |
1470 | reloid = RelationGetRelid(relation); |
1471 | |
1472 | tupdesc = RelationGetDescr(relation); |
1473 | |
1474 | /* Look up attribute number and scan pg_attrdef to find its tuple */ |
1475 | attnum = get_attnum(reloid, attname); |
1476 | defoid = InvalidOid; |
1477 | if (attnum != InvalidAttrNumber && tupdesc->constr != NULL) |
1478 | { |
1479 | Relation attrdef; |
1480 | ScanKeyData keys[2]; |
1481 | SysScanDesc scan; |
1482 | HeapTuple tup; |
1483 | |
1484 | attrdef = relation_open(AttrDefaultRelationId, AccessShareLock); |
1485 | ScanKeyInit(&keys[0], |
1486 | Anum_pg_attrdef_adrelid, |
1487 | BTEqualStrategyNumber, |
1488 | F_OIDEQ, |
1489 | ObjectIdGetDatum(reloid)); |
1490 | ScanKeyInit(&keys[1], |
1491 | Anum_pg_attrdef_adnum, |
1492 | BTEqualStrategyNumber, |
1493 | F_INT2EQ, |
1494 | Int16GetDatum(attnum)); |
1495 | scan = systable_beginscan(attrdef, AttrDefaultIndexId, true, |
1496 | NULL, 2, keys); |
1497 | if (HeapTupleIsValid(tup = systable_getnext(scan))) |
1498 | { |
1499 | Form_pg_attrdef atdform = (Form_pg_attrdef) GETSTRUCT(tup); |
1500 | |
1501 | defoid = atdform->oid; |
1502 | } |
1503 | |
1504 | systable_endscan(scan); |
1505 | relation_close(attrdef, AccessShareLock); |
1506 | } |
1507 | if (!OidIsValid(defoid)) |
1508 | { |
1509 | if (!missing_ok) |
1510 | ereport(ERROR, |
1511 | (errcode(ERRCODE_UNDEFINED_COLUMN), |
1512 | errmsg("default value for column \"%s\" of relation \"%s\" does not exist" , |
1513 | attname, NameListToString(relname)))); |
1514 | |
1515 | address.classId = AttrDefaultRelationId; |
1516 | address.objectId = InvalidOid; |
1517 | address.objectSubId = InvalidAttrNumber; |
1518 | relation_close(relation, lockmode); |
1519 | return address; |
1520 | } |
1521 | |
1522 | address.classId = AttrDefaultRelationId; |
1523 | address.objectId = defoid; |
1524 | address.objectSubId = 0; |
1525 | |
1526 | *relp = relation; |
1527 | return address; |
1528 | } |
1529 | |
1530 | /* |
1531 | * Find the ObjectAddress for a type or domain |
1532 | */ |
1533 | static ObjectAddress |
1534 | get_object_address_type(ObjectType objtype, TypeName *typename, bool missing_ok) |
1535 | { |
1536 | ObjectAddress address; |
1537 | Type tup; |
1538 | |
1539 | address.classId = TypeRelationId; |
1540 | address.objectId = InvalidOid; |
1541 | address.objectSubId = 0; |
1542 | |
1543 | tup = LookupTypeName(NULL, typename, NULL, missing_ok); |
1544 | if (!HeapTupleIsValid(tup)) |
1545 | { |
1546 | if (!missing_ok) |
1547 | ereport(ERROR, |
1548 | (errcode(ERRCODE_UNDEFINED_OBJECT), |
1549 | errmsg("type \"%s\" does not exist" , |
1550 | TypeNameToString(typename)))); |
1551 | return address; |
1552 | } |
1553 | address.objectId = typeTypeId(tup); |
1554 | |
1555 | if (objtype == OBJECT_DOMAIN) |
1556 | { |
1557 | if (((Form_pg_type) GETSTRUCT(tup))->typtype != TYPTYPE_DOMAIN) |
1558 | ereport(ERROR, |
1559 | (errcode(ERRCODE_WRONG_OBJECT_TYPE), |
1560 | errmsg("\"%s\" is not a domain" , |
1561 | TypeNameToString(typename)))); |
1562 | } |
1563 | |
1564 | ReleaseSysCache(tup); |
1565 | |
1566 | return address; |
1567 | } |
1568 | |
1569 | /* |
1570 | * Find the ObjectAddress for an opclass or opfamily. |
1571 | */ |
1572 | static ObjectAddress |
1573 | get_object_address_opcf(ObjectType objtype, List *object, bool missing_ok) |
1574 | { |
1575 | Oid amoid; |
1576 | ObjectAddress address; |
1577 | |
1578 | /* XXX no missing_ok support here */ |
1579 | amoid = get_index_am_oid(strVal(linitial(object)), false); |
1580 | object = list_copy_tail(object, 1); |
1581 | |
1582 | switch (objtype) |
1583 | { |
1584 | case OBJECT_OPCLASS: |
1585 | address.classId = OperatorClassRelationId; |
1586 | address.objectId = get_opclass_oid(amoid, object, missing_ok); |
1587 | address.objectSubId = 0; |
1588 | break; |
1589 | case OBJECT_OPFAMILY: |
1590 | address.classId = OperatorFamilyRelationId; |
1591 | address.objectId = get_opfamily_oid(amoid, object, missing_ok); |
1592 | address.objectSubId = 0; |
1593 | break; |
1594 | default: |
1595 | elog(ERROR, "unrecognized objtype: %d" , (int) objtype); |
1596 | /* placate compiler, which doesn't know elog won't return */ |
1597 | address.classId = InvalidOid; |
1598 | address.objectId = InvalidOid; |
1599 | address.objectSubId = 0; |
1600 | } |
1601 | |
1602 | return address; |
1603 | } |
1604 | |
1605 | /* |
1606 | * Find the ObjectAddress for an opclass/opfamily member. |
1607 | * |
1608 | * (The returned address corresponds to a pg_amop/pg_amproc object). |
1609 | */ |
1610 | static ObjectAddress |
1611 | get_object_address_opf_member(ObjectType objtype, |
1612 | List *object, bool missing_ok) |
1613 | { |
1614 | ObjectAddress famaddr; |
1615 | ObjectAddress address; |
1616 | ListCell *cell; |
1617 | List *copy; |
1618 | TypeName *typenames[2]; |
1619 | Oid typeoids[2]; |
1620 | int membernum; |
1621 | int i; |
1622 | |
1623 | /* |
1624 | * The last element of the object list contains the strategy or procedure |
1625 | * number. We need to strip that out before getting the opclass/family |
1626 | * address. The rest can be used directly by get_object_address_opcf(). |
1627 | */ |
1628 | membernum = atoi(strVal(llast(linitial(object)))); |
1629 | copy = list_truncate(list_copy(linitial(object)), list_length(linitial(object)) - 1); |
1630 | |
1631 | /* no missing_ok support here */ |
1632 | famaddr = get_object_address_opcf(OBJECT_OPFAMILY, copy, false); |
1633 | |
1634 | /* find out left/right type names and OIDs */ |
1635 | typenames[0] = typenames[1] = NULL; |
1636 | typeoids[0] = typeoids[1] = InvalidOid; |
1637 | i = 0; |
1638 | foreach(cell, lsecond(object)) |
1639 | { |
1640 | ObjectAddress typaddr; |
1641 | |
1642 | typenames[i] = lfirst_node(TypeName, cell); |
1643 | typaddr = get_object_address_type(OBJECT_TYPE, typenames[i], missing_ok); |
1644 | typeoids[i] = typaddr.objectId; |
1645 | if (++i >= 2) |
1646 | break; |
1647 | } |
1648 | |
1649 | switch (objtype) |
1650 | { |
1651 | case OBJECT_AMOP: |
1652 | { |
1653 | HeapTuple tp; |
1654 | |
1655 | ObjectAddressSet(address, AccessMethodOperatorRelationId, |
1656 | InvalidOid); |
1657 | |
1658 | tp = SearchSysCache4(AMOPSTRATEGY, |
1659 | ObjectIdGetDatum(famaddr.objectId), |
1660 | ObjectIdGetDatum(typeoids[0]), |
1661 | ObjectIdGetDatum(typeoids[1]), |
1662 | Int16GetDatum(membernum)); |
1663 | if (!HeapTupleIsValid(tp)) |
1664 | { |
1665 | if (!missing_ok) |
1666 | ereport(ERROR, |
1667 | (errcode(ERRCODE_UNDEFINED_OBJECT), |
1668 | errmsg("operator %d (%s, %s) of %s does not exist" , |
1669 | membernum, |
1670 | TypeNameToString(typenames[0]), |
1671 | TypeNameToString(typenames[1]), |
1672 | getObjectDescription(&famaddr)))); |
1673 | } |
1674 | else |
1675 | { |
1676 | address.objectId = ((Form_pg_amop) GETSTRUCT(tp))->oid; |
1677 | ReleaseSysCache(tp); |
1678 | } |
1679 | } |
1680 | break; |
1681 | |
1682 | case OBJECT_AMPROC: |
1683 | { |
1684 | HeapTuple tp; |
1685 | |
1686 | ObjectAddressSet(address, AccessMethodProcedureRelationId, |
1687 | InvalidOid); |
1688 | |
1689 | tp = SearchSysCache4(AMPROCNUM, |
1690 | ObjectIdGetDatum(famaddr.objectId), |
1691 | ObjectIdGetDatum(typeoids[0]), |
1692 | ObjectIdGetDatum(typeoids[1]), |
1693 | Int16GetDatum(membernum)); |
1694 | if (!HeapTupleIsValid(tp)) |
1695 | { |
1696 | if (!missing_ok) |
1697 | ereport(ERROR, |
1698 | (errcode(ERRCODE_UNDEFINED_OBJECT), |
1699 | errmsg("function %d (%s, %s) of %s does not exist" , |
1700 | membernum, |
1701 | TypeNameToString(typenames[0]), |
1702 | TypeNameToString(typenames[1]), |
1703 | getObjectDescription(&famaddr)))); |
1704 | } |
1705 | else |
1706 | { |
1707 | address.objectId = ((Form_pg_amproc) GETSTRUCT(tp))->oid; |
1708 | ReleaseSysCache(tp); |
1709 | } |
1710 | } |
1711 | break; |
1712 | default: |
1713 | elog(ERROR, "unrecognized objtype: %d" , (int) objtype); |
1714 | } |
1715 | |
1716 | return address; |
1717 | } |
1718 | |
1719 | /* |
1720 | * Find the ObjectAddress for a user mapping. |
1721 | */ |
1722 | static ObjectAddress |
1723 | get_object_address_usermapping(List *object, bool missing_ok) |
1724 | { |
1725 | ObjectAddress address; |
1726 | Oid userid; |
1727 | char *username; |
1728 | char *servername; |
1729 | ForeignServer *server; |
1730 | HeapTuple tp; |
1731 | |
1732 | ObjectAddressSet(address, UserMappingRelationId, InvalidOid); |
1733 | |
1734 | /* fetch string names from input lists, for error messages */ |
1735 | username = strVal(linitial(object)); |
1736 | servername = strVal(lsecond(object)); |
1737 | |
1738 | /* look up pg_authid OID of mapped user; InvalidOid if PUBLIC */ |
1739 | if (strcmp(username, "public" ) == 0) |
1740 | userid = InvalidOid; |
1741 | else |
1742 | { |
1743 | tp = SearchSysCache1(AUTHNAME, |
1744 | CStringGetDatum(username)); |
1745 | if (!HeapTupleIsValid(tp)) |
1746 | { |
1747 | if (!missing_ok) |
1748 | ereport(ERROR, |
1749 | (errcode(ERRCODE_UNDEFINED_OBJECT), |
1750 | errmsg("user mapping for user \"%s\" on server \"%s\" does not exist" , |
1751 | username, servername))); |
1752 | return address; |
1753 | } |
1754 | userid = ((Form_pg_authid) GETSTRUCT(tp))->oid; |
1755 | ReleaseSysCache(tp); |
1756 | } |
1757 | |
1758 | /* Now look up the pg_user_mapping tuple */ |
1759 | server = GetForeignServerByName(servername, true); |
1760 | if (!server) |
1761 | { |
1762 | if (!missing_ok) |
1763 | ereport(ERROR, |
1764 | (errcode(ERRCODE_UNDEFINED_OBJECT), |
1765 | errmsg("server \"%s\" does not exist" , servername))); |
1766 | return address; |
1767 | } |
1768 | tp = SearchSysCache2(USERMAPPINGUSERSERVER, |
1769 | ObjectIdGetDatum(userid), |
1770 | ObjectIdGetDatum(server->serverid)); |
1771 | if (!HeapTupleIsValid(tp)) |
1772 | { |
1773 | if (!missing_ok) |
1774 | ereport(ERROR, |
1775 | (errcode(ERRCODE_UNDEFINED_OBJECT), |
1776 | errmsg("user mapping for user \"%s\" on server \"%s\" does not exist" , |
1777 | username, servername))); |
1778 | return address; |
1779 | } |
1780 | |
1781 | address.objectId = ((Form_pg_user_mapping) GETSTRUCT(tp))->oid; |
1782 | |
1783 | ReleaseSysCache(tp); |
1784 | |
1785 | return address; |
1786 | } |
1787 | |
1788 | /* |
1789 | * Find the ObjectAddress for a publication relation. The first element of |
1790 | * the object parameter is the relation name, the second is the |
1791 | * publication name. |
1792 | */ |
1793 | static ObjectAddress |
1794 | get_object_address_publication_rel(List *object, |
1795 | Relation *relp, bool missing_ok) |
1796 | { |
1797 | ObjectAddress address; |
1798 | Relation relation; |
1799 | List *relname; |
1800 | char *pubname; |
1801 | Publication *pub; |
1802 | |
1803 | ObjectAddressSet(address, PublicationRelRelationId, InvalidOid); |
1804 | |
1805 | relname = linitial(object); |
1806 | relation = relation_openrv_extended(makeRangeVarFromNameList(relname), |
1807 | AccessShareLock, missing_ok); |
1808 | if (!relation) |
1809 | return address; |
1810 | |
1811 | /* fetch publication name from input list */ |
1812 | pubname = strVal(lsecond(object)); |
1813 | |
1814 | /* Now look up the pg_publication tuple */ |
1815 | pub = GetPublicationByName(pubname, missing_ok); |
1816 | if (!pub) |
1817 | { |
1818 | relation_close(relation, AccessShareLock); |
1819 | return address; |
1820 | } |
1821 | |
1822 | /* Find the publication relation mapping in syscache. */ |
1823 | address.objectId = |
1824 | GetSysCacheOid2(PUBLICATIONRELMAP, Anum_pg_publication_rel_oid, |
1825 | ObjectIdGetDatum(RelationGetRelid(relation)), |
1826 | ObjectIdGetDatum(pub->oid)); |
1827 | if (!OidIsValid(address.objectId)) |
1828 | { |
1829 | if (!missing_ok) |
1830 | ereport(ERROR, |
1831 | (errcode(ERRCODE_UNDEFINED_OBJECT), |
1832 | errmsg("publication relation \"%s\" in publication \"%s\" does not exist" , |
1833 | RelationGetRelationName(relation), pubname))); |
1834 | relation_close(relation, AccessShareLock); |
1835 | return address; |
1836 | } |
1837 | |
1838 | *relp = relation; |
1839 | return address; |
1840 | } |
1841 | |
1842 | /* |
1843 | * Find the ObjectAddress for a default ACL. |
1844 | */ |
1845 | static ObjectAddress |
1846 | get_object_address_defacl(List *object, bool missing_ok) |
1847 | { |
1848 | HeapTuple tp; |
1849 | Oid userid; |
1850 | Oid schemaid; |
1851 | char *username; |
1852 | char *schema; |
1853 | char objtype; |
1854 | char *objtype_str; |
1855 | ObjectAddress address; |
1856 | |
1857 | ObjectAddressSet(address, DefaultAclRelationId, InvalidOid); |
1858 | |
1859 | /* |
1860 | * First figure out the textual attributes so that they can be used for |
1861 | * error reporting. |
1862 | */ |
1863 | username = strVal(lsecond(object)); |
1864 | if (list_length(object) >= 3) |
1865 | schema = (char *) strVal(lthird(object)); |
1866 | else |
1867 | schema = NULL; |
1868 | |
1869 | /* |
1870 | * Decode defaclobjtype. Only first char is considered; the rest of the |
1871 | * string, if any, is blissfully ignored. |
1872 | */ |
1873 | objtype = ((char *) strVal(linitial(object)))[0]; |
1874 | switch (objtype) |
1875 | { |
1876 | case DEFACLOBJ_RELATION: |
1877 | objtype_str = "tables" ; |
1878 | break; |
1879 | case DEFACLOBJ_SEQUENCE: |
1880 | objtype_str = "sequences" ; |
1881 | break; |
1882 | case DEFACLOBJ_FUNCTION: |
1883 | objtype_str = "functions" ; |
1884 | break; |
1885 | case DEFACLOBJ_TYPE: |
1886 | objtype_str = "types" ; |
1887 | break; |
1888 | case DEFACLOBJ_NAMESPACE: |
1889 | objtype_str = "schemas" ; |
1890 | break; |
1891 | default: |
1892 | ereport(ERROR, |
1893 | (errcode(ERRCODE_INVALID_PARAMETER_VALUE), |
1894 | errmsg("unrecognized default ACL object type \"%c\"" , objtype), |
1895 | errhint("Valid object types are \"%c\", \"%c\", \"%c\", \"%c\", \"%c\"." , |
1896 | DEFACLOBJ_RELATION, |
1897 | DEFACLOBJ_SEQUENCE, |
1898 | DEFACLOBJ_FUNCTION, |
1899 | DEFACLOBJ_TYPE, |
1900 | DEFACLOBJ_NAMESPACE))); |
1901 | } |
1902 | |
1903 | /* |
1904 | * Look up user ID. Behave as "default ACL not found" if the user doesn't |
1905 | * exist. |
1906 | */ |
1907 | tp = SearchSysCache1(AUTHNAME, |
1908 | CStringGetDatum(username)); |
1909 | if (!HeapTupleIsValid(tp)) |
1910 | goto not_found; |
1911 | userid = ((Form_pg_authid) GETSTRUCT(tp))->oid; |
1912 | ReleaseSysCache(tp); |
1913 | |
1914 | /* |
1915 | * If a schema name was given, look up its OID. If it doesn't exist, |
1916 | * behave as "default ACL not found". |
1917 | */ |
1918 | if (schema) |
1919 | { |
1920 | schemaid = get_namespace_oid(schema, true); |
1921 | if (schemaid == InvalidOid) |
1922 | goto not_found; |
1923 | } |
1924 | else |
1925 | schemaid = InvalidOid; |
1926 | |
1927 | /* Finally, look up the pg_default_acl object */ |
1928 | tp = SearchSysCache3(DEFACLROLENSPOBJ, |
1929 | ObjectIdGetDatum(userid), |
1930 | ObjectIdGetDatum(schemaid), |
1931 | CharGetDatum(objtype)); |
1932 | if (!HeapTupleIsValid(tp)) |
1933 | goto not_found; |
1934 | |
1935 | address.objectId = ((Form_pg_default_acl) GETSTRUCT(tp))->oid; |
1936 | ReleaseSysCache(tp); |
1937 | |
1938 | return address; |
1939 | |
1940 | not_found: |
1941 | if (!missing_ok) |
1942 | { |
1943 | if (schema) |
1944 | ereport(ERROR, |
1945 | (errcode(ERRCODE_UNDEFINED_OBJECT), |
1946 | errmsg("default ACL for user \"%s\" in schema \"%s\" on %s does not exist" , |
1947 | username, schema, objtype_str))); |
1948 | else |
1949 | ereport(ERROR, |
1950 | (errcode(ERRCODE_UNDEFINED_OBJECT), |
1951 | errmsg("default ACL for user \"%s\" on %s does not exist" , |
1952 | username, objtype_str))); |
1953 | } |
1954 | return address; |
1955 | } |
1956 | |
1957 | /* |
1958 | * Convert an array of TEXT into a List of string Values, as emitted by the |
1959 | * parser, which is what get_object_address uses as input. |
1960 | */ |
1961 | static List * |
1962 | textarray_to_strvaluelist(ArrayType *arr) |
1963 | { |
1964 | Datum *elems; |
1965 | bool *nulls; |
1966 | int nelems; |
1967 | List *list = NIL; |
1968 | int i; |
1969 | |
1970 | deconstruct_array(arr, TEXTOID, -1, false, 'i', |
1971 | &elems, &nulls, &nelems); |
1972 | |
1973 | for (i = 0; i < nelems; i++) |
1974 | { |
1975 | if (nulls[i]) |
1976 | ereport(ERROR, |
1977 | (errcode(ERRCODE_INVALID_PARAMETER_VALUE), |
1978 | errmsg("name or argument lists may not contain nulls" ))); |
1979 | list = lappend(list, makeString(TextDatumGetCString(elems[i]))); |
1980 | } |
1981 | |
1982 | return list; |
1983 | } |
1984 | |
1985 | /* |
1986 | * SQL-callable version of get_object_address |
1987 | */ |
1988 | Datum |
1989 | pg_get_object_address(PG_FUNCTION_ARGS) |
1990 | { |
1991 | char *ttype = TextDatumGetCString(PG_GETARG_DATUM(0)); |
1992 | ArrayType *namearr = PG_GETARG_ARRAYTYPE_P(1); |
1993 | ArrayType *argsarr = PG_GETARG_ARRAYTYPE_P(2); |
1994 | int itype; |
1995 | ObjectType type; |
1996 | List *name = NIL; |
1997 | TypeName *typename = NULL; |
1998 | List *args = NIL; |
1999 | Node *objnode = NULL; |
2000 | ObjectAddress addr; |
2001 | TupleDesc tupdesc; |
2002 | Datum values[3]; |
2003 | bool nulls[3]; |
2004 | HeapTuple htup; |
2005 | Relation relation; |
2006 | |
2007 | /* Decode object type, raise error if unknown */ |
2008 | itype = read_objtype_from_string(ttype); |
2009 | if (itype < 0) |
2010 | ereport(ERROR, |
2011 | (errcode(ERRCODE_INVALID_PARAMETER_VALUE), |
2012 | errmsg("unsupported object type \"%s\"" , ttype))); |
2013 | type = (ObjectType) itype; |
2014 | |
2015 | /* |
2016 | * Convert the text array to the representation appropriate for the given |
2017 | * object type. Most use a simple string Values list, but there are some |
2018 | * exceptions. |
2019 | */ |
2020 | if (type == OBJECT_TYPE || type == OBJECT_DOMAIN || type == OBJECT_CAST || |
2021 | type == OBJECT_TRANSFORM || type == OBJECT_DOMCONSTRAINT) |
2022 | { |
2023 | Datum *elems; |
2024 | bool *nulls; |
2025 | int nelems; |
2026 | |
2027 | deconstruct_array(namearr, TEXTOID, -1, false, 'i', |
2028 | &elems, &nulls, &nelems); |
2029 | if (nelems != 1) |
2030 | ereport(ERROR, |
2031 | (errcode(ERRCODE_INVALID_PARAMETER_VALUE), |
2032 | errmsg("name list length must be exactly %d" , 1))); |
2033 | if (nulls[0]) |
2034 | ereport(ERROR, |
2035 | (errcode(ERRCODE_INVALID_PARAMETER_VALUE), |
2036 | errmsg("name or argument lists may not contain nulls" ))); |
2037 | typename = typeStringToTypeName(TextDatumGetCString(elems[0])); |
2038 | } |
2039 | else if (type == OBJECT_LARGEOBJECT) |
2040 | { |
2041 | Datum *elems; |
2042 | bool *nulls; |
2043 | int nelems; |
2044 | |
2045 | deconstruct_array(namearr, TEXTOID, -1, false, 'i', |
2046 | &elems, &nulls, &nelems); |
2047 | if (nelems != 1) |
2048 | ereport(ERROR, |
2049 | (errcode(ERRCODE_INVALID_PARAMETER_VALUE), |
2050 | errmsg("name list length must be exactly %d" , 1))); |
2051 | if (nulls[0]) |
2052 | ereport(ERROR, |
2053 | (errcode(ERRCODE_INVALID_PARAMETER_VALUE), |
2054 | errmsg("large object OID may not be null" ))); |
2055 | objnode = (Node *) makeFloat(TextDatumGetCString(elems[0])); |
2056 | } |
2057 | else |
2058 | { |
2059 | name = textarray_to_strvaluelist(namearr); |
2060 | if (list_length(name) < 1) |
2061 | ereport(ERROR, |
2062 | (errcode(ERRCODE_INVALID_PARAMETER_VALUE), |
2063 | errmsg("name list length must be at least %d" , 1))); |
2064 | } |
2065 | |
2066 | /* |
2067 | * If args are given, decode them according to the object type. |
2068 | */ |
2069 | if (type == OBJECT_AGGREGATE || |
2070 | type == OBJECT_FUNCTION || |
2071 | type == OBJECT_PROCEDURE || |
2072 | type == OBJECT_ROUTINE || |
2073 | type == OBJECT_OPERATOR || |
2074 | type == OBJECT_CAST || |
2075 | type == OBJECT_AMOP || |
2076 | type == OBJECT_AMPROC) |
2077 | { |
2078 | /* in these cases, the args list must be of TypeName */ |
2079 | Datum *elems; |
2080 | bool *nulls; |
2081 | int nelems; |
2082 | int i; |
2083 | |
2084 | deconstruct_array(argsarr, TEXTOID, -1, false, 'i', |
2085 | &elems, &nulls, &nelems); |
2086 | |
2087 | args = NIL; |
2088 | for (i = 0; i < nelems; i++) |
2089 | { |
2090 | if (nulls[i]) |
2091 | ereport(ERROR, |
2092 | (errcode(ERRCODE_INVALID_PARAMETER_VALUE), |
2093 | errmsg("name or argument lists may not contain nulls" ))); |
2094 | args = lappend(args, |
2095 | typeStringToTypeName(TextDatumGetCString(elems[i]))); |
2096 | } |
2097 | } |
2098 | else |
2099 | { |
2100 | /* For all other object types, use string Values */ |
2101 | args = textarray_to_strvaluelist(argsarr); |
2102 | } |
2103 | |
2104 | /* |
2105 | * get_object_address is pretty sensitive to the length of its input |
2106 | * lists; check that they're what it wants. |
2107 | */ |
2108 | switch (type) |
2109 | { |
2110 | case OBJECT_DOMCONSTRAINT: |
2111 | case OBJECT_CAST: |
2112 | case OBJECT_USER_MAPPING: |
2113 | case OBJECT_PUBLICATION_REL: |
2114 | case OBJECT_DEFACL: |
2115 | case OBJECT_TRANSFORM: |
2116 | if (list_length(args) != 1) |
2117 | ereport(ERROR, |
2118 | (errcode(ERRCODE_INVALID_PARAMETER_VALUE), |
2119 | errmsg("argument list length must be exactly %d" , 1))); |
2120 | break; |
2121 | case OBJECT_OPFAMILY: |
2122 | case OBJECT_OPCLASS: |
2123 | if (list_length(name) < 2) |
2124 | ereport(ERROR, |
2125 | (errcode(ERRCODE_INVALID_PARAMETER_VALUE), |
2126 | errmsg("name list length must be at least %d" , 2))); |
2127 | break; |
2128 | case OBJECT_AMOP: |
2129 | case OBJECT_AMPROC: |
2130 | if (list_length(name) < 3) |
2131 | ereport(ERROR, |
2132 | (errcode(ERRCODE_INVALID_PARAMETER_VALUE), |
2133 | errmsg("name list length must be at least %d" , 3))); |
2134 | /* fall through to check args length */ |
2135 | /* FALLTHROUGH */ |
2136 | case OBJECT_OPERATOR: |
2137 | if (list_length(args) != 2) |
2138 | ereport(ERROR, |
2139 | (errcode(ERRCODE_INVALID_PARAMETER_VALUE), |
2140 | errmsg("argument list length must be exactly %d" , 2))); |
2141 | break; |
2142 | default: |
2143 | break; |
2144 | } |
2145 | |
2146 | /* |
2147 | * Now build the Node type that get_object_address() expects for the given |
2148 | * type. |
2149 | */ |
2150 | switch (type) |
2151 | { |
2152 | case OBJECT_TABLE: |
2153 | case OBJECT_SEQUENCE: |
2154 | case OBJECT_VIEW: |
2155 | case OBJECT_MATVIEW: |
2156 | case OBJECT_INDEX: |
2157 | case OBJECT_FOREIGN_TABLE: |
2158 | case OBJECT_COLUMN: |
2159 | case OBJECT_ATTRIBUTE: |
2160 | case OBJECT_COLLATION: |
2161 | case OBJECT_CONVERSION: |
2162 | case OBJECT_STATISTIC_EXT: |
2163 | case OBJECT_TSPARSER: |
2164 | case OBJECT_TSDICTIONARY: |
2165 | case OBJECT_TSTEMPLATE: |
2166 | case OBJECT_TSCONFIGURATION: |
2167 | case OBJECT_DEFAULT: |
2168 | case OBJECT_POLICY: |
2169 | case OBJECT_RULE: |
2170 | case OBJECT_TRIGGER: |
2171 | case OBJECT_TABCONSTRAINT: |
2172 | case OBJECT_OPCLASS: |
2173 | case OBJECT_OPFAMILY: |
2174 | objnode = (Node *) name; |
2175 | break; |
2176 | case OBJECT_ACCESS_METHOD: |
2177 | case OBJECT_DATABASE: |
2178 | case OBJECT_EVENT_TRIGGER: |
2179 | case OBJECT_EXTENSION: |
2180 | case OBJECT_FDW: |
2181 | case OBJECT_FOREIGN_SERVER: |
2182 | case OBJECT_LANGUAGE: |
2183 | case OBJECT_PUBLICATION: |
2184 | case OBJECT_ROLE: |
2185 | case OBJECT_SCHEMA: |
2186 | case OBJECT_SUBSCRIPTION: |
2187 | case OBJECT_TABLESPACE: |
2188 | if (list_length(name) != 1) |
2189 | ereport(ERROR, |
2190 | (errcode(ERRCODE_INVALID_PARAMETER_VALUE), |
2191 | errmsg("name list length must be exactly %d" , 1))); |
2192 | objnode = linitial(name); |
2193 | break; |
2194 | case OBJECT_TYPE: |
2195 | case OBJECT_DOMAIN: |
2196 | objnode = (Node *) typename; |
2197 | break; |
2198 | case OBJECT_CAST: |
2199 | case OBJECT_DOMCONSTRAINT: |
2200 | case OBJECT_TRANSFORM: |
2201 | objnode = (Node *) list_make2(typename, linitial(args)); |
2202 | break; |
2203 | case OBJECT_PUBLICATION_REL: |
2204 | objnode = (Node *) list_make2(name, linitial(args)); |
2205 | break; |
2206 | case OBJECT_USER_MAPPING: |
2207 | objnode = (Node *) list_make2(linitial(name), linitial(args)); |
2208 | break; |
2209 | case OBJECT_DEFACL: |
2210 | objnode = (Node *) lcons(linitial(args), name); |
2211 | break; |
2212 | case OBJECT_AMOP: |
2213 | case OBJECT_AMPROC: |
2214 | objnode = (Node *) list_make2(name, args); |
2215 | break; |
2216 | case OBJECT_FUNCTION: |
2217 | case OBJECT_PROCEDURE: |
2218 | case OBJECT_ROUTINE: |
2219 | case OBJECT_AGGREGATE: |
2220 | case OBJECT_OPERATOR: |
2221 | { |
2222 | ObjectWithArgs *owa = makeNode(ObjectWithArgs); |
2223 | |
2224 | owa->objname = name; |
2225 | owa->objargs = args; |
2226 | objnode = (Node *) owa; |
2227 | break; |
2228 | } |
2229 | case OBJECT_LARGEOBJECT: |
2230 | /* already handled above */ |
2231 | break; |
2232 | /* no default, to let compiler warn about missing case */ |
2233 | } |
2234 | |
2235 | if (objnode == NULL) |
2236 | elog(ERROR, "unrecognized object type: %d" , type); |
2237 | |
2238 | addr = get_object_address(type, objnode, |
2239 | &relation, AccessShareLock, false); |
2240 | |
2241 | /* We don't need the relcache entry, thank you very much */ |
2242 | if (relation) |
2243 | relation_close(relation, AccessShareLock); |
2244 | |
2245 | tupdesc = CreateTemplateTupleDesc(3); |
2246 | TupleDescInitEntry(tupdesc, (AttrNumber) 1, "classid" , |
2247 | OIDOID, -1, 0); |
2248 | TupleDescInitEntry(tupdesc, (AttrNumber) 2, "objid" , |
2249 | OIDOID, -1, 0); |
2250 | TupleDescInitEntry(tupdesc, (AttrNumber) 3, "objsubid" , |
2251 | INT4OID, -1, 0); |
2252 | tupdesc = BlessTupleDesc(tupdesc); |
2253 | |
2254 | values[0] = ObjectIdGetDatum(addr.classId); |
2255 | values[1] = ObjectIdGetDatum(addr.objectId); |
2256 | values[2] = Int32GetDatum(addr.objectSubId); |
2257 | nulls[0] = false; |
2258 | nulls[1] = false; |
2259 | nulls[2] = false; |
2260 | |
2261 | htup = heap_form_tuple(tupdesc, values, nulls); |
2262 | |
2263 | PG_RETURN_DATUM(HeapTupleGetDatum(htup)); |
2264 | } |
2265 | |
2266 | /* |
2267 | * Check ownership of an object previously identified by get_object_address. |
2268 | */ |
2269 | void |
2270 | check_object_ownership(Oid roleid, ObjectType objtype, ObjectAddress address, |
2271 | Node *object, Relation relation) |
2272 | { |
2273 | switch (objtype) |
2274 | { |
2275 | case OBJECT_INDEX: |
2276 | case OBJECT_SEQUENCE: |
2277 | case OBJECT_TABLE: |
2278 | case OBJECT_VIEW: |
2279 | case OBJECT_MATVIEW: |
2280 | case OBJECT_FOREIGN_TABLE: |
2281 | case OBJECT_COLUMN: |
2282 | case OBJECT_RULE: |
2283 | case OBJECT_TRIGGER: |
2284 | case OBJECT_POLICY: |
2285 | case OBJECT_TABCONSTRAINT: |
2286 | if (!pg_class_ownercheck(RelationGetRelid(relation), roleid)) |
2287 | aclcheck_error(ACLCHECK_NOT_OWNER, objtype, |
2288 | RelationGetRelationName(relation)); |
2289 | break; |
2290 | case OBJECT_DATABASE: |
2291 | if (!pg_database_ownercheck(address.objectId, roleid)) |
2292 | aclcheck_error(ACLCHECK_NOT_OWNER, objtype, |
2293 | strVal((Value *) object)); |
2294 | break; |
2295 | case OBJECT_TYPE: |
2296 | case OBJECT_DOMAIN: |
2297 | case OBJECT_ATTRIBUTE: |
2298 | if (!pg_type_ownercheck(address.objectId, roleid)) |
2299 | aclcheck_error_type(ACLCHECK_NOT_OWNER, address.objectId); |
2300 | break; |
2301 | case OBJECT_DOMCONSTRAINT: |
2302 | { |
2303 | HeapTuple tuple; |
2304 | Oid contypid; |
2305 | |
2306 | tuple = SearchSysCache1(CONSTROID, |
2307 | ObjectIdGetDatum(address.objectId)); |
2308 | if (!HeapTupleIsValid(tuple)) |
2309 | elog(ERROR, "constraint with OID %u does not exist" , |
2310 | address.objectId); |
2311 | |
2312 | contypid = ((Form_pg_constraint) GETSTRUCT(tuple))->contypid; |
2313 | |
2314 | ReleaseSysCache(tuple); |
2315 | |
2316 | /* |
2317 | * Fallback to type ownership check in this case as this is |
2318 | * what domain constraints rely on. |
2319 | */ |
2320 | if (!pg_type_ownercheck(contypid, roleid)) |
2321 | aclcheck_error_type(ACLCHECK_NOT_OWNER, contypid); |
2322 | } |
2323 | break; |
2324 | case OBJECT_AGGREGATE: |
2325 | case OBJECT_FUNCTION: |
2326 | case OBJECT_PROCEDURE: |
2327 | case OBJECT_ROUTINE: |
2328 | if (!pg_proc_ownercheck(address.objectId, roleid)) |
2329 | aclcheck_error(ACLCHECK_NOT_OWNER, objtype, |
2330 | NameListToString((castNode(ObjectWithArgs, object))->objname)); |
2331 | break; |
2332 | case OBJECT_OPERATOR: |
2333 | if (!pg_oper_ownercheck(address.objectId, roleid)) |
2334 | aclcheck_error(ACLCHECK_NOT_OWNER, objtype, |
2335 | NameListToString((castNode(ObjectWithArgs, object))->objname)); |
2336 | break; |
2337 | case OBJECT_SCHEMA: |
2338 | if (!pg_namespace_ownercheck(address.objectId, roleid)) |
2339 | aclcheck_error(ACLCHECK_NOT_OWNER, objtype, |
2340 | strVal((Value *) object)); |
2341 | break; |
2342 | case OBJECT_COLLATION: |
2343 | if (!pg_collation_ownercheck(address.objectId, roleid)) |
2344 | aclcheck_error(ACLCHECK_NOT_OWNER, objtype, |
2345 | NameListToString(castNode(List, object))); |
2346 | break; |
2347 | case OBJECT_CONVERSION: |
2348 | if (!pg_conversion_ownercheck(address.objectId, roleid)) |
2349 | aclcheck_error(ACLCHECK_NOT_OWNER, objtype, |
2350 | NameListToString(castNode(List, object))); |
2351 | break; |
2352 | case OBJECT_EXTENSION: |
2353 | if (!pg_extension_ownercheck(address.objectId, roleid)) |
2354 | aclcheck_error(ACLCHECK_NOT_OWNER, objtype, |
2355 | strVal((Value *) object)); |
2356 | break; |
2357 | case OBJECT_FDW: |
2358 | if (!pg_foreign_data_wrapper_ownercheck(address.objectId, roleid)) |
2359 | aclcheck_error(ACLCHECK_NOT_OWNER, objtype, |
2360 | strVal((Value *) object)); |
2361 | break; |
2362 | case OBJECT_FOREIGN_SERVER: |
2363 | if (!pg_foreign_server_ownercheck(address.objectId, roleid)) |
2364 | aclcheck_error(ACLCHECK_NOT_OWNER, objtype, |
2365 | strVal((Value *) object)); |
2366 | break; |
2367 | case OBJECT_EVENT_TRIGGER: |
2368 | if (!pg_event_trigger_ownercheck(address.objectId, roleid)) |
2369 | aclcheck_error(ACLCHECK_NOT_OWNER, objtype, |
2370 | strVal((Value *) object)); |
2371 | break; |
2372 | case OBJECT_LANGUAGE: |
2373 | if (!pg_language_ownercheck(address.objectId, roleid)) |
2374 | aclcheck_error(ACLCHECK_NOT_OWNER, objtype, |
2375 | strVal((Value *) object)); |
2376 | break; |
2377 | case OBJECT_OPCLASS: |
2378 | if (!pg_opclass_ownercheck(address.objectId, roleid)) |
2379 | aclcheck_error(ACLCHECK_NOT_OWNER, objtype, |
2380 | NameListToString(castNode(List, object))); |
2381 | break; |
2382 | case OBJECT_OPFAMILY: |
2383 | if (!pg_opfamily_ownercheck(address.objectId, roleid)) |
2384 | aclcheck_error(ACLCHECK_NOT_OWNER, objtype, |
2385 | NameListToString(castNode(List, object))); |
2386 | break; |
2387 | case OBJECT_LARGEOBJECT: |
2388 | if (!lo_compat_privileges && |
2389 | !pg_largeobject_ownercheck(address.objectId, roleid)) |
2390 | ereport(ERROR, |
2391 | (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), |
2392 | errmsg("must be owner of large object %u" , |
2393 | address.objectId))); |
2394 | break; |
2395 | case OBJECT_CAST: |
2396 | { |
2397 | /* We can only check permissions on the source/target types */ |
2398 | TypeName *sourcetype = linitial_node(TypeName, castNode(List, object)); |
2399 | TypeName *targettype = lsecond_node(TypeName, castNode(List, object)); |
2400 | Oid sourcetypeid = typenameTypeId(NULL, sourcetype); |
2401 | Oid targettypeid = typenameTypeId(NULL, targettype); |
2402 | |
2403 | if (!pg_type_ownercheck(sourcetypeid, roleid) |
2404 | && !pg_type_ownercheck(targettypeid, roleid)) |
2405 | ereport(ERROR, |
2406 | (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), |
2407 | errmsg("must be owner of type %s or type %s" , |
2408 | format_type_be(sourcetypeid), |
2409 | format_type_be(targettypeid)))); |
2410 | } |
2411 | break; |
2412 | case OBJECT_PUBLICATION: |
2413 | if (!pg_publication_ownercheck(address.objectId, roleid)) |
2414 | aclcheck_error(ACLCHECK_NOT_OWNER, objtype, |
2415 | strVal((Value *) object)); |
2416 | break; |
2417 | case OBJECT_SUBSCRIPTION: |
2418 | if (!pg_subscription_ownercheck(address.objectId, roleid)) |
2419 | aclcheck_error(ACLCHECK_NOT_OWNER, objtype, |
2420 | strVal((Value *) object)); |
2421 | break; |
2422 | case OBJECT_TRANSFORM: |
2423 | { |
2424 | TypeName *typename = linitial_node(TypeName, castNode(List, object)); |
2425 | Oid typeid = typenameTypeId(NULL, typename); |
2426 | |
2427 | if (!pg_type_ownercheck(typeid, roleid)) |
2428 | aclcheck_error_type(ACLCHECK_NOT_OWNER, typeid); |
2429 | } |
2430 | break; |
2431 | case OBJECT_TABLESPACE: |
2432 | if (!pg_tablespace_ownercheck(address.objectId, roleid)) |
2433 | aclcheck_error(ACLCHECK_NOT_OWNER, objtype, |
2434 | strVal((Value *) object)); |
2435 | break; |
2436 | case OBJECT_TSDICTIONARY: |
2437 | if (!pg_ts_dict_ownercheck(address.objectId, roleid)) |
2438 | aclcheck_error(ACLCHECK_NOT_OWNER, objtype, |
2439 | NameListToString(castNode(List, object))); |
2440 | break; |
2441 | case OBJECT_TSCONFIGURATION: |
2442 | if (!pg_ts_config_ownercheck(address.objectId, roleid)) |
2443 | aclcheck_error(ACLCHECK_NOT_OWNER, objtype, |
2444 | NameListToString(castNode(List, object))); |
2445 | break; |
2446 | case OBJECT_ROLE: |
2447 | |
2448 | /* |
2449 | * We treat roles as being "owned" by those with CREATEROLE priv, |
2450 | * except that superusers are only owned by superusers. |
2451 | */ |
2452 | if (superuser_arg(address.objectId)) |
2453 | { |
2454 | if (!superuser_arg(roleid)) |
2455 | ereport(ERROR, |
2456 | (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), |
2457 | errmsg("must be superuser" ))); |
2458 | } |
2459 | else |
2460 | { |
2461 | if (!has_createrole_privilege(roleid)) |
2462 | ereport(ERROR, |
2463 | (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), |
2464 | errmsg("must have CREATEROLE privilege" ))); |
2465 | } |
2466 | break; |
2467 | case OBJECT_TSPARSER: |
2468 | case OBJECT_TSTEMPLATE: |
2469 | case OBJECT_ACCESS_METHOD: |
2470 | /* We treat these object types as being owned by superusers */ |
2471 | if (!superuser_arg(roleid)) |
2472 | ereport(ERROR, |
2473 | (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), |
2474 | errmsg("must be superuser" ))); |
2475 | break; |
2476 | case OBJECT_STATISTIC_EXT: |
2477 | if (!pg_statistics_object_ownercheck(address.objectId, roleid)) |
2478 | aclcheck_error_type(ACLCHECK_NOT_OWNER, address.objectId); |
2479 | break; |
2480 | default: |
2481 | elog(ERROR, "unrecognized object type: %d" , |
2482 | (int) objtype); |
2483 | } |
2484 | } |
2485 | |
2486 | /* |
2487 | * get_object_namespace |
2488 | * |
2489 | * Find the schema containing the specified object. For non-schema objects, |
2490 | * this function returns InvalidOid. |
2491 | */ |
2492 | Oid |
2493 | get_object_namespace(const ObjectAddress *address) |
2494 | { |
2495 | int cache; |
2496 | HeapTuple tuple; |
2497 | bool isnull; |
2498 | Oid oid; |
2499 | const ObjectPropertyType *property; |
2500 | |
2501 | /* If not owned by a namespace, just return InvalidOid. */ |
2502 | property = get_object_property_data(address->classId); |
2503 | if (property->attnum_namespace == InvalidAttrNumber) |
2504 | return InvalidOid; |
2505 | |
2506 | /* Currently, we can only handle object types with system caches. */ |
2507 | cache = property->oid_catcache_id; |
2508 | Assert(cache != -1); |
2509 | |
2510 | /* Fetch tuple from syscache and extract namespace attribute. */ |
2511 | tuple = SearchSysCache1(cache, ObjectIdGetDatum(address->objectId)); |
2512 | if (!HeapTupleIsValid(tuple)) |
2513 | elog(ERROR, "cache lookup failed for cache %d oid %u" , |
2514 | cache, address->objectId); |
2515 | oid = DatumGetObjectId(SysCacheGetAttr(cache, |
2516 | tuple, |
2517 | property->attnum_namespace, |
2518 | &isnull)); |
2519 | Assert(!isnull); |
2520 | ReleaseSysCache(tuple); |
2521 | |
2522 | return oid; |
2523 | } |
2524 | |
2525 | /* |
2526 | * Return ObjectType for the given object type as given by |
2527 | * getObjectTypeDescription; if no valid ObjectType code exists, but it's a |
2528 | * possible output type from getObjectTypeDescription, return -1. |
2529 | * Otherwise, an error is thrown. |
2530 | */ |
2531 | int |
2532 | read_objtype_from_string(const char *objtype) |
2533 | { |
2534 | int i; |
2535 | |
2536 | for (i = 0; i < lengthof(ObjectTypeMap); i++) |
2537 | { |
2538 | if (strcmp(ObjectTypeMap[i].tm_name, objtype) == 0) |
2539 | return ObjectTypeMap[i].tm_type; |
2540 | } |
2541 | ereport(ERROR, |
2542 | (errcode(ERRCODE_INVALID_PARAMETER_VALUE), |
2543 | errmsg("unrecognized object type \"%s\"" , objtype))); |
2544 | |
2545 | return -1; /* keep compiler quiet */ |
2546 | } |
2547 | |
2548 | /* |
2549 | * Interfaces to reference fields of ObjectPropertyType |
2550 | */ |
2551 | Oid |
2552 | get_object_oid_index(Oid class_id) |
2553 | { |
2554 | const ObjectPropertyType *prop = get_object_property_data(class_id); |
2555 | |
2556 | return prop->oid_index_oid; |
2557 | } |
2558 | |
2559 | int |
2560 | get_object_catcache_oid(Oid class_id) |
2561 | { |
2562 | const ObjectPropertyType *prop = get_object_property_data(class_id); |
2563 | |
2564 | return prop->oid_catcache_id; |
2565 | } |
2566 | |
2567 | int |
2568 | get_object_catcache_name(Oid class_id) |
2569 | { |
2570 | const ObjectPropertyType *prop = get_object_property_data(class_id); |
2571 | |
2572 | return prop->name_catcache_id; |
2573 | } |
2574 | |
2575 | AttrNumber |
2576 | get_object_attnum_oid(Oid class_id) |
2577 | { |
2578 | const ObjectPropertyType *prop = get_object_property_data(class_id); |
2579 | |
2580 | return prop->attnum_oid; |
2581 | } |
2582 | |
2583 | AttrNumber |
2584 | get_object_attnum_name(Oid class_id) |
2585 | { |
2586 | const ObjectPropertyType *prop = get_object_property_data(class_id); |
2587 | |
2588 | return prop->attnum_name; |
2589 | } |
2590 | |
2591 | AttrNumber |
2592 | get_object_attnum_namespace(Oid class_id) |
2593 | { |
2594 | const ObjectPropertyType *prop = get_object_property_data(class_id); |
2595 | |
2596 | return prop->attnum_namespace; |
2597 | } |
2598 | |
2599 | AttrNumber |
2600 | get_object_attnum_owner(Oid class_id) |
2601 | { |
2602 | const ObjectPropertyType *prop = get_object_property_data(class_id); |
2603 | |
2604 | return prop->attnum_owner; |
2605 | } |
2606 | |
2607 | AttrNumber |
2608 | get_object_attnum_acl(Oid class_id) |
2609 | { |
2610 | const ObjectPropertyType *prop = get_object_property_data(class_id); |
2611 | |
2612 | return prop->attnum_acl; |
2613 | } |
2614 | |
2615 | ObjectType |
2616 | get_object_type(Oid class_id, Oid object_id) |
2617 | { |
2618 | const ObjectPropertyType *prop = get_object_property_data(class_id); |
2619 | |
2620 | if (prop->objtype == OBJECT_TABLE) |
2621 | { |
2622 | /* |
2623 | * If the property data says it's a table, dig a little deeper to get |
2624 | * the real relation kind, so that callers can produce more precise |
2625 | * error messages. |
2626 | */ |
2627 | return get_relkind_objtype(get_rel_relkind(object_id)); |
2628 | } |
2629 | else |
2630 | return prop->objtype; |
2631 | } |
2632 | |
2633 | bool |
2634 | get_object_namensp_unique(Oid class_id) |
2635 | { |
2636 | const ObjectPropertyType *prop = get_object_property_data(class_id); |
2637 | |
2638 | return prop->is_nsp_name_unique; |
2639 | } |
2640 | |
2641 | /* |
2642 | * Return whether we have useful data for the given object class in the |
2643 | * ObjectProperty table. |
2644 | */ |
2645 | bool |
2646 | is_objectclass_supported(Oid class_id) |
2647 | { |
2648 | int index; |
2649 | |
2650 | for (index = 0; index < lengthof(ObjectProperty); index++) |
2651 | { |
2652 | if (ObjectProperty[index].class_oid == class_id) |
2653 | return true; |
2654 | } |
2655 | |
2656 | return false; |
2657 | } |
2658 | |
2659 | /* |
2660 | * Find ObjectProperty structure by class_id. |
2661 | */ |
2662 | static const ObjectPropertyType * |
2663 | get_object_property_data(Oid class_id) |
2664 | { |
2665 | static const ObjectPropertyType *prop_last = NULL; |
2666 | int index; |
2667 | |
2668 | /* |
2669 | * A shortcut to speed up multiple consecutive lookups of a particular |
2670 | * object class. |
2671 | */ |
2672 | if (prop_last && prop_last->class_oid == class_id) |
2673 | return prop_last; |
2674 | |
2675 | for (index = 0; index < lengthof(ObjectProperty); index++) |
2676 | { |
2677 | if (ObjectProperty[index].class_oid == class_id) |
2678 | { |
2679 | prop_last = &ObjectProperty[index]; |
2680 | return &ObjectProperty[index]; |
2681 | } |
2682 | } |
2683 | |
2684 | ereport(ERROR, |
2685 | (errmsg_internal("unrecognized class ID: %u" , class_id))); |
2686 | |
2687 | return NULL; /* keep MSC compiler happy */ |
2688 | } |
2689 | |
2690 | /* |
2691 | * Return a copy of the tuple for the object with the given object OID, from |
2692 | * the given catalog (which must have been opened by the caller and suitably |
2693 | * locked). NULL is returned if the OID is not found. |
2694 | * |
2695 | * We try a syscache first, if available. |
2696 | */ |
2697 | HeapTuple |
2698 | get_catalog_object_by_oid(Relation catalog, AttrNumber oidcol, Oid objectId) |
2699 | { |
2700 | HeapTuple tuple; |
2701 | Oid classId = RelationGetRelid(catalog); |
2702 | int oidCacheId = get_object_catcache_oid(classId); |
2703 | |
2704 | if (oidCacheId > 0) |
2705 | { |
2706 | tuple = SearchSysCacheCopy1(oidCacheId, ObjectIdGetDatum(objectId)); |
2707 | if (!HeapTupleIsValid(tuple)) /* should not happen */ |
2708 | return NULL; |
2709 | } |
2710 | else |
2711 | { |
2712 | Oid oidIndexId = get_object_oid_index(classId); |
2713 | SysScanDesc scan; |
2714 | ScanKeyData skey; |
2715 | |
2716 | Assert(OidIsValid(oidIndexId)); |
2717 | |
2718 | ScanKeyInit(&skey, |
2719 | oidcol, |
2720 | BTEqualStrategyNumber, F_OIDEQ, |
2721 | ObjectIdGetDatum(objectId)); |
2722 | |
2723 | scan = systable_beginscan(catalog, oidIndexId, true, |
2724 | NULL, 1, &skey); |
2725 | tuple = systable_getnext(scan); |
2726 | if (!HeapTupleIsValid(tuple)) |
2727 | { |
2728 | systable_endscan(scan); |
2729 | return NULL; |
2730 | } |
2731 | tuple = heap_copytuple(tuple); |
2732 | |
2733 | systable_endscan(scan); |
2734 | } |
2735 | |
2736 | return tuple; |
2737 | } |
2738 | |
2739 | /* |
2740 | * getObjectDescription: build an object description for messages |
2741 | * |
2742 | * The result is a palloc'd string. |
2743 | */ |
2744 | char * |
2745 | getObjectDescription(const ObjectAddress *object) |
2746 | { |
2747 | StringInfoData buffer; |
2748 | |
2749 | initStringInfo(&buffer); |
2750 | |
2751 | switch (getObjectClass(object)) |
2752 | { |
2753 | case OCLASS_CLASS: |
2754 | if (object->objectSubId == 0) |
2755 | getRelationDescription(&buffer, object->objectId); |
2756 | else |
2757 | { |
2758 | /* column, not whole relation */ |
2759 | StringInfoData rel; |
2760 | |
2761 | initStringInfo(&rel); |
2762 | getRelationDescription(&rel, object->objectId); |
2763 | /* translator: second %s is, e.g., "table %s" */ |
2764 | appendStringInfo(&buffer, _("column %s of %s" ), |
2765 | get_attname(object->objectId, |
2766 | object->objectSubId, |
2767 | false), |
2768 | rel.data); |
2769 | pfree(rel.data); |
2770 | } |
2771 | break; |
2772 | |
2773 | case OCLASS_PROC: |
2774 | appendStringInfo(&buffer, _("function %s" ), |
2775 | format_procedure(object->objectId)); |
2776 | break; |
2777 | |
2778 | case OCLASS_TYPE: |
2779 | appendStringInfo(&buffer, _("type %s" ), |
2780 | format_type_be(object->objectId)); |
2781 | break; |
2782 | |
2783 | case OCLASS_CAST: |
2784 | { |
2785 | Relation castDesc; |
2786 | ScanKeyData skey[1]; |
2787 | SysScanDesc rcscan; |
2788 | HeapTuple tup; |
2789 | Form_pg_cast castForm; |
2790 | |
2791 | castDesc = table_open(CastRelationId, AccessShareLock); |
2792 | |
2793 | ScanKeyInit(&skey[0], |
2794 | Anum_pg_cast_oid, |
2795 | BTEqualStrategyNumber, F_OIDEQ, |
2796 | ObjectIdGetDatum(object->objectId)); |
2797 | |
2798 | rcscan = systable_beginscan(castDesc, CastOidIndexId, true, |
2799 | NULL, 1, skey); |
2800 | |
2801 | tup = systable_getnext(rcscan); |
2802 | |
2803 | if (!HeapTupleIsValid(tup)) |
2804 | elog(ERROR, "could not find tuple for cast %u" , |
2805 | object->objectId); |
2806 | |
2807 | castForm = (Form_pg_cast) GETSTRUCT(tup); |
2808 | |
2809 | appendStringInfo(&buffer, _("cast from %s to %s" ), |
2810 | format_type_be(castForm->castsource), |
2811 | format_type_be(castForm->casttarget)); |
2812 | |
2813 | systable_endscan(rcscan); |
2814 | table_close(castDesc, AccessShareLock); |
2815 | break; |
2816 | } |
2817 | |
2818 | case OCLASS_COLLATION: |
2819 | { |
2820 | HeapTuple collTup; |
2821 | Form_pg_collation coll; |
2822 | char *nspname; |
2823 | |
2824 | collTup = SearchSysCache1(COLLOID, |
2825 | ObjectIdGetDatum(object->objectId)); |
2826 | if (!HeapTupleIsValid(collTup)) |
2827 | elog(ERROR, "cache lookup failed for collation %u" , |
2828 | object->objectId); |
2829 | coll = (Form_pg_collation) GETSTRUCT(collTup); |
2830 | |
2831 | /* Qualify the name if not visible in search path */ |
2832 | if (CollationIsVisible(object->objectId)) |
2833 | nspname = NULL; |
2834 | else |
2835 | nspname = get_namespace_name(coll->collnamespace); |
2836 | |
2837 | appendStringInfo(&buffer, _("collation %s" ), |
2838 | quote_qualified_identifier(nspname, |
2839 | NameStr(coll->collname))); |
2840 | ReleaseSysCache(collTup); |
2841 | break; |
2842 | } |
2843 | |
2844 | case OCLASS_CONSTRAINT: |
2845 | { |
2846 | HeapTuple conTup; |
2847 | Form_pg_constraint con; |
2848 | |
2849 | conTup = SearchSysCache1(CONSTROID, |
2850 | ObjectIdGetDatum(object->objectId)); |
2851 | if (!HeapTupleIsValid(conTup)) |
2852 | elog(ERROR, "cache lookup failed for constraint %u" , |
2853 | object->objectId); |
2854 | con = (Form_pg_constraint) GETSTRUCT(conTup); |
2855 | |
2856 | if (OidIsValid(con->conrelid)) |
2857 | { |
2858 | StringInfoData rel; |
2859 | |
2860 | initStringInfo(&rel); |
2861 | getRelationDescription(&rel, con->conrelid); |
2862 | /* translator: second %s is, e.g., "table %s" */ |
2863 | appendStringInfo(&buffer, _("constraint %s on %s" ), |
2864 | NameStr(con->conname), rel.data); |
2865 | pfree(rel.data); |
2866 | } |
2867 | else |
2868 | { |
2869 | appendStringInfo(&buffer, _("constraint %s" ), |
2870 | NameStr(con->conname)); |
2871 | } |
2872 | |
2873 | ReleaseSysCache(conTup); |
2874 | break; |
2875 | } |
2876 | |
2877 | case OCLASS_CONVERSION: |
2878 | { |
2879 | HeapTuple conTup; |
2880 | Form_pg_conversion conv; |
2881 | char *nspname; |
2882 | |
2883 | conTup = SearchSysCache1(CONVOID, |
2884 | ObjectIdGetDatum(object->objectId)); |
2885 | if (!HeapTupleIsValid(conTup)) |
2886 | elog(ERROR, "cache lookup failed for conversion %u" , |
2887 | object->objectId); |
2888 | conv = (Form_pg_conversion) GETSTRUCT(conTup); |
2889 | |
2890 | /* Qualify the name if not visible in search path */ |
2891 | if (ConversionIsVisible(object->objectId)) |
2892 | nspname = NULL; |
2893 | else |
2894 | nspname = get_namespace_name(conv->connamespace); |
2895 | |
2896 | appendStringInfo(&buffer, _("conversion %s" ), |
2897 | quote_qualified_identifier(nspname, |
2898 | NameStr(conv->conname))); |
2899 | ReleaseSysCache(conTup); |
2900 | break; |
2901 | } |
2902 | |
2903 | case OCLASS_DEFAULT: |
2904 | { |
2905 | Relation attrdefDesc; |
2906 | ScanKeyData skey[1]; |
2907 | SysScanDesc adscan; |
2908 | HeapTuple tup; |
2909 | Form_pg_attrdef attrdef; |
2910 | ObjectAddress colobject; |
2911 | |
2912 | attrdefDesc = table_open(AttrDefaultRelationId, AccessShareLock); |
2913 | |
2914 | ScanKeyInit(&skey[0], |
2915 | Anum_pg_attrdef_oid, |
2916 | BTEqualStrategyNumber, F_OIDEQ, |
2917 | ObjectIdGetDatum(object->objectId)); |
2918 | |
2919 | adscan = systable_beginscan(attrdefDesc, AttrDefaultOidIndexId, |
2920 | true, NULL, 1, skey); |
2921 | |
2922 | tup = systable_getnext(adscan); |
2923 | |
2924 | if (!HeapTupleIsValid(tup)) |
2925 | elog(ERROR, "could not find tuple for attrdef %u" , |
2926 | object->objectId); |
2927 | |
2928 | attrdef = (Form_pg_attrdef) GETSTRUCT(tup); |
2929 | |
2930 | colobject.classId = RelationRelationId; |
2931 | colobject.objectId = attrdef->adrelid; |
2932 | colobject.objectSubId = attrdef->adnum; |
2933 | |
2934 | /* translator: %s is typically "column %s of table %s" */ |
2935 | appendStringInfo(&buffer, _("default value for %s" ), |
2936 | getObjectDescription(&colobject)); |
2937 | |
2938 | systable_endscan(adscan); |
2939 | table_close(attrdefDesc, AccessShareLock); |
2940 | break; |
2941 | } |
2942 | |
2943 | case OCLASS_LANGUAGE: |
2944 | appendStringInfo(&buffer, _("language %s" ), |
2945 | get_language_name(object->objectId, false)); |
2946 | break; |
2947 | |
2948 | case OCLASS_LARGEOBJECT: |
2949 | appendStringInfo(&buffer, _("large object %u" ), |
2950 | object->objectId); |
2951 | break; |
2952 | |
2953 | case OCLASS_OPERATOR: |
2954 | appendStringInfo(&buffer, _("operator %s" ), |
2955 | format_operator(object->objectId)); |
2956 | break; |
2957 | |
2958 | case OCLASS_OPCLASS: |
2959 | { |
2960 | HeapTuple opcTup; |
2961 | Form_pg_opclass opcForm; |
2962 | HeapTuple amTup; |
2963 | Form_pg_am amForm; |
2964 | char *nspname; |
2965 | |
2966 | opcTup = SearchSysCache1(CLAOID, |
2967 | ObjectIdGetDatum(object->objectId)); |
2968 | if (!HeapTupleIsValid(opcTup)) |
2969 | elog(ERROR, "cache lookup failed for opclass %u" , |
2970 | object->objectId); |
2971 | opcForm = (Form_pg_opclass) GETSTRUCT(opcTup); |
2972 | |
2973 | amTup = SearchSysCache1(AMOID, |
2974 | ObjectIdGetDatum(opcForm->opcmethod)); |
2975 | if (!HeapTupleIsValid(amTup)) |
2976 | elog(ERROR, "cache lookup failed for access method %u" , |
2977 | opcForm->opcmethod); |
2978 | amForm = (Form_pg_am) GETSTRUCT(amTup); |
2979 | |
2980 | /* Qualify the name if not visible in search path */ |
2981 | if (OpclassIsVisible(object->objectId)) |
2982 | nspname = NULL; |
2983 | else |
2984 | nspname = get_namespace_name(opcForm->opcnamespace); |
2985 | |
2986 | appendStringInfo(&buffer, _("operator class %s for access method %s" ), |
2987 | quote_qualified_identifier(nspname, |
2988 | NameStr(opcForm->opcname)), |
2989 | NameStr(amForm->amname)); |
2990 | |
2991 | ReleaseSysCache(amTup); |
2992 | ReleaseSysCache(opcTup); |
2993 | break; |
2994 | } |
2995 | |
2996 | case OCLASS_OPFAMILY: |
2997 | getOpFamilyDescription(&buffer, object->objectId); |
2998 | break; |
2999 | |
3000 | case OCLASS_AM: |
3001 | { |
3002 | HeapTuple tup; |
3003 | |
3004 | tup = SearchSysCache1(AMOID, |
3005 | ObjectIdGetDatum(object->objectId)); |
3006 | if (!HeapTupleIsValid(tup)) |
3007 | elog(ERROR, "cache lookup failed for access method %u" , |
3008 | object->objectId); |
3009 | appendStringInfo(&buffer, _("access method %s" ), |
3010 | NameStr(((Form_pg_am) GETSTRUCT(tup))->amname)); |
3011 | ReleaseSysCache(tup); |
3012 | break; |
3013 | } |
3014 | |
3015 | case OCLASS_AMOP: |
3016 | { |
3017 | Relation amopDesc; |
3018 | HeapTuple tup; |
3019 | ScanKeyData skey[1]; |
3020 | SysScanDesc amscan; |
3021 | Form_pg_amop amopForm; |
3022 | StringInfoData opfam; |
3023 | |
3024 | amopDesc = table_open(AccessMethodOperatorRelationId, |
3025 | AccessShareLock); |
3026 | |
3027 | ScanKeyInit(&skey[0], |
3028 | Anum_pg_amop_oid, |
3029 | BTEqualStrategyNumber, F_OIDEQ, |
3030 | ObjectIdGetDatum(object->objectId)); |
3031 | |
3032 | amscan = systable_beginscan(amopDesc, AccessMethodOperatorOidIndexId, true, |
3033 | NULL, 1, skey); |
3034 | |
3035 | tup = systable_getnext(amscan); |
3036 | |
3037 | if (!HeapTupleIsValid(tup)) |
3038 | elog(ERROR, "could not find tuple for amop entry %u" , |
3039 | object->objectId); |
3040 | |
3041 | amopForm = (Form_pg_amop) GETSTRUCT(tup); |
3042 | |
3043 | initStringInfo(&opfam); |
3044 | getOpFamilyDescription(&opfam, amopForm->amopfamily); |
3045 | |
3046 | /*------ |
3047 | translator: %d is the operator strategy (a number), the |
3048 | first two %s's are data type names, the third %s is the |
3049 | description of the operator family, and the last %s is the |
3050 | textual form of the operator with arguments. */ |
3051 | appendStringInfo(&buffer, _("operator %d (%s, %s) of %s: %s" ), |
3052 | amopForm->amopstrategy, |
3053 | format_type_be(amopForm->amoplefttype), |
3054 | format_type_be(amopForm->amoprighttype), |
3055 | opfam.data, |
3056 | format_operator(amopForm->amopopr)); |
3057 | |
3058 | pfree(opfam.data); |
3059 | |
3060 | systable_endscan(amscan); |
3061 | table_close(amopDesc, AccessShareLock); |
3062 | break; |
3063 | } |
3064 | |
3065 | case OCLASS_AMPROC: |
3066 | { |
3067 | Relation amprocDesc; |
3068 | ScanKeyData skey[1]; |
3069 | SysScanDesc amscan; |
3070 | HeapTuple tup; |
3071 | Form_pg_amproc amprocForm; |
3072 | StringInfoData opfam; |
3073 | |
3074 | amprocDesc = table_open(AccessMethodProcedureRelationId, |
3075 | AccessShareLock); |
3076 | |
3077 | ScanKeyInit(&skey[0], |
3078 | Anum_pg_amproc_oid, |
3079 | BTEqualStrategyNumber, F_OIDEQ, |
3080 | ObjectIdGetDatum(object->objectId)); |
3081 | |
3082 | amscan = systable_beginscan(amprocDesc, AccessMethodProcedureOidIndexId, true, |
3083 | NULL, 1, skey); |
3084 | |
3085 | tup = systable_getnext(amscan); |
3086 | |
3087 | if (!HeapTupleIsValid(tup)) |
3088 | elog(ERROR, "could not find tuple for amproc entry %u" , |
3089 | object->objectId); |
3090 | |
3091 | amprocForm = (Form_pg_amproc) GETSTRUCT(tup); |
3092 | |
3093 | initStringInfo(&opfam); |
3094 | getOpFamilyDescription(&opfam, amprocForm->amprocfamily); |
3095 | |
3096 | /*------ |
3097 | translator: %d is the function number, the first two %s's |
3098 | are data type names, the third %s is the description of the |
3099 | operator family, and the last %s is the textual form of the |
3100 | function with arguments. */ |
3101 | appendStringInfo(&buffer, _("function %d (%s, %s) of %s: %s" ), |
3102 | amprocForm->amprocnum, |
3103 | format_type_be(amprocForm->amproclefttype), |
3104 | format_type_be(amprocForm->amprocrighttype), |
3105 | opfam.data, |
3106 | format_procedure(amprocForm->amproc)); |
3107 | |
3108 | pfree(opfam.data); |
3109 | |
3110 | systable_endscan(amscan); |
3111 | table_close(amprocDesc, AccessShareLock); |
3112 | break; |
3113 | } |
3114 | |
3115 | case OCLASS_REWRITE: |
3116 | { |
3117 | Relation ruleDesc; |
3118 | ScanKeyData skey[1]; |
3119 | SysScanDesc rcscan; |
3120 | HeapTuple tup; |
3121 | Form_pg_rewrite rule; |
3122 | StringInfoData rel; |
3123 | |
3124 | ruleDesc = table_open(RewriteRelationId, AccessShareLock); |
3125 | |
3126 | ScanKeyInit(&skey[0], |
3127 | Anum_pg_rewrite_oid, |
3128 | BTEqualStrategyNumber, F_OIDEQ, |
3129 | ObjectIdGetDatum(object->objectId)); |
3130 | |
3131 | rcscan = systable_beginscan(ruleDesc, RewriteOidIndexId, true, |
3132 | NULL, 1, skey); |
3133 | |
3134 | tup = systable_getnext(rcscan); |
3135 | |
3136 | if (!HeapTupleIsValid(tup)) |
3137 | elog(ERROR, "could not find tuple for rule %u" , |
3138 | object->objectId); |
3139 | rule = (Form_pg_rewrite) GETSTRUCT(tup); |
3140 | |
3141 | initStringInfo(&rel); |
3142 | getRelationDescription(&rel, rule->ev_class); |
3143 | |
3144 | /* translator: second %s is, e.g., "table %s" */ |
3145 | appendStringInfo(&buffer, _("rule %s on %s" ), |
3146 | NameStr(rule->rulename), rel.data); |
3147 | pfree(rel.data); |
3148 | systable_endscan(rcscan); |
3149 | table_close(ruleDesc, AccessShareLock); |
3150 | break; |
3151 | } |
3152 | |
3153 | case OCLASS_TRIGGER: |
3154 | { |
3155 | Relation trigDesc; |
3156 | ScanKeyData skey[1]; |
3157 | SysScanDesc tgscan; |
3158 | HeapTuple tup; |
3159 | Form_pg_trigger trig; |
3160 | StringInfoData rel; |
3161 | |
3162 | trigDesc = table_open(TriggerRelationId, AccessShareLock); |
3163 | |
3164 | ScanKeyInit(&skey[0], |
3165 | Anum_pg_trigger_oid, |
3166 | BTEqualStrategyNumber, F_OIDEQ, |
3167 | ObjectIdGetDatum(object->objectId)); |
3168 | |
3169 | tgscan = systable_beginscan(trigDesc, TriggerOidIndexId, true, |
3170 | NULL, 1, skey); |
3171 | |
3172 | tup = systable_getnext(tgscan); |
3173 | |
3174 | if (!HeapTupleIsValid(tup)) |
3175 | elog(ERROR, "could not find tuple for trigger %u" , |
3176 | object->objectId); |
3177 | trig = (Form_pg_trigger) GETSTRUCT(tup); |
3178 | |
3179 | initStringInfo(&rel); |
3180 | getRelationDescription(&rel, trig->tgrelid); |
3181 | |
3182 | /* translator: second %s is, e.g., "table %s" */ |
3183 | appendStringInfo(&buffer, _("trigger %s on %s" ), |
3184 | NameStr(trig->tgname), rel.data); |
3185 | pfree(rel.data); |
3186 | systable_endscan(tgscan); |
3187 | table_close(trigDesc, AccessShareLock); |
3188 | break; |
3189 | } |
3190 | |
3191 | case OCLASS_SCHEMA: |
3192 | { |
3193 | char *nspname; |
3194 | |
3195 | nspname = get_namespace_name(object->objectId); |
3196 | if (!nspname) |
3197 | elog(ERROR, "cache lookup failed for namespace %u" , |
3198 | object->objectId); |
3199 | appendStringInfo(&buffer, _("schema %s" ), nspname); |
3200 | break; |
3201 | } |
3202 | |
3203 | case OCLASS_STATISTIC_EXT: |
3204 | { |
3205 | HeapTuple stxTup; |
3206 | Form_pg_statistic_ext stxForm; |
3207 | char *nspname; |
3208 | |
3209 | stxTup = SearchSysCache1(STATEXTOID, |
3210 | ObjectIdGetDatum(object->objectId)); |
3211 | if (!HeapTupleIsValid(stxTup)) |
3212 | elog(ERROR, "could not find tuple for statistics object %u" , |
3213 | object->objectId); |
3214 | stxForm = (Form_pg_statistic_ext) GETSTRUCT(stxTup); |
3215 | |
3216 | /* Qualify the name if not visible in search path */ |
3217 | if (StatisticsObjIsVisible(object->objectId)) |
3218 | nspname = NULL; |
3219 | else |
3220 | nspname = get_namespace_name(stxForm->stxnamespace); |
3221 | |
3222 | appendStringInfo(&buffer, _("statistics object %s" ), |
3223 | quote_qualified_identifier(nspname, |
3224 | NameStr(stxForm->stxname))); |
3225 | |
3226 | ReleaseSysCache(stxTup); |
3227 | break; |
3228 | } |
3229 | |
3230 | case OCLASS_TSPARSER: |
3231 | { |
3232 | HeapTuple tup; |
3233 | Form_pg_ts_parser prsForm; |
3234 | char *nspname; |
3235 | |
3236 | tup = SearchSysCache1(TSPARSEROID, |
3237 | ObjectIdGetDatum(object->objectId)); |
3238 | if (!HeapTupleIsValid(tup)) |
3239 | elog(ERROR, "cache lookup failed for text search parser %u" , |
3240 | object->objectId); |
3241 | prsForm = (Form_pg_ts_parser) GETSTRUCT(tup); |
3242 | |
3243 | /* Qualify the name if not visible in search path */ |
3244 | if (TSParserIsVisible(object->objectId)) |
3245 | nspname = NULL; |
3246 | else |
3247 | nspname = get_namespace_name(prsForm->prsnamespace); |
3248 | |
3249 | appendStringInfo(&buffer, _("text search parser %s" ), |
3250 | quote_qualified_identifier(nspname, |
3251 | NameStr(prsForm->prsname))); |
3252 | ReleaseSysCache(tup); |
3253 | break; |
3254 | } |
3255 | |
3256 | case OCLASS_TSDICT: |
3257 | { |
3258 | HeapTuple tup; |
3259 | Form_pg_ts_dict dictForm; |
3260 | char *nspname; |
3261 | |
3262 | tup = SearchSysCache1(TSDICTOID, |
3263 | ObjectIdGetDatum(object->objectId)); |
3264 | if (!HeapTupleIsValid(tup)) |
3265 | elog(ERROR, "cache lookup failed for text search dictionary %u" , |
3266 | object->objectId); |
3267 | dictForm = (Form_pg_ts_dict) GETSTRUCT(tup); |
3268 | |
3269 | /* Qualify the name if not visible in search path */ |
3270 | if (TSDictionaryIsVisible(object->objectId)) |
3271 | nspname = NULL; |
3272 | else |
3273 | nspname = get_namespace_name(dictForm->dictnamespace); |
3274 | |
3275 | appendStringInfo(&buffer, _("text search dictionary %s" ), |
3276 | quote_qualified_identifier(nspname, |
3277 | NameStr(dictForm->dictname))); |
3278 | ReleaseSysCache(tup); |
3279 | break; |
3280 | } |
3281 | |
3282 | case OCLASS_TSTEMPLATE: |
3283 | { |
3284 | HeapTuple tup; |
3285 | Form_pg_ts_template tmplForm; |
3286 | char *nspname; |
3287 | |
3288 | tup = SearchSysCache1(TSTEMPLATEOID, |
3289 | ObjectIdGetDatum(object->objectId)); |
3290 | if (!HeapTupleIsValid(tup)) |
3291 | elog(ERROR, "cache lookup failed for text search template %u" , |
3292 | object->objectId); |
3293 | tmplForm = (Form_pg_ts_template) GETSTRUCT(tup); |
3294 | |
3295 | /* Qualify the name if not visible in search path */ |
3296 | if (TSTemplateIsVisible(object->objectId)) |
3297 | nspname = NULL; |
3298 | else |
3299 | nspname = get_namespace_name(tmplForm->tmplnamespace); |
3300 | |
3301 | appendStringInfo(&buffer, _("text search template %s" ), |
3302 | quote_qualified_identifier(nspname, |
3303 | NameStr(tmplForm->tmplname))); |
3304 | ReleaseSysCache(tup); |
3305 | break; |
3306 | } |
3307 | |
3308 | case OCLASS_TSCONFIG: |
3309 | { |
3310 | HeapTuple tup; |
3311 | Form_pg_ts_config cfgForm; |
3312 | char *nspname; |
3313 | |
3314 | tup = SearchSysCache1(TSCONFIGOID, |
3315 | ObjectIdGetDatum(object->objectId)); |
3316 | if (!HeapTupleIsValid(tup)) |
3317 | elog(ERROR, "cache lookup failed for text search configuration %u" , |
3318 | object->objectId); |
3319 | cfgForm = (Form_pg_ts_config) GETSTRUCT(tup); |
3320 | |
3321 | /* Qualify the name if not visible in search path */ |
3322 | if (TSConfigIsVisible(object->objectId)) |
3323 | nspname = NULL; |
3324 | else |
3325 | nspname = get_namespace_name(cfgForm->cfgnamespace); |
3326 | |
3327 | appendStringInfo(&buffer, _("text search configuration %s" ), |
3328 | quote_qualified_identifier(nspname, |
3329 | NameStr(cfgForm->cfgname))); |
3330 | ReleaseSysCache(tup); |
3331 | break; |
3332 | } |
3333 | |
3334 | case OCLASS_ROLE: |
3335 | { |
3336 | appendStringInfo(&buffer, _("role %s" ), |
3337 | GetUserNameFromId(object->objectId, false)); |
3338 | break; |
3339 | } |
3340 | |
3341 | case OCLASS_DATABASE: |
3342 | { |
3343 | char *datname; |
3344 | |
3345 | datname = get_database_name(object->objectId); |
3346 | if (!datname) |
3347 | elog(ERROR, "cache lookup failed for database %u" , |
3348 | object->objectId); |
3349 | appendStringInfo(&buffer, _("database %s" ), datname); |
3350 | break; |
3351 | } |
3352 | |
3353 | case OCLASS_TBLSPACE: |
3354 | { |
3355 | char *tblspace; |
3356 | |
3357 | tblspace = get_tablespace_name(object->objectId); |
3358 | if (!tblspace) |
3359 | elog(ERROR, "cache lookup failed for tablespace %u" , |
3360 | object->objectId); |
3361 | appendStringInfo(&buffer, _("tablespace %s" ), tblspace); |
3362 | break; |
3363 | } |
3364 | |
3365 | case OCLASS_FDW: |
3366 | { |
3367 | ForeignDataWrapper *fdw; |
3368 | |
3369 | fdw = GetForeignDataWrapper(object->objectId); |
3370 | appendStringInfo(&buffer, _("foreign-data wrapper %s" ), fdw->fdwname); |
3371 | break; |
3372 | } |
3373 | |
3374 | case OCLASS_FOREIGN_SERVER: |
3375 | { |
3376 | ForeignServer *srv; |
3377 | |
3378 | srv = GetForeignServer(object->objectId); |
3379 | appendStringInfo(&buffer, _("server %s" ), srv->servername); |
3380 | break; |
3381 | } |
3382 | |
3383 | case OCLASS_USER_MAPPING: |
3384 | { |
3385 | HeapTuple tup; |
3386 | Oid useid; |
3387 | char *usename; |
3388 | Form_pg_user_mapping umform; |
3389 | ForeignServer *srv; |
3390 | |
3391 | tup = SearchSysCache1(USERMAPPINGOID, |
3392 | ObjectIdGetDatum(object->objectId)); |
3393 | if (!HeapTupleIsValid(tup)) |
3394 | elog(ERROR, "cache lookup failed for user mapping %u" , |
3395 | object->objectId); |
3396 | umform = (Form_pg_user_mapping) GETSTRUCT(tup); |
3397 | useid = umform->umuser; |
3398 | srv = GetForeignServer(umform->umserver); |
3399 | |
3400 | ReleaseSysCache(tup); |
3401 | |
3402 | if (OidIsValid(useid)) |
3403 | usename = GetUserNameFromId(useid, false); |
3404 | else |
3405 | usename = "public" ; |
3406 | |
3407 | appendStringInfo(&buffer, _("user mapping for %s on server %s" ), usename, |
3408 | srv->servername); |
3409 | break; |
3410 | } |
3411 | |
3412 | case OCLASS_DEFACL: |
3413 | { |
3414 | Relation defaclrel; |
3415 | ScanKeyData skey[1]; |
3416 | SysScanDesc rcscan; |
3417 | HeapTuple tup; |
3418 | Form_pg_default_acl defacl; |
3419 | char *rolename; |
3420 | char *nspname; |
3421 | |
3422 | defaclrel = table_open(DefaultAclRelationId, AccessShareLock); |
3423 | |
3424 | ScanKeyInit(&skey[0], |
3425 | Anum_pg_default_acl_oid, |
3426 | BTEqualStrategyNumber, F_OIDEQ, |
3427 | ObjectIdGetDatum(object->objectId)); |
3428 | |
3429 | rcscan = systable_beginscan(defaclrel, DefaultAclOidIndexId, |
3430 | true, NULL, 1, skey); |
3431 | |
3432 | tup = systable_getnext(rcscan); |
3433 | |
3434 | if (!HeapTupleIsValid(tup)) |
3435 | elog(ERROR, "could not find tuple for default ACL %u" , |
3436 | object->objectId); |
3437 | |
3438 | defacl = (Form_pg_default_acl) GETSTRUCT(tup); |
3439 | |
3440 | rolename = GetUserNameFromId(defacl->defaclrole, false); |
3441 | |
3442 | if (OidIsValid(defacl->defaclnamespace)) |
3443 | nspname = get_namespace_name(defacl->defaclnamespace); |
3444 | else |
3445 | nspname = NULL; |
3446 | |
3447 | switch (defacl->defaclobjtype) |
3448 | { |
3449 | case DEFACLOBJ_RELATION: |
3450 | if (nspname) |
3451 | appendStringInfo(&buffer, |
3452 | _("default privileges on new relations belonging to role %s in schema %s" ), |
3453 | rolename, nspname); |
3454 | else |
3455 | appendStringInfo(&buffer, |
3456 | _("default privileges on new relations belonging to role %s" ), |
3457 | rolename); |
3458 | break; |
3459 | case DEFACLOBJ_SEQUENCE: |
3460 | if (nspname) |
3461 | appendStringInfo(&buffer, |
3462 | _("default privileges on new sequences belonging to role %s in schema %s" ), |
3463 | rolename, nspname); |
3464 | else |
3465 | appendStringInfo(&buffer, |
3466 | _("default privileges on new sequences belonging to role %s" ), |
3467 | rolename); |
3468 | break; |
3469 | case DEFACLOBJ_FUNCTION: |
3470 | if (nspname) |
3471 | appendStringInfo(&buffer, |
3472 | _("default privileges on new functions belonging to role %s in schema %s" ), |
3473 | rolename, nspname); |
3474 | else |
3475 | appendStringInfo(&buffer, |
3476 | _("default privileges on new functions belonging to role %s" ), |
3477 | rolename); |
3478 | break; |
3479 | case DEFACLOBJ_TYPE: |
3480 | if (nspname) |
3481 | appendStringInfo(&buffer, |
3482 | _("default privileges on new types belonging to role %s in schema %s" ), |
3483 | rolename, nspname); |
3484 | else |
3485 | appendStringInfo(&buffer, |
3486 | _("default privileges on new types belonging to role %s" ), |
3487 | rolename); |
3488 | break; |
3489 | case DEFACLOBJ_NAMESPACE: |
3490 | Assert(!nspname); |
3491 | appendStringInfo(&buffer, |
3492 | _("default privileges on new schemas belonging to role %s" ), |
3493 | rolename); |
3494 | break; |
3495 | default: |
3496 | /* shouldn't get here */ |
3497 | if (nspname) |
3498 | appendStringInfo(&buffer, |
3499 | _("default privileges belonging to role %s in schema %s" ), |
3500 | rolename, nspname); |
3501 | else |
3502 | appendStringInfo(&buffer, |
3503 | _("default privileges belonging to role %s" ), |
3504 | rolename); |
3505 | break; |
3506 | } |
3507 | |
3508 | systable_endscan(rcscan); |
3509 | table_close(defaclrel, AccessShareLock); |
3510 | break; |
3511 | } |
3512 | |
3513 | case OCLASS_EXTENSION: |
3514 | { |
3515 | char *extname; |
3516 | |
3517 | extname = get_extension_name(object->objectId); |
3518 | if (!extname) |
3519 | elog(ERROR, "cache lookup failed for extension %u" , |
3520 | object->objectId); |
3521 | appendStringInfo(&buffer, _("extension %s" ), extname); |
3522 | break; |
3523 | } |
3524 | |
3525 | case OCLASS_EVENT_TRIGGER: |
3526 | { |
3527 | HeapTuple tup; |
3528 | |
3529 | tup = SearchSysCache1(EVENTTRIGGEROID, |
3530 | ObjectIdGetDatum(object->objectId)); |
3531 | if (!HeapTupleIsValid(tup)) |
3532 | elog(ERROR, "cache lookup failed for event trigger %u" , |
3533 | object->objectId); |
3534 | appendStringInfo(&buffer, _("event trigger %s" ), |
3535 | NameStr(((Form_pg_event_trigger) GETSTRUCT(tup))->evtname)); |
3536 | ReleaseSysCache(tup); |
3537 | break; |
3538 | } |
3539 | |
3540 | case OCLASS_POLICY: |
3541 | { |
3542 | Relation policy_rel; |
3543 | ScanKeyData skey[1]; |
3544 | SysScanDesc sscan; |
3545 | HeapTuple tuple; |
3546 | Form_pg_policy form_policy; |
3547 | StringInfoData rel; |
3548 | |
3549 | policy_rel = table_open(PolicyRelationId, AccessShareLock); |
3550 | |
3551 | ScanKeyInit(&skey[0], |
3552 | Anum_pg_policy_oid, |
3553 | BTEqualStrategyNumber, F_OIDEQ, |
3554 | ObjectIdGetDatum(object->objectId)); |
3555 | |
3556 | sscan = systable_beginscan(policy_rel, PolicyOidIndexId, |
3557 | true, NULL, 1, skey); |
3558 | |
3559 | tuple = systable_getnext(sscan); |
3560 | |
3561 | if (!HeapTupleIsValid(tuple)) |
3562 | elog(ERROR, "could not find tuple for policy %u" , |
3563 | object->objectId); |
3564 | form_policy = (Form_pg_policy) GETSTRUCT(tuple); |
3565 | |
3566 | initStringInfo(&rel); |
3567 | getRelationDescription(&rel, form_policy->polrelid); |
3568 | |
3569 | /* translator: second %s is, e.g., "table %s" */ |
3570 | appendStringInfo(&buffer, _("policy %s on %s" ), |
3571 | NameStr(form_policy->polname), rel.data); |
3572 | pfree(rel.data); |
3573 | systable_endscan(sscan); |
3574 | table_close(policy_rel, AccessShareLock); |
3575 | break; |
3576 | } |
3577 | |
3578 | case OCLASS_PUBLICATION: |
3579 | { |
3580 | appendStringInfo(&buffer, _("publication %s" ), |
3581 | get_publication_name(object->objectId, |
3582 | false)); |
3583 | break; |
3584 | } |
3585 | |
3586 | case OCLASS_PUBLICATION_REL: |
3587 | { |
3588 | HeapTuple tup; |
3589 | char *pubname; |
3590 | Form_pg_publication_rel prform; |
3591 | StringInfoData rel; |
3592 | |
3593 | tup = SearchSysCache1(PUBLICATIONREL, |
3594 | ObjectIdGetDatum(object->objectId)); |
3595 | if (!HeapTupleIsValid(tup)) |
3596 | elog(ERROR, "cache lookup failed for publication table %u" , |
3597 | object->objectId); |
3598 | |
3599 | prform = (Form_pg_publication_rel) GETSTRUCT(tup); |
3600 | pubname = get_publication_name(prform->prpubid, false); |
3601 | |
3602 | initStringInfo(&rel); |
3603 | getRelationDescription(&rel, prform->prrelid); |
3604 | |
3605 | /* translator: first %s is, e.g., "table %s" */ |
3606 | appendStringInfo(&buffer, _("publication of %s in publication %s" ), |
3607 | rel.data, pubname); |
3608 | pfree(rel.data); |
3609 | ReleaseSysCache(tup); |
3610 | break; |
3611 | } |
3612 | |
3613 | case OCLASS_SUBSCRIPTION: |
3614 | { |
3615 | appendStringInfo(&buffer, _("subscription %s" ), |
3616 | get_subscription_name(object->objectId, |
3617 | false)); |
3618 | break; |
3619 | } |
3620 | |
3621 | case OCLASS_TRANSFORM: |
3622 | { |
3623 | HeapTuple trfTup; |
3624 | Form_pg_transform trfForm; |
3625 | |
3626 | trfTup = SearchSysCache1(TRFOID, |
3627 | ObjectIdGetDatum(object->objectId)); |
3628 | if (!HeapTupleIsValid(trfTup)) |
3629 | elog(ERROR, "could not find tuple for transform %u" , |
3630 | object->objectId); |
3631 | |
3632 | trfForm = (Form_pg_transform) GETSTRUCT(trfTup); |
3633 | |
3634 | appendStringInfo(&buffer, _("transform for %s language %s" ), |
3635 | format_type_be(trfForm->trftype), |
3636 | get_language_name(trfForm->trflang, false)); |
3637 | |
3638 | ReleaseSysCache(trfTup); |
3639 | break; |
3640 | } |
3641 | |
3642 | /* |
3643 | * There's intentionally no default: case here; we want the |
3644 | * compiler to warn if a new OCLASS hasn't been handled above. |
3645 | */ |
3646 | } |
3647 | |
3648 | return buffer.data; |
3649 | } |
3650 | |
3651 | /* |
3652 | * getObjectDescriptionOids: as above, except the object is specified by Oids |
3653 | */ |
3654 | char * |
3655 | getObjectDescriptionOids(Oid classid, Oid objid) |
3656 | { |
3657 | ObjectAddress address; |
3658 | |
3659 | address.classId = classid; |
3660 | address.objectId = objid; |
3661 | address.objectSubId = 0; |
3662 | |
3663 | return getObjectDescription(&address); |
3664 | } |
3665 | |
3666 | /* |
3667 | * subroutine for getObjectDescription: describe a relation |
3668 | * |
3669 | * The result is appended to "buffer". |
3670 | */ |
3671 | static void |
3672 | getRelationDescription(StringInfo buffer, Oid relid) |
3673 | { |
3674 | HeapTuple relTup; |
3675 | Form_pg_class relForm; |
3676 | char *nspname; |
3677 | char *relname; |
3678 | |
3679 | relTup = SearchSysCache1(RELOID, |
3680 | ObjectIdGetDatum(relid)); |
3681 | if (!HeapTupleIsValid(relTup)) |
3682 | elog(ERROR, "cache lookup failed for relation %u" , relid); |
3683 | relForm = (Form_pg_class) GETSTRUCT(relTup); |
3684 | |
3685 | /* Qualify the name if not visible in search path */ |
3686 | if (RelationIsVisible(relid)) |
3687 | nspname = NULL; |
3688 | else |
3689 | nspname = get_namespace_name(relForm->relnamespace); |
3690 | |
3691 | relname = quote_qualified_identifier(nspname, NameStr(relForm->relname)); |
3692 | |
3693 | switch (relForm->relkind) |
3694 | { |
3695 | case RELKIND_RELATION: |
3696 | case RELKIND_PARTITIONED_TABLE: |
3697 | appendStringInfo(buffer, _("table %s" ), |
3698 | relname); |
3699 | break; |
3700 | case RELKIND_INDEX: |
3701 | case RELKIND_PARTITIONED_INDEX: |
3702 | appendStringInfo(buffer, _("index %s" ), |
3703 | relname); |
3704 | break; |
3705 | case RELKIND_SEQUENCE: |
3706 | appendStringInfo(buffer, _("sequence %s" ), |
3707 | relname); |
3708 | break; |
3709 | case RELKIND_TOASTVALUE: |
3710 | appendStringInfo(buffer, _("toast table %s" ), |
3711 | relname); |
3712 | break; |
3713 | case RELKIND_VIEW: |
3714 | appendStringInfo(buffer, _("view %s" ), |
3715 | relname); |
3716 | break; |
3717 | case RELKIND_MATVIEW: |
3718 | appendStringInfo(buffer, _("materialized view %s" ), |
3719 | relname); |
3720 | break; |
3721 | case RELKIND_COMPOSITE_TYPE: |
3722 | appendStringInfo(buffer, _("composite type %s" ), |
3723 | relname); |
3724 | break; |
3725 | case RELKIND_FOREIGN_TABLE: |
3726 | appendStringInfo(buffer, _("foreign table %s" ), |
3727 | relname); |
3728 | break; |
3729 | default: |
3730 | /* shouldn't get here */ |
3731 | appendStringInfo(buffer, _("relation %s" ), |
3732 | relname); |
3733 | break; |
3734 | } |
3735 | |
3736 | ReleaseSysCache(relTup); |
3737 | } |
3738 | |
3739 | /* |
3740 | * subroutine for getObjectDescription: describe an operator family |
3741 | */ |
3742 | static void |
3743 | getOpFamilyDescription(StringInfo buffer, Oid opfid) |
3744 | { |
3745 | HeapTuple opfTup; |
3746 | Form_pg_opfamily opfForm; |
3747 | HeapTuple amTup; |
3748 | Form_pg_am amForm; |
3749 | char *nspname; |
3750 | |
3751 | opfTup = SearchSysCache1(OPFAMILYOID, ObjectIdGetDatum(opfid)); |
3752 | if (!HeapTupleIsValid(opfTup)) |
3753 | elog(ERROR, "cache lookup failed for opfamily %u" , opfid); |
3754 | opfForm = (Form_pg_opfamily) GETSTRUCT(opfTup); |
3755 | |
3756 | amTup = SearchSysCache1(AMOID, ObjectIdGetDatum(opfForm->opfmethod)); |
3757 | if (!HeapTupleIsValid(amTup)) |
3758 | elog(ERROR, "cache lookup failed for access method %u" , |
3759 | opfForm->opfmethod); |
3760 | amForm = (Form_pg_am) GETSTRUCT(amTup); |
3761 | |
3762 | /* Qualify the name if not visible in search path */ |
3763 | if (OpfamilyIsVisible(opfid)) |
3764 | nspname = NULL; |
3765 | else |
3766 | nspname = get_namespace_name(opfForm->opfnamespace); |
3767 | |
3768 | appendStringInfo(buffer, _("operator family %s for access method %s" ), |
3769 | quote_qualified_identifier(nspname, |
3770 | NameStr(opfForm->opfname)), |
3771 | NameStr(amForm->amname)); |
3772 | |
3773 | ReleaseSysCache(amTup); |
3774 | ReleaseSysCache(opfTup); |
3775 | } |
3776 | |
3777 | /* |
3778 | * SQL-level callable version of getObjectDescription |
3779 | */ |
3780 | Datum |
3781 | pg_describe_object(PG_FUNCTION_ARGS) |
3782 | { |
3783 | Oid classid = PG_GETARG_OID(0); |
3784 | Oid objid = PG_GETARG_OID(1); |
3785 | int32 objsubid = PG_GETARG_INT32(2); |
3786 | char *description; |
3787 | ObjectAddress address; |
3788 | |
3789 | /* for "pinned" items in pg_depend, return null */ |
3790 | if (!OidIsValid(classid) && !OidIsValid(objid)) |
3791 | PG_RETURN_NULL(); |
3792 | |
3793 | address.classId = classid; |
3794 | address.objectId = objid; |
3795 | address.objectSubId = objsubid; |
3796 | |
3797 | description = getObjectDescription(&address); |
3798 | PG_RETURN_TEXT_P(cstring_to_text(description)); |
3799 | } |
3800 | |
3801 | /* |
3802 | * SQL-level callable function to obtain object type + identity |
3803 | */ |
3804 | Datum |
3805 | pg_identify_object(PG_FUNCTION_ARGS) |
3806 | { |
3807 | Oid classid = PG_GETARG_OID(0); |
3808 | Oid objid = PG_GETARG_OID(1); |
3809 | int32 objsubid = PG_GETARG_INT32(2); |
3810 | Oid schema_oid = InvalidOid; |
3811 | const char *objname = NULL; |
3812 | ObjectAddress address; |
3813 | Datum values[4]; |
3814 | bool nulls[4]; |
3815 | TupleDesc tupdesc; |
3816 | HeapTuple htup; |
3817 | |
3818 | address.classId = classid; |
3819 | address.objectId = objid; |
3820 | address.objectSubId = objsubid; |
3821 | |
3822 | /* |
3823 | * Construct a tuple descriptor for the result row. This must match this |
3824 | * function's pg_proc entry! |
3825 | */ |
3826 | tupdesc = CreateTemplateTupleDesc(4); |
3827 | TupleDescInitEntry(tupdesc, (AttrNumber) 1, "type" , |
3828 | TEXTOID, -1, 0); |
3829 | TupleDescInitEntry(tupdesc, (AttrNumber) 2, "schema" , |
3830 | TEXTOID, -1, 0); |
3831 | TupleDescInitEntry(tupdesc, (AttrNumber) 3, "name" , |
3832 | TEXTOID, -1, 0); |
3833 | TupleDescInitEntry(tupdesc, (AttrNumber) 4, "identity" , |
3834 | TEXTOID, -1, 0); |
3835 | |
3836 | tupdesc = BlessTupleDesc(tupdesc); |
3837 | |
3838 | if (is_objectclass_supported(address.classId)) |
3839 | { |
3840 | HeapTuple objtup; |
3841 | Relation catalog = table_open(address.classId, AccessShareLock); |
3842 | |
3843 | objtup = get_catalog_object_by_oid(catalog, |
3844 | get_object_attnum_oid(address.classId), |
3845 | address.objectId); |
3846 | if (objtup != NULL) |
3847 | { |
3848 | bool isnull; |
3849 | AttrNumber nspAttnum; |
3850 | AttrNumber nameAttnum; |
3851 | |
3852 | nspAttnum = get_object_attnum_namespace(address.classId); |
3853 | if (nspAttnum != InvalidAttrNumber) |
3854 | { |
3855 | schema_oid = heap_getattr(objtup, nspAttnum, |
3856 | RelationGetDescr(catalog), &isnull); |
3857 | if (isnull) |
3858 | elog(ERROR, "invalid null namespace in object %u/%u/%d" , |
3859 | address.classId, address.objectId, address.objectSubId); |
3860 | } |
3861 | |
3862 | /* |
3863 | * We only return the object name if it can be used (together with |
3864 | * the schema name, if any) as a unique identifier. |
3865 | */ |
3866 | if (get_object_namensp_unique(address.classId)) |
3867 | { |
3868 | nameAttnum = get_object_attnum_name(address.classId); |
3869 | if (nameAttnum != InvalidAttrNumber) |
3870 | { |
3871 | Datum nameDatum; |
3872 | |
3873 | nameDatum = heap_getattr(objtup, nameAttnum, |
3874 | RelationGetDescr(catalog), &isnull); |
3875 | if (isnull) |
3876 | elog(ERROR, "invalid null name in object %u/%u/%d" , |
3877 | address.classId, address.objectId, address.objectSubId); |
3878 | objname = quote_identifier(NameStr(*(DatumGetName(nameDatum)))); |
3879 | } |
3880 | } |
3881 | } |
3882 | |
3883 | table_close(catalog, AccessShareLock); |
3884 | } |
3885 | |
3886 | /* object type */ |
3887 | values[0] = CStringGetTextDatum(getObjectTypeDescription(&address)); |
3888 | nulls[0] = false; |
3889 | |
3890 | /* schema name */ |
3891 | if (OidIsValid(schema_oid)) |
3892 | { |
3893 | const char *schema = quote_identifier(get_namespace_name(schema_oid)); |
3894 | |
3895 | values[1] = CStringGetTextDatum(schema); |
3896 | nulls[1] = false; |
3897 | } |
3898 | else |
3899 | nulls[1] = true; |
3900 | |
3901 | /* object name */ |
3902 | if (objname) |
3903 | { |
3904 | values[2] = CStringGetTextDatum(objname); |
3905 | nulls[2] = false; |
3906 | } |
3907 | else |
3908 | nulls[2] = true; |
3909 | |
3910 | /* object identity */ |
3911 | values[3] = CStringGetTextDatum(getObjectIdentity(&address)); |
3912 | nulls[3] = false; |
3913 | |
3914 | htup = heap_form_tuple(tupdesc, values, nulls); |
3915 | |
3916 | PG_RETURN_DATUM(HeapTupleGetDatum(htup)); |
3917 | } |
3918 | |
3919 | /* |
3920 | * SQL-level callable function to obtain object type + identity |
3921 | */ |
3922 | Datum |
3923 | pg_identify_object_as_address(PG_FUNCTION_ARGS) |
3924 | { |
3925 | Oid classid = PG_GETARG_OID(0); |
3926 | Oid objid = PG_GETARG_OID(1); |
3927 | int32 objsubid = PG_GETARG_INT32(2); |
3928 | ObjectAddress address; |
3929 | char *identity; |
3930 | List *names; |
3931 | List *args; |
3932 | Datum values[3]; |
3933 | bool nulls[3]; |
3934 | TupleDesc tupdesc; |
3935 | HeapTuple htup; |
3936 | |
3937 | address.classId = classid; |
3938 | address.objectId = objid; |
3939 | address.objectSubId = objsubid; |
3940 | |
3941 | /* |
3942 | * Construct a tuple descriptor for the result row. This must match this |
3943 | * function's pg_proc entry! |
3944 | */ |
3945 | tupdesc = CreateTemplateTupleDesc(3); |
3946 | TupleDescInitEntry(tupdesc, (AttrNumber) 1, "type" , |
3947 | TEXTOID, -1, 0); |
3948 | TupleDescInitEntry(tupdesc, (AttrNumber) 2, "object_names" , |
3949 | TEXTARRAYOID, -1, 0); |
3950 | TupleDescInitEntry(tupdesc, (AttrNumber) 3, "object_args" , |
3951 | TEXTARRAYOID, -1, 0); |
3952 | |
3953 | tupdesc = BlessTupleDesc(tupdesc); |
3954 | |
3955 | /* object type */ |
3956 | values[0] = CStringGetTextDatum(getObjectTypeDescription(&address)); |
3957 | nulls[0] = false; |
3958 | |
3959 | /* object identity */ |
3960 | identity = getObjectIdentityParts(&address, &names, &args); |
3961 | pfree(identity); |
3962 | |
3963 | /* object_names */ |
3964 | if (names != NIL) |
3965 | values[1] = PointerGetDatum(strlist_to_textarray(names)); |
3966 | else |
3967 | values[1] = PointerGetDatum(construct_empty_array(TEXTOID)); |
3968 | nulls[1] = false; |
3969 | |
3970 | /* object_args */ |
3971 | if (args) |
3972 | values[2] = PointerGetDatum(strlist_to_textarray(args)); |
3973 | else |
3974 | values[2] = PointerGetDatum(construct_empty_array(TEXTOID)); |
3975 | nulls[2] = false; |
3976 | |
3977 | htup = heap_form_tuple(tupdesc, values, nulls); |
3978 | |
3979 | PG_RETURN_DATUM(HeapTupleGetDatum(htup)); |
3980 | } |
3981 | |
3982 | /* |
3983 | * Return a palloc'ed string that describes the type of object that the |
3984 | * passed address is for. |
3985 | * |
3986 | * Keep ObjectTypeMap in sync with this. |
3987 | */ |
3988 | char * |
3989 | getObjectTypeDescription(const ObjectAddress *object) |
3990 | { |
3991 | StringInfoData buffer; |
3992 | |
3993 | initStringInfo(&buffer); |
3994 | |
3995 | switch (getObjectClass(object)) |
3996 | { |
3997 | case OCLASS_CLASS: |
3998 | getRelationTypeDescription(&buffer, object->objectId, |
3999 | object->objectSubId); |
4000 | break; |
4001 | |
4002 | case OCLASS_PROC: |
4003 | getProcedureTypeDescription(&buffer, object->objectId); |
4004 | break; |
4005 | |
4006 | case OCLASS_TYPE: |
4007 | appendStringInfoString(&buffer, "type" ); |
4008 | break; |
4009 | |
4010 | case OCLASS_CAST: |
4011 | appendStringInfoString(&buffer, "cast" ); |
4012 | break; |
4013 | |
4014 | case OCLASS_COLLATION: |
4015 | appendStringInfoString(&buffer, "collation" ); |
4016 | break; |
4017 | |
4018 | case OCLASS_CONSTRAINT: |
4019 | getConstraintTypeDescription(&buffer, object->objectId); |
4020 | break; |
4021 | |
4022 | case OCLASS_CONVERSION: |
4023 | appendStringInfoString(&buffer, "conversion" ); |
4024 | break; |
4025 | |
4026 | case OCLASS_DEFAULT: |
4027 | appendStringInfoString(&buffer, "default value" ); |
4028 | break; |
4029 | |
4030 | case OCLASS_LANGUAGE: |
4031 | appendStringInfoString(&buffer, "language" ); |
4032 | break; |
4033 | |
4034 | case OCLASS_LARGEOBJECT: |
4035 | appendStringInfoString(&buffer, "large object" ); |
4036 | break; |
4037 | |
4038 | case OCLASS_OPERATOR: |
4039 | appendStringInfoString(&buffer, "operator" ); |
4040 | break; |
4041 | |
4042 | case OCLASS_OPCLASS: |
4043 | appendStringInfoString(&buffer, "operator class" ); |
4044 | break; |
4045 | |
4046 | case OCLASS_OPFAMILY: |
4047 | appendStringInfoString(&buffer, "operator family" ); |
4048 | break; |
4049 | |
4050 | case OCLASS_AM: |
4051 | appendStringInfoString(&buffer, "access method" ); |
4052 | break; |
4053 | |
4054 | case OCLASS_AMOP: |
4055 | appendStringInfoString(&buffer, "operator of access method" ); |
4056 | break; |
4057 | |
4058 | case OCLASS_AMPROC: |
4059 | appendStringInfoString(&buffer, "function of access method" ); |
4060 | break; |
4061 | |
4062 | case OCLASS_REWRITE: |
4063 | appendStringInfoString(&buffer, "rule" ); |
4064 | break; |
4065 | |
4066 | case OCLASS_TRIGGER: |
4067 | appendStringInfoString(&buffer, "trigger" ); |
4068 | break; |
4069 | |
4070 | case OCLASS_SCHEMA: |
4071 | appendStringInfoString(&buffer, "schema" ); |
4072 | break; |
4073 | |
4074 | case OCLASS_STATISTIC_EXT: |
4075 | appendStringInfoString(&buffer, "statistics object" ); |
4076 | break; |
4077 | |
4078 | case OCLASS_TSPARSER: |
4079 | appendStringInfoString(&buffer, "text search parser" ); |
4080 | break; |
4081 | |
4082 | case OCLASS_TSDICT: |
4083 | appendStringInfoString(&buffer, "text search dictionary" ); |
4084 | break; |
4085 | |
4086 | case OCLASS_TSTEMPLATE: |
4087 | appendStringInfoString(&buffer, "text search template" ); |
4088 | break; |
4089 | |
4090 | case OCLASS_TSCONFIG: |
4091 | appendStringInfoString(&buffer, "text search configuration" ); |
4092 | break; |
4093 | |
4094 | case OCLASS_ROLE: |
4095 | appendStringInfoString(&buffer, "role" ); |
4096 | break; |
4097 | |
4098 | case OCLASS_DATABASE: |
4099 | appendStringInfoString(&buffer, "database" ); |
4100 | break; |
4101 | |
4102 | case OCLASS_TBLSPACE: |
4103 | appendStringInfoString(&buffer, "tablespace" ); |
4104 | break; |
4105 | |
4106 | case OCLASS_FDW: |
4107 | appendStringInfoString(&buffer, "foreign-data wrapper" ); |
4108 | break; |
4109 | |
4110 | case OCLASS_FOREIGN_SERVER: |
4111 | appendStringInfoString(&buffer, "server" ); |
4112 | break; |
4113 | |
4114 | case OCLASS_USER_MAPPING: |
4115 | appendStringInfoString(&buffer, "user mapping" ); |
4116 | break; |
4117 | |
4118 | case OCLASS_DEFACL: |
4119 | appendStringInfoString(&buffer, "default acl" ); |
4120 | break; |
4121 | |
4122 | case OCLASS_EXTENSION: |
4123 | appendStringInfoString(&buffer, "extension" ); |
4124 | break; |
4125 | |
4126 | case OCLASS_EVENT_TRIGGER: |
4127 | appendStringInfoString(&buffer, "event trigger" ); |
4128 | break; |
4129 | |
4130 | case OCLASS_POLICY: |
4131 | appendStringInfoString(&buffer, "policy" ); |
4132 | break; |
4133 | |
4134 | case OCLASS_PUBLICATION: |
4135 | appendStringInfoString(&buffer, "publication" ); |
4136 | break; |
4137 | |
4138 | case OCLASS_PUBLICATION_REL: |
4139 | appendStringInfoString(&buffer, "publication relation" ); |
4140 | break; |
4141 | |
4142 | case OCLASS_SUBSCRIPTION: |
4143 | appendStringInfoString(&buffer, "subscription" ); |
4144 | break; |
4145 | |
4146 | case OCLASS_TRANSFORM: |
4147 | appendStringInfoString(&buffer, "transform" ); |
4148 | break; |
4149 | |
4150 | /* |
4151 | * There's intentionally no default: case here; we want the |
4152 | * compiler to warn if a new OCLASS hasn't been handled above. |
4153 | */ |
4154 | } |
4155 | |
4156 | return buffer.data; |
4157 | } |
4158 | |
4159 | /* |
4160 | * subroutine for getObjectTypeDescription: describe a relation type |
4161 | */ |
4162 | static void |
4163 | getRelationTypeDescription(StringInfo buffer, Oid relid, int32 objectSubId) |
4164 | { |
4165 | HeapTuple relTup; |
4166 | Form_pg_class relForm; |
4167 | |
4168 | relTup = SearchSysCache1(RELOID, |
4169 | ObjectIdGetDatum(relid)); |
4170 | if (!HeapTupleIsValid(relTup)) |
4171 | elog(ERROR, "cache lookup failed for relation %u" , relid); |
4172 | relForm = (Form_pg_class) GETSTRUCT(relTup); |
4173 | |
4174 | switch (relForm->relkind) |
4175 | { |
4176 | case RELKIND_RELATION: |
4177 | case RELKIND_PARTITIONED_TABLE: |
4178 | appendStringInfoString(buffer, "table" ); |
4179 | break; |
4180 | case RELKIND_INDEX: |
4181 | case RELKIND_PARTITIONED_INDEX: |
4182 | appendStringInfoString(buffer, "index" ); |
4183 | break; |
4184 | case RELKIND_SEQUENCE: |
4185 | appendStringInfoString(buffer, "sequence" ); |
4186 | break; |
4187 | case RELKIND_TOASTVALUE: |
4188 | appendStringInfoString(buffer, "toast table" ); |
4189 | break; |
4190 | case RELKIND_VIEW: |
4191 | appendStringInfoString(buffer, "view" ); |
4192 | break; |
4193 | case RELKIND_MATVIEW: |
4194 | appendStringInfoString(buffer, "materialized view" ); |
4195 | break; |
4196 | case RELKIND_COMPOSITE_TYPE: |
4197 | appendStringInfoString(buffer, "composite type" ); |
4198 | break; |
4199 | case RELKIND_FOREIGN_TABLE: |
4200 | appendStringInfoString(buffer, "foreign table" ); |
4201 | break; |
4202 | default: |
4203 | /* shouldn't get here */ |
4204 | appendStringInfoString(buffer, "relation" ); |
4205 | break; |
4206 | } |
4207 | |
4208 | if (objectSubId != 0) |
4209 | appendStringInfoString(buffer, " column" ); |
4210 | |
4211 | ReleaseSysCache(relTup); |
4212 | } |
4213 | |
4214 | /* |
4215 | * subroutine for getObjectTypeDescription: describe a constraint type |
4216 | */ |
4217 | static void |
4218 | getConstraintTypeDescription(StringInfo buffer, Oid constroid) |
4219 | { |
4220 | Relation constrRel; |
4221 | HeapTuple constrTup; |
4222 | Form_pg_constraint constrForm; |
4223 | |
4224 | constrRel = table_open(ConstraintRelationId, AccessShareLock); |
4225 | constrTup = get_catalog_object_by_oid(constrRel, Anum_pg_constraint_oid, |
4226 | constroid); |
4227 | if (!HeapTupleIsValid(constrTup)) |
4228 | elog(ERROR, "cache lookup failed for constraint %u" , constroid); |
4229 | |
4230 | constrForm = (Form_pg_constraint) GETSTRUCT(constrTup); |
4231 | |
4232 | if (OidIsValid(constrForm->conrelid)) |
4233 | appendStringInfoString(buffer, "table constraint" ); |
4234 | else if (OidIsValid(constrForm->contypid)) |
4235 | appendStringInfoString(buffer, "domain constraint" ); |
4236 | else |
4237 | elog(ERROR, "invalid constraint %u" , constrForm->oid); |
4238 | |
4239 | table_close(constrRel, AccessShareLock); |
4240 | } |
4241 | |
4242 | /* |
4243 | * subroutine for getObjectTypeDescription: describe a procedure type |
4244 | */ |
4245 | static void |
4246 | getProcedureTypeDescription(StringInfo buffer, Oid procid) |
4247 | { |
4248 | HeapTuple procTup; |
4249 | Form_pg_proc procForm; |
4250 | |
4251 | procTup = SearchSysCache1(PROCOID, |
4252 | ObjectIdGetDatum(procid)); |
4253 | if (!HeapTupleIsValid(procTup)) |
4254 | elog(ERROR, "cache lookup failed for procedure %u" , procid); |
4255 | procForm = (Form_pg_proc) GETSTRUCT(procTup); |
4256 | |
4257 | if (procForm->prokind == PROKIND_AGGREGATE) |
4258 | appendStringInfoString(buffer, "aggregate" ); |
4259 | else if (procForm->prokind == PROKIND_PROCEDURE) |
4260 | appendStringInfoString(buffer, "procedure" ); |
4261 | else /* function or window function */ |
4262 | appendStringInfoString(buffer, "function" ); |
4263 | |
4264 | ReleaseSysCache(procTup); |
4265 | } |
4266 | |
4267 | /* |
4268 | * Obtain a given object's identity, as a palloc'ed string. |
4269 | * |
4270 | * This is for machine consumption, so it's not translated. All elements are |
4271 | * schema-qualified when appropriate. |
4272 | */ |
4273 | char * |
4274 | getObjectIdentity(const ObjectAddress *object) |
4275 | { |
4276 | return getObjectIdentityParts(object, NULL, NULL); |
4277 | } |
4278 | |
4279 | /* |
4280 | * As above, but more detailed. |
4281 | * |
4282 | * There are two sets of return values: the identity itself as a palloc'd |
4283 | * string is returned. objname and objargs, if not NULL, are output parameters |
4284 | * that receive lists of C-strings that are useful to give back to |
4285 | * get_object_address() to reconstruct the ObjectAddress. |
4286 | */ |
4287 | char * |
4288 | getObjectIdentityParts(const ObjectAddress *object, |
4289 | List **objname, List **objargs) |
4290 | { |
4291 | StringInfoData buffer; |
4292 | |
4293 | initStringInfo(&buffer); |
4294 | |
4295 | /* |
4296 | * Make sure that both objname and objargs were passed, or none was; and |
4297 | * initialize them to empty lists. For objname this is useless because it |
4298 | * will be initialized in all cases inside the switch; but we do it anyway |
4299 | * so that we can test below that no branch leaves it unset. |
4300 | */ |
4301 | Assert(PointerIsValid(objname) == PointerIsValid(objargs)); |
4302 | if (objname) |
4303 | { |
4304 | *objname = NIL; |
4305 | *objargs = NIL; |
4306 | } |
4307 | |
4308 | switch (getObjectClass(object)) |
4309 | { |
4310 | case OCLASS_CLASS: |
4311 | getRelationIdentity(&buffer, object->objectId, objname); |
4312 | if (object->objectSubId != 0) |
4313 | { |
4314 | char *attr; |
4315 | |
4316 | attr = get_attname(object->objectId, object->objectSubId, |
4317 | false); |
4318 | appendStringInfo(&buffer, ".%s" , quote_identifier(attr)); |
4319 | if (objname) |
4320 | *objname = lappend(*objname, attr); |
4321 | } |
4322 | break; |
4323 | |
4324 | case OCLASS_PROC: |
4325 | appendStringInfoString(&buffer, |
4326 | format_procedure_qualified(object->objectId)); |
4327 | if (objname) |
4328 | format_procedure_parts(object->objectId, objname, objargs); |
4329 | break; |
4330 | |
4331 | case OCLASS_TYPE: |
4332 | { |
4333 | char *typeout; |
4334 | |
4335 | typeout = format_type_be_qualified(object->objectId); |
4336 | appendStringInfoString(&buffer, typeout); |
4337 | if (objname) |
4338 | *objname = list_make1(typeout); |
4339 | } |
4340 | break; |
4341 | |
4342 | case OCLASS_CAST: |
4343 | { |
4344 | Relation castRel; |
4345 | HeapTuple tup; |
4346 | Form_pg_cast castForm; |
4347 | |
4348 | castRel = table_open(CastRelationId, AccessShareLock); |
4349 | |
4350 | tup = get_catalog_object_by_oid(castRel, Anum_pg_cast_oid, |
4351 | object->objectId); |
4352 | |
4353 | if (!HeapTupleIsValid(tup)) |
4354 | elog(ERROR, "could not find tuple for cast %u" , |
4355 | object->objectId); |
4356 | |
4357 | castForm = (Form_pg_cast) GETSTRUCT(tup); |
4358 | |
4359 | appendStringInfo(&buffer, "(%s AS %s)" , |
4360 | format_type_be_qualified(castForm->castsource), |
4361 | format_type_be_qualified(castForm->casttarget)); |
4362 | |
4363 | if (objname) |
4364 | { |
4365 | *objname = list_make1(format_type_be_qualified(castForm->castsource)); |
4366 | *objargs = list_make1(format_type_be_qualified(castForm->casttarget)); |
4367 | } |
4368 | |
4369 | table_close(castRel, AccessShareLock); |
4370 | break; |
4371 | } |
4372 | |
4373 | case OCLASS_COLLATION: |
4374 | { |
4375 | HeapTuple collTup; |
4376 | Form_pg_collation coll; |
4377 | char *schema; |
4378 | |
4379 | collTup = SearchSysCache1(COLLOID, |
4380 | ObjectIdGetDatum(object->objectId)); |
4381 | if (!HeapTupleIsValid(collTup)) |
4382 | elog(ERROR, "cache lookup failed for collation %u" , |
4383 | object->objectId); |
4384 | coll = (Form_pg_collation) GETSTRUCT(collTup); |
4385 | schema = get_namespace_name_or_temp(coll->collnamespace); |
4386 | appendStringInfoString(&buffer, |
4387 | quote_qualified_identifier(schema, |
4388 | NameStr(coll->collname))); |
4389 | if (objname) |
4390 | *objname = list_make2(schema, |
4391 | pstrdup(NameStr(coll->collname))); |
4392 | ReleaseSysCache(collTup); |
4393 | break; |
4394 | } |
4395 | |
4396 | case OCLASS_CONSTRAINT: |
4397 | { |
4398 | HeapTuple conTup; |
4399 | Form_pg_constraint con; |
4400 | |
4401 | conTup = SearchSysCache1(CONSTROID, |
4402 | ObjectIdGetDatum(object->objectId)); |
4403 | if (!HeapTupleIsValid(conTup)) |
4404 | elog(ERROR, "cache lookup failed for constraint %u" , |
4405 | object->objectId); |
4406 | con = (Form_pg_constraint) GETSTRUCT(conTup); |
4407 | |
4408 | if (OidIsValid(con->conrelid)) |
4409 | { |
4410 | appendStringInfo(&buffer, "%s on " , |
4411 | quote_identifier(NameStr(con->conname))); |
4412 | getRelationIdentity(&buffer, con->conrelid, objname); |
4413 | if (objname) |
4414 | *objname = lappend(*objname, pstrdup(NameStr(con->conname))); |
4415 | } |
4416 | else |
4417 | { |
4418 | ObjectAddress domain; |
4419 | |
4420 | Assert(OidIsValid(con->contypid)); |
4421 | domain.classId = TypeRelationId; |
4422 | domain.objectId = con->contypid; |
4423 | domain.objectSubId = 0; |
4424 | |
4425 | appendStringInfo(&buffer, "%s on %s" , |
4426 | quote_identifier(NameStr(con->conname)), |
4427 | getObjectIdentityParts(&domain, objname, objargs)); |
4428 | |
4429 | if (objname) |
4430 | *objargs = lappend(*objargs, pstrdup(NameStr(con->conname))); |
4431 | } |
4432 | |
4433 | ReleaseSysCache(conTup); |
4434 | break; |
4435 | } |
4436 | |
4437 | case OCLASS_CONVERSION: |
4438 | { |
4439 | HeapTuple conTup; |
4440 | Form_pg_conversion conForm; |
4441 | char *schema; |
4442 | |
4443 | conTup = SearchSysCache1(CONVOID, |
4444 | ObjectIdGetDatum(object->objectId)); |
4445 | if (!HeapTupleIsValid(conTup)) |
4446 | elog(ERROR, "cache lookup failed for conversion %u" , |
4447 | object->objectId); |
4448 | conForm = (Form_pg_conversion) GETSTRUCT(conTup); |
4449 | schema = get_namespace_name_or_temp(conForm->connamespace); |
4450 | appendStringInfoString(&buffer, |
4451 | quote_qualified_identifier(schema, |
4452 | NameStr(conForm->conname))); |
4453 | if (objname) |
4454 | *objname = list_make2(schema, |
4455 | pstrdup(NameStr(conForm->conname))); |
4456 | ReleaseSysCache(conTup); |
4457 | break; |
4458 | } |
4459 | |
4460 | case OCLASS_DEFAULT: |
4461 | { |
4462 | Relation attrdefDesc; |
4463 | ScanKeyData skey[1]; |
4464 | SysScanDesc adscan; |
4465 | |
4466 | HeapTuple tup; |
4467 | Form_pg_attrdef attrdef; |
4468 | ObjectAddress colobject; |
4469 | |
4470 | attrdefDesc = table_open(AttrDefaultRelationId, AccessShareLock); |
4471 | |
4472 | ScanKeyInit(&skey[0], |
4473 | Anum_pg_attrdef_oid, |
4474 | BTEqualStrategyNumber, F_OIDEQ, |
4475 | ObjectIdGetDatum(object->objectId)); |
4476 | |
4477 | adscan = systable_beginscan(attrdefDesc, AttrDefaultOidIndexId, |
4478 | true, NULL, 1, skey); |
4479 | |
4480 | tup = systable_getnext(adscan); |
4481 | |
4482 | if (!HeapTupleIsValid(tup)) |
4483 | elog(ERROR, "could not find tuple for attrdef %u" , |
4484 | object->objectId); |
4485 | |
4486 | attrdef = (Form_pg_attrdef) GETSTRUCT(tup); |
4487 | |
4488 | colobject.classId = RelationRelationId; |
4489 | colobject.objectId = attrdef->adrelid; |
4490 | colobject.objectSubId = attrdef->adnum; |
4491 | |
4492 | appendStringInfo(&buffer, "for %s" , |
4493 | getObjectIdentityParts(&colobject, |
4494 | objname, objargs)); |
4495 | |
4496 | systable_endscan(adscan); |
4497 | table_close(attrdefDesc, AccessShareLock); |
4498 | break; |
4499 | } |
4500 | |
4501 | case OCLASS_LANGUAGE: |
4502 | { |
4503 | HeapTuple langTup; |
4504 | Form_pg_language langForm; |
4505 | |
4506 | langTup = SearchSysCache1(LANGOID, |
4507 | ObjectIdGetDatum(object->objectId)); |
4508 | if (!HeapTupleIsValid(langTup)) |
4509 | elog(ERROR, "cache lookup failed for language %u" , |
4510 | object->objectId); |
4511 | langForm = (Form_pg_language) GETSTRUCT(langTup); |
4512 | appendStringInfoString(&buffer, |
4513 | quote_identifier(NameStr(langForm->lanname))); |
4514 | if (objname) |
4515 | *objname = list_make1(pstrdup(NameStr(langForm->lanname))); |
4516 | ReleaseSysCache(langTup); |
4517 | break; |
4518 | } |
4519 | case OCLASS_LARGEOBJECT: |
4520 | appendStringInfo(&buffer, "%u" , |
4521 | object->objectId); |
4522 | if (objname) |
4523 | *objname = list_make1(psprintf("%u" , object->objectId)); |
4524 | break; |
4525 | |
4526 | case OCLASS_OPERATOR: |
4527 | appendStringInfoString(&buffer, |
4528 | format_operator_qualified(object->objectId)); |
4529 | if (objname) |
4530 | format_operator_parts(object->objectId, objname, objargs); |
4531 | break; |
4532 | |
4533 | case OCLASS_OPCLASS: |
4534 | { |
4535 | HeapTuple opcTup; |
4536 | Form_pg_opclass opcForm; |
4537 | HeapTuple amTup; |
4538 | Form_pg_am amForm; |
4539 | char *schema; |
4540 | |
4541 | opcTup = SearchSysCache1(CLAOID, |
4542 | ObjectIdGetDatum(object->objectId)); |
4543 | if (!HeapTupleIsValid(opcTup)) |
4544 | elog(ERROR, "cache lookup failed for opclass %u" , |
4545 | object->objectId); |
4546 | opcForm = (Form_pg_opclass) GETSTRUCT(opcTup); |
4547 | schema = get_namespace_name_or_temp(opcForm->opcnamespace); |
4548 | |
4549 | amTup = SearchSysCache1(AMOID, |
4550 | ObjectIdGetDatum(opcForm->opcmethod)); |
4551 | if (!HeapTupleIsValid(amTup)) |
4552 | elog(ERROR, "cache lookup failed for access method %u" , |
4553 | opcForm->opcmethod); |
4554 | amForm = (Form_pg_am) GETSTRUCT(amTup); |
4555 | |
4556 | appendStringInfo(&buffer, "%s USING %s" , |
4557 | quote_qualified_identifier(schema, |
4558 | NameStr(opcForm->opcname)), |
4559 | quote_identifier(NameStr(amForm->amname))); |
4560 | if (objname) |
4561 | *objname = list_make3(pstrdup(NameStr(amForm->amname)), |
4562 | schema, |
4563 | pstrdup(NameStr(opcForm->opcname))); |
4564 | |
4565 | ReleaseSysCache(amTup); |
4566 | ReleaseSysCache(opcTup); |
4567 | break; |
4568 | } |
4569 | |
4570 | case OCLASS_OPFAMILY: |
4571 | getOpFamilyIdentity(&buffer, object->objectId, objname); |
4572 | break; |
4573 | |
4574 | case OCLASS_AM: |
4575 | { |
4576 | char *amname; |
4577 | |
4578 | amname = get_am_name(object->objectId); |
4579 | if (!amname) |
4580 | elog(ERROR, "cache lookup failed for access method %u" , |
4581 | object->objectId); |
4582 | appendStringInfoString(&buffer, quote_identifier(amname)); |
4583 | if (objname) |
4584 | *objname = list_make1(amname); |
4585 | } |
4586 | break; |
4587 | |
4588 | case OCLASS_AMOP: |
4589 | { |
4590 | Relation amopDesc; |
4591 | HeapTuple tup; |
4592 | ScanKeyData skey[1]; |
4593 | SysScanDesc amscan; |
4594 | Form_pg_amop amopForm; |
4595 | StringInfoData opfam; |
4596 | char *ltype; |
4597 | char *rtype; |
4598 | |
4599 | amopDesc = table_open(AccessMethodOperatorRelationId, |
4600 | AccessShareLock); |
4601 | |
4602 | ScanKeyInit(&skey[0], |
4603 | Anum_pg_amop_oid, |
4604 | BTEqualStrategyNumber, F_OIDEQ, |
4605 | ObjectIdGetDatum(object->objectId)); |
4606 | |
4607 | amscan = systable_beginscan(amopDesc, AccessMethodOperatorOidIndexId, true, |
4608 | NULL, 1, skey); |
4609 | |
4610 | tup = systable_getnext(amscan); |
4611 | |
4612 | if (!HeapTupleIsValid(tup)) |
4613 | elog(ERROR, "could not find tuple for amop entry %u" , |
4614 | object->objectId); |
4615 | |
4616 | amopForm = (Form_pg_amop) GETSTRUCT(tup); |
4617 | |
4618 | initStringInfo(&opfam); |
4619 | getOpFamilyIdentity(&opfam, amopForm->amopfamily, objname); |
4620 | |
4621 | ltype = format_type_be_qualified(amopForm->amoplefttype); |
4622 | rtype = format_type_be_qualified(amopForm->amoprighttype); |
4623 | |
4624 | if (objname) |
4625 | { |
4626 | *objname = lappend(*objname, |
4627 | psprintf("%d" , amopForm->amopstrategy)); |
4628 | *objargs = list_make2(ltype, rtype); |
4629 | } |
4630 | |
4631 | appendStringInfo(&buffer, "operator %d (%s, %s) of %s" , |
4632 | amopForm->amopstrategy, |
4633 | ltype, rtype, opfam.data); |
4634 | |
4635 | pfree(opfam.data); |
4636 | |
4637 | systable_endscan(amscan); |
4638 | table_close(amopDesc, AccessShareLock); |
4639 | break; |
4640 | } |
4641 | |
4642 | case OCLASS_AMPROC: |
4643 | { |
4644 | Relation amprocDesc; |
4645 | ScanKeyData skey[1]; |
4646 | SysScanDesc amscan; |
4647 | HeapTuple tup; |
4648 | Form_pg_amproc amprocForm; |
4649 | StringInfoData opfam; |
4650 | char *ltype; |
4651 | char *rtype; |
4652 | |
4653 | amprocDesc = table_open(AccessMethodProcedureRelationId, |
4654 | AccessShareLock); |
4655 | |
4656 | ScanKeyInit(&skey[0], |
4657 | Anum_pg_amproc_oid, |
4658 | BTEqualStrategyNumber, F_OIDEQ, |
4659 | ObjectIdGetDatum(object->objectId)); |
4660 | |
4661 | amscan = systable_beginscan(amprocDesc, AccessMethodProcedureOidIndexId, true, |
4662 | NULL, 1, skey); |
4663 | |
4664 | tup = systable_getnext(amscan); |
4665 | |
4666 | if (!HeapTupleIsValid(tup)) |
4667 | elog(ERROR, "could not find tuple for amproc entry %u" , |
4668 | object->objectId); |
4669 | |
4670 | amprocForm = (Form_pg_amproc) GETSTRUCT(tup); |
4671 | |
4672 | initStringInfo(&opfam); |
4673 | getOpFamilyIdentity(&opfam, amprocForm->amprocfamily, objname); |
4674 | |
4675 | ltype = format_type_be_qualified(amprocForm->amproclefttype); |
4676 | rtype = format_type_be_qualified(amprocForm->amprocrighttype); |
4677 | |
4678 | if (objname) |
4679 | { |
4680 | *objname = lappend(*objname, |
4681 | psprintf("%d" , amprocForm->amprocnum)); |
4682 | *objargs = list_make2(ltype, rtype); |
4683 | } |
4684 | |
4685 | appendStringInfo(&buffer, "function %d (%s, %s) of %s" , |
4686 | amprocForm->amprocnum, |
4687 | ltype, rtype, opfam.data); |
4688 | |
4689 | pfree(opfam.data); |
4690 | |
4691 | systable_endscan(amscan); |
4692 | table_close(amprocDesc, AccessShareLock); |
4693 | break; |
4694 | } |
4695 | |
4696 | case OCLASS_REWRITE: |
4697 | { |
4698 | Relation ruleDesc; |
4699 | HeapTuple tup; |
4700 | Form_pg_rewrite rule; |
4701 | |
4702 | ruleDesc = table_open(RewriteRelationId, AccessShareLock); |
4703 | |
4704 | tup = get_catalog_object_by_oid(ruleDesc, Anum_pg_rewrite_oid, |
4705 | object->objectId); |
4706 | |
4707 | if (!HeapTupleIsValid(tup)) |
4708 | elog(ERROR, "could not find tuple for rule %u" , |
4709 | object->objectId); |
4710 | |
4711 | rule = (Form_pg_rewrite) GETSTRUCT(tup); |
4712 | |
4713 | appendStringInfo(&buffer, "%s on " , |
4714 | quote_identifier(NameStr(rule->rulename))); |
4715 | getRelationIdentity(&buffer, rule->ev_class, objname); |
4716 | if (objname) |
4717 | *objname = lappend(*objname, pstrdup(NameStr(rule->rulename))); |
4718 | |
4719 | table_close(ruleDesc, AccessShareLock); |
4720 | break; |
4721 | } |
4722 | |
4723 | case OCLASS_TRIGGER: |
4724 | { |
4725 | Relation trigDesc; |
4726 | HeapTuple tup; |
4727 | Form_pg_trigger trig; |
4728 | |
4729 | trigDesc = table_open(TriggerRelationId, AccessShareLock); |
4730 | |
4731 | tup = get_catalog_object_by_oid(trigDesc, Anum_pg_trigger_oid, |
4732 | object->objectId); |
4733 | |
4734 | if (!HeapTupleIsValid(tup)) |
4735 | elog(ERROR, "could not find tuple for trigger %u" , |
4736 | object->objectId); |
4737 | |
4738 | trig = (Form_pg_trigger) GETSTRUCT(tup); |
4739 | |
4740 | appendStringInfo(&buffer, "%s on " , |
4741 | quote_identifier(NameStr(trig->tgname))); |
4742 | getRelationIdentity(&buffer, trig->tgrelid, objname); |
4743 | if (objname) |
4744 | *objname = lappend(*objname, pstrdup(NameStr(trig->tgname))); |
4745 | |
4746 | table_close(trigDesc, AccessShareLock); |
4747 | break; |
4748 | } |
4749 | |
4750 | case OCLASS_SCHEMA: |
4751 | { |
4752 | char *nspname; |
4753 | |
4754 | nspname = get_namespace_name_or_temp(object->objectId); |
4755 | if (!nspname) |
4756 | elog(ERROR, "cache lookup failed for namespace %u" , |
4757 | object->objectId); |
4758 | appendStringInfoString(&buffer, |
4759 | quote_identifier(nspname)); |
4760 | if (objname) |
4761 | *objname = list_make1(nspname); |
4762 | break; |
4763 | } |
4764 | |
4765 | case OCLASS_STATISTIC_EXT: |
4766 | { |
4767 | HeapTuple tup; |
4768 | Form_pg_statistic_ext formStatistic; |
4769 | char *schema; |
4770 | |
4771 | tup = SearchSysCache1(STATEXTOID, |
4772 | ObjectIdGetDatum(object->objectId)); |
4773 | if (!HeapTupleIsValid(tup)) |
4774 | elog(ERROR, "cache lookup failed for statistics object %u" , |
4775 | object->objectId); |
4776 | formStatistic = (Form_pg_statistic_ext) GETSTRUCT(tup); |
4777 | schema = get_namespace_name_or_temp(formStatistic->stxnamespace); |
4778 | appendStringInfoString(&buffer, |
4779 | quote_qualified_identifier(schema, |
4780 | NameStr(formStatistic->stxname))); |
4781 | if (objname) |
4782 | *objname = list_make2(schema, |
4783 | pstrdup(NameStr(formStatistic->stxname))); |
4784 | ReleaseSysCache(tup); |
4785 | } |
4786 | break; |
4787 | |
4788 | case OCLASS_TSPARSER: |
4789 | { |
4790 | HeapTuple tup; |
4791 | Form_pg_ts_parser formParser; |
4792 | char *schema; |
4793 | |
4794 | tup = SearchSysCache1(TSPARSEROID, |
4795 | ObjectIdGetDatum(object->objectId)); |
4796 | if (!HeapTupleIsValid(tup)) |
4797 | elog(ERROR, "cache lookup failed for text search parser %u" , |
4798 | object->objectId); |
4799 | formParser = (Form_pg_ts_parser) GETSTRUCT(tup); |
4800 | schema = get_namespace_name_or_temp(formParser->prsnamespace); |
4801 | appendStringInfoString(&buffer, |
4802 | quote_qualified_identifier(schema, |
4803 | NameStr(formParser->prsname))); |
4804 | if (objname) |
4805 | *objname = list_make2(schema, |
4806 | pstrdup(NameStr(formParser->prsname))); |
4807 | ReleaseSysCache(tup); |
4808 | break; |
4809 | } |
4810 | |
4811 | case OCLASS_TSDICT: |
4812 | { |
4813 | HeapTuple tup; |
4814 | Form_pg_ts_dict formDict; |
4815 | char *schema; |
4816 | |
4817 | tup = SearchSysCache1(TSDICTOID, |
4818 | ObjectIdGetDatum(object->objectId)); |
4819 | if (!HeapTupleIsValid(tup)) |
4820 | elog(ERROR, "cache lookup failed for text search dictionary %u" , |
4821 | object->objectId); |
4822 | formDict = (Form_pg_ts_dict) GETSTRUCT(tup); |
4823 | schema = get_namespace_name_or_temp(formDict->dictnamespace); |
4824 | appendStringInfoString(&buffer, |
4825 | quote_qualified_identifier(schema, |
4826 | NameStr(formDict->dictname))); |
4827 | if (objname) |
4828 | *objname = list_make2(schema, |
4829 | pstrdup(NameStr(formDict->dictname))); |
4830 | ReleaseSysCache(tup); |
4831 | break; |
4832 | } |
4833 | |
4834 | case OCLASS_TSTEMPLATE: |
4835 | { |
4836 | HeapTuple tup; |
4837 | Form_pg_ts_template formTmpl; |
4838 | char *schema; |
4839 | |
4840 | tup = SearchSysCache1(TSTEMPLATEOID, |
4841 | ObjectIdGetDatum(object->objectId)); |
4842 | if (!HeapTupleIsValid(tup)) |
4843 | elog(ERROR, "cache lookup failed for text search template %u" , |
4844 | object->objectId); |
4845 | formTmpl = (Form_pg_ts_template) GETSTRUCT(tup); |
4846 | schema = get_namespace_name_or_temp(formTmpl->tmplnamespace); |
4847 | appendStringInfoString(&buffer, |
4848 | quote_qualified_identifier(schema, |
4849 | NameStr(formTmpl->tmplname))); |
4850 | if (objname) |
4851 | *objname = list_make2(schema, |
4852 | pstrdup(NameStr(formTmpl->tmplname))); |
4853 | ReleaseSysCache(tup); |
4854 | break; |
4855 | } |
4856 | |
4857 | case OCLASS_TSCONFIG: |
4858 | { |
4859 | HeapTuple tup; |
4860 | Form_pg_ts_config formCfg; |
4861 | char *schema; |
4862 | |
4863 | tup = SearchSysCache1(TSCONFIGOID, |
4864 | ObjectIdGetDatum(object->objectId)); |
4865 | if (!HeapTupleIsValid(tup)) |
4866 | elog(ERROR, "cache lookup failed for text search configuration %u" , |
4867 | object->objectId); |
4868 | formCfg = (Form_pg_ts_config) GETSTRUCT(tup); |
4869 | schema = get_namespace_name_or_temp(formCfg->cfgnamespace); |
4870 | appendStringInfoString(&buffer, |
4871 | quote_qualified_identifier(schema, |
4872 | NameStr(formCfg->cfgname))); |
4873 | if (objname) |
4874 | *objname = list_make2(schema, |
4875 | pstrdup(NameStr(formCfg->cfgname))); |
4876 | ReleaseSysCache(tup); |
4877 | break; |
4878 | } |
4879 | |
4880 | case OCLASS_ROLE: |
4881 | { |
4882 | char *username; |
4883 | |
4884 | username = GetUserNameFromId(object->objectId, false); |
4885 | if (objname) |
4886 | *objname = list_make1(username); |
4887 | appendStringInfoString(&buffer, |
4888 | quote_identifier(username)); |
4889 | break; |
4890 | } |
4891 | |
4892 | case OCLASS_DATABASE: |
4893 | { |
4894 | char *datname; |
4895 | |
4896 | datname = get_database_name(object->objectId); |
4897 | if (!datname) |
4898 | elog(ERROR, "cache lookup failed for database %u" , |
4899 | object->objectId); |
4900 | if (objname) |
4901 | *objname = list_make1(datname); |
4902 | appendStringInfoString(&buffer, |
4903 | quote_identifier(datname)); |
4904 | break; |
4905 | } |
4906 | |
4907 | case OCLASS_TBLSPACE: |
4908 | { |
4909 | char *tblspace; |
4910 | |
4911 | tblspace = get_tablespace_name(object->objectId); |
4912 | if (!tblspace) |
4913 | elog(ERROR, "cache lookup failed for tablespace %u" , |
4914 | object->objectId); |
4915 | if (objname) |
4916 | *objname = list_make1(tblspace); |
4917 | appendStringInfoString(&buffer, |
4918 | quote_identifier(tblspace)); |
4919 | break; |
4920 | } |
4921 | |
4922 | case OCLASS_FDW: |
4923 | { |
4924 | ForeignDataWrapper *fdw; |
4925 | |
4926 | fdw = GetForeignDataWrapper(object->objectId); |
4927 | appendStringInfoString(&buffer, quote_identifier(fdw->fdwname)); |
4928 | if (objname) |
4929 | *objname = list_make1(pstrdup(fdw->fdwname)); |
4930 | break; |
4931 | } |
4932 | |
4933 | case OCLASS_FOREIGN_SERVER: |
4934 | { |
4935 | ForeignServer *srv; |
4936 | |
4937 | srv = GetForeignServer(object->objectId); |
4938 | appendStringInfoString(&buffer, |
4939 | quote_identifier(srv->servername)); |
4940 | if (objname) |
4941 | *objname = list_make1(pstrdup(srv->servername)); |
4942 | break; |
4943 | } |
4944 | |
4945 | case OCLASS_USER_MAPPING: |
4946 | { |
4947 | HeapTuple tup; |
4948 | Oid useid; |
4949 | Form_pg_user_mapping umform; |
4950 | ForeignServer *srv; |
4951 | const char *usename; |
4952 | |
4953 | tup = SearchSysCache1(USERMAPPINGOID, |
4954 | ObjectIdGetDatum(object->objectId)); |
4955 | if (!HeapTupleIsValid(tup)) |
4956 | elog(ERROR, "cache lookup failed for user mapping %u" , |
4957 | object->objectId); |
4958 | umform = (Form_pg_user_mapping) GETSTRUCT(tup); |
4959 | useid = umform->umuser; |
4960 | srv = GetForeignServer(umform->umserver); |
4961 | |
4962 | ReleaseSysCache(tup); |
4963 | |
4964 | if (OidIsValid(useid)) |
4965 | usename = GetUserNameFromId(useid, false); |
4966 | else |
4967 | usename = "public" ; |
4968 | |
4969 | if (objname) |
4970 | { |
4971 | *objname = list_make1(pstrdup(usename)); |
4972 | *objargs = list_make1(pstrdup(srv->servername)); |
4973 | } |
4974 | |
4975 | appendStringInfo(&buffer, "%s on server %s" , |
4976 | quote_identifier(usename), |
4977 | srv->servername); |
4978 | break; |
4979 | } |
4980 | |
4981 | case OCLASS_DEFACL: |
4982 | { |
4983 | Relation defaclrel; |
4984 | ScanKeyData skey[1]; |
4985 | SysScanDesc rcscan; |
4986 | HeapTuple tup; |
4987 | Form_pg_default_acl defacl; |
4988 | char *schema; |
4989 | char *username; |
4990 | |
4991 | defaclrel = table_open(DefaultAclRelationId, AccessShareLock); |
4992 | |
4993 | ScanKeyInit(&skey[0], |
4994 | Anum_pg_default_acl_oid, |
4995 | BTEqualStrategyNumber, F_OIDEQ, |
4996 | ObjectIdGetDatum(object->objectId)); |
4997 | |
4998 | rcscan = systable_beginscan(defaclrel, DefaultAclOidIndexId, |
4999 | true, NULL, 1, skey); |
5000 | |
5001 | tup = systable_getnext(rcscan); |
5002 | |
5003 | if (!HeapTupleIsValid(tup)) |
5004 | elog(ERROR, "could not find tuple for default ACL %u" , |
5005 | object->objectId); |
5006 | |
5007 | defacl = (Form_pg_default_acl) GETSTRUCT(tup); |
5008 | |
5009 | username = GetUserNameFromId(defacl->defaclrole, false); |
5010 | appendStringInfo(&buffer, |
5011 | "for role %s" , |
5012 | quote_identifier(username)); |
5013 | |
5014 | if (OidIsValid(defacl->defaclnamespace)) |
5015 | { |
5016 | schema = get_namespace_name_or_temp(defacl->defaclnamespace); |
5017 | appendStringInfo(&buffer, |
5018 | " in schema %s" , |
5019 | quote_identifier(schema)); |
5020 | } |
5021 | else |
5022 | schema = NULL; |
5023 | |
5024 | switch (defacl->defaclobjtype) |
5025 | { |
5026 | case DEFACLOBJ_RELATION: |
5027 | appendStringInfoString(&buffer, |
5028 | " on tables" ); |
5029 | break; |
5030 | case DEFACLOBJ_SEQUENCE: |
5031 | appendStringInfoString(&buffer, |
5032 | " on sequences" ); |
5033 | break; |
5034 | case DEFACLOBJ_FUNCTION: |
5035 | appendStringInfoString(&buffer, |
5036 | " on functions" ); |
5037 | break; |
5038 | case DEFACLOBJ_TYPE: |
5039 | appendStringInfoString(&buffer, |
5040 | " on types" ); |
5041 | break; |
5042 | case DEFACLOBJ_NAMESPACE: |
5043 | appendStringInfoString(&buffer, |
5044 | " on schemas" ); |
5045 | break; |
5046 | } |
5047 | |
5048 | if (objname) |
5049 | { |
5050 | *objname = list_make1(username); |
5051 | if (schema) |
5052 | *objname = lappend(*objname, schema); |
5053 | *objargs = list_make1(psprintf("%c" , defacl->defaclobjtype)); |
5054 | } |
5055 | |
5056 | systable_endscan(rcscan); |
5057 | table_close(defaclrel, AccessShareLock); |
5058 | break; |
5059 | } |
5060 | |
5061 | case OCLASS_EXTENSION: |
5062 | { |
5063 | char *extname; |
5064 | |
5065 | extname = get_extension_name(object->objectId); |
5066 | if (!extname) |
5067 | elog(ERROR, "cache lookup failed for extension %u" , |
5068 | object->objectId); |
5069 | appendStringInfoString(&buffer, quote_identifier(extname)); |
5070 | if (objname) |
5071 | *objname = list_make1(extname); |
5072 | break; |
5073 | } |
5074 | |
5075 | case OCLASS_EVENT_TRIGGER: |
5076 | { |
5077 | HeapTuple tup; |
5078 | Form_pg_event_trigger trigForm; |
5079 | |
5080 | /* no objname support here */ |
5081 | if (objname) |
5082 | *objname = NIL; |
5083 | |
5084 | tup = SearchSysCache1(EVENTTRIGGEROID, |
5085 | ObjectIdGetDatum(object->objectId)); |
5086 | if (!HeapTupleIsValid(tup)) |
5087 | elog(ERROR, "cache lookup failed for event trigger %u" , |
5088 | object->objectId); |
5089 | trigForm = (Form_pg_event_trigger) GETSTRUCT(tup); |
5090 | appendStringInfoString(&buffer, |
5091 | quote_identifier(NameStr(trigForm->evtname))); |
5092 | ReleaseSysCache(tup); |
5093 | break; |
5094 | } |
5095 | |
5096 | case OCLASS_POLICY: |
5097 | { |
5098 | Relation polDesc; |
5099 | HeapTuple tup; |
5100 | Form_pg_policy policy; |
5101 | |
5102 | polDesc = table_open(PolicyRelationId, AccessShareLock); |
5103 | |
5104 | tup = get_catalog_object_by_oid(polDesc, Anum_pg_policy_oid, |
5105 | object->objectId); |
5106 | |
5107 | if (!HeapTupleIsValid(tup)) |
5108 | elog(ERROR, "could not find tuple for policy %u" , |
5109 | object->objectId); |
5110 | |
5111 | policy = (Form_pg_policy) GETSTRUCT(tup); |
5112 | |
5113 | appendStringInfo(&buffer, "%s on " , |
5114 | quote_identifier(NameStr(policy->polname))); |
5115 | getRelationIdentity(&buffer, policy->polrelid, objname); |
5116 | if (objname) |
5117 | *objname = lappend(*objname, pstrdup(NameStr(policy->polname))); |
5118 | |
5119 | table_close(polDesc, AccessShareLock); |
5120 | break; |
5121 | } |
5122 | |
5123 | case OCLASS_PUBLICATION: |
5124 | { |
5125 | char *pubname; |
5126 | |
5127 | pubname = get_publication_name(object->objectId, false); |
5128 | appendStringInfoString(&buffer, |
5129 | quote_identifier(pubname)); |
5130 | if (objname) |
5131 | *objname = list_make1(pubname); |
5132 | break; |
5133 | } |
5134 | |
5135 | case OCLASS_PUBLICATION_REL: |
5136 | { |
5137 | HeapTuple tup; |
5138 | char *pubname; |
5139 | Form_pg_publication_rel prform; |
5140 | |
5141 | tup = SearchSysCache1(PUBLICATIONREL, |
5142 | ObjectIdGetDatum(object->objectId)); |
5143 | if (!HeapTupleIsValid(tup)) |
5144 | elog(ERROR, "cache lookup failed for publication table %u" , |
5145 | object->objectId); |
5146 | |
5147 | prform = (Form_pg_publication_rel) GETSTRUCT(tup); |
5148 | pubname = get_publication_name(prform->prpubid, false); |
5149 | |
5150 | getRelationIdentity(&buffer, prform->prrelid, objname); |
5151 | appendStringInfo(&buffer, " in publication %s" , pubname); |
5152 | |
5153 | if (objargs) |
5154 | *objargs = list_make1(pubname); |
5155 | |
5156 | ReleaseSysCache(tup); |
5157 | break; |
5158 | } |
5159 | |
5160 | case OCLASS_SUBSCRIPTION: |
5161 | { |
5162 | char *subname; |
5163 | |
5164 | subname = get_subscription_name(object->objectId, false); |
5165 | appendStringInfoString(&buffer, |
5166 | quote_identifier(subname)); |
5167 | if (objname) |
5168 | *objname = list_make1(subname); |
5169 | break; |
5170 | } |
5171 | |
5172 | case OCLASS_TRANSFORM: |
5173 | { |
5174 | Relation transformDesc; |
5175 | HeapTuple tup; |
5176 | Form_pg_transform transform; |
5177 | char *transformLang; |
5178 | char *transformType; |
5179 | |
5180 | transformDesc = table_open(TransformRelationId, AccessShareLock); |
5181 | |
5182 | tup = get_catalog_object_by_oid(transformDesc, |
5183 | Anum_pg_transform_oid, |
5184 | object->objectId); |
5185 | |
5186 | if (!HeapTupleIsValid(tup)) |
5187 | elog(ERROR, "could not find tuple for transform %u" , |
5188 | object->objectId); |
5189 | |
5190 | transform = (Form_pg_transform) GETSTRUCT(tup); |
5191 | |
5192 | transformType = format_type_be_qualified(transform->trftype); |
5193 | transformLang = get_language_name(transform->trflang, false); |
5194 | |
5195 | appendStringInfo(&buffer, "for %s on language %s" , |
5196 | transformType, |
5197 | transformLang); |
5198 | if (objname) |
5199 | { |
5200 | *objname = list_make1(transformType); |
5201 | *objargs = list_make1(pstrdup(transformLang)); |
5202 | } |
5203 | |
5204 | table_close(transformDesc, AccessShareLock); |
5205 | } |
5206 | break; |
5207 | |
5208 | /* |
5209 | * There's intentionally no default: case here; we want the |
5210 | * compiler to warn if a new OCLASS hasn't been handled above. |
5211 | */ |
5212 | } |
5213 | |
5214 | /* |
5215 | * If a get_object_address representation was requested, make sure we are |
5216 | * providing one. We don't check objargs, because many of the cases above |
5217 | * leave it as NIL. |
5218 | */ |
5219 | if (objname && *objname == NIL) |
5220 | elog(ERROR, "requested object address for unsupported object class %d: text result \"%s\"" , |
5221 | (int) getObjectClass(object), buffer.data); |
5222 | |
5223 | return buffer.data; |
5224 | } |
5225 | |
5226 | static void |
5227 | getOpFamilyIdentity(StringInfo buffer, Oid opfid, List **object) |
5228 | { |
5229 | HeapTuple opfTup; |
5230 | Form_pg_opfamily opfForm; |
5231 | HeapTuple amTup; |
5232 | Form_pg_am amForm; |
5233 | char *schema; |
5234 | |
5235 | opfTup = SearchSysCache1(OPFAMILYOID, ObjectIdGetDatum(opfid)); |
5236 | if (!HeapTupleIsValid(opfTup)) |
5237 | elog(ERROR, "cache lookup failed for opfamily %u" , opfid); |
5238 | opfForm = (Form_pg_opfamily) GETSTRUCT(opfTup); |
5239 | |
5240 | amTup = SearchSysCache1(AMOID, ObjectIdGetDatum(opfForm->opfmethod)); |
5241 | if (!HeapTupleIsValid(amTup)) |
5242 | elog(ERROR, "cache lookup failed for access method %u" , |
5243 | opfForm->opfmethod); |
5244 | amForm = (Form_pg_am) GETSTRUCT(amTup); |
5245 | |
5246 | schema = get_namespace_name_or_temp(opfForm->opfnamespace); |
5247 | appendStringInfo(buffer, "%s USING %s" , |
5248 | quote_qualified_identifier(schema, |
5249 | NameStr(opfForm->opfname)), |
5250 | NameStr(amForm->amname)); |
5251 | |
5252 | if (object) |
5253 | *object = list_make3(pstrdup(NameStr(amForm->amname)), |
5254 | pstrdup(schema), |
5255 | pstrdup(NameStr(opfForm->opfname))); |
5256 | |
5257 | ReleaseSysCache(amTup); |
5258 | ReleaseSysCache(opfTup); |
5259 | } |
5260 | |
5261 | /* |
5262 | * Append the relation identity (quoted qualified name) to the given |
5263 | * StringInfo. |
5264 | */ |
5265 | static void |
5266 | getRelationIdentity(StringInfo buffer, Oid relid, List **object) |
5267 | { |
5268 | HeapTuple relTup; |
5269 | Form_pg_class relForm; |
5270 | char *schema; |
5271 | |
5272 | relTup = SearchSysCache1(RELOID, |
5273 | ObjectIdGetDatum(relid)); |
5274 | if (!HeapTupleIsValid(relTup)) |
5275 | elog(ERROR, "cache lookup failed for relation %u" , relid); |
5276 | relForm = (Form_pg_class) GETSTRUCT(relTup); |
5277 | |
5278 | schema = get_namespace_name_or_temp(relForm->relnamespace); |
5279 | appendStringInfoString(buffer, |
5280 | quote_qualified_identifier(schema, |
5281 | NameStr(relForm->relname))); |
5282 | if (object) |
5283 | *object = list_make2(schema, pstrdup(NameStr(relForm->relname))); |
5284 | |
5285 | ReleaseSysCache(relTup); |
5286 | } |
5287 | |
5288 | /* |
5289 | * Auxiliary function to build a TEXT array out of a list of C-strings. |
5290 | */ |
5291 | ArrayType * |
5292 | strlist_to_textarray(List *list) |
5293 | { |
5294 | ArrayType *arr; |
5295 | Datum *datums; |
5296 | bool *nulls; |
5297 | int j = 0; |
5298 | ListCell *cell; |
5299 | MemoryContext memcxt; |
5300 | MemoryContext oldcxt; |
5301 | int lb[1]; |
5302 | |
5303 | /* Work in a temp context; easier than individually pfree'ing the Datums */ |
5304 | memcxt = AllocSetContextCreate(CurrentMemoryContext, |
5305 | "strlist to array" , |
5306 | ALLOCSET_DEFAULT_SIZES); |
5307 | oldcxt = MemoryContextSwitchTo(memcxt); |
5308 | |
5309 | datums = (Datum *) palloc(sizeof(Datum) * list_length(list)); |
5310 | nulls = palloc(sizeof(bool) * list_length(list)); |
5311 | |
5312 | foreach(cell, list) |
5313 | { |
5314 | char *name = lfirst(cell); |
5315 | |
5316 | if (name) |
5317 | { |
5318 | nulls[j] = false; |
5319 | datums[j++] = CStringGetTextDatum(name); |
5320 | } |
5321 | else |
5322 | nulls[j] = true; |
5323 | } |
5324 | |
5325 | MemoryContextSwitchTo(oldcxt); |
5326 | |
5327 | lb[0] = 1; |
5328 | arr = construct_md_array(datums, nulls, 1, &j, |
5329 | lb, TEXTOID, -1, false, 'i'); |
5330 | |
5331 | MemoryContextDelete(memcxt); |
5332 | |
5333 | return arr; |
5334 | } |
5335 | |
5336 | ObjectType |
5337 | get_relkind_objtype(char relkind) |
5338 | { |
5339 | switch (relkind) |
5340 | { |
5341 | case RELKIND_RELATION: |
5342 | case RELKIND_PARTITIONED_TABLE: |
5343 | return OBJECT_TABLE; |
5344 | case RELKIND_INDEX: |
5345 | case RELKIND_PARTITIONED_INDEX: |
5346 | return OBJECT_INDEX; |
5347 | case RELKIND_SEQUENCE: |
5348 | return OBJECT_SEQUENCE; |
5349 | case RELKIND_VIEW: |
5350 | return OBJECT_VIEW; |
5351 | case RELKIND_MATVIEW: |
5352 | return OBJECT_MATVIEW; |
5353 | case RELKIND_FOREIGN_TABLE: |
5354 | return OBJECT_FOREIGN_TABLE; |
5355 | |
5356 | /* |
5357 | * other relkinds are not supported here because they don't map to |
5358 | * OBJECT_* values |
5359 | */ |
5360 | default: |
5361 | elog(ERROR, "unexpected relkind: %d" , relkind); |
5362 | return 0; |
5363 | } |
5364 | } |
5365 | |