text: /*-------------------------------------------------------------------------
   *
   * 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-2022, 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/objectaccess.h"
  #include "catalog/pg_authid.h"
  #include "catalog/pg_collation.h"
  #include "catalog/pg_conversion.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 "catalog/pg_variable.h"
  #include "catalog/pg_package.h"
  #include "commands/dbcommands.h"
  #include "commands/packagecmds.h"
  #include "funcapi.h"
  #include "mb/pg_wchar.h"
  #include "miscadmin.h"
  #include "nodes/makefuncs.h"
  #include "parser/parse_func.h"
  #include "storage/ipc.h"
  #include "storage/lmgr.h"
  #include "storage/sinvaladt.h"
  #include "utils/acl.h"
  #include "utils/builtins.h"
  #include "utils/catcache.h"
  #include "utils/guc.h"
  #include "utils/inval.h"
  #include "utils/lsyscache.h"
  #include "utils/memutils.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.  (The only time this doesn't happen is
   * when we are obeying an override search path spec that says not to use the
   * temp namespace, or the temp namespace is included in the explicit list.)
   *
   * 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).
   *
   * We support a stack of "override" search path settings for use within
   * specific sections of backend code.  namespace_search_path is ignored
   * whenever the override stack is nonempty.  activeSearchPath is always
   * the actually active path; it points either to the search list of the
   * topmost stack entry, or 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.
   * We mark it invalid upon an assignment to namespace_search_path or receipt
   * of a syscache invalidation event for pg_namespace.  The recomputation
   * is done during the next non-overridden lookup attempt.  Note that an
   * override spec is never subject to recomputation.
   *
   * 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;
  
  /* Override requests are remembered in a stack of OverrideStackEntry structs */
  
  typedef struct
  {
  	List	   *searchPath;		/* the desired search path */
  	Oid			creationNamespace;	/* the desired creation namespace */
  	int			nestLevel;		/* subtransaction nesting level */
  } OverrideStackEntry;
  
  static List *overrideStack = NIL;
  
  /*
   * 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 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 NamespaceCallback(Datum arg, int cacheid, uint32 hashvalue);
  static bool MatchNamedCall(HeapTuple proctup, int nargs, List *argnames,
  						   bool include_out_arguments, int pronargs,
  						   int **argnumbers);
  
  
  /*
   * 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 = pg_namespace_aclcheck(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 (!pg_class_ownercheck(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)
  {
  	HeapTuple	reltup;
  	Form_pg_class relform;
  	Oid			relnamespace;
  	bool		visible;
  
  	reltup = SearchSysCache1(RELOID, ObjectIdGetDatum(relid));
  	if (!HeapTupleIsValid(reltup))
  		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;
  }
  
  /*
   * When we know a variable name, then we can find variable simply
   */
  Oid
  LookupVariable(const char *nspname, const char *varname, bool missing_ok)
  {
  	Oid			namespaceId;
  	Oid			varoid = InvalidOid;
  	ListCell   *l;
  
  	if (nspname)
  	{
  		namespaceId = LookupExplicitNamespace(nspname, missing_ok);
  		if (!OidIsValid(namespaceId))
  			return InvalidOid;
  
  		varoid = GetSysCacheOid2(VARIABLENAMENSP, Anum_pg_variable_oid,
  								 PointerGetDatum(varname),
  								 ObjectIdGetDatum(namespaceId));
  	}
  	else
  	{
  		/* search for it in search path */
  		recomputeNamespacePath();
  
  		foreach(l, activeSearchPath)
  		{
  			namespaceId = lfirst_oid(l);
  
  			varoid = GetSysCacheOid2(VARIABLENAMENSP, Anum_pg_variable_oid,
  									 PointerGetDatum(varname),
  									 ObjectIdGetDatum(namespaceId));
  
  			if (OidIsValid(varoid))
  				break;
  		}
  	}
  
  	if (!OidIsValid(varoid) && !missing_ok)
  	{
  		if (nspname)
  			ereport(ERROR,
  					(errcode(ERRCODE_UNDEFINED_OBJECT),
  					 errmsg("variable \"%s\".\"%s\" does not exist",
  							nspname, varname)));
  		else
  			ereport(ERROR,
  					(errcode(ERRCODE_UNDEFINED_OBJECT),
  					 errmsg("variable \"%s\" does not exist",
  							varname)));
  	}
  
  	return varoid;
  }
  
  /*
   * 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)
  {
  	HeapTuple	typtup;
  	Form_pg_type typform;
  	Oid			typnamespace;
  	bool		visible;
  
  	typtup = SearchSysCache1(TYPEOID, ObjectIdGetDatum(typid));
  	if (!HeapTupleIsValid(typtup))
  		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;
  }
  
  /*
   * HandleQualifiedName
   *		Handle the function's qualified-name and return the namespace id.
   *
   */
  Oid
  HandleQualifiedName(List *names, char **funcname, bool missing_ok)
  {
  	QualifiedName qu;
  	int         num;
  	char       *fname = NULL;
  	Oid         np_id;
  	Oid			pkgnamespaceId;
  
  	*funcname = NULL;
  	/* Extract the qualified names */
  	num = ExtractQualifiedName(names, &qu);
  	switch (num)
  	{
  		case 1:
  			fname = qu.func;
  			np_id = InvalidOid;
  			recomputeNamespacePath();
  			break;
  
  		case 2:
  			/* either schema.obj or pkg.obj */
  			fname = qu.func;
  
  			/* let try to find the package. */
  			np_id = get_package_oid(list_make1(linitial(names)), true);
  			if (OidIsValid(np_id))
  			{
  				/* Okay a package was found */
  				AclResult	aclresult;
  
  				aclresult = pg_package_aclcheck(np_id, GetUserId(), ACL_EXECUTE);
  				if (aclresult != ACLCHECK_OK)
  					aclcheck_error(aclresult, OBJECT_PACKAGE, qu.package);
  			}
  			else
  			{
  				/* no package was found, so let the system take care of the rest */
  				np_id = LookupExplicitNamespace(qu.schema, missing_ok);
  			}
  			break;
  
  		case 3:
  			fname = qu.func;
  
  			if (SearchSysCacheExists1(NAMESPACENAME, PointerGetDatum(qu.dbname))) /* select schema.pkg.func() */
  			{
  				pkgnamespaceId = get_namespace_oid(qu.dbname, false);
  				np_id = GetSysCacheOid2(PACKAGENAMENSP, Anum_pg_package_oid,						  
  													CStringGetDatum(qu.package),						  
  														ObjectIdGetDatum(pkgnamespaceId));
  				if (!OidIsValid(np_id))
  					ereport(ERROR,
  							(errcode(ERRCODE_UNDEFINED_SCHEMA),
  				 				errmsg("package \"%s\" does not exist", qu.package)));
  			}
  			else  /* select db.schema.func() */
  			{
  
  				if (strcmp(qu.dbname, get_database_name(MyDatabaseId)) != 0)
  					ereport(ERROR,
  							(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
  								errmsg("cross-database references are not implemented: %s",
  									NameListToString(names))));
  				else
  				{
  					np_id = LookupExplicitNamespace(qu.package, missing_ok);
  					if (!OidIsValid(np_id))
  						return InvalidOid;
  				}
  			}
  			break;
  
  		case 4:
  			fname = qu.func;
  
  			if (SearchSysCacheExists1(NAMESPACENAME, PointerGetDatum(qu.schema)))
  			{
  				pkgnamespaceId = get_namespace_oid(qu.schema, false);
  				np_id = GetSysCacheOid2(PACKAGENAMENSP, Anum_pg_package_oid,						  
  												CStringGetDatum(qu.package),					  
  														ObjectIdGetDatum(pkgnamespaceId));
  				if (!OidIsValid(np_id))
  					ereport(ERROR,
  							(errcode(ERRCODE_UNDEFINED_SCHEMA),
  				 				errmsg("package \"%s\" does not exist", qu.package)));
  			}
  			else
  				ereport(ERROR,
  						(errcode(ERRCODE_UNDEFINED_SCHEMA),
  				 			errmsg("schema \"%s\" does not exist", qu.schema)));		
  
  			break;
  
  		default:
  			ereport(ERROR,
  					(errcode(ERRCODE_SYNTAX_ERROR),
  					 errmsg("improper qualified name (too many dotted names).")));
  			break;
  	}
  
  	*funcname = fname;
  	return np_id;
  }
  
  
  /*
   * 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	   *funcname;
  	Oid			namespaceId;
  	CatCList   *catlist;
  	int			i;
  
  	/* check for caller error */
  	Assert(nargs >= 0 || !(expand_variadic | expand_defaults));
  
  	/* Handle the qualified-name and get namespaceId */
  	namespaceId = HandleQualifiedName(names, &funcname, missing_ok);
  	if (!OidIsValid(namespaceId) &&
  		list_length(names) >= 2)
  		return NULL;
  
  	/* 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 */
  			int			i;
  
  			for (i = 0; i < pronargs; i++)
  				newResult->args[i] = proargtypes[argnumbers[i]];
  		}
  		else
  		{
  			/* Simple positional case, just copy proargtypes as-is */
  			memcpy(newResult->args, proargtypes, pronargs * sizeof(Oid));
  		}
  		if (variadic)
  		{
  			int			i;
  
  			newResult->nvargs = effective_nargs - pronargs + 1;
  			/* Expand variadic argument into N copies of element type */
  			for (i = pronargs - 1; i < effective_nargs; i++)
  				newResult->args[i] = 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)
  {
  	HeapTuple	proctup;
  	Form_pg_proc procform;
  	Oid			pronamespace;
  	bool		visible;
  
  	proctup = SearchSysCache1(PROCOID, ObjectIdGetDatum(funcid));
  	if (!HeapTupleIsValid(proctup))
  		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, false, false, false, false);
  
  		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)
  {
  	HeapTuple	oprtup;
  	Form_pg_operator oprform;
  	Oid			oprnamespace;
  	bool		visible;
  
  	oprtup = SearchSysCache1(OPEROID, ObjectIdGetDatum(oprid));
  	if (!HeapTupleIsValid(oprtup))
  		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)
  {
  	HeapTuple	opctup;
  	Form_pg_opclass opcform;
  	Oid			opcnamespace;
  	bool		visible;
  
  	opctup = SearchSysCache1(CLAOID, ObjectIdGetDatum(opcid));
  	if (!HeapTupleIsValid(opctup))
  		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)
  {
  	HeapTuple	opftup;
  	Form_pg_opfamily opfform;
  	Oid			opfnamespace;
  	bool		visible;
  
  	opftup = SearchSysCache1(OPFAMILYOID, ObjectIdGetDatum(opfid));
  	if (!HeapTupleIsValid(opftup))
  		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)
  {
  	HeapTuple	colltup;
  	Form_pg_collation collform;
  	Oid			collnamespace;
  	bool		visible;
  
  	colltup = SearchSysCache1(COLLOID, ObjectIdGetDatum(collid));
  	if (!HeapTupleIsValid(colltup))
  		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)
  {
  	HeapTuple	contup;
  	Form_pg_conversion conform;
  	Oid			connamespace;
  	bool		visible;
  
  	contup = SearchSysCache1(CONVOID, ObjectIdGetDatum(conid));
  	if (!HeapTupleIsValid(contup))
  		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 relid)
  {
  	HeapTuple	stxtup;
  	Form_pg_statistic_ext stxform;
  	Oid			stxnamespace;
  	bool		visible;
  
  	stxtup = SearchSysCache1(STATEXTOID, ObjectIdGetDatum(relid));
  	if (!HeapTupleIsValid(stxtup))
  		elog(ERROR, "cache lookup failed for statistics object %u", relid);
  	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 == 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)
  {
  	HeapTuple	tup;
  	Form_pg_ts_parser form;
  	Oid			namespace;
  	bool		visible;
  
  	tup = SearchSysCache1(TSPARSEROID, ObjectIdGetDatum(prsId));
  	if (!HeapTupleIsValid(tup))
  		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)
  {
  	HeapTuple	tup;
  	Form_pg_ts_dict form;
  	Oid			namespace;
  	bool		visible;
  
  	tup = SearchSysCache1(TSDICTOID, ObjectIdGetDatum(dictId));
  	if (!HeapTupleIsValid(tup))
  		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)
  {
  	HeapTuple	tup;
  	Form_pg_ts_template form;
  	Oid			namespace;
  	bool		visible;
  
  	tup = SearchSysCache1(TSTEMPLATEOID, ObjectIdGetDatum(tmplId));
  	if (!HeapTupleIsValid(tup))
  		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)
  {
  	HeapTuple	tup;
  	Form_pg_ts_config form;
  	Oid			namespace;
  	bool		visible;
  
  	tup = SearchSysCache1(TSCONFIGOID, ObjectIdGetDatum(cfgid));
  	if (!HeapTupleIsValid(tup))
  		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(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;
  }
  
  /*
   * ExtractQualifiedName
   *		Given a possibly-qualified name expressed as a list of String nodes,
   *		extract these qualified names.
   */
  int
  ExtractQualifiedName(List *names, QualifiedName *qu)
  {
  	int len;
  
  	len = list_length(names);
  	
  	switch (len) // list_length(names
  	{
  		case 1:
  			qu->dbname = NULL;
  			qu->schema = NULL;
  			qu->package = NULL;
  			qu->func = strVal(linitial(names));
  			break;
  		case 2:
  			qu->dbname = NULL;
  			qu->schema = strVal(linitial(names));
  			qu->package = strVal(linitial(names));
  			qu->func = strVal(lsecond(names));	
  			break;
  		case 3:
  			qu->dbname = strVal(linitial(names));
  			qu->schema = strVal(lsecond(names));
  			qu->package = strVal(lsecond(names));
  			qu->func = strVal(lthird(names));	      
  			break;
  		case 4:
  			qu->dbname = strVal(linitial(names));
  			qu->schema = strVal(lsecond(names));
  			qu->package = strVal(lthird(names));
  			qu->func = strVal(lfourth(names));
  
  			/*
  			 * We check the catalog name and then ignore it.
  			 */
  			if (strcmp(qu->dbname, 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;
  	}
  
  	return len;
  }
  
  /*
   * 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 = pg_namespace_aclcheck(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 = pg_namespace_aclcheck(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(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(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(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(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;
  	int			backendId;
  
  	Assert(OidIsValid(MyDatabaseId));
  
  	backendId = GetTempNamespaceBackendId(namespaceId);
  
  	/* No such namespace, or its name shows it's not temp? */
  	if (backendId == InvalidBackendId)
  		return TEMP_NAMESPACE_NOT_TEMP;
  
  	/* Is the backend alive? */
  	proc = BackendIdGetProc(backendId);
  	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;
  }
  
  /*
   * GetTempNamespaceBackendId - if the given namespace is a temporary-table
   * namespace (either my own, or another backend's), return the BackendId
   * that owns it.  Temporary-toast-table namespaces are included, too.
   * If it isn't a temp namespace, return InvalidBackendId.
   */
  int
  GetTempNamespaceBackendId(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 InvalidBackendId;	/* 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 = InvalidBackendId;
  	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 */
  }
  
  
  /*
   * GetOverrideSearchPath - fetch current search path definition in form
   * used by PushOverrideSearchPath.
   *
   * 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.
   */
  OverrideSearchPath *
  GetOverrideSearchPath(MemoryContext context)
  {
  	OverrideSearchPath *result;
  	List	   *schemas;
  	MemoryContext oldcxt;
  	Oid			compatibleNS;
  
  	recomputeNamespacePath();
  
  	oldcxt = MemoryContextSwitchTo(context);
  
  	result = (OverrideSearchPath *) palloc0(sizeof(OverrideSearchPath));
  	schemas = list_copy(activeSearchPath);
  	compatibleNS = get_namespace_oid("oracle", true);
  	while (schemas && linitial_oid(schemas) != activeCreationNamespace)
  	{
  		if (linitial_oid(schemas) == myTempNamespace)
  			result->addTemp = true;
  		else if(OidIsValid(compatibleNS) && linitial_oid(schemas) == compatibleNS)
  		{
  			schemas = list_delete_first(schemas);
  			continue;
  		}
  		else
  		{
  			result->addCatalog = true;
  		}
  		schemas = list_delete_first(schemas);
  	}
  	result->schemas = schemas;
  	result->generation = activePathGeneration;
  
  	MemoryContextSwitchTo(oldcxt);
  
  	return result;
  }
  
  /*
   * CopyOverrideSearchPath - copy the specified OverrideSearchPath.
   *
   * The result structure is allocated in CurrentMemoryContext.
   */
  OverrideSearchPath *
  CopyOverrideSearchPath(OverrideSearchPath *path)
  {
  	OverrideSearchPath *result;
  
  	result = (OverrideSearchPath *) palloc(sizeof(OverrideSearchPath));
  	result->schemas = list_copy(path->schemas);
  	result->addCatalog = path->addCatalog;
  	result->addTemp = path->addTemp;
  	result->generation = path->generation;
  
  	return result;
  }
  
  /*
   * OverrideSearchPathMatchesCurrent - does path match current setting?
   *
   * 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
  OverrideSearchPathMatchesCurrent(OverrideSearchPath *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)
  	{
  		/*
  		 * TODO: figure out a way to further optimize this check. It could
  		 * potentially slow down the system.
  		 */
  		if (OidIsValid(lfirst_oid(lcp)) &&
  			get_package_name(lfirst_oid(lcp), true) != NULL)
  			continue;
  		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;
  }
  
  /*
   * PushOverrideSearchPath - temporarily override the search path
   *
   * We allow nested overrides, hence the push/pop terminology.  The GUC
   * search_path variable is ignored while an override is active.
   *
   * It's possible that newpath->useTemp is set but there is no longer any
   * active temp namespace, if the path was saved during a transaction that
   * created a temp namespace and was later rolled back.  In that case we just
   * ignore useTemp.  A plausible alternative would be to create a new temp
   * namespace, but for existing callers that's not necessary because an empty
   * temp namespace wouldn't affect their results anyway.
   *
   * It's also worth noting that other schemas listed in newpath might not
   * exist anymore either.  We don't worry about this because OIDs that match
   * no existing namespace will simply not produce any hits during searches.
   */
  void
  PushOverrideSearchPath(OverrideSearchPath *newpath)
  {
  	OverrideStackEntry *entry;
  	List	   *oidlist;
  	Oid			firstNS;
  	MemoryContext oldcxt;
  
  	/*
  	 * Copy the list for safekeeping, and insert implicitly-searched
  	 * namespaces as needed.  This code should track recomputeNamespacePath.
  	 */
  	oldcxt = MemoryContextSwitchTo(TopMemoryContext);
  
  	oidlist = list_copy(newpath->schemas);
  
  	/*
  	 * Remember the first member of the explicit list.
  	 */
  	if (oidlist == NIL)
  		firstNS = InvalidOid;
  	else
  		firstNS = linitial_oid(oidlist);
  
  	/*
  	 * 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 (newpath->addCatalog)
  		oidlist = lcons_oid(PG_CATALOG_NAMESPACE, oidlist);
  
  	if (newpath->addTemp && OidIsValid(myTempNamespace))
  		oidlist = lcons_oid(myTempNamespace, oidlist);
  
  	/*
  	 * Build the new stack entry, then insert it at the head of the list.
  	 */
  	entry = (OverrideStackEntry *) palloc(sizeof(OverrideStackEntry));
  	entry->searchPath = oidlist;
  	entry->creationNamespace = firstNS;
  	entry->nestLevel = GetCurrentTransactionNestLevel();
  
  	overrideStack = lcons(entry, overrideStack);
  
  	/* And make it active. */
  	activeSearchPath = entry->searchPath;
  	activeCreationNamespace = entry->creationNamespace;
  	activeTempCreationPending = false;	/* XXX is this OK? */
  
  	/*
  	 * We always increment activePathGeneration when pushing/popping an
  	 * override path.  In current usage, these actions always change the
  	 * effective path state, so there's no value in checking to see if it
  	 * didn't change.
  	 */
  	activePathGeneration++;
  
  	MemoryContextSwitchTo(oldcxt);
  }
  
  /*
   * PopOverrideSearchPath - undo a previous PushOverrideSearchPath
   *
   * Any push during a (sub)transaction will be popped automatically at abort.
   * But it's caller error if a push isn't popped in normal control flow.
   */
  void
  PopOverrideSearchPath(void)
  {
  	OverrideStackEntry *entry;
  
  	/* Sanity checks. */
  	if (overrideStack == NIL)
  		elog(ERROR, "bogus PopOverrideSearchPath call");
  	entry = (OverrideStackEntry *) linitial(overrideStack);
  	if (entry->nestLevel != GetCurrentTransactionNestLevel())
  		elog(ERROR, "bogus PopOverrideSearchPath call");
  
  	/* Pop the stack and free storage. */
  	overrideStack = list_delete_first(overrideStack);
  	list_free(entry->searchPath);
  	pfree(entry);
  
  	/* Activate the next level down. */
  	if (overrideStack)
  	{
  		entry = (OverrideStackEntry *) linitial(overrideStack);
  		activeSearchPath = entry->searchPath;
  		activeCreationNamespace = entry->creationNamespace;
  		activeTempCreationPending = false;	/* XXX is this OK? */
  	}
  	else
  	{
  		/* If not baseSearchPathValid, this is useless but harmless */
  		activeSearchPath = baseSearchPath;
  		activeCreationNamespace = baseCreationNamespace;
  		activeTempCreationPending = baseTempCreationPending;
  	}
  
  	/* As above, the generation always increments. */
  	activePathGeneration++;
  }
  
  
  /*
   * 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 *name, bool missing_ok)
  {
  	char	   *schemaname;
  	char	   *collation_name;
  	int32		dbencoding = GetDatabaseEncoding();
  	Oid			namespaceId;
  	Oid			colloid;
  	ListCell   *l;
  
  	/* deconstruct the name list */
  	DeconstructQualifiedName(name, &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(name), GetDatabaseEncodingName())));
  	return InvalidOid;
  }
  
  /*
   * get_conversion_oid - find a conversion by possibly qualified name
   */
  Oid
  get_conversion_oid(List *name, bool missing_ok)
  {
  	char	   *schemaname;
  	char	   *conversion_name;
  	Oid			namespaceId;
  	Oid			conoid = InvalidOid;
  	ListCell   *l;
  
  	/* deconstruct the name list */
  	DeconstructQualifiedName(name, &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(name))));
  	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;
  }
  
  /*
   * recomputeNamespacePath - recompute path derived variables if needed.
   */
  static void
  recomputeNamespacePath(void)
  {
  	Oid			roleid = GetUserId();
  	char	   *rawname;
  	List	   *namelist;
  	List	   *oidlist;
  	List	   *newpath;
  	ListCell   *l;
  	bool		temp_missing;
  	Oid			firstNS;
  	bool		pathChanged;
  	MemoryContext oldcxt;
  	Oid			compatibleNS;
  
  	/* Do nothing if an override search spec is active. */
  	if (overrideStack)
  		return;
  
  	/* Do nothing if path is already valid. */
  	if (baseSearchPathValid && namespaceUser == roleid)
  		return;
  
  	/* Need a modifiable copy of namespace_search_path string */
  	rawname = pstrdup(namespace_search_path);
  
  	/* 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) &&
  					!list_member_oid(oidlist, namespaceId) &&
  					pg_namespace_aclcheck(namespaceId, roleid,
  										  ACL_USAGE) == ACLCHECK_OK &&
  					InvokeNamespaceSearchHook(namespaceId, false))
  					oidlist = lappend_oid(oidlist, namespaceId);
  			}
  		}
  		else if (strcmp(curname, "pg_temp") == 0)
  		{
  			/* pg_temp --- substitute temp namespace, if any */
  			if (OidIsValid(myTempNamespace))
  			{
  				if (!list_member_oid(oidlist, myTempNamespace) &&
  					InvokeNamespaceSearchHook(myTempNamespace, false))
  					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) &&
  				!list_member_oid(oidlist, namespaceId) &&
  				pg_namespace_aclcheck(namespaceId, roleid,
  									  ACL_USAGE) == ACLCHECK_OK &&
  				InvokeNamespaceSearchHook(namespaceId, false))
  				oidlist = lappend_oid(oidlist, 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 (oidlist == NIL)
  		firstNS = InvalidOid;
  	else
  		firstNS = linitial_oid(oidlist);
  
  	/*
  	 * 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(oidlist, PG_CATALOG_NAMESPACE))
  		oidlist = lcons_oid(PG_CATALOG_NAMESPACE, oidlist);
  	/*
  	 *oracle namespace reference,for compatible
  	 */
  	if (compatible_db == COMPATIBLE_ORA)
  	{
  		compatibleNS = get_namespace_oid("oracle", true);
  		if (OidIsValid(compatibleNS))
  			if (!list_member_oid(oidlist, compatibleNS))
  				oidlist = lcons_oid(compatibleNS, oidlist);
  	}
  
  	if (OidIsValid(myTempNamespace) &&
  		!list_member_oid(oidlist, myTempNamespace))
  		oidlist = lcons_oid(myTempNamespace, oidlist);
  
  	/*
  	 * We want to detect the case where the effective value of the base search
  	 * path variables didn't change.  As long as we're doing so, we can avoid
  	 * copying the OID list unnecessarily.
  	 */
  	if (baseCreationNamespace == firstNS &&
  		baseTempCreationPending == temp_missing &&
  		equal(oidlist, baseSearchPath))
  	{
  		pathChanged = false;
  	}
  	else
  	{
  		pathChanged = true;
  
  		/* Must save OID list in permanent storage. */
  		oldcxt = MemoryContextSwitchTo(TopMemoryContext);
  		newpath = list_copy(oidlist);
  		MemoryContextSwitchTo(oldcxt);
  
  		/* Now safe to assign to state variables. */
  		list_free(baseSearchPath);
  		baseSearchPath = newpath;
  		baseCreationNamespace = firstNS;
  		baseTempCreationPending = 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; so
  	 * this does not deal with the situation where we have just popped an
  	 * override path and restored the prior state of the base path.  Instead
  	 * we rely on the override-popping logic to have bumped the generation.)
  	 */
  	if (pathChanged)
  		activePathGeneration++;
  
  	/* Clean up. */
  	pfree(rawname);
  	list_free(namelist);
  	list_free(oidlist);
  }
  
  /*
   * 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 (pg_database_aclcheck(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 BackendId N on the
  	 * primary, not to a hot standby session with the same BackendId.  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", MyBackendId);
  
  	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",
  			 MyBackendId);
  
  	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. */
  	AssertState(myTempNamespaceSubID == InvalidSubTransactionId);
  	myTempNamespaceSubID = GetCurrentSubTransactionId();
  
  	baseSearchPathValid = false;	/* need to rebuild list */
  }
  
  /*
   * 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 */
  
  			/*
  			 * 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;
  	}
  
  	/*
  	 * Clean up if someone failed to do PopOverrideSearchPath
  	 */
  	if (overrideStack)
  	{
  		if (isCommit)
  			elog(WARNING, "leaked override search path");
  		while (overrideStack)
  		{
  			OverrideStackEntry *entry;
  
  			entry = (OverrideStackEntry *) linitial(overrideStack);
  			overrideStack = list_delete_first(overrideStack);
  			list_free(entry->searchPath);
  			pfree(entry);
  		}
  		/* If not baseSearchPathValid, this is useless but harmless */
  		activeSearchPath = baseSearchPath;
  		activeCreationNamespace = baseCreationNamespace;
  		activeTempCreationPending = baseTempCreationPending;
  		/* Always bump generation --- see note in recomputeNamespacePath */
  		activePathGeneration++;
  	}
  }
  
  /*
   * 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)
  {
  	OverrideStackEntry *entry;
  
  	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 */
  
  			/*
  			 * 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;
  		}
  	}
  
  	/*
  	 * Clean up if someone failed to do PopOverrideSearchPath
  	 */
  	while (overrideStack)
  	{
  		entry = (OverrideStackEntry *) linitial(overrideStack);
  		if (entry->nestLevel < GetCurrentTransactionNestLevel())
  			break;
  		if (isCommit)
  			elog(WARNING, "leaked override search path");
  		overrideStack = list_delete_first(overrideStack);
  		list_free(entry->searchPath);
  		pfree(entry);
  		/* Always bump generation --- see note in recomputeNamespacePath */
  		activePathGeneration++;
  	}
  
  	/* Activate the next level down. */
  	if (overrideStack)
  	{
  		entry = (OverrideStackEntry *) linitial(overrideStack);
  		activeSearchPath = entry->searchPath;
  		activeCreationNamespace = entry->creationNamespace;
  		activeTempCreationPending = false;	/* XXX is this OK? */
  
  		/*
  		 * It's probably unnecessary to bump generation here, but this should
  		 * not be a performance-critical case, so better to be over-cautious.
  		 */
  		activePathGeneration++;
  	}
  	else
  	{
  		/* If not baseSearchPathValid, this is useless but harmless */
  		activeSearchPath = baseSearchPath;
  		activeCreationNamespace = baseCreationNamespace;
  		activeTempCreationPending = baseTempCreationPending;
  
  		/*
  		 * If we popped an override stack entry, then we already bumped the
  		 * generation above.  If we did not, then the above assignments did
  		 * nothing and we need not bump the generation.
  		 */
  	}
  }
  
  /*
   * 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();
  
  		RemoveTempRelations(myTempNamespace);
  
  		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)
  {
  	char	   *rawname;
  	List	   *namelist;
  
  	/* Need a modifiable copy of string */
  	rawname = pstrdup(*newval);
  
  	/* 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;
  	}
  
  	/*
  	 * 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.
  	 */
  
  	pfree(rawname);
  	list_free(namelist);
  
  	return true;
  }
  
  /* assign_hook: do extra actions as needed */
  void
  assign_search_path(const char *newval, void *extra)
  {
  	/*
  	 * 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.
  	 */
  	baseSearchPathValid = false;
  }
  
  /* assign_hook: do extra actions as needed */
  void
  assign_compatible_db(int newval, void *extra)
  {
  	/*
  	 * 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.
  	 */
  	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
  		 * of pg_namespace rows.
  		 */
  		CacheRegisterSyscacheCallback(NAMESPACEOID,
  									  NamespaceCallback,
  									  (Datum) 0);
  		/* Force search path to be recomputed on next use */
  		baseSearchPathValid = false;
  	}
  }
  
  /*
   * NamespaceCallback
   *		Syscache inval callback function
   */
  static void
  NamespaceCallback(Datum arg, int cacheid, uint32 hashvalue)
  {
  	/* Force search path to be recomputed on next use */
  	baseSearchPathValid = 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.  (There is no race
   * condition in the current coding because we don't accept sinval messages
   * between the SearchSysCacheExists test and the subsequent lookup.)
   */
  
  Datum
  pg_table_is_visible(PG_FUNCTION_ARGS)
  {
  	Oid			oid = PG_GETARG_OID(0);
  
  	if (!SearchSysCacheExists1(RELOID, ObjectIdGetDatum(oid)))
  		PG_RETURN_NULL();
  
  	PG_RETURN_BOOL(RelationIsVisible(oid));
  }
  
  Datum
  pg_type_is_visible(PG_FUNCTION_ARGS)
  {
  	Oid			oid = PG_GETARG_OID(0);
  
  	if (!SearchSysCacheExists1(TYPEOID, ObjectIdGetDatum(oid)))
  		PG_RETURN_NULL();
  
  	PG_RETURN_BOOL(TypeIsVisible(oid));
  }
  
  Datum
  pg_function_is_visible(PG_FUNCTION_ARGS)
  {
  	Oid			oid = PG_GETARG_OID(0);
  
  	if (!SearchSysCacheExists1(PROCOID, ObjectIdGetDatum(oid)))
  		PG_RETURN_NULL();
  
  	PG_RETURN_BOOL(FunctionIsVisible(oid));
  }
  
  Datum
  pg_operator_is_visible(PG_FUNCTION_ARGS)
  {
  	Oid			oid = PG_GETARG_OID(0);
  
  	if (!SearchSysCacheExists1(OPEROID, ObjectIdGetDatum(oid)))
  		PG_RETURN_NULL();
  
  	PG_RETURN_BOOL(OperatorIsVisible(oid));
  }
  
  Datum
  pg_opclass_is_visible(PG_FUNCTION_ARGS)
  {
  	Oid			oid = PG_GETARG_OID(0);
  
  	if (!SearchSysCacheExists1(CLAOID, ObjectIdGetDatum(oid)))
  		PG_RETURN_NULL();
  
  	PG_RETURN_BOOL(OpclassIsVisible(oid));
  }
  
  Datum
  pg_opfamily_is_visible(PG_FUNCTION_ARGS)
  {
  	Oid			oid = PG_GETARG_OID(0);
  
  	if (!SearchSysCacheExists1(OPFAMILYOID, ObjectIdGetDatum(oid)))
  		PG_RETURN_NULL();
  
  	PG_RETURN_BOOL(OpfamilyIsVisible(oid));
  }
  
  Datum
  pg_collation_is_visible(PG_FUNCTION_ARGS)
  {
  	Oid			oid = PG_GETARG_OID(0);
  
  	if (!SearchSysCacheExists1(COLLOID, ObjectIdGetDatum(oid)))
  		PG_RETURN_NULL();
  
  	PG_RETURN_BOOL(CollationIsVisible(oid));
  }
  
  Datum
  pg_conversion_is_visible(PG_FUNCTION_ARGS)
  {
  	Oid			oid = PG_GETARG_OID(0);
  
  	if (!SearchSysCacheExists1(CONVOID, ObjectIdGetDatum(oid)))
  		PG_RETURN_NULL();
  
  	PG_RETURN_BOOL(ConversionIsVisible(oid));
  }
  
  Datum
  pg_statistics_obj_is_visible(PG_FUNCTION_ARGS)
  {
  	Oid			oid = PG_GETARG_OID(0);
  
  	if (!SearchSysCacheExists1(STATEXTOID, ObjectIdGetDatum(oid)))
  		PG_RETURN_NULL();
  
  	PG_RETURN_BOOL(StatisticsObjIsVisible(oid));
  }
  
  Datum
  pg_ts_parser_is_visible(PG_FUNCTION_ARGS)
  {
  	Oid			oid = PG_GETARG_OID(0);
  
  	if (!SearchSysCacheExists1(TSPARSEROID, ObjectIdGetDatum(oid)))
  		PG_RETURN_NULL();
  
  	PG_RETURN_BOOL(TSParserIsVisible(oid));
  }
  
  Datum
  pg_ts_dict_is_visible(PG_FUNCTION_ARGS)
  {
  	Oid			oid = PG_GETARG_OID(0);
  
  	if (!SearchSysCacheExists1(TSDICTOID, ObjectIdGetDatum(oid)))
  		PG_RETURN_NULL();
  
  	PG_RETURN_BOOL(TSDictionaryIsVisible(oid));
  }
  
  Datum
  pg_ts_template_is_visible(PG_FUNCTION_ARGS)
  {
  	Oid			oid = PG_GETARG_OID(0);
  
  	if (!SearchSysCacheExists1(TSTEMPLATEOID, ObjectIdGetDatum(oid)))
  		PG_RETURN_NULL();
  
  	PG_RETURN_BOOL(TSTemplateIsVisible(oid));
  }
  
  Datum
  pg_ts_config_is_visible(PG_FUNCTION_ARGS)
  {
  	Oid			oid = PG_GETARG_OID(0);
  
  	if (!SearchSysCacheExists1(TSCONFIGOID, ObjectIdGetDatum(oid)))
  		PG_RETURN_NULL();
  
  	PG_RETURN_BOOL(TSConfigIsVisible(oid));
  }
  
  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));
  }
  