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