LCOV - code coverage report
Current view: top level - src/backend/catalog - namespace.c (source / functions) Hit Total Coverage
Test: PostgreSQL 19devel Lines: 1328 1536 86.5 %
Date: 2025-10-10 16:17:58 Functions: 101 110 91.8 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : /*-------------------------------------------------------------------------
       2             :  *
       3             :  * namespace.c
       4             :  *    code to support accessing and searching namespaces
       5             :  *
       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.
      10             :  *
      11             :  *
      12             :  * Portions Copyright (c) 1996-2025, PostgreSQL Global Development Group
      13             :  * Portions Copyright (c) 1994, Regents of the University of California
      14             :  *
      15             :  * IDENTIFICATION
      16             :  *    src/backend/catalog/namespace.c
      17             :  *
      18             :  *-------------------------------------------------------------------------
      19             :  */
      20             : #include "postgres.h"
      21             : 
      22             : #include "access/htup_details.h"
      23             : #include "access/parallel.h"
      24             : #include "access/xact.h"
      25             : #include "access/xlog.h"
      26             : #include "catalog/dependency.h"
      27             : #include "catalog/namespace.h"
      28             : #include "catalog/objectaccess.h"
      29             : #include "catalog/pg_authid.h"
      30             : #include "catalog/pg_collation.h"
      31             : #include "catalog/pg_conversion.h"
      32             : #include "catalog/pg_database.h"
      33             : #include "catalog/pg_namespace.h"
      34             : #include "catalog/pg_opclass.h"
      35             : #include "catalog/pg_operator.h"
      36             : #include "catalog/pg_opfamily.h"
      37             : #include "catalog/pg_proc.h"
      38             : #include "catalog/pg_statistic_ext.h"
      39             : #include "catalog/pg_ts_config.h"
      40             : #include "catalog/pg_ts_dict.h"
      41             : #include "catalog/pg_ts_parser.h"
      42             : #include "catalog/pg_ts_template.h"
      43             : #include "catalog/pg_type.h"
      44             : #include "common/hashfn_unstable.h"
      45             : #include "funcapi.h"
      46             : #include "mb/pg_wchar.h"
      47             : #include "miscadmin.h"
      48             : #include "nodes/makefuncs.h"
      49             : #include "storage/ipc.h"
      50             : #include "storage/lmgr.h"
      51             : #include "storage/procarray.h"
      52             : #include "utils/acl.h"
      53             : #include "utils/builtins.h"
      54             : #include "utils/catcache.h"
      55             : #include "utils/guc_hooks.h"
      56             : #include "utils/inval.h"
      57             : #include "utils/lsyscache.h"
      58             : #include "utils/memutils.h"
      59             : #include "utils/snapmgr.h"
      60             : #include "utils/syscache.h"
      61             : #include "utils/varlena.h"
      62             : 
      63             : 
      64             : /*
      65             :  * The namespace search path is a possibly-empty list of namespace OIDs.
      66             :  * In addition to the explicit list, implicitly-searched namespaces
      67             :  * may be included:
      68             :  *
      69             :  * 1. If a TEMP table namespace has been initialized in this session, it
      70             :  * is implicitly searched first.
      71             :  *
      72             :  * 2. The system catalog namespace is always searched.  If the system
      73             :  * namespace is present in the explicit path then it will be searched in
      74             :  * the specified order; otherwise it will be searched after TEMP tables and
      75             :  * *before* the explicit list.  (It might seem that the system namespace
      76             :  * should be implicitly last, but this behavior appears to be required by
      77             :  * SQL99.  Also, this provides a way to search the system namespace first
      78             :  * without thereby making it the default creation target namespace.)
      79             :  *
      80             :  * For security reasons, searches using the search path will ignore the temp
      81             :  * namespace when searching for any object type other than relations and
      82             :  * types.  (We must allow types since temp tables have rowtypes.)
      83             :  *
      84             :  * The default creation target namespace is always the first element of the
      85             :  * explicit list.  If the explicit list is empty, there is no default target.
      86             :  *
      87             :  * The textual specification of search_path can include "$user" to refer to
      88             :  * the namespace named the same as the current user, if any.  (This is just
      89             :  * ignored if there is no such namespace.)  Also, it can include "pg_temp"
      90             :  * to refer to the current backend's temp namespace.  This is usually also
      91             :  * ignorable if the temp namespace hasn't been set up, but there's a special
      92             :  * case: if "pg_temp" appears first then it should be the default creation
      93             :  * target.  We kluge this case a little bit so that the temp namespace isn't
      94             :  * set up until the first attempt to create something in it.  (The reason for
      95             :  * klugery is that we can't create the temp namespace outside a transaction,
      96             :  * but initial GUC processing of search_path happens outside a transaction.)
      97             :  * activeTempCreationPending is true if "pg_temp" appears first in the string
      98             :  * but is not reflected in activeCreationNamespace because the namespace isn't
      99             :  * set up yet.
     100             :  *
     101             :  * In bootstrap mode, the search path is set equal to "pg_catalog", so that
     102             :  * the system namespace is the only one searched or inserted into.
     103             :  * initdb is also careful to set search_path to "pg_catalog" for its
     104             :  * post-bootstrap standalone backend runs.  Otherwise the default search
     105             :  * path is determined by GUC.  The factory default path contains the PUBLIC
     106             :  * namespace (if it exists), preceded by the user's personal namespace
     107             :  * (if one exists).
     108             :  *
     109             :  * activeSearchPath is always the actually active path; it points to
     110             :  * baseSearchPath which is the list derived from namespace_search_path.
     111             :  *
     112             :  * If baseSearchPathValid is false, then baseSearchPath (and other derived
     113             :  * variables) need to be recomputed from namespace_search_path, or retrieved
     114             :  * from the search path cache if there haven't been any syscache
     115             :  * invalidations.  We mark it invalid upon an assignment to
     116             :  * namespace_search_path or receipt of a syscache invalidation event for
     117             :  * pg_namespace or pg_authid.  The recomputation is done during the next
     118             :  * lookup attempt.
     119             :  *
     120             :  * Any namespaces mentioned in namespace_search_path that are not readable
     121             :  * by the current user ID are simply left out of baseSearchPath; so
     122             :  * we have to be willing to recompute the path when current userid changes.
     123             :  * namespaceUser is the userid the path has been computed for.
     124             :  *
     125             :  * Note: all data pointed to by these List variables is in TopMemoryContext.
     126             :  *
     127             :  * activePathGeneration is incremented whenever the effective values of
     128             :  * activeSearchPath/activeCreationNamespace/activeTempCreationPending change.
     129             :  * This can be used to quickly detect whether any change has happened since
     130             :  * a previous examination of the search path state.
     131             :  */
     132             : 
     133             : /* These variables define the actually active state: */
     134             : 
     135             : static List *activeSearchPath = NIL;
     136             : 
     137             : /* default place to create stuff; if InvalidOid, no default */
     138             : static Oid  activeCreationNamespace = InvalidOid;
     139             : 
     140             : /* if true, activeCreationNamespace is wrong, it should be temp namespace */
     141             : static bool activeTempCreationPending = false;
     142             : 
     143             : /* current generation counter; make sure this is never zero */
     144             : static uint64 activePathGeneration = 1;
     145             : 
     146             : /* These variables are the values last derived from namespace_search_path: */
     147             : 
     148             : static List *baseSearchPath = NIL;
     149             : 
     150             : static Oid  baseCreationNamespace = InvalidOid;
     151             : 
     152             : static bool baseTempCreationPending = false;
     153             : 
     154             : static Oid  namespaceUser = InvalidOid;
     155             : 
     156             : /* The above four values are valid only if baseSearchPathValid */
     157             : static bool baseSearchPathValid = true;
     158             : 
     159             : /*
     160             :  * Storage for search path cache.  Clear searchPathCacheValid as a simple
     161             :  * way to invalidate *all* the cache entries, not just the active one.
     162             :  */
     163             : static bool searchPathCacheValid = false;
     164             : static MemoryContext SearchPathCacheContext = NULL;
     165             : 
     166             : typedef struct SearchPathCacheKey
     167             : {
     168             :     const char *searchPath;
     169             :     Oid         roleid;
     170             : } SearchPathCacheKey;
     171             : 
     172             : typedef struct SearchPathCacheEntry
     173             : {
     174             :     SearchPathCacheKey key;
     175             :     List       *oidlist;        /* namespace OIDs that pass ACL checks */
     176             :     List       *finalPath;      /* cached final computed search path */
     177             :     Oid         firstNS;        /* first explicitly-listed namespace */
     178             :     bool        temp_missing;
     179             :     bool        forceRecompute; /* force recompute of finalPath */
     180             : 
     181             :     /* needed for simplehash */
     182             :     char        status;
     183             : } SearchPathCacheEntry;
     184             : 
     185             : /*
     186             :  * myTempNamespace is InvalidOid until and unless a TEMP namespace is set up
     187             :  * in a particular backend session (this happens when a CREATE TEMP TABLE
     188             :  * command is first executed).  Thereafter it's the OID of the temp namespace.
     189             :  *
     190             :  * myTempToastNamespace is the OID of the namespace for my temp tables' toast
     191             :  * tables.  It is set when myTempNamespace is, and is InvalidOid before that.
     192             :  *
     193             :  * myTempNamespaceSubID shows whether we've created the TEMP namespace in the
     194             :  * current subtransaction.  The flag propagates up the subtransaction tree,
     195             :  * so the main transaction will correctly recognize the flag if all
     196             :  * intermediate subtransactions commit.  When it is InvalidSubTransactionId,
     197             :  * we either haven't made the TEMP namespace yet, or have successfully
     198             :  * committed its creation, depending on whether myTempNamespace is valid.
     199             :  */
     200             : static Oid  myTempNamespace = InvalidOid;
     201             : 
     202             : static Oid  myTempToastNamespace = InvalidOid;
     203             : 
     204             : static SubTransactionId myTempNamespaceSubID = InvalidSubTransactionId;
     205             : 
     206             : /*
     207             :  * This is the user's textual search path specification --- it's the value
     208             :  * of the GUC variable 'search_path'.
     209             :  */
     210             : char       *namespace_search_path = NULL;
     211             : 
     212             : 
     213             : /* Local functions */
     214             : static bool RelationIsVisibleExt(Oid relid, bool *is_missing);
     215             : static bool TypeIsVisibleExt(Oid typid, bool *is_missing);
     216             : static bool FunctionIsVisibleExt(Oid funcid, bool *is_missing);
     217             : static bool OperatorIsVisibleExt(Oid oprid, bool *is_missing);
     218             : static bool OpclassIsVisibleExt(Oid opcid, bool *is_missing);
     219             : static bool OpfamilyIsVisibleExt(Oid opfid, bool *is_missing);
     220             : static bool CollationIsVisibleExt(Oid collid, bool *is_missing);
     221             : static bool ConversionIsVisibleExt(Oid conid, bool *is_missing);
     222             : static bool StatisticsObjIsVisibleExt(Oid stxid, bool *is_missing);
     223             : static bool TSParserIsVisibleExt(Oid prsId, bool *is_missing);
     224             : static bool TSDictionaryIsVisibleExt(Oid dictId, bool *is_missing);
     225             : static bool TSTemplateIsVisibleExt(Oid tmplId, bool *is_missing);
     226             : static bool TSConfigIsVisibleExt(Oid cfgid, bool *is_missing);
     227             : static void recomputeNamespacePath(void);
     228             : static void AccessTempTableNamespace(bool force);
     229             : static void InitTempTableNamespace(void);
     230             : static void RemoveTempRelations(Oid tempNamespaceId);
     231             : static void RemoveTempRelationsCallback(int code, Datum arg);
     232             : static void InvalidationCallback(Datum arg, int cacheid, uint32 hashvalue);
     233             : static bool MatchNamedCall(HeapTuple proctup, int nargs, List *argnames,
     234             :                            bool include_out_arguments, int pronargs,
     235             :                            int **argnumbers, int *fgc_flags);
     236             : 
     237             : /*
     238             :  * Recomputing the namespace path can be costly when done frequently, such as
     239             :  * when a function has search_path set in proconfig. Add a search path cache
     240             :  * that can be used by recomputeNamespacePath().
     241             :  *
     242             :  * The cache is also used to remember already-validated strings in
     243             :  * check_search_path() to avoid the need to call SplitIdentifierString()
     244             :  * repeatedly.
     245             :  *
     246             :  * The search path cache is based on a wrapper around a simplehash hash table
     247             :  * (nsphash, defined below). The spcache wrapper deals with OOM while trying
     248             :  * to initialize a key, optimizes repeated lookups of the same key, and also
     249             :  * offers a more convenient API.
     250             :  */
     251             : 
     252             : static inline uint32
     253      162160 : spcachekey_hash(SearchPathCacheKey key)
     254             : {
     255             :     fasthash_state hs;
     256             :     int         sp_len;
     257             : 
     258      162160 :     fasthash_init(&hs, 0);
     259             : 
     260      162160 :     hs.accum = key.roleid;
     261      162160 :     fasthash_combine(&hs);
     262             : 
     263             :     /*
     264             :      * Combine search path into the hash and save the length for tweaking the
     265             :      * final mix.
     266             :      */
     267      162160 :     sp_len = fasthash_accum_cstring(&hs, key.searchPath);
     268             : 
     269      162160 :     return fasthash_final32(&hs, sp_len);
     270             : }
     271             : 
     272             : static inline bool
     273       62702 : spcachekey_equal(SearchPathCacheKey a, SearchPathCacheKey b)
     274             : {
     275      124960 :     return a.roleid == b.roleid &&
     276       62258 :         strcmp(a.searchPath, b.searchPath) == 0;
     277             : }
     278             : 
     279             : #define SH_PREFIX       nsphash
     280             : #define SH_ELEMENT_TYPE SearchPathCacheEntry
     281             : #define SH_KEY_TYPE     SearchPathCacheKey
     282             : #define SH_KEY          key
     283             : #define SH_HASH_KEY(tb, key)    spcachekey_hash(key)
     284             : #define SH_EQUAL(tb, a, b)      spcachekey_equal(a, b)
     285             : #define SH_SCOPE        static inline
     286             : #define SH_DECLARE
     287             : #define SH_DEFINE
     288             : #include "lib/simplehash.h"
     289             : 
     290             : /*
     291             :  * We only expect a small number of unique search_path strings to be used. If
     292             :  * this cache grows to an unreasonable size, reset it to avoid steady-state
     293             :  * memory growth. Most likely, only a few of those entries will benefit from
     294             :  * the cache, and the cache will be quickly repopulated with such entries.
     295             :  */
     296             : #define SPCACHE_RESET_THRESHOLD     256
     297             : 
     298             : static nsphash_hash *SearchPathCache = NULL;
     299             : static SearchPathCacheEntry *LastSearchPathCacheEntry = NULL;
     300             : 
     301             : /*
     302             :  * Create or reset search_path cache as necessary.
     303             :  */
     304             : static void
     305      225294 : spcache_init(void)
     306             : {
     307      225294 :     if (SearchPathCache && searchPathCacheValid &&
     308      188558 :         SearchPathCache->members < SPCACHE_RESET_THRESHOLD)
     309      188558 :         return;
     310             : 
     311       36736 :     searchPathCacheValid = false;
     312       36736 :     baseSearchPathValid = false;
     313             : 
     314             :     /*
     315             :      * Make sure we don't leave dangling pointers if a failure happens during
     316             :      * initialization.
     317             :      */
     318       36736 :     SearchPathCache = NULL;
     319       36736 :     LastSearchPathCacheEntry = NULL;
     320             : 
     321       36736 :     if (SearchPathCacheContext == NULL)
     322             :     {
     323             :         /* Make the context we'll keep search path cache hashtable in */
     324       20482 :         SearchPathCacheContext = AllocSetContextCreate(TopMemoryContext,
     325             :                                                        "search_path processing cache",
     326             :                                                        ALLOCSET_DEFAULT_SIZES);
     327             :     }
     328             :     else
     329             :     {
     330       16254 :         MemoryContextReset(SearchPathCacheContext);
     331             :     }
     332             : 
     333             :     /* arbitrary initial starting size of 16 elements */
     334       36736 :     SearchPathCache = nsphash_create(SearchPathCacheContext, 16, NULL);
     335       36736 :     searchPathCacheValid = true;
     336             : }
     337             : 
     338             : /*
     339             :  * Look up entry in search path cache without inserting. Returns NULL if not
     340             :  * present.
     341             :  */
     342             : static SearchPathCacheEntry *
     343      152032 : spcache_lookup(const char *searchPath, Oid roleid)
     344             : {
     345      152032 :     if (LastSearchPathCacheEntry &&
     346      149620 :         LastSearchPathCacheEntry->key.roleid == roleid &&
     347      149414 :         strcmp(LastSearchPathCacheEntry->key.searchPath, searchPath) == 0)
     348             :     {
     349      114872 :         return LastSearchPathCacheEntry;
     350             :     }
     351             :     else
     352             :     {
     353             :         SearchPathCacheEntry *entry;
     354       37160 :         SearchPathCacheKey cachekey = {
     355             :             .searchPath = searchPath,
     356             :             .roleid = roleid
     357             :         };
     358             : 
     359       37160 :         entry = nsphash_lookup(SearchPathCache, cachekey);
     360       37160 :         if (entry)
     361       27972 :             LastSearchPathCacheEntry = entry;
     362       37160 :         return entry;
     363             :     }
     364             : }
     365             : 
     366             : /*
     367             :  * Look up or insert entry in search path cache.
     368             :  *
     369             :  * Initialize key safely, so that OOM does not leave an entry without a valid
     370             :  * key. Caller must ensure that non-key contents are properly initialized.
     371             :  */
     372             : static SearchPathCacheEntry *
     373       82450 : spcache_insert(const char *searchPath, Oid roleid)
     374             : {
     375       82450 :     if (LastSearchPathCacheEntry &&
     376       45714 :         LastSearchPathCacheEntry->key.roleid == roleid &&
     377       42212 :         strcmp(LastSearchPathCacheEntry->key.searchPath, searchPath) == 0)
     378             :     {
     379        3228 :         return LastSearchPathCacheEntry;
     380             :     }
     381             :     else
     382             :     {
     383             :         SearchPathCacheEntry *entry;
     384       79222 :         SearchPathCacheKey cachekey = {
     385             :             .searchPath = searchPath,
     386             :             .roleid = roleid
     387             :         };
     388             : 
     389             :         /*
     390             :          * searchPath is not saved in SearchPathCacheContext. First perform a
     391             :          * lookup, and copy searchPath only if we need to create a new entry.
     392             :          */
     393       79222 :         entry = nsphash_lookup(SearchPathCache, cachekey);
     394             : 
     395       79222 :         if (!entry)
     396             :         {
     397             :             bool        found;
     398             : 
     399       45594 :             cachekey.searchPath = MemoryContextStrdup(SearchPathCacheContext, searchPath);
     400       45594 :             entry = nsphash_insert(SearchPathCache, cachekey, &found);
     401             :             Assert(!found);
     402             : 
     403       45594 :             entry->oidlist = NIL;
     404       45594 :             entry->finalPath = NIL;
     405       45594 :             entry->firstNS = InvalidOid;
     406       45594 :             entry->temp_missing = false;
     407       45594 :             entry->forceRecompute = false;
     408             :             /* do not touch entry->status, used by simplehash */
     409             :         }
     410             : 
     411       79222 :         LastSearchPathCacheEntry = entry;
     412       79222 :         return entry;
     413             :     }
     414             : }
     415             : 
     416             : /*
     417             :  * RangeVarGetRelidExtended
     418             :  *      Given a RangeVar describing an existing relation,
     419             :  *      select the proper namespace and look up the relation OID.
     420             :  *
     421             :  * If the schema or relation is not found, return InvalidOid if flags contains
     422             :  * RVR_MISSING_OK, otherwise raise an error.
     423             :  *
     424             :  * If flags contains RVR_NOWAIT, throw an error if we'd have to wait for a
     425             :  * lock.
     426             :  *
     427             :  * If flags contains RVR_SKIP_LOCKED, return InvalidOid if we'd have to wait
     428             :  * for a lock.
     429             :  *
     430             :  * flags cannot contain both RVR_NOWAIT and RVR_SKIP_LOCKED.
     431             :  *
     432             :  * Note that if RVR_MISSING_OK and RVR_SKIP_LOCKED are both specified, a
     433             :  * return value of InvalidOid could either mean the relation is missing or it
     434             :  * could not be locked.
     435             :  *
     436             :  * Callback allows caller to check permissions or acquire additional locks
     437             :  * prior to grabbing the relation lock.
     438             :  */
     439             : Oid
     440      700830 : RangeVarGetRelidExtended(const RangeVar *relation, LOCKMODE lockmode,
     441             :                          uint32 flags,
     442             :                          RangeVarGetRelidCallback callback, void *callback_arg)
     443             : {
     444             :     uint64      inval_count;
     445             :     Oid         relId;
     446      700830 :     Oid         oldRelId = InvalidOid;
     447      700830 :     bool        retry = false;
     448      700830 :     bool        missing_ok = (flags & RVR_MISSING_OK) != 0;
     449             : 
     450             :     /* verify that flags do no conflict */
     451             :     Assert(!((flags & RVR_NOWAIT) && (flags & RVR_SKIP_LOCKED)));
     452             : 
     453             :     /*
     454             :      * We check the catalog name and then ignore it.
     455             :      */
     456      700830 :     if (relation->catalogname)
     457             :     {
     458          80 :         if (strcmp(relation->catalogname, get_database_name(MyDatabaseId)) != 0)
     459          80 :             ereport(ERROR,
     460             :                     (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
     461             :                      errmsg("cross-database references are not implemented: \"%s.%s.%s\"",
     462             :                             relation->catalogname, relation->schemaname,
     463             :                             relation->relname)));
     464             :     }
     465             : 
     466             :     /*
     467             :      * DDL operations can change the results of a name lookup.  Since all such
     468             :      * operations will generate invalidation messages, we keep track of
     469             :      * whether any such messages show up while we're performing the operation,
     470             :      * and retry until either (1) no more invalidation messages show up or (2)
     471             :      * the answer doesn't change.
     472             :      *
     473             :      * But if lockmode = NoLock, then we assume that either the caller is OK
     474             :      * with the answer changing under them, or that they already hold some
     475             :      * appropriate lock, and therefore return the first answer we get without
     476             :      * checking for invalidation messages.  Also, if the requested lock is
     477             :      * already held, LockRelationOid will not AcceptInvalidationMessages, so
     478             :      * we may fail to notice a change.  We could protect against that case by
     479             :      * calling AcceptInvalidationMessages() before beginning this loop, but
     480             :      * that would add a significant amount overhead, so for now we don't.
     481             :      */
     482             :     for (;;)
     483             :     {
     484             :         /*
     485             :          * Remember this value, so that, after looking up the relation name
     486             :          * and locking its OID, we can check whether any invalidation messages
     487             :          * have been processed that might require a do-over.
     488             :          */
     489      705200 :         inval_count = SharedInvalidMessageCounter;
     490             : 
     491             :         /*
     492             :          * Some non-default relpersistence value may have been specified.  The
     493             :          * parser never generates such a RangeVar in simple DML, but it can
     494             :          * happen in contexts such as "CREATE TEMP TABLE foo (f1 int PRIMARY
     495             :          * KEY)".  Such a command will generate an added CREATE INDEX
     496             :          * operation, which must be careful to find the temp table, even when
     497             :          * pg_temp is not first in the search path.
     498             :          */
     499      705200 :         if (relation->relpersistence == RELPERSISTENCE_TEMP)
     500             :         {
     501         672 :             if (!OidIsValid(myTempNamespace))
     502           0 :                 relId = InvalidOid; /* this probably can't happen? */
     503             :             else
     504             :             {
     505         672 :                 if (relation->schemaname)
     506             :                 {
     507             :                     Oid         namespaceId;
     508             : 
     509          12 :                     namespaceId = LookupExplicitNamespace(relation->schemaname, missing_ok);
     510             : 
     511             :                     /*
     512             :                      * For missing_ok, allow a non-existent schema name to
     513             :                      * return InvalidOid.
     514             :                      */
     515          12 :                     if (namespaceId != myTempNamespace)
     516           0 :                         ereport(ERROR,
     517             :                                 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
     518             :                                  errmsg("temporary tables cannot specify a schema name")));
     519             :                 }
     520             : 
     521         672 :                 relId = get_relname_relid(relation->relname, myTempNamespace);
     522             :             }
     523             :         }
     524      704528 :         else if (relation->schemaname)
     525             :         {
     526             :             Oid         namespaceId;
     527             : 
     528             :             /* use exact schema given */
     529      273564 :             namespaceId = LookupExplicitNamespace(relation->schemaname, missing_ok);
     530      273456 :             if (missing_ok && !OidIsValid(namespaceId))
     531          84 :                 relId = InvalidOid;
     532             :             else
     533      273372 :                 relId = get_relname_relid(relation->relname, namespaceId);
     534             :         }
     535             :         else
     536             :         {
     537             :             /* search the namespace path */
     538      430964 :             relId = RelnameGetRelid(relation->relname);
     539             :         }
     540             : 
     541             :         /*
     542             :          * Invoke caller-supplied callback, if any.
     543             :          *
     544             :          * This callback is a good place to check permissions: we haven't
     545             :          * taken the table lock yet (and it's really best to check permissions
     546             :          * before locking anything!), but we've gotten far enough to know what
     547             :          * OID we think we should lock.  Of course, concurrent DDL might
     548             :          * change things while we're waiting for the lock, but in that case
     549             :          * the callback will be invoked again for the new OID.
     550             :          */
     551      705092 :         if (callback)
     552       89570 :             callback(relation, relId, oldRelId, callback_arg);
     553             : 
     554             :         /*
     555             :          * If no lock requested, we assume the caller knows what they're
     556             :          * doing.  They should have already acquired a heavyweight lock on
     557             :          * this relation earlier in the processing of this same statement, so
     558             :          * it wouldn't be appropriate to AcceptInvalidationMessages() here, as
     559             :          * that might pull the rug out from under them.
     560             :          */
     561      704756 :         if (lockmode == NoLock)
     562       56096 :             break;
     563             : 
     564             :         /*
     565             :          * If, upon retry, we get back the same OID we did last time, then the
     566             :          * invalidation messages we processed did not change the final answer.
     567             :          * So we're done.
     568             :          *
     569             :          * If we got a different OID, we've locked the relation that used to
     570             :          * have this name rather than the one that does now.  So release the
     571             :          * lock.
     572             :          */
     573      648660 :         if (retry)
     574             :         {
     575        4450 :             if (relId == oldRelId)
     576        4438 :                 break;
     577          12 :             if (OidIsValid(oldRelId))
     578          12 :                 UnlockRelationOid(oldRelId, lockmode);
     579             :         }
     580             : 
     581             :         /*
     582             :          * Lock relation.  This will also accept any pending invalidation
     583             :          * messages.  If we got back InvalidOid, indicating not found, then
     584             :          * there's nothing to lock, but we accept invalidation messages
     585             :          * anyway, to flush any negative catcache entries that may be
     586             :          * lingering.
     587             :          */
     588      644222 :         if (!OidIsValid(relId))
     589        1980 :             AcceptInvalidationMessages();
     590      642242 :         else if (!(flags & (RVR_NOWAIT | RVR_SKIP_LOCKED)))
     591      641570 :             LockRelationOid(relId, lockmode);
     592         672 :         else if (!ConditionalLockRelationOid(relId, lockmode))
     593             :         {
     594          16 :             int         elevel = (flags & RVR_SKIP_LOCKED) ? DEBUG1 : ERROR;
     595             : 
     596          16 :             if (relation->schemaname)
     597           0 :                 ereport(elevel,
     598             :                         (errcode(ERRCODE_LOCK_NOT_AVAILABLE),
     599             :                          errmsg("could not obtain lock on relation \"%s.%s\"",
     600             :                                 relation->schemaname, relation->relname)));
     601             :             else
     602          16 :                 ereport(elevel,
     603             :                         (errcode(ERRCODE_LOCK_NOT_AVAILABLE),
     604             :                          errmsg("could not obtain lock on relation \"%s\"",
     605             :                                 relation->relname)));
     606             : 
     607           8 :             return InvalidOid;
     608             :         }
     609             : 
     610             :         /*
     611             :          * If no invalidation message were processed, we're done!
     612             :          */
     613      644190 :         if (inval_count == SharedInvalidMessageCounter)
     614      639740 :             break;
     615             : 
     616             :         /*
     617             :          * Something may have changed.  Let's repeat the name lookup, to make
     618             :          * sure this name still references the same relation it did
     619             :          * previously.
     620             :          */
     621        4450 :         retry = true;
     622        4450 :         oldRelId = relId;
     623             :     }
     624             : 
     625      700274 :     if (!OidIsValid(relId))
     626             :     {
     627        2068 :         int         elevel = missing_ok ? DEBUG1 : ERROR;
     628             : 
     629        2068 :         if (relation->schemaname)
     630         274 :             ereport(elevel,
     631             :                     (errcode(ERRCODE_UNDEFINED_TABLE),
     632             :                      errmsg("relation \"%s.%s\" does not exist",
     633             :                             relation->schemaname, relation->relname)));
     634             :         else
     635        1794 :             ereport(elevel,
     636             :                     (errcode(ERRCODE_UNDEFINED_TABLE),
     637             :                      errmsg("relation \"%s\" does not exist",
     638             :                             relation->relname)));
     639             :     }
     640      699818 :     return relId;
     641             : }
     642             : 
     643             : /*
     644             :  * RangeVarGetCreationNamespace
     645             :  *      Given a RangeVar describing a to-be-created relation,
     646             :  *      choose which namespace to create it in.
     647             :  *
     648             :  * Note: calling this may result in a CommandCounterIncrement operation.
     649             :  * That will happen on the first request for a temp table in any particular
     650             :  * backend run; we will need to either create or clean out the temp schema.
     651             :  */
     652             : Oid
     653      128122 : RangeVarGetCreationNamespace(const RangeVar *newRelation)
     654             : {
     655             :     Oid         namespaceId;
     656             : 
     657             :     /*
     658             :      * We check the catalog name and then ignore it.
     659             :      */
     660      128122 :     if (newRelation->catalogname)
     661             :     {
     662           0 :         if (strcmp(newRelation->catalogname, get_database_name(MyDatabaseId)) != 0)
     663           0 :             ereport(ERROR,
     664             :                     (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
     665             :                      errmsg("cross-database references are not implemented: \"%s.%s.%s\"",
     666             :                             newRelation->catalogname, newRelation->schemaname,
     667             :                             newRelation->relname)));
     668             :     }
     669             : 
     670      128122 :     if (newRelation->schemaname)
     671             :     {
     672             :         /* check for pg_temp alias */
     673       42902 :         if (strcmp(newRelation->schemaname, "pg_temp") == 0)
     674             :         {
     675             :             /* Initialize temp namespace */
     676          68 :             AccessTempTableNamespace(false);
     677          68 :             return myTempNamespace;
     678             :         }
     679             :         /* use exact schema given */
     680       42834 :         namespaceId = get_namespace_oid(newRelation->schemaname, false);
     681             :         /* we do not check for USAGE rights here! */
     682             :     }
     683       85220 :     else if (newRelation->relpersistence == RELPERSISTENCE_TEMP)
     684             :     {
     685             :         /* Initialize temp namespace */
     686        6462 :         AccessTempTableNamespace(false);
     687        6462 :         return myTempNamespace;
     688             :     }
     689             :     else
     690             :     {
     691             :         /* use the default creation namespace */
     692       78758 :         recomputeNamespacePath();
     693       78758 :         if (activeTempCreationPending)
     694             :         {
     695             :             /* Need to initialize temp namespace */
     696           0 :             AccessTempTableNamespace(true);
     697           0 :             return myTempNamespace;
     698             :         }
     699       78758 :         namespaceId = activeCreationNamespace;
     700       78758 :         if (!OidIsValid(namespaceId))
     701           0 :             ereport(ERROR,
     702             :                     (errcode(ERRCODE_UNDEFINED_SCHEMA),
     703             :                      errmsg("no schema has been selected to create in")));
     704             :     }
     705             : 
     706             :     /* Note: callers will check for CREATE rights when appropriate */
     707             : 
     708      121592 :     return namespaceId;
     709             : }
     710             : 
     711             : /*
     712             :  * RangeVarGetAndCheckCreationNamespace
     713             :  *
     714             :  * This function returns the OID of the namespace in which a new relation
     715             :  * with a given name should be created.  If the user does not have CREATE
     716             :  * permission on the target namespace, this function will instead signal
     717             :  * an ERROR.
     718             :  *
     719             :  * If non-NULL, *existing_relation_id is set to the OID of any existing relation
     720             :  * with the same name which already exists in that namespace, or to InvalidOid
     721             :  * if no such relation exists.
     722             :  *
     723             :  * If lockmode != NoLock, the specified lock mode is acquired on the existing
     724             :  * relation, if any, provided that the current user owns the target relation.
     725             :  * However, if lockmode != NoLock and the user does not own the target
     726             :  * relation, we throw an ERROR, as we must not try to lock relations the
     727             :  * user does not have permissions on.
     728             :  *
     729             :  * As a side effect, this function acquires AccessShareLock on the target
     730             :  * namespace.  Without this, the namespace could be dropped before our
     731             :  * transaction commits, leaving behind relations with relnamespace pointing
     732             :  * to a no-longer-existent namespace.
     733             :  *
     734             :  * As a further side-effect, if the selected namespace is a temporary namespace,
     735             :  * we mark the RangeVar as RELPERSISTENCE_TEMP.
     736             :  */
     737             : Oid
     738      123212 : RangeVarGetAndCheckCreationNamespace(RangeVar *relation,
     739             :                                      LOCKMODE lockmode,
     740             :                                      Oid *existing_relation_id)
     741             : {
     742             :     uint64      inval_count;
     743             :     Oid         relid;
     744      123212 :     Oid         oldrelid = InvalidOid;
     745             :     Oid         nspid;
     746      123212 :     Oid         oldnspid = InvalidOid;
     747      123212 :     bool        retry = false;
     748             : 
     749             :     /*
     750             :      * We check the catalog name and then ignore it.
     751             :      */
     752      123212 :     if (relation->catalogname)
     753             :     {
     754           0 :         if (strcmp(relation->catalogname, get_database_name(MyDatabaseId)) != 0)
     755           0 :             ereport(ERROR,
     756             :                     (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
     757             :                      errmsg("cross-database references are not implemented: \"%s.%s.%s\"",
     758             :                             relation->catalogname, relation->schemaname,
     759             :                             relation->relname)));
     760             :     }
     761             : 
     762             :     /*
     763             :      * As in RangeVarGetRelidExtended(), we guard against concurrent DDL
     764             :      * operations by tracking whether any invalidation messages are processed
     765             :      * while we're doing the name lookups and acquiring locks.  See comments
     766             :      * in that function for a more detailed explanation of this logic.
     767             :      */
     768             :     for (;;)
     769        1868 :     {
     770             :         AclResult   aclresult;
     771             : 
     772      125080 :         inval_count = SharedInvalidMessageCounter;
     773             : 
     774             :         /* Look up creation namespace and check for existing relation. */
     775      125080 :         nspid = RangeVarGetCreationNamespace(relation);
     776             :         Assert(OidIsValid(nspid));
     777      125080 :         if (existing_relation_id != NULL)
     778       57102 :             relid = get_relname_relid(relation->relname, nspid);
     779             :         else
     780       67978 :             relid = InvalidOid;
     781             : 
     782             :         /*
     783             :          * In bootstrap processing mode, we don't bother with permissions or
     784             :          * locking.  Permissions might not be working yet, and locking is
     785             :          * unnecessary.
     786             :          */
     787      125080 :         if (IsBootstrapProcessingMode())
     788           0 :             break;
     789             : 
     790             :         /* Check namespace permissions. */
     791      125080 :         aclresult = object_aclcheck(NamespaceRelationId, nspid, GetUserId(), ACL_CREATE);
     792      125080 :         if (aclresult != ACLCHECK_OK)
     793           0 :             aclcheck_error(aclresult, OBJECT_SCHEMA,
     794           0 :                            get_namespace_name(nspid));
     795             : 
     796      125080 :         if (retry)
     797             :         {
     798             :             /* If nothing changed, we're done. */
     799        1868 :             if (relid == oldrelid && nspid == oldnspid)
     800        1868 :                 break;
     801             :             /* If creation namespace has changed, give up old lock. */
     802           0 :             if (nspid != oldnspid)
     803           0 :                 UnlockDatabaseObject(NamespaceRelationId, oldnspid, 0,
     804             :                                      AccessShareLock);
     805             :             /* If name points to something different, give up old lock. */
     806           0 :             if (relid != oldrelid && OidIsValid(oldrelid) && lockmode != NoLock)
     807           0 :                 UnlockRelationOid(oldrelid, lockmode);
     808             :         }
     809             : 
     810             :         /* Lock namespace. */
     811      123212 :         if (nspid != oldnspid)
     812      123212 :             LockDatabaseObject(NamespaceRelationId, nspid, 0, AccessShareLock);
     813             : 
     814             :         /* Lock relation, if required if and we have permission. */
     815      123212 :         if (lockmode != NoLock && OidIsValid(relid))
     816             :         {
     817         224 :             if (!object_ownercheck(RelationRelationId, relid, GetUserId()))
     818           0 :                 aclcheck_error(ACLCHECK_NOT_OWNER, get_relkind_objtype(get_rel_relkind(relid)),
     819           0 :                                relation->relname);
     820         224 :             if (relid != oldrelid)
     821         224 :                 LockRelationOid(relid, lockmode);
     822             :         }
     823             : 
     824             :         /* If no invalidation message were processed, we're done! */
     825      123212 :         if (inval_count == SharedInvalidMessageCounter)
     826      121344 :             break;
     827             : 
     828             :         /* Something may have changed, so recheck our work. */
     829        1868 :         retry = true;
     830        1868 :         oldrelid = relid;
     831        1868 :         oldnspid = nspid;
     832             :     }
     833             : 
     834      123212 :     RangeVarAdjustRelationPersistence(relation, nspid);
     835      123188 :     if (existing_relation_id != NULL)
     836       55564 :         *existing_relation_id = relid;
     837      123188 :     return nspid;
     838             : }
     839             : 
     840             : /*
     841             :  * Adjust the relpersistence for an about-to-be-created relation based on the
     842             :  * creation namespace, and throw an error for invalid combinations.
     843             :  */
     844             : void
     845      128788 : RangeVarAdjustRelationPersistence(RangeVar *newRelation, Oid nspid)
     846             : {
     847      128788 :     switch (newRelation->relpersistence)
     848             :     {
     849        6134 :         case RELPERSISTENCE_TEMP:
     850        6134 :             if (!isTempOrTempToastNamespace(nspid))
     851             :             {
     852          18 :                 if (isAnyTempNamespace(nspid))
     853           0 :                     ereport(ERROR,
     854             :                             (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
     855             :                              errmsg("cannot create relations in temporary schemas of other sessions")));
     856             :                 else
     857          18 :                     ereport(ERROR,
     858             :                             (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
     859             :                              errmsg("cannot create temporary relation in non-temporary schema")));
     860             :             }
     861        6116 :             break;
     862      122276 :         case RELPERSISTENCE_PERMANENT:
     863      122276 :             if (isTempOrTempToastNamespace(nspid))
     864          26 :                 newRelation->relpersistence = RELPERSISTENCE_TEMP;
     865      122250 :             else if (isAnyTempNamespace(nspid))
     866           0 :                 ereport(ERROR,
     867             :                         (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
     868             :                          errmsg("cannot create relations in temporary schemas of other sessions")));
     869      122276 :             break;
     870         378 :         default:
     871         378 :             if (isAnyTempNamespace(nspid))
     872           6 :                 ereport(ERROR,
     873             :                         (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
     874             :                          errmsg("only temporary relations may be created in temporary schemas")));
     875             :     }
     876      128764 : }
     877             : 
     878             : /*
     879             :  * RelnameGetRelid
     880             :  *      Try to resolve an unqualified relation name.
     881             :  *      Returns OID if relation found in search path, else InvalidOid.
     882             :  */
     883             : Oid
     884      431024 : RelnameGetRelid(const char *relname)
     885             : {
     886             :     Oid         relid;
     887             :     ListCell   *l;
     888             : 
     889      431024 :     recomputeNamespacePath();
     890             : 
     891      772936 :     foreach(l, activeSearchPath)
     892             :     {
     893      771112 :         Oid         namespaceId = lfirst_oid(l);
     894             : 
     895      771112 :         relid = get_relname_relid(relname, namespaceId);
     896      771112 :         if (OidIsValid(relid))
     897      429200 :             return relid;
     898             :     }
     899             : 
     900             :     /* Not found in path */
     901        1824 :     return InvalidOid;
     902             : }
     903             : 
     904             : 
     905             : /*
     906             :  * RelationIsVisible
     907             :  *      Determine whether a relation (identified by OID) is visible in the
     908             :  *      current search path.  Visible means "would be found by searching
     909             :  *      for the unqualified relation name".
     910             :  */
     911             : bool
     912      309854 : RelationIsVisible(Oid relid)
     913             : {
     914      309854 :     return RelationIsVisibleExt(relid, NULL);
     915             : }
     916             : 
     917             : /*
     918             :  * RelationIsVisibleExt
     919             :  *      As above, but if the relation isn't found and is_missing is not NULL,
     920             :  *      then set *is_missing = true and return false instead of throwing
     921             :  *      an error.  (Caller must initialize *is_missing = false.)
     922             :  */
     923             : static bool
     924      333548 : RelationIsVisibleExt(Oid relid, bool *is_missing)
     925             : {
     926             :     HeapTuple   reltup;
     927             :     Form_pg_class relform;
     928             :     Oid         relnamespace;
     929             :     bool        visible;
     930             : 
     931      333548 :     reltup = SearchSysCache1(RELOID, ObjectIdGetDatum(relid));
     932      333548 :     if (!HeapTupleIsValid(reltup))
     933             :     {
     934          10 :         if (is_missing != NULL)
     935             :         {
     936          10 :             *is_missing = true;
     937          10 :             return false;
     938             :         }
     939           0 :         elog(ERROR, "cache lookup failed for relation %u", relid);
     940             :     }
     941      333538 :     relform = (Form_pg_class) GETSTRUCT(reltup);
     942             : 
     943      333538 :     recomputeNamespacePath();
     944             : 
     945             :     /*
     946             :      * Quick check: if it ain't in the path at all, it ain't visible. Items in
     947             :      * the system namespace are surely in the path and so we needn't even do
     948             :      * list_member_oid() for them.
     949             :      */
     950      333538 :     relnamespace = relform->relnamespace;
     951      333538 :     if (relnamespace != PG_CATALOG_NAMESPACE &&
     952      276280 :         !list_member_oid(activeSearchPath, relnamespace))
     953      117758 :         visible = false;
     954             :     else
     955             :     {
     956             :         /*
     957             :          * If it is in the path, it might still not be visible; it could be
     958             :          * hidden by another relation of the same name earlier in the path. So
     959             :          * we must do a slow check for conflicting relations.
     960             :          */
     961      215780 :         char       *relname = NameStr(relform->relname);
     962             :         ListCell   *l;
     963             : 
     964      215780 :         visible = false;
     965      525700 :         foreach(l, activeSearchPath)
     966             :         {
     967      525700 :             Oid         namespaceId = lfirst_oid(l);
     968             : 
     969      525700 :             if (namespaceId == relnamespace)
     970             :             {
     971             :                 /* Found it first in path */
     972      215714 :                 visible = true;
     973      215714 :                 break;
     974             :             }
     975      309986 :             if (OidIsValid(get_relname_relid(relname, namespaceId)))
     976             :             {
     977             :                 /* Found something else first in path */
     978          66 :                 break;
     979             :             }
     980             :         }
     981             :     }
     982             : 
     983      333538 :     ReleaseSysCache(reltup);
     984             : 
     985      333538 :     return visible;
     986             : }
     987             : 
     988             : 
     989             : /*
     990             :  * TypenameGetTypid
     991             :  *      Wrapper for binary compatibility.
     992             :  */
     993             : Oid
     994           0 : TypenameGetTypid(const char *typname)
     995             : {
     996           0 :     return TypenameGetTypidExtended(typname, true);
     997             : }
     998             : 
     999             : /*
    1000             :  * TypenameGetTypidExtended
    1001             :  *      Try to resolve an unqualified datatype name.
    1002             :  *      Returns OID if type found in search path, else InvalidOid.
    1003             :  *
    1004             :  * This is essentially the same as RelnameGetRelid.
    1005             :  */
    1006             : Oid
    1007      362556 : TypenameGetTypidExtended(const char *typname, bool temp_ok)
    1008             : {
    1009             :     Oid         typid;
    1010             :     ListCell   *l;
    1011             : 
    1012      362556 :     recomputeNamespacePath();
    1013             : 
    1014      593494 :     foreach(l, activeSearchPath)
    1015             :     {
    1016      546364 :         Oid         namespaceId = lfirst_oid(l);
    1017             : 
    1018      546364 :         if (!temp_ok && namespaceId == myTempNamespace)
    1019        9504 :             continue;           /* do not look in temp namespace */
    1020             : 
    1021      536860 :         typid = GetSysCacheOid2(TYPENAMENSP, Anum_pg_type_oid,
    1022             :                                 PointerGetDatum(typname),
    1023             :                                 ObjectIdGetDatum(namespaceId));
    1024      536860 :         if (OidIsValid(typid))
    1025      315426 :             return typid;
    1026             :     }
    1027             : 
    1028             :     /* Not found in path */
    1029       47130 :     return InvalidOid;
    1030             : }
    1031             : 
    1032             : /*
    1033             :  * TypeIsVisible
    1034             :  *      Determine whether a type (identified by OID) is visible in the
    1035             :  *      current search path.  Visible means "would be found by searching
    1036             :  *      for the unqualified type name".
    1037             :  */
    1038             : bool
    1039      486708 : TypeIsVisible(Oid typid)
    1040             : {
    1041      486708 :     return TypeIsVisibleExt(typid, NULL);
    1042             : }
    1043             : 
    1044             : /*
    1045             :  * TypeIsVisibleExt
    1046             :  *      As above, but if the type isn't found and is_missing is not NULL,
    1047             :  *      then set *is_missing = true and return false instead of throwing
    1048             :  *      an error.  (Caller must initialize *is_missing = false.)
    1049             :  */
    1050             : static bool
    1051      490674 : TypeIsVisibleExt(Oid typid, bool *is_missing)
    1052             : {
    1053             :     HeapTuple   typtup;
    1054             :     Form_pg_type typform;
    1055             :     Oid         typnamespace;
    1056             :     bool        visible;
    1057             : 
    1058      490674 :     typtup = SearchSysCache1(TYPEOID, ObjectIdGetDatum(typid));
    1059      490674 :     if (!HeapTupleIsValid(typtup))
    1060             :     {
    1061           0 :         if (is_missing != NULL)
    1062             :         {
    1063           0 :             *is_missing = true;
    1064           0 :             return false;
    1065             :         }
    1066           0 :         elog(ERROR, "cache lookup failed for type %u", typid);
    1067             :     }
    1068      490674 :     typform = (Form_pg_type) GETSTRUCT(typtup);
    1069             : 
    1070      490674 :     recomputeNamespacePath();
    1071             : 
    1072             :     /*
    1073             :      * Quick check: if it ain't in the path at all, it ain't visible. Items in
    1074             :      * the system namespace are surely in the path and so we needn't even do
    1075             :      * list_member_oid() for them.
    1076             :      */
    1077      490674 :     typnamespace = typform->typnamespace;
    1078      490674 :     if (typnamespace != PG_CATALOG_NAMESPACE &&
    1079      104450 :         !list_member_oid(activeSearchPath, typnamespace))
    1080       22990 :         visible = false;
    1081             :     else
    1082             :     {
    1083             :         /*
    1084             :          * If it is in the path, it might still not be visible; it could be
    1085             :          * hidden by another type of the same name earlier in the path. So we
    1086             :          * must do a slow check for conflicting types.
    1087             :          */
    1088      467684 :         char       *typname = NameStr(typform->typname);
    1089             :         ListCell   *l;
    1090             : 
    1091      467684 :         visible = false;
    1092      608210 :         foreach(l, activeSearchPath)
    1093             :         {
    1094      608210 :             Oid         namespaceId = lfirst_oid(l);
    1095             : 
    1096      608210 :             if (namespaceId == typnamespace)
    1097             :             {
    1098             :                 /* Found it first in path */
    1099      467672 :                 visible = true;
    1100      467672 :                 break;
    1101             :             }
    1102      140538 :             if (SearchSysCacheExists2(TYPENAMENSP,
    1103             :                                       PointerGetDatum(typname),
    1104             :                                       ObjectIdGetDatum(namespaceId)))
    1105             :             {
    1106             :                 /* Found something else first in path */
    1107          12 :                 break;
    1108             :             }
    1109             :         }
    1110             :     }
    1111             : 
    1112      490674 :     ReleaseSysCache(typtup);
    1113             : 
    1114      490674 :     return visible;
    1115             : }
    1116             : 
    1117             : 
    1118             : /*
    1119             :  * FuncnameGetCandidates
    1120             :  *      Given a possibly-qualified routine name, argument count, and arg names,
    1121             :  *      retrieve a list of the possible matches.
    1122             :  *
    1123             :  * If nargs is -1, we return all routines matching the given name,
    1124             :  * regardless of argument count.  (argnames must be NIL, and expand_variadic
    1125             :  * and expand_defaults must be false, in this case.)
    1126             :  *
    1127             :  * If argnames isn't NIL, we are considering a named- or mixed-notation call,
    1128             :  * and only routines having all the listed argument names will be returned.
    1129             :  * (We assume that length(argnames) <= nargs and all the passed-in names are
    1130             :  * distinct.)  The returned structs will include an argnumbers array showing
    1131             :  * the actual argument index for each logical argument position.
    1132             :  *
    1133             :  * If expand_variadic is true, then variadic functions having the same number
    1134             :  * or fewer arguments will be retrieved, with the variadic argument and any
    1135             :  * additional argument positions filled with the variadic element type.
    1136             :  * nvargs in the returned struct is set to the number of such arguments.
    1137             :  * If expand_variadic is false, variadic arguments are not treated specially,
    1138             :  * and the returned nvargs will always be zero.
    1139             :  *
    1140             :  * If expand_defaults is true, functions that could match after insertion of
    1141             :  * default argument values will also be retrieved.  In this case the returned
    1142             :  * structs could have nargs > passed-in nargs, and ndargs is set to the number
    1143             :  * of additional args (which can be retrieved from the function's
    1144             :  * proargdefaults entry).
    1145             :  *
    1146             :  * If include_out_arguments is true, then OUT-mode arguments are considered to
    1147             :  * be included in the argument list.  Their types are included in the returned
    1148             :  * arrays, and argnumbers are indexes in proallargtypes not proargtypes.
    1149             :  * We also set nominalnargs to be the length of proallargtypes not proargtypes.
    1150             :  * Otherwise OUT-mode arguments are ignored.
    1151             :  *
    1152             :  * It is not possible for nvargs and ndargs to both be nonzero in the same
    1153             :  * list entry, since default insertion allows matches to functions with more
    1154             :  * than nargs arguments while the variadic transformation requires the same
    1155             :  * number or less.
    1156             :  *
    1157             :  * When argnames isn't NIL, the returned args[] type arrays are not ordered
    1158             :  * according to the functions' declarations, but rather according to the call:
    1159             :  * first any positional arguments, then the named arguments, then defaulted
    1160             :  * arguments (if needed and allowed by expand_defaults).  The argnumbers[]
    1161             :  * array can be used to map this back to the catalog information.
    1162             :  * argnumbers[k] is set to the proargtypes or proallargtypes index of the
    1163             :  * k'th call argument.
    1164             :  *
    1165             :  * We search a single namespace if the function name is qualified, else
    1166             :  * all namespaces in the search path.  In the multiple-namespace case,
    1167             :  * we arrange for entries in earlier namespaces to mask identical entries in
    1168             :  * later namespaces.
    1169             :  *
    1170             :  * When expanding variadics, we arrange for non-variadic functions to mask
    1171             :  * variadic ones if the expanded argument list is the same.  It is still
    1172             :  * possible for there to be conflicts between different variadic functions,
    1173             :  * however.
    1174             :  *
    1175             :  * It is guaranteed that the return list will never contain multiple entries
    1176             :  * with identical argument lists.  When expand_defaults is true, the entries
    1177             :  * could have more than nargs positions, but we still guarantee that they are
    1178             :  * distinct in the first nargs positions.  However, if argnames isn't NIL or
    1179             :  * either expand_variadic or expand_defaults is true, there might be multiple
    1180             :  * candidate functions that expand to identical argument lists.  Rather than
    1181             :  * throw error here, we report such situations by returning a single entry
    1182             :  * with oid = 0 that represents a set of such conflicting candidates.
    1183             :  * The caller might end up discarding such an entry anyway, but if it selects
    1184             :  * such an entry it should react as though the call were ambiguous.
    1185             :  *
    1186             :  * We return an empty list (NULL) if no suitable matches can be found.
    1187             :  * If the function name was schema-qualified with a schema that does not
    1188             :  * exist, then we return an empty list if missing_ok is true and otherwise
    1189             :  * throw an error.  (missing_ok does not affect the behavior otherwise.)
    1190             :  *
    1191             :  * The output argument *fgc_flags is filled with a bitmask indicating how
    1192             :  * far we were able to match the supplied information.  This is not of much
    1193             :  * interest if any candidates were found, but if not, it can help callers
    1194             :  * produce an on-point error message.
    1195             :  */
    1196             : FuncCandidateList
    1197      557064 : FuncnameGetCandidates(List *names, int nargs, List *argnames,
    1198             :                       bool expand_variadic, bool expand_defaults,
    1199             :                       bool include_out_arguments, bool missing_ok,
    1200             :                       int *fgc_flags)
    1201             : {
    1202      557064 :     FuncCandidateList resultList = NULL;
    1203      557064 :     bool        any_special = false;
    1204             :     char       *schemaname;
    1205             :     char       *funcname;
    1206             :     Oid         namespaceId;
    1207             :     CatCList   *catlist;
    1208             :     int         i;
    1209             : 
    1210             :     /* check for caller error */
    1211             :     Assert(nargs >= 0 || !(expand_variadic | expand_defaults));
    1212             : 
    1213             :     /* initialize output fgc_flags to empty */
    1214      557064 :     *fgc_flags = 0;
    1215             : 
    1216             :     /* deconstruct the name list */
    1217      557064 :     DeconstructQualifiedName(names, &schemaname, &funcname);
    1218             : 
    1219      557028 :     if (schemaname)
    1220             :     {
    1221             :         /* use exact schema given */
    1222      146032 :         *fgc_flags |= FGC_SCHEMA_GIVEN; /* report that a schema is given */
    1223      146032 :         namespaceId = LookupExplicitNamespace(schemaname, missing_ok);
    1224      146032 :         if (!OidIsValid(namespaceId))
    1225          48 :             return NULL;
    1226      145984 :         *fgc_flags |= FGC_SCHEMA_EXISTS;    /* report that the schema exists */
    1227             :     }
    1228             :     else
    1229             :     {
    1230             :         /* flag to indicate we need namespace search */
    1231      410996 :         namespaceId = InvalidOid;
    1232      410996 :         recomputeNamespacePath();
    1233             :     }
    1234             : 
    1235             :     /* Search syscache by name only */
    1236      556980 :     catlist = SearchSysCacheList1(PROCNAMEARGSNSP, CStringGetDatum(funcname));
    1237             : 
    1238     1731612 :     for (i = 0; i < catlist->n_members; i++)
    1239             :     {
    1240     1174632 :         HeapTuple   proctup = &catlist->members[i]->tuple;
    1241     1174632 :         Form_pg_proc procform = (Form_pg_proc) GETSTRUCT(proctup);
    1242     1174632 :         Oid        *proargtypes = procform->proargtypes.values;
    1243     1174632 :         int         pronargs = procform->pronargs;
    1244             :         int         effective_nargs;
    1245     1174632 :         int         pathpos = 0;
    1246             :         bool        variadic;
    1247             :         bool        use_defaults;
    1248             :         Oid         va_elem_type;
    1249     1174632 :         int        *argnumbers = NULL;
    1250             :         FuncCandidateList newResult;
    1251             : 
    1252     1174632 :         *fgc_flags |= FGC_NAME_EXISTS;  /* the name is present in pg_proc */
    1253             : 
    1254     1174632 :         if (OidIsValid(namespaceId))
    1255             :         {
    1256             :             /* Consider only procs in specified namespace */
    1257      287584 :             if (procform->pronamespace != namespaceId)
    1258      295692 :                 continue;
    1259             :         }
    1260             :         else
    1261             :         {
    1262             :             /*
    1263             :              * Consider only procs that are in the search path and are not in
    1264             :              * the temp namespace.
    1265             :              */
    1266             :             ListCell   *nsp;
    1267             : 
    1268     1155246 :             foreach(nsp, activeSearchPath)
    1269             :             {
    1270     1151916 :                 if (procform->pronamespace == lfirst_oid(nsp) &&
    1271      883754 :                     procform->pronamespace != myTempNamespace)
    1272      883718 :                     break;
    1273      268198 :                 pathpos++;
    1274             :             }
    1275      887048 :             if (nsp == NULL)
    1276        3330 :                 continue;       /* proc is not in search path */
    1277             :         }
    1278             : 
    1279     1161564 :         *fgc_flags |= FGC_NAME_VISIBLE; /* routine is in the right schema */
    1280             : 
    1281             :         /*
    1282             :          * If we are asked to match to OUT arguments, then use the
    1283             :          * proallargtypes array (which includes those); otherwise use
    1284             :          * proargtypes (which doesn't).  Of course, if proallargtypes is null,
    1285             :          * we always use proargtypes.
    1286             :          */
    1287     1161564 :         if (include_out_arguments)
    1288             :         {
    1289             :             Datum       proallargtypes;
    1290             :             bool        isNull;
    1291             : 
    1292         738 :             proallargtypes = SysCacheGetAttr(PROCNAMEARGSNSP, proctup,
    1293             :                                              Anum_pg_proc_proallargtypes,
    1294             :                                              &isNull);
    1295         738 :             if (!isNull)
    1296             :             {
    1297         222 :                 ArrayType  *arr = DatumGetArrayTypeP(proallargtypes);
    1298             : 
    1299         222 :                 pronargs = ARR_DIMS(arr)[0];
    1300         222 :                 if (ARR_NDIM(arr) != 1 ||
    1301         222 :                     pronargs < 0 ||
    1302         222 :                     ARR_HASNULL(arr) ||
    1303         222 :                     ARR_ELEMTYPE(arr) != OIDOID)
    1304           0 :                     elog(ERROR, "proallargtypes is not a 1-D Oid array or it contains nulls");
    1305             :                 Assert(pronargs >= procform->pronargs);
    1306         222 :                 proargtypes = (Oid *) ARR_DATA_PTR(arr);
    1307             :             }
    1308             :         }
    1309             : 
    1310     1161564 :         if (argnames != NIL)
    1311             :         {
    1312             :             /*
    1313             :              * Call uses named or mixed notation
    1314             :              *
    1315             :              * Check argument count.
    1316             :              */
    1317             :             Assert(nargs >= 0); /* -1 not supported with argnames */
    1318             : 
    1319       31902 :             if (pronargs > nargs && expand_defaults)
    1320             :             {
    1321             :                 /* Ignore if not enough default expressions */
    1322       13774 :                 if (nargs + procform->pronargdefaults < pronargs)
    1323        6472 :                     continue;
    1324        7302 :                 use_defaults = true;
    1325             :             }
    1326             :             else
    1327       18128 :                 use_defaults = false;
    1328             : 
    1329             :             /* Ignore if it doesn't match requested argument count */
    1330       25430 :             if (pronargs != nargs && !use_defaults)
    1331        9392 :                 continue;
    1332             : 
    1333             :             /* We found a routine with a suitable number of arguments */
    1334       16038 :             *fgc_flags |= FGC_ARGCOUNT_MATCH;
    1335             : 
    1336             :             /* Check for argument name match, generate positional mapping */
    1337       16038 :             if (!MatchNamedCall(proctup, nargs, argnames,
    1338             :                                 include_out_arguments, pronargs,
    1339             :                                 &argnumbers, fgc_flags))
    1340          30 :                 continue;
    1341             : 
    1342             :             /*
    1343             :              * Named or mixed notation can match a variadic function only if
    1344             :              * expand_variadic is off; otherwise there is no way to match the
    1345             :              * presumed-nameless parameters expanded from the variadic array.
    1346             :              * However, we postpone the check until here because we want to
    1347             :              * perform argument name matching anyway (using the variadic array
    1348             :              * argument's name).  This allows us to give an on-point error
    1349             :              * message if the user forgets to say VARIADIC in what would have
    1350             :              * been a valid call with it.
    1351             :              */
    1352       16008 :             if (OidIsValid(procform->provariadic) && expand_variadic)
    1353           6 :                 continue;
    1354       16002 :             va_elem_type = InvalidOid;
    1355       16002 :             variadic = false;
    1356             : 
    1357             :             /* We found a fully-valid call using argument names */
    1358       16002 :             *fgc_flags |= FGC_ARGNAMES_VALID;
    1359             : 
    1360             :             /* Named argument matching is always "special" */
    1361       16002 :             any_special = true;
    1362             :         }
    1363             :         else
    1364             :         {
    1365             :             /*
    1366             :              * Call uses positional notation
    1367             :              *
    1368             :              * Check if function is variadic, and get variadic element type if
    1369             :              * so.  If expand_variadic is false, we should just ignore
    1370             :              * variadic-ness.
    1371             :              */
    1372     1129662 :             if (pronargs <= nargs && expand_variadic)
    1373             :             {
    1374      700486 :                 va_elem_type = procform->provariadic;
    1375      700486 :                 variadic = OidIsValid(va_elem_type);
    1376      700486 :                 any_special |= variadic;
    1377             :             }
    1378             :             else
    1379             :             {
    1380      429176 :                 va_elem_type = InvalidOid;
    1381      429176 :                 variadic = false;
    1382             :             }
    1383             : 
    1384             :             /*
    1385             :              * Check if function can match by using parameter defaults.
    1386             :              */
    1387     1129662 :             if (pronargs > nargs && expand_defaults)
    1388             :             {
    1389             :                 /* Ignore if not enough default expressions */
    1390      205822 :                 if (nargs + procform->pronargdefaults < pronargs)
    1391      199076 :                     continue;
    1392        6746 :                 use_defaults = true;
    1393        6746 :                 any_special = true;
    1394             :             }
    1395             :             else
    1396      923840 :                 use_defaults = false;
    1397             : 
    1398             :             /* Ignore if it doesn't match requested argument count */
    1399      930586 :             if (nargs >= 0 && pronargs != nargs && !variadic && !use_defaults)
    1400       65540 :                 continue;
    1401             : 
    1402             :             /* We found a routine with a suitable number of arguments */
    1403      865046 :             *fgc_flags |= FGC_ARGCOUNT_MATCH;
    1404             :         }
    1405             : 
    1406             :         /*
    1407             :          * We must compute the effective argument list so that we can easily
    1408             :          * compare it to earlier results.  We waste a palloc cycle if it gets
    1409             :          * masked by an earlier result, but really that's a pretty infrequent
    1410             :          * case so it's not worth worrying about.
    1411             :          */
    1412      881048 :         effective_nargs = Max(pronargs, nargs);
    1413             :         newResult = (FuncCandidateList)
    1414      881048 :             palloc(offsetof(struct _FuncCandidateList, args) +
    1415             :                    effective_nargs * sizeof(Oid));
    1416      881048 :         newResult->pathpos = pathpos;
    1417      881048 :         newResult->oid = procform->oid;
    1418      881048 :         newResult->nominalnargs = pronargs;
    1419      881048 :         newResult->nargs = effective_nargs;
    1420      881048 :         newResult->argnumbers = argnumbers;
    1421      881048 :         if (argnumbers)
    1422             :         {
    1423             :             /* Re-order the argument types into call's logical order */
    1424       79550 :             for (int j = 0; j < pronargs; j++)
    1425       63548 :                 newResult->args[j] = proargtypes[argnumbers[j]];
    1426             :         }
    1427             :         else
    1428             :         {
    1429             :             /* Simple positional case, just copy proargtypes as-is */
    1430      865046 :             memcpy(newResult->args, proargtypes, pronargs * sizeof(Oid));
    1431             :         }
    1432      881048 :         if (variadic)
    1433             :         {
    1434       10946 :             newResult->nvargs = effective_nargs - pronargs + 1;
    1435             :             /* Expand variadic argument into N copies of element type */
    1436       81012 :             for (int j = pronargs - 1; j < effective_nargs; j++)
    1437       70066 :                 newResult->args[j] = va_elem_type;
    1438             :         }
    1439             :         else
    1440      870102 :             newResult->nvargs = 0;
    1441      881048 :         newResult->ndargs = use_defaults ? pronargs - nargs : 0;
    1442             : 
    1443             :         /*
    1444             :          * Does it have the same arguments as something we already accepted?
    1445             :          * If so, decide what to do to avoid returning duplicate argument
    1446             :          * lists.  We can skip this check for the single-namespace case if no
    1447             :          * special (named, variadic or defaults) match has been made, since
    1448             :          * then the unique index on pg_proc guarantees all the matches have
    1449             :          * different argument lists.
    1450             :          */
    1451      881048 :         if (resultList != NULL &&
    1452      326928 :             (any_special || !OidIsValid(namespaceId)))
    1453             :         {
    1454             :             /*
    1455             :              * If we have an ordered list from SearchSysCacheList (the normal
    1456             :              * case), then any conflicting proc must immediately adjoin this
    1457             :              * one in the list, so we only need to look at the newest result
    1458             :              * item.  If we have an unordered list, we have to scan the whole
    1459             :              * result list.  Also, if either the current candidate or any
    1460             :              * previous candidate is a special match, we can't assume that
    1461             :              * conflicts are adjacent.
    1462             :              *
    1463             :              * We ignore defaulted arguments in deciding what is a match.
    1464             :              */
    1465             :             FuncCandidateList prevResult;
    1466             : 
    1467      286984 :             if (catlist->ordered && !any_special)
    1468             :             {
    1469             :                 /* ndargs must be 0 if !any_special */
    1470      284348 :                 if (effective_nargs == resultList->nargs &&
    1471      284228 :                     memcmp(newResult->args,
    1472      284228 :                            resultList->args,
    1473             :                            effective_nargs * sizeof(Oid)) == 0)
    1474           8 :                     prevResult = resultList;
    1475             :                 else
    1476      284340 :                     prevResult = NULL;
    1477             :             }
    1478             :             else
    1479             :             {
    1480        2636 :                 int         cmp_nargs = newResult->nargs - newResult->ndargs;
    1481             : 
    1482        2636 :                 for (prevResult = resultList;
    1483        3214 :                      prevResult;
    1484         578 :                      prevResult = prevResult->next)
    1485             :                 {
    1486        2756 :                     if (cmp_nargs == prevResult->nargs - prevResult->ndargs &&
    1487        2756 :                         memcmp(newResult->args,
    1488        2756 :                                prevResult->args,
    1489             :                                cmp_nargs * sizeof(Oid)) == 0)
    1490        2178 :                         break;
    1491             :                 }
    1492             :             }
    1493             : 
    1494      286984 :             if (prevResult)
    1495             :             {
    1496             :                 /*
    1497             :                  * We have a match with a previous result.  Decide which one
    1498             :                  * to keep, or mark it ambiguous if we can't decide.  The
    1499             :                  * logic here is preference > 0 means prefer the old result,
    1500             :                  * preference < 0 means prefer the new, preference = 0 means
    1501             :                  * ambiguous.
    1502             :                  */
    1503             :                 int         preference;
    1504             : 
    1505        2186 :                 if (pathpos != prevResult->pathpos)
    1506             :                 {
    1507             :                     /*
    1508             :                      * Prefer the one that's earlier in the search path.
    1509             :                      */
    1510           2 :                     preference = pathpos - prevResult->pathpos;
    1511             :                 }
    1512        2184 :                 else if (variadic && prevResult->nvargs == 0)
    1513             :                 {
    1514             :                     /*
    1515             :                      * With variadic functions we could have, for example,
    1516             :                      * both foo(numeric) and foo(variadic numeric[]) in the
    1517             :                      * same namespace; if so we prefer the non-variadic match
    1518             :                      * on efficiency grounds.
    1519             :                      */
    1520        2064 :                     preference = 1;
    1521             :                 }
    1522         120 :                 else if (!variadic && prevResult->nvargs > 0)
    1523             :                 {
    1524          78 :                     preference = -1;
    1525             :                 }
    1526             :                 else
    1527             :                 {
    1528             :                     /*----------
    1529             :                      * We can't decide.  This can happen with, for example,
    1530             :                      * both foo(numeric, variadic numeric[]) and
    1531             :                      * foo(variadic numeric[]) in the same namespace, or
    1532             :                      * both foo(int) and foo (int, int default something)
    1533             :                      * in the same namespace, or both foo(a int, b text)
    1534             :                      * and foo(b text, a int) in the same namespace.
    1535             :                      *----------
    1536             :                      */
    1537          42 :                     preference = 0;
    1538             :                 }
    1539             : 
    1540        2186 :                 if (preference > 0)
    1541             :                 {
    1542             :                     /* keep previous result */
    1543        2066 :                     pfree(newResult);
    1544        2066 :                     continue;
    1545             :                 }
    1546         120 :                 else if (preference < 0)
    1547             :                 {
    1548             :                     /* remove previous result from the list */
    1549          78 :                     if (prevResult == resultList)
    1550          78 :                         resultList = prevResult->next;
    1551             :                     else
    1552             :                     {
    1553             :                         FuncCandidateList prevPrevResult;
    1554             : 
    1555           0 :                         for (prevPrevResult = resultList;
    1556           0 :                              prevPrevResult;
    1557           0 :                              prevPrevResult = prevPrevResult->next)
    1558             :                         {
    1559           0 :                             if (prevResult == prevPrevResult->next)
    1560             :                             {
    1561           0 :                                 prevPrevResult->next = prevResult->next;
    1562           0 :                                 break;
    1563             :                             }
    1564             :                         }
    1565             :                         Assert(prevPrevResult); /* assert we found it */
    1566             :                     }
    1567          78 :                     pfree(prevResult);
    1568             :                     /* fall through to add newResult to list */
    1569             :                 }
    1570             :                 else
    1571             :                 {
    1572             :                     /* mark old result as ambiguous, discard new */
    1573          42 :                     prevResult->oid = InvalidOid;
    1574          42 :                     pfree(newResult);
    1575          42 :                     continue;
    1576             :                 }
    1577             :             }
    1578             :         }
    1579             : 
    1580             :         /*
    1581             :          * Okay to add it to result list
    1582             :          */
    1583      878940 :         newResult->next = resultList;
    1584      878940 :         resultList = newResult;
    1585             :     }
    1586             : 
    1587      556980 :     ReleaseSysCacheList(catlist);
    1588             : 
    1589      556980 :     return resultList;
    1590             : }
    1591             : 
    1592             : /*
    1593             :  * MatchNamedCall
    1594             :  *      Given a pg_proc heap tuple and a call's list of argument names,
    1595             :  *      check whether the function could match the call.
    1596             :  *
    1597             :  * The call could match if all supplied argument names are accepted by
    1598             :  * the function, in positions after the last positional argument, and there
    1599             :  * are defaults for all unsupplied arguments.
    1600             :  *
    1601             :  * If include_out_arguments is true, we are treating OUT arguments as
    1602             :  * included in the argument list.  pronargs is the number of arguments
    1603             :  * we're considering (the length of either proargtypes or proallargtypes).
    1604             :  *
    1605             :  * The number of positional arguments is nargs - list_length(argnames).
    1606             :  * Note caller has already done basic checks on argument count.
    1607             :  *
    1608             :  * On match, return true and fill *argnumbers with a palloc'd array showing
    1609             :  * the mapping from call argument positions to actual function argument
    1610             :  * numbers.  Defaulted arguments are included in this map, at positions
    1611             :  * after the last supplied argument.
    1612             :  *
    1613             :  * We also add flag bits to *fgc_flags reporting on how far the match got.
    1614             :  */
    1615             : static bool
    1616       16038 : MatchNamedCall(HeapTuple proctup, int nargs, List *argnames,
    1617             :                bool include_out_arguments, int pronargs,
    1618             :                int **argnumbers, int *fgc_flags)
    1619             : {
    1620       16038 :     Form_pg_proc procform = (Form_pg_proc) GETSTRUCT(proctup);
    1621       16038 :     int         numposargs = nargs - list_length(argnames);
    1622             :     int         pronallargs;
    1623             :     Oid        *p_argtypes;
    1624             :     char      **p_argnames;
    1625             :     char       *p_argmodes;
    1626             :     bool        arggiven[FUNC_MAX_ARGS];
    1627       16038 :     bool        arg_filled_twice = false;
    1628             :     bool        isnull;
    1629             :     int         ap;             /* call args position */
    1630             :     int         pp;             /* proargs position */
    1631             :     ListCell   *lc;
    1632             : 
    1633             :     Assert(argnames != NIL);
    1634             :     Assert(numposargs >= 0);
    1635             :     Assert(nargs <= pronargs);
    1636             : 
    1637             :     /* Ignore this function if its proargnames is null */
    1638       16038 :     (void) SysCacheGetAttr(PROCOID, proctup, Anum_pg_proc_proargnames,
    1639             :                            &isnull);
    1640       16038 :     if (isnull)
    1641           0 :         return false;
    1642             : 
    1643             :     /* OK, let's extract the argument names and types */
    1644       16038 :     pronallargs = get_func_arg_info(proctup,
    1645             :                                     &p_argtypes, &p_argnames, &p_argmodes);
    1646             :     Assert(p_argnames != NULL);
    1647             : 
    1648             :     Assert(include_out_arguments ? (pronargs == pronallargs) : (pronargs <= pronallargs));
    1649             : 
    1650             :     /* initialize state for matching */
    1651       16038 :     *argnumbers = (int *) palloc(pronargs * sizeof(int));
    1652       16038 :     memset(arggiven, false, pronargs * sizeof(bool));
    1653             : 
    1654             :     /* there are numposargs positional args before the named args */
    1655       18552 :     for (ap = 0; ap < numposargs; ap++)
    1656             :     {
    1657        2514 :         (*argnumbers)[ap] = ap;
    1658        2514 :         arggiven[ap] = true;
    1659             :     }
    1660             : 
    1661             :     /* now examine the named args */
    1662       62356 :     foreach(lc, argnames)
    1663             :     {
    1664       46330 :         char       *argname = (char *) lfirst(lc);
    1665             :         bool        found;
    1666             :         int         i;
    1667             : 
    1668       46330 :         pp = 0;
    1669       46330 :         found = false;
    1670      106460 :         for (i = 0; i < pronallargs; i++)
    1671             :         {
    1672             :             /* consider only input params, except with include_out_arguments */
    1673      106448 :             if (!include_out_arguments &&
    1674       71190 :                 p_argmodes &&
    1675       71190 :                 (p_argmodes[i] != FUNC_PARAM_IN &&
    1676          84 :                  p_argmodes[i] != FUNC_PARAM_INOUT &&
    1677          84 :                  p_argmodes[i] != FUNC_PARAM_VARIADIC))
    1678          48 :                 continue;
    1679      106400 :             if (p_argnames[i] && strcmp(p_argnames[i], argname) == 0)
    1680             :             {
    1681             :                 /* note if argname matches a positional argument */
    1682       46318 :                 if (arggiven[pp])
    1683          12 :                     arg_filled_twice = true;
    1684       46318 :                 arggiven[pp] = true;
    1685       46318 :                 (*argnumbers)[ap] = pp;
    1686       46318 :                 found = true;
    1687       46318 :                 break;
    1688             :             }
    1689             :             /* increase pp only for considered parameters */
    1690       60082 :             pp++;
    1691             :         }
    1692             :         /* if name isn't in proargnames, fail */
    1693       46330 :         if (!found)
    1694          12 :             return false;
    1695       46318 :         ap++;
    1696             :     }
    1697             : 
    1698             :     Assert(ap == nargs);        /* processed all actual parameters */
    1699             : 
    1700             :     /* If we get here, the function did match all the supplied argnames */
    1701       16026 :     *fgc_flags |= FGC_ARGNAMES_MATCH;
    1702             : 
    1703             :     /* ... however, some of them might have been placed wrong */
    1704       16026 :     if (arg_filled_twice)
    1705          12 :         return false;           /* some argname matched a positional argument */
    1706             : 
    1707             :     /* If we get here, the call doesn't have invalid mixed notation */
    1708       16014 :     *fgc_flags |= FGC_ARGNAMES_NONDUP;
    1709             : 
    1710             :     /* Check for default arguments */
    1711       16014 :     if (nargs < pronargs)
    1712             :     {
    1713        7290 :         int         first_arg_with_default = pronargs - procform->pronargdefaults;
    1714             : 
    1715       47926 :         for (pp = numposargs; pp < pronargs; pp++)
    1716             :         {
    1717       40642 :             if (arggiven[pp])
    1718       25860 :                 continue;
    1719             :             /* fail if arg not given and no default available */
    1720       14782 :             if (pp < first_arg_with_default)
    1721           6 :                 return false;
    1722       14776 :             (*argnumbers)[ap++] = pp;
    1723             :         }
    1724             :     }
    1725             : 
    1726             :     Assert(ap == pronargs);     /* processed all function parameters */
    1727             : 
    1728             :     /* If we get here, the call supplies all the required arguments */
    1729       16008 :     *fgc_flags |= FGC_ARGNAMES_ALL;
    1730             : 
    1731       16008 :     return true;
    1732             : }
    1733             : 
    1734             : /*
    1735             :  * FunctionIsVisible
    1736             :  *      Determine whether a function (identified by OID) is visible in the
    1737             :  *      current search path.  Visible means "would be found by searching
    1738             :  *      for the unqualified function name with exact argument matches".
    1739             :  */
    1740             : bool
    1741       28444 : FunctionIsVisible(Oid funcid)
    1742             : {
    1743       28444 :     return FunctionIsVisibleExt(funcid, NULL);
    1744             : }
    1745             : 
    1746             : /*
    1747             :  * FunctionIsVisibleExt
    1748             :  *      As above, but if the function isn't found and is_missing is not NULL,
    1749             :  *      then set *is_missing = true and return false instead of throwing
    1750             :  *      an error.  (Caller must initialize *is_missing = false.)
    1751             :  */
    1752             : static bool
    1753       36038 : FunctionIsVisibleExt(Oid funcid, bool *is_missing)
    1754             : {
    1755             :     HeapTuple   proctup;
    1756             :     Form_pg_proc procform;
    1757             :     Oid         pronamespace;
    1758             :     bool        visible;
    1759             : 
    1760       36038 :     proctup = SearchSysCache1(PROCOID, ObjectIdGetDatum(funcid));
    1761       36038 :     if (!HeapTupleIsValid(proctup))
    1762             :     {
    1763           0 :         if (is_missing != NULL)
    1764             :         {
    1765           0 :             *is_missing = true;
    1766           0 :             return false;
    1767             :         }
    1768           0 :         elog(ERROR, "cache lookup failed for function %u", funcid);
    1769             :     }
    1770       36038 :     procform = (Form_pg_proc) GETSTRUCT(proctup);
    1771             : 
    1772       36038 :     recomputeNamespacePath();
    1773             : 
    1774             :     /*
    1775             :      * Quick check: if it ain't in the path at all, it ain't visible. Items in
    1776             :      * the system namespace are surely in the path and so we needn't even do
    1777             :      * list_member_oid() for them.
    1778             :      */
    1779       36038 :     pronamespace = procform->pronamespace;
    1780       36038 :     if (pronamespace != PG_CATALOG_NAMESPACE &&
    1781       14774 :         !list_member_oid(activeSearchPath, pronamespace))
    1782         830 :         visible = false;
    1783             :     else
    1784             :     {
    1785             :         /*
    1786             :          * If it is in the path, it might still not be visible; it could be
    1787             :          * hidden by another proc of the same name and arguments earlier in
    1788             :          * the path.  So we must do a slow check to see if this is the same
    1789             :          * proc that would be found by FuncnameGetCandidates.
    1790             :          */
    1791       35208 :         char       *proname = NameStr(procform->proname);
    1792       35208 :         int         nargs = procform->pronargs;
    1793             :         FuncCandidateList clist;
    1794             :         int         fgc_flags;
    1795             : 
    1796       35208 :         visible = false;
    1797             : 
    1798       35208 :         clist = FuncnameGetCandidates(list_make1(makeString(proname)),
    1799             :                                       nargs, NIL, false, false, false, false,
    1800             :                                       &fgc_flags);
    1801             : 
    1802       44390 :         for (; clist; clist = clist->next)
    1803             :         {
    1804       44378 :             if (memcmp(clist->args, procform->proargtypes.values,
    1805             :                        nargs * sizeof(Oid)) == 0)
    1806             :             {
    1807             :                 /* Found the expected entry; is it the right proc? */
    1808       35196 :                 visible = (clist->oid == funcid);
    1809       35196 :                 break;
    1810             :             }
    1811             :         }
    1812             :     }
    1813             : 
    1814       36038 :     ReleaseSysCache(proctup);
    1815             : 
    1816       36038 :     return visible;
    1817             : }
    1818             : 
    1819             : 
    1820             : /*
    1821             :  * OpernameGetOprid
    1822             :  *      Given a possibly-qualified operator name and exact input datatypes,
    1823             :  *      look up the operator.  Returns InvalidOid if not found.
    1824             :  *
    1825             :  * Pass oprleft = InvalidOid for a prefix op.
    1826             :  *
    1827             :  * If the operator name is not schema-qualified, it is sought in the current
    1828             :  * namespace search path.  If the name is schema-qualified and the given
    1829             :  * schema does not exist, InvalidOid is returned.
    1830             :  */
    1831             : Oid
    1832       93760 : OpernameGetOprid(List *names, Oid oprleft, Oid oprright)
    1833             : {
    1834             :     char       *schemaname;
    1835             :     char       *opername;
    1836             :     CatCList   *catlist;
    1837             :     ListCell   *l;
    1838             : 
    1839             :     /* deconstruct the name list */
    1840       93760 :     DeconstructQualifiedName(names, &schemaname, &opername);
    1841             : 
    1842       93760 :     if (schemaname)
    1843             :     {
    1844             :         /* search only in exact schema given */
    1845             :         Oid         namespaceId;
    1846             : 
    1847        2874 :         namespaceId = LookupExplicitNamespace(schemaname, true);
    1848        2874 :         if (OidIsValid(namespaceId))
    1849             :         {
    1850             :             HeapTuple   opertup;
    1851             : 
    1852        2850 :             opertup = SearchSysCache4(OPERNAMENSP,
    1853             :                                       CStringGetDatum(opername),
    1854             :                                       ObjectIdGetDatum(oprleft),
    1855             :                                       ObjectIdGetDatum(oprright),
    1856             :                                       ObjectIdGetDatum(namespaceId));
    1857        2850 :             if (HeapTupleIsValid(opertup))
    1858             :             {
    1859        1658 :                 Form_pg_operator operclass = (Form_pg_operator) GETSTRUCT(opertup);
    1860        1658 :                 Oid         result = operclass->oid;
    1861             : 
    1862        1658 :                 ReleaseSysCache(opertup);
    1863        1658 :                 return result;
    1864             :             }
    1865             :         }
    1866             : 
    1867        1216 :         return InvalidOid;
    1868             :     }
    1869             : 
    1870             :     /* Search syscache by name and argument types */
    1871       90886 :     catlist = SearchSysCacheList3(OPERNAMENSP,
    1872             :                                   CStringGetDatum(opername),
    1873             :                                   ObjectIdGetDatum(oprleft),
    1874             :                                   ObjectIdGetDatum(oprright));
    1875             : 
    1876       90886 :     if (catlist->n_members == 0)
    1877             :     {
    1878             :         /* no hope, fall out early */
    1879       18362 :         ReleaseSysCacheList(catlist);
    1880       18362 :         return InvalidOid;
    1881             :     }
    1882             : 
    1883             :     /*
    1884             :      * We have to find the list member that is first in the search path, if
    1885             :      * there's more than one.  This doubly-nested loop looks ugly, but in
    1886             :      * practice there should usually be few catlist members.
    1887             :      */
    1888       72524 :     recomputeNamespacePath();
    1889             : 
    1890       85950 :     foreach(l, activeSearchPath)
    1891             :     {
    1892       85938 :         Oid         namespaceId = lfirst_oid(l);
    1893             :         int         i;
    1894             : 
    1895       85938 :         if (namespaceId == myTempNamespace)
    1896        8952 :             continue;           /* do not look in temp namespace */
    1897             : 
    1898       81500 :         for (i = 0; i < catlist->n_members; i++)
    1899             :         {
    1900       77026 :             HeapTuple   opertup = &catlist->members[i]->tuple;
    1901       77026 :             Form_pg_operator operform = (Form_pg_operator) GETSTRUCT(opertup);
    1902             : 
    1903       77026 :             if (operform->oprnamespace == namespaceId)
    1904             :             {
    1905       72512 :                 Oid         result = operform->oid;
    1906             : 
    1907       72512 :                 ReleaseSysCacheList(catlist);
    1908       72512 :                 return result;
    1909             :             }
    1910             :         }
    1911             :     }
    1912             : 
    1913          12 :     ReleaseSysCacheList(catlist);
    1914          12 :     return InvalidOid;
    1915             : }
    1916             : 
    1917             : /*
    1918             :  * OpernameGetCandidates
    1919             :  *      Given a possibly-qualified operator name and operator kind,
    1920             :  *      retrieve a list of the possible matches.
    1921             :  *
    1922             :  * If oprkind is '\0', we return all operators matching the given name,
    1923             :  * regardless of arguments.
    1924             :  *
    1925             :  * We search a single namespace if the operator name is qualified, else
    1926             :  * all namespaces in the search path.  The return list will never contain
    1927             :  * multiple entries with identical argument lists --- in the multiple-
    1928             :  * namespace case, we arrange for entries in earlier namespaces to mask
    1929             :  * identical entries in later namespaces.
    1930             :  *
    1931             :  * The returned items always have two args[] entries --- the first will be
    1932             :  * InvalidOid for a prefix oprkind.  nargs is always 2, too.
    1933             :  *
    1934             :  * We return an empty list (NULL) if no suitable matches can be found.  If the
    1935             :  * operator name was schema-qualified with a schema that does not exist, then
    1936             :  * we return an empty list if missing_schema_ok is true and otherwise throw an
    1937             :  * error.  (missing_schema_ok does not affect the behavior otherwise.)
    1938             :  *
    1939             :  * The output argument *fgc_flags is filled with a bitmask indicating how
    1940             :  * far we were able to match the supplied information.  This is not of much
    1941             :  * interest if any candidates were found, but if not, it can help callers
    1942             :  * produce an on-point error message.
    1943             :  */
    1944             : FuncCandidateList
    1945       18466 : OpernameGetCandidates(List *names, char oprkind, bool missing_schema_ok,
    1946             :                       int *fgc_flags)
    1947             : {
    1948       18466 :     FuncCandidateList resultList = NULL;
    1949       18466 :     char       *resultSpace = NULL;
    1950       18466 :     int         nextResult = 0;
    1951             :     char       *schemaname;
    1952             :     char       *opername;
    1953             :     Oid         namespaceId;
    1954             :     CatCList   *catlist;
    1955             :     int         i;
    1956             : 
    1957             :     /* initialize output fgc_flags to empty */
    1958       18466 :     *fgc_flags = 0;
    1959             : 
    1960             :     /* deconstruct the name list */
    1961       18466 :     DeconstructQualifiedName(names, &schemaname, &opername);
    1962             : 
    1963       18466 :     if (schemaname)
    1964             :     {
    1965             :         /* use exact schema given */
    1966        1212 :         *fgc_flags |= FGC_SCHEMA_GIVEN; /* report that a schema is given */
    1967        1212 :         namespaceId = LookupExplicitNamespace(schemaname, missing_schema_ok);
    1968        1212 :         if (!OidIsValid(namespaceId))
    1969          18 :             return NULL;
    1970        1194 :         *fgc_flags |= FGC_SCHEMA_EXISTS;    /* report that the schema exists */
    1971             :     }
    1972             :     else
    1973             :     {
    1974             :         /* flag to indicate we need namespace search */
    1975       17254 :         namespaceId = InvalidOid;
    1976       17254 :         recomputeNamespacePath();
    1977             :     }
    1978             : 
    1979             :     /* Search syscache by name only */
    1980       18448 :     catlist = SearchSysCacheList1(OPERNAMENSP, CStringGetDatum(opername));
    1981             : 
    1982             :     /*
    1983             :      * In typical scenarios, most if not all of the operators found by the
    1984             :      * catcache search will end up getting returned; and there can be quite a
    1985             :      * few, for common operator names such as '=' or '+'.  To reduce the time
    1986             :      * spent in palloc, we allocate the result space as an array large enough
    1987             :      * to hold all the operators.  The original coding of this routine did a
    1988             :      * separate palloc for each operator, but profiling revealed that the
    1989             :      * pallocs used an unreasonably large fraction of parsing time.
    1990             :      */
    1991             : #define SPACE_PER_OP MAXALIGN(offsetof(struct _FuncCandidateList, args) + \
    1992             :                               2 * sizeof(Oid))
    1993             : 
    1994       18448 :     if (catlist->n_members > 0)
    1995       18430 :         resultSpace = palloc(catlist->n_members * SPACE_PER_OP);
    1996             : 
    1997      857726 :     for (i = 0; i < catlist->n_members; i++)
    1998             :     {
    1999      839278 :         HeapTuple   opertup = &catlist->members[i]->tuple;
    2000      839278 :         Form_pg_operator operform = (Form_pg_operator) GETSTRUCT(opertup);
    2001      839278 :         int         pathpos = 0;
    2002             :         FuncCandidateList newResult;
    2003             : 
    2004             :         /* Ignore operators of wrong kind, if specific kind requested */
    2005      839278 :         if (oprkind && operform->oprkind != oprkind)
    2006        9974 :             continue;
    2007             : 
    2008      829304 :         *fgc_flags |= FGC_NAME_EXISTS;  /* the name is present in pg_operator */
    2009             : 
    2010      829304 :         if (OidIsValid(namespaceId))
    2011             :         {
    2012             :             /* Consider only opers in specified namespace */
    2013       23466 :             if (operform->oprnamespace != namespaceId)
    2014           0 :                 continue;
    2015             :             /* No need to check args, they must all be different */
    2016             :         }
    2017             :         else
    2018             :         {
    2019             :             /*
    2020             :              * Consider only opers that are in the search path and are not in
    2021             :              * the temp namespace.
    2022             :              */
    2023             :             ListCell   *nsp;
    2024             : 
    2025      899916 :             foreach(nsp, activeSearchPath)
    2026             :             {
    2027      899034 :                 if (operform->oprnamespace == lfirst_oid(nsp) &&
    2028      804956 :                     operform->oprnamespace != myTempNamespace)
    2029      804956 :                     break;
    2030       94078 :                 pathpos++;
    2031             :             }
    2032      805838 :             if (nsp == NULL)
    2033         882 :                 continue;       /* oper is not in search path */
    2034             : 
    2035             :             /*
    2036             :              * Okay, it's in the search path, but does it have the same
    2037             :              * arguments as something we already accepted?  If so, keep only
    2038             :              * the one that appears earlier in the search path.
    2039             :              *
    2040             :              * If we have an ordered list from SearchSysCacheList (the normal
    2041             :              * case), then any conflicting oper must immediately adjoin this
    2042             :              * one in the list, so we only need to look at the newest result
    2043             :              * item.  If we have an unordered list, we have to scan the whole
    2044             :              * result list.
    2045             :              */
    2046      804956 :             if (resultList)
    2047             :             {
    2048             :                 FuncCandidateList prevResult;
    2049             : 
    2050      787726 :                 if (catlist->ordered)
    2051             :                 {
    2052      787726 :                     if (operform->oprleft == resultList->args[0] &&
    2053      227120 :                         operform->oprright == resultList->args[1])
    2054           0 :                         prevResult = resultList;
    2055             :                     else
    2056      787726 :                         prevResult = NULL;
    2057             :                 }
    2058             :                 else
    2059             :                 {
    2060           0 :                     for (prevResult = resultList;
    2061           0 :                          prevResult;
    2062           0 :                          prevResult = prevResult->next)
    2063             :                     {
    2064           0 :                         if (operform->oprleft == prevResult->args[0] &&
    2065           0 :                             operform->oprright == prevResult->args[1])
    2066           0 :                             break;
    2067             :                     }
    2068             :                 }
    2069      787726 :                 if (prevResult)
    2070             :                 {
    2071             :                     /* We have a match with a previous result */
    2072             :                     Assert(pathpos != prevResult->pathpos);
    2073           0 :                     if (pathpos > prevResult->pathpos)
    2074           0 :                         continue;   /* keep previous result */
    2075             :                     /* replace previous result */
    2076           0 :                     prevResult->pathpos = pathpos;
    2077           0 :                     prevResult->oid = operform->oid;
    2078           0 :                     continue;   /* args are same, of course */
    2079             :                 }
    2080             :             }
    2081             :         }
    2082             : 
    2083      828422 :         *fgc_flags |= FGC_NAME_VISIBLE; /* operator is in the right schema */
    2084             : 
    2085             :         /*
    2086             :          * Okay to add it to result list
    2087             :          */
    2088      828422 :         newResult = (FuncCandidateList) (resultSpace + nextResult);
    2089      828422 :         nextResult += SPACE_PER_OP;
    2090             : 
    2091      828422 :         newResult->pathpos = pathpos;
    2092      828422 :         newResult->oid = operform->oid;
    2093      828422 :         newResult->nominalnargs = 2;
    2094      828422 :         newResult->nargs = 2;
    2095      828422 :         newResult->nvargs = 0;
    2096      828422 :         newResult->ndargs = 0;
    2097      828422 :         newResult->argnumbers = NULL;
    2098      828422 :         newResult->args[0] = operform->oprleft;
    2099      828422 :         newResult->args[1] = operform->oprright;
    2100      828422 :         newResult->next = resultList;
    2101      828422 :         resultList = newResult;
    2102             :     }
    2103             : 
    2104       18448 :     ReleaseSysCacheList(catlist);
    2105             : 
    2106       18448 :     return resultList;
    2107             : }
    2108             : 
    2109             : /*
    2110             :  * OperatorIsVisible
    2111             :  *      Determine whether an operator (identified by OID) is visible in the
    2112             :  *      current search path.  Visible means "would be found by searching
    2113             :  *      for the unqualified operator name with exact argument matches".
    2114             :  */
    2115             : bool
    2116        3886 : OperatorIsVisible(Oid oprid)
    2117             : {
    2118        3886 :     return OperatorIsVisibleExt(oprid, NULL);
    2119             : }
    2120             : 
    2121             : /*
    2122             :  * OperatorIsVisibleExt
    2123             :  *      As above, but if the operator isn't found and is_missing is not NULL,
    2124             :  *      then set *is_missing = true and return false instead of throwing
    2125             :  *      an error.  (Caller must initialize *is_missing = false.)
    2126             :  */
    2127             : static bool
    2128        5586 : OperatorIsVisibleExt(Oid oprid, bool *is_missing)
    2129             : {
    2130             :     HeapTuple   oprtup;
    2131             :     Form_pg_operator oprform;
    2132             :     Oid         oprnamespace;
    2133             :     bool        visible;
    2134             : 
    2135        5586 :     oprtup = SearchSysCache1(OPEROID, ObjectIdGetDatum(oprid));
    2136        5586 :     if (!HeapTupleIsValid(oprtup))
    2137             :     {
    2138           0 :         if (is_missing != NULL)
    2139             :         {
    2140           0 :             *is_missing = true;
    2141           0 :             return false;
    2142             :         }
    2143           0 :         elog(ERROR, "cache lookup failed for operator %u", oprid);
    2144             :     }
    2145        5586 :     oprform = (Form_pg_operator) GETSTRUCT(oprtup);
    2146             : 
    2147        5586 :     recomputeNamespacePath();
    2148             : 
    2149             :     /*
    2150             :      * Quick check: if it ain't in the path at all, it ain't visible. Items in
    2151             :      * the system namespace are surely in the path and so we needn't even do
    2152             :      * list_member_oid() for them.
    2153             :      */
    2154        5586 :     oprnamespace = oprform->oprnamespace;
    2155        5586 :     if (oprnamespace != PG_CATALOG_NAMESPACE &&
    2156        1552 :         !list_member_oid(activeSearchPath, oprnamespace))
    2157         318 :         visible = false;
    2158             :     else
    2159             :     {
    2160             :         /*
    2161             :          * If it is in the path, it might still not be visible; it could be
    2162             :          * hidden by another operator of the same name and arguments earlier
    2163             :          * in the path.  So we must do a slow check to see if this is the same
    2164             :          * operator that would be found by OpernameGetOprid.
    2165             :          */
    2166        5268 :         char       *oprname = NameStr(oprform->oprname);
    2167             : 
    2168        5268 :         visible = (OpernameGetOprid(list_make1(makeString(oprname)),
    2169             :                                     oprform->oprleft, oprform->oprright)
    2170             :                    == oprid);
    2171             :     }
    2172             : 
    2173        5586 :     ReleaseSysCache(oprtup);
    2174             : 
    2175        5586 :     return visible;
    2176             : }
    2177             : 
    2178             : 
    2179             : /*
    2180             :  * OpclassnameGetOpcid
    2181             :  *      Try to resolve an unqualified index opclass name.
    2182             :  *      Returns OID if opclass found in search path, else InvalidOid.
    2183             :  *
    2184             :  * This is essentially the same as TypenameGetTypid, but we have to have
    2185             :  * an extra argument for the index AM OID.
    2186             :  */
    2187             : Oid
    2188       22792 : OpclassnameGetOpcid(Oid amid, const char *opcname)
    2189             : {
    2190             :     Oid         opcid;
    2191             :     ListCell   *l;
    2192             : 
    2193       22792 :     recomputeNamespacePath();
    2194             : 
    2195       23904 :     foreach(l, activeSearchPath)
    2196             :     {
    2197       23880 :         Oid         namespaceId = lfirst_oid(l);
    2198             : 
    2199       23880 :         if (namespaceId == myTempNamespace)
    2200         452 :             continue;           /* do not look in temp namespace */
    2201             : 
    2202       23428 :         opcid = GetSysCacheOid3(CLAAMNAMENSP, Anum_pg_opclass_oid,
    2203             :                                 ObjectIdGetDatum(amid),
    2204             :                                 PointerGetDatum(opcname),
    2205             :                                 ObjectIdGetDatum(namespaceId));
    2206       23428 :         if (OidIsValid(opcid))
    2207       22768 :             return opcid;
    2208             :     }
    2209             : 
    2210             :     /* Not found in path */
    2211          24 :     return InvalidOid;
    2212             : }
    2213             : 
    2214             : /*
    2215             :  * OpclassIsVisible
    2216             :  *      Determine whether an opclass (identified by OID) is visible in the
    2217             :  *      current search path.  Visible means "would be found by searching
    2218             :  *      for the unqualified opclass name".
    2219             :  */
    2220             : bool
    2221         858 : OpclassIsVisible(Oid opcid)
    2222             : {
    2223         858 :     return OpclassIsVisibleExt(opcid, NULL);
    2224             : }
    2225             : 
    2226             : /*
    2227             :  * OpclassIsVisibleExt
    2228             :  *      As above, but if the opclass isn't found and is_missing is not NULL,
    2229             :  *      then set *is_missing = true and return false instead of throwing
    2230             :  *      an error.  (Caller must initialize *is_missing = false.)
    2231             :  */
    2232             : static bool
    2233         876 : OpclassIsVisibleExt(Oid opcid, bool *is_missing)
    2234             : {
    2235             :     HeapTuple   opctup;
    2236             :     Form_pg_opclass opcform;
    2237             :     Oid         opcnamespace;
    2238             :     bool        visible;
    2239             : 
    2240         876 :     opctup = SearchSysCache1(CLAOID, ObjectIdGetDatum(opcid));
    2241         876 :     if (!HeapTupleIsValid(opctup))
    2242             :     {
    2243           0 :         if (is_missing != NULL)
    2244             :         {
    2245           0 :             *is_missing = true;
    2246           0 :             return false;
    2247             :         }
    2248           0 :         elog(ERROR, "cache lookup failed for opclass %u", opcid);
    2249             :     }
    2250         876 :     opcform = (Form_pg_opclass) GETSTRUCT(opctup);
    2251             : 
    2252         876 :     recomputeNamespacePath();
    2253             : 
    2254             :     /*
    2255             :      * Quick check: if it ain't in the path at all, it ain't visible. Items in
    2256             :      * the system namespace are surely in the path and so we needn't even do
    2257             :      * list_member_oid() for them.
    2258             :      */
    2259         876 :     opcnamespace = opcform->opcnamespace;
    2260         876 :     if (opcnamespace != PG_CATALOG_NAMESPACE &&
    2261         304 :         !list_member_oid(activeSearchPath, opcnamespace))
    2262          28 :         visible = false;
    2263             :     else
    2264             :     {
    2265             :         /*
    2266             :          * If it is in the path, it might still not be visible; it could be
    2267             :          * hidden by another opclass of the same name earlier in the path. So
    2268             :          * we must do a slow check to see if this opclass would be found by
    2269             :          * OpclassnameGetOpcid.
    2270             :          */
    2271         848 :         char       *opcname = NameStr(opcform->opcname);
    2272             : 
    2273         848 :         visible = (OpclassnameGetOpcid(opcform->opcmethod, opcname) == opcid);
    2274             :     }
    2275             : 
    2276         876 :     ReleaseSysCache(opctup);
    2277             : 
    2278         876 :     return visible;
    2279             : }
    2280             : 
    2281             : /*
    2282             :  * OpfamilynameGetOpfid
    2283             :  *      Try to resolve an unqualified index opfamily name.
    2284             :  *      Returns OID if opfamily found in search path, else InvalidOid.
    2285             :  *
    2286             :  * This is essentially the same as TypenameGetTypid, but we have to have
    2287             :  * an extra argument for the index AM OID.
    2288             :  */
    2289             : Oid
    2290        5424 : OpfamilynameGetOpfid(Oid amid, const char *opfname)
    2291             : {
    2292             :     Oid         opfid;
    2293             :     ListCell   *l;
    2294             : 
    2295        5424 :     recomputeNamespacePath();
    2296             : 
    2297       13454 :     foreach(l, activeSearchPath)
    2298             :     {
    2299       13442 :         Oid         namespaceId = lfirst_oid(l);
    2300             : 
    2301       13442 :         if (namespaceId == myTempNamespace)
    2302        3070 :             continue;           /* do not look in temp namespace */
    2303             : 
    2304       10372 :         opfid = GetSysCacheOid3(OPFAMILYAMNAMENSP, Anum_pg_opfamily_oid,
    2305             :                                 ObjectIdGetDatum(amid),
    2306             :                                 PointerGetDatum(opfname),
    2307             :                                 ObjectIdGetDatum(namespaceId));
    2308       10372 :         if (OidIsValid(opfid))
    2309        5412 :             return opfid;
    2310             :     }
    2311             : 
    2312             :     /* Not found in path */
    2313          12 :     return InvalidOid;
    2314             : }
    2315             : 
    2316             : /*
    2317             :  * OpfamilyIsVisible
    2318             :  *      Determine whether an opfamily (identified by OID) is visible in the
    2319             :  *      current search path.  Visible means "would be found by searching
    2320             :  *      for the unqualified opfamily name".
    2321             :  */
    2322             : bool
    2323        4102 : OpfamilyIsVisible(Oid opfid)
    2324             : {
    2325        4102 :     return OpfamilyIsVisibleExt(opfid, NULL);
    2326             : }
    2327             : 
    2328             : /*
    2329             :  * OpfamilyIsVisibleExt
    2330             :  *      As above, but if the opfamily isn't found and is_missing is not NULL,
    2331             :  *      then set *is_missing = true and return false instead of throwing
    2332             :  *      an error.  (Caller must initialize *is_missing = false.)
    2333             :  */
    2334             : static bool
    2335        4420 : OpfamilyIsVisibleExt(Oid opfid, bool *is_missing)
    2336             : {
    2337             :     HeapTuple   opftup;
    2338             :     Form_pg_opfamily opfform;
    2339             :     Oid         opfnamespace;
    2340             :     bool        visible;
    2341             : 
    2342        4420 :     opftup = SearchSysCache1(OPFAMILYOID, ObjectIdGetDatum(opfid));
    2343        4420 :     if (!HeapTupleIsValid(opftup))
    2344             :     {
    2345           0 :         if (is_missing != NULL)
    2346             :         {
    2347           0 :             *is_missing = true;
    2348           0 :             return false;
    2349             :         }
    2350           0 :         elog(ERROR, "cache lookup failed for opfamily %u", opfid);
    2351             :     }
    2352        4420 :     opfform = (Form_pg_opfamily) GETSTRUCT(opftup);
    2353             : 
    2354        4420 :     recomputeNamespacePath();
    2355             : 
    2356             :     /*
    2357             :      * Quick check: if it ain't in the path at all, it ain't visible. Items in
    2358             :      * the system namespace are surely in the path and so we needn't even do
    2359             :      * list_member_oid() for them.
    2360             :      */
    2361        4420 :     opfnamespace = opfform->opfnamespace;
    2362        4420 :     if (opfnamespace != PG_CATALOG_NAMESPACE &&
    2363        4078 :         !list_member_oid(activeSearchPath, opfnamespace))
    2364         196 :         visible = false;
    2365             :     else
    2366             :     {
    2367             :         /*
    2368             :          * If it is in the path, it might still not be visible; it could be
    2369             :          * hidden by another opfamily of the same name earlier in the path. So
    2370             :          * we must do a slow check to see if this opfamily would be found by
    2371             :          * OpfamilynameGetOpfid.
    2372             :          */
    2373        4224 :         char       *opfname = NameStr(opfform->opfname);
    2374             : 
    2375        4224 :         visible = (OpfamilynameGetOpfid(opfform->opfmethod, opfname) == opfid);
    2376             :     }
    2377             : 
    2378        4420 :     ReleaseSysCache(opftup);
    2379             : 
    2380        4420 :     return visible;
    2381             : }
    2382             : 
    2383             : /*
    2384             :  * lookup_collation
    2385             :  *      If there's a collation of the given name/namespace, and it works
    2386             :  *      with the given encoding, return its OID.  Else return InvalidOid.
    2387             :  */
    2388             : static Oid
    2389       12620 : lookup_collation(const char *collname, Oid collnamespace, int32 encoding)
    2390             : {
    2391             :     Oid         collid;
    2392             :     HeapTuple   colltup;
    2393             :     Form_pg_collation collform;
    2394             : 
    2395             :     /* Check for encoding-specific entry (exact match) */
    2396       12620 :     collid = GetSysCacheOid3(COLLNAMEENCNSP, Anum_pg_collation_oid,
    2397             :                              PointerGetDatum(collname),
    2398             :                              Int32GetDatum(encoding),
    2399             :                              ObjectIdGetDatum(collnamespace));
    2400       12620 :     if (OidIsValid(collid))
    2401         312 :         return collid;
    2402             : 
    2403             :     /*
    2404             :      * Check for any-encoding entry.  This takes a bit more work: while libc
    2405             :      * collations with collencoding = -1 do work with all encodings, ICU
    2406             :      * collations only work with certain encodings, so we have to check that
    2407             :      * aspect before deciding it's a match.
    2408             :      */
    2409       12308 :     colltup = SearchSysCache3(COLLNAMEENCNSP,
    2410             :                               PointerGetDatum(collname),
    2411             :                               Int32GetDatum(-1),
    2412             :                               ObjectIdGetDatum(collnamespace));
    2413       12308 :     if (!HeapTupleIsValid(colltup))
    2414        1384 :         return InvalidOid;
    2415       10924 :     collform = (Form_pg_collation) GETSTRUCT(colltup);
    2416       10924 :     if (collform->collprovider == COLLPROVIDER_ICU)
    2417             :     {
    2418        1364 :         if (is_encoding_supported_by_icu(encoding))
    2419        1364 :             collid = collform->oid;
    2420             :         else
    2421           0 :             collid = InvalidOid;
    2422             :     }
    2423             :     else
    2424             :     {
    2425        9560 :         collid = collform->oid;
    2426             :     }
    2427       10924 :     ReleaseSysCache(colltup);
    2428       10924 :     return collid;
    2429             : }
    2430             : 
    2431             : /*
    2432             :  * CollationGetCollid
    2433             :  *      Try to resolve an unqualified collation name.
    2434             :  *      Returns OID if collation found in search path, else InvalidOid.
    2435             :  *
    2436             :  * Note that this will only find collations that work with the current
    2437             :  * database's encoding.
    2438             :  */
    2439             : Oid
    2440         384 : CollationGetCollid(const char *collname)
    2441             : {
    2442         384 :     int32       dbencoding = GetDatabaseEncoding();
    2443             :     ListCell   *l;
    2444             : 
    2445         384 :     recomputeNamespacePath();
    2446             : 
    2447         602 :     foreach(l, activeSearchPath)
    2448             :     {
    2449         602 :         Oid         namespaceId = lfirst_oid(l);
    2450             :         Oid         collid;
    2451             : 
    2452         602 :         if (namespaceId == myTempNamespace)
    2453         148 :             continue;           /* do not look in temp namespace */
    2454             : 
    2455         454 :         collid = lookup_collation(collname, namespaceId, dbencoding);
    2456         454 :         if (OidIsValid(collid))
    2457         384 :             return collid;
    2458             :     }
    2459             : 
    2460             :     /* Not found in path */
    2461           0 :     return InvalidOid;
    2462             : }
    2463             : 
    2464             : /*
    2465             :  * CollationIsVisible
    2466             :  *      Determine whether a collation (identified by OID) is visible in the
    2467             :  *      current search path.  Visible means "would be found by searching
    2468             :  *      for the unqualified collation name".
    2469             :  *
    2470             :  * Note that only collations that work with the current database's encoding
    2471             :  * will be considered visible.
    2472             :  */
    2473             : bool
    2474         384 : CollationIsVisible(Oid collid)
    2475             : {
    2476         384 :     return CollationIsVisibleExt(collid, NULL);
    2477             : }
    2478             : 
    2479             : /*
    2480             :  * CollationIsVisibleExt
    2481             :  *      As above, but if the collation isn't found and is_missing is not NULL,
    2482             :  *      then set *is_missing = true and return false instead of throwing
    2483             :  *      an error.  (Caller must initialize *is_missing = false.)
    2484             :  */
    2485             : static bool
    2486         384 : CollationIsVisibleExt(Oid collid, bool *is_missing)
    2487             : {
    2488             :     HeapTuple   colltup;
    2489             :     Form_pg_collation collform;
    2490             :     Oid         collnamespace;
    2491             :     bool        visible;
    2492             : 
    2493         384 :     colltup = SearchSysCache1(COLLOID, ObjectIdGetDatum(collid));
    2494         384 :     if (!HeapTupleIsValid(colltup))
    2495             :     {
    2496           0 :         if (is_missing != NULL)
    2497             :         {
    2498           0 :             *is_missing = true;
    2499           0 :             return false;
    2500             :         }
    2501           0 :         elog(ERROR, "cache lookup failed for collation %u", collid);
    2502             :     }
    2503         384 :     collform = (Form_pg_collation) GETSTRUCT(colltup);
    2504             : 
    2505         384 :     recomputeNamespacePath();
    2506             : 
    2507             :     /*
    2508             :      * Quick check: if it ain't in the path at all, it ain't visible. Items in
    2509             :      * the system namespace are surely in the path and so we needn't even do
    2510             :      * list_member_oid() for them.
    2511             :      */
    2512         384 :     collnamespace = collform->collnamespace;
    2513         384 :     if (collnamespace != PG_CATALOG_NAMESPACE &&
    2514          70 :         !list_member_oid(activeSearchPath, collnamespace))
    2515           0 :         visible = false;
    2516             :     else
    2517             :     {
    2518             :         /*
    2519             :          * If it is in the path, it might still not be visible; it could be
    2520             :          * hidden by another collation of the same name earlier in the path,
    2521             :          * or it might not work with the current DB encoding.  So we must do a
    2522             :          * slow check to see if this collation would be found by
    2523             :          * CollationGetCollid.
    2524             :          */
    2525         384 :         char       *collname = NameStr(collform->collname);
    2526             : 
    2527         384 :         visible = (CollationGetCollid(collname) == collid);
    2528             :     }
    2529             : 
    2530         384 :     ReleaseSysCache(colltup);
    2531             : 
    2532         384 :     return visible;
    2533             : }
    2534             : 
    2535             : 
    2536             : /*
    2537             :  * ConversionGetConid
    2538             :  *      Try to resolve an unqualified conversion name.
    2539             :  *      Returns OID if conversion found in search path, else InvalidOid.
    2540             :  *
    2541             :  * This is essentially the same as RelnameGetRelid.
    2542             :  */
    2543             : Oid
    2544          18 : ConversionGetConid(const char *conname)
    2545             : {
    2546             :     Oid         conid;
    2547             :     ListCell   *l;
    2548             : 
    2549          18 :     recomputeNamespacePath();
    2550             : 
    2551          36 :     foreach(l, activeSearchPath)
    2552             :     {
    2553          36 :         Oid         namespaceId = lfirst_oid(l);
    2554             : 
    2555          36 :         if (namespaceId == myTempNamespace)
    2556           0 :             continue;           /* do not look in temp namespace */
    2557             : 
    2558          36 :         conid = GetSysCacheOid2(CONNAMENSP, Anum_pg_conversion_oid,
    2559             :                                 PointerGetDatum(conname),
    2560             :                                 ObjectIdGetDatum(namespaceId));
    2561          36 :         if (OidIsValid(conid))
    2562          18 :             return conid;
    2563             :     }
    2564             : 
    2565             :     /* Not found in path */
    2566           0 :     return InvalidOid;
    2567             : }
    2568             : 
    2569             : /*
    2570             :  * ConversionIsVisible
    2571             :  *      Determine whether a conversion (identified by OID) is visible in the
    2572             :  *      current search path.  Visible means "would be found by searching
    2573             :  *      for the unqualified conversion name".
    2574             :  */
    2575             : bool
    2576          30 : ConversionIsVisible(Oid conid)
    2577             : {
    2578          30 :     return ConversionIsVisibleExt(conid, NULL);
    2579             : }
    2580             : 
    2581             : /*
    2582             :  * ConversionIsVisibleExt
    2583             :  *      As above, but if the conversion isn't found and is_missing is not NULL,
    2584             :  *      then set *is_missing = true and return false instead of throwing
    2585             :  *      an error.  (Caller must initialize *is_missing = false.)
    2586             :  */
    2587             : static bool
    2588          30 : ConversionIsVisibleExt(Oid conid, bool *is_missing)
    2589             : {
    2590             :     HeapTuple   contup;
    2591             :     Form_pg_conversion conform;
    2592             :     Oid         connamespace;
    2593             :     bool        visible;
    2594             : 
    2595          30 :     contup = SearchSysCache1(CONVOID, ObjectIdGetDatum(conid));
    2596          30 :     if (!HeapTupleIsValid(contup))
    2597             :     {
    2598           0 :         if (is_missing != NULL)
    2599             :         {
    2600           0 :             *is_missing = true;
    2601           0 :             return false;
    2602             :         }
    2603           0 :         elog(ERROR, "cache lookup failed for conversion %u", conid);
    2604             :     }
    2605          30 :     conform = (Form_pg_conversion) GETSTRUCT(contup);
    2606             : 
    2607          30 :     recomputeNamespacePath();
    2608             : 
    2609             :     /*
    2610             :      * Quick check: if it ain't in the path at all, it ain't visible. Items in
    2611             :      * the system namespace are surely in the path and so we needn't even do
    2612             :      * list_member_oid() for them.
    2613             :      */
    2614          30 :     connamespace = conform->connamespace;
    2615          30 :     if (connamespace != PG_CATALOG_NAMESPACE &&
    2616          30 :         !list_member_oid(activeSearchPath, connamespace))
    2617          12 :         visible = false;
    2618             :     else
    2619             :     {
    2620             :         /*
    2621             :          * If it is in the path, it might still not be visible; it could be
    2622             :          * hidden by another conversion of the same name earlier in the path.
    2623             :          * So we must do a slow check to see if this conversion would be found
    2624             :          * by ConversionGetConid.
    2625             :          */
    2626          18 :         char       *conname = NameStr(conform->conname);
    2627             : 
    2628          18 :         visible = (ConversionGetConid(conname) == conid);
    2629             :     }
    2630             : 
    2631          30 :     ReleaseSysCache(contup);
    2632             : 
    2633          30 :     return visible;
    2634             : }
    2635             : 
    2636             : /*
    2637             :  * get_statistics_object_oid - find a statistics object by possibly qualified name
    2638             :  *
    2639             :  * If not found, returns InvalidOid if missing_ok, else throws error
    2640             :  */
    2641             : Oid
    2642         344 : get_statistics_object_oid(List *names, bool missing_ok)
    2643             : {
    2644             :     char       *schemaname;
    2645             :     char       *stats_name;
    2646             :     Oid         namespaceId;
    2647         344 :     Oid         stats_oid = InvalidOid;
    2648             :     ListCell   *l;
    2649             : 
    2650             :     /* deconstruct the name list */
    2651         344 :     DeconstructQualifiedName(names, &schemaname, &stats_name);
    2652             : 
    2653         344 :     if (schemaname)
    2654             :     {
    2655             :         /* use exact schema given */
    2656          36 :         namespaceId = LookupExplicitNamespace(schemaname, missing_ok);
    2657          36 :         if (missing_ok && !OidIsValid(namespaceId))
    2658           0 :             stats_oid = InvalidOid;
    2659             :         else
    2660          36 :             stats_oid = GetSysCacheOid2(STATEXTNAMENSP, Anum_pg_statistic_ext_oid,
    2661             :                                         PointerGetDatum(stats_name),
    2662             :                                         ObjectIdGetDatum(namespaceId));
    2663             :     }
    2664             :     else
    2665             :     {
    2666             :         /* search for it in search path */
    2667         308 :         recomputeNamespacePath();
    2668             : 
    2669         826 :         foreach(l, activeSearchPath)
    2670             :         {
    2671         808 :             namespaceId = lfirst_oid(l);
    2672             : 
    2673         808 :             if (namespaceId == myTempNamespace)
    2674         192 :                 continue;       /* do not look in temp namespace */
    2675         616 :             stats_oid = GetSysCacheOid2(STATEXTNAMENSP, Anum_pg_statistic_ext_oid,
    2676             :                                         PointerGetDatum(stats_name),
    2677             :                                         ObjectIdGetDatum(namespaceId));
    2678         616 :             if (OidIsValid(stats_oid))
    2679         290 :                 break;
    2680             :         }
    2681             :     }
    2682             : 
    2683         344 :     if (!OidIsValid(stats_oid) && !missing_ok)
    2684          12 :         ereport(ERROR,
    2685             :                 (errcode(ERRCODE_UNDEFINED_OBJECT),
    2686             :                  errmsg("statistics object \"%s\" does not exist",
    2687             :                         NameListToString(names))));
    2688             : 
    2689         332 :     return stats_oid;
    2690             : }
    2691             : 
    2692             : /*
    2693             :  * StatisticsObjIsVisible
    2694             :  *      Determine whether a statistics object (identified by OID) is visible in
    2695             :  *      the current search path.  Visible means "would be found by searching
    2696             :  *      for the unqualified statistics object name".
    2697             :  */
    2698             : bool
    2699         352 : StatisticsObjIsVisible(Oid stxid)
    2700             : {
    2701         352 :     return StatisticsObjIsVisibleExt(stxid, NULL);
    2702             : }
    2703             : 
    2704             : /*
    2705             :  * StatisticsObjIsVisibleExt
    2706             :  *      As above, but if the statistics object isn't found and is_missing is
    2707             :  *      not NULL, then set *is_missing = true and return false instead of
    2708             :  *      throwing an error.  (Caller must initialize *is_missing = false.)
    2709             :  */
    2710             : static bool
    2711         724 : StatisticsObjIsVisibleExt(Oid stxid, bool *is_missing)
    2712             : {
    2713             :     HeapTuple   stxtup;
    2714             :     Form_pg_statistic_ext stxform;
    2715             :     Oid         stxnamespace;
    2716             :     bool        visible;
    2717             : 
    2718         724 :     stxtup = SearchSysCache1(STATEXTOID, ObjectIdGetDatum(stxid));
    2719         724 :     if (!HeapTupleIsValid(stxtup))
    2720             :     {
    2721           0 :         if (is_missing != NULL)
    2722             :         {
    2723           0 :             *is_missing = true;
    2724           0 :             return false;
    2725             :         }
    2726           0 :         elog(ERROR, "cache lookup failed for statistics object %u", stxid);
    2727             :     }
    2728         724 :     stxform = (Form_pg_statistic_ext) GETSTRUCT(stxtup);
    2729             : 
    2730         724 :     recomputeNamespacePath();
    2731             : 
    2732             :     /*
    2733             :      * Quick check: if it ain't in the path at all, it ain't visible. Items in
    2734             :      * the system namespace are surely in the path and so we needn't even do
    2735             :      * list_member_oid() for them.
    2736             :      */
    2737         724 :     stxnamespace = stxform->stxnamespace;
    2738         724 :     if (stxnamespace != PG_CATALOG_NAMESPACE &&
    2739         724 :         !list_member_oid(activeSearchPath, stxnamespace))
    2740          78 :         visible = false;
    2741             :     else
    2742             :     {
    2743             :         /*
    2744             :          * If it is in the path, it might still not be visible; it could be
    2745             :          * hidden by another statistics object of the same name earlier in the
    2746             :          * path. So we must do a slow check for conflicting objects.
    2747             :          */
    2748         646 :         char       *stxname = NameStr(stxform->stxname);
    2749             :         ListCell   *l;
    2750             : 
    2751         646 :         visible = false;
    2752        1948 :         foreach(l, activeSearchPath)
    2753             :         {
    2754        1936 :             Oid         namespaceId = lfirst_oid(l);
    2755             : 
    2756        1936 :             if (namespaceId == myTempNamespace)
    2757         560 :                 continue;       /* do not look in temp namespace */
    2758             : 
    2759        1376 :             if (namespaceId == stxnamespace)
    2760             :             {
    2761             :                 /* Found it first in path */
    2762         634 :                 visible = true;
    2763         634 :                 break;
    2764             :             }
    2765         742 :             if (SearchSysCacheExists2(STATEXTNAMENSP,
    2766             :                                       PointerGetDatum(stxname),
    2767             :                                       ObjectIdGetDatum(namespaceId)))
    2768             :             {
    2769             :                 /* Found something else first in path */
    2770           0 :                 break;
    2771             :             }
    2772             :         }
    2773             :     }
    2774             : 
    2775         724 :     ReleaseSysCache(stxtup);
    2776             : 
    2777         724 :     return visible;
    2778             : }
    2779             : 
    2780             : /*
    2781             :  * get_ts_parser_oid - find a TS parser by possibly qualified name
    2782             :  *
    2783             :  * If not found, returns InvalidOid if missing_ok, else throws error
    2784             :  */
    2785             : Oid
    2786        2912 : get_ts_parser_oid(List *names, bool missing_ok)
    2787             : {
    2788             :     char       *schemaname;
    2789             :     char       *parser_name;
    2790             :     Oid         namespaceId;
    2791        2912 :     Oid         prsoid = InvalidOid;
    2792             :     ListCell   *l;
    2793             : 
    2794             :     /* deconstruct the name list */
    2795        2912 :     DeconstructQualifiedName(names, &schemaname, &parser_name);
    2796             : 
    2797        2900 :     if (schemaname)
    2798             :     {
    2799             :         /* use exact schema given */
    2800          46 :         namespaceId = LookupExplicitNamespace(schemaname, missing_ok);
    2801          46 :         if (missing_ok && !OidIsValid(namespaceId))
    2802           6 :             prsoid = InvalidOid;
    2803             :         else
    2804          40 :             prsoid = GetSysCacheOid2(TSPARSERNAMENSP, Anum_pg_ts_parser_oid,
    2805             :                                      PointerGetDatum(parser_name),
    2806             :                                      ObjectIdGetDatum(namespaceId));
    2807             :     }
    2808             :     else
    2809             :     {
    2810             :         /* search for it in search path */
    2811        2854 :         recomputeNamespacePath();
    2812             : 
    2813        2936 :         foreach(l, activeSearchPath)
    2814             :         {
    2815        2912 :             namespaceId = lfirst_oid(l);
    2816             : 
    2817        2912 :             if (namespaceId == myTempNamespace)
    2818           0 :                 continue;       /* do not look in temp namespace */
    2819             : 
    2820        2912 :             prsoid = GetSysCacheOid2(TSPARSERNAMENSP, Anum_pg_ts_parser_oid,
    2821             :                                      PointerGetDatum(parser_name),
    2822             :                                      ObjectIdGetDatum(namespaceId));
    2823        2912 :             if (OidIsValid(prsoid))
    2824        2830 :                 break;
    2825             :         }
    2826             :     }
    2827             : 
    2828        2900 :     if (!OidIsValid(prsoid) && !missing_ok)
    2829          30 :         ereport(ERROR,
    2830             :                 (errcode(ERRCODE_UNDEFINED_OBJECT),
    2831             :                  errmsg("text search parser \"%s\" does not exist",
    2832             :                         NameListToString(names))));
    2833             : 
    2834        2870 :     return prsoid;
    2835             : }
    2836             : 
    2837             : /*
    2838             :  * TSParserIsVisible
    2839             :  *      Determine whether a parser (identified by OID) is visible in the
    2840             :  *      current search path.  Visible means "would be found by searching
    2841             :  *      for the unqualified parser name".
    2842             :  */
    2843             : bool
    2844          30 : TSParserIsVisible(Oid prsId)
    2845             : {
    2846          30 :     return TSParserIsVisibleExt(prsId, NULL);
    2847             : }
    2848             : 
    2849             : /*
    2850             :  * TSParserIsVisibleExt
    2851             :  *      As above, but if the parser isn't found and is_missing is not NULL,
    2852             :  *      then set *is_missing = true and return false instead of throwing
    2853             :  *      an error.  (Caller must initialize *is_missing = false.)
    2854             :  */
    2855             : static bool
    2856          30 : TSParserIsVisibleExt(Oid prsId, bool *is_missing)
    2857             : {
    2858             :     HeapTuple   tup;
    2859             :     Form_pg_ts_parser form;
    2860             :     Oid         namespace;
    2861             :     bool        visible;
    2862             : 
    2863          30 :     tup = SearchSysCache1(TSPARSEROID, ObjectIdGetDatum(prsId));
    2864          30 :     if (!HeapTupleIsValid(tup))
    2865             :     {
    2866           0 :         if (is_missing != NULL)
    2867             :         {
    2868           0 :             *is_missing = true;
    2869           0 :             return false;
    2870             :         }
    2871           0 :         elog(ERROR, "cache lookup failed for text search parser %u", prsId);
    2872             :     }
    2873          30 :     form = (Form_pg_ts_parser) GETSTRUCT(tup);
    2874             : 
    2875          30 :     recomputeNamespacePath();
    2876             : 
    2877             :     /*
    2878             :      * Quick check: if it ain't in the path at all, it ain't visible. Items in
    2879             :      * the system namespace are surely in the path and so we needn't even do
    2880             :      * list_member_oid() for them.
    2881             :      */
    2882          30 :     namespace = form->prsnamespace;
    2883          30 :     if (namespace != PG_CATALOG_NAMESPACE &&
    2884          30 :         !list_member_oid(activeSearchPath, namespace))
    2885          12 :         visible = false;
    2886             :     else
    2887             :     {
    2888             :         /*
    2889             :          * If it is in the path, it might still not be visible; it could be
    2890             :          * hidden by another parser of the same name earlier in the path. So
    2891             :          * we must do a slow check for conflicting parsers.
    2892             :          */
    2893          18 :         char       *name = NameStr(form->prsname);
    2894             :         ListCell   *l;
    2895             : 
    2896          18 :         visible = false;
    2897          36 :         foreach(l, activeSearchPath)
    2898             :         {
    2899          36 :             Oid         namespaceId = lfirst_oid(l);
    2900             : 
    2901          36 :             if (namespaceId == myTempNamespace)
    2902           0 :                 continue;       /* do not look in temp namespace */
    2903             : 
    2904          36 :             if (namespaceId == namespace)
    2905             :             {
    2906             :                 /* Found it first in path */
    2907          18 :                 visible = true;
    2908          18 :                 break;
    2909             :             }
    2910          18 :             if (SearchSysCacheExists2(TSPARSERNAMENSP,
    2911             :                                       PointerGetDatum(name),
    2912             :                                       ObjectIdGetDatum(namespaceId)))
    2913             :             {
    2914             :                 /* Found something else first in path */
    2915           0 :                 break;
    2916             :             }
    2917             :         }
    2918             :     }
    2919             : 
    2920          30 :     ReleaseSysCache(tup);
    2921             : 
    2922          30 :     return visible;
    2923             : }
    2924             : 
    2925             : /*
    2926             :  * get_ts_dict_oid - find a TS dictionary by possibly qualified name
    2927             :  *
    2928             :  * If not found, returns InvalidOid if missing_ok, else throws error
    2929             :  */
    2930             : Oid
    2931       12296 : get_ts_dict_oid(List *names, bool missing_ok)
    2932             : {
    2933             :     char       *schemaname;
    2934             :     char       *dict_name;
    2935             :     Oid         namespaceId;
    2936       12296 :     Oid         dictoid = InvalidOid;
    2937             :     ListCell   *l;
    2938             : 
    2939             :     /* deconstruct the name list */
    2940       12296 :     DeconstructQualifiedName(names, &schemaname, &dict_name);
    2941             : 
    2942       12284 :     if (schemaname)
    2943             :     {
    2944             :         /* use exact schema given */
    2945         104 :         namespaceId = LookupExplicitNamespace(schemaname, missing_ok);
    2946         104 :         if (missing_ok && !OidIsValid(namespaceId))
    2947           6 :             dictoid = InvalidOid;
    2948             :         else
    2949          98 :             dictoid = GetSysCacheOid2(TSDICTNAMENSP, Anum_pg_ts_dict_oid,
    2950             :                                       PointerGetDatum(dict_name),
    2951             :                                       ObjectIdGetDatum(namespaceId));
    2952             :     }
    2953             :     else
    2954             :     {
    2955             :         /* search for it in search path */
    2956       12180 :         recomputeNamespacePath();
    2957             : 
    2958       13054 :         foreach(l, activeSearchPath)
    2959             :         {
    2960       13024 :             namespaceId = lfirst_oid(l);
    2961             : 
    2962       13024 :             if (namespaceId == myTempNamespace)
    2963           0 :                 continue;       /* do not look in temp namespace */
    2964             : 
    2965       13024 :             dictoid = GetSysCacheOid2(TSDICTNAMENSP, Anum_pg_ts_dict_oid,
    2966             :                                       PointerGetDatum(dict_name),
    2967             :                                       ObjectIdGetDatum(namespaceId));
    2968       13024 :             if (OidIsValid(dictoid))
    2969       12150 :                 break;
    2970             :         }
    2971             :     }
    2972             : 
    2973       12284 :     if (!OidIsValid(dictoid) && !missing_ok)
    2974          30 :         ereport(ERROR,
    2975             :                 (errcode(ERRCODE_UNDEFINED_OBJECT),
    2976             :                  errmsg("text search dictionary \"%s\" does not exist",
    2977             :                         NameListToString(names))));
    2978             : 
    2979       12254 :     return dictoid;
    2980             : }
    2981             : 
    2982             : /*
    2983             :  * TSDictionaryIsVisible
    2984             :  *      Determine whether a dictionary (identified by OID) is visible in the
    2985             :  *      current search path.  Visible means "would be found by searching
    2986             :  *      for the unqualified dictionary name".
    2987             :  */
    2988             : bool
    2989        6134 : TSDictionaryIsVisible(Oid dictId)
    2990             : {
    2991        6134 :     return TSDictionaryIsVisibleExt(dictId, NULL);
    2992             : }
    2993             : 
    2994             : /*
    2995             :  * TSDictionaryIsVisibleExt
    2996             :  *      As above, but if the dictionary isn't found and is_missing is not NULL,
    2997             :  *      then set *is_missing = true and return false instead of throwing
    2998             :  *      an error.  (Caller must initialize *is_missing = false.)
    2999             :  */
    3000             : static bool
    3001        6134 : TSDictionaryIsVisibleExt(Oid dictId, bool *is_missing)
    3002             : {
    3003             :     HeapTuple   tup;
    3004             :     Form_pg_ts_dict form;
    3005             :     Oid         namespace;
    3006             :     bool        visible;
    3007             : 
    3008        6134 :     tup = SearchSysCache1(TSDICTOID, ObjectIdGetDatum(dictId));
    3009        6134 :     if (!HeapTupleIsValid(tup))
    3010             :     {
    3011           0 :         if (is_missing != NULL)
    3012             :         {
    3013           0 :             *is_missing = true;
    3014           0 :             return false;
    3015             :         }
    3016           0 :         elog(ERROR, "cache lookup failed for text search dictionary %u",
    3017             :              dictId);
    3018             :     }
    3019        6134 :     form = (Form_pg_ts_dict) GETSTRUCT(tup);
    3020             : 
    3021        6134 :     recomputeNamespacePath();
    3022             : 
    3023             :     /*
    3024             :      * Quick check: if it ain't in the path at all, it ain't visible. Items in
    3025             :      * the system namespace are surely in the path and so we needn't even do
    3026             :      * list_member_oid() for them.
    3027             :      */
    3028        6134 :     namespace = form->dictnamespace;
    3029        6134 :     if (namespace != PG_CATALOG_NAMESPACE &&
    3030         306 :         !list_member_oid(activeSearchPath, namespace))
    3031         282 :         visible = false;
    3032             :     else
    3033             :     {
    3034             :         /*
    3035             :          * If it is in the path, it might still not be visible; it could be
    3036             :          * hidden by another dictionary of the same name earlier in the path.
    3037             :          * So we must do a slow check for conflicting dictionaries.
    3038             :          */
    3039        5852 :         char       *name = NameStr(form->dictname);
    3040             :         ListCell   *l;
    3041             : 
    3042        5852 :         visible = false;
    3043        5876 :         foreach(l, activeSearchPath)
    3044             :         {
    3045        5876 :             Oid         namespaceId = lfirst_oid(l);
    3046             : 
    3047        5876 :             if (namespaceId == myTempNamespace)
    3048           0 :                 continue;       /* do not look in temp namespace */
    3049             : 
    3050        5876 :             if (namespaceId == namespace)
    3051             :             {
    3052             :                 /* Found it first in path */
    3053        5852 :                 visible = true;
    3054        5852 :                 break;
    3055             :             }
    3056          24 :             if (SearchSysCacheExists2(TSDICTNAMENSP,
    3057             :                                       PointerGetDatum(name),
    3058             :                                       ObjectIdGetDatum(namespaceId)))
    3059             :             {
    3060             :                 /* Found something else first in path */
    3061           0 :                 break;
    3062             :             }
    3063             :         }
    3064             :     }
    3065             : 
    3066        6134 :     ReleaseSysCache(tup);
    3067             : 
    3068        6134 :     return visible;
    3069             : }
    3070             : 
    3071             : /*
    3072             :  * get_ts_template_oid - find a TS template by possibly qualified name
    3073             :  *
    3074             :  * If not found, returns InvalidOid if missing_ok, else throws error
    3075             :  */
    3076             : Oid
    3077        3124 : get_ts_template_oid(List *names, bool missing_ok)
    3078             : {
    3079             :     char       *schemaname;
    3080             :     char       *template_name;
    3081             :     Oid         namespaceId;
    3082        3124 :     Oid         tmploid = InvalidOid;
    3083             :     ListCell   *l;
    3084             : 
    3085             :     /* deconstruct the name list */
    3086        3124 :     DeconstructQualifiedName(names, &schemaname, &template_name);
    3087             : 
    3088        3112 :     if (schemaname)
    3089             :     {
    3090             :         /* use exact schema given */
    3091          56 :         namespaceId = LookupExplicitNamespace(schemaname, missing_ok);
    3092          56 :         if (missing_ok && !OidIsValid(namespaceId))
    3093           6 :             tmploid = InvalidOid;
    3094             :         else
    3095          50 :             tmploid = GetSysCacheOid2(TSTEMPLATENAMENSP, Anum_pg_ts_template_oid,
    3096             :                                       PointerGetDatum(template_name),
    3097             :                                       ObjectIdGetDatum(namespaceId));
    3098             :     }
    3099             :     else
    3100             :     {
    3101             :         /* search for it in search path */
    3102        3056 :         recomputeNamespacePath();
    3103             : 
    3104        3140 :         foreach(l, activeSearchPath)
    3105             :         {
    3106        3116 :             namespaceId = lfirst_oid(l);
    3107             : 
    3108        3116 :             if (namespaceId == myTempNamespace)
    3109           0 :                 continue;       /* do not look in temp namespace */
    3110             : 
    3111        3116 :             tmploid = GetSysCacheOid2(TSTEMPLATENAMENSP, Anum_pg_ts_template_oid,
    3112             :                                       PointerGetDatum(template_name),
    3113             :                                       ObjectIdGetDatum(namespaceId));
    3114        3116 :             if (OidIsValid(tmploid))
    3115        3032 :                 break;
    3116             :         }
    3117             :     }
    3118             : 
    3119        3112 :     if (!OidIsValid(tmploid) && !missing_ok)
    3120          30 :         ereport(ERROR,
    3121             :                 (errcode(ERRCODE_UNDEFINED_OBJECT),
    3122             :                  errmsg("text search template \"%s\" does not exist",
    3123             :                         NameListToString(names))));
    3124             : 
    3125        3082 :     return tmploid;
    3126             : }
    3127             : 
    3128             : /*
    3129             :  * TSTemplateIsVisible
    3130             :  *      Determine whether a template (identified by OID) is visible in the
    3131             :  *      current search path.  Visible means "would be found by searching
    3132             :  *      for the unqualified template name".
    3133             :  */
    3134             : bool
    3135          30 : TSTemplateIsVisible(Oid tmplId)
    3136             : {
    3137          30 :     return TSTemplateIsVisibleExt(tmplId, NULL);
    3138             : }
    3139             : 
    3140             : /*
    3141             :  * TSTemplateIsVisibleExt
    3142             :  *      As above, but if the template isn't found and is_missing is not NULL,
    3143             :  *      then set *is_missing = true and return false instead of throwing
    3144             :  *      an error.  (Caller must initialize *is_missing = false.)
    3145             :  */
    3146             : static bool
    3147          30 : TSTemplateIsVisibleExt(Oid tmplId, bool *is_missing)
    3148             : {
    3149             :     HeapTuple   tup;
    3150             :     Form_pg_ts_template form;
    3151             :     Oid         namespace;
    3152             :     bool        visible;
    3153             : 
    3154          30 :     tup = SearchSysCache1(TSTEMPLATEOID, ObjectIdGetDatum(tmplId));
    3155          30 :     if (!HeapTupleIsValid(tup))
    3156             :     {
    3157           0 :         if (is_missing != NULL)
    3158             :         {
    3159           0 :             *is_missing = true;
    3160           0 :             return false;
    3161             :         }
    3162           0 :         elog(ERROR, "cache lookup failed for text search template %u", tmplId);
    3163             :     }
    3164          30 :     form = (Form_pg_ts_template) GETSTRUCT(tup);
    3165             : 
    3166          30 :     recomputeNamespacePath();
    3167             : 
    3168             :     /*
    3169             :      * Quick check: if it ain't in the path at all, it ain't visible. Items in
    3170             :      * the system namespace are surely in the path and so we needn't even do
    3171             :      * list_member_oid() for them.
    3172             :      */
    3173          30 :     namespace = form->tmplnamespace;
    3174          30 :     if (namespace != PG_CATALOG_NAMESPACE &&
    3175          30 :         !list_member_oid(activeSearchPath, namespace))
    3176          12 :         visible = false;
    3177             :     else
    3178             :     {
    3179             :         /*
    3180             :          * If it is in the path, it might still not be visible; it could be
    3181             :          * hidden by another template of the same name earlier in the path. So
    3182             :          * we must do a slow check for conflicting templates.
    3183             :          */
    3184          18 :         char       *name = NameStr(form->tmplname);
    3185             :         ListCell   *l;
    3186             : 
    3187          18 :         visible = false;
    3188          36 :         foreach(l, activeSearchPath)
    3189             :         {
    3190          36 :             Oid         namespaceId = lfirst_oid(l);
    3191             : 
    3192          36 :             if (namespaceId == myTempNamespace)
    3193           0 :                 continue;       /* do not look in temp namespace */
    3194             : 
    3195          36 :             if (namespaceId == namespace)
    3196             :             {
    3197             :                 /* Found it first in path */
    3198          18 :                 visible = true;
    3199          18 :                 break;
    3200             :             }
    3201          18 :             if (SearchSysCacheExists2(TSTEMPLATENAMENSP,
    3202             :                                       PointerGetDatum(name),
    3203             :                                       ObjectIdGetDatum(namespaceId)))
    3204             :             {
    3205             :                 /* Found something else first in path */
    3206           0 :                 break;
    3207             :             }
    3208             :         }
    3209             :     }
    3210             : 
    3211          30 :     ReleaseSysCache(tup);
    3212             : 
    3213          30 :     return visible;
    3214             : }
    3215             : 
    3216             : /*
    3217             :  * get_ts_config_oid - find a TS config by possibly qualified name
    3218             :  *
    3219             :  * If not found, returns InvalidOid if missing_ok, else throws error
    3220             :  */
    3221             : Oid
    3222       19134 : get_ts_config_oid(List *names, bool missing_ok)
    3223             : {
    3224             :     char       *schemaname;
    3225             :     char       *config_name;
    3226             :     Oid         namespaceId;
    3227       19134 :     Oid         cfgoid = InvalidOid;
    3228             :     ListCell   *l;
    3229             : 
    3230             :     /* deconstruct the name list */
    3231       19134 :     DeconstructQualifiedName(names, &schemaname, &config_name);
    3232             : 
    3233       19122 :     if (schemaname)
    3234             :     {
    3235             :         /* use exact schema given */
    3236        5746 :         namespaceId = LookupExplicitNamespace(schemaname, missing_ok);
    3237        5746 :         if (missing_ok && !OidIsValid(namespaceId))
    3238           6 :             cfgoid = InvalidOid;
    3239             :         else
    3240        5740 :             cfgoid = GetSysCacheOid2(TSCONFIGNAMENSP, Anum_pg_ts_config_oid,
    3241             :                                      PointerGetDatum(config_name),
    3242             :                                      ObjectIdGetDatum(namespaceId));
    3243             :     }
    3244             :     else
    3245             :     {
    3246             :         /* search for it in search path */
    3247       13376 :         recomputeNamespacePath();
    3248             : 
    3249       14526 :         foreach(l, activeSearchPath)
    3250             :         {
    3251       14470 :             namespaceId = lfirst_oid(l);
    3252             : 
    3253       14470 :             if (namespaceId == myTempNamespace)
    3254         702 :                 continue;       /* do not look in temp namespace */
    3255             : 
    3256       13768 :             cfgoid = GetSysCacheOid2(TSCONFIGNAMENSP, Anum_pg_ts_config_oid,
    3257             :                                      PointerGetDatum(config_name),
    3258             :                                      ObjectIdGetDatum(namespaceId));
    3259       13768 :             if (OidIsValid(cfgoid))
    3260       13320 :                 break;
    3261             :         }
    3262             :     }
    3263             : 
    3264       19122 :     if (!OidIsValid(cfgoid) && !missing_ok)
    3265          30 :         ereport(ERROR,
    3266             :                 (errcode(ERRCODE_UNDEFINED_OBJECT),
    3267             :                  errmsg("text search configuration \"%s\" does not exist",
    3268             :                         NameListToString(names))));
    3269             : 
    3270       19092 :     return cfgoid;
    3271             : }
    3272             : 
    3273             : /*
    3274             :  * TSConfigIsVisible
    3275             :  *      Determine whether a text search configuration (identified by OID)
    3276             :  *      is visible in the current search path.  Visible means "would be found
    3277             :  *      by searching for the unqualified text search configuration name".
    3278             :  */
    3279             : bool
    3280          46 : TSConfigIsVisible(Oid cfgid)
    3281             : {
    3282          46 :     return TSConfigIsVisibleExt(cfgid, NULL);
    3283             : }
    3284             : 
    3285             : /*
    3286             :  * TSConfigIsVisibleExt
    3287             :  *      As above, but if the configuration isn't found and is_missing is not
    3288             :  *      NULL, then set *is_missing = true and return false instead of throwing
    3289             :  *      an error.  (Caller must initialize *is_missing = false.)
    3290             :  */
    3291             : static bool
    3292          46 : TSConfigIsVisibleExt(Oid cfgid, bool *is_missing)
    3293             : {
    3294             :     HeapTuple   tup;
    3295             :     Form_pg_ts_config form;
    3296             :     Oid         namespace;
    3297             :     bool        visible;
    3298             : 
    3299          46 :     tup = SearchSysCache1(TSCONFIGOID, ObjectIdGetDatum(cfgid));
    3300          46 :     if (!HeapTupleIsValid(tup))
    3301             :     {
    3302           0 :         if (is_missing != NULL)
    3303             :         {
    3304           0 :             *is_missing = true;
    3305           0 :             return false;
    3306             :         }
    3307           0 :         elog(ERROR, "cache lookup failed for text search configuration %u",
    3308             :              cfgid);
    3309             :     }
    3310          46 :     form = (Form_pg_ts_config) GETSTRUCT(tup);
    3311             : 
    3312          46 :     recomputeNamespacePath();
    3313             : 
    3314             :     /*
    3315             :      * Quick check: if it ain't in the path at all, it ain't visible. Items in
    3316             :      * the system namespace are surely in the path and so we needn't even do
    3317             :      * list_member_oid() for them.
    3318             :      */
    3319          46 :     namespace = form->cfgnamespace;
    3320          46 :     if (namespace != PG_CATALOG_NAMESPACE &&
    3321          46 :         !list_member_oid(activeSearchPath, namespace))
    3322          16 :         visible = false;
    3323             :     else
    3324             :     {
    3325             :         /*
    3326             :          * If it is in the path, it might still not be visible; it could be
    3327             :          * hidden by another configuration of the same name earlier in the
    3328             :          * path. So we must do a slow check for conflicting configurations.
    3329             :          */
    3330          30 :         char       *name = NameStr(form->cfgname);
    3331             :         ListCell   *l;
    3332             : 
    3333          30 :         visible = false;
    3334          60 :         foreach(l, activeSearchPath)
    3335             :         {
    3336          60 :             Oid         namespaceId = lfirst_oid(l);
    3337             : 
    3338          60 :             if (namespaceId == myTempNamespace)
    3339           0 :                 continue;       /* do not look in temp namespace */
    3340             : 
    3341          60 :             if (namespaceId == namespace)
    3342             :             {
    3343             :                 /* Found it first in path */
    3344          30 :                 visible = true;
    3345          30 :                 break;
    3346             :             }
    3347          30 :             if (SearchSysCacheExists2(TSCONFIGNAMENSP,
    3348             :                                       PointerGetDatum(name),
    3349             :                                       ObjectIdGetDatum(namespaceId)))
    3350             :             {
    3351             :                 /* Found something else first in path */
    3352           0 :                 break;
    3353             :             }
    3354             :         }
    3355             :     }
    3356             : 
    3357          46 :     ReleaseSysCache(tup);
    3358             : 
    3359          46 :     return visible;
    3360             : }
    3361             : 
    3362             : 
    3363             : /*
    3364             :  * DeconstructQualifiedName
    3365             :  *      Given a possibly-qualified name expressed as a list of String nodes,
    3366             :  *      extract the schema name and object name.
    3367             :  *
    3368             :  * *nspname_p is set to NULL if there is no explicit schema name.
    3369             :  */
    3370             : void
    3371     2142174 : DeconstructQualifiedName(const List *names,
    3372             :                          char **nspname_p,
    3373             :                          char **objname_p)
    3374             : {
    3375             :     char       *catalogname;
    3376     2142174 :     char       *schemaname = NULL;
    3377     2142174 :     char       *objname = NULL;
    3378             : 
    3379     2142174 :     switch (list_length(names))
    3380             :     {
    3381     1665224 :         case 1:
    3382     1665224 :             objname = strVal(linitial(names));
    3383     1665224 :             break;
    3384      476842 :         case 2:
    3385      476842 :             schemaname = strVal(linitial(names));
    3386      476842 :             objname = strVal(lsecond(names));
    3387      476842 :             break;
    3388         102 :         case 3:
    3389         102 :             catalogname = strVal(linitial(names));
    3390         102 :             schemaname = strVal(lsecond(names));
    3391         102 :             objname = strVal(lthird(names));
    3392             : 
    3393             :             /*
    3394             :              * We check the catalog name and then ignore it.
    3395             :              */
    3396         102 :             if (strcmp(catalogname, get_database_name(MyDatabaseId)) != 0)
    3397         102 :                 ereport(ERROR,
    3398             :                         (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
    3399             :                          errmsg("cross-database references are not implemented: %s",
    3400             :                                 NameListToString(names))));
    3401           0 :             break;
    3402           6 :         default:
    3403           6 :             ereport(ERROR,
    3404             :                     (errcode(ERRCODE_SYNTAX_ERROR),
    3405             :                      errmsg("improper qualified name (too many dotted names): %s",
    3406             :                             NameListToString(names))));
    3407             :             break;
    3408             :     }
    3409             : 
    3410     2142066 :     *nspname_p = schemaname;
    3411     2142066 :     *objname_p = objname;
    3412     2142066 : }
    3413             : 
    3414             : /*
    3415             :  * LookupNamespaceNoError
    3416             :  *      Look up a schema name.
    3417             :  *
    3418             :  * Returns the namespace OID, or InvalidOid if not found.
    3419             :  *
    3420             :  * Note this does NOT perform any permissions check --- callers are
    3421             :  * responsible for being sure that an appropriate check is made.
    3422             :  * In the majority of cases LookupExplicitNamespace is preferable.
    3423             :  */
    3424             : Oid
    3425         342 : LookupNamespaceNoError(const char *nspname)
    3426             : {
    3427             :     /* check for pg_temp alias */
    3428         342 :     if (strcmp(nspname, "pg_temp") == 0)
    3429             :     {
    3430           0 :         if (OidIsValid(myTempNamespace))
    3431             :         {
    3432           0 :             InvokeNamespaceSearchHook(myTempNamespace, true);
    3433           0 :             return myTempNamespace;
    3434             :         }
    3435             : 
    3436             :         /*
    3437             :          * Since this is used only for looking up existing objects, there is
    3438             :          * no point in trying to initialize the temp namespace here; and doing
    3439             :          * so might create problems for some callers. Just report "not found".
    3440             :          */
    3441           0 :         return InvalidOid;
    3442             :     }
    3443             : 
    3444         342 :     return get_namespace_oid(nspname, true);
    3445             : }
    3446             : 
    3447             : /*
    3448             :  * LookupExplicitNamespace
    3449             :  *      Process an explicitly-specified schema name: look up the schema
    3450             :  *      and verify we have USAGE (lookup) rights in it.
    3451             :  *
    3452             :  * Returns the namespace OID
    3453             :  */
    3454             : Oid
    3455      752454 : LookupExplicitNamespace(const char *nspname, bool missing_ok)
    3456             : {
    3457             :     Oid         namespaceId;
    3458             :     AclResult   aclresult;
    3459             : 
    3460             :     /* check for pg_temp alias */
    3461      752454 :     if (strcmp(nspname, "pg_temp") == 0)
    3462             :     {
    3463         344 :         if (OidIsValid(myTempNamespace))
    3464         344 :             return myTempNamespace;
    3465             : 
    3466             :         /*
    3467             :          * Since this is used only for looking up existing objects, there is
    3468             :          * no point in trying to initialize the temp namespace here; and doing
    3469             :          * so might create problems for some callers --- just fall through.
    3470             :          */
    3471             :     }
    3472             : 
    3473      752110 :     namespaceId = get_namespace_oid(nspname, missing_ok);
    3474      751992 :     if (missing_ok && !OidIsValid(namespaceId))
    3475         336 :         return InvalidOid;
    3476             : 
    3477      751656 :     aclresult = object_aclcheck(NamespaceRelationId, namespaceId, GetUserId(), ACL_USAGE);
    3478      751656 :     if (aclresult != ACLCHECK_OK)
    3479           8 :         aclcheck_error(aclresult, OBJECT_SCHEMA,
    3480             :                        nspname);
    3481             :     /* Schema search hook for this lookup */
    3482      751648 :     InvokeNamespaceSearchHook(namespaceId, true);
    3483             : 
    3484      751648 :     return namespaceId;
    3485             : }
    3486             : 
    3487             : /*
    3488             :  * LookupCreationNamespace
    3489             :  *      Look up the schema and verify we have CREATE rights on it.
    3490             :  *
    3491             :  * This is just like LookupExplicitNamespace except for the different
    3492             :  * permission check, and that we are willing to create pg_temp if needed.
    3493             :  *
    3494             :  * Note: calling this may result in a CommandCounterIncrement operation,
    3495             :  * if we have to create or clean out the temp namespace.
    3496             :  */
    3497             : Oid
    3498         486 : LookupCreationNamespace(const char *nspname)
    3499             : {
    3500             :     Oid         namespaceId;
    3501             :     AclResult   aclresult;
    3502             : 
    3503             :     /* check for pg_temp alias */
    3504         486 :     if (strcmp(nspname, "pg_temp") == 0)
    3505             :     {
    3506             :         /* Initialize temp namespace */
    3507         152 :         AccessTempTableNamespace(false);
    3508         152 :         return myTempNamespace;
    3509             :     }
    3510             : 
    3511         334 :     namespaceId = get_namespace_oid(nspname, false);
    3512             : 
    3513         332 :     aclresult = object_aclcheck(NamespaceRelationId, namespaceId, GetUserId(), ACL_CREATE);
    3514         332 :     if (aclresult != ACLCHECK_OK)
    3515           0 :         aclcheck_error(aclresult, OBJECT_SCHEMA,
    3516             :                        nspname);
    3517             : 
    3518         332 :     return namespaceId;
    3519             : }
    3520             : 
    3521             : /*
    3522             :  * Common checks on switching namespaces.
    3523             :  *
    3524             :  * We complain if either the old or new namespaces is a temporary schema
    3525             :  * (or temporary toast schema), or if either the old or new namespaces is the
    3526             :  * TOAST schema.
    3527             :  */
    3528             : void
    3529         540 : CheckSetNamespace(Oid oldNspOid, Oid nspOid)
    3530             : {
    3531             :     /* disallow renaming into or out of temp schemas */
    3532         540 :     if (isAnyTempNamespace(nspOid) || isAnyTempNamespace(oldNspOid))
    3533           0 :         ereport(ERROR,
    3534             :                 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
    3535             :                  errmsg("cannot move objects into or out of temporary schemas")));
    3536             : 
    3537             :     /* same for TOAST schema */
    3538         540 :     if (nspOid == PG_TOAST_NAMESPACE || oldNspOid == PG_TOAST_NAMESPACE)
    3539           0 :         ereport(ERROR,
    3540             :                 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
    3541             :                  errmsg("cannot move objects into or out of TOAST schema")));
    3542         540 : }
    3543             : 
    3544             : /*
    3545             :  * QualifiedNameGetCreationNamespace
    3546             :  *      Given a possibly-qualified name for an object (in List-of-Strings
    3547             :  *      format), determine what namespace the object should be created in.
    3548             :  *      Also extract and return the object name (last component of list).
    3549             :  *
    3550             :  * Note: this does not apply any permissions check.  Callers must check
    3551             :  * for CREATE rights on the selected namespace when appropriate.
    3552             :  *
    3553             :  * Note: calling this may result in a CommandCounterIncrement operation,
    3554             :  * if we have to create or clean out the temp namespace.
    3555             :  */
    3556             : Oid
    3557       39386 : QualifiedNameGetCreationNamespace(const List *names, char **objname_p)
    3558             : {
    3559             :     char       *schemaname;
    3560             :     Oid         namespaceId;
    3561             : 
    3562             :     /* deconstruct the name list */
    3563       39386 :     DeconstructQualifiedName(names, &schemaname, objname_p);
    3564             : 
    3565       39386 :     if (schemaname)
    3566             :     {
    3567             :         /* check for pg_temp alias */
    3568        1636 :         if (strcmp(schemaname, "pg_temp") == 0)
    3569             :         {
    3570             :             /* Initialize temp namespace */
    3571         306 :             AccessTempTableNamespace(false);
    3572         306 :             return myTempNamespace;
    3573             :         }
    3574             :         /* use exact schema given */
    3575        1330 :         namespaceId = get_namespace_oid(schemaname, false);
    3576             :         /* we do not check for USAGE rights here! */
    3577             :     }
    3578             :     else
    3579             :     {
    3580             :         /* use the default creation namespace */
    3581       37750 :         recomputeNamespacePath();
    3582       37750 :         if (activeTempCreationPending)
    3583             :         {
    3584             :             /* Need to initialize temp namespace */
    3585           0 :             AccessTempTableNamespace(true);
    3586           0 :             return myTempNamespace;
    3587             :         }
    3588       37750 :         namespaceId = activeCreationNamespace;
    3589       37750 :         if (!OidIsValid(namespaceId))
    3590           0 :             ereport(ERROR,
    3591             :                     (errcode(ERRCODE_UNDEFINED_SCHEMA),
    3592             :                      errmsg("no schema has been selected to create in")));
    3593             :     }
    3594             : 
    3595       39080 :     return namespaceId;
    3596             : }
    3597             : 
    3598             : /*
    3599             :  * get_namespace_oid - given a namespace name, look up the OID
    3600             :  *
    3601             :  * If missing_ok is false, throw an error if namespace name not found.  If
    3602             :  * true, just return InvalidOid.
    3603             :  */
    3604             : Oid
    3605      867652 : get_namespace_oid(const char *nspname, bool missing_ok)
    3606             : {
    3607             :     Oid         oid;
    3608             : 
    3609      867652 :     oid = GetSysCacheOid1(NAMESPACENAME, Anum_pg_namespace_oid,
    3610             :                           CStringGetDatum(nspname));
    3611      867652 :     if (!OidIsValid(oid) && !missing_ok)
    3612         184 :         ereport(ERROR,
    3613             :                 (errcode(ERRCODE_UNDEFINED_SCHEMA),
    3614             :                  errmsg("schema \"%s\" does not exist", nspname)));
    3615             : 
    3616      867468 :     return oid;
    3617             : }
    3618             : 
    3619             : /*
    3620             :  * makeRangeVarFromNameList
    3621             :  *      Utility routine to convert a qualified-name list into RangeVar form.
    3622             :  */
    3623             : RangeVar *
    3624       63240 : makeRangeVarFromNameList(const List *names)
    3625             : {
    3626       63240 :     RangeVar   *rel = makeRangeVar(NULL, NULL, -1);
    3627             : 
    3628       63240 :     switch (list_length(names))
    3629             :     {
    3630       43080 :         case 1:
    3631       43080 :             rel->relname = strVal(linitial(names));
    3632       43080 :             break;
    3633       20080 :         case 2:
    3634       20080 :             rel->schemaname = strVal(linitial(names));
    3635       20080 :             rel->relname = strVal(lsecond(names));
    3636       20080 :             break;
    3637          80 :         case 3:
    3638          80 :             rel->catalogname = strVal(linitial(names));
    3639          80 :             rel->schemaname = strVal(lsecond(names));
    3640          80 :             rel->relname = strVal(lthird(names));
    3641          80 :             break;
    3642           0 :         default:
    3643           0 :             ereport(ERROR,
    3644             :                     (errcode(ERRCODE_SYNTAX_ERROR),
    3645             :                      errmsg("improper relation name (too many dotted names): %s",
    3646             :                             NameListToString(names))));
    3647             :             break;
    3648             :     }
    3649             : 
    3650       63240 :     return rel;
    3651             : }
    3652             : 
    3653             : /*
    3654             :  * NameListToString
    3655             :  *      Utility routine to convert a qualified-name list into a string.
    3656             :  *
    3657             :  * This is used primarily to form error messages, and so we do not quote
    3658             :  * the list elements, for the sake of legibility.
    3659             :  *
    3660             :  * In most scenarios the list elements should always be String values,
    3661             :  * but we also allow A_Star for the convenience of ColumnRef processing.
    3662             :  */
    3663             : char *
    3664        1794 : NameListToString(const List *names)
    3665             : {
    3666             :     StringInfoData string;
    3667             :     ListCell   *l;
    3668             : 
    3669        1794 :     initStringInfo(&string);
    3670             : 
    3671        4032 :     foreach(l, names)
    3672             :     {
    3673        2238 :         Node       *name = (Node *) lfirst(l);
    3674             : 
    3675        2238 :         if (l != list_head(names))
    3676         444 :             appendStringInfoChar(&string, '.');
    3677             : 
    3678        2238 :         if (IsA(name, String))
    3679        2238 :             appendStringInfoString(&string, strVal(name));
    3680           0 :         else if (IsA(name, A_Star))
    3681           0 :             appendStringInfoChar(&string, '*');
    3682             :         else
    3683           0 :             elog(ERROR, "unexpected node type in name list: %d",
    3684             :                  (int) nodeTag(name));
    3685             :     }
    3686             : 
    3687        1794 :     return string.data;
    3688             : }
    3689             : 
    3690             : /*
    3691             :  * NameListToQuotedString
    3692             :  *      Utility routine to convert a qualified-name list into a string.
    3693             :  *
    3694             :  * Same as above except that names will be double-quoted where necessary,
    3695             :  * so the string could be re-parsed (eg, by textToQualifiedNameList).
    3696             :  */
    3697             : char *
    3698           0 : NameListToQuotedString(const List *names)
    3699             : {
    3700             :     StringInfoData string;
    3701             :     ListCell   *l;
    3702             : 
    3703           0 :     initStringInfo(&string);
    3704             : 
    3705           0 :     foreach(l, names)
    3706             :     {
    3707           0 :         if (l != list_head(names))
    3708           0 :             appendStringInfoChar(&string, '.');
    3709           0 :         appendStringInfoString(&string, quote_identifier(strVal(lfirst(l))));
    3710             :     }
    3711             : 
    3712           0 :     return string.data;
    3713             : }
    3714             : 
    3715             : /*
    3716             :  * isTempNamespace - is the given namespace my temporary-table namespace?
    3717             :  */
    3718             : bool
    3719       75438 : isTempNamespace(Oid namespaceId)
    3720             : {
    3721       75438 :     if (OidIsValid(myTempNamespace) && myTempNamespace == namespaceId)
    3722        1162 :         return true;
    3723       74276 :     return false;
    3724             : }
    3725             : 
    3726             : /*
    3727             :  * isTempToastNamespace - is the given namespace my temporary-toast-table
    3728             :  *      namespace?
    3729             :  */
    3730             : bool
    3731     6666350 : isTempToastNamespace(Oid namespaceId)
    3732             : {
    3733     6666350 :     if (OidIsValid(myTempToastNamespace) && myTempToastNamespace == namespaceId)
    3734        3704 :         return true;
    3735     6662646 :     return false;
    3736             : }
    3737             : 
    3738             : /*
    3739             :  * isTempOrTempToastNamespace - is the given namespace my temporary-table
    3740             :  *      namespace or my temporary-toast-table namespace?
    3741             :  */
    3742             : bool
    3743      189034 : isTempOrTempToastNamespace(Oid namespaceId)
    3744             : {
    3745      189034 :     if (OidIsValid(myTempNamespace) &&
    3746       72364 :         (myTempNamespace == namespaceId || myTempToastNamespace == namespaceId))
    3747       36968 :         return true;
    3748      152066 :     return false;
    3749             : }
    3750             : 
    3751             : /*
    3752             :  * isAnyTempNamespace - is the given namespace a temporary-table namespace
    3753             :  * (either my own, or another backend's)?  Temporary-toast-table namespaces
    3754             :  * are included, too.
    3755             :  */
    3756             : bool
    3757      140806 : isAnyTempNamespace(Oid namespaceId)
    3758             : {
    3759             :     bool        result;
    3760             :     char       *nspname;
    3761             : 
    3762             :     /* True if the namespace name starts with "pg_temp_" or "pg_toast_temp_" */
    3763      140806 :     nspname = get_namespace_name(namespaceId);
    3764      140806 :     if (!nspname)
    3765           2 :         return false;           /* no such namespace? */
    3766      277072 :     result = (strncmp(nspname, "pg_temp_", 8) == 0) ||
    3767      136268 :         (strncmp(nspname, "pg_toast_temp_", 14) == 0);
    3768      140804 :     pfree(nspname);
    3769      140804 :     return result;
    3770             : }
    3771             : 
    3772             : /*
    3773             :  * isOtherTempNamespace - is the given namespace some other backend's
    3774             :  * temporary-table namespace (including temporary-toast-table namespaces)?
    3775             :  *
    3776             :  * Note: for most purposes in the C code, this function is obsolete.  Use
    3777             :  * RELATION_IS_OTHER_TEMP() instead to detect non-local temp relations.
    3778             :  */
    3779             : bool
    3780       13318 : isOtherTempNamespace(Oid namespaceId)
    3781             : {
    3782             :     /* If it's my own temp namespace, say "false" */
    3783       13318 :     if (isTempOrTempToastNamespace(namespaceId))
    3784         204 :         return false;
    3785             :     /* Else, if it's any temp namespace, say "true" */
    3786       13114 :     return isAnyTempNamespace(namespaceId);
    3787             : }
    3788             : 
    3789             : /*
    3790             :  * checkTempNamespaceStatus - is the given namespace owned and actively used
    3791             :  * by a backend?
    3792             :  *
    3793             :  * Note: this can be used while scanning relations in pg_class to detect
    3794             :  * orphaned temporary tables or namespaces with a backend connected to a
    3795             :  * given database.  The result may be out of date quickly, so the caller
    3796             :  * must be careful how to handle this information.
    3797             :  */
    3798             : TempNamespaceStatus
    3799          18 : checkTempNamespaceStatus(Oid namespaceId)
    3800             : {
    3801             :     PGPROC     *proc;
    3802             :     ProcNumber  procNumber;
    3803             : 
    3804             :     Assert(OidIsValid(MyDatabaseId));
    3805             : 
    3806          18 :     procNumber = GetTempNamespaceProcNumber(namespaceId);
    3807             : 
    3808             :     /* No such namespace, or its name shows it's not temp? */
    3809          18 :     if (procNumber == INVALID_PROC_NUMBER)
    3810           0 :         return TEMP_NAMESPACE_NOT_TEMP;
    3811             : 
    3812             :     /* Is the backend alive? */
    3813          18 :     proc = ProcNumberGetProc(procNumber);
    3814          18 :     if (proc == NULL)
    3815           0 :         return TEMP_NAMESPACE_IDLE;
    3816             : 
    3817             :     /* Is the backend connected to the same database we are looking at? */
    3818          18 :     if (proc->databaseId != MyDatabaseId)
    3819           0 :         return TEMP_NAMESPACE_IDLE;
    3820             : 
    3821             :     /* Does the backend own the temporary namespace? */
    3822          18 :     if (proc->tempNamespaceId != namespaceId)
    3823           0 :         return TEMP_NAMESPACE_IDLE;
    3824             : 
    3825             :     /* Yup, so namespace is busy */
    3826          18 :     return TEMP_NAMESPACE_IN_USE;
    3827             : }
    3828             : 
    3829             : /*
    3830             :  * GetTempNamespaceProcNumber - if the given namespace is a temporary-table
    3831             :  * namespace (either my own, or another backend's), return the proc number
    3832             :  * that owns it.  Temporary-toast-table namespaces are included, too.
    3833             :  * If it isn't a temp namespace, return INVALID_PROC_NUMBER.
    3834             :  */
    3835             : ProcNumber
    3836          54 : GetTempNamespaceProcNumber(Oid namespaceId)
    3837             : {
    3838             :     int         result;
    3839             :     char       *nspname;
    3840             : 
    3841             :     /* See if the namespace name starts with "pg_temp_" or "pg_toast_temp_" */
    3842          54 :     nspname = get_namespace_name(namespaceId);
    3843          54 :     if (!nspname)
    3844           0 :         return INVALID_PROC_NUMBER; /* no such namespace? */
    3845          54 :     if (strncmp(nspname, "pg_temp_", 8) == 0)
    3846          54 :         result = atoi(nspname + 8);
    3847           0 :     else if (strncmp(nspname, "pg_toast_temp_", 14) == 0)
    3848           0 :         result = atoi(nspname + 14);
    3849             :     else
    3850           0 :         result = INVALID_PROC_NUMBER;
    3851          54 :     pfree(nspname);
    3852          54 :     return result;
    3853             : }
    3854             : 
    3855             : /*
    3856             :  * GetTempToastNamespace - get the OID of my temporary-toast-table namespace,
    3857             :  * which must already be assigned.  (This is only used when creating a toast
    3858             :  * table for a temp table, so we must have already done InitTempTableNamespace)
    3859             :  */
    3860             : Oid
    3861         946 : GetTempToastNamespace(void)
    3862             : {
    3863             :     Assert(OidIsValid(myTempToastNamespace));
    3864         946 :     return myTempToastNamespace;
    3865             : }
    3866             : 
    3867             : 
    3868             : /*
    3869             :  * GetTempNamespaceState - fetch status of session's temporary namespace
    3870             :  *
    3871             :  * This is used for conveying state to a parallel worker, and is not meant
    3872             :  * for general-purpose access.
    3873             :  */
    3874             : void
    3875         916 : GetTempNamespaceState(Oid *tempNamespaceId, Oid *tempToastNamespaceId)
    3876             : {
    3877             :     /* Return namespace OIDs, or 0 if session has not created temp namespace */
    3878         916 :     *tempNamespaceId = myTempNamespace;
    3879         916 :     *tempToastNamespaceId = myTempToastNamespace;
    3880         916 : }
    3881             : 
    3882             : /*
    3883             :  * SetTempNamespaceState - set status of session's temporary namespace
    3884             :  *
    3885             :  * This is used for conveying state to a parallel worker, and is not meant for
    3886             :  * general-purpose access.  By transferring these namespace OIDs to workers,
    3887             :  * we ensure they will have the same notion of the search path as their leader
    3888             :  * does.
    3889             :  */
    3890             : void
    3891        2746 : SetTempNamespaceState(Oid tempNamespaceId, Oid tempToastNamespaceId)
    3892             : {
    3893             :     /* Worker should not have created its own namespaces ... */
    3894             :     Assert(myTempNamespace == InvalidOid);
    3895             :     Assert(myTempToastNamespace == InvalidOid);
    3896             :     Assert(myTempNamespaceSubID == InvalidSubTransactionId);
    3897             : 
    3898             :     /* Assign same namespace OIDs that leader has */
    3899        2746 :     myTempNamespace = tempNamespaceId;
    3900        2746 :     myTempToastNamespace = tempToastNamespaceId;
    3901             : 
    3902             :     /*
    3903             :      * It's fine to leave myTempNamespaceSubID == InvalidSubTransactionId.
    3904             :      * Even if the namespace is new so far as the leader is concerned, it's
    3905             :      * not new to the worker, and we certainly wouldn't want the worker trying
    3906             :      * to destroy it.
    3907             :      */
    3908             : 
    3909        2746 :     baseSearchPathValid = false;    /* may need to rebuild list */
    3910        2746 :     searchPathCacheValid = false;
    3911        2746 : }
    3912             : 
    3913             : 
    3914             : /*
    3915             :  * GetSearchPathMatcher - fetch current search path definition.
    3916             :  *
    3917             :  * The result structure is allocated in the specified memory context
    3918             :  * (which might or might not be equal to CurrentMemoryContext); but any
    3919             :  * junk created by revalidation calculations will be in CurrentMemoryContext.
    3920             :  */
    3921             : SearchPathMatcher *
    3922       62322 : GetSearchPathMatcher(MemoryContext context)
    3923             : {
    3924             :     SearchPathMatcher *result;
    3925             :     List       *schemas;
    3926             :     MemoryContext oldcxt;
    3927             : 
    3928       62322 :     recomputeNamespacePath();
    3929             : 
    3930       62322 :     oldcxt = MemoryContextSwitchTo(context);
    3931             : 
    3932       62322 :     result = (SearchPathMatcher *) palloc0(sizeof(SearchPathMatcher));
    3933       62322 :     schemas = list_copy(activeSearchPath);
    3934      137090 :     while (schemas && linitial_oid(schemas) != activeCreationNamespace)
    3935             :     {
    3936       74768 :         if (linitial_oid(schemas) == myTempNamespace)
    3937       14888 :             result->addTemp = true;
    3938             :         else
    3939             :         {
    3940             :             Assert(linitial_oid(schemas) == PG_CATALOG_NAMESPACE);
    3941       59880 :             result->addCatalog = true;
    3942             :         }
    3943       74768 :         schemas = list_delete_first(schemas);
    3944             :     }
    3945       62322 :     result->schemas = schemas;
    3946       62322 :     result->generation = activePathGeneration;
    3947             : 
    3948       62322 :     MemoryContextSwitchTo(oldcxt);
    3949             : 
    3950       62322 :     return result;
    3951             : }
    3952             : 
    3953             : /*
    3954             :  * CopySearchPathMatcher - copy the specified SearchPathMatcher.
    3955             :  *
    3956             :  * The result structure is allocated in CurrentMemoryContext.
    3957             :  */
    3958             : SearchPathMatcher *
    3959           0 : CopySearchPathMatcher(SearchPathMatcher *path)
    3960             : {
    3961             :     SearchPathMatcher *result;
    3962             : 
    3963           0 :     result = (SearchPathMatcher *) palloc(sizeof(SearchPathMatcher));
    3964           0 :     result->schemas = list_copy(path->schemas);
    3965           0 :     result->addCatalog = path->addCatalog;
    3966           0 :     result->addTemp = path->addTemp;
    3967           0 :     result->generation = path->generation;
    3968             : 
    3969           0 :     return result;
    3970             : }
    3971             : 
    3972             : /*
    3973             :  * SearchPathMatchesCurrentEnvironment - does path match current environment?
    3974             :  *
    3975             :  * This is tested over and over in some common code paths, and in the typical
    3976             :  * scenario where the active search path seldom changes, it'll always succeed.
    3977             :  * We make that case fast by keeping a generation counter that is advanced
    3978             :  * whenever the active search path changes.
    3979             :  */
    3980             : bool
    3981      805656 : SearchPathMatchesCurrentEnvironment(SearchPathMatcher *path)
    3982             : {
    3983             :     ListCell   *lc,
    3984             :                *lcp;
    3985             : 
    3986      805656 :     recomputeNamespacePath();
    3987             : 
    3988             :     /* Quick out if already known equal to active path. */
    3989      805656 :     if (path->generation == activePathGeneration)
    3990      805214 :         return true;
    3991             : 
    3992             :     /* We scan down the activeSearchPath to see if it matches the input. */
    3993         442 :     lc = list_head(activeSearchPath);
    3994             : 
    3995             :     /* If path->addTemp, first item should be my temp namespace. */
    3996         442 :     if (path->addTemp)
    3997             :     {
    3998          86 :         if (lc && lfirst_oid(lc) == myTempNamespace)
    3999          74 :             lc = lnext(activeSearchPath, lc);
    4000             :         else
    4001          12 :             return false;
    4002             :     }
    4003             :     /* If path->addCatalog, next item should be pg_catalog. */
    4004         430 :     if (path->addCatalog)
    4005             :     {
    4006         284 :         if (lc && lfirst_oid(lc) == PG_CATALOG_NAMESPACE)
    4007         206 :             lc = lnext(activeSearchPath, lc);
    4008             :         else
    4009          78 :             return false;
    4010             :     }
    4011             :     /* We should now be looking at the activeCreationNamespace. */
    4012         352 :     if (activeCreationNamespace != (lc ? lfirst_oid(lc) : InvalidOid))
    4013          36 :         return false;
    4014             :     /* The remainder of activeSearchPath should match path->schemas. */
    4015         594 :     foreach(lcp, path->schemas)
    4016             :     {
    4017         316 :         if (lc && lfirst_oid(lc) == lfirst_oid(lcp))
    4018         278 :             lc = lnext(activeSearchPath, lc);
    4019             :         else
    4020          38 :             return false;
    4021             :     }
    4022         278 :     if (lc)
    4023          12 :         return false;
    4024             : 
    4025             :     /*
    4026             :      * Update path->generation so that future tests will return quickly, so
    4027             :      * long as the active search path doesn't change.
    4028             :      */
    4029         266 :     path->generation = activePathGeneration;
    4030             : 
    4031         266 :     return true;
    4032             : }
    4033             : 
    4034             : /*
    4035             :  * get_collation_oid - find a collation by possibly qualified name
    4036             :  *
    4037             :  * Note that this will only find collations that work with the current
    4038             :  * database's encoding.
    4039             :  */
    4040             : Oid
    4041       10926 : get_collation_oid(List *collname, bool missing_ok)
    4042             : {
    4043             :     char       *schemaname;
    4044             :     char       *collation_name;
    4045       10926 :     int32       dbencoding = GetDatabaseEncoding();
    4046             :     Oid         namespaceId;
    4047             :     Oid         colloid;
    4048             :     ListCell   *l;
    4049             : 
    4050             :     /* deconstruct the name list */
    4051       10926 :     DeconstructQualifiedName(collname, &schemaname, &collation_name);
    4052             : 
    4053       10926 :     if (schemaname)
    4054             :     {
    4055             :         /* use exact schema given */
    4056        6928 :         namespaceId = LookupExplicitNamespace(schemaname, missing_ok);
    4057        6928 :         if (missing_ok && !OidIsValid(namespaceId))
    4058          24 :             return InvalidOid;
    4059             : 
    4060        6904 :         colloid = lookup_collation(collation_name, namespaceId, dbencoding);
    4061        6904 :         if (OidIsValid(colloid))
    4062        6904 :             return colloid;
    4063             :     }
    4064             :     else
    4065             :     {
    4066             :         /* search for it in search path */
    4067        3998 :         recomputeNamespacePath();
    4068             : 
    4069        6650 :         foreach(l, activeSearchPath)
    4070             :         {
    4071        6600 :             namespaceId = lfirst_oid(l);
    4072             : 
    4073        6600 :             if (namespaceId == myTempNamespace)
    4074        1338 :                 continue;       /* do not look in temp namespace */
    4075             : 
    4076        5262 :             colloid = lookup_collation(collation_name, namespaceId, dbencoding);
    4077        5262 :             if (OidIsValid(colloid))
    4078        3948 :                 return colloid;
    4079             :         }
    4080             :     }
    4081             : 
    4082             :     /* Not found in path */
    4083          50 :     if (!missing_ok)
    4084          32 :         ereport(ERROR,
    4085             :                 (errcode(ERRCODE_UNDEFINED_OBJECT),
    4086             :                  errmsg("collation \"%s\" for encoding \"%s\" does not exist",
    4087             :                         NameListToString(collname), GetDatabaseEncodingName())));
    4088          18 :     return InvalidOid;
    4089             : }
    4090             : 
    4091             : /*
    4092             :  * get_conversion_oid - find a conversion by possibly qualified name
    4093             :  */
    4094             : Oid
    4095         192 : get_conversion_oid(List *conname, bool missing_ok)
    4096             : {
    4097             :     char       *schemaname;
    4098             :     char       *conversion_name;
    4099             :     Oid         namespaceId;
    4100         192 :     Oid         conoid = InvalidOid;
    4101             :     ListCell   *l;
    4102             : 
    4103             :     /* deconstruct the name list */
    4104         192 :     DeconstructQualifiedName(conname, &schemaname, &conversion_name);
    4105             : 
    4106         180 :     if (schemaname)
    4107             :     {
    4108             :         /* use exact schema given */
    4109          38 :         namespaceId = LookupExplicitNamespace(schemaname, missing_ok);
    4110          38 :         if (missing_ok && !OidIsValid(namespaceId))
    4111           6 :             conoid = InvalidOid;
    4112             :         else
    4113          32 :             conoid = GetSysCacheOid2(CONNAMENSP, Anum_pg_conversion_oid,
    4114             :                                      PointerGetDatum(conversion_name),
    4115             :                                      ObjectIdGetDatum(namespaceId));
    4116             :     }
    4117             :     else
    4118             :     {
    4119             :         /* search for it in search path */
    4120         142 :         recomputeNamespacePath();
    4121             : 
    4122         314 :         foreach(l, activeSearchPath)
    4123             :         {
    4124         284 :             namespaceId = lfirst_oid(l);
    4125             : 
    4126         284 :             if (namespaceId == myTempNamespace)
    4127           0 :                 continue;       /* do not look in temp namespace */
    4128             : 
    4129         284 :             conoid = GetSysCacheOid2(CONNAMENSP, Anum_pg_conversion_oid,
    4130             :                                      PointerGetDatum(conversion_name),
    4131             :                                      ObjectIdGetDatum(namespaceId));
    4132         284 :             if (OidIsValid(conoid))
    4133         112 :                 return conoid;
    4134             :         }
    4135             :     }
    4136             : 
    4137             :     /* Not found in path */
    4138          68 :     if (!OidIsValid(conoid) && !missing_ok)
    4139          36 :         ereport(ERROR,
    4140             :                 (errcode(ERRCODE_UNDEFINED_OBJECT),
    4141             :                  errmsg("conversion \"%s\" does not exist",
    4142             :                         NameListToString(conname))));
    4143          32 :     return conoid;
    4144             : }
    4145             : 
    4146             : /*
    4147             :  * FindDefaultConversionProc - find default encoding conversion proc
    4148             :  */
    4149             : Oid
    4150        6786 : FindDefaultConversionProc(int32 for_encoding, int32 to_encoding)
    4151             : {
    4152             :     Oid         proc;
    4153             :     ListCell   *l;
    4154             : 
    4155        6786 :     recomputeNamespacePath();
    4156             : 
    4157        6786 :     foreach(l, activeSearchPath)
    4158             :     {
    4159        6786 :         Oid         namespaceId = lfirst_oid(l);
    4160             : 
    4161        6786 :         if (namespaceId == myTempNamespace)
    4162           0 :             continue;           /* do not look in temp namespace */
    4163             : 
    4164        6786 :         proc = FindDefaultConversion(namespaceId, for_encoding, to_encoding);
    4165        6786 :         if (OidIsValid(proc))
    4166        6786 :             return proc;
    4167             :     }
    4168             : 
    4169             :     /* Not found in path */
    4170           0 :     return InvalidOid;
    4171             : }
    4172             : 
    4173             : /*
    4174             :  * Look up namespace IDs and perform ACL checks. Return newly-allocated list.
    4175             :  */
    4176             : static List *
    4177       38660 : preprocessNamespacePath(const char *searchPath, Oid roleid,
    4178             :                         bool *temp_missing)
    4179             : {
    4180             :     char       *rawname;
    4181             :     List       *namelist;
    4182             :     List       *oidlist;
    4183             :     ListCell   *l;
    4184             : 
    4185             :     /* Need a modifiable copy */
    4186       38660 :     rawname = pstrdup(searchPath);
    4187             : 
    4188             :     /* Parse string into list of identifiers */
    4189       38660 :     if (!SplitIdentifierString(rawname, ',', &namelist))
    4190             :     {
    4191             :         /* syntax error in name list */
    4192             :         /* this should not happen if GUC checked check_search_path */
    4193           0 :         elog(ERROR, "invalid list syntax");
    4194             :     }
    4195             : 
    4196             :     /*
    4197             :      * Convert the list of names to a list of OIDs.  If any names are not
    4198             :      * recognizable or we don't have read access, just leave them out of the
    4199             :      * list.  (We can't raise an error, since the search_path setting has
    4200             :      * already been accepted.)  Don't make duplicate entries, either.
    4201             :      */
    4202       38660 :     oidlist = NIL;
    4203       38660 :     *temp_missing = false;
    4204      106554 :     foreach(l, namelist)
    4205             :     {
    4206       67894 :         char       *curname = (char *) lfirst(l);
    4207             :         Oid         namespaceId;
    4208             : 
    4209       67894 :         if (strcmp(curname, "$user") == 0)
    4210             :         {
    4211             :             /* $user --- substitute namespace matching user name, if any */
    4212             :             HeapTuple   tuple;
    4213             : 
    4214       30720 :             tuple = SearchSysCache1(AUTHOID, ObjectIdGetDatum(roleid));
    4215       30720 :             if (HeapTupleIsValid(tuple))
    4216             :             {
    4217             :                 char       *rname;
    4218             : 
    4219       30720 :                 rname = NameStr(((Form_pg_authid) GETSTRUCT(tuple))->rolname);
    4220       30720 :                 namespaceId = get_namespace_oid(rname, true);
    4221       30720 :                 ReleaseSysCache(tuple);
    4222       30726 :                 if (OidIsValid(namespaceId) &&
    4223           6 :                     object_aclcheck(NamespaceRelationId, namespaceId, roleid,
    4224             :                                     ACL_USAGE) == ACLCHECK_OK)
    4225           6 :                     oidlist = lappend_oid(oidlist, namespaceId);
    4226             :             }
    4227             :         }
    4228       37174 :         else if (strcmp(curname, "pg_temp") == 0)
    4229             :         {
    4230             :             /* pg_temp --- substitute temp namespace, if any */
    4231        1186 :             if (OidIsValid(myTempNamespace))
    4232         292 :                 oidlist = lappend_oid(oidlist, myTempNamespace);
    4233             :             else
    4234             :             {
    4235             :                 /* If it ought to be the creation namespace, set flag */
    4236         894 :                 if (oidlist == NIL)
    4237           6 :                     *temp_missing = true;
    4238             :             }
    4239             :         }
    4240             :         else
    4241             :         {
    4242             :             /* normal namespace reference */
    4243       35988 :             namespaceId = get_namespace_oid(curname, true);
    4244       71910 :             if (OidIsValid(namespaceId) &&
    4245       35922 :                 object_aclcheck(NamespaceRelationId, namespaceId, roleid,
    4246             :                                 ACL_USAGE) == ACLCHECK_OK)
    4247       35892 :                 oidlist = lappend_oid(oidlist, namespaceId);
    4248             :         }
    4249             :     }
    4250             : 
    4251       38660 :     pfree(rawname);
    4252       38660 :     list_free(namelist);
    4253             : 
    4254       38660 :     return oidlist;
    4255             : }
    4256             : 
    4257             : /*
    4258             :  * Remove duplicates, run namespace search hooks, and prepend
    4259             :  * implicitly-searched namespaces. Return newly-allocated list.
    4260             :  *
    4261             :  * If an object_access_hook is present, this must always be recalculated. It
    4262             :  * may seem that duplicate elimination is not dependent on the result of the
    4263             :  * hook, but if a hook returns different results on different calls for the
    4264             :  * same namespace ID, then it could affect the order in which that namespace
    4265             :  * appears in the final list.
    4266             :  */
    4267             : static List *
    4268       37866 : finalNamespacePath(List *oidlist, Oid *firstNS)
    4269             : {
    4270       37866 :     List       *finalPath = NIL;
    4271             :     ListCell   *lc;
    4272             : 
    4273       74064 :     foreach(lc, oidlist)
    4274             :     {
    4275       36198 :         Oid         namespaceId = lfirst_oid(lc);
    4276             : 
    4277       36198 :         if (!list_member_oid(finalPath, namespaceId))
    4278             :         {
    4279       36184 :             if (InvokeNamespaceSearchHook(namespaceId, false))
    4280       36184 :                 finalPath = lappend_oid(finalPath, namespaceId);
    4281             :         }
    4282             :     }
    4283             : 
    4284             :     /*
    4285             :      * Remember the first member of the explicit list.  (Note: this is
    4286             :      * nominally wrong if temp_missing, but we need it anyway to distinguish
    4287             :      * explicit from implicit mention of pg_catalog.)
    4288             :      */
    4289       37866 :     if (finalPath == NIL)
    4290        2658 :         *firstNS = InvalidOid;
    4291             :     else
    4292       35208 :         *firstNS = linitial_oid(finalPath);
    4293             : 
    4294             :     /*
    4295             :      * Add any implicitly-searched namespaces to the list.  Note these go on
    4296             :      * the front, not the back; also notice that we do not check USAGE
    4297             :      * permissions for these.
    4298             :      */
    4299       37866 :     if (!list_member_oid(finalPath, PG_CATALOG_NAMESPACE))
    4300       36638 :         finalPath = lcons_oid(PG_CATALOG_NAMESPACE, finalPath);
    4301             : 
    4302       37866 :     if (OidIsValid(myTempNamespace) &&
    4303        4034 :         !list_member_oid(finalPath, myTempNamespace))
    4304        3742 :         finalPath = lcons_oid(myTempNamespace, finalPath);
    4305             : 
    4306       37866 :     return finalPath;
    4307             : }
    4308             : 
    4309             : /*
    4310             :  * Retrieve search path information from the cache; or if not there, fill
    4311             :  * it. The returned entry is valid only until the next call to this function.
    4312             :  */
    4313             : static const SearchPathCacheEntry *
    4314       73264 : cachedNamespacePath(const char *searchPath, Oid roleid)
    4315             : {
    4316             :     MemoryContext oldcxt;
    4317             :     SearchPathCacheEntry *entry;
    4318             : 
    4319       73264 :     spcache_init();
    4320             : 
    4321       73264 :     entry = spcache_insert(searchPath, roleid);
    4322             : 
    4323             :     /*
    4324             :      * An OOM may have resulted in a cache entry with missing 'oidlist' or
    4325             :      * 'finalPath', so just compute whatever is missing.
    4326             :      */
    4327             : 
    4328       73264 :     if (entry->oidlist == NIL)
    4329             :     {
    4330       38660 :         oldcxt = MemoryContextSwitchTo(SearchPathCacheContext);
    4331       38660 :         entry->oidlist = preprocessNamespacePath(searchPath, roleid,
    4332             :                                                  &entry->temp_missing);
    4333       38660 :         MemoryContextSwitchTo(oldcxt);
    4334             :     }
    4335             : 
    4336             :     /*
    4337             :      * If a hook is set, we must recompute finalPath from the oidlist each
    4338             :      * time, because the hook may affect the result. This is still much faster
    4339             :      * than recomputing from the string (and doing catalog lookups and ACL
    4340             :      * checks).
    4341             :      */
    4342       73264 :     if (entry->finalPath == NIL || object_access_hook ||
    4343       35398 :         entry->forceRecompute)
    4344             :     {
    4345       37866 :         list_free(entry->finalPath);
    4346       37866 :         entry->finalPath = NIL;
    4347             : 
    4348       37866 :         oldcxt = MemoryContextSwitchTo(SearchPathCacheContext);
    4349       37866 :         entry->finalPath = finalNamespacePath(entry->oidlist,
    4350             :                                               &entry->firstNS);
    4351       37866 :         MemoryContextSwitchTo(oldcxt);
    4352             : 
    4353             :         /*
    4354             :          * If an object_access_hook is set when finalPath is calculated, the
    4355             :          * result may be affected by the hook. Force recomputation of
    4356             :          * finalPath the next time this cache entry is used, even if the
    4357             :          * object_access_hook is not set at that time.
    4358             :          */
    4359       37866 :         entry->forceRecompute = object_access_hook ? true : false;
    4360             :     }
    4361             : 
    4362       73264 :     return entry;
    4363             : }
    4364             : 
    4365             : /*
    4366             :  * recomputeNamespacePath - recompute path derived variables if needed.
    4367             :  */
    4368             : static void
    4369     3916412 : recomputeNamespacePath(void)
    4370             : {
    4371     3916412 :     Oid         roleid = GetUserId();
    4372             :     bool        pathChanged;
    4373             :     const SearchPathCacheEntry *entry;
    4374             : 
    4375             :     /* Do nothing if path is already valid. */
    4376     3916412 :     if (baseSearchPathValid && namespaceUser == roleid)
    4377     3843148 :         return;
    4378             : 
    4379       73264 :     entry = cachedNamespacePath(namespace_search_path, roleid);
    4380             : 
    4381       73264 :     if (baseCreationNamespace == entry->firstNS &&
    4382      104130 :         baseTempCreationPending == entry->temp_missing &&
    4383       52062 :         equal(entry->finalPath, baseSearchPath))
    4384             :     {
    4385       48826 :         pathChanged = false;
    4386             :     }
    4387             :     else
    4388             :     {
    4389             :         MemoryContext oldcxt;
    4390             :         List       *newpath;
    4391             : 
    4392       24438 :         pathChanged = true;
    4393             : 
    4394             :         /* Must save OID list in permanent storage. */
    4395       24438 :         oldcxt = MemoryContextSwitchTo(TopMemoryContext);
    4396       24438 :         newpath = list_copy(entry->finalPath);
    4397       24438 :         MemoryContextSwitchTo(oldcxt);
    4398             : 
    4399             :         /* Now safe to assign to state variables. */
    4400       24438 :         list_free(baseSearchPath);
    4401       24438 :         baseSearchPath = newpath;
    4402       24438 :         baseCreationNamespace = entry->firstNS;
    4403       24438 :         baseTempCreationPending = entry->temp_missing;
    4404             :     }
    4405             : 
    4406             :     /* Mark the path valid. */
    4407       73264 :     baseSearchPathValid = true;
    4408       73264 :     namespaceUser = roleid;
    4409             : 
    4410             :     /* And make it active. */
    4411       73264 :     activeSearchPath = baseSearchPath;
    4412       73264 :     activeCreationNamespace = baseCreationNamespace;
    4413       73264 :     activeTempCreationPending = baseTempCreationPending;
    4414             : 
    4415             :     /*
    4416             :      * Bump the generation only if something actually changed.  (Notice that
    4417             :      * what we compared to was the old state of the base path variables.)
    4418             :      */
    4419       73264 :     if (pathChanged)
    4420       24438 :         activePathGeneration++;
    4421             : }
    4422             : 
    4423             : /*
    4424             :  * AccessTempTableNamespace
    4425             :  *      Provide access to a temporary namespace, potentially creating it
    4426             :  *      if not present yet.  This routine registers if the namespace gets
    4427             :  *      in use in this transaction.  'force' can be set to true to allow
    4428             :  *      the caller to enforce the creation of the temporary namespace for
    4429             :  *      use in this backend, which happens if its creation is pending.
    4430             :  */
    4431             : static void
    4432        6994 : AccessTempTableNamespace(bool force)
    4433             : {
    4434             :     /*
    4435             :      * Make note that this temporary namespace has been accessed in this
    4436             :      * transaction.
    4437             :      */
    4438        6994 :     MyXactFlags |= XACT_FLAGS_ACCESSEDTEMPNAMESPACE;
    4439             : 
    4440             :     /*
    4441             :      * If the caller attempting to access a temporary schema expects the
    4442             :      * creation of the namespace to be pending and should be enforced, then go
    4443             :      * through the creation.
    4444             :      */
    4445        6994 :     if (!force && OidIsValid(myTempNamespace))
    4446        6342 :         return;
    4447             : 
    4448             :     /*
    4449             :      * The temporary tablespace does not exist yet and is wanted, so
    4450             :      * initialize it.
    4451             :      */
    4452         652 :     InitTempTableNamespace();
    4453             : }
    4454             : 
    4455             : /*
    4456             :  * InitTempTableNamespace
    4457             :  *      Initialize temp table namespace on first use in a particular backend
    4458             :  */
    4459             : static void
    4460         652 : InitTempTableNamespace(void)
    4461             : {
    4462             :     char        namespaceName[NAMEDATALEN];
    4463             :     Oid         namespaceId;
    4464             :     Oid         toastspaceId;
    4465             : 
    4466             :     Assert(!OidIsValid(myTempNamespace));
    4467             : 
    4468             :     /*
    4469             :      * First, do permission check to see if we are authorized to make temp
    4470             :      * tables.  We use a nonstandard error message here since "databasename:
    4471             :      * permission denied" might be a tad cryptic.
    4472             :      *
    4473             :      * Note that ACL_CREATE_TEMP rights are rechecked in pg_namespace_aclmask;
    4474             :      * that's necessary since current user ID could change during the session.
    4475             :      * But there's no need to make the namespace in the first place until a
    4476             :      * temp table creation request is made by someone with appropriate rights.
    4477             :      */
    4478         652 :     if (object_aclcheck(DatabaseRelationId, MyDatabaseId, GetUserId(),
    4479             :                         ACL_CREATE_TEMP) != ACLCHECK_OK)
    4480           0 :         ereport(ERROR,
    4481             :                 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
    4482             :                  errmsg("permission denied to create temporary tables in database \"%s\"",
    4483             :                         get_database_name(MyDatabaseId))));
    4484             : 
    4485             :     /*
    4486             :      * Do not allow a Hot Standby session to make temp tables.  Aside from
    4487             :      * problems with modifying the system catalogs, there is a naming
    4488             :      * conflict: pg_temp_N belongs to the session with proc number N on the
    4489             :      * primary, not to a hot standby session with the same proc number.  We
    4490             :      * should not be able to get here anyway due to XactReadOnly checks, but
    4491             :      * let's just make real sure.  Note that this also backstops various
    4492             :      * operations that allow XactReadOnly transactions to modify temp tables;
    4493             :      * they'd need RecoveryInProgress checks if not for this.
    4494             :      */
    4495         652 :     if (RecoveryInProgress())
    4496           0 :         ereport(ERROR,
    4497             :                 (errcode(ERRCODE_READ_ONLY_SQL_TRANSACTION),
    4498             :                  errmsg("cannot create temporary tables during recovery")));
    4499             : 
    4500             :     /* Parallel workers can't create temporary tables, either. */
    4501         652 :     if (IsParallelWorker())
    4502           0 :         ereport(ERROR,
    4503             :                 (errcode(ERRCODE_READ_ONLY_SQL_TRANSACTION),
    4504             :                  errmsg("cannot create temporary tables during a parallel operation")));
    4505             : 
    4506         652 :     snprintf(namespaceName, sizeof(namespaceName), "pg_temp_%d", MyProcNumber);
    4507             : 
    4508         652 :     namespaceId = get_namespace_oid(namespaceName, true);
    4509         652 :     if (!OidIsValid(namespaceId))
    4510             :     {
    4511             :         /*
    4512             :          * First use of this temp namespace in this database; create it. The
    4513             :          * temp namespaces are always owned by the superuser.  We leave their
    4514             :          * permissions at default --- i.e., no access except to superuser ---
    4515             :          * to ensure that unprivileged users can't peek at other backends'
    4516             :          * temp tables.  This works because the places that access the temp
    4517             :          * namespace for my own backend skip permissions checks on it.
    4518             :          */
    4519         426 :         namespaceId = NamespaceCreate(namespaceName, BOOTSTRAP_SUPERUSERID,
    4520             :                                       true);
    4521             :         /* Advance command counter to make namespace visible */
    4522         426 :         CommandCounterIncrement();
    4523             :     }
    4524             :     else
    4525             :     {
    4526             :         /*
    4527             :          * If the namespace already exists, clean it out (in case the former
    4528             :          * owner crashed without doing so).
    4529             :          */
    4530         226 :         RemoveTempRelations(namespaceId);
    4531             :     }
    4532             : 
    4533             :     /*
    4534             :      * If the corresponding toast-table namespace doesn't exist yet, create
    4535             :      * it. (We assume there is no need to clean it out if it does exist, since
    4536             :      * dropping a parent table should make its toast table go away.)
    4537             :      */
    4538         652 :     snprintf(namespaceName, sizeof(namespaceName), "pg_toast_temp_%d",
    4539             :              MyProcNumber);
    4540             : 
    4541         652 :     toastspaceId = get_namespace_oid(namespaceName, true);
    4542         652 :     if (!OidIsValid(toastspaceId))
    4543             :     {
    4544         426 :         toastspaceId = NamespaceCreate(namespaceName, BOOTSTRAP_SUPERUSERID,
    4545             :                                        true);
    4546             :         /* Advance command counter to make namespace visible */
    4547         426 :         CommandCounterIncrement();
    4548             :     }
    4549             : 
    4550             :     /*
    4551             :      * Okay, we've prepared the temp namespace ... but it's not committed yet,
    4552             :      * so all our work could be undone by transaction rollback.  Set flag for
    4553             :      * AtEOXact_Namespace to know what to do.
    4554             :      */
    4555         652 :     myTempNamespace = namespaceId;
    4556         652 :     myTempToastNamespace = toastspaceId;
    4557             : 
    4558             :     /*
    4559             :      * Mark MyProc as owning this namespace which other processes can use to
    4560             :      * decide if a temporary namespace is in use or not.  We assume that
    4561             :      * assignment of namespaceId is an atomic operation.  Even if it is not,
    4562             :      * the temporary relation which resulted in the creation of this temporary
    4563             :      * namespace is still locked until the current transaction commits, and
    4564             :      * its pg_namespace row is not visible yet.  However it does not matter:
    4565             :      * this flag makes the namespace as being in use, so no objects created on
    4566             :      * it would be removed concurrently.
    4567             :      */
    4568         652 :     MyProc->tempNamespaceId = namespaceId;
    4569             : 
    4570             :     /* It should not be done already. */
    4571             :     Assert(myTempNamespaceSubID == InvalidSubTransactionId);
    4572         652 :     myTempNamespaceSubID = GetCurrentSubTransactionId();
    4573             : 
    4574         652 :     baseSearchPathValid = false;    /* need to rebuild list */
    4575         652 :     searchPathCacheValid = false;
    4576         652 : }
    4577             : 
    4578             : /*
    4579             :  * End-of-transaction cleanup for namespaces.
    4580             :  */
    4581             : void
    4582     1050950 : AtEOXact_Namespace(bool isCommit, bool parallel)
    4583             : {
    4584             :     /*
    4585             :      * If we abort the transaction in which a temp namespace was selected,
    4586             :      * we'll have to do any creation or cleanout work over again.  So, just
    4587             :      * forget the namespace entirely until next time.  On the other hand, if
    4588             :      * we commit then register an exit callback to clean out the temp tables
    4589             :      * at backend shutdown.  (We only want to register the callback once per
    4590             :      * session, so this is a good place to do it.)
    4591             :      */
    4592     1050950 :     if (myTempNamespaceSubID != InvalidSubTransactionId && !parallel)
    4593             :     {
    4594         650 :         if (isCommit)
    4595         626 :             before_shmem_exit(RemoveTempRelationsCallback, 0);
    4596             :         else
    4597             :         {
    4598          24 :             myTempNamespace = InvalidOid;
    4599          24 :             myTempToastNamespace = InvalidOid;
    4600          24 :             baseSearchPathValid = false;    /* need to rebuild list */
    4601          24 :             searchPathCacheValid = false;
    4602             : 
    4603             :             /*
    4604             :              * Reset the temporary namespace flag in MyProc.  We assume that
    4605             :              * this operation is atomic.
    4606             :              *
    4607             :              * Because this transaction is aborting, the pg_namespace row is
    4608             :              * not visible to anyone else anyway, but that doesn't matter:
    4609             :              * it's not a problem if objects contained in this namespace are
    4610             :              * removed concurrently.
    4611             :              */
    4612          24 :             MyProc->tempNamespaceId = InvalidOid;
    4613             :         }
    4614         650 :         myTempNamespaceSubID = InvalidSubTransactionId;
    4615             :     }
    4616             : 
    4617     1050950 : }
    4618             : 
    4619             : /*
    4620             :  * AtEOSubXact_Namespace
    4621             :  *
    4622             :  * At subtransaction commit, propagate the temp-namespace-creation
    4623             :  * flag to the parent subtransaction.
    4624             :  *
    4625             :  * At subtransaction abort, forget the flag if we set it up.
    4626             :  */
    4627             : void
    4628       20032 : AtEOSubXact_Namespace(bool isCommit, SubTransactionId mySubid,
    4629             :                       SubTransactionId parentSubid)
    4630             : {
    4631             : 
    4632       20032 :     if (myTempNamespaceSubID == mySubid)
    4633             :     {
    4634           8 :         if (isCommit)
    4635           6 :             myTempNamespaceSubID = parentSubid;
    4636             :         else
    4637             :         {
    4638           2 :             myTempNamespaceSubID = InvalidSubTransactionId;
    4639             :             /* TEMP namespace creation failed, so reset state */
    4640           2 :             myTempNamespace = InvalidOid;
    4641           2 :             myTempToastNamespace = InvalidOid;
    4642           2 :             baseSearchPathValid = false;    /* need to rebuild list */
    4643           2 :             searchPathCacheValid = false;
    4644             : 
    4645             :             /*
    4646             :              * Reset the temporary namespace flag in MyProc.  We assume that
    4647             :              * this operation is atomic.
    4648             :              *
    4649             :              * Because this subtransaction is aborting, the pg_namespace row
    4650             :              * is not visible to anyone else anyway, but that doesn't matter:
    4651             :              * it's not a problem if objects contained in this namespace are
    4652             :              * removed concurrently.
    4653             :              */
    4654           2 :             MyProc->tempNamespaceId = InvalidOid;
    4655             :         }
    4656             :     }
    4657       20032 : }
    4658             : 
    4659             : /*
    4660             :  * Remove all relations in the specified temp namespace.
    4661             :  *
    4662             :  * This is called at backend shutdown (if we made any temp relations).
    4663             :  * It is also called when we begin using a pre-existing temp namespace,
    4664             :  * in order to clean out any relations that might have been created by
    4665             :  * a crashed backend.
    4666             :  */
    4667             : static void
    4668         866 : RemoveTempRelations(Oid tempNamespaceId)
    4669             : {
    4670             :     ObjectAddress object;
    4671             : 
    4672             :     /*
    4673             :      * We want to get rid of everything in the target namespace, but not the
    4674             :      * namespace itself (deleting it only to recreate it later would be a
    4675             :      * waste of cycles).  Hence, specify SKIP_ORIGINAL.  It's also an INTERNAL
    4676             :      * deletion, and we want to not drop any extensions that might happen to
    4677             :      * own temp objects.
    4678             :      */
    4679         866 :     object.classId = NamespaceRelationId;
    4680         866 :     object.objectId = tempNamespaceId;
    4681         866 :     object.objectSubId = 0;
    4682             : 
    4683         866 :     performDeletion(&object, DROP_CASCADE,
    4684             :                     PERFORM_DELETION_INTERNAL |
    4685             :                     PERFORM_DELETION_QUIETLY |
    4686             :                     PERFORM_DELETION_SKIP_ORIGINAL |
    4687             :                     PERFORM_DELETION_SKIP_EXTENSIONS);
    4688         866 : }
    4689             : 
    4690             : /*
    4691             :  * Callback to remove temp relations at backend exit.
    4692             :  */
    4693             : static void
    4694         626 : RemoveTempRelationsCallback(int code, Datum arg)
    4695             : {
    4696         626 :     if (OidIsValid(myTempNamespace))    /* should always be true */
    4697             :     {
    4698             :         /* Need to ensure we have a usable transaction. */
    4699         626 :         AbortOutOfAnyTransaction();
    4700         626 :         StartTransactionCommand();
    4701         626 :         PushActiveSnapshot(GetTransactionSnapshot());
    4702             : 
    4703         626 :         RemoveTempRelations(myTempNamespace);
    4704             : 
    4705         626 :         PopActiveSnapshot();
    4706         626 :         CommitTransactionCommand();
    4707             :     }
    4708         626 : }
    4709             : 
    4710             : /*
    4711             :  * Remove all temp tables from the temporary namespace.
    4712             :  */
    4713             : void
    4714          14 : ResetTempTableNamespace(void)
    4715             : {
    4716          14 :     if (OidIsValid(myTempNamespace))
    4717          14 :         RemoveTempRelations(myTempNamespace);
    4718          14 : }
    4719             : 
    4720             : 
    4721             : /*
    4722             :  * Routines for handling the GUC variable 'search_path'.
    4723             :  */
    4724             : 
    4725             : /* check_hook: validate new search_path value */
    4726             : bool
    4727      370186 : check_search_path(char **newval, void **extra, GucSource source)
    4728             : {
    4729      370186 :     Oid         roleid = InvalidOid;
    4730      370186 :     const char *searchPath = *newval;
    4731             :     char       *rawname;
    4732             :     List       *namelist;
    4733      370186 :     bool        use_cache = (SearchPathCacheContext != NULL);
    4734             : 
    4735             :     /*
    4736             :      * We used to try to check that the named schemas exist, but there are
    4737             :      * many valid use-cases for having search_path settings that include
    4738             :      * schemas that don't exist; and often, we are not inside a transaction
    4739             :      * here and so can't consult the system catalogs anyway.  So now, the only
    4740             :      * requirement is syntactic validity of the identifier list.
    4741             :      *
    4742             :      * Checking only the syntactic validity also allows us to use the search
    4743             :      * path cache (if available) to avoid calling SplitIdentifierString() on
    4744             :      * the same string repeatedly.
    4745             :      */
    4746      370186 :     if (use_cache)
    4747             :     {
    4748      152032 :         spcache_init();
    4749             : 
    4750      152032 :         roleid = GetUserId();
    4751             : 
    4752      152032 :         if (spcache_lookup(searchPath, roleid) != NULL)
    4753      142844 :             return true;
    4754             :     }
    4755             : 
    4756             :     /*
    4757             :      * Ensure validity check succeeds before creating cache entry.
    4758             :      */
    4759             : 
    4760      227342 :     rawname = pstrdup(searchPath);  /* need a modifiable copy */
    4761             : 
    4762             :     /* Parse string into list of identifiers */
    4763      227342 :     if (!SplitIdentifierString(rawname, ',', &namelist))
    4764             :     {
    4765             :         /* syntax error in name list */
    4766           0 :         GUC_check_errdetail("List syntax is invalid.");
    4767           0 :         pfree(rawname);
    4768           0 :         list_free(namelist);
    4769           0 :         return false;
    4770             :     }
    4771      227342 :     pfree(rawname);
    4772      227342 :     list_free(namelist);
    4773             : 
    4774             :     /* OK to create empty cache entry */
    4775      227342 :     if (use_cache)
    4776        9188 :         (void) spcache_insert(searchPath, roleid);
    4777             : 
    4778      227342 :     return true;
    4779             : }
    4780             : 
    4781             : /* assign_hook: do extra actions as needed */
    4782             : void
    4783      726128 : assign_search_path(const char *newval, void *extra)
    4784             : {
    4785             :     /* don't access search_path during bootstrap */
    4786             :     Assert(!IsBootstrapProcessingMode());
    4787             : 
    4788             :     /*
    4789             :      * We mark the path as needing recomputation, but don't do anything until
    4790             :      * it's needed.  This avoids trying to do database access during GUC
    4791             :      * initialization, or outside a transaction.
    4792             :      *
    4793             :      * This does not invalidate the search path cache, so if this value had
    4794             :      * been previously set and no syscache invalidations happened,
    4795             :      * recomputation may not be necessary.
    4796             :      */
    4797      726128 :     baseSearchPathValid = false;
    4798      726128 : }
    4799             : 
    4800             : /*
    4801             :  * InitializeSearchPath: initialize module during InitPostgres.
    4802             :  *
    4803             :  * This is called after we are up enough to be able to do catalog lookups.
    4804             :  */
    4805             : void
    4806       32442 : InitializeSearchPath(void)
    4807             : {
    4808       32442 :     if (IsBootstrapProcessingMode())
    4809             :     {
    4810             :         /*
    4811             :          * In bootstrap mode, the search path must be 'pg_catalog' so that
    4812             :          * tables are created in the proper namespace; ignore the GUC setting.
    4813             :          */
    4814             :         MemoryContext oldcxt;
    4815             : 
    4816         100 :         oldcxt = MemoryContextSwitchTo(TopMemoryContext);
    4817         100 :         baseSearchPath = list_make1_oid(PG_CATALOG_NAMESPACE);
    4818         100 :         MemoryContextSwitchTo(oldcxt);
    4819         100 :         baseCreationNamespace = PG_CATALOG_NAMESPACE;
    4820         100 :         baseTempCreationPending = false;
    4821         100 :         baseSearchPathValid = true;
    4822         100 :         namespaceUser = GetUserId();
    4823         100 :         activeSearchPath = baseSearchPath;
    4824         100 :         activeCreationNamespace = baseCreationNamespace;
    4825         100 :         activeTempCreationPending = baseTempCreationPending;
    4826         100 :         activePathGeneration++; /* pro forma */
    4827             :     }
    4828             :     else
    4829             :     {
    4830             :         /*
    4831             :          * In normal mode, arrange for a callback on any syscache invalidation
    4832             :          * that will affect the search_path cache.
    4833             :          */
    4834             : 
    4835             :         /* namespace name or ACLs may have changed */
    4836       32342 :         CacheRegisterSyscacheCallback(NAMESPACEOID,
    4837             :                                       InvalidationCallback,
    4838             :                                       (Datum) 0);
    4839             : 
    4840             :         /* role name may affect the meaning of "$user" */
    4841       32342 :         CacheRegisterSyscacheCallback(AUTHOID,
    4842             :                                       InvalidationCallback,
    4843             :                                       (Datum) 0);
    4844             : 
    4845             :         /* role membership may affect ACLs */
    4846       32342 :         CacheRegisterSyscacheCallback(AUTHMEMROLEMEM,
    4847             :                                       InvalidationCallback,
    4848             :                                       (Datum) 0);
    4849             : 
    4850             :         /* database owner may affect ACLs */
    4851       32342 :         CacheRegisterSyscacheCallback(DATABASEOID,
    4852             :                                       InvalidationCallback,
    4853             :                                       (Datum) 0);
    4854             : 
    4855             :         /* Force search path to be recomputed on next use */
    4856       32342 :         baseSearchPathValid = false;
    4857       32342 :         searchPathCacheValid = false;
    4858             :     }
    4859       32442 : }
    4860             : 
    4861             : /*
    4862             :  * InvalidationCallback
    4863             :  *      Syscache inval callback function
    4864             :  */
    4865             : static void
    4866       76620 : InvalidationCallback(Datum arg, int cacheid, uint32 hashvalue)
    4867             : {
    4868             :     /*
    4869             :      * Force search path to be recomputed on next use, also invalidating the
    4870             :      * search path cache (because namespace names, ACLs, or role names may
    4871             :      * have changed).
    4872             :      */
    4873       76620 :     baseSearchPathValid = false;
    4874       76620 :     searchPathCacheValid = false;
    4875       76620 : }
    4876             : 
    4877             : /*
    4878             :  * Fetch the active search path. The return value is a palloc'ed list
    4879             :  * of OIDs; the caller is responsible for freeing this storage as
    4880             :  * appropriate.
    4881             :  *
    4882             :  * The returned list includes the implicitly-prepended namespaces only if
    4883             :  * includeImplicit is true.
    4884             :  *
    4885             :  * Note: calling this may result in a CommandCounterIncrement operation,
    4886             :  * if we have to create or clean out the temp namespace.
    4887             :  */
    4888             : List *
    4889        1018 : fetch_search_path(bool includeImplicit)
    4890             : {
    4891             :     List       *result;
    4892             : 
    4893        1018 :     recomputeNamespacePath();
    4894             : 
    4895             :     /*
    4896             :      * If the temp namespace should be first, force it to exist.  This is so
    4897             :      * that callers can trust the result to reflect the actual default
    4898             :      * creation namespace.  It's a bit bogus to do this here, since
    4899             :      * current_schema() is supposedly a stable function without side-effects,
    4900             :      * but the alternatives seem worse.
    4901             :      */
    4902        1018 :     if (activeTempCreationPending)
    4903             :     {
    4904           6 :         AccessTempTableNamespace(true);
    4905           6 :         recomputeNamespacePath();
    4906             :     }
    4907             : 
    4908        1018 :     result = list_copy(activeSearchPath);
    4909        1018 :     if (!includeImplicit)
    4910             :     {
    4911        1742 :         while (result && linitial_oid(result) != activeCreationNamespace)
    4912         910 :             result = list_delete_first(result);
    4913             :     }
    4914             : 
    4915        1018 :     return result;
    4916             : }
    4917             : 
    4918             : /*
    4919             :  * Fetch the active search path into a caller-allocated array of OIDs.
    4920             :  * Returns the number of path entries.  (If this is more than sarray_len,
    4921             :  * then the data didn't fit and is not all stored.)
    4922             :  *
    4923             :  * The returned list always includes the implicitly-prepended namespaces,
    4924             :  * but never includes the temp namespace.  (This is suitable for existing
    4925             :  * users, which would want to ignore the temp namespace anyway.)  This
    4926             :  * definition allows us to not worry about initializing the temp namespace.
    4927             :  */
    4928             : int
    4929      686718 : fetch_search_path_array(Oid *sarray, int sarray_len)
    4930             : {
    4931      686718 :     int         count = 0;
    4932             :     ListCell   *l;
    4933             : 
    4934      686718 :     recomputeNamespacePath();
    4935             : 
    4936     2039996 :     foreach(l, activeSearchPath)
    4937             :     {
    4938     1353278 :         Oid         namespaceId = lfirst_oid(l);
    4939             : 
    4940     1353278 :         if (namespaceId == myTempNamespace)
    4941      184906 :             continue;           /* do not include temp namespace */
    4942             : 
    4943     1168372 :         if (count < sarray_len)
    4944     1168372 :             sarray[count] = namespaceId;
    4945     1168372 :         count++;
    4946             :     }
    4947             : 
    4948      686718 :     return count;
    4949             : }
    4950             : 
    4951             : 
    4952             : /*
    4953             :  * Export the FooIsVisible functions as SQL-callable functions.
    4954             :  *
    4955             :  * Note: as of Postgres 8.4, these will silently return NULL if called on
    4956             :  * a nonexistent object OID, rather than failing.  This is to avoid race
    4957             :  * condition errors when a query that's scanning a catalog using an MVCC
    4958             :  * snapshot uses one of these functions.  The underlying IsVisible functions
    4959             :  * always use an up-to-date snapshot and so might see the object as already
    4960             :  * gone when it's still visible to the transaction snapshot.
    4961             :  */
    4962             : 
    4963             : Datum
    4964       23694 : pg_table_is_visible(PG_FUNCTION_ARGS)
    4965             : {
    4966       23694 :     Oid         oid = PG_GETARG_OID(0);
    4967             :     bool        result;
    4968       23694 :     bool        is_missing = false;
    4969             : 
    4970       23694 :     result = RelationIsVisibleExt(oid, &is_missing);
    4971             : 
    4972       23694 :     if (is_missing)
    4973          10 :         PG_RETURN_NULL();
    4974       23684 :     PG_RETURN_BOOL(result);
    4975             : }
    4976             : 
    4977             : Datum
    4978        3966 : pg_type_is_visible(PG_FUNCTION_ARGS)
    4979             : {
    4980        3966 :     Oid         oid = PG_GETARG_OID(0);
    4981             :     bool        result;
    4982        3966 :     bool        is_missing = false;
    4983             : 
    4984        3966 :     result = TypeIsVisibleExt(oid, &is_missing);
    4985             : 
    4986        3966 :     if (is_missing)
    4987           0 :         PG_RETURN_NULL();
    4988        3966 :     PG_RETURN_BOOL(result);
    4989             : }
    4990             : 
    4991             : Datum
    4992        7594 : pg_function_is_visible(PG_FUNCTION_ARGS)
    4993             : {
    4994        7594 :     Oid         oid = PG_GETARG_OID(0);
    4995             :     bool        result;
    4996        7594 :     bool        is_missing = false;
    4997             : 
    4998        7594 :     result = FunctionIsVisibleExt(oid, &is_missing);
    4999             : 
    5000        7594 :     if (is_missing)
    5001           0 :         PG_RETURN_NULL();
    5002        7594 :     PG_RETURN_BOOL(result);
    5003             : }
    5004             : 
    5005             : Datum
    5006        1700 : pg_operator_is_visible(PG_FUNCTION_ARGS)
    5007             : {
    5008        1700 :     Oid         oid = PG_GETARG_OID(0);
    5009             :     bool        result;
    5010        1700 :     bool        is_missing = false;
    5011             : 
    5012        1700 :     result = OperatorIsVisibleExt(oid, &is_missing);
    5013             : 
    5014        1700 :     if (is_missing)
    5015           0 :         PG_RETURN_NULL();
    5016        1700 :     PG_RETURN_BOOL(result);
    5017             : }
    5018             : 
    5019             : Datum
    5020          18 : pg_opclass_is_visible(PG_FUNCTION_ARGS)
    5021             : {
    5022          18 :     Oid         oid = PG_GETARG_OID(0);
    5023             :     bool        result;
    5024          18 :     bool        is_missing = false;
    5025             : 
    5026          18 :     result = OpclassIsVisibleExt(oid, &is_missing);
    5027             : 
    5028          18 :     if (is_missing)
    5029           0 :         PG_RETURN_NULL();
    5030          18 :     PG_RETURN_BOOL(result);
    5031             : }
    5032             : 
    5033             : Datum
    5034         318 : pg_opfamily_is_visible(PG_FUNCTION_ARGS)
    5035             : {
    5036         318 :     Oid         oid = PG_GETARG_OID(0);
    5037             :     bool        result;
    5038         318 :     bool        is_missing = false;
    5039             : 
    5040         318 :     result = OpfamilyIsVisibleExt(oid, &is_missing);
    5041             : 
    5042         318 :     if (is_missing)
    5043           0 :         PG_RETURN_NULL();
    5044         318 :     PG_RETURN_BOOL(result);
    5045             : }
    5046             : 
    5047             : Datum
    5048           0 : pg_collation_is_visible(PG_FUNCTION_ARGS)
    5049             : {
    5050           0 :     Oid         oid = PG_GETARG_OID(0);
    5051             :     bool        result;
    5052           0 :     bool        is_missing = false;
    5053             : 
    5054           0 :     result = CollationIsVisibleExt(oid, &is_missing);
    5055             : 
    5056           0 :     if (is_missing)
    5057           0 :         PG_RETURN_NULL();
    5058           0 :     PG_RETURN_BOOL(result);
    5059             : }
    5060             : 
    5061             : Datum
    5062           0 : pg_conversion_is_visible(PG_FUNCTION_ARGS)
    5063             : {
    5064           0 :     Oid         oid = PG_GETARG_OID(0);
    5065             :     bool        result;
    5066           0 :     bool        is_missing = false;
    5067             : 
    5068           0 :     result = ConversionIsVisibleExt(oid, &is_missing);
    5069             : 
    5070           0 :     if (is_missing)
    5071           0 :         PG_RETURN_NULL();
    5072           0 :     PG_RETURN_BOOL(result);
    5073             : }
    5074             : 
    5075             : Datum
    5076         372 : pg_statistics_obj_is_visible(PG_FUNCTION_ARGS)
    5077             : {
    5078         372 :     Oid         oid = PG_GETARG_OID(0);
    5079             :     bool        result;
    5080         372 :     bool        is_missing = false;
    5081             : 
    5082         372 :     result = StatisticsObjIsVisibleExt(oid, &is_missing);
    5083             : 
    5084         372 :     if (is_missing)
    5085           0 :         PG_RETURN_NULL();
    5086         372 :     PG_RETURN_BOOL(result);
    5087             : }
    5088             : 
    5089             : Datum
    5090           0 : pg_ts_parser_is_visible(PG_FUNCTION_ARGS)
    5091             : {
    5092           0 :     Oid         oid = PG_GETARG_OID(0);
    5093             :     bool        result;
    5094           0 :     bool        is_missing = false;
    5095             : 
    5096           0 :     result = TSParserIsVisibleExt(oid, &is_missing);
    5097             : 
    5098           0 :     if (is_missing)
    5099           0 :         PG_RETURN_NULL();
    5100           0 :     PG_RETURN_BOOL(result);
    5101             : }
    5102             : 
    5103             : Datum
    5104           0 : pg_ts_dict_is_visible(PG_FUNCTION_ARGS)
    5105             : {
    5106           0 :     Oid         oid = PG_GETARG_OID(0);
    5107             :     bool        result;
    5108           0 :     bool        is_missing = false;
    5109             : 
    5110           0 :     result = TSDictionaryIsVisibleExt(oid, &is_missing);
    5111             : 
    5112           0 :     if (is_missing)
    5113           0 :         PG_RETURN_NULL();
    5114           0 :     PG_RETURN_BOOL(result);
    5115             : }
    5116             : 
    5117             : Datum
    5118           0 : pg_ts_template_is_visible(PG_FUNCTION_ARGS)
    5119             : {
    5120           0 :     Oid         oid = PG_GETARG_OID(0);
    5121             :     bool        result;
    5122           0 :     bool        is_missing = false;
    5123             : 
    5124           0 :     result = TSTemplateIsVisibleExt(oid, &is_missing);
    5125             : 
    5126           0 :     if (is_missing)
    5127           0 :         PG_RETURN_NULL();
    5128           0 :     PG_RETURN_BOOL(result);
    5129             : }
    5130             : 
    5131             : Datum
    5132           0 : pg_ts_config_is_visible(PG_FUNCTION_ARGS)
    5133             : {
    5134           0 :     Oid         oid = PG_GETARG_OID(0);
    5135             :     bool        result;
    5136           0 :     bool        is_missing = false;
    5137             : 
    5138           0 :     result = TSConfigIsVisibleExt(oid, &is_missing);
    5139             : 
    5140           0 :     if (is_missing)
    5141           0 :         PG_RETURN_NULL();
    5142           0 :     PG_RETURN_BOOL(result);
    5143             : }
    5144             : 
    5145             : Datum
    5146         644 : pg_my_temp_schema(PG_FUNCTION_ARGS)
    5147             : {
    5148         644 :     PG_RETURN_OID(myTempNamespace);
    5149             : }
    5150             : 
    5151             : Datum
    5152       13318 : pg_is_other_temp_schema(PG_FUNCTION_ARGS)
    5153             : {
    5154       13318 :     Oid         oid = PG_GETARG_OID(0);
    5155             : 
    5156       13318 :     PG_RETURN_BOOL(isOtherTempNamespace(oid));
    5157             : }

Generated by: LCOV version 1.16