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