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