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

Generated by: LCOV version 1.14