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