LCOV - code coverage report
Current view: top level - src/backend/catalog - namespace.c (source / functions) Hit Total Coverage
Test: PostgreSQL 17devel Lines: 1284 1504 85.4 %
Date: 2023-12-10 04:11:27 Functions: 100 110 90.9 %
Legend: Lines: hit not hit

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

Generated by: LCOV version 1.14