Coverage Report

Created: 2025-09-27 06:52

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