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