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

Generated by: LCOV version 2.0-1