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