1 /*-------------------------------------------------------------------------
4 * code to support accessing and searching namespaces
6 * This is separate from pg_namespace.c, which contains the routines that
7 * directly manipulate the pg_namespace system catalog. This module
8 * provides routines associated with defining a "namespace search path"
9 * and implementing search-path-controlled searches.
12 * Portions Copyright (c) 1996-2003, PostgreSQL Global Development Group
13 * Portions Copyright (c) 1994, Regents of the University of California
16 * $PostgreSQL: pgsql/src/backend/catalog/namespace.c,v 1.61 2003/12/29 21:33:09 tgl Exp $
18 *-------------------------------------------------------------------------
22 #include "access/xact.h"
23 #include "catalog/catname.h"
24 #include "catalog/dependency.h"
25 #include "catalog/namespace.h"
26 #include "catalog/pg_conversion.h"
27 #include "catalog/pg_namespace.h"
28 #include "catalog/pg_opclass.h"
29 #include "catalog/pg_operator.h"
30 #include "catalog/pg_proc.h"
31 #include "catalog/pg_shadow.h"
32 #include "catalog/pg_type.h"
33 #include "commands/dbcommands.h"
34 #include "lib/stringinfo.h"
35 #include "miscadmin.h"
36 #include "nodes/makefuncs.h"
37 #include "storage/backendid.h"
38 #include "storage/ipc.h"
39 #include "utils/acl.h"
40 #include "utils/builtins.h"
41 #include "utils/catcache.h"
42 #include "utils/inval.h"
43 #include "utils/lsyscache.h"
44 #include "utils/memutils.h"
45 #include "utils/syscache.h"
49 * The namespace search path is a possibly-empty list of namespace OIDs.
50 * In addition to the explicit list, several implicitly-searched namespaces
53 * 1. If a "special" namespace has been set by PushSpecialNamespace, it is
54 * always searched first. (This is a hack for CREATE SCHEMA.)
56 * 2. If a TEMP table namespace has been initialized in this session, it
57 * is always searched just after any special namespace.
59 * 3. The system catalog namespace is always searched. If the system
60 * namespace is present in the explicit path then it will be searched in
61 * the specified order; otherwise it will be searched after TEMP tables and
62 * *before* the explicit list. (It might seem that the system namespace
63 * should be implicitly last, but this behavior appears to be required by
64 * SQL99. Also, this provides a way to search the system namespace first
65 * without thereby making it the default creation target namespace.)
67 * The default creation target namespace is normally equal to the first
68 * element of the explicit list, but is the "special" namespace when one
69 * has been set. If the explicit list is empty and there is no special
70 * namespace, there is no default target.
72 * In bootstrap mode, the search path is set equal to 'pg_catalog', so that
73 * the system namespace is the only one searched or inserted into.
74 * The initdb script is also careful to set search_path to 'pg_catalog' for
75 * its post-bootstrap standalone backend runs. Otherwise the default search
76 * path is determined by GUC. The factory default path contains the PUBLIC
77 * namespace (if it exists), preceded by the user's personal namespace
80 * If namespaceSearchPathValid is false, then namespaceSearchPath (and other
81 * derived variables) need to be recomputed from namespace_search_path.
82 * We mark it invalid upon an assignment to namespace_search_path or receipt
83 * of a syscache invalidation event for pg_namespace. The recomputation
84 * is done during the next lookup attempt.
86 * Any namespaces mentioned in namespace_search_path that are not readable
87 * by the current user ID are simply left out of namespaceSearchPath; so
88 * we have to be willing to recompute the path when current userid changes.
89 * namespaceUser is the userid the path has been computed for.
92 static List *namespaceSearchPath = NIL;
94 static Oid namespaceUser = InvalidOid;
96 /* default place to create stuff; if InvalidOid, no default */
97 static Oid defaultCreationNamespace = InvalidOid;
99 /* first explicit member of list; usually same as defaultCreationNamespace */
100 static Oid firstExplicitNamespace = InvalidOid;
102 /* The above four values are valid only if namespaceSearchPathValid */
103 static bool namespaceSearchPathValid = true;
106 * myTempNamespace is InvalidOid until and unless a TEMP namespace is set up
107 * in a particular backend session (this happens when a CREATE TEMP TABLE
108 * command is first executed). Thereafter it's the OID of the temp namespace.
109 * firstTempTransaction flags whether we've committed creation of the TEMP
112 static Oid myTempNamespace = InvalidOid;
114 static bool firstTempTransaction = false;
117 * "Special" namespace for CREATE SCHEMA. If set, it's the first search
118 * path element, and also the default creation namespace.
120 static Oid mySpecialNamespace = InvalidOid;
123 * This is the text equivalent of the search path --- it's the value
124 * of the GUC variable 'search_path'.
126 char *namespace_search_path = NULL;
129 /* Local functions */
130 static void recomputeNamespacePath(void);
131 static void InitTempTableNamespace(void);
132 static void RemoveTempRelations(Oid tempNamespaceId);
133 static void RemoveTempRelationsCallback(int code, Datum arg);
134 static void NamespaceCallback(Datum arg, Oid relid);
136 /* These don't really need to appear in any header file */
137 Datum pg_table_is_visible(PG_FUNCTION_ARGS);
138 Datum pg_type_is_visible(PG_FUNCTION_ARGS);
139 Datum pg_function_is_visible(PG_FUNCTION_ARGS);
140 Datum pg_operator_is_visible(PG_FUNCTION_ARGS);
141 Datum pg_opclass_is_visible(PG_FUNCTION_ARGS);
142 Datum pg_conversion_is_visible(PG_FUNCTION_ARGS);
147 * Given a RangeVar describing an existing relation,
148 * select the proper namespace and look up the relation OID.
150 * If the relation is not found, return InvalidOid if failOK = true,
151 * otherwise raise an error.
154 RangeVarGetRelid(const RangeVar *relation, bool failOK)
160 * We check the catalog name and then ignore it.
162 if (relation->catalogname)
164 if (strcmp(relation->catalogname, get_database_name(MyDatabaseId)) != 0)
166 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
167 errmsg("cross-database references are not implemented")));
170 if (relation->schemaname)
172 /* use exact schema given */
173 namespaceId = LookupExplicitNamespace(relation->schemaname);
174 relId = get_relname_relid(relation->relname, namespaceId);
178 /* search the namespace path */
179 relId = RelnameGetRelid(relation->relname);
182 if (!OidIsValid(relId) && !failOK)
184 if (relation->schemaname)
186 (errcode(ERRCODE_UNDEFINED_TABLE),
187 errmsg("relation \"%s.%s\" does not exist",
188 relation->schemaname, relation->relname)));
191 (errcode(ERRCODE_UNDEFINED_TABLE),
192 errmsg("relation \"%s\" does not exist",
193 relation->relname)));
199 * RangeVarGetCreationNamespace
200 * Given a RangeVar describing a to-be-created relation,
201 * choose which namespace to create it in.
203 * Note: calling this may result in a CommandCounterIncrement operation.
204 * That will happen on the first request for a temp table in any particular
205 * backend run; we will need to either create or clean out the temp schema.
208 RangeVarGetCreationNamespace(const RangeVar *newRelation)
213 * We check the catalog name and then ignore it.
215 if (newRelation->catalogname)
217 if (strcmp(newRelation->catalogname, get_database_name(MyDatabaseId)) != 0)
219 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
220 errmsg("cross-database references are not implemented")));
223 if (newRelation->istemp)
225 /* TEMP tables are created in our backend-local temp namespace */
226 if (newRelation->schemaname)
228 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
229 errmsg("temporary tables may not specify a schema name")));
230 /* Initialize temp namespace if first time through */
231 if (!OidIsValid(myTempNamespace))
232 InitTempTableNamespace();
233 return myTempNamespace;
236 if (newRelation->schemaname)
238 /* use exact schema given */
239 namespaceId = GetSysCacheOid(NAMESPACENAME,
240 CStringGetDatum(newRelation->schemaname),
242 if (!OidIsValid(namespaceId))
244 (errcode(ERRCODE_UNDEFINED_SCHEMA),
245 errmsg("schema \"%s\" does not exist",
246 newRelation->schemaname)));
247 /* we do not check for USAGE rights here! */
251 /* use the default creation namespace */
252 recomputeNamespacePath();
253 namespaceId = defaultCreationNamespace;
254 if (!OidIsValid(namespaceId))
256 (errcode(ERRCODE_UNDEFINED_SCHEMA),
257 errmsg("no schema has been selected to create in")));
260 /* Note: callers will check for CREATE rights when appropriate */
267 * Try to resolve an unqualified relation name.
268 * Returns OID if relation found in search path, else InvalidOid.
271 RelnameGetRelid(const char *relname)
276 recomputeNamespacePath();
278 foreach(lptr, namespaceSearchPath)
280 Oid namespaceId = lfirsto(lptr);
282 relid = get_relname_relid(relname, namespaceId);
283 if (OidIsValid(relid))
287 /* Not found in path */
294 * Determine whether a relation (identified by OID) is visible in the
295 * current search path. Visible means "would be found by searching
296 * for the unqualified relation name".
299 RelationIsVisible(Oid relid)
302 Form_pg_class relform;
306 reltup = SearchSysCache(RELOID,
307 ObjectIdGetDatum(relid),
309 if (!HeapTupleIsValid(reltup))
310 elog(ERROR, "cache lookup failed for relation %u", relid);
311 relform = (Form_pg_class) GETSTRUCT(reltup);
313 recomputeNamespacePath();
316 * Quick check: if it ain't in the path at all, it ain't visible.
317 * Items in the system namespace are surely in the path and so we
318 * needn't even do oidMember() for them.
320 relnamespace = relform->relnamespace;
321 if (relnamespace != PG_CATALOG_NAMESPACE &&
322 !oidMember(relnamespace, namespaceSearchPath))
327 * If it is in the path, it might still not be visible; it could
328 * be hidden by another relation of the same name earlier in the
329 * path. So we must do a slow check to see if this rel would be
330 * found by RelnameGetRelid.
332 char *relname = NameStr(relform->relname);
334 visible = (RelnameGetRelid(relname) == relid);
337 ReleaseSysCache(reltup);
345 * Try to resolve an unqualified datatype name.
346 * Returns OID if type found in search path, else InvalidOid.
348 * This is essentially the same as RelnameGetRelid.
351 TypenameGetTypid(const char *typname)
356 recomputeNamespacePath();
358 foreach(lptr, namespaceSearchPath)
360 Oid namespaceId = lfirsto(lptr);
362 typid = GetSysCacheOid(TYPENAMENSP,
363 PointerGetDatum(typname),
364 ObjectIdGetDatum(namespaceId),
366 if (OidIsValid(typid))
370 /* Not found in path */
376 * Determine whether a type (identified by OID) is visible in the
377 * current search path. Visible means "would be found by searching
378 * for the unqualified type name".
381 TypeIsVisible(Oid typid)
384 Form_pg_type typform;
388 typtup = SearchSysCache(TYPEOID,
389 ObjectIdGetDatum(typid),
391 if (!HeapTupleIsValid(typtup))
392 elog(ERROR, "cache lookup failed for type %u", typid);
393 typform = (Form_pg_type) GETSTRUCT(typtup);
395 recomputeNamespacePath();
398 * Quick check: if it ain't in the path at all, it ain't visible.
399 * Items in the system namespace are surely in the path and so we
400 * needn't even do oidMember() for them.
402 typnamespace = typform->typnamespace;
403 if (typnamespace != PG_CATALOG_NAMESPACE &&
404 !oidMember(typnamespace, namespaceSearchPath))
409 * If it is in the path, it might still not be visible; it could
410 * be hidden by another type of the same name earlier in the path.
411 * So we must do a slow check to see if this type would be found
412 * by TypenameGetTypid.
414 char *typname = NameStr(typform->typname);
416 visible = (TypenameGetTypid(typname) == typid);
419 ReleaseSysCache(typtup);
426 * FuncnameGetCandidates
427 * Given a possibly-qualified function name and argument count,
428 * retrieve a list of the possible matches.
430 * If nargs is -1, we return all functions matching the given name,
431 * regardless of argument count.
433 * We search a single namespace if the function name is qualified, else
434 * all namespaces in the search path. The return list will never contain
435 * multiple entries with identical argument lists --- in the multiple-
436 * namespace case, we arrange for entries in earlier namespaces to mask
437 * identical entries in later namespaces.
440 FuncnameGetCandidates(List *names, int nargs)
442 FuncCandidateList resultList = NULL;
449 /* deconstruct the name list */
450 DeconstructQualifiedName(names, &schemaname, &funcname);
454 /* use exact schema given */
455 namespaceId = LookupExplicitNamespace(schemaname);
459 /* flag to indicate we need namespace search */
460 namespaceId = InvalidOid;
461 recomputeNamespacePath();
464 /* Search syscache by name and (optionally) nargs only */
466 catlist = SearchSysCacheList(PROCNAMENSP, 2,
467 CStringGetDatum(funcname),
468 Int16GetDatum(nargs),
471 catlist = SearchSysCacheList(PROCNAMENSP, 1,
472 CStringGetDatum(funcname),
475 for (i = 0; i < catlist->n_members; i++)
477 HeapTuple proctup = &catlist->members[i]->tuple;
478 Form_pg_proc procform = (Form_pg_proc) GETSTRUCT(proctup);
480 FuncCandidateList newResult;
482 nargs = procform->pronargs;
484 if (OidIsValid(namespaceId))
486 /* Consider only procs in specified namespace */
487 if (procform->pronamespace != namespaceId)
489 /* No need to check args, they must all be different */
493 /* Consider only procs that are in the search path */
496 foreach(nsp, namespaceSearchPath)
498 if (procform->pronamespace == lfirsto(nsp))
503 continue; /* proc is not in search path */
506 * Okay, it's in the search path, but does it have the same
507 * arguments as something we already accepted? If so, keep
508 * only the one that appears earlier in the search path.
510 * If we have an ordered list from SearchSysCacheList (the normal
511 * case), then any conflicting proc must immediately adjoin
512 * this one in the list, so we only need to look at the newest
513 * result item. If we have an unordered list, we have to scan
514 * the whole result list.
518 FuncCandidateList prevResult;
520 if (catlist->ordered)
522 if (nargs == resultList->nargs &&
523 memcmp(procform->proargtypes, resultList->args,
524 nargs * sizeof(Oid)) == 0)
525 prevResult = resultList;
531 for (prevResult = resultList;
533 prevResult = prevResult->next)
535 if (nargs == prevResult->nargs &&
536 memcmp(procform->proargtypes, prevResult->args,
537 nargs * sizeof(Oid)) == 0)
543 /* We have a match with a previous result */
544 Assert(pathpos != prevResult->pathpos);
545 if (pathpos > prevResult->pathpos)
546 continue; /* keep previous result */
547 /* replace previous result */
548 prevResult->pathpos = pathpos;
549 prevResult->oid = HeapTupleGetOid(proctup);
550 continue; /* args are same, of course */
556 * Okay to add it to result list
558 newResult = (FuncCandidateList)
559 palloc(sizeof(struct _FuncCandidateList) - sizeof(Oid)
560 + nargs * sizeof(Oid));
561 newResult->pathpos = pathpos;
562 newResult->oid = HeapTupleGetOid(proctup);
563 newResult->nargs = nargs;
564 memcpy(newResult->args, procform->proargtypes, nargs * sizeof(Oid));
566 newResult->next = resultList;
567 resultList = newResult;
570 ReleaseSysCacheList(catlist);
577 * Determine whether a function (identified by OID) is visible in the
578 * current search path. Visible means "would be found by searching
579 * for the unqualified function name with exact argument matches".
582 FunctionIsVisible(Oid funcid)
585 Form_pg_proc procform;
589 proctup = SearchSysCache(PROCOID,
590 ObjectIdGetDatum(funcid),
592 if (!HeapTupleIsValid(proctup))
593 elog(ERROR, "cache lookup failed for function %u", funcid);
594 procform = (Form_pg_proc) GETSTRUCT(proctup);
596 recomputeNamespacePath();
599 * Quick check: if it ain't in the path at all, it ain't visible.
600 * Items in the system namespace are surely in the path and so we
601 * needn't even do oidMember() for them.
603 pronamespace = procform->pronamespace;
604 if (pronamespace != PG_CATALOG_NAMESPACE &&
605 !oidMember(pronamespace, namespaceSearchPath))
610 * If it is in the path, it might still not be visible; it could
611 * be hidden by another proc of the same name and arguments
612 * earlier in the path. So we must do a slow check to see if this
613 * is the same proc that would be found by FuncnameGetCandidates.
615 char *proname = NameStr(procform->proname);
616 int nargs = procform->pronargs;
617 FuncCandidateList clist;
621 clist = FuncnameGetCandidates(makeList1(makeString(proname)), nargs);
623 for (; clist; clist = clist->next)
625 if (memcmp(clist->args, procform->proargtypes,
626 nargs * sizeof(Oid)) == 0)
628 /* Found the expected entry; is it the right proc? */
629 visible = (clist->oid == funcid);
635 ReleaseSysCache(proctup);
642 * OpernameGetCandidates
643 * Given a possibly-qualified operator name and operator kind,
644 * retrieve a list of the possible matches.
646 * If oprkind is '\0', we return all operators matching the given name,
647 * regardless of arguments.
649 * We search a single namespace if the operator name is qualified, else
650 * all namespaces in the search path. The return list will never contain
651 * multiple entries with identical argument lists --- in the multiple-
652 * namespace case, we arrange for entries in earlier namespaces to mask
653 * identical entries in later namespaces.
655 * The returned items always have two args[] entries --- one or the other
656 * will be InvalidOid for a prefix or postfix oprkind. nargs is 2, too.
659 OpernameGetCandidates(List *names, char oprkind)
661 FuncCandidateList resultList = NULL;
662 char *resultSpace = NULL;
670 /* deconstruct the name list */
671 DeconstructQualifiedName(names, &schemaname, &opername);
675 /* use exact schema given */
676 namespaceId = LookupExplicitNamespace(schemaname);
680 /* flag to indicate we need namespace search */
681 namespaceId = InvalidOid;
682 recomputeNamespacePath();
685 /* Search syscache by name only */
686 catlist = SearchSysCacheList(OPERNAMENSP, 1,
687 CStringGetDatum(opername),
691 * In typical scenarios, most if not all of the operators found by the
692 * catcache search will end up getting returned; and there can be quite
693 * a few, for common operator names such as '=' or '+'. To reduce the
694 * time spent in palloc, we allocate the result space as an array large
695 * enough to hold all the operators. The original coding of this routine
696 * did a separate palloc for each operator, but profiling revealed that
697 * the pallocs used an unreasonably large fraction of parsing time.
699 #define SPACE_PER_OP MAXALIGN(sizeof(struct _FuncCandidateList) + sizeof(Oid))
701 if (catlist->n_members > 0)
702 resultSpace = palloc(catlist->n_members * SPACE_PER_OP);
704 for (i = 0; i < catlist->n_members; i++)
706 HeapTuple opertup = &catlist->members[i]->tuple;
707 Form_pg_operator operform = (Form_pg_operator) GETSTRUCT(opertup);
709 FuncCandidateList newResult;
711 /* Ignore operators of wrong kind, if specific kind requested */
712 if (oprkind && operform->oprkind != oprkind)
715 if (OidIsValid(namespaceId))
717 /* Consider only opers in specified namespace */
718 if (operform->oprnamespace != namespaceId)
720 /* No need to check args, they must all be different */
724 /* Consider only opers that are in the search path */
727 foreach(nsp, namespaceSearchPath)
729 if (operform->oprnamespace == lfirsto(nsp))
734 continue; /* oper is not in search path */
737 * Okay, it's in the search path, but does it have the same
738 * arguments as something we already accepted? If so, keep
739 * only the one that appears earlier in the search path.
741 * If we have an ordered list from SearchSysCacheList (the normal
742 * case), then any conflicting oper must immediately adjoin
743 * this one in the list, so we only need to look at the newest
744 * result item. If we have an unordered list, we have to scan
745 * the whole result list.
749 FuncCandidateList prevResult;
751 if (catlist->ordered)
753 if (operform->oprleft == resultList->args[0] &&
754 operform->oprright == resultList->args[1])
755 prevResult = resultList;
761 for (prevResult = resultList;
763 prevResult = prevResult->next)
765 if (operform->oprleft == prevResult->args[0] &&
766 operform->oprright == prevResult->args[1])
772 /* We have a match with a previous result */
773 Assert(pathpos != prevResult->pathpos);
774 if (pathpos > prevResult->pathpos)
775 continue; /* keep previous result */
776 /* replace previous result */
777 prevResult->pathpos = pathpos;
778 prevResult->oid = HeapTupleGetOid(opertup);
779 continue; /* args are same, of course */
785 * Okay to add it to result list
787 newResult = (FuncCandidateList) (resultSpace + nextResult);
788 nextResult += SPACE_PER_OP;
790 newResult->pathpos = pathpos;
791 newResult->oid = HeapTupleGetOid(opertup);
792 newResult->nargs = 2;
793 newResult->args[0] = operform->oprleft;
794 newResult->args[1] = operform->oprright;
795 newResult->next = resultList;
796 resultList = newResult;
799 ReleaseSysCacheList(catlist);
806 * Determine whether an operator (identified by OID) is visible in the
807 * current search path. Visible means "would be found by searching
808 * for the unqualified operator name with exact argument matches".
811 OperatorIsVisible(Oid oprid)
814 Form_pg_operator oprform;
818 oprtup = SearchSysCache(OPEROID,
819 ObjectIdGetDatum(oprid),
821 if (!HeapTupleIsValid(oprtup))
822 elog(ERROR, "cache lookup failed for operator %u", oprid);
823 oprform = (Form_pg_operator) GETSTRUCT(oprtup);
825 recomputeNamespacePath();
828 * Quick check: if it ain't in the path at all, it ain't visible.
829 * Items in the system namespace are surely in the path and so we
830 * needn't even do oidMember() for them.
832 oprnamespace = oprform->oprnamespace;
833 if (oprnamespace != PG_CATALOG_NAMESPACE &&
834 !oidMember(oprnamespace, namespaceSearchPath))
839 * If it is in the path, it might still not be visible; it could
840 * be hidden by another operator of the same name and arguments
841 * earlier in the path. So we must do a slow check to see if this
842 * is the same operator that would be found by
843 * OpernameGetCandidates.
845 char *oprname = NameStr(oprform->oprname);
846 FuncCandidateList clist;
850 clist = OpernameGetCandidates(makeList1(makeString(oprname)),
853 for (; clist; clist = clist->next)
855 if (clist->args[0] == oprform->oprleft &&
856 clist->args[1] == oprform->oprright)
858 /* Found the expected entry; is it the right op? */
859 visible = (clist->oid == oprid);
865 ReleaseSysCache(oprtup);
872 * OpclassGetCandidates
873 * Given an index access method OID, retrieve a list of all the
874 * opclasses for that AM that are visible in the search path.
876 * NOTE: the opcname_tmp field in the returned structs should not be used
877 * by callers, because it points at syscache entries that we release at
878 * the end of this routine. If any callers needed the name information,
879 * we could pstrdup() the names ... but at present it'd be wasteful.
882 OpclassGetCandidates(Oid amid)
884 OpclassCandidateList resultList = NULL;
888 /* Search syscache by AM OID only */
889 catlist = SearchSysCacheList(CLAAMNAMENSP, 1,
890 ObjectIdGetDatum(amid),
893 recomputeNamespacePath();
895 for (i = 0; i < catlist->n_members; i++)
897 HeapTuple opctup = &catlist->members[i]->tuple;
898 Form_pg_opclass opcform = (Form_pg_opclass) GETSTRUCT(opctup);
900 OpclassCandidateList newResult;
903 /* Consider only opclasses that are in the search path */
904 foreach(nsp, namespaceSearchPath)
906 if (opcform->opcnamespace == lfirsto(nsp))
911 continue; /* opclass is not in search path */
914 * Okay, it's in the search path, but does it have the same name
915 * as something we already accepted? If so, keep only the one
916 * that appears earlier in the search path.
918 * If we have an ordered list from SearchSysCacheList (the normal
919 * case), then any conflicting opclass must immediately adjoin
920 * this one in the list, so we only need to look at the newest
921 * result item. If we have an unordered list, we have to scan the
926 OpclassCandidateList prevResult;
928 if (catlist->ordered)
930 if (strcmp(NameStr(opcform->opcname),
931 resultList->opcname_tmp) == 0)
932 prevResult = resultList;
938 for (prevResult = resultList;
940 prevResult = prevResult->next)
942 if (strcmp(NameStr(opcform->opcname),
943 prevResult->opcname_tmp) == 0)
949 /* We have a match with a previous result */
950 Assert(pathpos != prevResult->pathpos);
951 if (pathpos > prevResult->pathpos)
952 continue; /* keep previous result */
953 /* replace previous result */
954 prevResult->opcname_tmp = NameStr(opcform->opcname);
955 prevResult->pathpos = pathpos;
956 prevResult->oid = HeapTupleGetOid(opctup);
957 prevResult->opcintype = opcform->opcintype;
958 prevResult->opcdefault = opcform->opcdefault;
959 prevResult->opckeytype = opcform->opckeytype;
965 * Okay to add it to result list
967 newResult = (OpclassCandidateList)
968 palloc(sizeof(struct _OpclassCandidateList));
969 newResult->opcname_tmp = NameStr(opcform->opcname);
970 newResult->pathpos = pathpos;
971 newResult->oid = HeapTupleGetOid(opctup);
972 newResult->opcintype = opcform->opcintype;
973 newResult->opcdefault = opcform->opcdefault;
974 newResult->opckeytype = opcform->opckeytype;
975 newResult->next = resultList;
976 resultList = newResult;
979 ReleaseSysCacheList(catlist);
985 * OpclassnameGetOpcid
986 * Try to resolve an unqualified index opclass name.
987 * Returns OID if opclass found in search path, else InvalidOid.
989 * This is essentially the same as TypenameGetTypid, but we have to have
990 * an extra argument for the index AM OID.
993 OpclassnameGetOpcid(Oid amid, const char *opcname)
998 recomputeNamespacePath();
1000 foreach(lptr, namespaceSearchPath)
1002 Oid namespaceId = lfirsto(lptr);
1004 opcid = GetSysCacheOid(CLAAMNAMENSP,
1005 ObjectIdGetDatum(amid),
1006 PointerGetDatum(opcname),
1007 ObjectIdGetDatum(namespaceId),
1009 if (OidIsValid(opcid))
1013 /* Not found in path */
1019 * Determine whether an opclass (identified by OID) is visible in the
1020 * current search path. Visible means "would be found by searching
1021 * for the unqualified opclass name".
1024 OpclassIsVisible(Oid opcid)
1027 Form_pg_opclass opcform;
1031 opctup = SearchSysCache(CLAOID,
1032 ObjectIdGetDatum(opcid),
1034 if (!HeapTupleIsValid(opctup))
1035 elog(ERROR, "cache lookup failed for opclass %u", opcid);
1036 opcform = (Form_pg_opclass) GETSTRUCT(opctup);
1038 recomputeNamespacePath();
1041 * Quick check: if it ain't in the path at all, it ain't visible.
1042 * Items in the system namespace are surely in the path and so we
1043 * needn't even do oidMember() for them.
1045 opcnamespace = opcform->opcnamespace;
1046 if (opcnamespace != PG_CATALOG_NAMESPACE &&
1047 !oidMember(opcnamespace, namespaceSearchPath))
1052 * If it is in the path, it might still not be visible; it could
1053 * be hidden by another opclass of the same name earlier in the
1054 * path. So we must do a slow check to see if this opclass would
1055 * be found by OpclassnameGetOpcid.
1057 char *opcname = NameStr(opcform->opcname);
1059 visible = (OpclassnameGetOpcid(opcform->opcamid, opcname) == opcid);
1062 ReleaseSysCache(opctup);
1068 * ConversionGetConid
1069 * Try to resolve an unqualified conversion name.
1070 * Returns OID if conversion found in search path, else InvalidOid.
1072 * This is essentially the same as RelnameGetRelid.
1075 ConversionGetConid(const char *conname)
1080 recomputeNamespacePath();
1082 foreach(lptr, namespaceSearchPath)
1084 Oid namespaceId = lfirsto(lptr);
1086 conid = GetSysCacheOid(CONNAMENSP,
1087 PointerGetDatum(conname),
1088 ObjectIdGetDatum(namespaceId),
1090 if (OidIsValid(conid))
1094 /* Not found in path */
1099 * ConversionIsVisible
1100 * Determine whether a conversion (identified by OID) is visible in the
1101 * current search path. Visible means "would be found by searching
1102 * for the unqualified conversion name".
1105 ConversionIsVisible(Oid conid)
1108 Form_pg_conversion conform;
1112 contup = SearchSysCache(CONOID,
1113 ObjectIdGetDatum(conid),
1115 if (!HeapTupleIsValid(contup))
1116 elog(ERROR, "cache lookup failed for conversion %u", conid);
1117 conform = (Form_pg_conversion) GETSTRUCT(contup);
1119 recomputeNamespacePath();
1122 * Quick check: if it ain't in the path at all, it ain't visible.
1123 * Items in the system namespace are surely in the path and so we
1124 * needn't even do oidMember() for them.
1126 connamespace = conform->connamespace;
1127 if (connamespace != PG_CATALOG_NAMESPACE &&
1128 !oidMember(connamespace, namespaceSearchPath))
1133 * If it is in the path, it might still not be visible; it could
1134 * be hidden by another conversion of the same name earlier in the
1135 * path. So we must do a slow check to see if this conversion
1136 * would be found by ConversionGetConid.
1138 char *conname = NameStr(conform->conname);
1140 visible = (ConversionGetConid(conname) == conid);
1143 ReleaseSysCache(contup);
1149 * DeconstructQualifiedName
1150 * Given a possibly-qualified name expressed as a list of String nodes,
1151 * extract the schema name and object name.
1153 * *nspname_p is set to NULL if there is no explicit schema name.
1156 DeconstructQualifiedName(List *names,
1161 char *schemaname = NULL;
1162 char *objname = NULL;
1164 switch (length(names))
1167 objname = strVal(lfirst(names));
1170 schemaname = strVal(lfirst(names));
1171 objname = strVal(lsecond(names));
1174 catalogname = strVal(lfirst(names));
1175 schemaname = strVal(lsecond(names));
1176 objname = strVal(lthird(names));
1179 * We check the catalog name and then ignore it.
1181 if (strcmp(catalogname, get_database_name(MyDatabaseId)) != 0)
1183 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
1184 errmsg("cross-database references are not implemented")));
1188 (errcode(ERRCODE_SYNTAX_ERROR),
1189 errmsg("improper qualified name (too many dotted names): %s",
1190 NameListToString(names))));
1194 *nspname_p = schemaname;
1195 *objname_p = objname;
1199 * LookupExplicitNamespace
1200 * Process an explicitly-specified schema name: look up the schema
1201 * and verify we have USAGE (lookup) rights in it.
1203 * Returns the namespace OID. Raises ereport if any problem.
1206 LookupExplicitNamespace(const char *nspname)
1209 AclResult aclresult;
1211 namespaceId = GetSysCacheOid(NAMESPACENAME,
1212 CStringGetDatum(nspname),
1214 if (!OidIsValid(namespaceId))
1216 (errcode(ERRCODE_UNDEFINED_SCHEMA),
1217 errmsg("schema \"%s\" does not exist", nspname)));
1219 aclresult = pg_namespace_aclcheck(namespaceId, GetUserId(), ACL_USAGE);
1220 if (aclresult != ACLCHECK_OK)
1221 aclcheck_error(aclresult, ACL_KIND_NAMESPACE,
1228 * QualifiedNameGetCreationNamespace
1229 * Given a possibly-qualified name for an object (in List-of-Values
1230 * format), determine what namespace the object should be created in.
1231 * Also extract and return the object name (last component of list).
1233 * This is *not* used for tables. Hence, the TEMP table namespace is
1234 * never selected as the creation target.
1237 QualifiedNameGetCreationNamespace(List *names, char **objname_p)
1243 /* deconstruct the name list */
1244 DeconstructQualifiedName(names, &schemaname, &objname);
1248 /* use exact schema given */
1249 namespaceId = GetSysCacheOid(NAMESPACENAME,
1250 CStringGetDatum(schemaname),
1252 if (!OidIsValid(namespaceId))
1254 (errcode(ERRCODE_UNDEFINED_SCHEMA),
1255 errmsg("schema \"%s\" does not exist", schemaname)));
1256 /* we do not check for USAGE rights here! */
1260 /* use the default creation namespace */
1261 recomputeNamespacePath();
1262 namespaceId = defaultCreationNamespace;
1263 if (!OidIsValid(namespaceId))
1265 (errcode(ERRCODE_UNDEFINED_SCHEMA),
1266 errmsg("no schema has been selected to create in")));
1269 /* Note: callers will check for CREATE rights when appropriate */
1271 *objname_p = objname;
1276 * makeRangeVarFromNameList
1277 * Utility routine to convert a qualified-name list into RangeVar form.
1280 makeRangeVarFromNameList(List *names)
1282 RangeVar *rel = makeRangeVar(NULL, NULL);
1284 switch (length(names))
1287 rel->relname = strVal(lfirst(names));
1290 rel->schemaname = strVal(lfirst(names));
1291 rel->relname = strVal(lsecond(names));
1294 rel->catalogname = strVal(lfirst(names));
1295 rel->schemaname = strVal(lsecond(names));
1296 rel->relname = strVal(lthird(names));
1300 (errcode(ERRCODE_SYNTAX_ERROR),
1301 errmsg("improper relation name (too many dotted names): %s",
1302 NameListToString(names))));
1311 * Utility routine to convert a qualified-name list into a string.
1313 * This is used primarily to form error messages, and so we do not quote
1314 * the list elements, for the sake of legibility.
1317 NameListToString(List *names)
1319 StringInfoData string;
1322 initStringInfo(&string);
1327 appendStringInfoChar(&string, '.');
1328 appendStringInfoString(&string, strVal(lfirst(l)));
1335 * NameListToQuotedString
1336 * Utility routine to convert a qualified-name list into a string.
1338 * Same as above except that names will be double-quoted where necessary,
1339 * so the string could be re-parsed (eg, by textToQualifiedNameList).
1342 NameListToQuotedString(List *names)
1344 StringInfoData string;
1347 initStringInfo(&string);
1352 appendStringInfoChar(&string, '.');
1353 appendStringInfoString(&string, quote_identifier(strVal(lfirst(l))));
1360 * isTempNamespace - is the given namespace my temporary-table namespace?
1363 isTempNamespace(Oid namespaceId)
1365 if (OidIsValid(myTempNamespace) && myTempNamespace == namespaceId)
1371 * isOtherTempNamespace - is the given namespace some other backend's
1372 * temporary-table namespace?
1375 isOtherTempNamespace(Oid namespaceId)
1380 /* If it's my own temp namespace, say "false" */
1381 if (isTempNamespace(namespaceId))
1383 /* Else, if the namespace name starts with "pg_temp_", say "true" */
1384 nspname = get_namespace_name(namespaceId);
1386 return false; /* no such namespace? */
1387 result = (strncmp(nspname, "pg_temp_", 8) == 0);
1393 * PushSpecialNamespace - push a "special" namespace onto the front of the
1396 * This is a slightly messy hack intended only for support of CREATE SCHEMA.
1397 * Although the API is defined to allow a stack of pushed namespaces, we
1398 * presently only support one at a time.
1400 * The pushed namespace will be removed from the search path at end of
1401 * transaction, whether commit or abort.
1404 PushSpecialNamespace(Oid namespaceId)
1406 Assert(!OidIsValid(mySpecialNamespace));
1407 mySpecialNamespace = namespaceId;
1408 namespaceSearchPathValid = false;
1412 * PopSpecialNamespace - remove previously pushed special namespace.
1415 PopSpecialNamespace(Oid namespaceId)
1417 Assert(mySpecialNamespace == namespaceId);
1418 mySpecialNamespace = InvalidOid;
1419 namespaceSearchPathValid = false;
1423 * FindConversionByName - find a conversion by possibly qualified name
1426 FindConversionByName(List *name)
1429 char *conversion_name;
1434 /* deconstruct the name list */
1435 DeconstructQualifiedName(name, &schemaname, &conversion_name);
1439 /* use exact schema given */
1440 namespaceId = LookupExplicitNamespace(schemaname);
1441 return FindConversion(conversion_name, namespaceId);
1445 /* search for it in search path */
1446 recomputeNamespacePath();
1448 foreach(lptr, namespaceSearchPath)
1450 namespaceId = lfirsto(lptr);
1451 conoid = FindConversion(conversion_name, namespaceId);
1452 if (OidIsValid(conoid))
1457 /* Not found in path */
1462 * FindDefaultConversionProc - find default encoding conversion proc
1465 FindDefaultConversionProc(int4 for_encoding, int4 to_encoding)
1470 recomputeNamespacePath();
1472 foreach(lptr, namespaceSearchPath)
1474 Oid namespaceId = lfirsto(lptr);
1476 proc = FindDefaultConversion(namespaceId, for_encoding, to_encoding);
1477 if (OidIsValid(proc))
1481 /* Not found in path */
1486 * recomputeNamespacePath - recompute path derived variables if needed.
1489 recomputeNamespacePath(void)
1491 AclId userId = GetUserId();
1498 MemoryContext oldcxt;
1501 * Do nothing if path is already valid.
1503 if (namespaceSearchPathValid && namespaceUser == userId)
1506 /* Need a modifiable copy of namespace_search_path string */
1507 rawname = pstrdup(namespace_search_path);
1509 /* Parse string into list of identifiers */
1510 if (!SplitIdentifierString(rawname, ',', &namelist))
1512 /* syntax error in name list */
1513 /* this should not happen if GUC checked check_search_path */
1514 elog(ERROR, "invalid list syntax");
1518 * Convert the list of names to a list of OIDs. If any names are not
1519 * recognizable or we don't have read access, just leave them out of
1520 * the list. (We can't raise an error, since the search_path setting
1521 * has already been accepted.) Don't make duplicate entries, either.
1524 foreach(l, namelist)
1526 char *curname = (char *) lfirst(l);
1529 if (strcmp(curname, "$user") == 0)
1531 /* $user --- substitute namespace matching user name, if any */
1534 tuple = SearchSysCache(SHADOWSYSID,
1535 ObjectIdGetDatum(userId),
1537 if (HeapTupleIsValid(tuple))
1541 uname = NameStr(((Form_pg_shadow) GETSTRUCT(tuple))->usename);
1542 namespaceId = GetSysCacheOid(NAMESPACENAME,
1543 CStringGetDatum(uname),
1545 ReleaseSysCache(tuple);
1546 if (OidIsValid(namespaceId) &&
1547 !oidMember(namespaceId, oidlist) &&
1548 pg_namespace_aclcheck(namespaceId, userId,
1549 ACL_USAGE) == ACLCHECK_OK)
1550 oidlist = lappendo(oidlist, namespaceId);
1555 /* normal namespace reference */
1556 namespaceId = GetSysCacheOid(NAMESPACENAME,
1557 CStringGetDatum(curname),
1559 if (OidIsValid(namespaceId) &&
1560 !oidMember(namespaceId, oidlist) &&
1561 pg_namespace_aclcheck(namespaceId, userId,
1562 ACL_USAGE) == ACLCHECK_OK)
1563 oidlist = lappendo(oidlist, namespaceId);
1568 * Remember the first member of the explicit list.
1571 firstNS = InvalidOid;
1573 firstNS = lfirsto(oidlist);
1576 * Add any implicitly-searched namespaces to the list. Note these go
1577 * on the front, not the back; also notice that we do not check USAGE
1578 * permissions for these.
1580 if (!oidMember(PG_CATALOG_NAMESPACE, oidlist))
1581 oidlist = lconso(PG_CATALOG_NAMESPACE, oidlist);
1583 if (OidIsValid(myTempNamespace) &&
1584 !oidMember(myTempNamespace, oidlist))
1585 oidlist = lconso(myTempNamespace, oidlist);
1587 if (OidIsValid(mySpecialNamespace) &&
1588 !oidMember(mySpecialNamespace, oidlist))
1589 oidlist = lconso(mySpecialNamespace, oidlist);
1592 * Now that we've successfully built the new list of namespace OIDs,
1593 * save it in permanent storage.
1595 oldcxt = MemoryContextSwitchTo(TopMemoryContext);
1596 newpath = listCopy(oidlist);
1597 MemoryContextSwitchTo(oldcxt);
1599 /* Now safe to assign to state variable. */
1600 freeList(namespaceSearchPath);
1601 namespaceSearchPath = newpath;
1604 * Update info derived from search path.
1606 firstExplicitNamespace = firstNS;
1607 if (OidIsValid(mySpecialNamespace))
1608 defaultCreationNamespace = mySpecialNamespace;
1610 defaultCreationNamespace = firstNS;
1612 /* Mark the path valid. */
1613 namespaceSearchPathValid = true;
1614 namespaceUser = userId;
1623 * InitTempTableNamespace
1624 * Initialize temp table namespace on first use in a particular backend
1627 InitTempTableNamespace(void)
1629 char namespaceName[NAMEDATALEN];
1633 * First, do permission check to see if we are authorized to make temp
1634 * tables. We use a nonstandard error message here since
1635 * "databasename: permission denied" might be a tad cryptic.
1637 * Note we apply the check to the session user, not the currently active
1638 * userid, since we are not going to change our minds about temp table
1639 * availability during the session.
1641 if (pg_database_aclcheck(MyDatabaseId, GetSessionUserId(),
1642 ACL_CREATE_TEMP) != ACLCHECK_OK)
1644 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
1645 errmsg("permission denied to create temporary tables in database \"%s\"",
1646 get_database_name(MyDatabaseId))));
1648 snprintf(namespaceName, sizeof(namespaceName), "pg_temp_%d", MyBackendId);
1650 namespaceId = GetSysCacheOid(NAMESPACENAME,
1651 CStringGetDatum(namespaceName),
1653 if (!OidIsValid(namespaceId))
1656 * First use of this temp namespace in this database; create it.
1657 * The temp namespaces are always owned by the superuser. We
1658 * leave their permissions at default --- i.e., no access except
1659 * to superuser --- to ensure that unprivileged users can't peek
1660 * at other backends' temp tables. This works because the places
1661 * that access the temp namespace for my own backend skip
1662 * permissions checks on it.
1664 namespaceId = NamespaceCreate(namespaceName, BOOTSTRAP_USESYSID);
1665 /* Advance command counter to make namespace visible */
1666 CommandCounterIncrement();
1671 * If the namespace already exists, clean it out (in case the
1672 * former owner crashed without doing so).
1674 RemoveTempRelations(namespaceId);
1678 * Okay, we've prepared the temp namespace ... but it's not committed
1679 * yet, so all our work could be undone by transaction rollback. Set
1680 * flag for AtEOXact_Namespace to know what to do.
1682 myTempNamespace = namespaceId;
1684 firstTempTransaction = true;
1686 namespaceSearchPathValid = false; /* need to rebuild list */
1690 * End-of-transaction cleanup for namespaces.
1693 AtEOXact_Namespace(bool isCommit)
1696 * If we abort the transaction in which a temp namespace was selected,
1697 * we'll have to do any creation or cleanout work over again. So,
1698 * just forget the namespace entirely until next time. On the other
1699 * hand, if we commit then register an exit callback to clean out the
1700 * temp tables at backend shutdown. (We only want to register the
1701 * callback once per session, so this is a good place to do it.)
1703 if (firstTempTransaction)
1706 on_shmem_exit(RemoveTempRelationsCallback, 0);
1709 myTempNamespace = InvalidOid;
1710 namespaceSearchPathValid = false; /* need to rebuild list */
1712 firstTempTransaction = false;
1716 * Clean up if someone failed to do PopSpecialNamespace
1718 if (OidIsValid(mySpecialNamespace))
1720 mySpecialNamespace = InvalidOid;
1721 namespaceSearchPathValid = false; /* need to rebuild list */
1726 * Remove all relations in the specified temp namespace.
1728 * This is called at backend shutdown (if we made any temp relations).
1729 * It is also called when we begin using a pre-existing temp namespace,
1730 * in order to clean out any relations that might have been created by
1731 * a crashed backend.
1734 RemoveTempRelations(Oid tempNamespaceId)
1736 ObjectAddress object;
1739 * We want to get rid of everything in the target namespace, but not
1740 * the namespace itself (deleting it only to recreate it later would
1741 * be a waste of cycles). We do this by finding everything that has a
1742 * dependency on the namespace.
1744 object.classId = get_system_catalog_relid(NamespaceRelationName);
1745 object.objectId = tempNamespaceId;
1746 object.objectSubId = 0;
1748 deleteWhatDependsOn(&object, false);
1752 * Callback to remove temp relations at backend exit.
1755 RemoveTempRelationsCallback(int code, Datum arg)
1757 if (OidIsValid(myTempNamespace)) /* should always be true */
1759 /* Need to ensure we have a usable transaction. */
1760 AbortOutOfAnyTransaction();
1761 StartTransactionCommand();
1763 RemoveTempRelations(myTempNamespace);
1765 CommitTransactionCommand();
1771 * Routines for handling the GUC variable 'search_path'.
1774 /* assign_hook: validate new search_path, do extra actions as needed */
1776 assign_search_path(const char *newval, bool doit, bool interactive)
1782 /* Need a modifiable copy of string */
1783 rawname = pstrdup(newval);
1785 /* Parse string into list of identifiers */
1786 if (!SplitIdentifierString(rawname, ',', &namelist))
1788 /* syntax error in name list */
1795 * If we aren't inside a transaction, we cannot do database access so
1796 * cannot verify the individual names. Must accept the list on faith.
1798 if (interactive && IsTransactionState())
1801 * Verify that all the names are either valid namespace names or
1802 * "$user". We do not require $user to correspond to a valid
1803 * namespace. We do not check for USAGE rights, either; should
1806 foreach(l, namelist)
1808 char *curname = (char *) lfirst(l);
1810 if (strcmp(curname, "$user") == 0)
1812 if (!SearchSysCacheExists(NAMESPACENAME,
1813 CStringGetDatum(curname),
1816 (errcode(ERRCODE_UNDEFINED_SCHEMA),
1817 errmsg("schema \"%s\" does not exist", curname)));
1825 * We mark the path as needing recomputation, but don't do anything
1826 * until it's needed. This avoids trying to do database access during
1827 * GUC initialization.
1830 namespaceSearchPathValid = false;
1836 * InitializeSearchPath: initialize module during InitPostgres.
1838 * This is called after we are up enough to be able to do catalog lookups.
1841 InitializeSearchPath(void)
1843 if (IsBootstrapProcessingMode())
1846 * In bootstrap mode, the search path must be 'pg_catalog' so that
1847 * tables are created in the proper namespace; ignore the GUC
1850 MemoryContext oldcxt;
1852 oldcxt = MemoryContextSwitchTo(TopMemoryContext);
1853 namespaceSearchPath = makeListo1(PG_CATALOG_NAMESPACE);
1854 MemoryContextSwitchTo(oldcxt);
1855 defaultCreationNamespace = PG_CATALOG_NAMESPACE;
1856 firstExplicitNamespace = PG_CATALOG_NAMESPACE;
1857 namespaceSearchPathValid = true;
1858 namespaceUser = GetUserId();
1863 * In normal mode, arrange for a callback on any syscache
1864 * invalidation of pg_namespace rows.
1866 CacheRegisterSyscacheCallback(NAMESPACEOID,
1869 /* Force search path to be recomputed on next use */
1870 namespaceSearchPathValid = false;
1876 * Syscache inval callback function
1879 NamespaceCallback(Datum arg, Oid relid)
1881 /* Force search path to be recomputed on next use */
1882 namespaceSearchPathValid = false;
1886 * Fetch the active search path, expressed as a List of OIDs.
1888 * The returned list includes the implicitly-prepended namespaces only if
1889 * includeImplicit is true.
1891 * NB: caller must treat the list as read-only!
1894 fetch_search_path(bool includeImplicit)
1898 recomputeNamespacePath();
1900 result = namespaceSearchPath;
1901 if (!includeImplicit)
1903 while (result && lfirsto(result) != firstExplicitNamespace)
1904 result = lnext(result);
1911 * Export the FooIsVisible functions as SQL-callable functions.
1915 pg_table_is_visible(PG_FUNCTION_ARGS)
1917 Oid oid = PG_GETARG_OID(0);
1919 PG_RETURN_BOOL(RelationIsVisible(oid));
1923 pg_type_is_visible(PG_FUNCTION_ARGS)
1925 Oid oid = PG_GETARG_OID(0);
1927 PG_RETURN_BOOL(TypeIsVisible(oid));
1931 pg_function_is_visible(PG_FUNCTION_ARGS)
1933 Oid oid = PG_GETARG_OID(0);
1935 PG_RETURN_BOOL(FunctionIsVisible(oid));
1939 pg_operator_is_visible(PG_FUNCTION_ARGS)
1941 Oid oid = PG_GETARG_OID(0);
1943 PG_RETURN_BOOL(OperatorIsVisible(oid));
1947 pg_opclass_is_visible(PG_FUNCTION_ARGS)
1949 Oid oid = PG_GETARG_OID(0);
1951 PG_RETURN_BOOL(OpclassIsVisible(oid));
1955 pg_conversion_is_visible(PG_FUNCTION_ARGS)
1957 Oid oid = PG_GETARG_OID(0);
1959 PG_RETURN_BOOL(ConversionIsVisible(oid));