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