]> granicus.if.org Git - postgresql/blob - src/backend/catalog/namespace.c
46e0312b992b27b6869c7e06f8298f3cae635a79
[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.99 2007/08/27 03:36:08 tgl 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
623                          * in 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
953                          * in 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 we
1437                  * 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
1566                  * path. 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.
1694                  * So 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                  * Since this is used only for looking up existing objects, there
1930                  * is no point in trying to initialize the temp namespace here;
1931                  * and doing so might create problems for some callers.
1932                  * Just fall through and give the "does not exist" error.
1933                  */
1934         }
1935
1936         namespaceId = GetSysCacheOid(NAMESPACENAME,
1937                                                                  CStringGetDatum(nspname),
1938                                                                  0, 0, 0);
1939         if (!OidIsValid(namespaceId))
1940                 ereport(ERROR,
1941                                 (errcode(ERRCODE_UNDEFINED_SCHEMA),
1942                                  errmsg("schema \"%s\" does not exist", nspname)));
1943
1944         aclresult = pg_namespace_aclcheck(namespaceId, GetUserId(), ACL_USAGE);
1945         if (aclresult != ACLCHECK_OK)
1946                 aclcheck_error(aclresult, ACL_KIND_NAMESPACE,
1947                                            nspname);
1948
1949         return namespaceId;
1950 }
1951
1952 /*
1953  * LookupCreationNamespace
1954  *              Look up the schema and verify we have CREATE rights on it.
1955  *
1956  * This is just like LookupExplicitNamespace except for the permission check,
1957  * and that we are willing to create pg_temp if needed.
1958  *
1959  * Note: calling this may result in a CommandCounterIncrement operation,
1960  * if we have to create or clean out the temp namespace.
1961  */
1962 Oid
1963 LookupCreationNamespace(const char *nspname)
1964 {
1965         Oid                     namespaceId;
1966         AclResult       aclresult;
1967
1968         /* check for pg_temp alias */
1969         if (strcmp(nspname, "pg_temp") == 0)
1970         {
1971                 /* Initialize temp namespace if first time through */
1972                 if (!OidIsValid(myTempNamespace))
1973                         InitTempTableNamespace();
1974                 return myTempNamespace;
1975         }
1976
1977         namespaceId = GetSysCacheOid(NAMESPACENAME,
1978                                                                  CStringGetDatum(nspname),
1979                                                                  0, 0, 0);
1980         if (!OidIsValid(namespaceId))
1981                 ereport(ERROR,
1982                                 (errcode(ERRCODE_UNDEFINED_SCHEMA),
1983                                  errmsg("schema \"%s\" does not exist", nspname)));
1984
1985         aclresult = pg_namespace_aclcheck(namespaceId, GetUserId(), ACL_CREATE);
1986         if (aclresult != ACLCHECK_OK)
1987                 aclcheck_error(aclresult, ACL_KIND_NAMESPACE,
1988                                            nspname);
1989
1990         return namespaceId;
1991 }
1992
1993 /*
1994  * QualifiedNameGetCreationNamespace
1995  *              Given a possibly-qualified name for an object (in List-of-Values
1996  *              format), determine what namespace the object should be created in.
1997  *              Also extract and return the object name (last component of list).
1998  *
1999  * Note: this does not apply any permissions check.  Callers must check
2000  * for CREATE rights on the selected namespace when appropriate.
2001  *
2002  * Note: calling this may result in a CommandCounterIncrement operation,
2003  * if we have to create or clean out the temp namespace.
2004  */
2005 Oid
2006 QualifiedNameGetCreationNamespace(List *names, char **objname_p)
2007 {
2008         char       *schemaname;
2009         Oid                     namespaceId;
2010
2011         /* deconstruct the name list */
2012         DeconstructQualifiedName(names, &schemaname, objname_p);
2013
2014         if (schemaname)
2015         {
2016                 /* check for pg_temp alias */
2017                 if (strcmp(schemaname, "pg_temp") == 0)
2018                 {
2019                         /* Initialize temp namespace if first time through */
2020                         if (!OidIsValid(myTempNamespace))
2021                                 InitTempTableNamespace();
2022                         return myTempNamespace;
2023                 }
2024                 /* use exact schema given */
2025                 namespaceId = GetSysCacheOid(NAMESPACENAME,
2026                                                                          CStringGetDatum(schemaname),
2027                                                                          0, 0, 0);
2028                 if (!OidIsValid(namespaceId))
2029                         ereport(ERROR,
2030                                         (errcode(ERRCODE_UNDEFINED_SCHEMA),
2031                                          errmsg("schema \"%s\" does not exist", schemaname)));
2032                 /* we do not check for USAGE rights here! */
2033         }
2034         else
2035         {
2036                 /* use the default creation namespace */
2037                 recomputeNamespacePath();
2038                 if (activeTempCreationPending)
2039                 {
2040                         /* Need to initialize temp namespace */
2041                         InitTempTableNamespace();
2042                         return myTempNamespace;
2043                 }
2044                 namespaceId = activeCreationNamespace;
2045                 if (!OidIsValid(namespaceId))
2046                         ereport(ERROR,
2047                                         (errcode(ERRCODE_UNDEFINED_SCHEMA),
2048                                          errmsg("no schema has been selected to create in")));
2049         }
2050
2051         return namespaceId;
2052 }
2053
2054 /*
2055  * makeRangeVarFromNameList
2056  *              Utility routine to convert a qualified-name list into RangeVar form.
2057  */
2058 RangeVar *
2059 makeRangeVarFromNameList(List *names)
2060 {
2061         RangeVar   *rel = makeRangeVar(NULL, NULL);
2062
2063         switch (list_length(names))
2064         {
2065                 case 1:
2066                         rel->relname = strVal(linitial(names));
2067                         break;
2068                 case 2:
2069                         rel->schemaname = strVal(linitial(names));
2070                         rel->relname = strVal(lsecond(names));
2071                         break;
2072                 case 3:
2073                         rel->catalogname = strVal(linitial(names));
2074                         rel->schemaname = strVal(lsecond(names));
2075                         rel->relname = strVal(lthird(names));
2076                         break;
2077                 default:
2078                         ereport(ERROR,
2079                                         (errcode(ERRCODE_SYNTAX_ERROR),
2080                                  errmsg("improper relation name (too many dotted names): %s",
2081                                                 NameListToString(names))));
2082                         break;
2083         }
2084
2085         return rel;
2086 }
2087
2088 /*
2089  * NameListToString
2090  *              Utility routine to convert a qualified-name list into a string.
2091  *
2092  * This is used primarily to form error messages, and so we do not quote
2093  * the list elements, for the sake of legibility.
2094  */
2095 char *
2096 NameListToString(List *names)
2097 {
2098         StringInfoData string;
2099         ListCell   *l;
2100
2101         initStringInfo(&string);
2102
2103         foreach(l, names)
2104         {
2105                 if (l != list_head(names))
2106                         appendStringInfoChar(&string, '.');
2107                 appendStringInfoString(&string, strVal(lfirst(l)));
2108         }
2109
2110         return string.data;
2111 }
2112
2113 /*
2114  * NameListToQuotedString
2115  *              Utility routine to convert a qualified-name list into a string.
2116  *
2117  * Same as above except that names will be double-quoted where necessary,
2118  * so the string could be re-parsed (eg, by textToQualifiedNameList).
2119  */
2120 char *
2121 NameListToQuotedString(List *names)
2122 {
2123         StringInfoData string;
2124         ListCell   *l;
2125
2126         initStringInfo(&string);
2127
2128         foreach(l, names)
2129         {
2130                 if (l != list_head(names))
2131                         appendStringInfoChar(&string, '.');
2132                 appendStringInfoString(&string, quote_identifier(strVal(lfirst(l))));
2133         }
2134
2135         return string.data;
2136 }
2137
2138 /*
2139  * isTempNamespace - is the given namespace my temporary-table namespace?
2140  */
2141 bool
2142 isTempNamespace(Oid namespaceId)
2143 {
2144         if (OidIsValid(myTempNamespace) && myTempNamespace == namespaceId)
2145                 return true;
2146         return false;
2147 }
2148
2149 /*
2150  * isTempToastNamespace - is the given namespace my temporary-toast-table
2151  *              namespace?
2152  */
2153 bool
2154 isTempToastNamespace(Oid namespaceId)
2155 {
2156         if (OidIsValid(myTempToastNamespace) && myTempToastNamespace == namespaceId)
2157                 return true;
2158         return false;
2159 }
2160
2161 /*
2162  * isTempOrToastNamespace - is the given namespace my temporary-table
2163  *              namespace or my temporary-toast-table namespace?
2164  */
2165 bool
2166 isTempOrToastNamespace(Oid namespaceId)
2167 {
2168         if (OidIsValid(myTempNamespace) &&
2169                 (myTempNamespace == namespaceId || myTempToastNamespace == namespaceId))
2170                 return true;
2171         return false;
2172 }
2173
2174 /*
2175  * isAnyTempNamespace - is the given namespace a temporary-table namespace
2176  * (either my own, or another backend's)?  Temporary-toast-table namespaces
2177  * are included, too.
2178  */
2179 bool
2180 isAnyTempNamespace(Oid namespaceId)
2181 {
2182         bool            result;
2183         char       *nspname;
2184
2185         /* True if the namespace name starts with "pg_temp_" or "pg_toast_temp_" */
2186         nspname = get_namespace_name(namespaceId);
2187         if (!nspname)
2188                 return false;                   /* no such namespace? */
2189         result = (strncmp(nspname, "pg_temp_", 8) == 0) ||
2190                 (strncmp(nspname, "pg_toast_temp_", 14) == 0);
2191         pfree(nspname);
2192         return result;
2193 }
2194
2195 /*
2196  * isOtherTempNamespace - is the given namespace some other backend's
2197  * temporary-table namespace (including temporary-toast-table namespaces)?
2198  */
2199 bool
2200 isOtherTempNamespace(Oid namespaceId)
2201 {
2202         /* If it's my own temp namespace, say "false" */
2203         if (isTempOrToastNamespace(namespaceId))
2204                 return false;
2205         /* Else, if it's any temp namespace, say "true" */
2206         return isAnyTempNamespace(namespaceId);
2207 }
2208
2209 /*
2210  * GetTempToastNamespace - get the OID of my temporary-toast-table namespace,
2211  * which must already be assigned.  (This is only used when creating a toast
2212  * table for a temp table, so we must have already done InitTempTableNamespace)
2213  */
2214 Oid
2215 GetTempToastNamespace(void)
2216 {
2217         Assert(OidIsValid(myTempToastNamespace));
2218         return myTempToastNamespace;
2219 }
2220
2221
2222 /*
2223  * GetOverrideSearchPath - fetch current search path definition in form
2224  * used by PushOverrideSearchPath.
2225  *
2226  * The result structure is allocated in the specified memory context
2227  * (which might or might not be equal to CurrentMemoryContext); but any
2228  * junk created by revalidation calculations will be in CurrentMemoryContext.
2229  */
2230 OverrideSearchPath *
2231 GetOverrideSearchPath(MemoryContext context)
2232 {
2233         OverrideSearchPath *result;
2234         List       *schemas;
2235         MemoryContext oldcxt;
2236
2237         recomputeNamespacePath();
2238
2239         oldcxt = MemoryContextSwitchTo(context);
2240
2241         result = (OverrideSearchPath *) palloc0(sizeof(OverrideSearchPath));
2242         schemas = list_copy(activeSearchPath);
2243         while (schemas && linitial_oid(schemas) != activeCreationNamespace)
2244         {
2245                 if (linitial_oid(schemas) == myTempNamespace)
2246                         result->addTemp = true;
2247                 else
2248                 {
2249                         Assert(linitial_oid(schemas) == PG_CATALOG_NAMESPACE);
2250                         result->addCatalog = true;
2251                 }
2252                 schemas = list_delete_first(schemas);
2253         }
2254         result->schemas = schemas;
2255
2256         MemoryContextSwitchTo(oldcxt);
2257
2258         return result;
2259 }
2260
2261 /*
2262  * PushOverrideSearchPath - temporarily override the search path
2263  *
2264  * We allow nested overrides, hence the push/pop terminology.  The GUC
2265  * search_path variable is ignored while an override is active.
2266  */
2267 void
2268 PushOverrideSearchPath(OverrideSearchPath *newpath)
2269 {
2270         OverrideStackEntry *entry;
2271         List       *oidlist;
2272         Oid                     firstNS;
2273         MemoryContext oldcxt;
2274
2275         /*
2276          * Copy the list for safekeeping, and insert implicitly-searched
2277          * namespaces as needed.  This code should track recomputeNamespacePath.
2278          */
2279         oldcxt = MemoryContextSwitchTo(TopMemoryContext);
2280
2281         oidlist = list_copy(newpath->schemas);
2282
2283         /*
2284          * Remember the first member of the explicit list.
2285          */
2286         if (oidlist == NIL)
2287                 firstNS = InvalidOid;
2288         else
2289                 firstNS = linitial_oid(oidlist);
2290
2291         /*
2292          * Add any implicitly-searched namespaces to the list.  Note these go on
2293          * the front, not the back; also notice that we do not check USAGE
2294          * permissions for these.
2295          */
2296         if (newpath->addCatalog)
2297                 oidlist = lcons_oid(PG_CATALOG_NAMESPACE, oidlist);
2298
2299         if (newpath->addTemp)
2300         {
2301                 Assert(OidIsValid(myTempNamespace));
2302                 oidlist = lcons_oid(myTempNamespace, oidlist);
2303         }
2304
2305         /*
2306          * Build the new stack entry, then insert it at the head of the list.
2307          */
2308         entry = (OverrideStackEntry *) palloc(sizeof(OverrideStackEntry));
2309         entry->searchPath = oidlist;
2310         entry->creationNamespace = firstNS;
2311         entry->nestLevel = GetCurrentTransactionNestLevel();
2312
2313         overrideStack = lcons(entry, overrideStack);
2314
2315         /* And make it active. */
2316         activeSearchPath = entry->searchPath;
2317         activeCreationNamespace = entry->creationNamespace;
2318         activeTempCreationPending = false;                      /* XXX is this OK? */
2319
2320         MemoryContextSwitchTo(oldcxt);
2321 }
2322
2323 /*
2324  * PopOverrideSearchPath - undo a previous PushOverrideSearchPath
2325  *
2326  * Any push during a (sub)transaction will be popped automatically at abort.
2327  * But it's caller error if a push isn't popped in normal control flow.
2328  */
2329 void
2330 PopOverrideSearchPath(void)
2331 {
2332         OverrideStackEntry *entry;
2333
2334         /* Sanity checks. */
2335         if (overrideStack == NIL)
2336                 elog(ERROR, "bogus PopOverrideSearchPath call");
2337         entry = (OverrideStackEntry *) linitial(overrideStack);
2338         if (entry->nestLevel != GetCurrentTransactionNestLevel())
2339                 elog(ERROR, "bogus PopOverrideSearchPath call");
2340
2341         /* Pop the stack and free storage. */
2342         overrideStack = list_delete_first(overrideStack);
2343         list_free(entry->searchPath);
2344         pfree(entry);
2345
2346         /* Activate the next level down. */
2347         if (overrideStack)
2348         {
2349                 entry = (OverrideStackEntry *) linitial(overrideStack);
2350                 activeSearchPath = entry->searchPath;
2351                 activeCreationNamespace = entry->creationNamespace;
2352                 activeTempCreationPending = false;                      /* XXX is this OK? */
2353         }
2354         else
2355         {
2356                 /* If not baseSearchPathValid, this is useless but harmless */
2357                 activeSearchPath = baseSearchPath;
2358                 activeCreationNamespace = baseCreationNamespace;
2359                 activeTempCreationPending = baseTempCreationPending;
2360         }
2361 }
2362
2363
2364 /*
2365  * FindConversionByName - find a conversion by possibly qualified name
2366  */
2367 Oid
2368 FindConversionByName(List *name)
2369 {
2370         char       *schemaname;
2371         char       *conversion_name;
2372         Oid                     namespaceId;
2373         Oid                     conoid;
2374         ListCell   *l;
2375
2376         /* deconstruct the name list */
2377         DeconstructQualifiedName(name, &schemaname, &conversion_name);
2378
2379         if (schemaname)
2380         {
2381                 /* use exact schema given */
2382                 namespaceId = LookupExplicitNamespace(schemaname);
2383                 return FindConversion(conversion_name, namespaceId);
2384         }
2385         else
2386         {
2387                 /* search for it in search path */
2388                 recomputeNamespacePath();
2389
2390                 foreach(l, activeSearchPath)
2391                 {
2392                         namespaceId = lfirst_oid(l);
2393
2394                         if (namespaceId == myTempNamespace)
2395                                 continue;                       /* do not look in temp namespace */
2396
2397                         conoid = FindConversion(conversion_name, namespaceId);
2398                         if (OidIsValid(conoid))
2399                                 return conoid;
2400                 }
2401         }
2402
2403         /* Not found in path */
2404         return InvalidOid;
2405 }
2406
2407 /*
2408  * FindDefaultConversionProc - find default encoding conversion proc
2409  */
2410 Oid
2411 FindDefaultConversionProc(int4 for_encoding, int4 to_encoding)
2412 {
2413         Oid                     proc;
2414         ListCell   *l;
2415
2416         recomputeNamespacePath();
2417
2418         foreach(l, activeSearchPath)
2419         {
2420                 Oid                     namespaceId = lfirst_oid(l);
2421
2422                 if (namespaceId == myTempNamespace)
2423                         continue;                       /* do not look in temp namespace */
2424
2425                 proc = FindDefaultConversion(namespaceId, for_encoding, to_encoding);
2426                 if (OidIsValid(proc))
2427                         return proc;
2428         }
2429
2430         /* Not found in path */
2431         return InvalidOid;
2432 }
2433
2434 /*
2435  * recomputeNamespacePath - recompute path derived variables if needed.
2436  */
2437 static void
2438 recomputeNamespacePath(void)
2439 {
2440         Oid                     roleid = GetUserId();
2441         char       *rawname;
2442         List       *namelist;
2443         List       *oidlist;
2444         List       *newpath;
2445         ListCell   *l;
2446         bool            temp_missing;
2447         Oid                     firstNS;
2448         MemoryContext oldcxt;
2449
2450         /* Do nothing if an override search spec is active. */
2451         if (overrideStack)
2452                 return;
2453
2454         /* Do nothing if path is already valid. */
2455         if (baseSearchPathValid && namespaceUser == roleid)
2456                 return;
2457
2458         /* Need a modifiable copy of namespace_search_path string */
2459         rawname = pstrdup(namespace_search_path);
2460
2461         /* Parse string into list of identifiers */
2462         if (!SplitIdentifierString(rawname, ',', &namelist))
2463         {
2464                 /* syntax error in name list */
2465                 /* this should not happen if GUC checked check_search_path */
2466                 elog(ERROR, "invalid list syntax");
2467         }
2468
2469         /*
2470          * Convert the list of names to a list of OIDs.  If any names are not
2471          * recognizable or we don't have read access, just leave them out of the
2472          * list.  (We can't raise an error, since the search_path setting has
2473          * already been accepted.)      Don't make duplicate entries, either.
2474          */
2475         oidlist = NIL;
2476         temp_missing = false;
2477         foreach(l, namelist)
2478         {
2479                 char       *curname = (char *) lfirst(l);
2480                 Oid                     namespaceId;
2481
2482                 if (strcmp(curname, "$user") == 0)
2483                 {
2484                         /* $user --- substitute namespace matching user name, if any */
2485                         HeapTuple       tuple;
2486
2487                         tuple = SearchSysCache(AUTHOID,
2488                                                                    ObjectIdGetDatum(roleid),
2489                                                                    0, 0, 0);
2490                         if (HeapTupleIsValid(tuple))
2491                         {
2492                                 char       *rname;
2493
2494                                 rname = NameStr(((Form_pg_authid) GETSTRUCT(tuple))->rolname);
2495                                 namespaceId = GetSysCacheOid(NAMESPACENAME,
2496                                                                                          CStringGetDatum(rname),
2497                                                                                          0, 0, 0);
2498                                 ReleaseSysCache(tuple);
2499                                 if (OidIsValid(namespaceId) &&
2500                                         !list_member_oid(oidlist, namespaceId) &&
2501                                         pg_namespace_aclcheck(namespaceId, roleid,
2502                                                                                   ACL_USAGE) == ACLCHECK_OK)
2503                                         oidlist = lappend_oid(oidlist, namespaceId);
2504                         }
2505                 }
2506                 else if (strcmp(curname, "pg_temp") == 0)
2507                 {
2508                         /* pg_temp --- substitute temp namespace, if any */
2509                         if (OidIsValid(myTempNamespace))
2510                         {
2511                                 if (!list_member_oid(oidlist, myTempNamespace))
2512                                         oidlist = lappend_oid(oidlist, myTempNamespace);
2513                         }
2514                         else
2515                         {
2516                                 /* If it ought to be the creation namespace, set flag */
2517                                 if (oidlist == NIL)
2518                                         temp_missing = true;
2519                         }
2520                 }
2521                 else
2522                 {
2523                         /* normal namespace reference */
2524                         namespaceId = GetSysCacheOid(NAMESPACENAME,
2525                                                                                  CStringGetDatum(curname),
2526                                                                                  0, 0, 0);
2527                         if (OidIsValid(namespaceId) &&
2528                                 !list_member_oid(oidlist, namespaceId) &&
2529                                 pg_namespace_aclcheck(namespaceId, roleid,
2530                                                                           ACL_USAGE) == ACLCHECK_OK)
2531                                 oidlist = lappend_oid(oidlist, namespaceId);
2532                 }
2533         }
2534
2535         /*
2536          * Remember the first member of the explicit list.  (Note: this is
2537          * nominally wrong if temp_missing, but we need it anyway to distinguish
2538          * explicit from implicit mention of pg_catalog.)
2539          */
2540         if (oidlist == NIL)
2541                 firstNS = InvalidOid;
2542         else
2543                 firstNS = linitial_oid(oidlist);
2544
2545         /*
2546          * Add any implicitly-searched namespaces to the list.  Note these go on
2547          * the front, not the back; also notice that we do not check USAGE
2548          * permissions for these.
2549          */
2550         if (!list_member_oid(oidlist, PG_CATALOG_NAMESPACE))
2551                 oidlist = lcons_oid(PG_CATALOG_NAMESPACE, oidlist);
2552
2553         if (OidIsValid(myTempNamespace) &&
2554                 !list_member_oid(oidlist, myTempNamespace))
2555                 oidlist = lcons_oid(myTempNamespace, oidlist);
2556
2557         /*
2558          * Now that we've successfully built the new list of namespace OIDs, save
2559          * it in permanent storage.
2560          */
2561         oldcxt = MemoryContextSwitchTo(TopMemoryContext);
2562         newpath = list_copy(oidlist);
2563         MemoryContextSwitchTo(oldcxt);
2564
2565         /* Now safe to assign to state variables. */
2566         list_free(baseSearchPath);
2567         baseSearchPath = newpath;
2568         baseCreationNamespace = firstNS;
2569         baseTempCreationPending = temp_missing;
2570
2571         /* Mark the path valid. */
2572         baseSearchPathValid = true;
2573         namespaceUser = roleid;
2574
2575         /* And make it active. */
2576         activeSearchPath = baseSearchPath;
2577         activeCreationNamespace = baseCreationNamespace;
2578         activeTempCreationPending = baseTempCreationPending;
2579
2580         /* Clean up. */
2581         pfree(rawname);
2582         list_free(namelist);
2583         list_free(oidlist);
2584 }
2585
2586 /*
2587  * InitTempTableNamespace
2588  *              Initialize temp table namespace on first use in a particular backend
2589  */
2590 static void
2591 InitTempTableNamespace(void)
2592 {
2593         char            namespaceName[NAMEDATALEN];
2594         Oid                     namespaceId;
2595         Oid                     toastspaceId;
2596
2597         Assert(!OidIsValid(myTempNamespace));
2598
2599         /*
2600          * First, do permission check to see if we are authorized to make temp
2601          * tables.      We use a nonstandard error message here since "databasename:
2602          * permission denied" might be a tad cryptic.
2603          *
2604          * Note that ACL_CREATE_TEMP rights are rechecked in pg_namespace_aclmask;
2605          * that's necessary since current user ID could change during the session.
2606          * But there's no need to make the namespace in the first place until a
2607          * temp table creation request is made by someone with appropriate rights.
2608          */
2609         if (pg_database_aclcheck(MyDatabaseId, GetUserId(),
2610                                                          ACL_CREATE_TEMP) != ACLCHECK_OK)
2611                 ereport(ERROR,
2612                                 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
2613                                  errmsg("permission denied to create temporary tables in database \"%s\"",
2614                                                 get_database_name(MyDatabaseId))));
2615
2616         snprintf(namespaceName, sizeof(namespaceName), "pg_temp_%d", MyBackendId);
2617
2618         namespaceId = GetSysCacheOid(NAMESPACENAME,
2619                                                                  CStringGetDatum(namespaceName),
2620                                                                  0, 0, 0);
2621         if (!OidIsValid(namespaceId))
2622         {
2623                 /*
2624                  * First use of this temp namespace in this database; create it. The
2625                  * temp namespaces are always owned by the superuser.  We leave their
2626                  * permissions at default --- i.e., no access except to superuser ---
2627                  * to ensure that unprivileged users can't peek at other backends'
2628                  * temp tables.  This works because the places that access the temp
2629                  * namespace for my own backend skip permissions checks on it.
2630                  */
2631                 namespaceId = NamespaceCreate(namespaceName, BOOTSTRAP_SUPERUSERID);
2632                 /* Advance command counter to make namespace visible */
2633                 CommandCounterIncrement();
2634         }
2635         else
2636         {
2637                 /*
2638                  * If the namespace already exists, clean it out (in case the former
2639                  * owner crashed without doing so).
2640                  */
2641                 RemoveTempRelations(namespaceId);
2642         }
2643
2644         /*
2645          * If the corresponding temp-table namespace doesn't exist yet, create it.
2646          * (We assume there is no need to clean it out if it does exist, since
2647          * dropping a parent table should make its toast table go away.)
2648          */
2649         snprintf(namespaceName, sizeof(namespaceName), "pg_toast_temp_%d",
2650                          MyBackendId);
2651
2652         toastspaceId = GetSysCacheOid(NAMESPACENAME,
2653                                                                   CStringGetDatum(namespaceName),
2654                                                                   0, 0, 0);
2655         if (!OidIsValid(toastspaceId))
2656         {
2657                 toastspaceId = NamespaceCreate(namespaceName, BOOTSTRAP_SUPERUSERID);
2658                 /* Advance command counter to make namespace visible */
2659                 CommandCounterIncrement();
2660         }
2661
2662         /*
2663          * Okay, we've prepared the temp namespace ... but it's not committed yet,
2664          * so all our work could be undone by transaction rollback.  Set flag for
2665          * AtEOXact_Namespace to know what to do.
2666          */
2667         myTempNamespace = namespaceId;
2668         myTempToastNamespace = toastspaceId;
2669
2670         /* It should not be done already. */
2671         AssertState(myTempNamespaceSubID == InvalidSubTransactionId);
2672         myTempNamespaceSubID = GetCurrentSubTransactionId();
2673
2674         baseSearchPathValid = false;    /* need to rebuild list */
2675 }
2676
2677 /*
2678  * End-of-transaction cleanup for namespaces.
2679  */
2680 void
2681 AtEOXact_Namespace(bool isCommit)
2682 {
2683         /*
2684          * If we abort the transaction in which a temp namespace was selected,
2685          * we'll have to do any creation or cleanout work over again.  So, just
2686          * forget the namespace entirely until next time.  On the other hand, if
2687          * we commit then register an exit callback to clean out the temp tables
2688          * at backend shutdown.  (We only want to register the callback once per
2689          * session, so this is a good place to do it.)
2690          */
2691         if (myTempNamespaceSubID != InvalidSubTransactionId)
2692         {
2693                 if (isCommit)
2694                         on_shmem_exit(RemoveTempRelationsCallback, 0);
2695                 else
2696                 {
2697                         myTempNamespace = InvalidOid;
2698                         myTempToastNamespace = InvalidOid;
2699                         baseSearchPathValid = false;    /* need to rebuild list */
2700                 }
2701                 myTempNamespaceSubID = InvalidSubTransactionId;
2702         }
2703
2704         /*
2705          * Clean up if someone failed to do PopOverrideSearchPath
2706          */
2707         if (overrideStack)
2708         {
2709                 if (isCommit)
2710                         elog(WARNING, "leaked override search path");
2711                 while (overrideStack)
2712                 {
2713                         OverrideStackEntry *entry;
2714
2715                         entry = (OverrideStackEntry *) linitial(overrideStack);
2716                         overrideStack = list_delete_first(overrideStack);
2717                         list_free(entry->searchPath);
2718                         pfree(entry);
2719                 }
2720                 /* If not baseSearchPathValid, this is useless but harmless */
2721                 activeSearchPath = baseSearchPath;
2722                 activeCreationNamespace = baseCreationNamespace;
2723                 activeTempCreationPending = baseTempCreationPending;
2724         }
2725 }
2726
2727 /*
2728  * AtEOSubXact_Namespace
2729  *
2730  * At subtransaction commit, propagate the temp-namespace-creation
2731  * flag to the parent subtransaction.
2732  *
2733  * At subtransaction abort, forget the flag if we set it up.
2734  */
2735 void
2736 AtEOSubXact_Namespace(bool isCommit, SubTransactionId mySubid,
2737                                           SubTransactionId parentSubid)
2738 {
2739         OverrideStackEntry *entry;
2740
2741         if (myTempNamespaceSubID == mySubid)
2742         {
2743                 if (isCommit)
2744                         myTempNamespaceSubID = parentSubid;
2745                 else
2746                 {
2747                         myTempNamespaceSubID = InvalidSubTransactionId;
2748                         /* TEMP namespace creation failed, so reset state */
2749                         myTempNamespace = InvalidOid;
2750                         myTempToastNamespace = InvalidOid;
2751                         baseSearchPathValid = false;    /* need to rebuild list */
2752                 }
2753         }
2754
2755         /*
2756          * Clean up if someone failed to do PopOverrideSearchPath
2757          */
2758         while (overrideStack)
2759         {
2760                 entry = (OverrideStackEntry *) linitial(overrideStack);
2761                 if (entry->nestLevel < GetCurrentTransactionNestLevel())
2762                         break;
2763                 if (isCommit)
2764                         elog(WARNING, "leaked override search path");
2765                 overrideStack = list_delete_first(overrideStack);
2766                 list_free(entry->searchPath);
2767                 pfree(entry);
2768         }
2769
2770         /* Activate the next level down. */
2771         if (overrideStack)
2772         {
2773                 entry = (OverrideStackEntry *) linitial(overrideStack);
2774                 activeSearchPath = entry->searchPath;
2775                 activeCreationNamespace = entry->creationNamespace;
2776                 activeTempCreationPending = false;                      /* XXX is this OK? */
2777         }
2778         else
2779         {
2780                 /* If not baseSearchPathValid, this is useless but harmless */
2781                 activeSearchPath = baseSearchPath;
2782                 activeCreationNamespace = baseCreationNamespace;
2783                 activeTempCreationPending = baseTempCreationPending;
2784         }
2785 }
2786
2787 /*
2788  * Remove all relations in the specified temp namespace.
2789  *
2790  * This is called at backend shutdown (if we made any temp relations).
2791  * It is also called when we begin using a pre-existing temp namespace,
2792  * in order to clean out any relations that might have been created by
2793  * a crashed backend.
2794  */
2795 static void
2796 RemoveTempRelations(Oid tempNamespaceId)
2797 {
2798         ObjectAddress object;
2799
2800         /*
2801          * We want to get rid of everything in the target namespace, but not the
2802          * namespace itself (deleting it only to recreate it later would be a
2803          * waste of cycles).  We do this by finding everything that has a
2804          * dependency on the namespace.
2805          */
2806         object.classId = NamespaceRelationId;
2807         object.objectId = tempNamespaceId;
2808         object.objectSubId = 0;
2809
2810         deleteWhatDependsOn(&object, false);
2811 }
2812
2813 /*
2814  * Callback to remove temp relations at backend exit.
2815  */
2816 static void
2817 RemoveTempRelationsCallback(int code, Datum arg)
2818 {
2819         if (OidIsValid(myTempNamespace))        /* should always be true */
2820         {
2821                 /* Need to ensure we have a usable transaction. */
2822                 AbortOutOfAnyTransaction();
2823                 StartTransactionCommand();
2824
2825                 RemoveTempRelations(myTempNamespace);
2826
2827                 CommitTransactionCommand();
2828         }
2829 }
2830
2831 /*
2832  * Remove all temp tables from the temporary namespace.
2833  */
2834 void
2835 ResetTempTableNamespace(void)
2836 {
2837         if (OidIsValid(myTempNamespace))
2838                 RemoveTempRelations(myTempNamespace);
2839 }
2840
2841
2842 /*
2843  * Routines for handling the GUC variable 'search_path'.
2844  */
2845
2846 /* assign_hook: validate new search_path, do extra actions as needed */
2847 const char *
2848 assign_search_path(const char *newval, bool doit, GucSource source)
2849 {
2850         char       *rawname;
2851         List       *namelist;
2852         ListCell   *l;
2853
2854         /* Need a modifiable copy of string */
2855         rawname = pstrdup(newval);
2856
2857         /* Parse string into list of identifiers */
2858         if (!SplitIdentifierString(rawname, ',', &namelist))
2859         {
2860                 /* syntax error in name list */
2861                 pfree(rawname);
2862                 list_free(namelist);
2863                 return NULL;
2864         }
2865
2866         /*
2867          * If we aren't inside a transaction, we cannot do database access so
2868          * cannot verify the individual names.  Must accept the list on faith.
2869          */
2870         if (source >= PGC_S_INTERACTIVE && IsTransactionState())
2871         {
2872                 /*
2873                  * Verify that all the names are either valid namespace names or
2874                  * "$user" or "pg_temp".  We do not require $user to correspond to a
2875                  * valid namespace, and pg_temp might not exist yet.  We do not check
2876                  * for USAGE rights, either; should we?
2877                  *
2878                  * When source == PGC_S_TEST, we are checking the argument of an ALTER
2879                  * DATABASE SET or ALTER USER SET command.      It could be that the
2880                  * intended use of the search path is for some other database, so we
2881                  * should not error out if it mentions schemas not present in the
2882                  * current database.  We reduce the message to NOTICE instead.
2883                  */
2884                 foreach(l, namelist)
2885                 {
2886                         char       *curname = (char *) lfirst(l);
2887
2888                         if (strcmp(curname, "$user") == 0)
2889                                 continue;
2890                         if (strcmp(curname, "pg_temp") == 0)
2891                                 continue;
2892                         if (!SearchSysCacheExists(NAMESPACENAME,
2893                                                                           CStringGetDatum(curname),
2894                                                                           0, 0, 0))
2895                                 ereport((source == PGC_S_TEST) ? NOTICE : ERROR,
2896                                                 (errcode(ERRCODE_UNDEFINED_SCHEMA),
2897                                                  errmsg("schema \"%s\" does not exist", curname)));
2898                 }
2899         }
2900
2901         pfree(rawname);
2902         list_free(namelist);
2903
2904         /*
2905          * We mark the path as needing recomputation, but don't do anything until
2906          * it's needed.  This avoids trying to do database access during GUC
2907          * initialization.
2908          */
2909         if (doit)
2910                 baseSearchPathValid = false;
2911
2912         return newval;
2913 }
2914
2915 /*
2916  * InitializeSearchPath: initialize module during InitPostgres.
2917  *
2918  * This is called after we are up enough to be able to do catalog lookups.
2919  */
2920 void
2921 InitializeSearchPath(void)
2922 {
2923         if (IsBootstrapProcessingMode())
2924         {
2925                 /*
2926                  * In bootstrap mode, the search path must be 'pg_catalog' so that
2927                  * tables are created in the proper namespace; ignore the GUC setting.
2928                  */
2929                 MemoryContext oldcxt;
2930
2931                 oldcxt = MemoryContextSwitchTo(TopMemoryContext);
2932                 baseSearchPath = list_make1_oid(PG_CATALOG_NAMESPACE);
2933                 MemoryContextSwitchTo(oldcxt);
2934                 baseCreationNamespace = PG_CATALOG_NAMESPACE;
2935                 baseTempCreationPending = false;
2936                 baseSearchPathValid = true;
2937                 namespaceUser = GetUserId();
2938                 activeSearchPath = baseSearchPath;
2939                 activeCreationNamespace = baseCreationNamespace;
2940                 activeTempCreationPending = baseTempCreationPending;
2941         }
2942         else
2943         {
2944                 /*
2945                  * In normal mode, arrange for a callback on any syscache invalidation
2946                  * of pg_namespace rows.
2947                  */
2948                 CacheRegisterSyscacheCallback(NAMESPACEOID,
2949                                                                           NamespaceCallback,
2950                                                                           (Datum) 0);
2951                 /* Force search path to be recomputed on next use */
2952                 baseSearchPathValid = false;
2953         }
2954 }
2955
2956 /*
2957  * NamespaceCallback
2958  *              Syscache inval callback function
2959  */
2960 static void
2961 NamespaceCallback(Datum arg, Oid relid)
2962 {
2963         /* Force search path to be recomputed on next use */
2964         baseSearchPathValid = false;
2965 }
2966
2967 /*
2968  * Fetch the active search path. The return value is a palloc'ed list
2969  * of OIDs; the caller is responsible for freeing this storage as
2970  * appropriate.
2971  *
2972  * The returned list includes the implicitly-prepended namespaces only if
2973  * includeImplicit is true.
2974  *
2975  * Note: calling this may result in a CommandCounterIncrement operation,
2976  * if we have to create or clean out the temp namespace.
2977  */
2978 List *
2979 fetch_search_path(bool includeImplicit)
2980 {
2981         List       *result;
2982
2983         recomputeNamespacePath();
2984
2985         /*
2986          * If the temp namespace should be first, force it to exist.  This is
2987          * so that callers can trust the result to reflect the actual default
2988          * creation namespace.  It's a bit bogus to do this here, since
2989          * current_schema() is supposedly a stable function without side-effects,
2990          * but the alternatives seem worse.
2991          */
2992         if (activeTempCreationPending)
2993         {
2994                 InitTempTableNamespace();
2995                 recomputeNamespacePath();
2996         }
2997
2998         result = list_copy(activeSearchPath);
2999         if (!includeImplicit)
3000         {
3001                 while (result && linitial_oid(result) != activeCreationNamespace)
3002                         result = list_delete_first(result);
3003         }
3004
3005         return result;
3006 }
3007
3008 /*
3009  * Export the FooIsVisible functions as SQL-callable functions.
3010  */
3011
3012 Datum
3013 pg_table_is_visible(PG_FUNCTION_ARGS)
3014 {
3015         Oid                     oid = PG_GETARG_OID(0);
3016
3017         PG_RETURN_BOOL(RelationIsVisible(oid));
3018 }
3019
3020 Datum
3021 pg_type_is_visible(PG_FUNCTION_ARGS)
3022 {
3023         Oid                     oid = PG_GETARG_OID(0);
3024
3025         PG_RETURN_BOOL(TypeIsVisible(oid));
3026 }
3027
3028 Datum
3029 pg_function_is_visible(PG_FUNCTION_ARGS)
3030 {
3031         Oid                     oid = PG_GETARG_OID(0);
3032
3033         PG_RETURN_BOOL(FunctionIsVisible(oid));
3034 }
3035
3036 Datum
3037 pg_operator_is_visible(PG_FUNCTION_ARGS)
3038 {
3039         Oid                     oid = PG_GETARG_OID(0);
3040
3041         PG_RETURN_BOOL(OperatorIsVisible(oid));
3042 }
3043
3044 Datum
3045 pg_opclass_is_visible(PG_FUNCTION_ARGS)
3046 {
3047         Oid                     oid = PG_GETARG_OID(0);
3048
3049         PG_RETURN_BOOL(OpclassIsVisible(oid));
3050 }
3051
3052 Datum
3053 pg_conversion_is_visible(PG_FUNCTION_ARGS)
3054 {
3055         Oid                     oid = PG_GETARG_OID(0);
3056
3057         PG_RETURN_BOOL(ConversionIsVisible(oid));
3058 }
3059
3060 Datum
3061 pg_ts_parser_is_visible(PG_FUNCTION_ARGS)
3062 {
3063         Oid                     oid = PG_GETARG_OID(0);
3064
3065         PG_RETURN_BOOL(TSParserIsVisible(oid));
3066 }
3067
3068 Datum
3069 pg_ts_dict_is_visible(PG_FUNCTION_ARGS)
3070 {
3071         Oid                     oid = PG_GETARG_OID(0);
3072
3073         PG_RETURN_BOOL(TSDictionaryIsVisible(oid));
3074 }
3075
3076 Datum
3077 pg_ts_template_is_visible(PG_FUNCTION_ARGS)
3078 {
3079         Oid                     oid = PG_GETARG_OID(0);
3080
3081         PG_RETURN_BOOL(TSTemplateIsVisible(oid));
3082 }
3083
3084 Datum
3085 pg_ts_config_is_visible(PG_FUNCTION_ARGS)
3086 {
3087         Oid                     oid = PG_GETARG_OID(0);
3088
3089         PG_RETURN_BOOL(TSConfigIsVisible(oid));
3090 }
3091
3092 Datum
3093 pg_my_temp_schema(PG_FUNCTION_ARGS)
3094 {
3095         PG_RETURN_OID(myTempNamespace);
3096 }
3097
3098 Datum
3099 pg_is_other_temp_schema(PG_FUNCTION_ARGS)
3100 {
3101         Oid                     oid = PG_GETARG_OID(0);
3102
3103         PG_RETURN_BOOL(isOtherTempNamespace(oid));
3104 }