]> granicus.if.org Git - postgresql/blob - src/backend/catalog/namespace.c
pgindent run for 8.3.
[postgresql] / src / backend / catalog / namespace.c
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-2007, PostgreSQL Global Development Group
13  * Portions Copyright (c) 1994, Regents of the University of California
14  *
15  * IDENTIFICATION
16  *        $PostgreSQL: pgsql/src/backend/catalog/namespace.c,v 1.100 2007/11/15 21:14:33 momjian Exp $
17  *
18  *-------------------------------------------------------------------------
19  */
20 #include "postgres.h"
21
22 #include "access/xact.h"
23 #include "catalog/dependency.h"
24 #include "catalog/namespace.h"
25 #include "catalog/pg_authid.h"
26 #include "catalog/pg_conversion.h"
27 #include "catalog/pg_namespace.h"
28 #include "catalog/pg_opclass.h"
29 #include "catalog/pg_operator.h"
30 #include "catalog/pg_opfamily.h"
31 #include "catalog/pg_proc.h"
32 #include "catalog/pg_ts_config.h"
33 #include "catalog/pg_ts_dict.h"
34 #include "catalog/pg_ts_parser.h"
35 #include "catalog/pg_ts_template.h"
36 #include "catalog/pg_type.h"
37 #include "commands/dbcommands.h"
38 #include "miscadmin.h"
39 #include "nodes/makefuncs.h"
40 #include "storage/backendid.h"
41 #include "storage/ipc.h"
42 #include "utils/acl.h"
43 #include "utils/builtins.h"
44 #include "utils/guc.h"
45 #include "utils/inval.h"
46 #include "utils/lsyscache.h"
47 #include "utils/memutils.h"
48 #include "utils/syscache.h"
49
50
51 /*
52  * The namespace search path is a possibly-empty list of namespace OIDs.
53  * In addition to the explicit list, implicitly-searched namespaces
54  * may be included:
55  *
56  * 1. If a TEMP table namespace has been initialized in this session, it
57  * is implicitly searched first.  (The only time this doesn't happen is
58  * when we are obeying an override search path spec that says not to use the
59  * temp namespace, or the temp namespace is included in the explicit list.)
60  *
61  * 2. The system catalog namespace is always searched.  If the system
62  * namespace is present in the explicit path then it will be searched in
63  * the specified order; otherwise it will be searched after TEMP tables and
64  * *before* the explicit list.  (It might seem that the system namespace
65  * should be implicitly last, but this behavior appears to be required by
66  * SQL99.  Also, this provides a way to search the system namespace first
67  * without thereby making it the default creation target namespace.)
68  *
69  * For security reasons, searches using the search path will ignore the temp
70  * namespace when searching for any object type other than relations and
71  * types.  (We must allow types since temp tables have rowtypes.)
72  *
73  * The default creation target namespace is always the first element of the
74  * explicit list.  If the explicit list is empty, there is no default target.
75  *
76  * The textual specification of search_path can include "$user" to refer to
77  * the namespace named the same as the current user, if any.  (This is just
78  * ignored if there is no such namespace.)      Also, it can include "pg_temp"
79  * to refer to the current backend's temp namespace.  This is usually also
80  * ignorable if the temp namespace hasn't been set up, but there's a special
81  * case: if "pg_temp" appears first then it should be the default creation
82  * target.      We kluge this case a little bit so that the temp namespace isn't
83  * set up until the first attempt to create something in it.  (The reason for
84  * klugery is that we can't create the temp namespace outside a transaction,
85  * but initial GUC processing of search_path happens outside a transaction.)
86  * activeTempCreationPending is TRUE if "pg_temp" appears first in the string
87  * but is not reflected in activeCreationNamespace because the namespace isn't
88  * set up yet.
89  *
90  * In bootstrap mode, the search path is set equal to "pg_catalog", so that
91  * the system namespace is the only one searched or inserted into.
92  * initdb is also careful to set search_path to "pg_catalog" for its
93  * post-bootstrap standalone backend runs.      Otherwise the default search
94  * path is determined by GUC.  The factory default path contains the PUBLIC
95  * namespace (if it exists), preceded by the user's personal namespace
96  * (if one exists).
97  *
98  * We support a stack of "override" search path settings for use within
99  * specific sections of backend code.  namespace_search_path is ignored
100  * whenever the override stack is nonempty.  activeSearchPath is always
101  * the actually active path; it points either to the search list of the
102  * topmost stack entry, or to baseSearchPath which is the list derived
103  * from namespace_search_path.
104  *
105  * If baseSearchPathValid is false, then baseSearchPath (and other
106  * derived variables) need to be recomputed from namespace_search_path.
107  * We mark it invalid upon an assignment to namespace_search_path or receipt
108  * of a syscache invalidation event for pg_namespace.  The recomputation
109  * is done during the next non-overridden lookup attempt.  Note that an
110  * override spec is never subject to recomputation.
111  *
112  * Any namespaces mentioned in namespace_search_path that are not readable
113  * by the current user ID are simply left out of baseSearchPath; so
114  * we have to be willing to recompute the path when current userid changes.
115  * namespaceUser is the userid the path has been computed for.
116  *
117  * Note: all data pointed to by these List variables is in TopMemoryContext.
118  */
119
120 /* These variables define the actually active state: */
121
122 static List *activeSearchPath = NIL;
123
124 /* default place to create stuff; if InvalidOid, no default */
125 static Oid      activeCreationNamespace = InvalidOid;
126
127 /* if TRUE, activeCreationNamespace is wrong, it should be temp namespace */
128 static bool activeTempCreationPending = false;
129
130 /* These variables are the values last derived from namespace_search_path: */
131
132 static List *baseSearchPath = NIL;
133
134 static Oid      baseCreationNamespace = InvalidOid;
135
136 static bool baseTempCreationPending = false;
137
138 static Oid      namespaceUser = InvalidOid;
139
140 /* The above four values are valid only if baseSearchPathValid */
141 static bool baseSearchPathValid = true;
142
143 /* Override requests are remembered in a stack of OverrideStackEntry structs */
144
145 typedef struct
146 {
147         List       *searchPath;         /* the desired search path */
148         Oid                     creationNamespace;              /* the desired creation namespace */
149         int                     nestLevel;              /* subtransaction nesting level */
150 }       OverrideStackEntry;
151
152 static List *overrideStack = NIL;
153
154 /*
155  * myTempNamespace is InvalidOid until and unless a TEMP namespace is set up
156  * in a particular backend session (this happens when a CREATE TEMP TABLE
157  * command is first executed).  Thereafter it's the OID of the temp namespace.
158  *
159  * myTempToastNamespace is the OID of the namespace for my temp tables' toast
160  * tables.      It is set when myTempNamespace is, and is InvalidOid before that.
161  *
162  * myTempNamespaceSubID shows whether we've created the TEMP namespace in the
163  * current subtransaction.      The flag propagates up the subtransaction tree,
164  * so the main transaction will correctly recognize the flag if all
165  * intermediate subtransactions commit.  When it is InvalidSubTransactionId,
166  * we either haven't made the TEMP namespace yet, or have successfully
167  * committed its creation, depending on whether myTempNamespace is valid.
168  */
169 static Oid      myTempNamespace = InvalidOid;
170
171 static Oid      myTempToastNamespace = InvalidOid;
172
173 static SubTransactionId myTempNamespaceSubID = InvalidSubTransactionId;
174
175 /*
176  * This is the user's textual search path specification --- it's the value
177  * of the GUC variable 'search_path'.
178  */
179 char       *namespace_search_path = NULL;
180
181
182 /* Local functions */
183 static void recomputeNamespacePath(void);
184 static void InitTempTableNamespace(void);
185 static void RemoveTempRelations(Oid tempNamespaceId);
186 static void RemoveTempRelationsCallback(int code, Datum arg);
187 static void NamespaceCallback(Datum arg, Oid relid);
188
189 /* These don't really need to appear in any header file */
190 Datum           pg_table_is_visible(PG_FUNCTION_ARGS);
191 Datum           pg_type_is_visible(PG_FUNCTION_ARGS);
192 Datum           pg_function_is_visible(PG_FUNCTION_ARGS);
193 Datum           pg_operator_is_visible(PG_FUNCTION_ARGS);
194 Datum           pg_opclass_is_visible(PG_FUNCTION_ARGS);
195 Datum           pg_conversion_is_visible(PG_FUNCTION_ARGS);
196 Datum           pg_ts_parser_is_visible(PG_FUNCTION_ARGS);
197 Datum           pg_ts_dict_is_visible(PG_FUNCTION_ARGS);
198 Datum           pg_ts_template_is_visible(PG_FUNCTION_ARGS);
199 Datum           pg_ts_config_is_visible(PG_FUNCTION_ARGS);
200 Datum           pg_my_temp_schema(PG_FUNCTION_ARGS);
201 Datum           pg_is_other_temp_schema(PG_FUNCTION_ARGS);
202
203
204 /*
205  * RangeVarGetRelid
206  *              Given a RangeVar describing an existing relation,
207  *              select the proper namespace and look up the relation OID.
208  *
209  * If the relation is not found, return InvalidOid if failOK = true,
210  * otherwise raise an error.
211  */
212 Oid
213 RangeVarGetRelid(const RangeVar *relation, bool failOK)
214 {
215         Oid                     namespaceId;
216         Oid                     relId;
217
218         /*
219          * We check the catalog name and then ignore it.
220          */
221         if (relation->catalogname)
222         {
223                 if (strcmp(relation->catalogname, get_database_name(MyDatabaseId)) != 0)
224                         ereport(ERROR,
225                                         (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
226                                          errmsg("cross-database references are not implemented: \"%s.%s.%s\"",
227                                                         relation->catalogname, relation->schemaname,
228                                                         relation->relname)));
229         }
230
231         /*
232          * If istemp is set, this is a reference to a temp relation.  The parser
233          * never generates such a RangeVar in simple DML, but it can happen in
234          * contexts such as "CREATE TEMP TABLE foo (f1 int PRIMARY KEY)".  Such a
235          * command will generate an added CREATE INDEX operation, which must be
236          * careful to find the temp table, even when pg_temp is not first in the
237          * search path.
238          */
239         if (relation->istemp)
240         {
241                 if (relation->schemaname)
242                         ereport(ERROR,
243                                         (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
244                                    errmsg("temporary tables cannot specify a schema name")));
245                 if (OidIsValid(myTempNamespace))
246                         relId = get_relname_relid(relation->relname, myTempNamespace);
247                 else    /* this probably can't happen? */
248                         relId = InvalidOid;
249         }
250         else if (relation->schemaname)
251         {
252                 /* use exact schema given */
253                 namespaceId = LookupExplicitNamespace(relation->schemaname);
254                 relId = get_relname_relid(relation->relname, namespaceId);
255         }
256         else
257         {
258                 /* search the namespace path */
259                 relId = RelnameGetRelid(relation->relname);
260         }
261
262         if (!OidIsValid(relId) && !failOK)
263         {
264                 if (relation->schemaname)
265                         ereport(ERROR,
266                                         (errcode(ERRCODE_UNDEFINED_TABLE),
267                                          errmsg("relation \"%s.%s\" does not exist",
268                                                         relation->schemaname, relation->relname)));
269                 else
270                         ereport(ERROR,
271                                         (errcode(ERRCODE_UNDEFINED_TABLE),
272                                          errmsg("relation \"%s\" does not exist",
273                                                         relation->relname)));
274         }
275         return relId;
276 }
277
278 /*
279  * RangeVarGetCreationNamespace
280  *              Given a RangeVar describing a to-be-created relation,
281  *              choose which namespace to create it in.
282  *
283  * Note: calling this may result in a CommandCounterIncrement operation.
284  * That will happen on the first request for a temp table in any particular
285  * backend run; we will need to either create or clean out the temp schema.
286  */
287 Oid
288 RangeVarGetCreationNamespace(const RangeVar *newRelation)
289 {
290         Oid                     namespaceId;
291
292         /*
293          * We check the catalog name and then ignore it.
294          */
295         if (newRelation->catalogname)
296         {
297                 if (strcmp(newRelation->catalogname, get_database_name(MyDatabaseId)) != 0)
298                         ereport(ERROR,
299                                         (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
300                                          errmsg("cross-database references are not implemented: \"%s.%s.%s\"",
301                                                         newRelation->catalogname, newRelation->schemaname,
302                                                         newRelation->relname)));
303         }
304
305         if (newRelation->istemp)
306         {
307                 /* TEMP tables are created in our backend-local temp namespace */
308                 if (newRelation->schemaname)
309                         ereport(ERROR,
310                                         (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
311                                    errmsg("temporary tables cannot specify a schema name")));
312                 /* Initialize temp namespace if first time through */
313                 if (!OidIsValid(myTempNamespace))
314                         InitTempTableNamespace();
315                 return myTempNamespace;
316         }
317
318         if (newRelation->schemaname)
319         {
320                 /* check for pg_temp alias */
321                 if (strcmp(newRelation->schemaname, "pg_temp") == 0)
322                 {
323                         /* Initialize temp namespace if first time through */
324                         if (!OidIsValid(myTempNamespace))
325                                 InitTempTableNamespace();
326                         return myTempNamespace;
327                 }
328                 /* use exact schema given */
329                 namespaceId = GetSysCacheOid(NAMESPACENAME,
330                                                                          CStringGetDatum(newRelation->schemaname),
331                                                                          0, 0, 0);
332                 if (!OidIsValid(namespaceId))
333                         ereport(ERROR,
334                                         (errcode(ERRCODE_UNDEFINED_SCHEMA),
335                                          errmsg("schema \"%s\" does not exist",
336                                                         newRelation->schemaname)));
337                 /* we do not check for USAGE rights here! */
338         }
339         else
340         {
341                 /* use the default creation namespace */
342                 recomputeNamespacePath();
343                 if (activeTempCreationPending)
344                 {
345                         /* Need to initialize temp namespace */
346                         InitTempTableNamespace();
347                         return myTempNamespace;
348                 }
349                 namespaceId = activeCreationNamespace;
350                 if (!OidIsValid(namespaceId))
351                         ereport(ERROR,
352                                         (errcode(ERRCODE_UNDEFINED_SCHEMA),
353                                          errmsg("no schema has been selected to create in")));
354         }
355
356         /* Note: callers will check for CREATE rights when appropriate */
357
358         return namespaceId;
359 }
360
361 /*
362  * RelnameGetRelid
363  *              Try to resolve an unqualified relation name.
364  *              Returns OID if relation found in search path, else InvalidOid.
365  */
366 Oid
367 RelnameGetRelid(const char *relname)
368 {
369         Oid                     relid;
370         ListCell   *l;
371
372         recomputeNamespacePath();
373
374         foreach(l, activeSearchPath)
375         {
376                 Oid                     namespaceId = lfirst_oid(l);
377
378                 relid = get_relname_relid(relname, namespaceId);
379                 if (OidIsValid(relid))
380                         return relid;
381         }
382
383         /* Not found in path */
384         return InvalidOid;
385 }
386
387
388 /*
389  * RelationIsVisible
390  *              Determine whether a relation (identified by OID) is visible in the
391  *              current search path.  Visible means "would be found by searching
392  *              for the unqualified relation name".
393  */
394 bool
395 RelationIsVisible(Oid relid)
396 {
397         HeapTuple       reltup;
398         Form_pg_class relform;
399         Oid                     relnamespace;
400         bool            visible;
401
402         reltup = SearchSysCache(RELOID,
403                                                         ObjectIdGetDatum(relid),
404                                                         0, 0, 0);
405         if (!HeapTupleIsValid(reltup))
406                 elog(ERROR, "cache lookup failed for relation %u", relid);
407         relform = (Form_pg_class) GETSTRUCT(reltup);
408
409         recomputeNamespacePath();
410
411         /*
412          * Quick check: if it ain't in the path at all, it ain't visible. Items in
413          * the system namespace are surely in the path and so we needn't even do
414          * list_member_oid() for them.
415          */
416         relnamespace = relform->relnamespace;
417         if (relnamespace != PG_CATALOG_NAMESPACE &&
418                 !list_member_oid(activeSearchPath, relnamespace))
419                 visible = false;
420         else
421         {
422                 /*
423                  * If it is in the path, it might still not be visible; it could be
424                  * hidden by another relation of the same name earlier in the path. So
425                  * we must do a slow check for conflicting relations.
426                  */
427                 char       *relname = NameStr(relform->relname);
428                 ListCell   *l;
429
430                 visible = false;
431                 foreach(l, activeSearchPath)
432                 {
433                         Oid                     namespaceId = lfirst_oid(l);
434
435                         if (namespaceId == relnamespace)
436                         {
437                                 /* Found it first in path */
438                                 visible = true;
439                                 break;
440                         }
441                         if (OidIsValid(get_relname_relid(relname, namespaceId)))
442                         {
443                                 /* Found something else first in path */
444                                 break;
445                         }
446                 }
447         }
448
449         ReleaseSysCache(reltup);
450
451         return visible;
452 }
453
454
455 /*
456  * TypenameGetTypid
457  *              Try to resolve an unqualified datatype name.
458  *              Returns OID if type found in search path, else InvalidOid.
459  *
460  * This is essentially the same as RelnameGetRelid.
461  */
462 Oid
463 TypenameGetTypid(const char *typname)
464 {
465         Oid                     typid;
466         ListCell   *l;
467
468         recomputeNamespacePath();
469
470         foreach(l, activeSearchPath)
471         {
472                 Oid                     namespaceId = lfirst_oid(l);
473
474                 typid = GetSysCacheOid(TYPENAMENSP,
475                                                            PointerGetDatum(typname),
476                                                            ObjectIdGetDatum(namespaceId),
477                                                            0, 0);
478                 if (OidIsValid(typid))
479                         return typid;
480         }
481
482         /* Not found in path */
483         return InvalidOid;
484 }
485
486 /*
487  * TypeIsVisible
488  *              Determine whether a type (identified by OID) is visible in the
489  *              current search path.  Visible means "would be found by searching
490  *              for the unqualified type name".
491  */
492 bool
493 TypeIsVisible(Oid typid)
494 {
495         HeapTuple       typtup;
496         Form_pg_type typform;
497         Oid                     typnamespace;
498         bool            visible;
499
500         typtup = SearchSysCache(TYPEOID,
501                                                         ObjectIdGetDatum(typid),
502                                                         0, 0, 0);
503         if (!HeapTupleIsValid(typtup))
504                 elog(ERROR, "cache lookup failed for type %u", typid);
505         typform = (Form_pg_type) GETSTRUCT(typtup);
506
507         recomputeNamespacePath();
508
509         /*
510          * Quick check: if it ain't in the path at all, it ain't visible. Items in
511          * the system namespace are surely in the path and so we needn't even do
512          * list_member_oid() for them.
513          */
514         typnamespace = typform->typnamespace;
515         if (typnamespace != PG_CATALOG_NAMESPACE &&
516                 !list_member_oid(activeSearchPath, typnamespace))
517                 visible = false;
518         else
519         {
520                 /*
521                  * If it is in the path, it might still not be visible; it could be
522                  * hidden by another type of the same name earlier in the path. So we
523                  * must do a slow check for conflicting types.
524                  */
525                 char       *typname = NameStr(typform->typname);
526                 ListCell   *l;
527
528                 visible = false;
529                 foreach(l, activeSearchPath)
530                 {
531                         Oid                     namespaceId = lfirst_oid(l);
532
533                         if (namespaceId == typnamespace)
534                         {
535                                 /* Found it first in path */
536                                 visible = true;
537                                 break;
538                         }
539                         if (SearchSysCacheExists(TYPENAMENSP,
540                                                                          PointerGetDatum(typname),
541                                                                          ObjectIdGetDatum(namespaceId),
542                                                                          0, 0))
543                         {
544                                 /* Found something else first in path */
545                                 break;
546                         }
547                 }
548         }
549
550         ReleaseSysCache(typtup);
551
552         return visible;
553 }
554
555
556 /*
557  * FuncnameGetCandidates
558  *              Given a possibly-qualified function name and argument count,
559  *              retrieve a list of the possible matches.
560  *
561  * If nargs is -1, we return all functions matching the given name,
562  * regardless of argument count.
563  *
564  * We search a single namespace if the function name is qualified, else
565  * all namespaces in the search path.  The return list will never contain
566  * multiple entries with identical argument lists --- in the multiple-
567  * namespace case, we arrange for entries in earlier namespaces to mask
568  * identical entries in later namespaces.
569  */
570 FuncCandidateList
571 FuncnameGetCandidates(List *names, int nargs)
572 {
573         FuncCandidateList resultList = NULL;
574         char       *schemaname;
575         char       *funcname;
576         Oid                     namespaceId;
577         CatCList   *catlist;
578         int                     i;
579
580         /* deconstruct the name list */
581         DeconstructQualifiedName(names, &schemaname, &funcname);
582
583         if (schemaname)
584         {
585                 /* use exact schema given */
586                 namespaceId = LookupExplicitNamespace(schemaname);
587         }
588         else
589         {
590                 /* flag to indicate we need namespace search */
591                 namespaceId = InvalidOid;
592                 recomputeNamespacePath();
593         }
594
595         /* Search syscache by name only */
596         catlist = SearchSysCacheList(PROCNAMEARGSNSP, 1,
597                                                                  CStringGetDatum(funcname),
598                                                                  0, 0, 0);
599
600         for (i = 0; i < catlist->n_members; i++)
601         {
602                 HeapTuple       proctup = &catlist->members[i]->tuple;
603                 Form_pg_proc procform = (Form_pg_proc) GETSTRUCT(proctup);
604                 int                     pronargs = procform->pronargs;
605                 int                     pathpos = 0;
606                 FuncCandidateList newResult;
607
608                 /* Ignore if it doesn't match requested argument count */
609                 if (nargs >= 0 && pronargs != nargs)
610                         continue;
611
612                 if (OidIsValid(namespaceId))
613                 {
614                         /* Consider only procs in specified namespace */
615                         if (procform->pronamespace != namespaceId)
616                                 continue;
617                         /* No need to check args, they must all be different */
618                 }
619                 else
620                 {
621                         /*
622                          * Consider only procs that are in the search path and are not in
623                          * the temp namespace.
624                          */
625                         ListCell   *nsp;
626
627                         foreach(nsp, activeSearchPath)
628                         {
629                                 if (procform->pronamespace == lfirst_oid(nsp) &&
630                                         procform->pronamespace != myTempNamespace)
631                                         break;
632                                 pathpos++;
633                         }
634                         if (nsp == NULL)
635                                 continue;               /* proc is not in search path */
636
637                         /*
638                          * Okay, it's in the search path, but does it have the same
639                          * arguments as something we already accepted?  If so, keep only
640                          * the one that appears earlier in the search path.
641                          *
642                          * If we have an ordered list from SearchSysCacheList (the normal
643                          * case), then any conflicting proc must immediately adjoin this
644                          * one in the list, so we only need to look at the newest result
645                          * item.  If we have an unordered list, we have to scan the whole
646                          * result list.
647                          */
648                         if (resultList)
649                         {
650                                 FuncCandidateList prevResult;
651
652                                 if (catlist->ordered)
653                                 {
654                                         if (pronargs == resultList->nargs &&
655                                                 memcmp(procform->proargtypes.values,
656                                                            resultList->args,
657                                                            pronargs * sizeof(Oid)) == 0)
658                                                 prevResult = resultList;
659                                         else
660                                                 prevResult = NULL;
661                                 }
662                                 else
663                                 {
664                                         for (prevResult = resultList;
665                                                  prevResult;
666                                                  prevResult = prevResult->next)
667                                         {
668                                                 if (pronargs == prevResult->nargs &&
669                                                         memcmp(procform->proargtypes.values,
670                                                                    prevResult->args,
671                                                                    pronargs * sizeof(Oid)) == 0)
672                                                         break;
673                                         }
674                                 }
675                                 if (prevResult)
676                                 {
677                                         /* We have a match with a previous result */
678                                         Assert(pathpos != prevResult->pathpos);
679                                         if (pathpos > prevResult->pathpos)
680                                                 continue;               /* keep previous result */
681                                         /* replace previous result */
682                                         prevResult->pathpos = pathpos;
683                                         prevResult->oid = HeapTupleGetOid(proctup);
684                                         continue;       /* args are same, of course */
685                                 }
686                         }
687                 }
688
689                 /*
690                  * Okay to add it to result list
691                  */
692                 newResult = (FuncCandidateList)
693                         palloc(sizeof(struct _FuncCandidateList) - sizeof(Oid)
694                                    + pronargs * sizeof(Oid));
695                 newResult->pathpos = pathpos;
696                 newResult->oid = HeapTupleGetOid(proctup);
697                 newResult->nargs = pronargs;
698                 memcpy(newResult->args, procform->proargtypes.values,
699                            pronargs * sizeof(Oid));
700
701                 newResult->next = resultList;
702                 resultList = newResult;
703         }
704
705         ReleaseSysCacheList(catlist);
706
707         return resultList;
708 }
709
710 /*
711  * FunctionIsVisible
712  *              Determine whether a function (identified by OID) is visible in the
713  *              current search path.  Visible means "would be found by searching
714  *              for the unqualified function name with exact argument matches".
715  */
716 bool
717 FunctionIsVisible(Oid funcid)
718 {
719         HeapTuple       proctup;
720         Form_pg_proc procform;
721         Oid                     pronamespace;
722         bool            visible;
723
724         proctup = SearchSysCache(PROCOID,
725                                                          ObjectIdGetDatum(funcid),
726                                                          0, 0, 0);
727         if (!HeapTupleIsValid(proctup))
728                 elog(ERROR, "cache lookup failed for function %u", funcid);
729         procform = (Form_pg_proc) GETSTRUCT(proctup);
730
731         recomputeNamespacePath();
732
733         /*
734          * Quick check: if it ain't in the path at all, it ain't visible. Items in
735          * the system namespace are surely in the path and so we needn't even do
736          * list_member_oid() for them.
737          */
738         pronamespace = procform->pronamespace;
739         if (pronamespace != PG_CATALOG_NAMESPACE &&
740                 !list_member_oid(activeSearchPath, pronamespace))
741                 visible = false;
742         else
743         {
744                 /*
745                  * If it is in the path, it might still not be visible; it could be
746                  * hidden by another proc of the same name and arguments earlier in
747                  * the path.  So we must do a slow check to see if this is the same
748                  * proc that would be found by FuncnameGetCandidates.
749                  */
750                 char       *proname = NameStr(procform->proname);
751                 int                     nargs = procform->pronargs;
752                 FuncCandidateList clist;
753
754                 visible = false;
755
756                 clist = FuncnameGetCandidates(list_make1(makeString(proname)), nargs);
757
758                 for (; clist; clist = clist->next)
759                 {
760                         if (memcmp(clist->args, procform->proargtypes.values,
761                                            nargs * sizeof(Oid)) == 0)
762                         {
763                                 /* Found the expected entry; is it the right proc? */
764                                 visible = (clist->oid == funcid);
765                                 break;
766                         }
767                 }
768         }
769
770         ReleaseSysCache(proctup);
771
772         return visible;
773 }
774
775
776 /*
777  * OpernameGetOprid
778  *              Given a possibly-qualified operator name and exact input datatypes,
779  *              look up the operator.  Returns InvalidOid if not found.
780  *
781  * Pass oprleft = InvalidOid for a prefix op, oprright = InvalidOid for
782  * a postfix op.
783  *
784  * If the operator name is not schema-qualified, it is sought in the current
785  * namespace search path.
786  */
787 Oid
788 OpernameGetOprid(List *names, Oid oprleft, Oid oprright)
789 {
790         char       *schemaname;
791         char       *opername;
792         CatCList   *catlist;
793         ListCell   *l;
794
795         /* deconstruct the name list */
796         DeconstructQualifiedName(names, &schemaname, &opername);
797
798         if (schemaname)
799         {
800                 /* search only in exact schema given */
801                 Oid                     namespaceId;
802                 HeapTuple       opertup;
803
804                 namespaceId = LookupExplicitNamespace(schemaname);
805                 opertup = SearchSysCache(OPERNAMENSP,
806                                                                  CStringGetDatum(opername),
807                                                                  ObjectIdGetDatum(oprleft),
808                                                                  ObjectIdGetDatum(oprright),
809                                                                  ObjectIdGetDatum(namespaceId));
810                 if (HeapTupleIsValid(opertup))
811                 {
812                         Oid                     result = HeapTupleGetOid(opertup);
813
814                         ReleaseSysCache(opertup);
815                         return result;
816                 }
817                 return InvalidOid;
818         }
819
820         /* Search syscache by name and argument types */
821         catlist = SearchSysCacheList(OPERNAMENSP, 3,
822                                                                  CStringGetDatum(opername),
823                                                                  ObjectIdGetDatum(oprleft),
824                                                                  ObjectIdGetDatum(oprright),
825                                                                  0);
826
827         if (catlist->n_members == 0)
828         {
829                 /* no hope, fall out early */
830                 ReleaseSysCacheList(catlist);
831                 return InvalidOid;
832         }
833
834         /*
835          * We have to find the list member that is first in the search path, if
836          * there's more than one.  This doubly-nested loop looks ugly, but in
837          * practice there should usually be few catlist members.
838          */
839         recomputeNamespacePath();
840
841         foreach(l, activeSearchPath)
842         {
843                 Oid                     namespaceId = lfirst_oid(l);
844                 int                     i;
845
846                 if (namespaceId == myTempNamespace)
847                         continue;                       /* do not look in temp namespace */
848
849                 for (i = 0; i < catlist->n_members; i++)
850                 {
851                         HeapTuple       opertup = &catlist->members[i]->tuple;
852                         Form_pg_operator operform = (Form_pg_operator) GETSTRUCT(opertup);
853
854                         if (operform->oprnamespace == namespaceId)
855                         {
856                                 Oid                     result = HeapTupleGetOid(opertup);
857
858                                 ReleaseSysCacheList(catlist);
859                                 return result;
860                         }
861                 }
862         }
863
864         ReleaseSysCacheList(catlist);
865         return InvalidOid;
866 }
867
868 /*
869  * OpernameGetCandidates
870  *              Given a possibly-qualified operator name and operator kind,
871  *              retrieve a list of the possible matches.
872  *
873  * If oprkind is '\0', we return all operators matching the given name,
874  * regardless of arguments.
875  *
876  * We search a single namespace if the operator name is qualified, else
877  * all namespaces in the search path.  The return list will never contain
878  * multiple entries with identical argument lists --- in the multiple-
879  * namespace case, we arrange for entries in earlier namespaces to mask
880  * identical entries in later namespaces.
881  *
882  * The returned items always have two args[] entries --- one or the other
883  * will be InvalidOid for a prefix or postfix oprkind.  nargs is 2, too.
884  */
885 FuncCandidateList
886 OpernameGetCandidates(List *names, char oprkind)
887 {
888         FuncCandidateList resultList = NULL;
889         char       *resultSpace = NULL;
890         int                     nextResult = 0;
891         char       *schemaname;
892         char       *opername;
893         Oid                     namespaceId;
894         CatCList   *catlist;
895         int                     i;
896
897         /* deconstruct the name list */
898         DeconstructQualifiedName(names, &schemaname, &opername);
899
900         if (schemaname)
901         {
902                 /* use exact schema given */
903                 namespaceId = LookupExplicitNamespace(schemaname);
904         }
905         else
906         {
907                 /* flag to indicate we need namespace search */
908                 namespaceId = InvalidOid;
909                 recomputeNamespacePath();
910         }
911
912         /* Search syscache by name only */
913         catlist = SearchSysCacheList(OPERNAMENSP, 1,
914                                                                  CStringGetDatum(opername),
915                                                                  0, 0, 0);
916
917         /*
918          * In typical scenarios, most if not all of the operators found by the
919          * catcache search will end up getting returned; and there can be quite a
920          * few, for common operator names such as '=' or '+'.  To reduce the time
921          * spent in palloc, we allocate the result space as an array large enough
922          * to hold all the operators.  The original coding of this routine did a
923          * separate palloc for each operator, but profiling revealed that the
924          * pallocs used an unreasonably large fraction of parsing time.
925          */
926 #define SPACE_PER_OP MAXALIGN(sizeof(struct _FuncCandidateList) + sizeof(Oid))
927
928         if (catlist->n_members > 0)
929                 resultSpace = palloc(catlist->n_members * SPACE_PER_OP);
930
931         for (i = 0; i < catlist->n_members; i++)
932         {
933                 HeapTuple       opertup = &catlist->members[i]->tuple;
934                 Form_pg_operator operform = (Form_pg_operator) GETSTRUCT(opertup);
935                 int                     pathpos = 0;
936                 FuncCandidateList newResult;
937
938                 /* Ignore operators of wrong kind, if specific kind requested */
939                 if (oprkind && operform->oprkind != oprkind)
940                         continue;
941
942                 if (OidIsValid(namespaceId))
943                 {
944                         /* Consider only opers in specified namespace */
945                         if (operform->oprnamespace != namespaceId)
946                                 continue;
947                         /* No need to check args, they must all be different */
948                 }
949                 else
950                 {
951                         /*
952                          * Consider only opers that are in the search path and are not in
953                          * the temp namespace.
954                          */
955                         ListCell   *nsp;
956
957                         foreach(nsp, activeSearchPath)
958                         {
959                                 if (operform->oprnamespace == lfirst_oid(nsp) &&
960                                         operform->oprnamespace != myTempNamespace)
961                                         break;
962                                 pathpos++;
963                         }
964                         if (nsp == NULL)
965                                 continue;               /* oper is not in search path */
966
967                         /*
968                          * Okay, it's in the search path, but does it have the same
969                          * arguments as something we already accepted?  If so, keep only
970                          * the one that appears earlier in the search path.
971                          *
972                          * If we have an ordered list from SearchSysCacheList (the normal
973                          * case), then any conflicting oper must immediately adjoin this
974                          * one in the list, so we only need to look at the newest result
975                          * item.  If we have an unordered list, we have to scan the whole
976                          * result list.
977                          */
978                         if (resultList)
979                         {
980                                 FuncCandidateList prevResult;
981
982                                 if (catlist->ordered)
983                                 {
984                                         if (operform->oprleft == resultList->args[0] &&
985                                                 operform->oprright == resultList->args[1])
986                                                 prevResult = resultList;
987                                         else
988                                                 prevResult = NULL;
989                                 }
990                                 else
991                                 {
992                                         for (prevResult = resultList;
993                                                  prevResult;
994                                                  prevResult = prevResult->next)
995                                         {
996                                                 if (operform->oprleft == prevResult->args[0] &&
997                                                         operform->oprright == prevResult->args[1])
998                                                         break;
999                                         }
1000                                 }
1001                                 if (prevResult)
1002                                 {
1003                                         /* We have a match with a previous result */
1004                                         Assert(pathpos != prevResult->pathpos);
1005                                         if (pathpos > prevResult->pathpos)
1006                                                 continue;               /* keep previous result */
1007                                         /* replace previous result */
1008                                         prevResult->pathpos = pathpos;
1009                                         prevResult->oid = HeapTupleGetOid(opertup);
1010                                         continue;       /* args are same, of course */
1011                                 }
1012                         }
1013                 }
1014
1015                 /*
1016                  * Okay to add it to result list
1017                  */
1018                 newResult = (FuncCandidateList) (resultSpace + nextResult);
1019                 nextResult += SPACE_PER_OP;
1020
1021                 newResult->pathpos = pathpos;
1022                 newResult->oid = HeapTupleGetOid(opertup);
1023                 newResult->nargs = 2;
1024                 newResult->args[0] = operform->oprleft;
1025                 newResult->args[1] = operform->oprright;
1026                 newResult->next = resultList;
1027                 resultList = newResult;
1028         }
1029
1030         ReleaseSysCacheList(catlist);
1031
1032         return resultList;
1033 }
1034
1035 /*
1036  * OperatorIsVisible
1037  *              Determine whether an operator (identified by OID) is visible in the
1038  *              current search path.  Visible means "would be found by searching
1039  *              for the unqualified operator name with exact argument matches".
1040  */
1041 bool
1042 OperatorIsVisible(Oid oprid)
1043 {
1044         HeapTuple       oprtup;
1045         Form_pg_operator oprform;
1046         Oid                     oprnamespace;
1047         bool            visible;
1048
1049         oprtup = SearchSysCache(OPEROID,
1050                                                         ObjectIdGetDatum(oprid),
1051                                                         0, 0, 0);
1052         if (!HeapTupleIsValid(oprtup))
1053                 elog(ERROR, "cache lookup failed for operator %u", oprid);
1054         oprform = (Form_pg_operator) GETSTRUCT(oprtup);
1055
1056         recomputeNamespacePath();
1057
1058         /*
1059          * Quick check: if it ain't in the path at all, it ain't visible. Items in
1060          * the system namespace are surely in the path and so we needn't even do
1061          * list_member_oid() for them.
1062          */
1063         oprnamespace = oprform->oprnamespace;
1064         if (oprnamespace != PG_CATALOG_NAMESPACE &&
1065                 !list_member_oid(activeSearchPath, oprnamespace))
1066                 visible = false;
1067         else
1068         {
1069                 /*
1070                  * If it is in the path, it might still not be visible; it could be
1071                  * hidden by another operator of the same name and arguments earlier
1072                  * in the path.  So we must do a slow check to see if this is the same
1073                  * operator that would be found by OpernameGetOprId.
1074                  */
1075                 char       *oprname = NameStr(oprform->oprname);
1076
1077                 visible = (OpernameGetOprid(list_make1(makeString(oprname)),
1078                                                                         oprform->oprleft, oprform->oprright)
1079                                    == oprid);
1080         }
1081
1082         ReleaseSysCache(oprtup);
1083
1084         return visible;
1085 }
1086
1087
1088 /*
1089  * OpclassnameGetOpcid
1090  *              Try to resolve an unqualified index opclass name.
1091  *              Returns OID if opclass found in search path, else InvalidOid.
1092  *
1093  * This is essentially the same as TypenameGetTypid, but we have to have
1094  * an extra argument for the index AM OID.
1095  */
1096 Oid
1097 OpclassnameGetOpcid(Oid amid, const char *opcname)
1098 {
1099         Oid                     opcid;
1100         ListCell   *l;
1101
1102         recomputeNamespacePath();
1103
1104         foreach(l, activeSearchPath)
1105         {
1106                 Oid                     namespaceId = lfirst_oid(l);
1107
1108                 if (namespaceId == myTempNamespace)
1109                         continue;                       /* do not look in temp namespace */
1110
1111                 opcid = GetSysCacheOid(CLAAMNAMENSP,
1112                                                            ObjectIdGetDatum(amid),
1113                                                            PointerGetDatum(opcname),
1114                                                            ObjectIdGetDatum(namespaceId),
1115                                                            0);
1116                 if (OidIsValid(opcid))
1117                         return opcid;
1118         }
1119
1120         /* Not found in path */
1121         return InvalidOid;
1122 }
1123
1124 /*
1125  * OpclassIsVisible
1126  *              Determine whether an opclass (identified by OID) is visible in the
1127  *              current search path.  Visible means "would be found by searching
1128  *              for the unqualified opclass name".
1129  */
1130 bool
1131 OpclassIsVisible(Oid opcid)
1132 {
1133         HeapTuple       opctup;
1134         Form_pg_opclass opcform;
1135         Oid                     opcnamespace;
1136         bool            visible;
1137
1138         opctup = SearchSysCache(CLAOID,
1139                                                         ObjectIdGetDatum(opcid),
1140                                                         0, 0, 0);
1141         if (!HeapTupleIsValid(opctup))
1142                 elog(ERROR, "cache lookup failed for opclass %u", opcid);
1143         opcform = (Form_pg_opclass) GETSTRUCT(opctup);
1144
1145         recomputeNamespacePath();
1146
1147         /*
1148          * Quick check: if it ain't in the path at all, it ain't visible. Items in
1149          * the system namespace are surely in the path and so we needn't even do
1150          * list_member_oid() for them.
1151          */
1152         opcnamespace = opcform->opcnamespace;
1153         if (opcnamespace != PG_CATALOG_NAMESPACE &&
1154                 !list_member_oid(activeSearchPath, opcnamespace))
1155                 visible = false;
1156         else
1157         {
1158                 /*
1159                  * If it is in the path, it might still not be visible; it could be
1160                  * hidden by another opclass of the same name earlier in the path. So
1161                  * we must do a slow check to see if this opclass would be found by
1162                  * OpclassnameGetOpcid.
1163                  */
1164                 char       *opcname = NameStr(opcform->opcname);
1165
1166                 visible = (OpclassnameGetOpcid(opcform->opcmethod, opcname) == opcid);
1167         }
1168
1169         ReleaseSysCache(opctup);
1170
1171         return visible;
1172 }
1173
1174 /*
1175  * OpfamilynameGetOpfid
1176  *              Try to resolve an unqualified index opfamily name.
1177  *              Returns OID if opfamily found in search path, else InvalidOid.
1178  *
1179  * This is essentially the same as TypenameGetTypid, but we have to have
1180  * an extra argument for the index AM OID.
1181  */
1182 Oid
1183 OpfamilynameGetOpfid(Oid amid, const char *opfname)
1184 {
1185         Oid                     opfid;
1186         ListCell   *l;
1187
1188         recomputeNamespacePath();
1189
1190         foreach(l, activeSearchPath)
1191         {
1192                 Oid                     namespaceId = lfirst_oid(l);
1193
1194                 if (namespaceId == myTempNamespace)
1195                         continue;                       /* do not look in temp namespace */
1196
1197                 opfid = GetSysCacheOid(OPFAMILYAMNAMENSP,
1198                                                            ObjectIdGetDatum(amid),
1199                                                            PointerGetDatum(opfname),
1200                                                            ObjectIdGetDatum(namespaceId),
1201                                                            0);
1202                 if (OidIsValid(opfid))
1203                         return opfid;
1204         }
1205
1206         /* Not found in path */
1207         return InvalidOid;
1208 }
1209
1210 /*
1211  * OpfamilyIsVisible
1212  *              Determine whether an opfamily (identified by OID) is visible in the
1213  *              current search path.  Visible means "would be found by searching
1214  *              for the unqualified opfamily name".
1215  */
1216 bool
1217 OpfamilyIsVisible(Oid opfid)
1218 {
1219         HeapTuple       opftup;
1220         Form_pg_opfamily opfform;
1221         Oid                     opfnamespace;
1222         bool            visible;
1223
1224         opftup = SearchSysCache(OPFAMILYOID,
1225                                                         ObjectIdGetDatum(opfid),
1226                                                         0, 0, 0);
1227         if (!HeapTupleIsValid(opftup))
1228                 elog(ERROR, "cache lookup failed for opfamily %u", opfid);
1229         opfform = (Form_pg_opfamily) GETSTRUCT(opftup);
1230
1231         recomputeNamespacePath();
1232
1233         /*
1234          * Quick check: if it ain't in the path at all, it ain't visible. Items in
1235          * the system namespace are surely in the path and so we needn't even do
1236          * list_member_oid() for them.
1237          */
1238         opfnamespace = opfform->opfnamespace;
1239         if (opfnamespace != PG_CATALOG_NAMESPACE &&
1240                 !list_member_oid(activeSearchPath, opfnamespace))
1241                 visible = false;
1242         else
1243         {
1244                 /*
1245                  * If it is in the path, it might still not be visible; it could be
1246                  * hidden by another opfamily of the same name earlier in the path. So
1247                  * we must do a slow check to see if this opfamily would be found by
1248                  * OpfamilynameGetOpfid.
1249                  */
1250                 char       *opfname = NameStr(opfform->opfname);
1251
1252                 visible = (OpfamilynameGetOpfid(opfform->opfmethod, opfname) == opfid);
1253         }
1254
1255         ReleaseSysCache(opftup);
1256
1257         return visible;
1258 }
1259
1260 /*
1261  * ConversionGetConid
1262  *              Try to resolve an unqualified conversion name.
1263  *              Returns OID if conversion found in search path, else InvalidOid.
1264  *
1265  * This is essentially the same as RelnameGetRelid.
1266  */
1267 Oid
1268 ConversionGetConid(const char *conname)
1269 {
1270         Oid                     conid;
1271         ListCell   *l;
1272
1273         recomputeNamespacePath();
1274
1275         foreach(l, activeSearchPath)
1276         {
1277                 Oid                     namespaceId = lfirst_oid(l);
1278
1279                 if (namespaceId == myTempNamespace)
1280                         continue;                       /* do not look in temp namespace */
1281
1282                 conid = GetSysCacheOid(CONNAMENSP,
1283                                                            PointerGetDatum(conname),
1284                                                            ObjectIdGetDatum(namespaceId),
1285                                                            0, 0);
1286                 if (OidIsValid(conid))
1287                         return conid;
1288         }
1289
1290         /* Not found in path */
1291         return InvalidOid;
1292 }
1293
1294 /*
1295  * ConversionIsVisible
1296  *              Determine whether a conversion (identified by OID) is visible in the
1297  *              current search path.  Visible means "would be found by searching
1298  *              for the unqualified conversion name".
1299  */
1300 bool
1301 ConversionIsVisible(Oid conid)
1302 {
1303         HeapTuple       contup;
1304         Form_pg_conversion conform;
1305         Oid                     connamespace;
1306         bool            visible;
1307
1308         contup = SearchSysCache(CONVOID,
1309                                                         ObjectIdGetDatum(conid),
1310                                                         0, 0, 0);
1311         if (!HeapTupleIsValid(contup))
1312                 elog(ERROR, "cache lookup failed for conversion %u", conid);
1313         conform = (Form_pg_conversion) GETSTRUCT(contup);
1314
1315         recomputeNamespacePath();
1316
1317         /*
1318          * Quick check: if it ain't in the path at all, it ain't visible. Items in
1319          * the system namespace are surely in the path and so we needn't even do
1320          * list_member_oid() for them.
1321          */
1322         connamespace = conform->connamespace;
1323         if (connamespace != PG_CATALOG_NAMESPACE &&
1324                 !list_member_oid(activeSearchPath, connamespace))
1325                 visible = false;
1326         else
1327         {
1328                 /*
1329                  * If it is in the path, it might still not be visible; it could be
1330                  * hidden by another conversion of the same name earlier in the path.
1331                  * So we must do a slow check to see if this conversion would be found
1332                  * by ConversionGetConid.
1333                  */
1334                 char       *conname = NameStr(conform->conname);
1335
1336                 visible = (ConversionGetConid(conname) == conid);
1337         }
1338
1339         ReleaseSysCache(contup);
1340
1341         return visible;
1342 }
1343
1344 /*
1345  * TSParserGetPrsid - find a TS parser by possibly qualified name
1346  *
1347  * If not found, returns InvalidOid if failOK, else throws error
1348  */
1349 Oid
1350 TSParserGetPrsid(List *names, bool failOK)
1351 {
1352         char       *schemaname;
1353         char       *parser_name;
1354         Oid                     namespaceId;
1355         Oid                     prsoid = InvalidOid;
1356         ListCell   *l;
1357
1358         /* deconstruct the name list */
1359         DeconstructQualifiedName(names, &schemaname, &parser_name);
1360
1361         if (schemaname)
1362         {
1363                 /* use exact schema given */
1364                 namespaceId = LookupExplicitNamespace(schemaname);
1365                 prsoid = GetSysCacheOid(TSPARSERNAMENSP,
1366                                                                 PointerGetDatum(parser_name),
1367                                                                 ObjectIdGetDatum(namespaceId),
1368                                                                 0, 0);
1369         }
1370         else
1371         {
1372                 /* search for it in search path */
1373                 recomputeNamespacePath();
1374
1375                 foreach(l, activeSearchPath)
1376                 {
1377                         namespaceId = lfirst_oid(l);
1378
1379                         if (namespaceId == myTempNamespace)
1380                                 continue;               /* do not look in temp namespace */
1381
1382                         prsoid = GetSysCacheOid(TSPARSERNAMENSP,
1383                                                                         PointerGetDatum(parser_name),
1384                                                                         ObjectIdGetDatum(namespaceId),
1385                                                                         0, 0);
1386                         if (OidIsValid(prsoid))
1387                                 break;
1388                 }
1389         }
1390
1391         if (!OidIsValid(prsoid) && !failOK)
1392                 ereport(ERROR,
1393                                 (errcode(ERRCODE_UNDEFINED_OBJECT),
1394                                  errmsg("text search parser \"%s\" does not exist",
1395                                                 NameListToString(names))));
1396
1397         return prsoid;
1398 }
1399
1400 /*
1401  * TSParserIsVisible
1402  *              Determine whether a parser (identified by OID) is visible in the
1403  *              current search path.  Visible means "would be found by searching
1404  *              for the unqualified parser name".
1405  */
1406 bool
1407 TSParserIsVisible(Oid prsId)
1408 {
1409         HeapTuple       tup;
1410         Form_pg_ts_parser form;
1411         Oid                     namespace;
1412         bool            visible;
1413
1414         tup = SearchSysCache(TSPARSEROID,
1415                                                  ObjectIdGetDatum(prsId),
1416                                                  0, 0, 0);
1417         if (!HeapTupleIsValid(tup))
1418                 elog(ERROR, "cache lookup failed for text search parser %u", prsId);
1419         form = (Form_pg_ts_parser) GETSTRUCT(tup);
1420
1421         recomputeNamespacePath();
1422
1423         /*
1424          * Quick check: if it ain't in the path at all, it ain't visible. Items in
1425          * the system namespace are surely in the path and so we needn't even do
1426          * list_member_oid() for them.
1427          */
1428         namespace = form->prsnamespace;
1429         if (namespace != PG_CATALOG_NAMESPACE &&
1430                 !list_member_oid(activeSearchPath, namespace))
1431                 visible = false;
1432         else
1433         {
1434                 /*
1435                  * If it is in the path, it might still not be visible; it could be
1436                  * hidden by another parser of the same name earlier in the path. So
1437                  * we must do a slow check for conflicting parsers.
1438                  */
1439                 char       *name = NameStr(form->prsname);
1440                 ListCell   *l;
1441
1442                 visible = false;
1443                 foreach(l, activeSearchPath)
1444                 {
1445                         Oid                     namespaceId = lfirst_oid(l);
1446
1447                         if (namespaceId == myTempNamespace)
1448                                 continue;               /* do not look in temp namespace */
1449
1450                         if (namespaceId == namespace)
1451                         {
1452                                 /* Found it first in path */
1453                                 visible = true;
1454                                 break;
1455                         }
1456                         if (SearchSysCacheExists(TSPARSERNAMENSP,
1457                                                                          PointerGetDatum(name),
1458                                                                          ObjectIdGetDatum(namespaceId),
1459                                                                          0, 0))
1460                         {
1461                                 /* Found something else first in path */
1462                                 break;
1463                         }
1464                 }
1465         }
1466
1467         ReleaseSysCache(tup);
1468
1469         return visible;
1470 }
1471
1472 /*
1473  * TSDictionaryGetDictid - find a TS dictionary by possibly qualified name
1474  *
1475  * If not found, returns InvalidOid if failOK, else throws error
1476  */
1477 Oid
1478 TSDictionaryGetDictid(List *names, bool failOK)
1479 {
1480         char       *schemaname;
1481         char       *dict_name;
1482         Oid                     namespaceId;
1483         Oid                     dictoid = InvalidOid;
1484         ListCell   *l;
1485
1486         /* deconstruct the name list */
1487         DeconstructQualifiedName(names, &schemaname, &dict_name);
1488
1489         if (schemaname)
1490         {
1491                 /* use exact schema given */
1492                 namespaceId = LookupExplicitNamespace(schemaname);
1493                 dictoid = GetSysCacheOid(TSDICTNAMENSP,
1494                                                                  PointerGetDatum(dict_name),
1495                                                                  ObjectIdGetDatum(namespaceId),
1496                                                                  0, 0);
1497         }
1498         else
1499         {
1500                 /* search for it in search path */
1501                 recomputeNamespacePath();
1502
1503                 foreach(l, activeSearchPath)
1504                 {
1505                         namespaceId = lfirst_oid(l);
1506
1507                         if (namespaceId == myTempNamespace)
1508                                 continue;               /* do not look in temp namespace */
1509
1510                         dictoid = GetSysCacheOid(TSDICTNAMENSP,
1511                                                                          PointerGetDatum(dict_name),
1512                                                                          ObjectIdGetDatum(namespaceId),
1513                                                                          0, 0);
1514                         if (OidIsValid(dictoid))
1515                                 break;
1516                 }
1517         }
1518
1519         if (!OidIsValid(dictoid) && !failOK)
1520                 ereport(ERROR,
1521                                 (errcode(ERRCODE_UNDEFINED_OBJECT),
1522                                  errmsg("text search dictionary \"%s\" does not exist",
1523                                                 NameListToString(names))));
1524
1525         return dictoid;
1526 }
1527
1528 /*
1529  * TSDictionaryIsVisible
1530  *              Determine whether a dictionary (identified by OID) is visible in the
1531  *              current search path.  Visible means "would be found by searching
1532  *              for the unqualified dictionary name".
1533  */
1534 bool
1535 TSDictionaryIsVisible(Oid dictId)
1536 {
1537         HeapTuple       tup;
1538         Form_pg_ts_dict form;
1539         Oid                     namespace;
1540         bool            visible;
1541
1542         tup = SearchSysCache(TSDICTOID,
1543                                                  ObjectIdGetDatum(dictId),
1544                                                  0, 0, 0);
1545         if (!HeapTupleIsValid(tup))
1546                 elog(ERROR, "cache lookup failed for text search dictionary %u",
1547                          dictId);
1548         form = (Form_pg_ts_dict) GETSTRUCT(tup);
1549
1550         recomputeNamespacePath();
1551
1552         /*
1553          * Quick check: if it ain't in the path at all, it ain't visible. Items in
1554          * the system namespace are surely in the path and so we needn't even do
1555          * list_member_oid() for them.
1556          */
1557         namespace = form->dictnamespace;
1558         if (namespace != PG_CATALOG_NAMESPACE &&
1559                 !list_member_oid(activeSearchPath, namespace))
1560                 visible = false;
1561         else
1562         {
1563                 /*
1564                  * If it is in the path, it might still not be visible; it could be
1565                  * hidden by another dictionary of the same name earlier in the path.
1566                  * So we must do a slow check for conflicting dictionaries.
1567                  */
1568                 char       *name = NameStr(form->dictname);
1569                 ListCell   *l;
1570
1571                 visible = false;
1572                 foreach(l, activeSearchPath)
1573                 {
1574                         Oid                     namespaceId = lfirst_oid(l);
1575
1576                         if (namespaceId == myTempNamespace)
1577                                 continue;               /* do not look in temp namespace */
1578
1579                         if (namespaceId == namespace)
1580                         {
1581                                 /* Found it first in path */
1582                                 visible = true;
1583                                 break;
1584                         }
1585                         if (SearchSysCacheExists(TSDICTNAMENSP,
1586                                                                          PointerGetDatum(name),
1587                                                                          ObjectIdGetDatum(namespaceId),
1588                                                                          0, 0))
1589                         {
1590                                 /* Found something else first in path */
1591                                 break;
1592                         }
1593                 }
1594         }
1595
1596         ReleaseSysCache(tup);
1597
1598         return visible;
1599 }
1600
1601 /*
1602  * TSTemplateGetTmplid - find a TS template by possibly qualified name
1603  *
1604  * If not found, returns InvalidOid if failOK, else throws error
1605  */
1606 Oid
1607 TSTemplateGetTmplid(List *names, bool failOK)
1608 {
1609         char       *schemaname;
1610         char       *template_name;
1611         Oid                     namespaceId;
1612         Oid                     tmploid = InvalidOid;
1613         ListCell   *l;
1614
1615         /* deconstruct the name list */
1616         DeconstructQualifiedName(names, &schemaname, &template_name);
1617
1618         if (schemaname)
1619         {
1620                 /* use exact schema given */
1621                 namespaceId = LookupExplicitNamespace(schemaname);
1622                 tmploid = GetSysCacheOid(TSTEMPLATENAMENSP,
1623                                                                  PointerGetDatum(template_name),
1624                                                                  ObjectIdGetDatum(namespaceId),
1625                                                                  0, 0);
1626         }
1627         else
1628         {
1629                 /* search for it in search path */
1630                 recomputeNamespacePath();
1631
1632                 foreach(l, activeSearchPath)
1633                 {
1634                         namespaceId = lfirst_oid(l);
1635
1636                         if (namespaceId == myTempNamespace)
1637                                 continue;               /* do not look in temp namespace */
1638
1639                         tmploid = GetSysCacheOid(TSTEMPLATENAMENSP,
1640                                                                          PointerGetDatum(template_name),
1641                                                                          ObjectIdGetDatum(namespaceId),
1642                                                                          0, 0);
1643                         if (OidIsValid(tmploid))
1644                                 break;
1645                 }
1646         }
1647
1648         if (!OidIsValid(tmploid) && !failOK)
1649                 ereport(ERROR,
1650                                 (errcode(ERRCODE_UNDEFINED_OBJECT),
1651                                  errmsg("text search template \"%s\" does not exist",
1652                                                 NameListToString(names))));
1653
1654         return tmploid;
1655 }
1656
1657 /*
1658  * TSTemplateIsVisible
1659  *              Determine whether a template (identified by OID) is visible in the
1660  *              current search path.  Visible means "would be found by searching
1661  *              for the unqualified template name".
1662  */
1663 bool
1664 TSTemplateIsVisible(Oid tmplId)
1665 {
1666         HeapTuple       tup;
1667         Form_pg_ts_template form;
1668         Oid                     namespace;
1669         bool            visible;
1670
1671         tup = SearchSysCache(TSTEMPLATEOID,
1672                                                  ObjectIdGetDatum(tmplId),
1673                                                  0, 0, 0);
1674         if (!HeapTupleIsValid(tup))
1675                 elog(ERROR, "cache lookup failed for text search template %u", tmplId);
1676         form = (Form_pg_ts_template) GETSTRUCT(tup);
1677
1678         recomputeNamespacePath();
1679
1680         /*
1681          * Quick check: if it ain't in the path at all, it ain't visible. Items in
1682          * the system namespace are surely in the path and so we needn't even do
1683          * list_member_oid() for them.
1684          */
1685         namespace = form->tmplnamespace;
1686         if (namespace != PG_CATALOG_NAMESPACE &&
1687                 !list_member_oid(activeSearchPath, namespace))
1688                 visible = false;
1689         else
1690         {
1691                 /*
1692                  * If it is in the path, it might still not be visible; it could be
1693                  * hidden by another template of the same name earlier in the path. So
1694                  * we must do a slow check for conflicting templates.
1695                  */
1696                 char       *name = NameStr(form->tmplname);
1697                 ListCell   *l;
1698
1699                 visible = false;
1700                 foreach(l, activeSearchPath)
1701                 {
1702                         Oid                     namespaceId = lfirst_oid(l);
1703
1704                         if (namespaceId == myTempNamespace)
1705                                 continue;               /* do not look in temp namespace */
1706
1707                         if (namespaceId == namespace)
1708                         {
1709                                 /* Found it first in path */
1710                                 visible = true;
1711                                 break;
1712                         }
1713                         if (SearchSysCacheExists(TSTEMPLATENAMENSP,
1714                                                                          PointerGetDatum(name),
1715                                                                          ObjectIdGetDatum(namespaceId),
1716                                                                          0, 0))
1717                         {
1718                                 /* Found something else first in path */
1719                                 break;
1720                         }
1721                 }
1722         }
1723
1724         ReleaseSysCache(tup);
1725
1726         return visible;
1727 }
1728
1729 /*
1730  * TSConfigGetCfgid - find a TS config by possibly qualified name
1731  *
1732  * If not found, returns InvalidOid if failOK, else throws error
1733  */
1734 Oid
1735 TSConfigGetCfgid(List *names, bool failOK)
1736 {
1737         char       *schemaname;
1738         char       *config_name;
1739         Oid                     namespaceId;
1740         Oid                     cfgoid = InvalidOid;
1741         ListCell   *l;
1742
1743         /* deconstruct the name list */
1744         DeconstructQualifiedName(names, &schemaname, &config_name);
1745
1746         if (schemaname)
1747         {
1748                 /* use exact schema given */
1749                 namespaceId = LookupExplicitNamespace(schemaname);
1750                 cfgoid = GetSysCacheOid(TSCONFIGNAMENSP,
1751                                                                 PointerGetDatum(config_name),
1752                                                                 ObjectIdGetDatum(namespaceId),
1753                                                                 0, 0);
1754         }
1755         else
1756         {
1757                 /* search for it in search path */
1758                 recomputeNamespacePath();
1759
1760                 foreach(l, activeSearchPath)
1761                 {
1762                         namespaceId = lfirst_oid(l);
1763
1764                         if (namespaceId == myTempNamespace)
1765                                 continue;               /* do not look in temp namespace */
1766
1767                         cfgoid = GetSysCacheOid(TSCONFIGNAMENSP,
1768                                                                         PointerGetDatum(config_name),
1769                                                                         ObjectIdGetDatum(namespaceId),
1770                                                                         0, 0);
1771                         if (OidIsValid(cfgoid))
1772                                 break;
1773                 }
1774         }
1775
1776         if (!OidIsValid(cfgoid) && !failOK)
1777                 ereport(ERROR,
1778                                 (errcode(ERRCODE_UNDEFINED_OBJECT),
1779                                  errmsg("text search configuration \"%s\" does not exist",
1780                                                 NameListToString(names))));
1781
1782         return cfgoid;
1783 }
1784
1785 /*
1786  * TSConfigIsVisible
1787  *              Determine whether a text search configuration (identified by OID)
1788  *              is visible in the current search path.  Visible means "would be found
1789  *              by searching for the unqualified text search configuration name".
1790  */
1791 bool
1792 TSConfigIsVisible(Oid cfgid)
1793 {
1794         HeapTuple       tup;
1795         Form_pg_ts_config form;
1796         Oid                     namespace;
1797         bool            visible;
1798
1799         tup = SearchSysCache(TSCONFIGOID,
1800                                                  ObjectIdGetDatum(cfgid),
1801                                                  0, 0, 0);
1802         if (!HeapTupleIsValid(tup))
1803                 elog(ERROR, "cache lookup failed for text search configuration %u",
1804                          cfgid);
1805         form = (Form_pg_ts_config) GETSTRUCT(tup);
1806
1807         recomputeNamespacePath();
1808
1809         /*
1810          * Quick check: if it ain't in the path at all, it ain't visible. Items in
1811          * the system namespace are surely in the path and so we needn't even do
1812          * list_member_oid() for them.
1813          */
1814         namespace = form->cfgnamespace;
1815         if (namespace != PG_CATALOG_NAMESPACE &&
1816                 !list_member_oid(activeSearchPath, namespace))
1817                 visible = false;
1818         else
1819         {
1820                 /*
1821                  * If it is in the path, it might still not be visible; it could be
1822                  * hidden by another configuration of the same name earlier in the
1823                  * path. So we must do a slow check for conflicting configurations.
1824                  */
1825                 char       *name = NameStr(form->cfgname);
1826                 ListCell   *l;
1827
1828                 visible = false;
1829                 foreach(l, activeSearchPath)
1830                 {
1831                         Oid                     namespaceId = lfirst_oid(l);
1832
1833                         if (namespaceId == myTempNamespace)
1834                                 continue;               /* do not look in temp namespace */
1835
1836                         if (namespaceId == namespace)
1837                         {
1838                                 /* Found it first in path */
1839                                 visible = true;
1840                                 break;
1841                         }
1842                         if (SearchSysCacheExists(TSCONFIGNAMENSP,
1843                                                                          PointerGetDatum(name),
1844                                                                          ObjectIdGetDatum(namespaceId),
1845                                                                          0, 0))
1846                         {
1847                                 /* Found something else first in path */
1848                                 break;
1849                         }
1850                 }
1851         }
1852
1853         ReleaseSysCache(tup);
1854
1855         return visible;
1856 }
1857
1858
1859 /*
1860  * DeconstructQualifiedName
1861  *              Given a possibly-qualified name expressed as a list of String nodes,
1862  *              extract the schema name and object name.
1863  *
1864  * *nspname_p is set to NULL if there is no explicit schema name.
1865  */
1866 void
1867 DeconstructQualifiedName(List *names,
1868                                                  char **nspname_p,
1869                                                  char **objname_p)
1870 {
1871         char       *catalogname;
1872         char       *schemaname = NULL;
1873         char       *objname = NULL;
1874
1875         switch (list_length(names))
1876         {
1877                 case 1:
1878                         objname = strVal(linitial(names));
1879                         break;
1880                 case 2:
1881                         schemaname = strVal(linitial(names));
1882                         objname = strVal(lsecond(names));
1883                         break;
1884                 case 3:
1885                         catalogname = strVal(linitial(names));
1886                         schemaname = strVal(lsecond(names));
1887                         objname = strVal(lthird(names));
1888
1889                         /*
1890                          * We check the catalog name and then ignore it.
1891                          */
1892                         if (strcmp(catalogname, get_database_name(MyDatabaseId)) != 0)
1893                                 ereport(ERROR,
1894                                                 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
1895                                   errmsg("cross-database references are not implemented: %s",
1896                                                  NameListToString(names))));
1897                         break;
1898                 default:
1899                         ereport(ERROR,
1900                                         (errcode(ERRCODE_SYNTAX_ERROR),
1901                                 errmsg("improper qualified name (too many dotted names): %s",
1902                                            NameListToString(names))));
1903                         break;
1904         }
1905
1906         *nspname_p = schemaname;
1907         *objname_p = objname;
1908 }
1909
1910 /*
1911  * LookupExplicitNamespace
1912  *              Process an explicitly-specified schema name: look up the schema
1913  *              and verify we have USAGE (lookup) rights in it.
1914  *
1915  * Returns the namespace OID.  Raises ereport if any problem.
1916  */
1917 Oid
1918 LookupExplicitNamespace(const char *nspname)
1919 {
1920         Oid                     namespaceId;
1921         AclResult       aclresult;
1922
1923         /* check for pg_temp alias */
1924         if (strcmp(nspname, "pg_temp") == 0)
1925         {
1926                 if (OidIsValid(myTempNamespace))
1927                         return myTempNamespace;
1928
1929                 /*
1930                  * Since this is used only for looking up existing objects, there is
1931                  * no point in trying to initialize the temp namespace here; and doing
1932                  * so might create problems for some callers. Just fall through and
1933                  * give the "does not exist" error.
1934                  */
1935         }
1936
1937         namespaceId = GetSysCacheOid(NAMESPACENAME,
1938                                                                  CStringGetDatum(nspname),
1939                                                                  0, 0, 0);
1940         if (!OidIsValid(namespaceId))
1941                 ereport(ERROR,
1942                                 (errcode(ERRCODE_UNDEFINED_SCHEMA),
1943                                  errmsg("schema \"%s\" does not exist", nspname)));
1944
1945         aclresult = pg_namespace_aclcheck(namespaceId, GetUserId(), ACL_USAGE);
1946         if (aclresult != ACLCHECK_OK)
1947                 aclcheck_error(aclresult, ACL_KIND_NAMESPACE,
1948                                            nspname);
1949
1950         return namespaceId;
1951 }
1952
1953 /*
1954  * LookupCreationNamespace
1955  *              Look up the schema and verify we have CREATE rights on it.
1956  *
1957  * This is just like LookupExplicitNamespace except for the permission check,
1958  * and that we are willing to create pg_temp if needed.
1959  *
1960  * Note: calling this may result in a CommandCounterIncrement operation,
1961  * if we have to create or clean out the temp namespace.
1962  */
1963 Oid
1964 LookupCreationNamespace(const char *nspname)
1965 {
1966         Oid                     namespaceId;
1967         AclResult       aclresult;
1968
1969         /* check for pg_temp alias */
1970         if (strcmp(nspname, "pg_temp") == 0)
1971         {
1972                 /* Initialize temp namespace if first time through */
1973                 if (!OidIsValid(myTempNamespace))
1974                         InitTempTableNamespace();
1975                 return myTempNamespace;
1976         }
1977
1978         namespaceId = GetSysCacheOid(NAMESPACENAME,
1979                                                                  CStringGetDatum(nspname),
1980                                                                  0, 0, 0);
1981         if (!OidIsValid(namespaceId))
1982                 ereport(ERROR,
1983                                 (errcode(ERRCODE_UNDEFINED_SCHEMA),
1984                                  errmsg("schema \"%s\" does not exist", nspname)));
1985
1986         aclresult = pg_namespace_aclcheck(namespaceId, GetUserId(), ACL_CREATE);
1987         if (aclresult != ACLCHECK_OK)
1988                 aclcheck_error(aclresult, ACL_KIND_NAMESPACE,
1989                                            nspname);
1990
1991         return namespaceId;
1992 }
1993
1994 /*
1995  * QualifiedNameGetCreationNamespace
1996  *              Given a possibly-qualified name for an object (in List-of-Values
1997  *              format), determine what namespace the object should be created in.
1998  *              Also extract and return the object name (last component of list).
1999  *
2000  * Note: this does not apply any permissions check.  Callers must check
2001  * for CREATE rights on the selected namespace when appropriate.
2002  *
2003  * Note: calling this may result in a CommandCounterIncrement operation,
2004  * if we have to create or clean out the temp namespace.
2005  */
2006 Oid
2007 QualifiedNameGetCreationNamespace(List *names, char **objname_p)
2008 {
2009         char       *schemaname;
2010         Oid                     namespaceId;
2011
2012         /* deconstruct the name list */
2013         DeconstructQualifiedName(names, &schemaname, objname_p);
2014
2015         if (schemaname)
2016         {
2017                 /* check for pg_temp alias */
2018                 if (strcmp(schemaname, "pg_temp") == 0)
2019                 {
2020                         /* Initialize temp namespace if first time through */
2021                         if (!OidIsValid(myTempNamespace))
2022                                 InitTempTableNamespace();
2023                         return myTempNamespace;
2024                 }
2025                 /* use exact schema given */
2026                 namespaceId = GetSysCacheOid(NAMESPACENAME,
2027                                                                          CStringGetDatum(schemaname),
2028                                                                          0, 0, 0);
2029                 if (!OidIsValid(namespaceId))
2030                         ereport(ERROR,
2031                                         (errcode(ERRCODE_UNDEFINED_SCHEMA),
2032                                          errmsg("schema \"%s\" does not exist", schemaname)));
2033                 /* we do not check for USAGE rights here! */
2034         }
2035         else
2036         {
2037                 /* use the default creation namespace */
2038                 recomputeNamespacePath();
2039                 if (activeTempCreationPending)
2040                 {
2041                         /* Need to initialize temp namespace */
2042                         InitTempTableNamespace();
2043                         return myTempNamespace;
2044                 }
2045                 namespaceId = activeCreationNamespace;
2046                 if (!OidIsValid(namespaceId))
2047                         ereport(ERROR,
2048                                         (errcode(ERRCODE_UNDEFINED_SCHEMA),
2049                                          errmsg("no schema has been selected to create in")));
2050         }
2051
2052         return namespaceId;
2053 }
2054
2055 /*
2056  * makeRangeVarFromNameList
2057  *              Utility routine to convert a qualified-name list into RangeVar form.
2058  */
2059 RangeVar *
2060 makeRangeVarFromNameList(List *names)
2061 {
2062         RangeVar   *rel = makeRangeVar(NULL, NULL);
2063
2064         switch (list_length(names))
2065         {
2066                 case 1:
2067                         rel->relname = strVal(linitial(names));
2068                         break;
2069                 case 2:
2070                         rel->schemaname = strVal(linitial(names));
2071                         rel->relname = strVal(lsecond(names));
2072                         break;
2073                 case 3:
2074                         rel->catalogname = strVal(linitial(names));
2075                         rel->schemaname = strVal(lsecond(names));
2076                         rel->relname = strVal(lthird(names));
2077                         break;
2078                 default:
2079                         ereport(ERROR,
2080                                         (errcode(ERRCODE_SYNTAX_ERROR),
2081                                  errmsg("improper relation name (too many dotted names): %s",
2082                                                 NameListToString(names))));
2083                         break;
2084         }
2085
2086         return rel;
2087 }
2088
2089 /*
2090  * NameListToString
2091  *              Utility routine to convert a qualified-name list into a string.
2092  *
2093  * This is used primarily to form error messages, and so we do not quote
2094  * the list elements, for the sake of legibility.
2095  */
2096 char *
2097 NameListToString(List *names)
2098 {
2099         StringInfoData string;
2100         ListCell   *l;
2101
2102         initStringInfo(&string);
2103
2104         foreach(l, names)
2105         {
2106                 if (l != list_head(names))
2107                         appendStringInfoChar(&string, '.');
2108                 appendStringInfoString(&string, strVal(lfirst(l)));
2109         }
2110
2111         return string.data;
2112 }
2113
2114 /*
2115  * NameListToQuotedString
2116  *              Utility routine to convert a qualified-name list into a string.
2117  *
2118  * Same as above except that names will be double-quoted where necessary,
2119  * so the string could be re-parsed (eg, by textToQualifiedNameList).
2120  */
2121 char *
2122 NameListToQuotedString(List *names)
2123 {
2124         StringInfoData string;
2125         ListCell   *l;
2126
2127         initStringInfo(&string);
2128
2129         foreach(l, names)
2130         {
2131                 if (l != list_head(names))
2132                         appendStringInfoChar(&string, '.');
2133                 appendStringInfoString(&string, quote_identifier(strVal(lfirst(l))));
2134         }
2135
2136         return string.data;
2137 }
2138
2139 /*
2140  * isTempNamespace - is the given namespace my temporary-table namespace?
2141  */
2142 bool
2143 isTempNamespace(Oid namespaceId)
2144 {
2145         if (OidIsValid(myTempNamespace) && myTempNamespace == namespaceId)
2146                 return true;
2147         return false;
2148 }
2149
2150 /*
2151  * isTempToastNamespace - is the given namespace my temporary-toast-table
2152  *              namespace?
2153  */
2154 bool
2155 isTempToastNamespace(Oid namespaceId)
2156 {
2157         if (OidIsValid(myTempToastNamespace) && myTempToastNamespace == namespaceId)
2158                 return true;
2159         return false;
2160 }
2161
2162 /*
2163  * isTempOrToastNamespace - is the given namespace my temporary-table
2164  *              namespace or my temporary-toast-table namespace?
2165  */
2166 bool
2167 isTempOrToastNamespace(Oid namespaceId)
2168 {
2169         if (OidIsValid(myTempNamespace) &&
2170          (myTempNamespace == namespaceId || myTempToastNamespace == namespaceId))
2171                 return true;
2172         return false;
2173 }
2174
2175 /*
2176  * isAnyTempNamespace - is the given namespace a temporary-table namespace
2177  * (either my own, or another backend's)?  Temporary-toast-table namespaces
2178  * are included, too.
2179  */
2180 bool
2181 isAnyTempNamespace(Oid namespaceId)
2182 {
2183         bool            result;
2184         char       *nspname;
2185
2186         /* True if the namespace name starts with "pg_temp_" or "pg_toast_temp_" */
2187         nspname = get_namespace_name(namespaceId);
2188         if (!nspname)
2189                 return false;                   /* no such namespace? */
2190         result = (strncmp(nspname, "pg_temp_", 8) == 0) ||
2191                 (strncmp(nspname, "pg_toast_temp_", 14) == 0);
2192         pfree(nspname);
2193         return result;
2194 }
2195
2196 /*
2197  * isOtherTempNamespace - is the given namespace some other backend's
2198  * temporary-table namespace (including temporary-toast-table namespaces)?
2199  */
2200 bool
2201 isOtherTempNamespace(Oid namespaceId)
2202 {
2203         /* If it's my own temp namespace, say "false" */
2204         if (isTempOrToastNamespace(namespaceId))
2205                 return false;
2206         /* Else, if it's any temp namespace, say "true" */
2207         return isAnyTempNamespace(namespaceId);
2208 }
2209
2210 /*
2211  * GetTempToastNamespace - get the OID of my temporary-toast-table namespace,
2212  * which must already be assigned.      (This is only used when creating a toast
2213  * table for a temp table, so we must have already done InitTempTableNamespace)
2214  */
2215 Oid
2216 GetTempToastNamespace(void)
2217 {
2218         Assert(OidIsValid(myTempToastNamespace));
2219         return myTempToastNamespace;
2220 }
2221
2222
2223 /*
2224  * GetOverrideSearchPath - fetch current search path definition in form
2225  * used by PushOverrideSearchPath.
2226  *
2227  * The result structure is allocated in the specified memory context
2228  * (which might or might not be equal to CurrentMemoryContext); but any
2229  * junk created by revalidation calculations will be in CurrentMemoryContext.
2230  */
2231 OverrideSearchPath *
2232 GetOverrideSearchPath(MemoryContext context)
2233 {
2234         OverrideSearchPath *result;
2235         List       *schemas;
2236         MemoryContext oldcxt;
2237
2238         recomputeNamespacePath();
2239
2240         oldcxt = MemoryContextSwitchTo(context);
2241
2242         result = (OverrideSearchPath *) palloc0(sizeof(OverrideSearchPath));
2243         schemas = list_copy(activeSearchPath);
2244         while (schemas && linitial_oid(schemas) != activeCreationNamespace)
2245         {
2246                 if (linitial_oid(schemas) == myTempNamespace)
2247                         result->addTemp = true;
2248                 else
2249                 {
2250                         Assert(linitial_oid(schemas) == PG_CATALOG_NAMESPACE);
2251                         result->addCatalog = true;
2252                 }
2253                 schemas = list_delete_first(schemas);
2254         }
2255         result->schemas = schemas;
2256
2257         MemoryContextSwitchTo(oldcxt);
2258
2259         return result;
2260 }
2261
2262 /*
2263  * PushOverrideSearchPath - temporarily override the search path
2264  *
2265  * We allow nested overrides, hence the push/pop terminology.  The GUC
2266  * search_path variable is ignored while an override is active.
2267  */
2268 void
2269 PushOverrideSearchPath(OverrideSearchPath * newpath)
2270 {
2271         OverrideStackEntry *entry;
2272         List       *oidlist;
2273         Oid                     firstNS;
2274         MemoryContext oldcxt;
2275
2276         /*
2277          * Copy the list for safekeeping, and insert implicitly-searched
2278          * namespaces as needed.  This code should track recomputeNamespacePath.
2279          */
2280         oldcxt = MemoryContextSwitchTo(TopMemoryContext);
2281
2282         oidlist = list_copy(newpath->schemas);
2283
2284         /*
2285          * Remember the first member of the explicit list.
2286          */
2287         if (oidlist == NIL)
2288                 firstNS = InvalidOid;
2289         else
2290                 firstNS = linitial_oid(oidlist);
2291
2292         /*
2293          * Add any implicitly-searched namespaces to the list.  Note these go on
2294          * the front, not the back; also notice that we do not check USAGE
2295          * permissions for these.
2296          */
2297         if (newpath->addCatalog)
2298                 oidlist = lcons_oid(PG_CATALOG_NAMESPACE, oidlist);
2299
2300         if (newpath->addTemp)
2301         {
2302                 Assert(OidIsValid(myTempNamespace));
2303                 oidlist = lcons_oid(myTempNamespace, oidlist);
2304         }
2305
2306         /*
2307          * Build the new stack entry, then insert it at the head of the list.
2308          */
2309         entry = (OverrideStackEntry *) palloc(sizeof(OverrideStackEntry));
2310         entry->searchPath = oidlist;
2311         entry->creationNamespace = firstNS;
2312         entry->nestLevel = GetCurrentTransactionNestLevel();
2313
2314         overrideStack = lcons(entry, overrideStack);
2315
2316         /* And make it active. */
2317         activeSearchPath = entry->searchPath;
2318         activeCreationNamespace = entry->creationNamespace;
2319         activeTempCreationPending = false;      /* XXX is this OK? */
2320
2321         MemoryContextSwitchTo(oldcxt);
2322 }
2323
2324 /*
2325  * PopOverrideSearchPath - undo a previous PushOverrideSearchPath
2326  *
2327  * Any push during a (sub)transaction will be popped automatically at abort.
2328  * But it's caller error if a push isn't popped in normal control flow.
2329  */
2330 void
2331 PopOverrideSearchPath(void)
2332 {
2333         OverrideStackEntry *entry;
2334
2335         /* Sanity checks. */
2336         if (overrideStack == NIL)
2337                 elog(ERROR, "bogus PopOverrideSearchPath call");
2338         entry = (OverrideStackEntry *) linitial(overrideStack);
2339         if (entry->nestLevel != GetCurrentTransactionNestLevel())
2340                 elog(ERROR, "bogus PopOverrideSearchPath call");
2341
2342         /* Pop the stack and free storage. */
2343         overrideStack = list_delete_first(overrideStack);
2344         list_free(entry->searchPath);
2345         pfree(entry);
2346
2347         /* Activate the next level down. */
2348         if (overrideStack)
2349         {
2350                 entry = (OverrideStackEntry *) linitial(overrideStack);
2351                 activeSearchPath = entry->searchPath;
2352                 activeCreationNamespace = entry->creationNamespace;
2353                 activeTempCreationPending = false;              /* XXX is this OK? */
2354         }
2355         else
2356         {
2357                 /* If not baseSearchPathValid, this is useless but harmless */
2358                 activeSearchPath = baseSearchPath;
2359                 activeCreationNamespace = baseCreationNamespace;
2360                 activeTempCreationPending = baseTempCreationPending;
2361         }
2362 }
2363
2364
2365 /*
2366  * FindConversionByName - find a conversion by possibly qualified name
2367  */
2368 Oid
2369 FindConversionByName(List *name)
2370 {
2371         char       *schemaname;
2372         char       *conversion_name;
2373         Oid                     namespaceId;
2374         Oid                     conoid;
2375         ListCell   *l;
2376
2377         /* deconstruct the name list */
2378         DeconstructQualifiedName(name, &schemaname, &conversion_name);
2379
2380         if (schemaname)
2381         {
2382                 /* use exact schema given */
2383                 namespaceId = LookupExplicitNamespace(schemaname);
2384                 return FindConversion(conversion_name, namespaceId);
2385         }
2386         else
2387         {
2388                 /* search for it in search path */
2389                 recomputeNamespacePath();
2390
2391                 foreach(l, activeSearchPath)
2392                 {
2393                         namespaceId = lfirst_oid(l);
2394
2395                         if (namespaceId == myTempNamespace)
2396                                 continue;               /* do not look in temp namespace */
2397
2398                         conoid = FindConversion(conversion_name, namespaceId);
2399                         if (OidIsValid(conoid))
2400                                 return conoid;
2401                 }
2402         }
2403
2404         /* Not found in path */
2405         return InvalidOid;
2406 }
2407
2408 /*
2409  * FindDefaultConversionProc - find default encoding conversion proc
2410  */
2411 Oid
2412 FindDefaultConversionProc(int4 for_encoding, int4 to_encoding)
2413 {
2414         Oid                     proc;
2415         ListCell   *l;
2416
2417         recomputeNamespacePath();
2418
2419         foreach(l, activeSearchPath)
2420         {
2421                 Oid                     namespaceId = lfirst_oid(l);
2422
2423                 if (namespaceId == myTempNamespace)
2424                         continue;                       /* do not look in temp namespace */
2425
2426                 proc = FindDefaultConversion(namespaceId, for_encoding, to_encoding);
2427                 if (OidIsValid(proc))
2428                         return proc;
2429         }
2430
2431         /* Not found in path */
2432         return InvalidOid;
2433 }
2434
2435 /*
2436  * recomputeNamespacePath - recompute path derived variables if needed.
2437  */
2438 static void
2439 recomputeNamespacePath(void)
2440 {
2441         Oid                     roleid = GetUserId();
2442         char       *rawname;
2443         List       *namelist;
2444         List       *oidlist;
2445         List       *newpath;
2446         ListCell   *l;
2447         bool            temp_missing;
2448         Oid                     firstNS;
2449         MemoryContext oldcxt;
2450
2451         /* Do nothing if an override search spec is active. */
2452         if (overrideStack)
2453                 return;
2454
2455         /* Do nothing if path is already valid. */
2456         if (baseSearchPathValid && namespaceUser == roleid)
2457                 return;
2458
2459         /* Need a modifiable copy of namespace_search_path string */
2460         rawname = pstrdup(namespace_search_path);
2461
2462         /* Parse string into list of identifiers */
2463         if (!SplitIdentifierString(rawname, ',', &namelist))
2464         {
2465                 /* syntax error in name list */
2466                 /* this should not happen if GUC checked check_search_path */
2467                 elog(ERROR, "invalid list syntax");
2468         }
2469
2470         /*
2471          * Convert the list of names to a list of OIDs.  If any names are not
2472          * recognizable or we don't have read access, just leave them out of the
2473          * list.  (We can't raise an error, since the search_path setting has
2474          * already been accepted.)      Don't make duplicate entries, either.
2475          */
2476         oidlist = NIL;
2477         temp_missing = false;
2478         foreach(l, namelist)
2479         {
2480                 char       *curname = (char *) lfirst(l);
2481                 Oid                     namespaceId;
2482
2483                 if (strcmp(curname, "$user") == 0)
2484                 {
2485                         /* $user --- substitute namespace matching user name, if any */
2486                         HeapTuple       tuple;
2487
2488                         tuple = SearchSysCache(AUTHOID,
2489                                                                    ObjectIdGetDatum(roleid),
2490                                                                    0, 0, 0);
2491                         if (HeapTupleIsValid(tuple))
2492                         {
2493                                 char       *rname;
2494
2495                                 rname = NameStr(((Form_pg_authid) GETSTRUCT(tuple))->rolname);
2496                                 namespaceId = GetSysCacheOid(NAMESPACENAME,
2497                                                                                          CStringGetDatum(rname),
2498                                                                                          0, 0, 0);
2499                                 ReleaseSysCache(tuple);
2500                                 if (OidIsValid(namespaceId) &&
2501                                         !list_member_oid(oidlist, namespaceId) &&
2502                                         pg_namespace_aclcheck(namespaceId, roleid,
2503                                                                                   ACL_USAGE) == ACLCHECK_OK)
2504                                         oidlist = lappend_oid(oidlist, namespaceId);
2505                         }
2506                 }
2507                 else if (strcmp(curname, "pg_temp") == 0)
2508                 {
2509                         /* pg_temp --- substitute temp namespace, if any */
2510                         if (OidIsValid(myTempNamespace))
2511                         {
2512                                 if (!list_member_oid(oidlist, myTempNamespace))
2513                                         oidlist = lappend_oid(oidlist, myTempNamespace);
2514                         }
2515                         else
2516                         {
2517                                 /* If it ought to be the creation namespace, set flag */
2518                                 if (oidlist == NIL)
2519                                         temp_missing = true;
2520                         }
2521                 }
2522                 else
2523                 {
2524                         /* normal namespace reference */
2525                         namespaceId = GetSysCacheOid(NAMESPACENAME,
2526                                                                                  CStringGetDatum(curname),
2527                                                                                  0, 0, 0);
2528                         if (OidIsValid(namespaceId) &&
2529                                 !list_member_oid(oidlist, namespaceId) &&
2530                                 pg_namespace_aclcheck(namespaceId, roleid,
2531                                                                           ACL_USAGE) == ACLCHECK_OK)
2532                                 oidlist = lappend_oid(oidlist, namespaceId);
2533                 }
2534         }
2535
2536         /*
2537          * Remember the first member of the explicit list.      (Note: this is
2538          * nominally wrong if temp_missing, but we need it anyway to distinguish
2539          * explicit from implicit mention of pg_catalog.)
2540          */
2541         if (oidlist == NIL)
2542                 firstNS = InvalidOid;
2543         else
2544                 firstNS = linitial_oid(oidlist);
2545
2546         /*
2547          * Add any implicitly-searched namespaces to the list.  Note these go on
2548          * the front, not the back; also notice that we do not check USAGE
2549          * permissions for these.
2550          */
2551         if (!list_member_oid(oidlist, PG_CATALOG_NAMESPACE))
2552                 oidlist = lcons_oid(PG_CATALOG_NAMESPACE, oidlist);
2553
2554         if (OidIsValid(myTempNamespace) &&
2555                 !list_member_oid(oidlist, myTempNamespace))
2556                 oidlist = lcons_oid(myTempNamespace, oidlist);
2557
2558         /*
2559          * Now that we've successfully built the new list of namespace OIDs, save
2560          * it in permanent storage.
2561          */
2562         oldcxt = MemoryContextSwitchTo(TopMemoryContext);
2563         newpath = list_copy(oidlist);
2564         MemoryContextSwitchTo(oldcxt);
2565
2566         /* Now safe to assign to state variables. */
2567         list_free(baseSearchPath);
2568         baseSearchPath = newpath;
2569         baseCreationNamespace = firstNS;
2570         baseTempCreationPending = temp_missing;
2571
2572         /* Mark the path valid. */
2573         baseSearchPathValid = true;
2574         namespaceUser = roleid;
2575
2576         /* And make it active. */
2577         activeSearchPath = baseSearchPath;
2578         activeCreationNamespace = baseCreationNamespace;
2579         activeTempCreationPending = baseTempCreationPending;
2580
2581         /* Clean up. */
2582         pfree(rawname);
2583         list_free(namelist);
2584         list_free(oidlist);
2585 }
2586
2587 /*
2588  * InitTempTableNamespace
2589  *              Initialize temp table namespace on first use in a particular backend
2590  */
2591 static void
2592 InitTempTableNamespace(void)
2593 {
2594         char            namespaceName[NAMEDATALEN];
2595         Oid                     namespaceId;
2596         Oid                     toastspaceId;
2597
2598         Assert(!OidIsValid(myTempNamespace));
2599
2600         /*
2601          * First, do permission check to see if we are authorized to make temp
2602          * tables.      We use a nonstandard error message here since "databasename:
2603          * permission denied" might be a tad cryptic.
2604          *
2605          * Note that ACL_CREATE_TEMP rights are rechecked in pg_namespace_aclmask;
2606          * that's necessary since current user ID could change during the session.
2607          * But there's no need to make the namespace in the first place until a
2608          * temp table creation request is made by someone with appropriate rights.
2609          */
2610         if (pg_database_aclcheck(MyDatabaseId, GetUserId(),
2611                                                          ACL_CREATE_TEMP) != ACLCHECK_OK)
2612                 ereport(ERROR,
2613                                 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
2614                                  errmsg("permission denied to create temporary tables in database \"%s\"",
2615                                                 get_database_name(MyDatabaseId))));
2616
2617         snprintf(namespaceName, sizeof(namespaceName), "pg_temp_%d", MyBackendId);
2618
2619         namespaceId = GetSysCacheOid(NAMESPACENAME,
2620                                                                  CStringGetDatum(namespaceName),
2621                                                                  0, 0, 0);
2622         if (!OidIsValid(namespaceId))
2623         {
2624                 /*
2625                  * First use of this temp namespace in this database; create it. The
2626                  * temp namespaces are always owned by the superuser.  We leave their
2627                  * permissions at default --- i.e., no access except to superuser ---
2628                  * to ensure that unprivileged users can't peek at other backends'
2629                  * temp tables.  This works because the places that access the temp
2630                  * namespace for my own backend skip permissions checks on it.
2631                  */
2632                 namespaceId = NamespaceCreate(namespaceName, BOOTSTRAP_SUPERUSERID);
2633                 /* Advance command counter to make namespace visible */
2634                 CommandCounterIncrement();
2635         }
2636         else
2637         {
2638                 /*
2639                  * If the namespace already exists, clean it out (in case the former
2640                  * owner crashed without doing so).
2641                  */
2642                 RemoveTempRelations(namespaceId);
2643         }
2644
2645         /*
2646          * If the corresponding temp-table namespace doesn't exist yet, create it.
2647          * (We assume there is no need to clean it out if it does exist, since
2648          * dropping a parent table should make its toast table go away.)
2649          */
2650         snprintf(namespaceName, sizeof(namespaceName), "pg_toast_temp_%d",
2651                          MyBackendId);
2652
2653         toastspaceId = GetSysCacheOid(NAMESPACENAME,
2654                                                                   CStringGetDatum(namespaceName),
2655                                                                   0, 0, 0);
2656         if (!OidIsValid(toastspaceId))
2657         {
2658                 toastspaceId = NamespaceCreate(namespaceName, BOOTSTRAP_SUPERUSERID);
2659                 /* Advance command counter to make namespace visible */
2660                 CommandCounterIncrement();
2661         }
2662
2663         /*
2664          * Okay, we've prepared the temp namespace ... but it's not committed yet,
2665          * so all our work could be undone by transaction rollback.  Set flag for
2666          * AtEOXact_Namespace to know what to do.
2667          */
2668         myTempNamespace = namespaceId;
2669         myTempToastNamespace = toastspaceId;
2670
2671         /* It should not be done already. */
2672         AssertState(myTempNamespaceSubID == InvalidSubTransactionId);
2673         myTempNamespaceSubID = GetCurrentSubTransactionId();
2674
2675         baseSearchPathValid = false;    /* need to rebuild list */
2676 }
2677
2678 /*
2679  * End-of-transaction cleanup for namespaces.
2680  */
2681 void
2682 AtEOXact_Namespace(bool isCommit)
2683 {
2684         /*
2685          * If we abort the transaction in which a temp namespace was selected,
2686          * we'll have to do any creation or cleanout work over again.  So, just
2687          * forget the namespace entirely until next time.  On the other hand, if
2688          * we commit then register an exit callback to clean out the temp tables
2689          * at backend shutdown.  (We only want to register the callback once per
2690          * session, so this is a good place to do it.)
2691          */
2692         if (myTempNamespaceSubID != InvalidSubTransactionId)
2693         {
2694                 if (isCommit)
2695                         on_shmem_exit(RemoveTempRelationsCallback, 0);
2696                 else
2697                 {
2698                         myTempNamespace = InvalidOid;
2699                         myTempToastNamespace = InvalidOid;
2700                         baseSearchPathValid = false;            /* need to rebuild list */
2701                 }
2702                 myTempNamespaceSubID = InvalidSubTransactionId;
2703         }
2704
2705         /*
2706          * Clean up if someone failed to do PopOverrideSearchPath
2707          */
2708         if (overrideStack)
2709         {
2710                 if (isCommit)
2711                         elog(WARNING, "leaked override search path");
2712                 while (overrideStack)
2713                 {
2714                         OverrideStackEntry *entry;
2715
2716                         entry = (OverrideStackEntry *) linitial(overrideStack);
2717                         overrideStack = list_delete_first(overrideStack);
2718                         list_free(entry->searchPath);
2719                         pfree(entry);
2720                 }
2721                 /* If not baseSearchPathValid, this is useless but harmless */
2722                 activeSearchPath = baseSearchPath;
2723                 activeCreationNamespace = baseCreationNamespace;
2724                 activeTempCreationPending = baseTempCreationPending;
2725         }
2726 }
2727
2728 /*
2729  * AtEOSubXact_Namespace
2730  *
2731  * At subtransaction commit, propagate the temp-namespace-creation
2732  * flag to the parent subtransaction.
2733  *
2734  * At subtransaction abort, forget the flag if we set it up.
2735  */
2736 void
2737 AtEOSubXact_Namespace(bool isCommit, SubTransactionId mySubid,
2738                                           SubTransactionId parentSubid)
2739 {
2740         OverrideStackEntry *entry;
2741
2742         if (myTempNamespaceSubID == mySubid)
2743         {
2744                 if (isCommit)
2745                         myTempNamespaceSubID = parentSubid;
2746                 else
2747                 {
2748                         myTempNamespaceSubID = InvalidSubTransactionId;
2749                         /* TEMP namespace creation failed, so reset state */
2750                         myTempNamespace = InvalidOid;
2751                         myTempToastNamespace = InvalidOid;
2752                         baseSearchPathValid = false;            /* need to rebuild list */
2753                 }
2754         }
2755
2756         /*
2757          * Clean up if someone failed to do PopOverrideSearchPath
2758          */
2759         while (overrideStack)
2760         {
2761                 entry = (OverrideStackEntry *) linitial(overrideStack);
2762                 if (entry->nestLevel < GetCurrentTransactionNestLevel())
2763                         break;
2764                 if (isCommit)
2765                         elog(WARNING, "leaked override search path");
2766                 overrideStack = list_delete_first(overrideStack);
2767                 list_free(entry->searchPath);
2768                 pfree(entry);
2769         }
2770
2771         /* Activate the next level down. */
2772         if (overrideStack)
2773         {
2774                 entry = (OverrideStackEntry *) linitial(overrideStack);
2775                 activeSearchPath = entry->searchPath;
2776                 activeCreationNamespace = entry->creationNamespace;
2777                 activeTempCreationPending = false;              /* XXX is this OK? */
2778         }
2779         else
2780         {
2781                 /* If not baseSearchPathValid, this is useless but harmless */
2782                 activeSearchPath = baseSearchPath;
2783                 activeCreationNamespace = baseCreationNamespace;
2784                 activeTempCreationPending = baseTempCreationPending;
2785         }
2786 }
2787
2788 /*
2789  * Remove all relations in the specified temp namespace.
2790  *
2791  * This is called at backend shutdown (if we made any temp relations).
2792  * It is also called when we begin using a pre-existing temp namespace,
2793  * in order to clean out any relations that might have been created by
2794  * a crashed backend.
2795  */
2796 static void
2797 RemoveTempRelations(Oid tempNamespaceId)
2798 {
2799         ObjectAddress object;
2800
2801         /*
2802          * We want to get rid of everything in the target namespace, but not the
2803          * namespace itself (deleting it only to recreate it later would be a
2804          * waste of cycles).  We do this by finding everything that has a
2805          * dependency on the namespace.
2806          */
2807         object.classId = NamespaceRelationId;
2808         object.objectId = tempNamespaceId;
2809         object.objectSubId = 0;
2810
2811         deleteWhatDependsOn(&object, false);
2812 }
2813
2814 /*
2815  * Callback to remove temp relations at backend exit.
2816  */
2817 static void
2818 RemoveTempRelationsCallback(int code, Datum arg)
2819 {
2820         if (OidIsValid(myTempNamespace))        /* should always be true */
2821         {
2822                 /* Need to ensure we have a usable transaction. */
2823                 AbortOutOfAnyTransaction();
2824                 StartTransactionCommand();
2825
2826                 RemoveTempRelations(myTempNamespace);
2827
2828                 CommitTransactionCommand();
2829         }
2830 }
2831
2832 /*
2833  * Remove all temp tables from the temporary namespace.
2834  */
2835 void
2836 ResetTempTableNamespace(void)
2837 {
2838         if (OidIsValid(myTempNamespace))
2839                 RemoveTempRelations(myTempNamespace);
2840 }
2841
2842
2843 /*
2844  * Routines for handling the GUC variable 'search_path'.
2845  */
2846
2847 /* assign_hook: validate new search_path, do extra actions as needed */
2848 const char *
2849 assign_search_path(const char *newval, bool doit, GucSource source)
2850 {
2851         char       *rawname;
2852         List       *namelist;
2853         ListCell   *l;
2854
2855         /* Need a modifiable copy of string */
2856         rawname = pstrdup(newval);
2857
2858         /* Parse string into list of identifiers */
2859         if (!SplitIdentifierString(rawname, ',', &namelist))
2860         {
2861                 /* syntax error in name list */
2862                 pfree(rawname);
2863                 list_free(namelist);
2864                 return NULL;
2865         }
2866
2867         /*
2868          * If we aren't inside a transaction, we cannot do database access so
2869          * cannot verify the individual names.  Must accept the list on faith.
2870          */
2871         if (source >= PGC_S_INTERACTIVE && IsTransactionState())
2872         {
2873                 /*
2874                  * Verify that all the names are either valid namespace names or
2875                  * "$user" or "pg_temp".  We do not require $user to correspond to a
2876                  * valid namespace, and pg_temp might not exist yet.  We do not check
2877                  * for USAGE rights, either; should we?
2878                  *
2879                  * When source == PGC_S_TEST, we are checking the argument of an ALTER
2880                  * DATABASE SET or ALTER USER SET command.      It could be that the
2881                  * intended use of the search path is for some other database, so we
2882                  * should not error out if it mentions schemas not present in the
2883                  * current database.  We reduce the message to NOTICE instead.
2884                  */
2885                 foreach(l, namelist)
2886                 {
2887                         char       *curname = (char *) lfirst(l);
2888
2889                         if (strcmp(curname, "$user") == 0)
2890                                 continue;
2891                         if (strcmp(curname, "pg_temp") == 0)
2892                                 continue;
2893                         if (!SearchSysCacheExists(NAMESPACENAME,
2894                                                                           CStringGetDatum(curname),
2895                                                                           0, 0, 0))
2896                                 ereport((source == PGC_S_TEST) ? NOTICE : ERROR,
2897                                                 (errcode(ERRCODE_UNDEFINED_SCHEMA),
2898                                                  errmsg("schema \"%s\" does not exist", curname)));
2899                 }
2900         }
2901
2902         pfree(rawname);
2903         list_free(namelist);
2904
2905         /*
2906          * We mark the path as needing recomputation, but don't do anything until
2907          * it's needed.  This avoids trying to do database access during GUC
2908          * initialization.
2909          */
2910         if (doit)
2911                 baseSearchPathValid = false;
2912
2913         return newval;
2914 }
2915
2916 /*
2917  * InitializeSearchPath: initialize module during InitPostgres.
2918  *
2919  * This is called after we are up enough to be able to do catalog lookups.
2920  */
2921 void
2922 InitializeSearchPath(void)
2923 {
2924         if (IsBootstrapProcessingMode())
2925         {
2926                 /*
2927                  * In bootstrap mode, the search path must be 'pg_catalog' so that
2928                  * tables are created in the proper namespace; ignore the GUC setting.
2929                  */
2930                 MemoryContext oldcxt;
2931
2932                 oldcxt = MemoryContextSwitchTo(TopMemoryContext);
2933                 baseSearchPath = list_make1_oid(PG_CATALOG_NAMESPACE);
2934                 MemoryContextSwitchTo(oldcxt);
2935                 baseCreationNamespace = PG_CATALOG_NAMESPACE;
2936                 baseTempCreationPending = false;
2937                 baseSearchPathValid = true;
2938                 namespaceUser = GetUserId();
2939                 activeSearchPath = baseSearchPath;
2940                 activeCreationNamespace = baseCreationNamespace;
2941                 activeTempCreationPending = baseTempCreationPending;
2942         }
2943         else
2944         {
2945                 /*
2946                  * In normal mode, arrange for a callback on any syscache invalidation
2947                  * of pg_namespace rows.
2948                  */
2949                 CacheRegisterSyscacheCallback(NAMESPACEOID,
2950                                                                           NamespaceCallback,
2951                                                                           (Datum) 0);
2952                 /* Force search path to be recomputed on next use */
2953                 baseSearchPathValid = false;
2954         }
2955 }
2956
2957 /*
2958  * NamespaceCallback
2959  *              Syscache inval callback function
2960  */
2961 static void
2962 NamespaceCallback(Datum arg, Oid relid)
2963 {
2964         /* Force search path to be recomputed on next use */
2965         baseSearchPathValid = false;
2966 }
2967
2968 /*
2969  * Fetch the active search path. The return value is a palloc'ed list
2970  * of OIDs; the caller is responsible for freeing this storage as
2971  * appropriate.
2972  *
2973  * The returned list includes the implicitly-prepended namespaces only if
2974  * includeImplicit is true.
2975  *
2976  * Note: calling this may result in a CommandCounterIncrement operation,
2977  * if we have to create or clean out the temp namespace.
2978  */
2979 List *
2980 fetch_search_path(bool includeImplicit)
2981 {
2982         List       *result;
2983
2984         recomputeNamespacePath();
2985
2986         /*
2987          * If the temp namespace should be first, force it to exist.  This is so
2988          * that callers can trust the result to reflect the actual default
2989          * creation namespace.  It's a bit bogus to do this here, since
2990          * current_schema() is supposedly a stable function without side-effects,
2991          * but the alternatives seem worse.
2992          */
2993         if (activeTempCreationPending)
2994         {
2995                 InitTempTableNamespace();
2996                 recomputeNamespacePath();
2997         }
2998
2999         result = list_copy(activeSearchPath);
3000         if (!includeImplicit)
3001         {
3002                 while (result && linitial_oid(result) != activeCreationNamespace)
3003                         result = list_delete_first(result);
3004         }
3005
3006         return result;
3007 }
3008
3009 /*
3010  * Export the FooIsVisible functions as SQL-callable functions.
3011  */
3012
3013 Datum
3014 pg_table_is_visible(PG_FUNCTION_ARGS)
3015 {
3016         Oid                     oid = PG_GETARG_OID(0);
3017
3018         PG_RETURN_BOOL(RelationIsVisible(oid));
3019 }
3020
3021 Datum
3022 pg_type_is_visible(PG_FUNCTION_ARGS)
3023 {
3024         Oid                     oid = PG_GETARG_OID(0);
3025
3026         PG_RETURN_BOOL(TypeIsVisible(oid));
3027 }
3028
3029 Datum
3030 pg_function_is_visible(PG_FUNCTION_ARGS)
3031 {
3032         Oid                     oid = PG_GETARG_OID(0);
3033
3034         PG_RETURN_BOOL(FunctionIsVisible(oid));
3035 }
3036
3037 Datum
3038 pg_operator_is_visible(PG_FUNCTION_ARGS)
3039 {
3040         Oid                     oid = PG_GETARG_OID(0);
3041
3042         PG_RETURN_BOOL(OperatorIsVisible(oid));
3043 }
3044
3045 Datum
3046 pg_opclass_is_visible(PG_FUNCTION_ARGS)
3047 {
3048         Oid                     oid = PG_GETARG_OID(0);
3049
3050         PG_RETURN_BOOL(OpclassIsVisible(oid));
3051 }
3052
3053 Datum
3054 pg_conversion_is_visible(PG_FUNCTION_ARGS)
3055 {
3056         Oid                     oid = PG_GETARG_OID(0);
3057
3058         PG_RETURN_BOOL(ConversionIsVisible(oid));
3059 }
3060
3061 Datum
3062 pg_ts_parser_is_visible(PG_FUNCTION_ARGS)
3063 {
3064         Oid                     oid = PG_GETARG_OID(0);
3065
3066         PG_RETURN_BOOL(TSParserIsVisible(oid));
3067 }
3068
3069 Datum
3070 pg_ts_dict_is_visible(PG_FUNCTION_ARGS)
3071 {
3072         Oid                     oid = PG_GETARG_OID(0);
3073
3074         PG_RETURN_BOOL(TSDictionaryIsVisible(oid));
3075 }
3076
3077 Datum
3078 pg_ts_template_is_visible(PG_FUNCTION_ARGS)
3079 {
3080         Oid                     oid = PG_GETARG_OID(0);
3081
3082         PG_RETURN_BOOL(TSTemplateIsVisible(oid));
3083 }
3084
3085 Datum
3086 pg_ts_config_is_visible(PG_FUNCTION_ARGS)
3087 {
3088         Oid                     oid = PG_GETARG_OID(0);
3089
3090         PG_RETURN_BOOL(TSConfigIsVisible(oid));
3091 }
3092
3093 Datum
3094 pg_my_temp_schema(PG_FUNCTION_ARGS)
3095 {
3096         PG_RETURN_OID(myTempNamespace);
3097 }
3098
3099 Datum
3100 pg_is_other_temp_schema(PG_FUNCTION_ARGS)
3101 {
3102         Oid                     oid = PG_GETARG_OID(0);
3103
3104         PG_RETURN_BOOL(isOtherTempNamespace(oid));
3105 }