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