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