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