Eine aufbereitete Darstellung der Quelle

 
     
 
 
Anforderungen  |   Konzepte  |   Entwurf  |   Entwicklung  |   Qualitätssicherung  |   Lebenszyklus  |   Steuerung
 
 
 
 

Benutzer

Impressum namespace.c   Interaktion und
Portierbarkeitunbekannt

 
/*-------------------------------------------------------------------------
 *
 * namespace.c
 *   code to support accessing and searching namespaces
 *
 * This is separate from pg_namespace.c, which contains the routines that
 * directly manipulate the pg_namespace system catalog.  This module
 * provides routines associated with defining a "namespace search path"
 * and implementing search-path-controlled searches.
 *
 *
 * Portions Copyright (c) 1996-2025, PostgreSQL Global Development Group
 * Portions Copyright (c) 1994, Regents of the University of California
 *
 * IDENTIFICATION
 *   src/backend/catalog/namespace.c
 *
 *-------------------------------------------------------------------------
 */

#include "postgres.h"

#include "access/htup_details.h"
#include "access/parallel.h"
#include "access/xact.h"
#include "access/xlog.h"
#include "catalog/dependency.h"
#include "catalog/namespace.h"
#include "catalog/objectaccess.h"
#include "catalog/pg_authid.h"
#include "catalog/pg_collation.h"
#include "catalog/pg_conversion.h"
#include "catalog/pg_database.h"
#include "catalog/pg_namespace.h"
#include "catalog/pg_opclass.h"
#include "catalog/pg_operator.h"
#include "catalog/pg_opfamily.h"
#include "catalog/pg_proc.h"
#include "catalog/pg_statistic_ext.h"
#include "catalog/pg_ts_config.h"
#include "catalog/pg_ts_dict.h"
#include "catalog/pg_ts_parser.h"
#include "catalog/pg_ts_template.h"
#include "catalog/pg_type.h"
#include "commands/dbcommands.h"
#include "common/hashfn_unstable.h"
#include "funcapi.h"
#include "mb/pg_wchar.h"
#include "miscadmin.h"
#include "nodes/makefuncs.h"
#include "storage/ipc.h"
#include "storage/lmgr.h"
#include "storage/procarray.h"
#include "utils/acl.h"
#include "utils/builtins.h"
#include "utils/catcache.h"
#include "utils/guc_hooks.h"
#include "utils/inval.h"
#include "utils/lsyscache.h"
#include "utils/memutils.h"
#include "utils/snapmgr.h"
#include "utils/syscache.h"
#include "utils/varlena.h"


/*
 * The namespace search path is a possibly-empty list of namespace OIDs.
 * In addition to the explicit list, implicitly-searched namespaces
 * may be included:
 *
 * 1. If a TEMP table namespace has been initialized in this session, it
 * is implicitly searched first.
 *
 * 2. The system catalog namespace is always searched.  If the system
 * namespace is present in the explicit path then it will be searched in
 * the specified order; otherwise it will be searched after TEMP tables and
 * *before* the explicit list.  (It might seem that the system namespace
 * should be implicitly last, but this behavior appears to be required by
 * SQL99.  Also, this provides a way to search the system namespace first
 * without thereby making it the default creation target namespace.)
 *
 * For security reasons, searches using the search path will ignore the temp
 * namespace when searching for any object type other than relations and
 * types.  (We must allow types since temp tables have rowtypes.)
 *
 * The default creation target namespace is always the first element of the
 * explicit list.  If the explicit list is empty, there is no default target.
 *
 * The textual specification of search_path can include "$user" to refer to
 * the namespace named the same as the current user, if any.  (This is just
 * ignored if there is no such namespace.) Also, it can include "pg_temp"
 * to refer to the current backend's temp namespace.  This is usually also
 * ignorable if the temp namespace hasn't been set up, but there's a special
 * case: if "pg_temp" appears first then it should be the default creation
 * target.  We kluge this case a little bit so that the temp namespace isn't
 * set up until the first attempt to create something in it.  (The reason for
 * klugery is that we can't create the temp namespace outside a transaction,
 * but initial GUC processing of search_path happens outside a transaction.)
 * activeTempCreationPending is true if "pg_temp" appears first in the string
 * but is not reflected in activeCreationNamespace because the namespace isn't
 * set up yet.
 *
 * In bootstrap mode, the search path is set equal to "pg_catalog", so that
 * the system namespace is the only one searched or inserted into.
 * initdb is also careful to set search_path to "pg_catalog" for its
 * post-bootstrap standalone backend runs.  Otherwise the default search
 * path is determined by GUC.  The factory default path contains the PUBLIC
 * namespace (if it exists), preceded by the user's personal namespace
 * (if one exists).
 *
 * activeSearchPath is always the actually active path; it points to
 * baseSearchPath which is the list derived from namespace_search_path.
 *
 * If baseSearchPathValid is false, then baseSearchPath (and other derived
 * variables) need to be recomputed from namespace_search_path, or retrieved
 * from the search path cache if there haven't been any syscache
 * invalidations.  We mark it invalid upon an assignment to
 * namespace_search_path or receipt of a syscache invalidation event for
 * pg_namespace or pg_authid.  The recomputation is done during the next
 * lookup attempt.
 *
 * Any namespaces mentioned in namespace_search_path that are not readable
 * by the current user ID are simply left out of baseSearchPath; so
 * we have to be willing to recompute the path when current userid changes.
 * namespaceUser is the userid the path has been computed for.
 *
 * Note: all data pointed to by these List variables is in TopMemoryContext.
 *
 * activePathGeneration is incremented whenever the effective values of
 * activeSearchPath/activeCreationNamespace/activeTempCreationPending change.
 * This can be used to quickly detect whether any change has happened since
 * a previous examination of the search path state.
 */


/* These variables define the actually active state: */

static List *activeSearchPath = NIL;

/* default place to create stuff; if InvalidOid, no default */
static Oid activeCreationNamespace = InvalidOid;

/* if true, activeCreationNamespace is wrong, it should be temp namespace */
static bool activeTempCreationPending = false;

/* current generation counter; make sure this is never zero */
static uint64 activePathGeneration = 1;

/* These variables are the values last derived from namespace_search_path: */

static List *baseSearchPath = NIL;

static Oid baseCreationNamespace = InvalidOid;

static bool baseTempCreationPending = false;

static Oid namespaceUser = InvalidOid;

/* The above four values are valid only if baseSearchPathValid */
static bool baseSearchPathValid = true;

/*
 * Storage for search path cache.  Clear searchPathCacheValid as a simple
 * way to invalidate *all* the cache entries, not just the active one.
 */

static bool searchPathCacheValid = false;
static MemoryContext SearchPathCacheContext = NULL;

typedef struct SearchPathCacheKey
{
 const char *searchPath;
 Oid   roleid;
} SearchPathCacheKey;

typedef struct SearchPathCacheEntry
{
 SearchPathCacheKey key;
 List    *oidlist;  /* namespace OIDs that pass ACL checks */
 List    *finalPath;  /* cached final computed search path */
 Oid   firstNS;  /* first explicitly-listed namespace */
 bool  temp_missing;
 bool  forceRecompute; /* force recompute of finalPath */

 /* needed for simplehash */
 char  status;
} SearchPathCacheEntry;

/*
 * myTempNamespace is InvalidOid until and unless a TEMP namespace is set up
 * in a particular backend session (this happens when a CREATE TEMP TABLE
 * command is first executed).  Thereafter it's the OID of the temp namespace.
 *
 * myTempToastNamespace is the OID of the namespace for my temp tables' toast
 * tables.  It is set when myTempNamespace is, and is InvalidOid before that.
 *
 * myTempNamespaceSubID shows whether we've created the TEMP namespace in the
 * current subtransaction.  The flag propagates up the subtransaction tree,
 * so the main transaction will correctly recognize the flag if all
 * intermediate subtransactions commit.  When it is InvalidSubTransactionId,
 * we either haven't made the TEMP namespace yet, or have successfully
 * committed its creation, depending on whether myTempNamespace is valid.
 */

static Oid myTempNamespace = InvalidOid;

static Oid myTempToastNamespace = InvalidOid;

static SubTransactionId myTempNamespaceSubID = InvalidSubTransactionId;

/*
 * This is the user's textual search path specification --- it's the value
 * of the GUC variable 'search_path'.
 */

char    *namespace_search_path = NULL;


/* Local functions */
static bool RelationIsVisibleExt(Oid relid, bool *is_missing);
static bool TypeIsVisibleExt(Oid typid, bool *is_missing);
static bool FunctionIsVisibleExt(Oid funcid, bool *is_missing);
static bool OperatorIsVisibleExt(Oid oprid, bool *is_missing);
static bool OpclassIsVisibleExt(Oid opcid, bool *is_missing);
static bool OpfamilyIsVisibleExt(Oid opfid, bool *is_missing);
static bool CollationIsVisibleExt(Oid collid, bool *is_missing);
static bool ConversionIsVisibleExt(Oid conid, bool *is_missing);
static bool StatisticsObjIsVisibleExt(Oid stxid, bool *is_missing);
static bool TSParserIsVisibleExt(Oid prsId, bool *is_missing);
static bool TSDictionaryIsVisibleExt(Oid dictId, bool *is_missing);
static bool TSTemplateIsVisibleExt(Oid tmplId, bool *is_missing);
static bool TSConfigIsVisibleExt(Oid cfgid, bool *is_missing);
static void recomputeNamespacePath(void);
static void AccessTempTableNamespace(bool force);
static void InitTempTableNamespace(void);
static void RemoveTempRelations(Oid tempNamespaceId);
static void RemoveTempRelationsCallback(int code, Datum arg);
static void InvalidationCallback(Datum arg, int cacheid, uint32 hashvalue);
static bool MatchNamedCall(HeapTuple proctup, int nargs, List *argnames,
         bool include_out_arguments, int pronargs,
         int **argnumbers);

/*
 * Recomputing the namespace path can be costly when done frequently, such as
 * when a function has search_path set in proconfig. Add a search path cache
 * that can be used by recomputeNamespacePath().
 *
 * The cache is also used to remember already-validated strings in
 * check_search_path() to avoid the need to call SplitIdentifierString()
 * repeatedly.
 *
 * The search path cache is based on a wrapper around a simplehash hash table
 * (nsphash, defined below). The spcache wrapper deals with OOM while trying
 * to initialize a key, optimizes repeated lookups of the same key, and also
 * offers a more convenient API.
 */


static inline uint32
spcachekey_hash(SearchPathCacheKey key)
{
 fasthash_state hs;
 int   sp_len;

 fasthash_init(&hs, 0);

 hs.accum = key.roleid;
 fasthash_combine(&hs);

 /*
  * Combine search path into the hash and save the length for tweaking the
  * final mix.
 */

 sp_len = fasthash_accum_cstring(&hs, key.searchPath);

 return fasthash_final32(&hs, sp_len);
}

static inline bool
spcachekey_equal(SearchPathCacheKey a, SearchPathCacheKey b)
{
 return a.roleid == b.roleid &&
  strcmp(a.searchPath, b.searchPath) == 0;
}

#define SH_PREFIX  nsphash
#define SH_ELEMENT_TYPE SearchPathCacheEntry
#define SH_KEY_TYPE  SearchPathCacheKey
#define SH_KEY   key
#define SH_HASH_KEY(tb, key)    spcachekey_hash(key)
#define SH_EQUAL(tb, a, b)  spcachekey_equal(a, b)
#define SH_SCOPE  static inline
#define SH_DECLARE
#define SH_DEFINE
#include "lib/simplehash.h"

/*
 * We only expect a small number of unique search_path strings to be used. If
 * this cache grows to an unreasonable size, reset it to avoid steady-state
 * memory growth. Most likely, only a few of those entries will benefit from
 * the cache, and the cache will be quickly repopulated with such entries.
 */

#define SPCACHE_RESET_THRESHOLD  256

static nsphash_hash *SearchPathCache = NULL;
static SearchPathCacheEntry *LastSearchPathCacheEntry = NULL;

/*
 * Create or reset search_path cache as necessary.
 */

static void
spcache_init(void)
{
 if (SearchPathCache && searchPathCacheValid &&
  SearchPathCache->members < SPCACHE_RESET_THRESHOLD)
  return;

 searchPathCacheValid = false;
 baseSearchPathValid = false;

 /*
  * Make sure we don't leave dangling pointers if a failure happens during
  * initialization.
 */

 SearchPathCache = NULL;
 LastSearchPathCacheEntry = NULL;

 if (SearchPathCacheContext == NULL)
 {
  /* Make the context we'll keep search path cache hashtable in */
  SearchPathCacheContext = AllocSetContextCreate(TopMemoryContext,
                "search_path processing cache",
                ALLOCSET_DEFAULT_SIZES);
 }
 else
 {
  MemoryContextReset(SearchPathCacheContext);
 }

 /* arbitrary initial starting size of 16 elements */
 SearchPathCache = nsphash_create(SearchPathCacheContext, 16, NULL);
 searchPathCacheValid = true;
}

/*
 * Look up entry in search path cache without inserting. Returns NULL if not
 * present.
 */

static SearchPathCacheEntry *
spcache_lookup(const char *searchPath, Oid roleid)
{
 if (LastSearchPathCacheEntry &&
  LastSearchPathCacheEntry->key.roleid == roleid &&
  strcmp(LastSearchPathCacheEntry->key.searchPath, searchPath) == 0)
 {
  return LastSearchPathCacheEntry;
 }
 else
 {
  SearchPathCacheEntry *entry;
  SearchPathCacheKey cachekey = {
   .searchPath = searchPath,
   .roleid = roleid
  };

  entry = nsphash_lookup(SearchPathCache, cachekey);
  if (entry)
   LastSearchPathCacheEntry = entry;
  return entry;
 }
}

/*
 * Look up or insert entry in search path cache.
 *
 * Initialize key safely, so that OOM does not leave an entry without a valid
 * key. Caller must ensure that non-key contents are properly initialized.
 */

static SearchPathCacheEntry *
spcache_insert(const char *searchPath, Oid roleid)
{
 if (LastSearchPathCacheEntry &&
  LastSearchPathCacheEntry->key.roleid == roleid &&
  strcmp(LastSearchPathCacheEntry->key.searchPath, searchPath) == 0)
 {
  return LastSearchPathCacheEntry;
 }
 else
 {
  SearchPathCacheEntry *entry;
  SearchPathCacheKey cachekey = {
   .searchPath = searchPath,
   .roleid = roleid
  };

  /*
   * searchPath is not saved in SearchPathCacheContext. First perform a
   * lookup, and copy searchPath only if we need to create a new entry.
 */

  entry = nsphash_lookup(SearchPathCache, cachekey);

  if (!entry)
  {
   bool  found;

   cachekey.searchPath = MemoryContextStrdup(SearchPathCacheContext, searchPath);
   entry = nsphash_insert(SearchPathCache, cachekey, &found);
   Assert(!found);

   entry->oidlist = NIL;
   entry->finalPath = NIL;
   entry->firstNS = InvalidOid;
   entry->temp_missing = false;
   entry->forceRecompute = false;
   /* do not touch entry->status, used by simplehash */
  }

  LastSearchPathCacheEntry = entry;
  return entry;
 }
}

/*
 * RangeVarGetRelidExtended
 *  Given a RangeVar describing an existing relation,
 *  select the proper namespace and look up the relation OID.
 *
 * If the schema or relation is not found, return InvalidOid if flags contains
 * RVR_MISSING_OK, otherwise raise an error.
 *
 * If flags contains RVR_NOWAIT, throw an error if we'd have to wait for a
 * lock.
 *
 * If flags contains RVR_SKIP_LOCKED, return InvalidOid if we'd have to wait
 * for a lock.
 *
 * flags cannot contain both RVR_NOWAIT and RVR_SKIP_LOCKED.
 *
 * Note that if RVR_MISSING_OK and RVR_SKIP_LOCKED are both specified, a
 * return value of InvalidOid could either mean the relation is missing or it
 * could not be locked.
 *
 * Callback allows caller to check permissions or acquire additional locks
 * prior to grabbing the relation lock.
 */

Oid
RangeVarGetRelidExtended(const RangeVar *relation, LOCKMODE lockmode,
       uint32 flags,
       RangeVarGetRelidCallback callback, void *callback_arg)
{
 uint64  inval_count;
 Oid   relId;
 Oid   oldRelId = InvalidOid;
 bool  retry = false;
 bool  missing_ok = (flags & RVR_MISSING_OK) != 0;

 /* verify that flags do no conflict */
 Assert(!((flags & RVR_NOWAIT) && (flags & RVR_SKIP_LOCKED)));

 /*
  * We check the catalog name and then ignore it.
 */

 if (relation->catalogname)
 {
  if (strcmp(relation->catalogname, get_database_name(MyDatabaseId)) != 0)
   ereport(ERROR,
     (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
      errmsg("cross-database references are not implemented: \"%s.%s.%s\"",
       relation->catalogname, relation->schemaname,
       relation->relname)));
 }

 /*
  * DDL operations can change the results of a name lookup.  Since all such
  * operations will generate invalidation messages, we keep track of
  * whether any such messages show up while we're performing the operation,
  * and retry until either (1) no more invalidation messages show up or (2)
  * the answer doesn't change.
  *
  * But if lockmode = NoLock, then we assume that either the caller is OK
  * with the answer changing under them, or that they already hold some
  * appropriate lock, and therefore return the first answer we get without
  * checking for invalidation messages.  Also, if the requested lock is
  * already held, LockRelationOid will not AcceptInvalidationMessages, so
  * we may fail to notice a change.  We could protect against that case by
  * calling AcceptInvalidationMessages() before beginning this loop, but
  * that would add a significant amount overhead, so for now we don't.
 */

 for (;;)
 {
  /*
   * Remember this value, so that, after looking up the relation name
   * and locking its OID, we can check whether any invalidation messages
   * have been processed that might require a do-over.
 */

  inval_count = SharedInvalidMessageCounter;

  /*
   * Some non-default relpersistence value may have been specified.  The
   * parser never generates such a RangeVar in simple DML, but it can
   * happen in contexts such as "CREATE TEMP TABLE foo (f1 int PRIMARY
   * KEY)".  Such a command will generate an added CREATE INDEX
   * operation, which must be careful to find the temp table, even when
   * pg_temp is not first in the search path.
 */

  if (relation->relpersistence == RELPERSISTENCE_TEMP)
  {
   if (!OidIsValid(myTempNamespace))
    relId = InvalidOid; /* this probably can't happen? */
   else
   {
    if (relation->schemaname)
    {
     Oid   namespaceId;

     namespaceId = LookupExplicitNamespace(relation->schemaname, missing_ok);

     /*
      * For missing_ok, allow a non-existent schema name to
      * return InvalidOid.
 */

     if (namespaceId != myTempNamespace)
      ereport(ERROR,
        (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
         errmsg("temporary tables cannot specify a schema name")));
    }

    relId = get_relname_relid(relation->relname, myTempNamespace);
   }
  }
  else if (relation->schemaname)
  {
   Oid   namespaceId;

   /* use exact schema given */
   namespaceId = LookupExplicitNamespace(relation->schemaname, missing_ok);
   if (missing_ok && !OidIsValid(namespaceId))
    relId = InvalidOid;
   else
    relId = get_relname_relid(relation->relname, namespaceId);
  }
  else
  {
   /* search the namespace path */
   relId = RelnameGetRelid(relation->relname);
  }

  /*
   * Invoke caller-supplied callback, if any.
   *
   * This callback is a good place to check permissions: we haven't
   * taken the table lock yet (and it's really best to check permissions
   * before locking anything!), but we've gotten far enough to know what
   * OID we think we should lock.  Of course, concurrent DDL might
   * change things while we're waiting for the lock, but in that case
   * the callback will be invoked again for the new OID.
 */

  if (callback)
   callback(relation, relId, oldRelId, callback_arg);

  /*
   * If no lock requested, we assume the caller knows what they're
   * doing.  They should have already acquired a heavyweight lock on
   * this relation earlier in the processing of this same statement, so
   * it wouldn't be appropriate to AcceptInvalidationMessages() here, as
   * that might pull the rug out from under them.
 */

  if (lockmode == NoLock)
   break;

  /*
   * If, upon retry, we get back the same OID we did last time, then the
   * invalidation messages we processed did not change the final answer.
   * So we're done.
   *
   * If we got a different OID, we've locked the relation that used to
   * have this name rather than the one that does now.  So release the
   * lock.
 */

  if (retry)
  {
   if (relId == oldRelId)
    break;
   if (OidIsValid(oldRelId))
    UnlockRelationOid(oldRelId, lockmode);
  }

  /*
   * Lock relation.  This will also accept any pending invalidation
   * messages.  If we got back InvalidOid, indicating not found, then
   * there's nothing to lock, but we accept invalidation messages
   * anyway, to flush any negative catcache entries that may be
   * lingering.
 */

  if (!OidIsValid(relId))
   AcceptInvalidationMessages();
  else if (!(flags & (RVR_NOWAIT | RVR_SKIP_LOCKED)))
   LockRelationOid(relId, lockmode);
  else if (!ConditionalLockRelationOid(relId, lockmode))
  {
   int   elevel = (flags & RVR_SKIP_LOCKED) ? DEBUG1 : ERROR;

   if (relation->schemaname)
    ereport(elevel,
      (errcode(ERRCODE_LOCK_NOT_AVAILABLE),
       errmsg("could not obtain lock on relation \"%s.%s\"",
        relation->schemaname, relation->relname)));
   else
    ereport(elevel,
      (errcode(ERRCODE_LOCK_NOT_AVAILABLE),
       errmsg("could not obtain lock on relation \"%s\"",
        relation->relname)));

   return InvalidOid;
  }

  /*
   * If no invalidation message were processed, we're done!
 */

  if (inval_count == SharedInvalidMessageCounter)
   break;

  /*
   * Something may have changed.  Let's repeat the name lookup, to make
   * sure this name still references the same relation it did
   * previously.
 */

  retry = true;
  oldRelId = relId;
 }

 if (!OidIsValid(relId))
 {
  int   elevel = missing_ok ? DEBUG1 : ERROR;

  if (relation->schemaname)
   ereport(elevel,
     (errcode(ERRCODE_UNDEFINED_TABLE),
      errmsg("relation \"%s.%s\" does not exist",
       relation->schemaname, relation->relname)));
  else
   ereport(elevel,
     (errcode(ERRCODE_UNDEFINED_TABLE),
      errmsg("relation \"%s\" does not exist",
       relation->relname)));
 }
 return relId;
}

/*
 * RangeVarGetCreationNamespace
 *  Given a RangeVar describing a to-be-created relation,
 *  choose which namespace to create it in.
 *
 * Note: calling this may result in a CommandCounterIncrement operation.
 * That will happen on the first request for a temp table in any particular
 * backend run; we will need to either create or clean out the temp schema.
 */

Oid
RangeVarGetCreationNamespace(const RangeVar *newRelation)
{
 Oid   namespaceId;

 /*
  * We check the catalog name and then ignore it.
 */

 if (newRelation->catalogname)
 {
  if (strcmp(newRelation->catalogname, get_database_name(MyDatabaseId)) != 0)
   ereport(ERROR,
     (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
      errmsg("cross-database references are not implemented: \"%s.%s.%s\"",
       newRelation->catalogname, newRelation->schemaname,
       newRelation->relname)));
 }

 if (newRelation->schemaname)
 {
  /* check for pg_temp alias */
  if (strcmp(newRelation->schemaname, "pg_temp") == 0)
  {
   /* Initialize temp namespace */
   AccessTempTableNamespace(false);
   return myTempNamespace;
  }
  /* use exact schema given */
  namespaceId = get_namespace_oid(newRelation->schemaname, false);
  /* we do not check for USAGE rights here! */
 }
 else if (newRelation->relpersistence == RELPERSISTENCE_TEMP)
 {
  /* Initialize temp namespace */
  AccessTempTableNamespace(false);
  return myTempNamespace;
 }
 else
 {
  /* use the default creation namespace */
  recomputeNamespacePath();
  if (activeTempCreationPending)
  {
   /* Need to initialize temp namespace */
   AccessTempTableNamespace(true);
   return myTempNamespace;
  }
  namespaceId = activeCreationNamespace;
  if (!OidIsValid(namespaceId))
   ereport(ERROR,
     (errcode(ERRCODE_UNDEFINED_SCHEMA),
      errmsg("no schema has been selected to create in")));
 }

 /* Note: callers will check for CREATE rights when appropriate */

 return namespaceId;
}

/*
 * RangeVarGetAndCheckCreationNamespace
 *
 * This function returns the OID of the namespace in which a new relation
 * with a given name should be created.  If the user does not have CREATE
 * permission on the target namespace, this function will instead signal
 * an ERROR.
 *
 * If non-NULL, *existing_relation_id is set to the OID of any existing relation
 * with the same name which already exists in that namespace, or to InvalidOid
 * if no such relation exists.
 *
 * If lockmode != NoLock, the specified lock mode is acquired on the existing
 * relation, if any, provided that the current user owns the target relation.
 * However, if lockmode != NoLock and the user does not own the target
 * relation, we throw an ERROR, as we must not try to lock relations the
 * user does not have permissions on.
 *
 * As a side effect, this function acquires AccessShareLock on the target
 * namespace.  Without this, the namespace could be dropped before our
 * transaction commits, leaving behind relations with relnamespace pointing
 * to a no-longer-existent namespace.
 *
 * As a further side-effect, if the selected namespace is a temporary namespace,
 * we mark the RangeVar as RELPERSISTENCE_TEMP.
 */

Oid
RangeVarGetAndCheckCreationNamespace(RangeVar *relation,
          LOCKMODE lockmode,
          Oid *existing_relation_id)
{
 uint64  inval_count;
 Oid   relid;
 Oid   oldrelid = InvalidOid;
 Oid   nspid;
 Oid   oldnspid = InvalidOid;
 bool  retry = false;

 /*
  * We check the catalog name and then ignore it.
 */

 if (relation->catalogname)
 {
  if (strcmp(relation->catalogname, get_database_name(MyDatabaseId)) != 0)
   ereport(ERROR,
     (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
      errmsg("cross-database references are not implemented: \"%s.%s.%s\"",
       relation->catalogname, relation->schemaname,
       relation->relname)));
 }

 /*
  * As in RangeVarGetRelidExtended(), we guard against concurrent DDL
  * operations by tracking whether any invalidation messages are processed
  * while we're doing the name lookups and acquiring locks.  See comments
  * in that function for a more detailed explanation of this logic.
 */

 for (;;)
 {
  AclResult aclresult;

  inval_count = SharedInvalidMessageCounter;

  /* Look up creation namespace and check for existing relation. */
  nspid = RangeVarGetCreationNamespace(relation);
  Assert(OidIsValid(nspid));
  if (existing_relation_id != NULL)
   relid = get_relname_relid(relation->relname, nspid);
  else
   relid = InvalidOid;

  /*
   * In bootstrap processing mode, we don't bother with permissions or
   * locking.  Permissions might not be working yet, and locking is
   * unnecessary.
 */

  if (IsBootstrapProcessingMode())
   break;

  /* Check namespace permissions. */
  aclresult = object_aclcheck(NamespaceRelationId, nspid, GetUserId(), ACL_CREATE);
  if (aclresult != ACLCHECK_OK)
   aclcheck_error(aclresult, OBJECT_SCHEMA,
         get_namespace_name(nspid));

  if (retry)
  {
   /* If nothing changed, we're done. */
   if (relid == oldrelid && nspid == oldnspid)
    break;
   /* If creation namespace has changed, give up old lock. */
   if (nspid != oldnspid)
    UnlockDatabaseObject(NamespaceRelationId, oldnspid, 0,
          AccessShareLock);
   /* If name points to something different, give up old lock. */
   if (relid != oldrelid && OidIsValid(oldrelid) && lockmode != NoLock)
    UnlockRelationOid(oldrelid, lockmode);
  }

  /* Lock namespace. */
  if (nspid != oldnspid)
   LockDatabaseObject(NamespaceRelationId, nspid, 0, AccessShareLock);

  /* Lock relation, if required if and we have permission. */
  if (lockmode != NoLock && OidIsValid(relid))
  {
   if (!object_ownercheck(RelationRelationId, relid, GetUserId()))
    aclcheck_error(ACLCHECK_NOT_OWNER, get_relkind_objtype(get_rel_relkind(relid)),
          relation->relname);
   if (relid != oldrelid)
    LockRelationOid(relid, lockmode);
  }

  /* If no invalidation message were processed, we're done! */
  if (inval_count == SharedInvalidMessageCounter)
   break;

  /* Something may have changed, so recheck our work. */
  retry = true;
  oldrelid = relid;
  oldnspid = nspid;
 }

 RangeVarAdjustRelationPersistence(relation, nspid);
 if (existing_relation_id != NULL)
  *existing_relation_id = relid;
 return nspid;
}

/*
 * Adjust the relpersistence for an about-to-be-created relation based on the
 * creation namespace, and throw an error for invalid combinations.
 */

void
RangeVarAdjustRelationPersistence(RangeVar *newRelation, Oid nspid)
{
 switch (newRelation->relpersistence)
 {
  case RELPERSISTENCE_TEMP:
   if (!isTempOrTempToastNamespace(nspid))
   {
    if (isAnyTempNamespace(nspid))
     ereport(ERROR,
       (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
        errmsg("cannot create relations in temporary schemas of other sessions")));
    else
     ereport(ERROR,
       (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
        errmsg("cannot create temporary relation in non-temporary schema")));
   }
   break;
  case RELPERSISTENCE_PERMANENT:
   if (isTempOrTempToastNamespace(nspid))
    newRelation->relpersistence = RELPERSISTENCE_TEMP;
   else if (isAnyTempNamespace(nspid))
    ereport(ERROR,
      (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
       errmsg("cannot create relations in temporary schemas of other sessions")));
   break;
  default:
   if (isAnyTempNamespace(nspid))
    ereport(ERROR,
      (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
       errmsg("only temporary relations may be created in temporary schemas")));
 }
}

/*
 * RelnameGetRelid
 *  Try to resolve an unqualified relation name.
 *  Returns OID if relation found in search path, else InvalidOid.
 */

Oid
RelnameGetRelid(const char *relname)
{
 Oid   relid;
 ListCell   *l;

 recomputeNamespacePath();

 foreach(l, activeSearchPath)
 {
  Oid   namespaceId = lfirst_oid(l);

  relid = get_relname_relid(relname, namespaceId);
  if (OidIsValid(relid))
   return relid;
 }

 /* Not found in path */
 return InvalidOid;
}


/*
 * RelationIsVisible
 *  Determine whether a relation (identified by OID) is visible in the
 *  current search path.  Visible means "would be found by searching
 *  for the unqualified relation name".
 */

bool
RelationIsVisible(Oid relid)
{
 return RelationIsVisibleExt(relid, NULL);
}

/*
 * RelationIsVisibleExt
 *  As above, but if the relation isn't found and is_missing is not NULL,
 *  then set *is_missing = true and return false instead of throwing
 *  an error.  (Caller must initialize *is_missing = false.)
 */

static bool
RelationIsVisibleExt(Oid relid, bool *is_missing)
{
 HeapTuple reltup;
 Form_pg_class relform;
 Oid   relnamespace;
 bool  visible;

 reltup = SearchSysCache1(RELOID, ObjectIdGetDatum(relid));
 if (!HeapTupleIsValid(reltup))
 {
  if (is_missing != NULL)
  {
   *is_missing = true;
   return false;
  }
  elog(ERROR, "cache lookup failed for relation %u", relid);
 }
 relform = (Form_pg_class) GETSTRUCT(reltup);

 recomputeNamespacePath();

 /*
  * Quick check: if it ain't in the path at all, it ain't visible. Items in
  * the system namespace are surely in the path and so we needn't even do
  * list_member_oid() for them.
 */

 relnamespace = relform->relnamespace;
 if (relnamespace != PG_CATALOG_NAMESPACE &&
  !list_member_oid(activeSearchPath, relnamespace))
  visible = false;
 else
 {
  /*
   * If it is in the path, it might still not be visible; it could be
   * hidden by another relation of the same name earlier in the path. So
   * we must do a slow check for conflicting relations.
 */

  char    *relname = NameStr(relform->relname);
  ListCell   *l;

  visible = false;
  foreach(l, activeSearchPath)
  {
   Oid   namespaceId = lfirst_oid(l);

   if (namespaceId == relnamespace)
   {
    /* Found it first in path */
    visible = true;
    break;
   }
   if (OidIsValid(get_relname_relid(relname, namespaceId)))
   {
    /* Found something else first in path */
    break;
   }
  }
 }

 ReleaseSysCache(reltup);

 return visible;
}


/*
 * TypenameGetTypid
 *  Wrapper for binary compatibility.
 */

Oid
TypenameGetTypid(const char *typname)
{
 return TypenameGetTypidExtended(typname, true);
}

/*
 * TypenameGetTypidExtended
 *  Try to resolve an unqualified datatype name.
 *  Returns OID if type found in search path, else InvalidOid.
 *
 * This is essentially the same as RelnameGetRelid.
 */

Oid
TypenameGetTypidExtended(const char *typname, bool temp_ok)
{
 Oid   typid;
 ListCell   *l;

 recomputeNamespacePath();

 foreach(l, activeSearchPath)
 {
  Oid   namespaceId = lfirst_oid(l);

  if (!temp_ok && namespaceId == myTempNamespace)
   continue;   /* do not look in temp namespace */

  typid = GetSysCacheOid2(TYPENAMENSP, Anum_pg_type_oid,
        PointerGetDatum(typname),
        ObjectIdGetDatum(namespaceId));
  if (OidIsValid(typid))
   return typid;
 }

 /* Not found in path */
 return InvalidOid;
}

/*
 * TypeIsVisible
 *  Determine whether a type (identified by OID) is visible in the
 *  current search path.  Visible means "would be found by searching
 *  for the unqualified type name".
 */

bool
TypeIsVisible(Oid typid)
{
 return TypeIsVisibleExt(typid, NULL);
}

/*
 * TypeIsVisibleExt
 *  As above, but if the type isn't found and is_missing is not NULL,
 *  then set *is_missing = true and return false instead of throwing
 *  an error.  (Caller must initialize *is_missing = false.)
 */

static bool
TypeIsVisibleExt(Oid typid, bool *is_missing)
{
 HeapTuple typtup;
 Form_pg_type typform;
 Oid   typnamespace;
 bool  visible;

 typtup = SearchSysCache1(TYPEOID, ObjectIdGetDatum(typid));
 if (!HeapTupleIsValid(typtup))
 {
  if (is_missing != NULL)
  {
   *is_missing = true;
   return false;
  }
  elog(ERROR, "cache lookup failed for type %u", typid);
 }
 typform = (Form_pg_type) GETSTRUCT(typtup);

 recomputeNamespacePath();

 /*
  * Quick check: if it ain't in the path at all, it ain't visible. Items in
  * the system namespace are surely in the path and so we needn't even do
  * list_member_oid() for them.
 */

 typnamespace = typform->typnamespace;
 if (typnamespace != PG_CATALOG_NAMESPACE &&
  !list_member_oid(activeSearchPath, typnamespace))
  visible = false;
 else
 {
  /*
   * If it is in the path, it might still not be visible; it could be
   * hidden by another type of the same name earlier in the path. So we
   * must do a slow check for conflicting types.
 */

  char    *typname = NameStr(typform->typname);
  ListCell   *l;

  visible = false;
  foreach(l, activeSearchPath)
  {
   Oid   namespaceId = lfirst_oid(l);

   if (namespaceId == typnamespace)
   {
    /* Found it first in path */
    visible = true;
    break;
   }
   if (SearchSysCacheExists2(TYPENAMENSP,
           PointerGetDatum(typname),
           ObjectIdGetDatum(namespaceId)))
   {
    /* Found something else first in path */
    break;
   }
  }
 }

 ReleaseSysCache(typtup);

 return visible;
}


/*
 * FuncnameGetCandidates
 *  Given a possibly-qualified function name and argument count,
 *  retrieve a list of the possible matches.
 *
 * If nargs is -1, we return all functions matching the given name,
 * regardless of argument count.  (argnames must be NIL, and expand_variadic
 * and expand_defaults must be false, in this case.)
 *
 * If argnames isn't NIL, we are considering a named- or mixed-notation call,
 * and only functions having all the listed argument names will be returned.
 * (We assume that length(argnames) <= nargs and all the passed-in names are
 * distinct.)  The returned structs will include an argnumbers array showing
 * the actual argument index for each logical argument position.
 *
 * If expand_variadic is true, then variadic functions having the same number
 * or fewer arguments will be retrieved, with the variadic argument and any
 * additional argument positions filled with the variadic element type.
 * nvargs in the returned struct is set to the number of such arguments.
 * If expand_variadic is false, variadic arguments are not treated specially,
 * and the returned nvargs will always be zero.
 *
 * If expand_defaults is true, functions that could match after insertion of
 * default argument values will also be retrieved.  In this case the returned
 * structs could have nargs > passed-in nargs, and ndargs is set to the number
 * of additional args (which can be retrieved from the function's
 * proargdefaults entry).
 *
 * If include_out_arguments is true, then OUT-mode arguments are considered to
 * be included in the argument list.  Their types are included in the returned
 * arrays, and argnumbers are indexes in proallargtypes not proargtypes.
 * We also set nominalnargs to be the length of proallargtypes not proargtypes.
 * Otherwise OUT-mode arguments are ignored.
 *
 * It is not possible for nvargs and ndargs to both be nonzero in the same
 * list entry, since default insertion allows matches to functions with more
 * than nargs arguments while the variadic transformation requires the same
 * number or less.
 *
 * When argnames isn't NIL, the returned args[] type arrays are not ordered
 * according to the functions' declarations, but rather according to the call:
 * first any positional arguments, then the named arguments, then defaulted
 * arguments (if needed and allowed by expand_defaults).  The argnumbers[]
 * array can be used to map this back to the catalog information.
 * argnumbers[k] is set to the proargtypes or proallargtypes index of the
 * k'th call argument.
 *
 * We search a single namespace if the function name is qualified, else
 * all namespaces in the search path.  In the multiple-namespace case,
 * we arrange for entries in earlier namespaces to mask identical entries in
 * later namespaces.
 *
 * When expanding variadics, we arrange for non-variadic functions to mask
 * variadic ones if the expanded argument list is the same.  It is still
 * possible for there to be conflicts between different variadic functions,
 * however.
 *
 * It is guaranteed that the return list will never contain multiple entries
 * with identical argument lists.  When expand_defaults is true, the entries
 * could have more than nargs positions, but we still guarantee that they are
 * distinct in the first nargs positions.  However, if argnames isn't NIL or
 * either expand_variadic or expand_defaults is true, there might be multiple
 * candidate functions that expand to identical argument lists.  Rather than
 * throw error here, we report such situations by returning a single entry
 * with oid = 0 that represents a set of such conflicting candidates.
 * The caller might end up discarding such an entry anyway, but if it selects
 * such an entry it should react as though the call were ambiguous.
 *
 * If missing_ok is true, an empty list (NULL) is returned if the name was
 * schema-qualified with a schema that does not exist.  Likewise if no
 * candidate is found for other reasons.
 */

FuncCandidateList
FuncnameGetCandidates(List *names, int nargs, List *argnames,
       bool expand_variadic, bool expand_defaults,
       bool include_out_arguments, bool missing_ok)
{
 FuncCandidateList resultList = NULL;
 bool  any_special = false;
 char    *schemaname;
 char    *funcname;
 Oid   namespaceId;
 CatCList   *catlist;
 int   i;

 /* check for caller error */
 Assert(nargs >= 0 || !(expand_variadic | expand_defaults));

 /* deconstruct the name list */
 DeconstructQualifiedName(names, &schemaname, &funcname);

 if (schemaname)
 {
  /* use exact schema given */
  namespaceId = LookupExplicitNamespace(schemaname, missing_ok);
  if (!OidIsValid(namespaceId))
   return NULL;
 }
 else
 {
  /* flag to indicate we need namespace search */
  namespaceId = InvalidOid;
  recomputeNamespacePath();
 }

 /* Search syscache by name only */
 catlist = SearchSysCacheList1(PROCNAMEARGSNSP, CStringGetDatum(funcname));

 for (i = 0; i < catlist->n_members; i++)
 {
  HeapTuple proctup = &catlist->members[i]->tuple;
  Form_pg_proc procform = (Form_pg_proc) GETSTRUCT(proctup);
  Oid     *proargtypes = procform->proargtypes.values;
  int   pronargs = procform->pronargs;
  int   effective_nargs;
  int   pathpos = 0;
  bool  variadic;
  bool  use_defaults;
  Oid   va_elem_type;
  int     *argnumbers = NULL;
  FuncCandidateList newResult;

  if (OidIsValid(namespaceId))
  {
   /* Consider only procs in specified namespace */
   if (procform->pronamespace != namespaceId)
    continue;
  }
  else
  {
   /*
    * Consider only procs that are in the search path and are not in
    * the temp namespace.
 */

   ListCell   *nsp;

   foreach(nsp, activeSearchPath)
   {
    if (procform->pronamespace == lfirst_oid(nsp) &&
     procform->pronamespace != myTempNamespace)
     break;
    pathpos++;
   }
   if (nsp == NULL)
    continue;  /* proc is not in search path */
  }

  /*
   * If we are asked to match to OUT arguments, then use the
   * proallargtypes array (which includes those); otherwise use
   * proargtypes (which doesn't).  Of course, if proallargtypes is null,
   * we always use proargtypes.
 */

  if (include_out_arguments)
  {
   Datum  proallargtypes;
   bool  isNull;

   proallargtypes = SysCacheGetAttr(PROCNAMEARGSNSP, proctup,
            Anum_pg_proc_proallargtypes,
            &isNull);
   if (!isNull)
   {
    ArrayType  *arr = DatumGetArrayTypeP(proallargtypes);

    pronargs = ARR_DIMS(arr)[0];
    if (ARR_NDIM(arr) != 1 ||
     pronargs < 0 ||
     ARR_HASNULL(arr) ||
     ARR_ELEMTYPE(arr) != OIDOID)
     elog(ERROR, "proallargtypes is not a 1-D Oid array or it contains nulls");
    Assert(pronargs >= procform->pronargs);
    proargtypes = (Oid *) ARR_DATA_PTR(arr);
   }
  }

  if (argnames != NIL)
  {
   /*
    * Call uses named or mixed notation
    *
    * Named or mixed notation can match a variadic function only if
    * expand_variadic is off; otherwise there is no way to match the
    * presumed-nameless parameters expanded from the variadic array.
 */

   if (OidIsValid(procform->provariadic) && expand_variadic)
    continue;
   va_elem_type = InvalidOid;
   variadic = false;

   /*
    * Check argument count.
 */

   Assert(nargs >= 0); /* -1 not supported with argnames */

   if (pronargs > nargs && expand_defaults)
   {
    /* Ignore if not enough default expressions */
    if (nargs + procform->pronargdefaults < pronargs)
     continue;
    use_defaults = true;
   }
   else
    use_defaults = false;

   /* Ignore if it doesn't match requested argument count */
   if (pronargs != nargs && !use_defaults)
    continue;

   /* Check for argument name match, generate positional mapping */
   if (!MatchNamedCall(proctup, nargs, argnames,
        include_out_arguments, pronargs,
        &argnumbers))
    continue;

   /* Named argument matching is always "special" */
   any_special = true;
  }
  else
  {
   /*
    * Call uses positional notation
    *
    * Check if function is variadic, and get variadic element type if
    * so.  If expand_variadic is false, we should just ignore
    * variadic-ness.
 */

   if (pronargs <= nargs && expand_variadic)
   {
    va_elem_type = procform->provariadic;
    variadic = OidIsValid(va_elem_type);
    any_special |= variadic;
   }
   else
   {
    va_elem_type = InvalidOid;
    variadic = false;
   }

   /*
    * Check if function can match by using parameter defaults.
 */

   if (pronargs > nargs && expand_defaults)
   {
    /* Ignore if not enough default expressions */
    if (nargs + procform->pronargdefaults < pronargs)
     continue;
    use_defaults = true;
    any_special = true;
   }
   else
    use_defaults = false;

   /* Ignore if it doesn't match requested argument count */
   if (nargs >= 0 && pronargs != nargs && !variadic && !use_defaults)
    continue;
  }

  /*
   * We must compute the effective argument list so that we can easily
   * compare it to earlier results.  We waste a palloc cycle if it gets
   * masked by an earlier result, but really that's a pretty infrequent
   * case so it's not worth worrying about.
 */

  effective_nargs = Max(pronargs, nargs);
  newResult = (FuncCandidateList)
   palloc(offsetof(struct _FuncCandidateList, args) +
       effective_nargs * sizeof(Oid));
  newResult->pathpos = pathpos;
  newResult->oid = procform->oid;
  newResult->nominalnargs = pronargs;
  newResult->nargs = effective_nargs;
  newResult->argnumbers = argnumbers;
  if (argnumbers)
  {
   /* Re-order the argument types into call's logical order */
   for (int j = 0; j < pronargs; j++)
    newResult->args[j] = proargtypes[argnumbers[j]];
  }
  else
  {
   /* Simple positional case, just copy proargtypes as-is */
   memcpy(newResult->args, proargtypes, pronargs * sizeof(Oid));
  }
  if (variadic)
  {
   newResult->nvargs = effective_nargs - pronargs + 1;
   /* Expand variadic argument into N copies of element type */
   for (int j = pronargs - 1; j < effective_nargs; j++)
    newResult->args[j] = va_elem_type;
  }
  else
   newResult->nvargs = 0;
  newResult->ndargs = use_defaults ? pronargs - nargs : 0;

  /*
   * Does it have the same arguments as something we already accepted?
   * If so, decide what to do to avoid returning duplicate argument
   * lists.  We can skip this check for the single-namespace case if no
   * special (named, variadic or defaults) match has been made, since
   * then the unique index on pg_proc guarantees all the matches have
   * different argument lists.
 */

  if (resultList != NULL &&
   (any_special || !OidIsValid(namespaceId)))
  {
   /*
    * If we have an ordered list from SearchSysCacheList (the normal
    * case), then any conflicting proc must immediately adjoin this
    * one in the list, so we only need to look at the newest result
    * item.  If we have an unordered list, we have to scan the whole
    * result list.  Also, if either the current candidate or any
    * previous candidate is a special match, we can't assume that
    * conflicts are adjacent.
    *
    * We ignore defaulted arguments in deciding what is a match.
 */

   FuncCandidateList prevResult;

   if (catlist->ordered && !any_special)
   {
    /* ndargs must be 0 if !any_special */
    if (effective_nargs == resultList->nargs &&
     memcmp(newResult->args,
         resultList->args,
         effective_nargs * sizeof(Oid)) == 0)
     prevResult = resultList;
    else
     prevResult = NULL;
   }
   else
   {
    int   cmp_nargs = newResult->nargs - newResult->ndargs;

    for (prevResult = resultList;
      prevResult;
      prevResult = prevResult->next)
    {
     if (cmp_nargs == prevResult->nargs - prevResult->ndargs &&
      memcmp(newResult->args,
          prevResult->args,
          cmp_nargs * sizeof(Oid)) == 0)
      break;
    }
   }

   if (prevResult)
   {
    /*
     * We have a match with a previous result.  Decide which one
     * to keep, or mark it ambiguous if we can't decide.  The
     * logic here is preference > 0 means prefer the old result,
     * preference < 0 means prefer the new, preference = 0 means
     * ambiguous.
 */

    int   preference;

    if (pathpos != prevResult->pathpos)
    {
     /*
      * Prefer the one that's earlier in the search path.
 */

     preference = pathpos - prevResult->pathpos;
    }
    else if (variadic && prevResult->nvargs == 0)
    {
     /*
      * With variadic functions we could have, for example,
      * both foo(numeric) and foo(variadic numeric[]) in the
      * same namespace; if so we prefer the non-variadic match
      * on efficiency grounds.
 */

     preference = 1;
    }
    else if (!variadic && prevResult->nvargs > 0)
    {
     preference = -1;
    }
    else
    {
     /*----------
      * We can't decide.  This can happen with, for example,
      * both foo(numeric, variadic numeric[]) and
      * foo(variadic numeric[]) in the same namespace, or
      * both foo(int) and foo (int, int default something)
      * in the same namespace, or both foo(a int, b text)
      * and foo(b text, a int) in the same namespace.
      *----------
 */

     preference = 0;
    }

    if (preference > 0)
    {
     /* keep previous result */
     pfree(newResult);
     continue;
    }
    else if (preference < 0)
    {
     /* remove previous result from the list */
     if (prevResult == resultList)
      resultList = prevResult->next;
     else
     {
      FuncCandidateList prevPrevResult;

      for (prevPrevResult = resultList;
        prevPrevResult;
        prevPrevResult = prevPrevResult->next)
      {
       if (prevResult == prevPrevResult->next)
       {
        prevPrevResult->next = prevResult->next;
        break;
       }
      }
      Assert(prevPrevResult); /* assert we found it */
     }
     pfree(prevResult);
     /* fall through to add newResult to list */
    }
    else
    {
     /* mark old result as ambiguous, discard new */
     prevResult->oid = InvalidOid;
     pfree(newResult);
     continue;
    }
   }
  }

  /*
   * Okay to add it to result list
 */

  newResult->next = resultList;
  resultList = newResult;
 }

 ReleaseSysCacheList(catlist);

 return resultList;
}

/*
 * MatchNamedCall
 *  Given a pg_proc heap tuple and a call's list of argument names,
 *  check whether the function could match the call.
 *
 * The call could match if all supplied argument names are accepted by
 * the function, in positions after the last positional argument, and there
 * are defaults for all unsupplied arguments.
 *
 * If include_out_arguments is true, we are treating OUT arguments as
 * included in the argument list.  pronargs is the number of arguments
 * we're considering (the length of either proargtypes or proallargtypes).
 *
 * The number of positional arguments is nargs - list_length(argnames).
 * Note caller has already done basic checks on argument count.
 *
 * On match, return true and fill *argnumbers with a palloc'd array showing
 * the mapping from call argument positions to actual function argument
 * numbers.  Defaulted arguments are included in this map, at positions
 * after the last supplied argument.
 */

static bool
MatchNamedCall(HeapTuple proctup, int nargs, List *argnames,
      bool include_out_arguments, int pronargs,
      int **argnumbers)
{
 Form_pg_proc procform = (Form_pg_proc) GETSTRUCT(proctup);
 int   numposargs = nargs - list_length(argnames);
 int   pronallargs;
 Oid     *p_argtypes;
 char   **p_argnames;
 char    *p_argmodes;
 bool  arggiven[FUNC_MAX_ARGS];
 bool  isnull;
 int   ap;    /* call args position */
 int   pp;    /* proargs position */
 ListCell   *lc;

 Assert(argnames != NIL);
 Assert(numposargs >= 0);
 Assert(nargs <= pronargs);

 /* Ignore this function if its proargnames is null */
 (void) SysCacheGetAttr(PROCOID, proctup, Anum_pg_proc_proargnames,
         &isnull);
 if (isnull)
  return false;

 /* OK, let's extract the argument names and types */
 pronallargs = get_func_arg_info(proctup,
         &p_argtypes, &p_argnames, &p_argmodes);
 Assert(p_argnames != NULL);

 Assert(include_out_arguments ? (pronargs == pronallargs) : (pronargs <= pronallargs));

 /* initialize state for matching */
 *argnumbers = (int *) palloc(pronargs * sizeof(int));
 memset(arggiven, false, pronargs * sizeof(bool));

 /* there are numposargs positional args before the named args */
 for (ap = 0; ap < numposargs; ap++)
 {
  (*argnumbers)[ap] = ap;
  arggiven[ap] = true;
 }

 /* now examine the named args */
 foreach(lc, argnames)
 {
  char    *argname = (char *) lfirst(lc);
  bool  found;
  int   i;

  pp = 0;
  found = false;
  for (i = 0; i < pronallargs; i++)
  {
   /* consider only input params, except with include_out_arguments */
   if (!include_out_arguments &&
    p_argmodes &&
    (p_argmodes[i] != FUNC_PARAM_IN &&
     p_argmodes[i] != FUNC_PARAM_INOUT &&
     p_argmodes[i] != FUNC_PARAM_VARIADIC))
    continue;
   if (p_argnames[i] && strcmp(p_argnames[i], argname) == 0)
   {
    /* fail if argname matches a positional argument */
    if (arggiven[pp])
     return false;
    arggiven[pp] = true;
    (*argnumbers)[ap] = pp;
    found = true;
    break;
   }
   /* increase pp only for considered parameters */
   pp++;
  }
  /* if name isn't in proargnames, fail */
  if (!found)
   return false;
  ap++;
 }

 Assert(ap == nargs);  /* processed all actual parameters */

 /* Check for default arguments */
 if (nargs < pronargs)
 {
  int   first_arg_with_default = pronargs - procform->pronargdefaults;

  for (pp = numposargs; pp < pronargs; pp++)
  {
   if (arggiven[pp])
    continue;
   /* fail if arg not given and no default available */
   if (pp < first_arg_with_default)
    return false;
   (*argnumbers)[ap++] = pp;
  }
 }

 Assert(ap == pronargs);  /* processed all function parameters */

 return true;
}

/*
 * FunctionIsVisible
 *  Determine whether a function (identified by OID) is visible in the
 *  current search path.  Visible means "would be found by searching
 *  for the unqualified function name with exact argument matches".
 */

bool
FunctionIsVisible(Oid funcid)
{
 return FunctionIsVisibleExt(funcid, NULL);
}

/*
 * FunctionIsVisibleExt
 *  As above, but if the function isn't found and is_missing is not NULL,
 *  then set *is_missing = true and return false instead of throwing
 *  an error.  (Caller must initialize *is_missing = false.)
 */

static bool
FunctionIsVisibleExt(Oid funcid, bool *is_missing)
{
 HeapTuple proctup;
 Form_pg_proc procform;
 Oid   pronamespace;
 bool  visible;

 proctup = SearchSysCache1(PROCOID, ObjectIdGetDatum(funcid));
 if (!HeapTupleIsValid(proctup))
 {
  if (is_missing != NULL)
  {
   *is_missing = true;
   return false;
  }
  elog(ERROR, "cache lookup failed for function %u", funcid);
 }
 procform = (Form_pg_proc) GETSTRUCT(proctup);

 recomputeNamespacePath();

 /*
  * Quick check: if it ain't in the path at all, it ain't visible. Items in
  * the system namespace are surely in the path and so we needn't even do
  * list_member_oid() for them.
 */

 pronamespace = procform->pronamespace;
 if (pronamespace != PG_CATALOG_NAMESPACE &&
  !list_member_oid(activeSearchPath, pronamespace))
  visible = false;
 else
 {
  /*
   * If it is in the path, it might still not be visible; it could be
   * hidden by another proc of the same name and arguments earlier in
   * the path.  So we must do a slow check to see if this is the same
   * proc that would be found by FuncnameGetCandidates.
 */

  char    *proname = NameStr(procform->proname);
  int   nargs = procform->pronargs;
  FuncCandidateList clist;

  visible = false;

  clist = FuncnameGetCandidates(list_make1(makeString(proname)),
           nargs, NIL, falsefalsefalsefalse);

  for (; clist; clist = clist->next)
  {
   if (memcmp(clist->args, procform->proargtypes.values,
        nargs * sizeof(Oid)) == 0)
   {
    /* Found the expected entry; is it the right proc? */
    visible = (clist->oid == funcid);
    break;
   }
  }
 }

 ReleaseSysCache(proctup);

 return visible;
}


/*
 * OpernameGetOprid
 *  Given a possibly-qualified operator name and exact input datatypes,
 *  look up the operator.  Returns InvalidOid if not found.
 *
 * Pass oprleft = InvalidOid for a prefix op.
 *
 * If the operator name is not schema-qualified, it is sought in the current
 * namespace search path.  If the name is schema-qualified and the given
 * schema does not exist, InvalidOid is returned.
 */

Oid
OpernameGetOprid(List *names, Oid oprleft, Oid oprright)
{
 char    *schemaname;
 char    *opername;
 CatCList   *catlist;
 ListCell   *l;

 /* deconstruct the name list */
 DeconstructQualifiedName(names, &schemaname, &opername);

 if (schemaname)
 {
  /* search only in exact schema given */
  Oid   namespaceId;

  namespaceId = LookupExplicitNamespace(schemaname, true);
  if (OidIsValid(namespaceId))
  {
   HeapTuple opertup;

   opertup = SearchSysCache4(OPERNAMENSP,
           CStringGetDatum(opername),
           ObjectIdGetDatum(oprleft),
           ObjectIdGetDatum(oprright),
           ObjectIdGetDatum(namespaceId));
   if (HeapTupleIsValid(opertup))
   {
    Form_pg_operator operclass = (Form_pg_operator) GETSTRUCT(opertup);
    Oid   result = operclass->oid;

    ReleaseSysCache(opertup);
    return result;
   }
  }

  return InvalidOid;
 }

 /* Search syscache by name and argument types */
 catlist = SearchSysCacheList3(OPERNAMENSP,
          CStringGetDatum(opername),
          ObjectIdGetDatum(oprleft),
          ObjectIdGetDatum(oprright));

 if (catlist->n_members == 0)
 {
  /* no hope, fall out early */
  ReleaseSysCacheList(catlist);
  return InvalidOid;
 }

 /*
  * We have to find the list member that is first in the search path, if
  * there's more than one.  This doubly-nested loop looks ugly, but in
  * practice there should usually be few catlist members.
 */

 recomputeNamespacePath();

 foreach(l, activeSearchPath)
 {
  Oid   namespaceId = lfirst_oid(l);
  int   i;

  if (namespaceId == myTempNamespace)
   continue;   /* do not look in temp namespace */

  for (i = 0; i < catlist->n_members; i++)
  {
   HeapTuple opertup = &catlist->members[i]->tuple;
   Form_pg_operator operform = (Form_pg_operator) GETSTRUCT(opertup);

   if (operform->oprnamespace == namespaceId)
   {
    Oid   result = operform->oid;

    ReleaseSysCacheList(catlist);
    return result;
   }
  }
 }

 ReleaseSysCacheList(catlist);
 return InvalidOid;
}

/*
 * OpernameGetCandidates
 *  Given a possibly-qualified operator name and operator kind,
 *  retrieve a list of the possible matches.
 *
 * If oprkind is '\0', we return all operators matching the given name,
 * regardless of arguments.
 *
 * We search a single namespace if the operator name is qualified, else
 * all namespaces in the search path.  The return list will never contain
 * multiple entries with identical argument lists --- in the multiple-
 * namespace case, we arrange for entries in earlier namespaces to mask
 * identical entries in later namespaces.
 *
 * The returned items always have two args[] entries --- the first will be
 * InvalidOid for a prefix oprkind.  nargs is always 2, too.
 */

FuncCandidateList
OpernameGetCandidates(List *names, char oprkind, bool missing_schema_ok)
{
 FuncCandidateList resultList = NULL;
 char    *resultSpace = NULL;
 int   nextResult = 0;
 char    *schemaname;
 char    *opername;
 Oid   namespaceId;
 CatCList   *catlist;
 int   i;

 /* deconstruct the name list */
 DeconstructQualifiedName(names, &schemaname, &opername);

 if (schemaname)
 {
  /* use exact schema given */
  namespaceId = LookupExplicitNamespace(schemaname, missing_schema_ok);
  if (missing_schema_ok && !OidIsValid(namespaceId))
   return NULL;
 }
 else
 {
  /* flag to indicate we need namespace search */
  namespaceId = InvalidOid;
  recomputeNamespacePath();
 }

 /* Search syscache by name only */
 catlist = SearchSysCacheList1(OPERNAMENSP, CStringGetDatum(opername));

 /*
  * In typical scenarios, most if not all of the operators found by the
  * catcache search will end up getting returned; and there can be quite a
  * few, for common operator names such as '=' or '+'.  To reduce the time
  * spent in palloc, we allocate the result space as an array large enough
  * to hold all the operators.  The original coding of this routine did a
  * separate palloc for each operator, but profiling revealed that the
  * pallocs used an unreasonably large fraction of parsing time.
 */

#define SPACE_PER_OP MAXALIGN(offsetof(struct _FuncCandidateList, args) + \
         2 * sizeof(Oid))

 if (catlist->n_members > 0)
  resultSpace = palloc(catlist->n_members * SPACE_PER_OP);

 for (i = 0; i < catlist->n_members; i++)
 {
  HeapTuple opertup = &catlist->members[i]->tuple;
  Form_pg_operator operform = (Form_pg_operator) GETSTRUCT(opertup);
  int   pathpos = 0;
  FuncCandidateList newResult;

  /* Ignore operators of wrong kind, if specific kind requested */
  if (oprkind && operform->oprkind != oprkind)
   continue;

  if (OidIsValid(namespaceId))
  {
   /* Consider only opers in specified namespace */
   if (operform->oprnamespace != namespaceId)
    continue;
   /* No need to check args, they must all be different */
  }
  else
  {
   /*
    * Consider only opers that are in the search path and are not in
    * the temp namespace.
 */

   ListCell   *nsp;

   foreach(nsp, activeSearchPath)
   {
    if (operform->oprnamespace == lfirst_oid(nsp) &&
     operform->oprnamespace != myTempNamespace)
     break;
    pathpos++;
   }
   if (nsp == NULL)
    continue;  /* oper is not in search path */

   /*
    * Okay, it's in the search path, but does it have the same
    * arguments as something we already accepted? If so, keep only
    * the one that appears earlier in the search path.
    *
    * If we have an ordered list from SearchSysCacheList (the normal
    * case), then any conflicting oper must immediately adjoin this
    * one in the list, so we only need to look at the newest result
    * item.  If we have an unordered list, we have to scan the whole
    * result list.
 */

   if (resultList)
   {
    FuncCandidateList prevResult;

    if (catlist->ordered)
    {
     if (operform->oprleft == resultList->args[0] &&
      operform->oprright == resultList->args[1])
      prevResult = resultList;
     else
      prevResult = NULL;
    }
    else
    {
     for (prevResult = resultList;
       prevResult;
       prevResult = prevResult->next)
     {
      if (operform->oprleft == prevResult->args[0] &&
       operform->oprright == prevResult->args[1])
       break;
     }
    }
    if (prevResult)
    {
     /* We have a match with a previous result */
     Assert(pathpos != prevResult->pathpos);
     if (pathpos > prevResult->pathpos)
      continue/* keep previous result */
     /* replace previous result */
     prevResult->pathpos = pathpos;
     prevResult->oid = operform->oid;
     continue/* args are same, of course */
    }
   }
  }

  /*
   * Okay to add it to result list
 */

  newResult = (FuncCandidateList) (resultSpace + nextResult);
  nextResult += SPACE_PER_OP;

  newResult->pathpos = pathpos;
  newResult->oid = operform->oid;
  newResult->nominalnargs = 2;
  newResult->nargs = 2;
  newResult->nvargs = 0;
  newResult->ndargs = 0;
  newResult->argnumbers = NULL;
  newResult->args[0] = operform->oprleft;
  newResult->args[1] = operform->oprright;
  newResult->next = resultList;
  resultList = newResult;
 }

 ReleaseSysCacheList(catlist);

 return resultList;
}

/*
 * OperatorIsVisible
 *  Determine whether an operator (identified by OID) is visible in the
 *  current search path.  Visible means "would be found by searching
 *  for the unqualified operator name with exact argument matches".
 */

bool
OperatorIsVisible(Oid oprid)
{
 return OperatorIsVisibleExt(oprid, NULL);
}

/*
 * OperatorIsVisibleExt
 *  As above, but if the operator isn't found and is_missing is not NULL,
 *  then set *is_missing = true and return false instead of throwing
 *  an error.  (Caller must initialize *is_missing = false.)
 */

static bool
OperatorIsVisibleExt(Oid oprid, bool *is_missing)
{
 HeapTuple oprtup;
 Form_pg_operator oprform;
 Oid   oprnamespace;
 bool  visible;

 oprtup = SearchSysCache1(OPEROID, ObjectIdGetDatum(oprid));
 if (!HeapTupleIsValid(oprtup))
 {
  if (is_missing != NULL)
  {
   *is_missing = true;
   return false;
  }
  elog(ERROR, "cache lookup failed for operator %u", oprid);
 }
 oprform = (Form_pg_operator) GETSTRUCT(oprtup);

 recomputeNamespacePath();

 /*
  * Quick check: if it ain't in the path at all, it ain't visible. Items in
  * the system namespace are surely in the path and so we needn't even do
  * list_member_oid() for them.
 */

 oprnamespace = oprform->oprnamespace;
 if (oprnamespace != PG_CATALOG_NAMESPACE &&
  !list_member_oid(activeSearchPath, oprnamespace))
  visible = false;
 else
 {
  /*
   * If it is in the path, it might still not be visible; it could be
   * hidden by another operator of the same name and arguments earlier
   * in the path.  So we must do a slow check to see if this is the same
   * operator that would be found by OpernameGetOprid.
 */

  char    *oprname = NameStr(oprform->oprname);

  visible = (OpernameGetOprid(list_make1(makeString(oprname)),
         oprform->oprleft, oprform->oprright)
       == oprid);
 }

 ReleaseSysCache(oprtup);

 return visible;
}


/*
 * OpclassnameGetOpcid
 *  Try to resolve an unqualified index opclass name.
 *  Returns OID if opclass found in search path, else InvalidOid.
 *
 * This is essentially the same as TypenameGetTypid, but we have to have
 * an extra argument for the index AM OID.
 */

Oid
OpclassnameGetOpcid(Oid amid, const char *opcname)
{
 Oid   opcid;
 ListCell   *l;

 recomputeNamespacePath();

 foreach(l, activeSearchPath)
 {
  Oid   namespaceId = lfirst_oid(l);

  if (namespaceId == myTempNamespace)
   continue;   /* do not look in temp namespace */

  opcid = GetSysCacheOid3(CLAAMNAMENSP, Anum_pg_opclass_oid,
        ObjectIdGetDatum(amid),
        PointerGetDatum(opcname),
        ObjectIdGetDatum(namespaceId));
  if (OidIsValid(opcid))
   return opcid;
 }

 /* Not found in path */
 return InvalidOid;
}

/*
 * OpclassIsVisible
 *  Determine whether an opclass (identified by OID) is visible in the
 *  current search path.  Visible means "would be found by searching
 *  for the unqualified opclass name".
 */

bool
OpclassIsVisible(Oid opcid)
{
 return OpclassIsVisibleExt(opcid, NULL);
}

/*
 * OpclassIsVisibleExt
 *  As above, but if the opclass isn't found and is_missing is not NULL,
 *  then set *is_missing = true and return false instead of throwing
 *  an error.  (Caller must initialize *is_missing = false.)
 */

static bool
OpclassIsVisibleExt(Oid opcid, bool *is_missing)
{
 HeapTuple opctup;
 Form_pg_opclass opcform;
 Oid   opcnamespace;
 bool  visible;

 opctup = SearchSysCache1(CLAOID, ObjectIdGetDatum(opcid));
 if (!HeapTupleIsValid(opctup))
 {
  if (is_missing != NULL)
  {
   *is_missing = true;
   return false;
  }
  elog(ERROR, "cache lookup failed for opclass %u", opcid);
 }
 opcform = (Form_pg_opclass) GETSTRUCT(opctup);

 recomputeNamespacePath();

 /*
  * Quick check: if it ain't in the path at all, it ain't visible. Items in
  * the system namespace are surely in the path and so we needn't even do
  * list_member_oid() for them.
 */

 opcnamespace = opcform->opcnamespace;
 if (opcnamespace != PG_CATALOG_NAMESPACE &&
  !list_member_oid(activeSearchPath, opcnamespace))
  visible = false;
 else
 {
  /*
   * If it is in the path, it might still not be visible; it could be
   * hidden by another opclass of the same name earlier in the path. So
   * we must do a slow check to see if this opclass would be found by
   * OpclassnameGetOpcid.
 */

  char    *opcname = NameStr(opcform->opcname);

  visible = (OpclassnameGetOpcid(opcform->opcmethod, opcname) == opcid);
 }

 ReleaseSysCache(opctup);

 return visible;
}

/*
 * OpfamilynameGetOpfid
 *  Try to resolve an unqualified index opfamily name.
 *  Returns OID if opfamily found in search path, else InvalidOid.
 *
 * This is essentially the same as TypenameGetTypid, but we have to have
 * an extra argument for the index AM OID.
 */

Oid
OpfamilynameGetOpfid(Oid amid, const char *opfname)
{
 Oid   opfid;
 ListCell   *l;

 recomputeNamespacePath();

 foreach(l, activeSearchPath)
 {
  Oid   namespaceId = lfirst_oid(l);

  if (namespaceId == myTempNamespace)
   continue;   /* do not look in temp namespace */

  opfid = GetSysCacheOid3(OPFAMILYAMNAMENSP, Anum_pg_opfamily_oid,
        ObjectIdGetDatum(amid),
        PointerGetDatum(opfname),
        ObjectIdGetDatum(namespaceId));
  if (OidIsValid(opfid))
   return opfid;
 }

 /* Not found in path */
 return InvalidOid;
}

/*
 * OpfamilyIsVisible
 *  Determine whether an opfamily (identified by OID) is visible in the
 *  current search path.  Visible means "would be found by searching
 *  for the unqualified opfamily name".
 */

bool
OpfamilyIsVisible(Oid opfid)
{
 return OpfamilyIsVisibleExt(opfid, NULL);
}

/*
 * OpfamilyIsVisibleExt
 *  As above, but if the opfamily isn't found and is_missing is not NULL,
 *  then set *is_missing = true and return false instead of throwing
 *  an error.  (Caller must initialize *is_missing = false.)
 */

static bool
OpfamilyIsVisibleExt(Oid opfid, bool *is_missing)
{
 HeapTuple opftup;
 Form_pg_opfamily opfform;
 Oid   opfnamespace;
 bool  visible;

 opftup = SearchSysCache1(OPFAMILYOID, ObjectIdGetDatum(opfid));
 if (!HeapTupleIsValid(opftup))
 {
  if (is_missing != NULL)
  {
   *is_missing = true;
   return false;
  }
  elog(ERROR, "cache lookup failed for opfamily %u", opfid);
 }
 opfform = (Form_pg_opfamily) GETSTRUCT(opftup);

 recomputeNamespacePath();

 /*
  * Quick check: if it ain't in the path at all, it ain't visible. Items in
  * the system namespace are surely in the path and so we needn't even do
  * list_member_oid() for them.
 */

 opfnamespace = opfform->opfnamespace;
 if (opfnamespace != PG_CATALOG_NAMESPACE &&
  !list_member_oid(activeSearchPath, opfnamespace))
  visible = false;
 else
 {
  /*
   * If it is in the path, it might still not be visible; it could be
   * hidden by another opfamily of the same name earlier in the path. So
   * we must do a slow check to see if this opfamily would be found by
   * OpfamilynameGetOpfid.
 */

  char    *opfname = NameStr(opfform->opfname);

  visible = (OpfamilynameGetOpfid(opfform->opfmethod, opfname) == opfid);
 }

 ReleaseSysCache(opftup);

 return visible;
}

/*
 * lookup_collation
 *  If there's a collation of the given name/namespace, and it works
 *  with the given encoding, return its OID.  Else return InvalidOid.
 */

static Oid
lookup_collation(const char *collname, Oid collnamespace, int32 encoding)
{
 Oid   collid;
 HeapTuple colltup;
 Form_pg_collation collform;

 /* Check for encoding-specific entry (exact match) */
 collid = GetSysCacheOid3(COLLNAMEENCNSP, Anum_pg_collation_oid,
        PointerGetDatum(collname),
        Int32GetDatum(encoding),
        ObjectIdGetDatum(collnamespace));
 if (OidIsValid(collid))
  return collid;

 /*
  * Check for any-encoding entry.  This takes a bit more work: while libc
  * collations with collencoding = -1 do work with all encodings, ICU
  * collations only work with certain encodings, so we have to check that
  * aspect before deciding it's a match.
 */

 colltup = SearchSysCache3(COLLNAMEENCNSP,
         PointerGetDatum(collname),
         Int32GetDatum(-1),
         ObjectIdGetDatum(collnamespace));
 if (!HeapTupleIsValid(colltup))
  return InvalidOid;
 collform = (Form_pg_collation) GETSTRUCT(colltup);
 if (collform->collprovider == COLLPROVIDER_ICU)
 {
  if (is_encoding_supported_by_icu(encoding))
   collid = collform->oid;
  else
   collid = InvalidOid;
 }
 else
 {
  collid = collform->oid;
 }
 ReleaseSysCache(colltup);
 return collid;
}

/*
 * CollationGetCollid
 *  Try to resolve an unqualified collation name.
 *  Returns OID if collation found in search path, else InvalidOid.
 *
 * Note that this will only find collations that work with the current
 * database's encoding.
 */

Oid
CollationGetCollid(const char *collname)
{
 int32  dbencoding = GetDatabaseEncoding();
 ListCell   *l;

 recomputeNamespacePath();

 foreach(l, activeSearchPath)
 {
  Oid   namespaceId = lfirst_oid(l);
  Oid   collid;

  if (namespaceId == myTempNamespace)
   continue;   /* do not look in temp namespace */

  collid = lookup_collation(collname, namespaceId, dbencoding);
  if (OidIsValid(collid))
   return collid;
 }

 /* Not found in path */
 return InvalidOid;
}

/*
 * CollationIsVisible
 *  Determine whether a collation (identified by OID) is visible in the
 *  current search path.  Visible means "would be found by searching
 *  for the unqualified collation name".
 *
 * Note that only collations that work with the current database's encoding
 * will be considered visible.
 */

bool
CollationIsVisible(Oid collid)
{
 return CollationIsVisibleExt(collid, NULL);
}

/*
 * CollationIsVisibleExt
 *  As above, but if the collation isn't found and is_missing is not NULL,
 *  then set *is_missing = true and return false instead of throwing
 *  an error.  (Caller must initialize *is_missing = false.)
 */

static bool
CollationIsVisibleExt(Oid collid, bool *is_missing)
{
 HeapTuple colltup;
 Form_pg_collation collform;
 Oid   collnamespace;
 bool  visible;

 colltup = SearchSysCache1(COLLOID, ObjectIdGetDatum(collid));
 if (!HeapTupleIsValid(colltup))
 {
  if (is_missing != NULL)
  {
   *is_missing = true;
   return false;
  }
  elog(ERROR, "cache lookup failed for collation %u", collid);
 }
 collform = (Form_pg_collation) GETSTRUCT(colltup);

 recomputeNamespacePath();

 /*
  * Quick check: if it ain't in the path at all, it ain't visible. Items in
  * the system namespace are surely in the path and so we needn't even do
  * list_member_oid() for them.
 */

 collnamespace = collform->collnamespace;
 if (collnamespace != PG_CATALOG_NAMESPACE &&
  !list_member_oid(activeSearchPath, collnamespace))
  visible = false;
 else
 {
  /*
   * If it is in the path, it might still not be visible; it could be
   * hidden by another collation of the same name earlier in the path,
   * or it might not work with the current DB encoding.  So we must do a
   * slow check to see if this collation would be found by
   * CollationGetCollid.
 */

  char    *collname = NameStr(collform->collname);

  visible = (CollationGetCollid(collname) == collid);
 }

 ReleaseSysCache(colltup);

 return visible;
}


/*
 * ConversionGetConid
 *  Try to resolve an unqualified conversion name.
 *  Returns OID if conversion found in search path, else InvalidOid.
 *
 * This is essentially the same as RelnameGetRelid.
 */

Oid
ConversionGetConid(const char *conname)
{
 Oid   conid;
 ListCell   *l;

 recomputeNamespacePath();

 foreach(l, activeSearchPath)
 {
  Oid   namespaceId = lfirst_oid(l);

  if (namespaceId == myTempNamespace)
   continue;   /* do not look in temp namespace */

  conid = GetSysCacheOid2(CONNAMENSP, Anum_pg_conversion_oid,
        PointerGetDatum(conname),
        ObjectIdGetDatum(namespaceId));
  if (OidIsValid(conid))
   return conid;
 }

 /* Not found in path */
 return InvalidOid;
}

/*
 * ConversionIsVisible
 *  Determine whether a conversion (identified by OID) is visible in the
 *  current search path.  Visible means "would be found by searching
 *  for the unqualified conversion name".
 */

bool
ConversionIsVisible(Oid conid)
{
 return ConversionIsVisibleExt(conid, NULL);
}

/*
 * ConversionIsVisibleExt
 *  As above, but if the conversion isn't found and is_missing is not NULL,
 *  then set *is_missing = true and return false instead of throwing
 *  an error.  (Caller must initialize *is_missing = false.)
 */

static bool
ConversionIsVisibleExt(Oid conid, bool *is_missing)
{
 HeapTuple contup;
 Form_pg_conversion conform;
 Oid   connamespace;
 bool  visible;

 contup = SearchSysCache1(CONVOID, ObjectIdGetDatum(conid));
 if (!HeapTupleIsValid(contup))
 {
  if (is_missing != NULL)
  {
   *is_missing = true;
   return false;
  }
  elog(ERROR, "cache lookup failed for conversion %u", conid);
 }
 conform = (Form_pg_conversion) GETSTRUCT(contup);

 recomputeNamespacePath();

 /*
  * Quick check: if it ain't in the path at all, it ain't visible. Items in
  * the system namespace are surely in the path and so we needn't even do
  * list_member_oid() for them.
 */

 connamespace = conform->connamespace;
 if (connamespace != PG_CATALOG_NAMESPACE &&
  !list_member_oid(activeSearchPath, connamespace))
  visible = false;
 else
 {
  /*
   * If it is in the path, it might still not be visible; it could be
   * hidden by another conversion of the same name earlier in the path.
   * So we must do a slow check to see if this conversion would be found
   * by ConversionGetConid.
 */

  char    *conname = NameStr(conform->conname);

  visible = (ConversionGetConid(conname) == conid);
 }

 ReleaseSysCache(contup);

 return visible;
}

/*
 * get_statistics_object_oid - find a statistics object by possibly qualified name
 *
 * If not found, returns InvalidOid if missing_ok, else throws error
 */

Oid
get_statistics_object_oid(List *names, bool missing_ok)
{
 char    *schemaname;
 char    *stats_name;
 Oid   namespaceId;
 Oid   stats_oid = InvalidOid;
 ListCell   *l;

 /* deconstruct the name list */
 DeconstructQualifiedName(names, &schemaname, &stats_name);

 if (schemaname)
 {
  /* use exact schema given */
  namespaceId = LookupExplicitNamespace(schemaname, missing_ok);
  if (missing_ok && !OidIsValid(namespaceId))
   stats_oid = InvalidOid;
  else
   stats_oid = GetSysCacheOid2(STATEXTNAMENSP, Anum_pg_statistic_ext_oid,
          PointerGetDatum(stats_name),
          ObjectIdGetDatum(namespaceId));
 }
 else
 {
  /* search for it in search path */
  recomputeNamespacePath();

  foreach(l, activeSearchPath)
  {
   namespaceId = lfirst_oid(l);

   if (namespaceId == myTempNamespace)
    continue;  /* do not look in temp namespace */
   stats_oid = GetSysCacheOid2(STATEXTNAMENSP, Anum_pg_statistic_ext_oid,
          PointerGetDatum(stats_name),
          ObjectIdGetDatum(namespaceId));
   if (OidIsValid(stats_oid))
    break;
  }
 }

 if (!OidIsValid(stats_oid) && !missing_ok)
  ereport(ERROR,
    (errcode(ERRCODE_UNDEFINED_OBJECT),
     errmsg("statistics object \"%s\" does not exist",
      NameListToString(names))));

 return stats_oid;
}

/*
 * StatisticsObjIsVisible
 *  Determine whether a statistics object (identified by OID) is visible in
 *  the current search path.  Visible means "would be found by searching
 *  for the unqualified statistics object name".
 */

bool
StatisticsObjIsVisible(Oid stxid)
{
 return StatisticsObjIsVisibleExt(stxid, NULL);
}

/*
 * StatisticsObjIsVisibleExt
 *  As above, but if the statistics object isn't found and is_missing is
 *  not NULL, then set *is_missing = true and return false instead of
 *  throwing an error.  (Caller must initialize *is_missing = false.)
 */

static bool
StatisticsObjIsVisibleExt(Oid stxid, bool *is_missing)
{
 HeapTuple stxtup;
 Form_pg_statistic_ext stxform;
 Oid   stxnamespace;
 bool  visible;

 stxtup = SearchSysCache1(STATEXTOID, ObjectIdGetDatum(stxid));
 if (!HeapTupleIsValid(stxtup))
 {
  if (is_missing != NULL)
  {
   *is_missing = true;
   return false;
  }
  elog(ERROR, "cache lookup failed for statistics object %u", stxid);
 }
 stxform = (Form_pg_statistic_ext) GETSTRUCT(stxtup);

 recomputeNamespacePath();

 /*
  * Quick check: if it ain't in the path at all, it ain't visible. Items in
  * the system namespace are surely in the path and so we needn't even do
  * list_member_oid() for them.
 */

 stxnamespace = stxform->stxnamespace;
 if (stxnamespace != PG_CATALOG_NAMESPACE &&
  !list_member_oid(activeSearchPath, stxnamespace))
  visible = false;
 else
 {
  /*
   * If it is in the path, it might still not be visible; it could be
   * hidden by another statistics object of the same name earlier in the
   * path. So we must do a slow check for conflicting objects.
 */

  char    *stxname = NameStr(stxform->stxname);
  ListCell   *l;

  visible = false;
  foreach(l, activeSearchPath)
  {
   Oid   namespaceId = lfirst_oid(l);

   if (namespaceId == myTempNamespace)
    continue;  /* do not look in temp namespace */

   if (namespaceId == stxnamespace)
   {
    /* Found it first in path */
    visible = true;
    break;
   }
   if (SearchSysCacheExists2(STATEXTNAMENSP,
           PointerGetDatum(stxname),
           ObjectIdGetDatum(namespaceId)))
   {
    /* Found something else first in path */
    break;
   }
  }
 }

 ReleaseSysCache(stxtup);

 return visible;
}

/*
 * get_ts_parser_oid - find a TS parser by possibly qualified name
 *
 * If not found, returns InvalidOid if missing_ok, else throws error
 */

Oid
get_ts_parser_oid(List *names, bool missing_ok)
{
 char    *schemaname;
 char    *parser_name;
 Oid   namespaceId;
 Oid   prsoid = InvalidOid;
 ListCell   *l;

 /* deconstruct the name list */
 DeconstructQualifiedName(names, &schemaname, &parser_name);

 if (schemaname)
 {
  /* use exact schema given */
  namespaceId = LookupExplicitNamespace(schemaname, missing_ok);
  if (missing_ok && !OidIsValid(namespaceId))
   prsoid = InvalidOid;
  else
   prsoid = GetSysCacheOid2(TSPARSERNAMENSP, Anum_pg_ts_parser_oid,
          PointerGetDatum(parser_name),
          ObjectIdGetDatum(namespaceId));
 }
 else
 {
  /* search for it in search path */
  recomputeNamespacePath();

  foreach(l, activeSearchPath)
  {
   namespaceId = lfirst_oid(l);

   if (namespaceId == myTempNamespace)
    continue;  /* do not look in temp namespace */

   prsoid = GetSysCacheOid2(TSPARSERNAMENSP, Anum_pg_ts_parser_oid,
          PointerGetDatum(parser_name),
          ObjectIdGetDatum(namespaceId));
   if (OidIsValid(prsoid))
    break;
  }
 }

 if (!OidIsValid(prsoid) && !missing_ok)
  ereport(ERROR,
    (errcode(ERRCODE_UNDEFINED_OBJECT),
     errmsg("text search parser \"%s\" does not exist",
      NameListToString(names))));

 return prsoid;
}

/*
 * TSParserIsVisible
 *  Determine whether a parser (identified by OID) is visible in the
 *  current search path.  Visible means "would be found by searching
 *  for the unqualified parser name".
 */

bool
TSParserIsVisible(Oid prsId)
{
 return TSParserIsVisibleExt(prsId, NULL);
}

/*
 * TSParserIsVisibleExt
 *  As above, but if the parser isn't found and is_missing is not NULL,
 *  then set *is_missing = true and return false instead of throwing
 *  an error.  (Caller must initialize *is_missing = false.)
 */

static bool
TSParserIsVisibleExt(Oid prsId, bool *is_missing)
{
 HeapTuple tup;
 Form_pg_ts_parser form;
 Oid   namespace;
 bool  visible;

 tup = SearchSysCache1(TSPARSEROID, ObjectIdGetDatum(prsId));
 if (!HeapTupleIsValid(tup))
 {
  if (is_missing != NULL)
  {
   *is_missing = true;
   return false;
  }
  elog(ERROR, "cache lookup failed for text search parser %u", prsId);
 }
 form = (Form_pg_ts_parser) GETSTRUCT(tup);

 recomputeNamespacePath();

 /*
  * Quick check: if it ain't in the path at all, it ain't visible. Items in
  * the system namespace are surely in the path and so we needn't even do
  * list_member_oid() for them.
 */

 namespace = form->prsnamespace;
 if (namespace != PG_CATALOG_NAMESPACE &&
  !list_member_oid(activeSearchPath, namespace))
  visible = false;
 else
 {
  /*
   * If it is in the path, it might still not be visible; it could be
   * hidden by another parser of the same name earlier in the path. So
   * we must do a slow check for conflicting parsers.
 */

  char    *name = NameStr(form->prsname);
  ListCell   *l;

  visible = false;
  foreach(l, activeSearchPath)
  {
   Oid   namespaceId = lfirst_oid(l);

   if (namespaceId == myTempNamespace)
    continue;  /* do not look in temp namespace */

   if (namespaceId == namespace)
   {
    /* Found it first in path */
    visible = true;
    break;
   }
   if (SearchSysCacheExists2(TSPARSERNAMENSP,
           PointerGetDatum(name),
           ObjectIdGetDatum(namespaceId)))
   {
    /* Found something else first in path */
    break;
   }
  }
 }

 ReleaseSysCache(tup);

 return visible;
}

/*
 * get_ts_dict_oid - find a TS dictionary by possibly qualified name
 *
 * If not found, returns InvalidOid if missing_ok, else throws error
 */

Oid
get_ts_dict_oid(List *names, bool missing_ok)
{
 char    *schemaname;
 char    *dict_name;
 Oid   namespaceId;
 Oid   dictoid = InvalidOid;
 ListCell   *l;

 /* deconstruct the name list */
 DeconstructQualifiedName(names, &schemaname, &dict_name);

 if (schemaname)
 {
  /* use exact schema given */
  namespaceId = LookupExplicitNamespace(schemaname, missing_ok);
  if (missing_ok && !OidIsValid(namespaceId))
   dictoid = InvalidOid;
  else
   dictoid = GetSysCacheOid2(TSDICTNAMENSP, Anum_pg_ts_dict_oid,
           PointerGetDatum(dict_name),
           ObjectIdGetDatum(namespaceId));
 }
 else
 {
  /* search for it in search path */
  recomputeNamespacePath();

  foreach(l, activeSearchPath)
  {
   namespaceId = lfirst_oid(l);

   if (namespaceId == myTempNamespace)
    continue;  /* do not look in temp namespace */

   dictoid = GetSysCacheOid2(TSDICTNAMENSP, Anum_pg_ts_dict_oid,
           PointerGetDatum(dict_name),
           ObjectIdGetDatum(namespaceId));
   if (OidIsValid(dictoid))
    break;
  }
 }

 if (!OidIsValid(dictoid) && !missing_ok)
  ereport(ERROR,
    (errcode(ERRCODE_UNDEFINED_OBJECT),
     errmsg("text search dictionary \"%s\" does not exist",
      NameListToString(names))));

 return dictoid;
}

/*
 * TSDictionaryIsVisible
 *  Determine whether a dictionary (identified by OID) is visible in the
 *  current search path.  Visible means "would be found by searching
 *  for the unqualified dictionary name".
 */

bool
TSDictionaryIsVisible(Oid dictId)
{
 return TSDictionaryIsVisibleExt(dictId, NULL);
}

/*
 * TSDictionaryIsVisibleExt
 *  As above, but if the dictionary isn't found and is_missing is not NULL,
 *  then set *is_missing = true and return false instead of throwing
 *  an error.  (Caller must initialize *is_missing = false.)
 */

static bool
TSDictionaryIsVisibleExt(Oid dictId, bool *is_missing)
{
 HeapTuple tup;
 Form_pg_ts_dict form;
 Oid   namespace;
 bool  visible;

 tup = SearchSysCache1(TSDICTOID, ObjectIdGetDatum(dictId));
 if (!HeapTupleIsValid(tup))
 {
  if (is_missing != NULL)
  {
   *is_missing = true;
   return false;
  }
  elog(ERROR, "cache lookup failed for text search dictionary %u",
    dictId);
 }
 form = (Form_pg_ts_dict) GETSTRUCT(tup);

 recomputeNamespacePath();

 /*
  * Quick check: if it ain't in the path at all, it ain't visible. Items in
  * the system namespace are surely in the path and so we needn't even do
  * list_member_oid() for them.
 */

 namespace = form->dictnamespace;
 if (namespace != PG_CATALOG_NAMESPACE &&
  !list_member_oid(activeSearchPath, namespace))
  visible = false;
 else
 {
  /*
   * If it is in the path, it might still not be visible; it could be
   * hidden by another dictionary of the same name earlier in the path.
   * So we must do a slow check for conflicting dictionaries.
 */

  char    *name = NameStr(form->dictname);
  ListCell   *l;

  visible = false;
  foreach(l, activeSearchPath)
  {
   Oid   namespaceId = lfirst_oid(l);

   if (namespaceId == myTempNamespace)
    continue;  /* do not look in temp namespace */

   if (namespaceId == namespace)
   {
    /* Found it first in path */
    visible = true;
    break;
   }
   if (SearchSysCacheExists2(TSDICTNAMENSP,
           PointerGetDatum(name),
           ObjectIdGetDatum(namespaceId)))
   {
    /* Found something else first in path */
    break;
   }
  }
 }

 ReleaseSysCache(tup);

 return visible;
}

/*
 * get_ts_template_oid - find a TS template by possibly qualified name
 *
 * If not found, returns InvalidOid if missing_ok, else throws error
 */

Oid
get_ts_template_oid(List *names, bool missing_ok)
{
 char    *schemaname;
 char    *template_name;
 Oid   namespaceId;
 Oid   tmploid = InvalidOid;
 ListCell   *l;

 /* deconstruct the name list */
 DeconstructQualifiedName(names, &schemaname, &template_name);

 if (schemaname)
 {
  /* use exact schema given */
  namespaceId = LookupExplicitNamespace(schemaname, missing_ok);
  if (missing_ok && !OidIsValid(namespaceId))
   tmploid = InvalidOid;
  else
   tmploid = GetSysCacheOid2(TSTEMPLATENAMENSP, Anum_pg_ts_template_oid,
           PointerGetDatum(template_name),
           ObjectIdGetDatum(namespaceId));
 }
 else
 {
  /* search for it in search path */
  recomputeNamespacePath();

  foreach(l, activeSearchPath)
  {
   namespaceId = lfirst_oid(l);

   if (namespaceId == myTempNamespace)
    continue;  /* do not look in temp namespace */

   tmploid = GetSysCacheOid2(TSTEMPLATENAMENSP, Anum_pg_ts_template_oid,
           PointerGetDatum(template_name),
           ObjectIdGetDatum(namespaceId));
   if (OidIsValid(tmploid))
    break;
  }
 }

 if (!OidIsValid(tmploid) && !missing_ok)
  ereport(ERROR,
    (errcode(ERRCODE_UNDEFINED_OBJECT),
     errmsg("text search template \"%s\" does not exist",
      NameListToString(names))));

 return tmploid;
}

/*
 * TSTemplateIsVisible
 *  Determine whether a template (identified by OID) is visible in the
 *  current search path.  Visible means "would be found by searching
 *  for the unqualified template name".
 */

bool
TSTemplateIsVisible(Oid tmplId)
{
 return TSTemplateIsVisibleExt(tmplId, NULL);
}

/*
 * TSTemplateIsVisibleExt
 *  As above, but if the template isn't found and is_missing is not NULL,
 *  then set *is_missing = true and return false instead of throwing
 *  an error.  (Caller must initialize *is_missing = false.)
 */

static bool
TSTemplateIsVisibleExt(Oid tmplId, bool *is_missing)
{
 HeapTuple tup;
 Form_pg_ts_template form;
 Oid   namespace;
 bool  visible;

 tup = SearchSysCache1(TSTEMPLATEOID, ObjectIdGetDatum(tmplId));
 if (!HeapTupleIsValid(tup))
 {
  if (is_missing != NULL)
  {
   *is_missing = true;
   return false;
  }
  elog(ERROR, "cache lookup failed for text search template %u", tmplId);
 }
 form = (Form_pg_ts_template) GETSTRUCT(tup);

 recomputeNamespacePath();

 /*
  * Quick check: if it ain't in the path at all, it ain't visible. Items in
  * the system namespace are surely in the path and so we needn't even do
  * list_member_oid() for them.
 */

 namespace = form->tmplnamespace;
 if (namespace != PG_CATALOG_NAMESPACE &&
  !list_member_oid(activeSearchPath, namespace))
  visible = false;
 else
 {
  /*
   * If it is in the path, it might still not be visible; it could be
   * hidden by another template of the same name earlier in the path. So
   * we must do a slow check for conflicting templates.
 */

  char    *name = NameStr(form->tmplname);
  ListCell   *l;

  visible = false;
  foreach(l, activeSearchPath)
  {
   Oid   namespaceId = lfirst_oid(l);

   if (namespaceId == myTempNamespace)
    continue;  /* do not look in temp namespace */

   if (namespaceId == namespace)
   {
    /* Found it first in path */
    visible = true;
    break;
   }
   if (SearchSysCacheExists2(TSTEMPLATENAMENSP,
           PointerGetDatum(name),
           ObjectIdGetDatum(namespaceId)))
   {
    /* Found something else first in path */
    break;
   }
  }
 }

 ReleaseSysCache(tup);

 return visible;
}

/*
 * get_ts_config_oid - find a TS config by possibly qualified name
 *
 * If not found, returns InvalidOid if missing_ok, else throws error
 */

Oid
get_ts_config_oid(List *names, bool missing_ok)
{
 char    *schemaname;
 char    *config_name;
 Oid   namespaceId;
 Oid   cfgoid = InvalidOid;
 ListCell   *l;

 /* deconstruct the name list */
 DeconstructQualifiedName(names, &schemaname, &config_name);

 if (schemaname)
 {
  /* use exact schema given */
  namespaceId = LookupExplicitNamespace(schemaname, missing_ok);
  if (missing_ok && !OidIsValid(namespaceId))
   cfgoid = InvalidOid;
  else
   cfgoid = GetSysCacheOid2(TSCONFIGNAMENSP, Anum_pg_ts_config_oid,
          PointerGetDatum(config_name),
          ObjectIdGetDatum(namespaceId));
 }
 else
 {
  /* search for it in search path */
  recomputeNamespacePath();

  foreach(l, activeSearchPath)
  {
   namespaceId = lfirst_oid(l);

   if (namespaceId == myTempNamespace)
    continue;  /* do not look in temp namespace */

   cfgoid = GetSysCacheOid2(TSCONFIGNAMENSP, Anum_pg_ts_config_oid,
          PointerGetDatum(config_name),
          ObjectIdGetDatum(namespaceId));
   if (OidIsValid(cfgoid))
    break;
  }
 }

 if (!OidIsValid(cfgoid) && !missing_ok)
  ereport(ERROR,
    (errcode(ERRCODE_UNDEFINED_OBJECT),
     errmsg("text search configuration \"%s\" does not exist",
      NameListToString(names))));

 return cfgoid;
}

/*
 * TSConfigIsVisible
 *  Determine whether a text search configuration (identified by OID)
 *  is visible in the current search path.  Visible means "would be found
 *  by searching for the unqualified text search configuration name".
 */

bool
TSConfigIsVisible(Oid cfgid)
{
 return TSConfigIsVisibleExt(cfgid, NULL);
}

/*
 * TSConfigIsVisibleExt
 *  As above, but if the configuration isn't found and is_missing is not
 *  NULL, then set *is_missing = true and return false instead of throwing
 *  an error.  (Caller must initialize *is_missing = false.)
 */

static bool
TSConfigIsVisibleExt(Oid cfgid, bool *is_missing)
{
 HeapTuple tup;
 Form_pg_ts_config form;
 Oid   namespace;
 bool  visible;

 tup = SearchSysCache1(TSCONFIGOID, ObjectIdGetDatum(cfgid));
 if (!HeapTupleIsValid(tup))
 {
  if (is_missing != NULL)
  {
   *is_missing = true;
   return false;
  }
  elog(ERROR, "cache lookup failed for text search configuration %u",
    cfgid);
 }
 form = (Form_pg_ts_config) GETSTRUCT(tup);

 recomputeNamespacePath();

 /*
  * Quick check: if it ain't in the path at all, it ain't visible. Items in
  * the system namespace are surely in the path and so we needn't even do
  * list_member_oid() for them.
 */

 namespace = form->cfgnamespace;
 if (namespace != PG_CATALOG_NAMESPACE &&
  !list_member_oid(activeSearchPath, namespace))
  visible = false;
 else
 {
  /*
   * If it is in the path, it might still not be visible; it could be
   * hidden by another configuration of the same name earlier in the
   * path. So we must do a slow check for conflicting configurations.
 */

  char    *name = NameStr(form->cfgname);
  ListCell   *l;

  visible = false;
  foreach(l, activeSearchPath)
  {
   Oid   namespaceId = lfirst_oid(l);

   if (namespaceId == myTempNamespace)
    continue;  /* do not look in temp namespace */

   if (namespaceId == namespace)
   {
    /* Found it first in path */
    visible = true;
    break;
   }
   if (SearchSysCacheExists2(TSCONFIGNAMENSP,
           PointerGetDatum(name),
           ObjectIdGetDatum(namespaceId)))
   {
    /* Found something else first in path */
    break;
   }
  }
 }

 ReleaseSysCache(tup);

 return visible;
}


/*
 * DeconstructQualifiedName
 *  Given a possibly-qualified name expressed as a list of String nodes,
 *  extract the schema name and object name.
 *
 * *nspname_p is set to NULL if there is no explicit schema name.
 */

void
DeconstructQualifiedName(const List *names,
       char **nspname_p,
       char **objname_p)
{
 char    *catalogname;
 char    *schemaname = NULL;
 char    *objname = NULL;

 switch (list_length(names))
 {
  case 1:
   objname = strVal(linitial(names));
   break;
  case 2:
   schemaname = strVal(linitial(names));
   objname = strVal(lsecond(names));
   break;
  case 3:
   catalogname = strVal(linitial(names));
   schemaname = strVal(lsecond(names));
   objname = strVal(lthird(names));

   /*
    * We check the catalog name and then ignore it.
 */

   if (strcmp(catalogname, get_database_name(MyDatabaseId)) != 0)
    ereport(ERROR,
      (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
       errmsg("cross-database references are not implemented: %s",
        NameListToString(names))));
   break;
  default:
   ereport(ERROR,
     (errcode(ERRCODE_SYNTAX_ERROR),
      errmsg("improper qualified name (too many dotted names): %s",
       NameListToString(names))));
   break;
 }

 *nspname_p = schemaname;
 *objname_p = objname;
}

/*
 * LookupNamespaceNoError
 *  Look up a schema name.
 *
 * Returns the namespace OID, or InvalidOid if not found.
 *
 * Note this does NOT perform any permissions check --- callers are
 * responsible for being sure that an appropriate check is made.
 * In the majority of cases LookupExplicitNamespace is preferable.
 */

Oid
LookupNamespaceNoError(const char *nspname)
{
 /* check for pg_temp alias */
 if (strcmp(nspname, "pg_temp") == 0)
 {
  if (OidIsValid(myTempNamespace))
  {
   InvokeNamespaceSearchHook(myTempNamespace, true);
   return myTempNamespace;
  }

  /*
   * Since this is used only for looking up existing objects, there is
   * no point in trying to initialize the temp namespace here; and doing
   * so might create problems for some callers. Just report "not found".
 */

  return InvalidOid;
 }

 return get_namespace_oid(nspname, true);
}

/*
 * LookupExplicitNamespace
 *  Process an explicitly-specified schema name: look up the schema
 *  and verify we have USAGE (lookup) rights in it.
 *
 * Returns the namespace OID
 */

Oid
LookupExplicitNamespace(const char *nspname, bool missing_ok)
{
 Oid   namespaceId;
 AclResult aclresult;

 /* check for pg_temp alias */
 if (strcmp(nspname, "pg_temp") == 0)
 {
  if (OidIsValid(myTempNamespace))
   return myTempNamespace;

  /*
   * Since this is used only for looking up existing objects, there is
   * no point in trying to initialize the temp namespace here; and doing
   * so might create problems for some callers --- just fall through.
 */

 }

 namespaceId = get_namespace_oid(nspname, missing_ok);
 if (missing_ok && !OidIsValid(namespaceId))
  return InvalidOid;

 aclresult = object_aclcheck(NamespaceRelationId, namespaceId, GetUserId(), ACL_USAGE);
 if (aclresult != ACLCHECK_OK)
  aclcheck_error(aclresult, OBJECT_SCHEMA,
        nspname);
 /* Schema search hook for this lookup */
 InvokeNamespaceSearchHook(namespaceId, true);

 return namespaceId;
}

/*
 * LookupCreationNamespace
 *  Look up the schema and verify we have CREATE rights on it.
 *
 * This is just like LookupExplicitNamespace except for the different
 * permission check, and that we are willing to create pg_temp if needed.
 *
 * Note: calling this may result in a CommandCounterIncrement operation,
 * if we have to create or clean out the temp namespace.
 */

Oid
LookupCreationNamespace(const char *nspname)
{
 Oid   namespaceId;
 AclResult aclresult;

 /* check for pg_temp alias */
 if (strcmp(nspname, "pg_temp") == 0)
 {
  /* Initialize temp namespace */
  AccessTempTableNamespace(false);
  return myTempNamespace;
 }

 namespaceId = get_namespace_oid(nspname, false);

 aclresult = object_aclcheck(NamespaceRelationId, namespaceId, GetUserId(), ACL_CREATE);
 if (aclresult != ACLCHECK_OK)
  aclcheck_error(aclresult, OBJECT_SCHEMA,
        nspname);

 return namespaceId;
}

/*
 * Common checks on switching namespaces.
 *
 * We complain if either the old or new namespaces is a temporary schema
 * (or temporary toast schema), or if either the old or new namespaces is the
 * TOAST schema.
 */

void
CheckSetNamespace(Oid oldNspOid, Oid nspOid)
{
 /* disallow renaming into or out of temp schemas */
 if (isAnyTempNamespace(nspOid) || isAnyTempNamespace(oldNspOid))
  ereport(ERROR,
    (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
     errmsg("cannot move objects into or out of temporary schemas")));

 /* same for TOAST schema */
 if (nspOid == PG_TOAST_NAMESPACE || oldNspOid == PG_TOAST_NAMESPACE)
  ereport(ERROR,
    (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
     errmsg("cannot move objects into or out of TOAST schema")));
}

/*
 * QualifiedNameGetCreationNamespace
 *  Given a possibly-qualified name for an object (in List-of-Strings
 *  format), determine what namespace the object should be created in.
 *  Also extract and return the object name (last component of list).
 *
 * Note: this does not apply any permissions check.  Callers must check
 * for CREATE rights on the selected namespace when appropriate.
 *
 * Note: calling this may result in a CommandCounterIncrement operation,
 * if we have to create or clean out the temp namespace.
 */

Oid
QualifiedNameGetCreationNamespace(const List *names, char **objname_p)
{
 char    *schemaname;
 Oid   namespaceId;

 /* deconstruct the name list */
 DeconstructQualifiedName(names, &schemaname, objname_p);

 if (schemaname)
 {
  /* check for pg_temp alias */
  if (strcmp(schemaname, "pg_temp") == 0)
  {
   /* Initialize temp namespace */
   AccessTempTableNamespace(false);
   return myTempNamespace;
  }
  /* use exact schema given */
  namespaceId = get_namespace_oid(schemaname, false);
  /* we do not check for USAGE rights here! */
 }
 else
 {
  /* use the default creation namespace */
  recomputeNamespacePath();
  if (activeTempCreationPending)
  {
   /* Need to initialize temp namespace */
   AccessTempTableNamespace(true);
   return myTempNamespace;
  }
  namespaceId = activeCreationNamespace;
  if (!OidIsValid(namespaceId))
   ereport(ERROR,
     (errcode(ERRCODE_UNDEFINED_SCHEMA),
      errmsg("no schema has been selected to create in")));
 }

 return namespaceId;
}

/*
 * get_namespace_oid - given a namespace name, look up the OID
 *
 * If missing_ok is false, throw an error if namespace name not found.  If
 * true, just return InvalidOid.
 */

Oid
get_namespace_oid(const char *nspname, bool missing_ok)
{
 Oid   oid;

 oid = GetSysCacheOid1(NAMESPACENAME, Anum_pg_namespace_oid,
        CStringGetDatum(nspname));
 if (!OidIsValid(oid) && !missing_ok)
  ereport(ERROR,
    (errcode(ERRCODE_UNDEFINED_SCHEMA),
     errmsg("schema \"%s\" does not exist", nspname)));

 return oid;
}

/*
 * makeRangeVarFromNameList
 *  Utility routine to convert a qualified-name list into RangeVar form.
 */

RangeVar *
makeRangeVarFromNameList(const List *names)
{
 RangeVar   *rel = makeRangeVar(NULL, NULL, -1);

 switch (list_length(names))
 {
  case 1:
   rel->relname = strVal(linitial(names));
   break;
  case 2:
   rel->schemaname = strVal(linitial(names));
   rel->relname = strVal(lsecond(names));
   break;
  case 3:
   rel->catalogname = strVal(linitial(names));
   rel->schemaname = strVal(lsecond(names));
   rel->relname = strVal(lthird(names));
   break;
  default:
   ereport(ERROR,
     (errcode(ERRCODE_SYNTAX_ERROR),
      errmsg("improper relation name (too many dotted names): %s",
       NameListToString(names))));
   break;
 }

 return rel;
}

/*
 * NameListToString
 *  Utility routine to convert a qualified-name list into a string.
 *
 * This is used primarily to form error messages, and so we do not quote
 * the list elements, for the sake of legibility.
 *
 * In most scenarios the list elements should always be String values,
 * but we also allow A_Star for the convenience of ColumnRef processing.
 */

char *
NameListToString(const List *names)
{
 StringInfoData string;
 ListCell   *l;

 initStringInfo(&string);

 foreach(l, names)
 {
  Node    *name = (Node *) lfirst(l);

  if (l != list_head(names))
   appendStringInfoChar(&string, '.');

  if (IsA(name, String))
   appendStringInfoString(&string, strVal(name));
  else if (IsA(name, A_Star))
   appendStringInfoChar(&string, '*');
  else
   elog(ERROR, "unexpected node type in name list: %d",
     (int) nodeTag(name));
 }

 return string.data;
}

/*
 * NameListToQuotedString
 *  Utility routine to convert a qualified-name list into a string.
 *
 * Same as above except that names will be double-quoted where necessary,
 * so the string could be re-parsed (eg, by textToQualifiedNameList).
 */

char *
NameListToQuotedString(const List *names)
{
 StringInfoData string;
 ListCell   *l;

 initStringInfo(&string);

 foreach(l, names)
 {
  if (l != list_head(names))
   appendStringInfoChar(&string, '.');
  appendStringInfoString(&string, quote_identifier(strVal(lfirst(l))));
 }

 return string.data;
}

/*
 * isTempNamespace - is the given namespace my temporary-table namespace?
 */

bool
isTempNamespace(Oid namespaceId)
{
 if (OidIsValid(myTempNamespace) && myTempNamespace == namespaceId)
  return true;
 return false;
}

/*
 * isTempToastNamespace - is the given namespace my temporary-toast-table
 *  namespace?
 */

bool
isTempToastNamespace(Oid namespaceId)
{
 if (OidIsValid(myTempToastNamespace) && myTempToastNamespace == namespaceId)
  return true;
 return false;
}

/*
 * isTempOrTempToastNamespace - is the given namespace my temporary-table
 *  namespace or my temporary-toast-table namespace?
 */

bool
isTempOrTempToastNamespace(Oid namespaceId)
{
 if (OidIsValid(myTempNamespace) &&
  (myTempNamespace == namespaceId || myTempToastNamespace == namespaceId))
  return true;
 return false;
}

/*
 * isAnyTempNamespace - is the given namespace a temporary-table namespace
 * (either my own, or another backend's)?  Temporary-toast-table namespaces
 * are included, too.
 */

bool
isAnyTempNamespace(Oid namespaceId)
{
 bool  result;
 char    *nspname;

 /* True if the namespace name starts with "pg_temp_" or "pg_toast_temp_" */
 nspname = get_namespace_name(namespaceId);
 if (!nspname)
  return false;   /* no such namespace? */
 result = (strncmp(nspname, "pg_temp_"8) == 0) ||
  (strncmp(nspname, "pg_toast_temp_"14) == 0);
 pfree(nspname);
 return result;
}

/*
 * isOtherTempNamespace - is the given namespace some other backend's
 * temporary-table namespace (including temporary-toast-table namespaces)?
 *
 * Note: for most purposes in the C code, this function is obsolete.  Use
 * RELATION_IS_OTHER_TEMP() instead to detect non-local temp relations.
 */

bool
isOtherTempNamespace(Oid namespaceId)
{
 /* If it's my own temp namespace, say "false" */
 if (isTempOrTempToastNamespace(namespaceId))
  return false;
 /* Else, if it's any temp namespace, say "true" */
 return isAnyTempNamespace(namespaceId);
}

/*
 * checkTempNamespaceStatus - is the given namespace owned and actively used
 * by a backend?
 *
 * Note: this can be used while scanning relations in pg_class to detect
 * orphaned temporary tables or namespaces with a backend connected to a
 * given database.  The result may be out of date quickly, so the caller
 * must be careful how to handle this information.
 */

TempNamespaceStatus
checkTempNamespaceStatus(Oid namespaceId)
{
 PGPROC    *proc;
 ProcNumber procNumber;

 Assert(OidIsValid(MyDatabaseId));

 procNumber = GetTempNamespaceProcNumber(namespaceId);

 /* No such namespace, or its name shows it's not temp? */
 if (procNumber == INVALID_PROC_NUMBER)
  return TEMP_NAMESPACE_NOT_TEMP;

 /* Is the backend alive? */
 proc = ProcNumberGetProc(procNumber);
 if (proc == NULL)
  return TEMP_NAMESPACE_IDLE;

 /* Is the backend connected to the same database we are looking at? */
 if (proc->databaseId != MyDatabaseId)
  return TEMP_NAMESPACE_IDLE;

 /* Does the backend own the temporary namespace? */
 if (proc->tempNamespaceId != namespaceId)
  return TEMP_NAMESPACE_IDLE;

 /* Yup, so namespace is busy */
 return TEMP_NAMESPACE_IN_USE;
}

/*
 * GetTempNamespaceProcNumber - if the given namespace is a temporary-table
 * namespace (either my own, or another backend's), return the proc number
 * that owns it.  Temporary-toast-table namespaces are included, too.
 * If it isn't a temp namespace, return INVALID_PROC_NUMBER.
 */

ProcNumber
GetTempNamespaceProcNumber(Oid namespaceId)
{
 int   result;
 char    *nspname;

 /* See if the namespace name starts with "pg_temp_" or "pg_toast_temp_" */
 nspname = get_namespace_name(namespaceId);
 if (!nspname)
  return INVALID_PROC_NUMBER; /* no such namespace? */
 if (strncmp(nspname, "pg_temp_"8) == 0)
  result = atoi(nspname + 8);
 else if (strncmp(nspname, "pg_toast_temp_"14) == 0)
  result = atoi(nspname + 14);
 else
  result = INVALID_PROC_NUMBER;
 pfree(nspname);
 return result;
}

/*
 * GetTempToastNamespace - get the OID of my temporary-toast-table namespace,
 * which must already be assigned.  (This is only used when creating a toast
 * table for a temp table, so we must have already done InitTempTableNamespace)
 */

Oid
GetTempToastNamespace(void)
{
 Assert(OidIsValid(myTempToastNamespace));
 return myTempToastNamespace;
}


/*
 * GetTempNamespaceState - fetch status of session's temporary namespace
 *
 * This is used for conveying state to a parallel worker, and is not meant
 * for general-purpose access.
 */

void
GetTempNamespaceState(Oid *tempNamespaceId, Oid *tempToastNamespaceId)
{
 /* Return namespace OIDs, or 0 if session has not created temp namespace */
 *tempNamespaceId = myTempNamespace;
 *tempToastNamespaceId = myTempToastNamespace;
}

/*
 * SetTempNamespaceState - set status of session's temporary namespace
 *
 * This is used for conveying state to a parallel worker, and is not meant for
 * general-purpose access.  By transferring these namespace OIDs to workers,
 * we ensure they will have the same notion of the search path as their leader
 * does.
 */

void
SetTempNamespaceState(Oid tempNamespaceId, Oid tempToastNamespaceId)
{
 /* Worker should not have created its own namespaces ... */
 Assert(myTempNamespace == InvalidOid);
 Assert(myTempToastNamespace == InvalidOid);
 Assert(myTempNamespaceSubID == InvalidSubTransactionId);

 /* Assign same namespace OIDs that leader has */
 myTempNamespace = tempNamespaceId;
 myTempToastNamespace = tempToastNamespaceId;

 /*
  * It's fine to leave myTempNamespaceSubID == InvalidSubTransactionId.
  * Even if the namespace is new so far as the leader is concerned, it's
  * not new to the worker, and we certainly wouldn't want the worker trying
  * to destroy it.
 */


 baseSearchPathValid = false/* may need to rebuild list */
 searchPathCacheValid = false;
}


/*
 * GetSearchPathMatcher - fetch current search path definition.
 *
 * The result structure is allocated in the specified memory context
 * (which might or might not be equal to CurrentMemoryContext); but any
 * junk created by revalidation calculations will be in CurrentMemoryContext.
 */

SearchPathMatcher *
GetSearchPathMatcher(MemoryContext context)
{
 SearchPathMatcher *result;
 List    *schemas;
 MemoryContext oldcxt;

 recomputeNamespacePath();

 oldcxt = MemoryContextSwitchTo(context);

 result = (SearchPathMatcher *) palloc0(sizeof(SearchPathMatcher));
 schemas = list_copy(activeSearchPath);
 while (schemas && linitial_oid(schemas) != activeCreationNamespace)
 {
  if (linitial_oid(schemas) == myTempNamespace)
   result->addTemp = true;
  else
  {
   Assert(linitial_oid(schemas) == PG_CATALOG_NAMESPACE);
   result->addCatalog = true;
  }
  schemas = list_delete_first(schemas);
 }
 result->schemas = schemas;
 result->generation = activePathGeneration;

 MemoryContextSwitchTo(oldcxt);

 return result;
}

/*
 * CopySearchPathMatcher - copy the specified SearchPathMatcher.
 *
 * The result structure is allocated in CurrentMemoryContext.
 */

SearchPathMatcher *
CopySearchPathMatcher(SearchPathMatcher *path)
{
 SearchPathMatcher *result;

 result = (SearchPathMatcher *) palloc(sizeof(SearchPathMatcher));
 result->schemas = list_copy(path->schemas);
 result->addCatalog = path->addCatalog;
 result->addTemp = path->addTemp;
 result->generation = path->generation;

 return result;
}

/*
 * SearchPathMatchesCurrentEnvironment - does path match current environment?
 *
 * This is tested over and over in some common code paths, and in the typical
 * scenario where the active search path seldom changes, it'll always succeed.
 * We make that case fast by keeping a generation counter that is advanced
 * whenever the active search path changes.
 */

bool
SearchPathMatchesCurrentEnvironment(SearchPathMatcher *path)
{
 ListCell   *lc,
      *lcp;

 recomputeNamespacePath();

 /* Quick out if already known equal to active path. */
 if (path->generation == activePathGeneration)
  return true;

 /* We scan down the activeSearchPath to see if it matches the input. */
 lc = list_head(activeSearchPath);

 /* If path->addTemp, first item should be my temp namespace. */
 if (path->addTemp)
 {
  if (lc && lfirst_oid(lc) == myTempNamespace)
   lc = lnext(activeSearchPath, lc);
  else
   return false;
 }
 /* If path->addCatalog, next item should be pg_catalog. */
 if (path->addCatalog)
 {
  if (lc && lfirst_oid(lc) == PG_CATALOG_NAMESPACE)
   lc = lnext(activeSearchPath, lc);
  else
   return false;
 }
 /* We should now be looking at the activeCreationNamespace. */
 if (activeCreationNamespace != (lc ? lfirst_oid(lc) : InvalidOid))
  return false;
 /* The remainder of activeSearchPath should match path->schemas. */
 foreach(lcp, path->schemas)
 {
  if (lc && lfirst_oid(lc) == lfirst_oid(lcp))
   lc = lnext(activeSearchPath, lc);
  else
   return false;
 }
 if (lc)
  return false;

 /*
  * Update path->generation so that future tests will return quickly, so
  * long as the active search path doesn't change.
 */

 path->generation = activePathGeneration;

 return true;
}

/*
 * get_collation_oid - find a collation by possibly qualified name
 *
 * Note that this will only find collations that work with the current
 * database's encoding.
 */

Oid
get_collation_oid(List *collname, bool missing_ok)
{
 char    *schemaname;
 char    *collation_name;
 int32  dbencoding = GetDatabaseEncoding();
 Oid   namespaceId;
 Oid   colloid;
 ListCell   *l;

 /* deconstruct the name list */
 DeconstructQualifiedName(collname, &schemaname, &collation_name);

 if (schemaname)
 {
  /* use exact schema given */
  namespaceId = LookupExplicitNamespace(schemaname, missing_ok);
  if (missing_ok && !OidIsValid(namespaceId))
   return InvalidOid;

  colloid = lookup_collation(collation_name, namespaceId, dbencoding);
  if (OidIsValid(colloid))
   return colloid;
 }
 else
 {
  /* search for it in search path */
  recomputeNamespacePath();

  foreach(l, activeSearchPath)
  {
   namespaceId = lfirst_oid(l);

   if (namespaceId == myTempNamespace)
    continue;  /* do not look in temp namespace */

   colloid = lookup_collation(collation_name, namespaceId, dbencoding);
   if (OidIsValid(colloid))
    return colloid;
  }
 }

 /* Not found in path */
 if (!missing_ok)
  ereport(ERROR,
    (errcode(ERRCODE_UNDEFINED_OBJECT),
     errmsg("collation \"%s\" for encoding \"%s\" does not exist",
      NameListToString(collname), GetDatabaseEncodingName())));
 return InvalidOid;
}

/*
 * get_conversion_oid - find a conversion by possibly qualified name
 */

Oid
get_conversion_oid(List *conname, bool missing_ok)
{
 char    *schemaname;
 char    *conversion_name;
 Oid   namespaceId;
 Oid   conoid = InvalidOid;
 ListCell   *l;

 /* deconstruct the name list */
 DeconstructQualifiedName(conname, &schemaname, &conversion_name);

 if (schemaname)
 {
  /* use exact schema given */
  namespaceId = LookupExplicitNamespace(schemaname, missing_ok);
  if (missing_ok && !OidIsValid(namespaceId))
   conoid = InvalidOid;
  else
   conoid = GetSysCacheOid2(CONNAMENSP, Anum_pg_conversion_oid,
          PointerGetDatum(conversion_name),
          ObjectIdGetDatum(namespaceId));
 }
 else
 {
  /* search for it in search path */
  recomputeNamespacePath();

  foreach(l, activeSearchPath)
  {
   namespaceId = lfirst_oid(l);

   if (namespaceId == myTempNamespace)
    continue;  /* do not look in temp namespace */

   conoid = GetSysCacheOid2(CONNAMENSP, Anum_pg_conversion_oid,
          PointerGetDatum(conversion_name),
          ObjectIdGetDatum(namespaceId));
   if (OidIsValid(conoid))
    return conoid;
  }
 }

 /* Not found in path */
 if (!OidIsValid(conoid) && !missing_ok)
  ereport(ERROR,
    (errcode(ERRCODE_UNDEFINED_OBJECT),
     errmsg("conversion \"%s\" does not exist",
      NameListToString(conname))));
 return conoid;
}

/*
 * FindDefaultConversionProc - find default encoding conversion proc
 */

Oid
FindDefaultConversionProc(int32 for_encoding, int32 to_encoding)
{
 Oid   proc;
 ListCell   *l;

 recomputeNamespacePath();

 foreach(l, activeSearchPath)
 {
  Oid   namespaceId = lfirst_oid(l);

  if (namespaceId == myTempNamespace)
   continue;   /* do not look in temp namespace */

  proc = FindDefaultConversion(namespaceId, for_encoding, to_encoding);
  if (OidIsValid(proc))
   return proc;
 }

 /* Not found in path */
 return InvalidOid;
}

/*
 * Look up namespace IDs and perform ACL checks. Return newly-allocated list.
 */

static List *
preprocessNamespacePath(const char *searchPath, Oid roleid,
      bool *temp_missing)
{
 char    *rawname;
 List    *namelist;
 List    *oidlist;
 ListCell   *l;

 /* Need a modifiable copy */
 rawname = pstrdup(searchPath);

 /* Parse string into list of identifiers */
 if (!SplitIdentifierString(rawname, ',', &namelist))
 {
  /* syntax error in name list */
  /* this should not happen if GUC checked check_search_path */
  elog(ERROR, "invalid list syntax");
 }

 /*
  * Convert the list of names to a list of OIDs.  If any names are not
  * recognizable or we don't have read access, just leave them out of the
  * list.  (We can't raise an error, since the search_path setting has
  * already been accepted.) Don't make duplicate entries, either.
 */

 oidlist = NIL;
 *temp_missing = false;
 foreach(l, namelist)
 {
  char    *curname = (char *) lfirst(l);
  Oid   namespaceId;

  if (strcmp(curname, "$user") == 0)
  {
   /* $user --- substitute namespace matching user name, if any */
   HeapTuple tuple;

   tuple = SearchSysCache1(AUTHOID, ObjectIdGetDatum(roleid));
   if (HeapTupleIsValid(tuple))
   {
    char    *rname;

    rname = NameStr(((Form_pg_authid) GETSTRUCT(tuple))->rolname);
    namespaceId = get_namespace_oid(rname, true);
    ReleaseSysCache(tuple);
    if (OidIsValid(namespaceId) &&
     object_aclcheck(NamespaceRelationId, namespaceId, roleid,
         ACL_USAGE) == ACLCHECK_OK)
     oidlist = lappend_oid(oidlist, namespaceId);
   }
  }
  else if (strcmp(curname, "pg_temp") == 0)
  {
   /* pg_temp --- substitute temp namespace, if any */
   if (OidIsValid(myTempNamespace))
    oidlist = lappend_oid(oidlist, myTempNamespace);
   else
   {
    /* If it ought to be the creation namespace, set flag */
    if (oidlist == NIL)
     *temp_missing = true;
   }
  }
  else
  {
   /* normal namespace reference */
   namespaceId = get_namespace_oid(curname, true);
   if (OidIsValid(namespaceId) &&
    object_aclcheck(NamespaceRelationId, namespaceId, roleid,
        ACL_USAGE) == ACLCHECK_OK)
    oidlist = lappend_oid(oidlist, namespaceId);
  }
 }

 pfree(rawname);
 list_free(namelist);

 return oidlist;
}

/*
 * Remove duplicates, run namespace search hooks, and prepend
 * implicitly-searched namespaces. Return newly-allocated list.
 *
 * If an object_access_hook is present, this must always be recalculated. It
 * may seem that duplicate elimination is not dependent on the result of the
 * hook, but if a hook returns different results on different calls for the
 * same namespace ID, then it could affect the order in which that namespace
 * appears in the final list.
 */

static List *
finalNamespacePath(List *oidlist, Oid *firstNS)
{
 List    *finalPath = NIL;
 ListCell   *lc;

 foreach(lc, oidlist)
 {
  Oid   namespaceId = lfirst_oid(lc);

  if (!list_member_oid(finalPath, namespaceId))
  {
   if (InvokeNamespaceSearchHook(namespaceId, false))
    finalPath = lappend_oid(finalPath, namespaceId);
  }
 }

 /*
  * Remember the first member of the explicit list.  (Note: this is
  * nominally wrong if temp_missing, but we need it anyway to distinguish
  * explicit from implicit mention of pg_catalog.)
 */

 if (finalPath == NIL)
  *firstNS = InvalidOid;
 else
  *firstNS = linitial_oid(finalPath);

 /*
  * Add any implicitly-searched namespaces to the list.  Note these go on
  * the front, not the back; also notice that we do not check USAGE
  * permissions for these.
 */

 if (!list_member_oid(finalPath, PG_CATALOG_NAMESPACE))
  finalPath = lcons_oid(PG_CATALOG_NAMESPACE, finalPath);

 if (OidIsValid(myTempNamespace) &&
  !list_member_oid(finalPath, myTempNamespace))
  finalPath = lcons_oid(myTempNamespace, finalPath);

 return finalPath;
}

/*
 * Retrieve search path information from the cache; or if not there, fill
 * it. The returned entry is valid only until the next call to this function.
 */

static const SearchPathCacheEntry *
cachedNamespacePath(const char *searchPath, Oid roleid)
{
 MemoryContext oldcxt;
 SearchPathCacheEntry *entry;

 spcache_init();

 entry = spcache_insert(searchPath, roleid);

 /*
  * An OOM may have resulted in a cache entry with missing 'oidlist' or
  * 'finalPath', so just compute whatever is missing.
 */


 if (entry->oidlist == NIL)
 {
  oldcxt = MemoryContextSwitchTo(SearchPathCacheContext);
  entry->oidlist = preprocessNamespacePath(searchPath, roleid,
             &entry->temp_missing);
  MemoryContextSwitchTo(oldcxt);
 }

 /*
  * If a hook is set, we must recompute finalPath from the oidlist each
  * time, because the hook may affect the result. This is still much faster
  * than recomputing from the string (and doing catalog lookups and ACL
  * checks).
 */

 if (entry->finalPath == NIL || object_access_hook ||
  entry->forceRecompute)
 {
  list_free(entry->finalPath);
  entry->finalPath = NIL;

  oldcxt = MemoryContextSwitchTo(SearchPathCacheContext);
  entry->finalPath = finalNamespacePath(entry->oidlist,
             &entry->firstNS);
  MemoryContextSwitchTo(oldcxt);

  /*
   * If an object_access_hook is set when finalPath is calculated, the
   * result may be affected by the hook. Force recomputation of
   * finalPath the next time this cache entry is used, even if the
   * object_access_hook is not set at that time.
 */

  entry->forceRecompute = object_access_hook ? true : false;
 }

 return entry;
}

/*
 * recomputeNamespacePath - recompute path derived variables if needed.
 */

static void
recomputeNamespacePath(void)
{
 Oid   roleid = GetUserId();
 bool  pathChanged;
 const SearchPathCacheEntry *entry;

 /* Do nothing if path is already valid. */
 if (baseSearchPathValid && namespaceUser == roleid)
  return;

 entry = cachedNamespacePath(namespace_search_path, roleid);

 if (baseCreationNamespace == entry->firstNS &&
  baseTempCreationPending == entry->temp_missing &&
  equal(entry->finalPath, baseSearchPath))
 {
  pathChanged = false;
 }
 else
 {
  MemoryContext oldcxt;
  List    *newpath;

  pathChanged = true;

  /* Must save OID list in permanent storage. */
  oldcxt = MemoryContextSwitchTo(TopMemoryContext);
  newpath = list_copy(entry->finalPath);
  MemoryContextSwitchTo(oldcxt);

  /* Now safe to assign to state variables. */
  list_free(baseSearchPath);
  baseSearchPath = newpath;
  baseCreationNamespace = entry->firstNS;
  baseTempCreationPending = entry->temp_missing;
 }

 /* Mark the path valid. */
 baseSearchPathValid = true;
 namespaceUser = roleid;

 /* And make it active. */
 activeSearchPath = baseSearchPath;
 activeCreationNamespace = baseCreationNamespace;
 activeTempCreationPending = baseTempCreationPending;

 /*
  * Bump the generation only if something actually changed.  (Notice that
  * what we compared to was the old state of the base path variables.)
 */

 if (pathChanged)
  activePathGeneration++;
}

/*
 * AccessTempTableNamespace
 *  Provide access to a temporary namespace, potentially creating it
 *  if not present yet.  This routine registers if the namespace gets
 *  in use in this transaction.  'force' can be set to true to allow
 *  the caller to enforce the creation of the temporary namespace for
 *  use in this backend, which happens if its creation is pending.
 */

static void
AccessTempTableNamespace(bool force)
{
 /*
  * Make note that this temporary namespace has been accessed in this
  * transaction.
 */

 MyXactFlags |= XACT_FLAGS_ACCESSEDTEMPNAMESPACE;

 /*
  * If the caller attempting to access a temporary schema expects the
  * creation of the namespace to be pending and should be enforced, then go
  * through the creation.
 */

 if (!force && OidIsValid(myTempNamespace))
  return;

 /*
  * The temporary tablespace does not exist yet and is wanted, so
  * initialize it.
 */

 InitTempTableNamespace();
}

/*
 * InitTempTableNamespace
 *  Initialize temp table namespace on first use in a particular backend
 */

static void
InitTempTableNamespace(void)
{
 char  namespaceName[NAMEDATALEN];
 Oid   namespaceId;
 Oid   toastspaceId;

 Assert(!OidIsValid(myTempNamespace));

 /*
  * First, do permission check to see if we are authorized to make temp
  * tables.  We use a nonstandard error message here since "databasename:
  * permission denied" might be a tad cryptic.
  *
  * Note that ACL_CREATE_TEMP rights are rechecked in pg_namespace_aclmask;
  * that's necessary since current user ID could change during the session.
  * But there's no need to make the namespace in the first place until a
  * temp table creation request is made by someone with appropriate rights.
 */

 if (object_aclcheck(DatabaseRelationId, MyDatabaseId, GetUserId(),
      ACL_CREATE_TEMP) != ACLCHECK_OK)
  ereport(ERROR,
    (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
     errmsg("permission denied to create temporary tables in database \"%s\"",
      get_database_name(MyDatabaseId))));

 /*
  * Do not allow a Hot Standby session to make temp tables.  Aside from
  * problems with modifying the system catalogs, there is a naming
  * conflict: pg_temp_N belongs to the session with proc number N on the
  * primary, not to a hot standby session with the same proc number.  We
  * should not be able to get here anyway due to XactReadOnly checks, but
  * let's just make real sure.  Note that this also backstops various
  * operations that allow XactReadOnly transactions to modify temp tables;
  * they'd need RecoveryInProgress checks if not for this.
 */

 if (RecoveryInProgress())
  ereport(ERROR,
    (errcode(ERRCODE_READ_ONLY_SQL_TRANSACTION),
     errmsg("cannot create temporary tables during recovery")));

 /* Parallel workers can't create temporary tables, either. */
 if (IsParallelWorker())
  ereport(ERROR,
    (errcode(ERRCODE_READ_ONLY_SQL_TRANSACTION),
     errmsg("cannot create temporary tables during a parallel operation")));

 snprintf(namespaceName, sizeof(namespaceName), "pg_temp_%d", MyProcNumber);

 namespaceId = get_namespace_oid(namespaceName, true);
 if (!OidIsValid(namespaceId))
 {
  /*
   * First use of this temp namespace in this database; create it. The
   * temp namespaces are always owned by the superuser.  We leave their
   * permissions at default --- i.e., no access except to superuser ---
   * to ensure that unprivileged users can't peek at other backends'
   * temp tables.  This works because the places that access the temp
   * namespace for my own backend skip permissions checks on it.
 */

  namespaceId = NamespaceCreate(namespaceName, BOOTSTRAP_SUPERUSERID,
           true);
  /* Advance command counter to make namespace visible */
  CommandCounterIncrement();
 }
 else
 {
  /*
   * If the namespace already exists, clean it out (in case the former
   * owner crashed without doing so).
 */

  RemoveTempRelations(namespaceId);
 }

 /*
  * If the corresponding toast-table namespace doesn't exist yet, create
  * it. (We assume there is no need to clean it out if it does exist, since
  * dropping a parent table should make its toast table go away.)
 */

 snprintf(namespaceName, sizeof(namespaceName), "pg_toast_temp_%d",
    MyProcNumber);

 toastspaceId = get_namespace_oid(namespaceName, true);
 if (!OidIsValid(toastspaceId))
 {
  toastspaceId = NamespaceCreate(namespaceName, BOOTSTRAP_SUPERUSERID,
            true);
  /* Advance command counter to make namespace visible */
  CommandCounterIncrement();
 }

 /*
  * Okay, we've prepared the temp namespace ... but it's not committed yet,
  * so all our work could be undone by transaction rollback.  Set flag for
  * AtEOXact_Namespace to know what to do.
 */

 myTempNamespace = namespaceId;
 myTempToastNamespace = toastspaceId;

 /*
  * Mark MyProc as owning this namespace which other processes can use to
  * decide if a temporary namespace is in use or not.  We assume that
  * assignment of namespaceId is an atomic operation.  Even if it is not,
  * the temporary relation which resulted in the creation of this temporary
  * namespace is still locked until the current transaction commits, and
  * its pg_namespace row is not visible yet.  However it does not matter:
  * this flag makes the namespace as being in use, so no objects created on
  * it would be removed concurrently.
 */

 MyProc->tempNamespaceId = namespaceId;

 /* It should not be done already. */
 Assert(myTempNamespaceSubID == InvalidSubTransactionId);
 myTempNamespaceSubID = GetCurrentSubTransactionId();

 baseSearchPathValid = false/* need to rebuild list */
 searchPathCacheValid = false;
}

/*
 * End-of-transaction cleanup for namespaces.
 */

void
AtEOXact_Namespace(bool isCommit, bool parallel)
{
 /*
  * If we abort the transaction in which a temp namespace was selected,
  * we'll have to do any creation or cleanout work over again.  So, just
  * forget the namespace entirely until next time.  On the other hand, if
  * we commit then register an exit callback to clean out the temp tables
  * at backend shutdown.  (We only want to register the callback once per
  * session, so this is a good place to do it.)
 */

 if (myTempNamespaceSubID != InvalidSubTransactionId && !parallel)
 {
  if (isCommit)
   before_shmem_exit(RemoveTempRelationsCallback, 0);
  else
  {
   myTempNamespace = InvalidOid;
   myTempToastNamespace = InvalidOid;
   baseSearchPathValid = false/* need to rebuild list */
   searchPathCacheValid = false;

   /*
    * Reset the temporary namespace flag in MyProc.  We assume that
    * this operation is atomic.
    *
    * Because this transaction is aborting, the pg_namespace row is
    * not visible to anyone else anyway, but that doesn't matter:
    * it's not a problem if objects contained in this namespace are
    * removed concurrently.
 */

   MyProc->tempNamespaceId = InvalidOid;
  }
  myTempNamespaceSubID = InvalidSubTransactionId;
 }

}

/*
 * AtEOSubXact_Namespace
 *
 * At subtransaction commit, propagate the temp-namespace-creation
 * flag to the parent subtransaction.
 *
 * At subtransaction abort, forget the flag if we set it up.
 */

void
AtEOSubXact_Namespace(bool isCommit, SubTransactionId mySubid,
       SubTransactionId parentSubid)
{

 if (myTempNamespaceSubID == mySubid)
 {
  if (isCommit)
   myTempNamespaceSubID = parentSubid;
  else
  {
   myTempNamespaceSubID = InvalidSubTransactionId;
   /* TEMP namespace creation failed, so reset state */
   myTempNamespace = InvalidOid;
   myTempToastNamespace = InvalidOid;
   baseSearchPathValid = false/* need to rebuild list */
   searchPathCacheValid = false;

   /*
    * Reset the temporary namespace flag in MyProc.  We assume that
    * this operation is atomic.
    *
    * Because this subtransaction is aborting, the pg_namespace row
    * is not visible to anyone else anyway, but that doesn't matter:
    * it's not a problem if objects contained in this namespace are
    * removed concurrently.
 */

   MyProc->tempNamespaceId = InvalidOid;
  }
 }
}

/*
 * Remove all relations in the specified temp namespace.
 *
 * This is called at backend shutdown (if we made any temp relations).
 * It is also called when we begin using a pre-existing temp namespace,
 * in order to clean out any relations that might have been created by
 * a crashed backend.
 */

static void
RemoveTempRelations(Oid tempNamespaceId)
{
 ObjectAddress object;

 /*
  * We want to get rid of everything in the target namespace, but not the
  * namespace itself (deleting it only to recreate it later would be a
  * waste of cycles).  Hence, specify SKIP_ORIGINAL.  It's also an INTERNAL
  * deletion, and we want to not drop any extensions that might happen to
  * own temp objects.
 */

 object.classId = NamespaceRelationId;
 object.objectId = tempNamespaceId;
 object.objectSubId = 0;

 performDeletion(&object, DROP_CASCADE,
     PERFORM_DELETION_INTERNAL |
     PERFORM_DELETION_QUIETLY |
     PERFORM_DELETION_SKIP_ORIGINAL |
     PERFORM_DELETION_SKIP_EXTENSIONS);
}

/*
 * Callback to remove temp relations at backend exit.
 */

static void
RemoveTempRelationsCallback(int code, Datum arg)
{
 if (OidIsValid(myTempNamespace)) /* should always be true */
 {
  /* Need to ensure we have a usable transaction. */
  AbortOutOfAnyTransaction();
  StartTransactionCommand();
  PushActiveSnapshot(GetTransactionSnapshot());

  RemoveTempRelations(myTempNamespace);

  PopActiveSnapshot();
  CommitTransactionCommand();
 }
}

/*
 * Remove all temp tables from the temporary namespace.
 */

void
ResetTempTableNamespace(void)
{
 if (OidIsValid(myTempNamespace))
  RemoveTempRelations(myTempNamespace);
}


/*
 * Routines for handling the GUC variable 'search_path'.
 */


/* check_hook: validate new search_path value */
bool
check_search_path(char **newval, void **extra, GucSource source)
{
 Oid   roleid = InvalidOid;
 const char *searchPath = *newval;
 char    *rawname;
 List    *namelist;
 bool  use_cache = (SearchPathCacheContext != NULL);

 /*
  * We used to try to check that the named schemas exist, but there are
  * many valid use-cases for having search_path settings that include
  * schemas that don't exist; and often, we are not inside a transaction
  * here and so can't consult the system catalogs anyway.  So now, the only
  * requirement is syntactic validity of the identifier list.
  *
  * Checking only the syntactic validity also allows us to use the search
  * path cache (if available) to avoid calling SplitIdentifierString() on
  * the same string repeatedly.
 */

 if (use_cache)
 {
  spcache_init();

  roleid = GetUserId();

  if (spcache_lookup(searchPath, roleid) != NULL)
   return true;
 }

 /*
  * Ensure validity check succeeds before creating cache entry.
 */


 rawname = pstrdup(searchPath); /* need a modifiable copy */

 /* Parse string into list of identifiers */
 if (!SplitIdentifierString(rawname, ',', &namelist))
 {
  /* syntax error in name list */
  GUC_check_errdetail("List syntax is invalid.");
  pfree(rawname);
  list_free(namelist);
  return false;
 }
 pfree(rawname);
 list_free(namelist);

 /* OK to create empty cache entry */
 if (use_cache)
  (void) spcache_insert(searchPath, roleid);

 return true;
}

/* assign_hook: do extra actions as needed */
void
assign_search_path(const char *newval, void *extra)
{
 /* don't access search_path during bootstrap */
 Assert(!IsBootstrapProcessingMode());

 /*
  * We mark the path as needing recomputation, but don't do anything until
  * it's needed.  This avoids trying to do database access during GUC
  * initialization, or outside a transaction.
  *
  * This does not invalidate the search path cache, so if this value had
  * been previously set and no syscache invalidations happened,
  * recomputation may not be necessary.
 */

 baseSearchPathValid = false;
}

/*
 * InitializeSearchPath: initialize module during InitPostgres.
 *
 * This is called after we are up enough to be able to do catalog lookups.
 */

void
InitializeSearchPath(void)
{
 if (IsBootstrapProcessingMode())
 {
  /*
   * In bootstrap mode, the search path must be 'pg_catalog' so that
   * tables are created in the proper namespace; ignore the GUC setting.
 */

  MemoryContext oldcxt;

  oldcxt = MemoryContextSwitchTo(TopMemoryContext);
  baseSearchPath = list_make1_oid(PG_CATALOG_NAMESPACE);
  MemoryContextSwitchTo(oldcxt);
  baseCreationNamespace = PG_CATALOG_NAMESPACE;
  baseTempCreationPending = false;
  baseSearchPathValid = true;
  namespaceUser = GetUserId();
  activeSearchPath = baseSearchPath;
  activeCreationNamespace = baseCreationNamespace;
  activeTempCreationPending = baseTempCreationPending;
  activePathGeneration++; /* pro forma */
 }
 else
 {
  /*
   * In normal mode, arrange for a callback on any syscache invalidation
   * that will affect the search_path cache.
 */


  /* namespace name or ACLs may have changed */
  CacheRegisterSyscacheCallback(NAMESPACEOID,
           InvalidationCallback,
           (Datum) 0);

  /* role name may affect the meaning of "$user" */
  CacheRegisterSyscacheCallback(AUTHOID,
           InvalidationCallback,
           (Datum) 0);

  /* role membership may affect ACLs */
  CacheRegisterSyscacheCallback(AUTHMEMROLEMEM,
           InvalidationCallback,
           (Datum) 0);

  /* database owner may affect ACLs */
  CacheRegisterSyscacheCallback(DATABASEOID,
           InvalidationCallback,
           (Datum) 0);

  /* Force search path to be recomputed on next use */
  baseSearchPathValid = false;
  searchPathCacheValid = false;
 }
}

/*
 * InvalidationCallback
 *  Syscache inval callback function
 */

static void
InvalidationCallback(Datum arg, int cacheid, uint32 hashvalue)
{
 /*
  * Force search path to be recomputed on next use, also invalidating the
  * search path cache (because namespace names, ACLs, or role names may
  * have changed).
 */

 baseSearchPathValid = false;
 searchPathCacheValid = false;
}

/*
 * Fetch the active search path. The return value is a palloc'ed list
 * of OIDs; the caller is responsible for freeing this storage as
 * appropriate.
 *
 * The returned list includes the implicitly-prepended namespaces only if
 * includeImplicit is true.
 *
 * Note: calling this may result in a CommandCounterIncrement operation,
 * if we have to create or clean out the temp namespace.
 */

List *
fetch_search_path(bool includeImplicit)
{
 List    *result;

 recomputeNamespacePath();

 /*
  * If the temp namespace should be first, force it to exist.  This is so
  * that callers can trust the result to reflect the actual default
  * creation namespace.  It's a bit bogus to do this here, since
  * current_schema() is supposedly a stable function without side-effects,
  * but the alternatives seem worse.
 */

 if (activeTempCreationPending)
 {
  AccessTempTableNamespace(true);
  recomputeNamespacePath();
 }

 result = list_copy(activeSearchPath);
 if (!includeImplicit)
 {
  while (result && linitial_oid(result) != activeCreationNamespace)
   result = list_delete_first(result);
 }

 return result;
}

/*
 * Fetch the active search path into a caller-allocated array of OIDs.
 * Returns the number of path entries.  (If this is more than sarray_len,
 * then the data didn't fit and is not all stored.)
 *
 * The returned list always includes the implicitly-prepended namespaces,
 * but never includes the temp namespace.  (This is suitable for existing
 * users, which would want to ignore the temp namespace anyway.)  This
 * definition allows us to not worry about initializing the temp namespace.
 */

int
fetch_search_path_array(Oid *sarray, int sarray_len)
{
 int   count = 0;
 ListCell   *l;

 recomputeNamespacePath();

 foreach(l, activeSearchPath)
 {
  Oid   namespaceId = lfirst_oid(l);

  if (namespaceId == myTempNamespace)
   continue;   /* do not include temp namespace */

  if (count < sarray_len)
   sarray[count] = namespaceId;
  count++;
 }

 return count;
}


/*
 * Export the FooIsVisible functions as SQL-callable functions.
 *
 * Note: as of Postgres 8.4, these will silently return NULL if called on
 * a nonexistent object OID, rather than failing.  This is to avoid race
 * condition errors when a query that's scanning a catalog using an MVCC
 * snapshot uses one of these functions.  The underlying IsVisible functions
 * always use an up-to-date snapshot and so might see the object as already
 * gone when it's still visible to the transaction snapshot.
 */


Datum
pg_table_is_visible(PG_FUNCTION_ARGS)
{
 Oid   oid = PG_GETARG_OID(0);
 bool  result;
 bool  is_missing = false;

 result = RelationIsVisibleExt(oid, &is_missing);

 if (is_missing)
  PG_RETURN_NULL();
 PG_RETURN_BOOL(result);
}

Datum
pg_type_is_visible(PG_FUNCTION_ARGS)
{
 Oid   oid = PG_GETARG_OID(0);
 bool  result;
 bool  is_missing = false;

 result = TypeIsVisibleExt(oid, &is_missing);

 if (is_missing)
  PG_RETURN_NULL();
 PG_RETURN_BOOL(result);
}

Datum
pg_function_is_visible(PG_FUNCTION_ARGS)
{
 Oid   oid = PG_GETARG_OID(0);
 bool  result;
 bool  is_missing = false;

 result = FunctionIsVisibleExt(oid, &is_missing);

 if (is_missing)
  PG_RETURN_NULL();
 PG_RETURN_BOOL(result);
}

Datum
pg_operator_is_visible(PG_FUNCTION_ARGS)
{
 Oid   oid = PG_GETARG_OID(0);
 bool  result;
 bool  is_missing = false;

 result = OperatorIsVisibleExt(oid, &is_missing);

 if (is_missing)
  PG_RETURN_NULL();
 PG_RETURN_BOOL(result);
}

Datum
pg_opclass_is_visible(PG_FUNCTION_ARGS)
{
 Oid   oid = PG_GETARG_OID(0);
 bool  result;
 bool  is_missing = false;

 result = OpclassIsVisibleExt(oid, &is_missing);

 if (is_missing)
  PG_RETURN_NULL();
 PG_RETURN_BOOL(result);
}

Datum
pg_opfamily_is_visible(PG_FUNCTION_ARGS)
{
 Oid   oid = PG_GETARG_OID(0);
 bool  result;
 bool  is_missing = false;

 result = OpfamilyIsVisibleExt(oid, &is_missing);

 if (is_missing)
  PG_RETURN_NULL();
 PG_RETURN_BOOL(result);
}

Datum
pg_collation_is_visible(PG_FUNCTION_ARGS)
{
 Oid   oid = PG_GETARG_OID(0);
 bool  result;
 bool  is_missing = false;

 result = CollationIsVisibleExt(oid, &is_missing);

 if (is_missing)
  PG_RETURN_NULL();
 PG_RETURN_BOOL(result);
}

Datum
pg_conversion_is_visible(PG_FUNCTION_ARGS)
{
 Oid   oid = PG_GETARG_OID(0);
 bool  result;
 bool  is_missing = false;

 result = ConversionIsVisibleExt(oid, &is_missing);

 if (is_missing)
  PG_RETURN_NULL();
 PG_RETURN_BOOL(result);
}

Datum
pg_statistics_obj_is_visible(PG_FUNCTION_ARGS)
{
 Oid   oid = PG_GETARG_OID(0);
 bool  result;
 bool  is_missing = false;

 result = StatisticsObjIsVisibleExt(oid, &is_missing);

 if (is_missing)
  PG_RETURN_NULL();
 PG_RETURN_BOOL(result);
}

Datum
pg_ts_parser_is_visible(PG_FUNCTION_ARGS)
{
 Oid   oid = PG_GETARG_OID(0);
 bool  result;
 bool  is_missing = false;

 result = TSParserIsVisibleExt(oid, &is_missing);

 if (is_missing)
  PG_RETURN_NULL();
 PG_RETURN_BOOL(result);
}

Datum
pg_ts_dict_is_visible(PG_FUNCTION_ARGS)
{
 Oid   oid = PG_GETARG_OID(0);
 bool  result;
 bool  is_missing = false;

 result = TSDictionaryIsVisibleExt(oid, &is_missing);

 if (is_missing)
  PG_RETURN_NULL();
 PG_RETURN_BOOL(result);
}

Datum
pg_ts_template_is_visible(PG_FUNCTION_ARGS)
{
 Oid   oid = PG_GETARG_OID(0);
 bool  result;
 bool  is_missing = false;

 result = TSTemplateIsVisibleExt(oid, &is_missing);

 if (is_missing)
  PG_RETURN_NULL();
 PG_RETURN_BOOL(result);
}

Datum
pg_ts_config_is_visible(PG_FUNCTION_ARGS)
{
 Oid   oid = PG_GETARG_OID(0);
 bool  result;
 bool  is_missing = false;

 result = TSConfigIsVisibleExt(oid, &is_missing);

 if (is_missing)
  PG_RETURN_NULL();
 PG_RETURN_BOOL(result);
}

Datum
pg_my_temp_schema(PG_FUNCTION_ARGS)
{
 PG_RETURN_OID(myTempNamespace);
}

Datum
pg_is_other_temp_schema(PG_FUNCTION_ARGS)
{
 Oid   oid = PG_GETARG_OID(0);

 PG_RETURN_BOOL(isOtherTempNamespace(oid));
}

Messung V0.5 in Prozent
C=93 H=95 G=93

[Verzeichnis aufwärts0.220unsichere VerbindungÜbersetzung europäischer Sprachen durch Browser2026-08-08]

                                                                                                                                                                                                                                                                                                                                                                                                     


Neuigkeiten

     Aktuelles
     Motto des Tages

Open Source Software

     Quellcodebibliothek
     Eigene Quellcodes
     Fremde Quellcodes
     Suchen

Jenseits des Üblichen ....
    

Besucherstatistik

Besucherstatistik

Statistik
#Sources=141584
#Domains=752002