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