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