Coverage Report

Created: 2025-06-15 06:31

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