]> granicus.if.org Git - postgresql/blob - src/backend/catalog/namespace.c
Restructure aclcheck error reporting to make permission-failure
[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-2001, PostgreSQL Global Development Group
13  * Portions Copyright (c) 1994, Regents of the University of California
14  *
15  * IDENTIFICATION
16  *        $Header: /cvsroot/pgsql/src/backend/catalog/namespace.c,v 1.14 2002/04/27 03:45:00 tgl Exp $
17  *
18  *-------------------------------------------------------------------------
19  */
20 #include "postgres.h"
21
22 #include "access/heapam.h"
23 #include "access/xact.h"
24 #include "catalog/catalog.h"
25 #include "catalog/catname.h"
26 #include "catalog/heap.h"
27 #include "catalog/namespace.h"
28 #include "catalog/pg_inherits.h"
29 #include "catalog/pg_namespace.h"
30 #include "catalog/pg_opclass.h"
31 #include "catalog/pg_operator.h"
32 #include "catalog/pg_proc.h"
33 #include "catalog/pg_shadow.h"
34 #include "lib/stringinfo.h"
35 #include "miscadmin.h"
36 #include "nodes/makefuncs.h"
37 #include "storage/backendid.h"
38 #include "utils/acl.h"
39 #include "utils/builtins.h"
40 #include "utils/fmgroids.h"
41 #include "utils/guc.h"
42 #include "utils/catcache.h"
43 #include "utils/lsyscache.h"
44 #include "utils/syscache.h"
45
46
47 /*
48  * The namespace search path is a possibly-empty list of namespace OIDs.
49  * In addition to the explicit list, the TEMP table namespace is always
50  * implicitly searched first (if it's been initialized).  Also, the system
51  * catalog namespace is always searched.  If the system namespace is
52  * explicitly present in the path then it will be searched in the specified
53  * order; otherwise it will be searched after TEMP tables and *before* the
54  * explicit list.  (It might seem that the system namespace should be
55  * implicitly last, but this behavior appears to be required by SQL99.
56  * Also, this provides a way to search the system namespace first without
57  * thereby making it the default creation target namespace.)
58  *
59  * The default creation target namespace is kept equal to the first element
60  * of the (explicit) list.  If the list is empty, there is no default target.
61  *
62  * In bootstrap mode, the search path is set equal to 'pg_catalog', so that
63  * the system namespace is the only one searched or inserted into.
64  * The initdb script is also careful to set search_path to 'pg_catalog' for
65  * its post-bootstrap standalone backend runs.  Otherwise the default search
66  * path is determined by GUC.  The factory default path contains the PUBLIC
67  * namespace (if it exists), preceded by the user's personal namespace
68  * (if one exists).
69  */
70
71 static List *namespaceSearchPath = NIL;
72
73 /* this flag must be updated correctly when namespaceSearchPath is changed */
74 static bool pathContainsSystemNamespace = false;
75
76 /* default place to create stuff; if InvalidOid, no default */
77 static Oid      defaultCreationNamespace = InvalidOid;
78
79 /*
80  * myTempNamespace is InvalidOid until and unless a TEMP namespace is set up
81  * in a particular backend session (this happens when a CREATE TEMP TABLE
82  * command is first executed).  Thereafter it's the OID of the temp namespace.
83  */
84 static Oid      myTempNamespace = InvalidOid;
85
86 /*
87  * This is the text equivalent of the search path --- it's the value
88  * of the GUC variable 'search_path'.
89  */
90 char *namespace_search_path = NULL;
91
92
93 /*
94  * Deletion ordering constraint item.
95  */
96 typedef struct DelConstraint
97 {
98         Oid                     referencer;             /* table to delete first */
99         Oid                     referencee;             /* table to delete second */
100         int                     pred;                   /* workspace for TopoSortRels */
101         struct DelConstraint *link;     /* workspace for TopoSortRels */
102 } DelConstraint;
103
104
105 /* Local functions */
106 static Oid      GetTempTableNamespace(void);
107 static void RemoveTempRelations(Oid tempNamespaceId);
108 static List *FindTempRelations(Oid tempNamespaceId);
109 static List *FindDeletionConstraints(List *relOids);
110 static List *TopoSortRels(List *relOids, List *constraintList);
111 static void RemoveTempRelationsCallback(void);
112
113
114 /*
115  * RangeVarGetRelid
116  *              Given a RangeVar describing an existing relation,
117  *              select the proper namespace and look up the relation OID.
118  *
119  * If the relation is not found, return InvalidOid if failOK = true,
120  * otherwise raise an error.
121  */
122 Oid
123 RangeVarGetRelid(const RangeVar *relation, bool failOK)
124 {
125         Oid                     namespaceId;
126         Oid                     relId;
127
128         /*
129          * We check the catalog name and then ignore it.
130          */
131         if (relation->catalogname)
132         {
133                 if (strcmp(relation->catalogname, DatabaseName) != 0)
134                         elog(ERROR, "Cross-database references are not implemented");
135         }
136
137         if (relation->schemaname)
138         {
139                 /* use exact schema given */
140                 namespaceId = GetSysCacheOid(NAMESPACENAME,
141                                                                          CStringGetDatum(relation->schemaname),
142                                                                          0, 0, 0);
143                 if (!OidIsValid(namespaceId))
144                         elog(ERROR, "Namespace \"%s\" does not exist",
145                                  relation->schemaname);
146                 relId = get_relname_relid(relation->relname, namespaceId);
147         }
148         else
149         {
150                 /* search the namespace path */
151                 relId = RelnameGetRelid(relation->relname);
152         }
153
154         if (!OidIsValid(relId) && !failOK)
155         {
156                 if (relation->schemaname)
157                         elog(ERROR, "Relation \"%s\".\"%s\" does not exist",
158                                  relation->schemaname, relation->relname);
159                 else
160                         elog(ERROR, "Relation \"%s\" does not exist",
161                                  relation->relname);
162         }
163         return relId;
164 }
165
166 /*
167  * RangeVarGetCreationNamespace
168  *              Given a RangeVar describing a to-be-created relation,
169  *              choose which namespace to create it in.
170  *
171  * Note: calling this may result in a CommandCounterIncrement operation.
172  * That will happen on the first request for a temp table in any particular
173  * backend run; we will need to either create or clean out the temp schema.
174  */
175 Oid
176 RangeVarGetCreationNamespace(const RangeVar *newRelation)
177 {
178         Oid                     namespaceId;
179
180         /*
181          * We check the catalog name and then ignore it.
182          */
183         if (newRelation->catalogname)
184         {
185                 if (strcmp(newRelation->catalogname, DatabaseName) != 0)
186                         elog(ERROR, "Cross-database references are not implemented");
187         }
188
189         if (newRelation->istemp)
190         {
191                 /* TEMP tables are created in our backend-local temp namespace */
192                 if (newRelation->schemaname)
193                         elog(ERROR, "TEMP tables may not specify a namespace");
194                 /* Initialize temp namespace if first time through */
195                 if (!OidIsValid(myTempNamespace))
196                         myTempNamespace = GetTempTableNamespace();
197                 return myTempNamespace;
198         }
199
200         if (newRelation->schemaname)
201         {
202                 /* use exact schema given */
203                 namespaceId = GetSysCacheOid(NAMESPACENAME,
204                                                                          CStringGetDatum(newRelation->schemaname),
205                                                                          0, 0, 0);
206                 if (!OidIsValid(namespaceId))
207                         elog(ERROR, "Namespace \"%s\" does not exist",
208                                  newRelation->schemaname);
209         }
210         else
211         {
212                 /* use the default creation namespace */
213                 namespaceId = defaultCreationNamespace;
214                 if (!OidIsValid(namespaceId))
215                         elog(ERROR, "No namespace has been selected to create in");
216         }
217
218         return namespaceId;
219 }
220
221 /*
222  * RelnameGetRelid
223  *              Try to resolve an unqualified relation name.
224  *              Returns OID if relation found in search path, else InvalidOid.
225  */
226 Oid
227 RelnameGetRelid(const char *relname)
228 {
229         Oid                     relid;
230         List       *lptr;
231
232         /*
233          * If a TEMP-table namespace has been set up, it is implicitly first
234          * in the search path.
235          */
236         if (OidIsValid(myTempNamespace))
237         {
238                 relid = get_relname_relid(relname, myTempNamespace);
239                 if (OidIsValid(relid))
240                         return relid;
241         }
242
243         /*
244          * If system namespace is not in path, implicitly search it before path
245          */
246         if (!pathContainsSystemNamespace)
247         {
248                 relid = get_relname_relid(relname, PG_CATALOG_NAMESPACE);
249                 if (OidIsValid(relid))
250                         return relid;
251         }
252
253         /*
254          * Else search the path
255          */
256         foreach(lptr, namespaceSearchPath)
257         {
258                 Oid                     namespaceId = (Oid) lfirsti(lptr);
259
260                 relid = get_relname_relid(relname, namespaceId);
261                 if (OidIsValid(relid))
262                         return relid;
263         }
264
265         /* Not found in path */
266         return InvalidOid;
267 }
268
269 /*
270  * TypenameGetTypid
271  *              Try to resolve an unqualified datatype name.
272  *              Returns OID if type found in search path, else InvalidOid.
273  *
274  * This is essentially the same as RelnameGetRelid, but we never search
275  * the TEMP table namespace --- there is no reason to refer to the types
276  * of temp tables, AFAICS.
277  */
278 Oid
279 TypenameGetTypid(const char *typname)
280 {
281         Oid                     typid;
282         List       *lptr;
283
284         /*
285          * If system namespace is not in path, implicitly search it before path
286          */
287         if (!pathContainsSystemNamespace)
288         {
289                 typid = GetSysCacheOid(TYPENAMENSP,
290                                                            PointerGetDatum(typname),
291                                                            ObjectIdGetDatum(PG_CATALOG_NAMESPACE),
292                                                            0, 0);
293                 if (OidIsValid(typid))
294                         return typid;
295         }
296
297         /*
298          * Else search the path
299          */
300         foreach(lptr, namespaceSearchPath)
301         {
302                 Oid                     namespaceId = (Oid) lfirsti(lptr);
303
304                 typid = GetSysCacheOid(TYPENAMENSP,
305                                                            PointerGetDatum(typname),
306                                                            ObjectIdGetDatum(namespaceId),
307                                                            0, 0);
308                 if (OidIsValid(typid))
309                         return typid;
310         }
311
312         /* Not found in path */
313         return InvalidOid;
314 }
315
316 /*
317  * OpclassnameGetOpcid
318  *              Try to resolve an unqualified index opclass name.
319  *              Returns OID if opclass found in search path, else InvalidOid.
320  *
321  * This is essentially the same as TypenameGetTypid, but we have to have
322  * an extra argument for the index AM OID.
323  */
324 Oid
325 OpclassnameGetOpcid(Oid amid, const char *opcname)
326 {
327         Oid                     opcid;
328         List       *lptr;
329
330         /*
331          * If system namespace is not in path, implicitly search it before path
332          */
333         if (!pathContainsSystemNamespace)
334         {
335                 opcid = GetSysCacheOid(CLAAMNAMENSP,
336                                                            ObjectIdGetDatum(amid),
337                                                            PointerGetDatum(opcname),
338                                                            ObjectIdGetDatum(PG_CATALOG_NAMESPACE),
339                                                            0);
340                 if (OidIsValid(opcid))
341                         return opcid;
342         }
343
344         /*
345          * Else search the path
346          */
347         foreach(lptr, namespaceSearchPath)
348         {
349                 Oid                     namespaceId = (Oid) lfirsti(lptr);
350
351                 opcid = GetSysCacheOid(CLAAMNAMENSP,
352                                                            ObjectIdGetDatum(amid),
353                                                            PointerGetDatum(opcname),
354                                                            ObjectIdGetDatum(namespaceId),
355                                                            0);
356                 if (OidIsValid(opcid))
357                         return opcid;
358         }
359
360         /* Not found in path */
361         return InvalidOid;
362 }
363
364 /*
365  * FuncnameGetCandidates
366  *              Given a possibly-qualified function name and argument count,
367  *              retrieve a list of the possible matches.
368  *
369  * If nargs is -1, we return all functions matching the given name,
370  * regardless of argument count.
371  *
372  * We search a single namespace if the function name is qualified, else
373  * all namespaces in the search path.  The return list will never contain
374  * multiple entries with identical argument lists --- in the multiple-
375  * namespace case, we arrange for entries in earlier namespaces to mask
376  * identical entries in later namespaces.
377  */
378 FuncCandidateList
379 FuncnameGetCandidates(List *names, int nargs)
380 {
381         FuncCandidateList resultList = NULL;
382         char       *catalogname;
383         char       *schemaname = NULL;
384         char       *funcname = NULL;
385         Oid                     namespaceId;
386         CatCList   *catlist;
387         int                     i;
388
389         /* deconstruct the name list */
390         switch (length(names))
391         {
392                 case 1:
393                         funcname = strVal(lfirst(names));
394                         break;
395                 case 2:
396                         schemaname = strVal(lfirst(names));
397                         funcname = strVal(lsecond(names));
398                         break;
399                 case 3:
400                         catalogname = strVal(lfirst(names));
401                         schemaname = strVal(lsecond(names));
402                         funcname = strVal(lfirst(lnext(lnext(names))));
403                         /*
404                          * We check the catalog name and then ignore it.
405                          */
406                         if (strcmp(catalogname, DatabaseName) != 0)
407                                 elog(ERROR, "Cross-database references are not implemented");
408                         break;
409                 default:
410                         elog(ERROR, "Improper qualified name (too many dotted names)");
411                         break;
412         }
413
414         if (schemaname)
415         {
416                 /* use exact schema given */
417                 namespaceId = GetSysCacheOid(NAMESPACENAME,
418                                                                          CStringGetDatum(schemaname),
419                                                                          0, 0, 0);
420                 if (!OidIsValid(namespaceId))
421                         elog(ERROR, "Namespace \"%s\" does not exist",
422                                  schemaname);
423         }
424         else
425         {
426                 /* flag to indicate we need namespace search */
427                 namespaceId = InvalidOid;
428         }
429
430         /* Search syscache by name and (optionally) nargs only */
431         if (nargs >= 0)
432                 catlist = SearchSysCacheList(PROCNAMENSP, 2,
433                                                                          CStringGetDatum(funcname),
434                                                                          Int16GetDatum(nargs),
435                                                                          0, 0);
436         else
437                 catlist = SearchSysCacheList(PROCNAMENSP, 1,
438                                                                          CStringGetDatum(funcname),
439                                                                          0, 0, 0);
440
441         for (i = 0; i < catlist->n_members; i++)
442         {
443                 HeapTuple       proctup = &catlist->members[i]->tuple;
444                 Form_pg_proc procform = (Form_pg_proc) GETSTRUCT(proctup);
445                 int                     pathpos = 0;
446                 FuncCandidateList newResult;
447
448                 nargs = procform->pronargs;
449
450                 if (OidIsValid(namespaceId))
451                 {
452                         /* Consider only procs in specified namespace */
453                         if (procform->pronamespace != namespaceId)
454                                 continue;
455                         /* No need to check args, they must all be different */
456                 }
457                 else
458                 {
459                         /* Consider only procs that are in the search path */
460                         if (pathContainsSystemNamespace ||
461                                 !IsSystemNamespace(procform->pronamespace))
462                         {
463                                 List       *nsp;
464
465                                 foreach(nsp, namespaceSearchPath)
466                                 {
467                                         pathpos++;
468                                         if (procform->pronamespace == (Oid) lfirsti(nsp))
469                                                 break;
470                                 }
471                                 if (nsp == NIL)
472                                         continue;       /* proc is not in search path */
473                         }
474
475                         /*
476                          * Okay, it's in the search path, but does it have the same
477                          * arguments as something we already accepted?  If so, keep
478                          * only the one that appears earlier in the search path.
479                          *
480                          * If we have an ordered list from SearchSysCacheList (the
481                          * normal case), then any conflicting proc must immediately
482                          * adjoin this one in the list, so we only need to look at
483                          * the newest result item.  If we have an unordered list,
484                          * we have to scan the whole result list.
485                          */
486                         if (resultList)
487                         {
488                                 FuncCandidateList       prevResult;
489
490                                 if (catlist->ordered)
491                                 {
492                                         if (nargs == resultList->nargs &&
493                                                 memcmp(procform->proargtypes, resultList->args,
494                                                            nargs * sizeof(Oid)) == 0)
495                                                 prevResult = resultList;
496                                         else
497                                                 prevResult = NULL;
498                                 }
499                                 else
500                                 {
501                                         for (prevResult = resultList;
502                                                  prevResult;
503                                                  prevResult = prevResult->next)
504                                         {
505                                                 if (nargs == prevResult->nargs &&
506                                                         memcmp(procform->proargtypes, prevResult->args,
507                                                                    nargs * sizeof(Oid)) == 0)
508                                                         break;
509                                         }
510                                 }
511                                 if (prevResult)
512                                 {
513                                         /* We have a match with a previous result */
514                                         Assert(pathpos != prevResult->pathpos);
515                                         if (pathpos > prevResult->pathpos)
516                                                 continue; /* keep previous result */
517                                         /* replace previous result */
518                                         prevResult->pathpos = pathpos;
519                                         prevResult->oid = proctup->t_data->t_oid;
520                                         continue;       /* args are same, of course */
521                                 }
522                         }
523                 }
524
525                 /*
526                  * Okay to add it to result list
527                  */
528                 newResult = (FuncCandidateList)
529                         palloc(sizeof(struct _FuncCandidateList) - sizeof(Oid)
530                                    + nargs * sizeof(Oid));
531                 newResult->pathpos = pathpos;
532                 newResult->oid = proctup->t_data->t_oid;
533                 newResult->nargs = nargs;
534                 memcpy(newResult->args, procform->proargtypes, nargs * sizeof(Oid));
535
536                 newResult->next = resultList;
537                 resultList = newResult;
538         }
539
540         ReleaseSysCacheList(catlist);
541
542         return resultList;
543 }
544
545 /*
546  * OpernameGetCandidates
547  *              Given a possibly-qualified operator name and operator kind,
548  *              retrieve a list of the possible matches.
549  *
550  * If oprkind is '\0', we return all operators matching the given name,
551  * regardless of arguments.
552  *
553  * We search a single namespace if the operator name is qualified, else
554  * all namespaces in the search path.  The return list will never contain
555  * multiple entries with identical argument lists --- in the multiple-
556  * namespace case, we arrange for entries in earlier namespaces to mask
557  * identical entries in later namespaces.
558  *
559  * The returned items always have two args[] entries --- one or the other
560  * will be InvalidOid for a prefix or postfix oprkind.  nargs is 2, too.
561  */
562 FuncCandidateList
563 OpernameGetCandidates(List *names, char oprkind)
564 {
565         FuncCandidateList resultList = NULL;
566         char       *catalogname;
567         char       *schemaname = NULL;
568         char       *opername = NULL;
569         Oid                     namespaceId;
570         CatCList   *catlist;
571         int                     i;
572
573         /* deconstruct the name list */
574         switch (length(names))
575         {
576                 case 1:
577                         opername = strVal(lfirst(names));
578                         break;
579                 case 2:
580                         schemaname = strVal(lfirst(names));
581                         opername = strVal(lsecond(names));
582                         break;
583                 case 3:
584                         catalogname = strVal(lfirst(names));
585                         schemaname = strVal(lsecond(names));
586                         opername = strVal(lfirst(lnext(lnext(names))));
587                         /*
588                          * We check the catalog name and then ignore it.
589                          */
590                         if (strcmp(catalogname, DatabaseName) != 0)
591                                 elog(ERROR, "Cross-database references are not implemented");
592                         break;
593                 default:
594                         elog(ERROR, "Improper qualified name (too many dotted names)");
595                         break;
596         }
597
598         if (schemaname)
599         {
600                 /* use exact schema given */
601                 namespaceId = GetSysCacheOid(NAMESPACENAME,
602                                                                          CStringGetDatum(schemaname),
603                                                                          0, 0, 0);
604                 if (!OidIsValid(namespaceId))
605                         elog(ERROR, "Namespace \"%s\" does not exist",
606                                  schemaname);
607         }
608         else
609         {
610                 /* flag to indicate we need namespace search */
611                 namespaceId = InvalidOid;
612         }
613
614         /* Search syscache by name only */
615         catlist = SearchSysCacheList(OPERNAMENSP, 1,
616                                                                  CStringGetDatum(opername),
617                                                                  0, 0, 0);
618
619         for (i = 0; i < catlist->n_members; i++)
620         {
621                 HeapTuple       opertup = &catlist->members[i]->tuple;
622                 Form_pg_operator operform = (Form_pg_operator) GETSTRUCT(opertup);
623                 int                     pathpos = 0;
624                 FuncCandidateList newResult;
625
626                 /* Ignore operators of wrong kind, if specific kind requested */
627                 if (oprkind && operform->oprkind != oprkind)
628                         continue;
629
630                 if (OidIsValid(namespaceId))
631                 {
632                         /* Consider only opers in specified namespace */
633                         if (operform->oprnamespace != namespaceId)
634                                 continue;
635                         /* No need to check args, they must all be different */
636                 }
637                 else
638                 {
639                         /* Consider only opers that are in the search path */
640                         if (pathContainsSystemNamespace ||
641                                 !IsSystemNamespace(operform->oprnamespace))
642                         {
643                                 List       *nsp;
644
645                                 foreach(nsp, namespaceSearchPath)
646                                 {
647                                         pathpos++;
648                                         if (operform->oprnamespace == (Oid) lfirsti(nsp))
649                                                 break;
650                                 }
651                                 if (nsp == NIL)
652                                         continue;       /* oper is not in search path */
653                         }
654
655                         /*
656                          * Okay, it's in the search path, but does it have the same
657                          * arguments as something we already accepted?  If so, keep
658                          * only the one that appears earlier in the search path.
659                          *
660                          * If we have an ordered list from SearchSysCacheList (the
661                          * normal case), then any conflicting oper must immediately
662                          * adjoin this one in the list, so we only need to look at
663                          * the newest result item.  If we have an unordered list,
664                          * we have to scan the whole result list.
665                          */
666                         if (resultList)
667                         {
668                                 FuncCandidateList       prevResult;
669
670                                 if (catlist->ordered)
671                                 {
672                                         if (operform->oprleft == resultList->args[0] &&
673                                                 operform->oprright == resultList->args[1])
674                                                 prevResult = resultList;
675                                         else
676                                                 prevResult = NULL;
677                                 }
678                                 else
679                                 {
680                                         for (prevResult = resultList;
681                                                  prevResult;
682                                                  prevResult = prevResult->next)
683                                         {
684                                                 if (operform->oprleft == prevResult->args[0] &&
685                                                         operform->oprright == prevResult->args[1])
686                                                         break;
687                                         }
688                                 }
689                                 if (prevResult)
690                                 {
691                                         /* We have a match with a previous result */
692                                         Assert(pathpos != prevResult->pathpos);
693                                         if (pathpos > prevResult->pathpos)
694                                                 continue; /* keep previous result */
695                                         /* replace previous result */
696                                         prevResult->pathpos = pathpos;
697                                         prevResult->oid = opertup->t_data->t_oid;
698                                         continue;       /* args are same, of course */
699                                 }
700                         }
701                 }
702
703                 /*
704                  * Okay to add it to result list
705                  */
706                 newResult = (FuncCandidateList)
707                         palloc(sizeof(struct _FuncCandidateList) + sizeof(Oid));
708                 newResult->pathpos = pathpos;
709                 newResult->oid = opertup->t_data->t_oid;
710                 newResult->nargs = 2;
711                 newResult->args[0] = operform->oprleft;
712                 newResult->args[1] = operform->oprright;
713                 newResult->next = resultList;
714                 resultList = newResult;
715         }
716
717         ReleaseSysCacheList(catlist);
718
719         return resultList;
720 }
721
722 /*
723  * OpclassGetCandidates
724  *              Given an index access method OID, retrieve a list of all the
725  *              opclasses for that AM that are visible in the search path.
726  *
727  * NOTE: the opcname_tmp field in the returned structs should not be used
728  * by callers, because it points at syscache entries that we release at
729  * the end of this routine.  If any callers needed the name information,
730  * we could pstrdup() the names ... but at present it'd be wasteful.
731  */
732 OpclassCandidateList
733 OpclassGetCandidates(Oid amid)
734 {
735         OpclassCandidateList resultList = NULL;
736         CatCList   *catlist;
737         int                     i;
738
739         /* Search syscache by AM OID only */
740         catlist = SearchSysCacheList(CLAAMNAMENSP, 1,
741                                                                  ObjectIdGetDatum(amid),
742                                                                  0, 0, 0);
743
744         for (i = 0; i < catlist->n_members; i++)
745         {
746                 HeapTuple       opctup = &catlist->members[i]->tuple;
747                 Form_pg_opclass opcform = (Form_pg_opclass) GETSTRUCT(opctup);
748                 int                     pathpos = 0;
749                 OpclassCandidateList newResult;
750
751                 /* Consider only opclasses that are in the search path */
752                 if (pathContainsSystemNamespace ||
753                         !IsSystemNamespace(opcform->opcnamespace))
754                 {
755                         List       *nsp;
756
757                         foreach(nsp, namespaceSearchPath)
758                         {
759                                 pathpos++;
760                                 if (opcform->opcnamespace == (Oid) lfirsti(nsp))
761                                         break;
762                         }
763                         if (nsp == NIL)
764                                 continue;               /* opclass is not in search path */
765                 }
766
767                 /*
768                  * Okay, it's in the search path, but does it have the same name
769                  * as something we already accepted?  If so, keep
770                  * only the one that appears earlier in the search path.
771                  *
772                  * If we have an ordered list from SearchSysCacheList (the
773                  * normal case), then any conflicting opclass must immediately
774                  * adjoin this one in the list, so we only need to look at
775                  * the newest result item.  If we have an unordered list,
776                  * we have to scan the whole result list.
777                  */
778                 if (resultList)
779                 {
780                         OpclassCandidateList    prevResult;
781
782                         if (catlist->ordered)
783                         {
784                                 if (strcmp(NameStr(opcform->opcname),
785                                                    resultList->opcname_tmp) == 0)
786                                         prevResult = resultList;
787                                 else
788                                         prevResult = NULL;
789                         }
790                         else
791                         {
792                                 for (prevResult = resultList;
793                                          prevResult;
794                                          prevResult = prevResult->next)
795                                 {
796                                         if (strcmp(NameStr(opcform->opcname),
797                                                            prevResult->opcname_tmp) == 0)
798                                                 break;
799                                 }
800                         }
801                         if (prevResult)
802                         {
803                                 /* We have a match with a previous result */
804                                 Assert(pathpos != prevResult->pathpos);
805                                 if (pathpos > prevResult->pathpos)
806                                         continue; /* keep previous result */
807                                 /* replace previous result */
808                                 prevResult->opcname_tmp = NameStr(opcform->opcname);
809                                 prevResult->pathpos = pathpos;
810                                 prevResult->oid = opctup->t_data->t_oid;
811                                 prevResult->opcintype = opcform->opcintype;
812                                 prevResult->opcdefault = opcform->opcdefault;
813                                 prevResult->opckeytype = opcform->opckeytype;
814                                 continue;
815                         }
816                 }
817
818                 /*
819                  * Okay to add it to result list
820                  */
821                 newResult = (OpclassCandidateList)
822                         palloc(sizeof(struct _OpclassCandidateList));
823                 newResult->opcname_tmp = NameStr(opcform->opcname);
824                 newResult->pathpos = pathpos;
825                 newResult->oid = opctup->t_data->t_oid;
826                 newResult->opcintype = opcform->opcintype;
827                 newResult->opcdefault = opcform->opcdefault;
828                 newResult->opckeytype = opcform->opckeytype;
829                 newResult->next = resultList;
830                 resultList = newResult;
831         }
832
833         ReleaseSysCacheList(catlist);
834
835         return resultList;
836 }
837
838
839 /*
840  * QualifiedNameGetCreationNamespace
841  *              Given a possibly-qualified name for an object (in List-of-Values
842  *              format), determine what namespace the object should be created in.
843  *              Also extract and return the object name (last component of list).
844  *
845  * This is *not* used for tables.  Hence, the TEMP table namespace is
846  * never selected as the creation target.
847  */
848 Oid
849 QualifiedNameGetCreationNamespace(List *names, char **objname_p)
850 {
851         char       *catalogname;
852         char       *schemaname = NULL;
853         char       *objname = NULL;
854         Oid                     namespaceId;
855
856         /* deconstruct the name list */
857         switch (length(names))
858         {
859                 case 1:
860                         objname = strVal(lfirst(names));
861                         break;
862                 case 2:
863                         schemaname = strVal(lfirst(names));
864                         objname = strVal(lsecond(names));
865                         break;
866                 case 3:
867                         catalogname = strVal(lfirst(names));
868                         schemaname = strVal(lsecond(names));
869                         objname = strVal(lfirst(lnext(lnext(names))));
870                         /*
871                          * We check the catalog name and then ignore it.
872                          */
873                         if (strcmp(catalogname, DatabaseName) != 0)
874                                 elog(ERROR, "Cross-database references are not implemented");
875                         break;
876                 default:
877                         elog(ERROR, "Improper qualified name (too many dotted names)");
878                         break;
879         }
880
881         if (schemaname)
882         {
883                 /* use exact schema given */
884                 namespaceId = GetSysCacheOid(NAMESPACENAME,
885                                                                          CStringGetDatum(schemaname),
886                                                                          0, 0, 0);
887                 if (!OidIsValid(namespaceId))
888                         elog(ERROR, "Namespace \"%s\" does not exist",
889                                  schemaname);
890         }
891         else
892         {
893                 /* use the default creation namespace */
894                 namespaceId = defaultCreationNamespace;
895                 if (!OidIsValid(namespaceId))
896                         elog(ERROR, "No namespace has been selected to create in");
897         }
898
899         *objname_p = objname;
900         return namespaceId;
901 }
902
903 /*
904  * makeRangeVarFromNameList
905  *              Utility routine to convert a qualified-name list into RangeVar form.
906  */
907 RangeVar *
908 makeRangeVarFromNameList(List *names)
909 {
910         RangeVar   *rel = makeRangeVar(NULL, NULL);
911
912         switch (length(names))
913         {
914                 case 1:
915                         rel->relname = strVal(lfirst(names));
916                         break;
917                 case 2:
918                         rel->schemaname = strVal(lfirst(names));
919                         rel->relname = strVal(lsecond(names));
920                         break;
921                 case 3:
922                         rel->catalogname = strVal(lfirst(names));
923                         rel->schemaname = strVal(lsecond(names));
924                         rel->relname = strVal(lfirst(lnext(lnext(names))));
925                         break;
926                 default:
927                         elog(ERROR, "Improper relation name (too many dotted names)");
928                         break;
929         }
930
931         return rel;
932 }
933
934 /*
935  * NameListToString
936  *              Utility routine to convert a qualified-name list into a string.
937  *              Used primarily to form error messages.
938  */
939 char *
940 NameListToString(List *names)
941 {
942         StringInfoData string;
943         List            *l;
944
945         initStringInfo(&string);
946
947         foreach(l, names)
948         {
949                 if (l != names)
950                         appendStringInfoChar(&string, '.');
951                 appendStringInfo(&string, "%s", strVal(lfirst(l)));
952         }
953
954         return string.data;
955 }
956
957 /*
958  * isTempNamespace - is the given namespace my temporary-table namespace?
959  */
960 bool
961 isTempNamespace(Oid namespaceId)
962 {
963         if (OidIsValid(myTempNamespace) && myTempNamespace == namespaceId)
964                 return true;
965         return false;
966 }
967
968 /*
969  * GetTempTableNamespace
970  *              Initialize temp table namespace on first use in a particular backend
971  */
972 static Oid
973 GetTempTableNamespace(void)
974 {
975         char            namespaceName[NAMEDATALEN];
976         Oid                     namespaceId;
977
978         /*
979          * First, do permission check to see if we are authorized to make
980          * temp tables.  We use a nonstandard error message here since
981          * "databasename: permission denied" might be a tad cryptic.
982          */
983         if (pg_database_aclcheck(MyDatabaseId, GetUserId(),
984                                                          ACL_CREATE_TEMP) != ACLCHECK_OK)
985                 elog(ERROR, "%s: not authorized to create temp tables",
986                          DatabaseName);
987
988         snprintf(namespaceName, NAMEDATALEN, "pg_temp_%d", MyBackendId);
989
990         namespaceId = GetSysCacheOid(NAMESPACENAME,
991                                                                  CStringGetDatum(namespaceName),
992                                                                  0, 0, 0);
993         if (!OidIsValid(namespaceId))
994         {
995                 /*
996                  * First use of this temp namespace in this database; create it.
997                  * The temp namespaces are always owned by the superuser.
998                  */
999                 namespaceId = NamespaceCreate(namespaceName, BOOTSTRAP_USESYSID);
1000                 /* Advance command counter to make namespace visible */
1001                 CommandCounterIncrement();
1002         }
1003         else
1004         {
1005                 /*
1006                  * If the namespace already exists, clean it out (in case the
1007                  * former owner crashed without doing so).
1008                  */
1009                 RemoveTempRelations(namespaceId);
1010         }
1011
1012         /*
1013          * Register exit callback to clean out temp tables at backend shutdown.
1014          */
1015         on_shmem_exit(RemoveTempRelationsCallback, 0);
1016
1017         return namespaceId;
1018 }
1019
1020 /*
1021  * Remove all relations in the specified temp namespace.
1022  *
1023  * This is called at backend shutdown (if we made any temp relations).
1024  * It is also called when we begin using a pre-existing temp namespace,
1025  * in order to clean out any relations that might have been created by
1026  * a crashed backend.
1027  */
1028 static void
1029 RemoveTempRelations(Oid tempNamespaceId)
1030 {
1031         List       *tempRelList;
1032         List       *constraintList;
1033         List       *lptr;
1034
1035         /* Get a list of relations to delete */
1036         tempRelList = FindTempRelations(tempNamespaceId);
1037
1038         if (tempRelList == NIL)
1039                 return;                                 /* nothing to do */
1040
1041         /* If more than one, sort them to respect any deletion-order constraints */
1042         if (length(tempRelList) > 1)
1043         {
1044                 constraintList = FindDeletionConstraints(tempRelList);
1045                 if (constraintList != NIL)
1046                         tempRelList = TopoSortRels(tempRelList, constraintList);
1047         }
1048
1049         /* Scan the list and delete all entries */
1050         foreach(lptr, tempRelList)
1051         {
1052                 Oid                     reloid = (Oid) lfirsti(lptr);
1053
1054                 heap_drop_with_catalog(reloid, true);
1055                 /*
1056                  * Advance cmd counter to make catalog changes visible, in case
1057                  * a later entry depends on this one.
1058                  */
1059                 CommandCounterIncrement();
1060         }
1061 }
1062
1063 /*
1064  * Find all relations in the specified temp namespace.
1065  *
1066  * Returns a list of relation OIDs.
1067  */
1068 static List *
1069 FindTempRelations(Oid tempNamespaceId)
1070 {
1071         List       *tempRelList = NIL;
1072         Relation        pgclass;
1073         HeapScanDesc scan;
1074         HeapTuple       tuple;
1075         ScanKeyData key;
1076
1077         /*
1078          * Scan pg_class to find all the relations in the target namespace.
1079          * Ignore indexes, though, on the assumption that they'll go away
1080          * when their tables are deleted.
1081          */
1082         ScanKeyEntryInitialize(&key, 0x0,
1083                                                    Anum_pg_class_relnamespace,
1084                                                    F_OIDEQ,
1085                                                    ObjectIdGetDatum(tempNamespaceId));
1086
1087         pgclass = heap_openr(RelationRelationName, AccessShareLock);
1088         scan = heap_beginscan(pgclass, false, SnapshotNow, 1, &key);
1089
1090         while (HeapTupleIsValid(tuple = heap_getnext(scan, 0)))
1091         {
1092                 switch (((Form_pg_class) GETSTRUCT(tuple))->relkind)
1093                 {
1094                         case RELKIND_RELATION:
1095                         case RELKIND_SEQUENCE:
1096                         case RELKIND_VIEW:
1097                                 tempRelList = lconsi(tuple->t_data->t_oid, tempRelList);
1098                                 break;
1099                         default:
1100                                 break;
1101                 }
1102         }
1103
1104         heap_endscan(scan);
1105         heap_close(pgclass, AccessShareLock);
1106
1107         return tempRelList;
1108 }
1109
1110 /*
1111  * Find deletion-order constraints involving the given relation OIDs.
1112  *
1113  * Returns a list of DelConstraint objects.
1114  */
1115 static List *
1116 FindDeletionConstraints(List *relOids)
1117 {
1118         List       *constraintList = NIL;
1119         Relation        inheritsrel;
1120         HeapScanDesc scan;
1121         HeapTuple       tuple;
1122
1123         /*
1124          * Scan pg_inherits to find parents and children that are in the list.
1125          */
1126         inheritsrel = heap_openr(InheritsRelationName, AccessShareLock);
1127         scan = heap_beginscan(inheritsrel, 0, SnapshotNow, 0, NULL);
1128
1129         while (HeapTupleIsValid(tuple = heap_getnext(scan, 0)))
1130         {
1131                 Oid             inhrelid = ((Form_pg_inherits) GETSTRUCT(tuple))->inhrelid;
1132                 Oid             inhparent = ((Form_pg_inherits) GETSTRUCT(tuple))->inhparent;
1133
1134                 if (intMember(inhrelid, relOids) && intMember(inhparent, relOids))
1135                 {
1136                         DelConstraint  *item;
1137
1138                         item = (DelConstraint *) palloc(sizeof(DelConstraint));
1139                         item->referencer = inhrelid;
1140                         item->referencee = inhparent;
1141                         constraintList = lcons(item, constraintList);
1142                 }
1143         }
1144
1145         heap_endscan(scan);
1146         heap_close(inheritsrel, AccessShareLock);
1147
1148         return constraintList;
1149 }
1150
1151 /*
1152  * TopoSortRels -- topological sort of a list of rels to delete
1153  *
1154  * This is a lot simpler and slower than, for example, the topological sort
1155  * algorithm shown in Knuth's Volume 1.  However, we are not likely to be
1156  * working with more than a few constraints, so the apparent slowness of the
1157  * algorithm won't really matter.
1158  */
1159 static List *
1160 TopoSortRels(List *relOids, List *constraintList)
1161 {
1162         int                     queue_size = length(relOids);
1163         Oid                *rels;
1164         int                *beforeConstraints;
1165         DelConstraint **afterConstraints;
1166         List       *resultList = NIL;
1167         List       *lptr;
1168         int                     i,
1169                                 j,
1170                                 k,
1171                                 last;
1172
1173         /* Allocate workspace */
1174         rels = (Oid *) palloc(queue_size * sizeof(Oid));
1175         beforeConstraints = (int *) palloc(queue_size * sizeof(int));
1176         afterConstraints = (DelConstraint **)
1177                 palloc(queue_size * sizeof(DelConstraint*));
1178
1179         /* Build an array of the target relation OIDs */
1180         i = 0;
1181         foreach(lptr, relOids)
1182         {
1183                 rels[i++] = (Oid) lfirsti(lptr);
1184         }
1185
1186         /*
1187          * Scan the constraints, and for each rel in the array, generate a
1188          * count of the number of constraints that say it must be before
1189          * something else, plus a list of the constraints that say it must be
1190          * after something else. The count for the j'th rel is stored in
1191          * beforeConstraints[j], and the head of its list in
1192          * afterConstraints[j].  Each constraint stores its list link in
1193          * its link field (note any constraint will be in just one list).
1194          * The array index for the before-rel of each constraint is
1195          * remembered in the constraint's pred field.
1196          */
1197         MemSet(beforeConstraints, 0, queue_size * sizeof(int));
1198         MemSet(afterConstraints, 0, queue_size * sizeof(DelConstraint*));
1199         foreach(lptr, constraintList)
1200         {
1201                 DelConstraint  *constraint = (DelConstraint *) lfirst(lptr);
1202                 Oid                     rel;
1203
1204                 /* Find the referencer rel in the array */
1205                 rel = constraint->referencer;
1206                 for (j = queue_size; --j >= 0;)
1207                 {
1208                         if (rels[j] == rel)
1209                                 break;
1210                 }
1211                 Assert(j >= 0);                 /* should have found a match */
1212                 /* Find the referencee rel in the array */
1213                 rel = constraint->referencee;
1214                 for (k = queue_size; --k >= 0;)
1215                 {
1216                         if (rels[k] == rel)
1217                                 break;
1218                 }
1219                 Assert(k >= 0);                 /* should have found a match */
1220                 beforeConstraints[j]++; /* referencer must come before */
1221                 /* add this constraint to list of after-constraints for referencee */
1222                 constraint->pred = j;
1223                 constraint->link = afterConstraints[k];
1224                 afterConstraints[k] = constraint;
1225         }
1226         /*--------------------
1227          * Now scan the rels array backwards.   At each step, output the
1228          * last rel that has no remaining before-constraints, and decrease
1229          * the beforeConstraints count of each of the rels it was constrained
1230          * against.  (This is the right order since we are building the result
1231          * list back-to-front.)
1232          * i = counter for number of rels left to output
1233          * j = search index for rels[]
1234          * dc = temp for scanning constraint list for rel j
1235          * last = last valid index in rels (avoid redundant searches)
1236          *--------------------
1237          */
1238         last = queue_size - 1;
1239         for (i = queue_size; --i >= 0;)
1240         {
1241                 DelConstraint  *dc;
1242
1243                 /* Find next candidate to output */
1244                 while (rels[last] == InvalidOid)
1245                         last--;
1246                 for (j = last; j >= 0; j--)
1247                 {
1248                         if (rels[j] != InvalidOid && beforeConstraints[j] == 0)
1249                                 break;
1250                 }
1251                 /* If no available candidate, topological sort fails */
1252                 if (j < 0)
1253                         elog(ERROR, "TopoSortRels: failed to find a workable deletion ordering");
1254                 /* Output candidate, and mark it done by zeroing rels[] entry */
1255                 resultList = lconsi(rels[j], resultList);
1256                 rels[j] = InvalidOid;
1257                 /* Update beforeConstraints counts of its predecessors */
1258                 for (dc = afterConstraints[j]; dc; dc = dc->link)
1259                         beforeConstraints[dc->pred]--;
1260         }
1261
1262         /* Done */
1263         return resultList;
1264 }
1265
1266 /*
1267  * Callback to remove temp relations at backend exit.
1268  */
1269 static void
1270 RemoveTempRelationsCallback(void)
1271 {
1272         if (OidIsValid(myTempNamespace)) /* should always be true */
1273         {
1274                 /* Need to ensure we have a usable transaction. */
1275                 AbortOutOfAnyTransaction();
1276                 StartTransactionCommand();
1277
1278                 RemoveTempRelations(myTempNamespace);
1279
1280                 CommitTransactionCommand();
1281         }
1282 }
1283
1284 /*
1285  * Routines for handling the GUC variable 'search_path'.
1286  */
1287
1288 /* parse_hook: is proposed value valid? */
1289 bool
1290 check_search_path(const char *proposed)
1291 {
1292         char       *rawname;
1293         List       *namelist;
1294         List       *l;
1295
1296         /* Need a modifiable copy of string */
1297         rawname = pstrdup(proposed);
1298
1299         /* Parse string into list of identifiers */
1300         if (!SplitIdentifierString(rawname, ',', &namelist))
1301         {
1302                 /* syntax error in name list */
1303                 pfree(rawname);
1304                 freeList(namelist);
1305                 return false;
1306         }
1307
1308         /*
1309          * If we aren't inside a transaction, we cannot do database access so
1310          * cannot verify the individual names.  Must accept the list on faith.
1311          * (This case can happen, for example, when the postmaster reads a
1312          * search_path setting from postgresql.conf.)
1313          */
1314         if (!IsTransactionState())
1315         {
1316                 pfree(rawname);
1317                 freeList(namelist);
1318                 return true;
1319         }
1320
1321         /*
1322          * Verify that all the names are either valid namespace names or "$user".
1323          * (We do not require $user to correspond to a valid namespace; should we?)
1324          */
1325         foreach(l, namelist)
1326         {
1327                 char   *curname = (char *) lfirst(l);
1328
1329                 if (strcmp(curname, "$user") == 0)
1330                         continue;
1331                 if (!SearchSysCacheExists(NAMESPACENAME,
1332                                                                   CStringGetDatum(curname),
1333                                                                   0, 0, 0))
1334                 {
1335                         pfree(rawname);
1336                         freeList(namelist);
1337                         return false;
1338                 }
1339         }
1340
1341         pfree(rawname);
1342         freeList(namelist);
1343
1344         return true;
1345 }
1346
1347 /* assign_hook: do extra actions needed when assigning to search_path */
1348 void
1349 assign_search_path(const char *newval)
1350 {
1351         char       *rawname;
1352         List       *namelist;
1353         List       *oidlist;
1354         List       *newpath;
1355         List       *l;
1356         MemoryContext oldcxt;
1357
1358         /*
1359          * If we aren't inside a transaction, we cannot do database access so
1360          * cannot look up the names.  In this case, do nothing; the internal
1361          * search path will be fixed later by InitializeSearchPath.  (We assume
1362          * this situation can only happen in the postmaster or early in backend
1363          * startup.)
1364          */
1365         if (!IsTransactionState())
1366                 return;
1367
1368         /* Need a modifiable copy of string */
1369         rawname = pstrdup(newval);
1370
1371         /* Parse string into list of identifiers */
1372         if (!SplitIdentifierString(rawname, ',', &namelist))
1373         {
1374                 /* syntax error in name list */
1375                 /* this should not happen if GUC checked check_search_path */
1376                 elog(ERROR, "assign_search_path: invalid list syntax");
1377         }
1378
1379         /*
1380          * Convert the list of names to a list of OIDs.  If any names are not
1381          * recognizable, just leave them out of the list.  (This is our only
1382          * reasonable recourse when the already-accepted default is bogus.)
1383          */
1384         oidlist = NIL;
1385         foreach(l, namelist)
1386         {
1387                 char   *curname = (char *) lfirst(l);
1388                 Oid             namespaceId;
1389
1390                 if (strcmp(curname, "$user") == 0)
1391                 {
1392                         /* $user --- substitute namespace matching user name, if any */
1393                         HeapTuple       tuple;
1394
1395                         tuple = SearchSysCache(SHADOWSYSID,
1396                                                                    ObjectIdGetDatum(GetSessionUserId()),
1397                                                                    0, 0, 0);
1398                         if (HeapTupleIsValid(tuple))
1399                         {
1400                                 char   *uname;
1401
1402                                 uname = NameStr(((Form_pg_shadow) GETSTRUCT(tuple))->usename);
1403                                 namespaceId = GetSysCacheOid(NAMESPACENAME,
1404                                                                                          CStringGetDatum(uname),
1405                                                                                          0, 0, 0);
1406                                 if (OidIsValid(namespaceId))
1407                                         oidlist = lappendi(oidlist, namespaceId);
1408                                 ReleaseSysCache(tuple);
1409                         }
1410                 }
1411                 else
1412                 {
1413                         /* normal namespace reference */
1414                         namespaceId = GetSysCacheOid(NAMESPACENAME,
1415                                                                                  CStringGetDatum(curname),
1416                                                                                  0, 0, 0);
1417                         if (OidIsValid(namespaceId))
1418                                 oidlist = lappendi(oidlist, namespaceId);
1419                 }
1420         }
1421
1422         /*
1423          * Now that we've successfully built the new list of namespace OIDs,
1424          * save it in permanent storage.
1425          */
1426         oldcxt = MemoryContextSwitchTo(TopMemoryContext);
1427         newpath = listCopy(oidlist);
1428         MemoryContextSwitchTo(oldcxt);
1429
1430         /* Now safe to assign to state variable. */
1431         freeList(namespaceSearchPath);
1432         namespaceSearchPath = newpath;
1433
1434         /*
1435          * Update info derived from search path.
1436          */
1437         pathContainsSystemNamespace = intMember(PG_CATALOG_NAMESPACE,
1438                                                                                         namespaceSearchPath);
1439
1440         if (namespaceSearchPath == NIL)
1441                 defaultCreationNamespace = InvalidOid;
1442         else
1443                 defaultCreationNamespace = (Oid) lfirsti(namespaceSearchPath);
1444
1445         /* Clean up. */
1446         pfree(rawname);
1447         freeList(namelist);
1448         freeList(oidlist);
1449 }
1450
1451 /*
1452  * InitializeSearchPath: initialize module during InitPostgres.
1453  *
1454  * This is called after we are up enough to be able to do catalog lookups.
1455  */
1456 void
1457 InitializeSearchPath(void)
1458 {
1459         if (IsBootstrapProcessingMode())
1460         {
1461                 /*
1462                  * In bootstrap mode, the search path must be 'pg_catalog' so that
1463                  * tables are created in the proper namespace; ignore the GUC setting.
1464                  */
1465                 MemoryContext oldcxt;
1466
1467                 oldcxt = MemoryContextSwitchTo(TopMemoryContext);
1468                 namespaceSearchPath = makeListi1(PG_CATALOG_NAMESPACE);
1469                 MemoryContextSwitchTo(oldcxt);
1470                 pathContainsSystemNamespace = true;
1471                 defaultCreationNamespace = PG_CATALOG_NAMESPACE;
1472         }
1473         else
1474         {
1475                 /*
1476                  * If a search path setting was provided before we were able to
1477                  * execute lookups, establish the internal search path now.
1478                  */
1479                 if (namespace_search_path && *namespace_search_path &&
1480                         namespaceSearchPath == NIL)
1481                         assign_search_path(namespace_search_path);
1482         }
1483 }
1484
1485 /*
1486  * Fetch the active search path, expressed as a List of OIDs.
1487  *
1488  * NB: caller must treat the list as read-only!
1489  */
1490 List *
1491 fetch_search_path(void)
1492 {
1493         return namespaceSearchPath;
1494 }