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