LCOV - code coverage report
Current view: top level - src/backend/catalog - namespace.c (source / functions) Hit Total Coverage
Test: PostgreSQL 17devel Lines: 1294 1507 85.9 %
Date: 2024-05-19 07:11:14 Functions: 101 110 91.8 %
Legend: Lines: hit not hit

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

Generated by: LCOV version 1.14