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