]> granicus.if.org Git - postgresql/blob - src/backend/catalog/namespace.c
Implement partial-key searching of syscaches, per recent suggestion
[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.6 2002/04/06 06:59:21 tgl Exp $
17  *
18  *-------------------------------------------------------------------------
19  */
20 #include "postgres.h"
21
22 #include "access/heapam.h"
23 #include "access/xact.h"
24 #include "catalog/catname.h"
25 #include "catalog/heap.h"
26 #include "catalog/namespace.h"
27 #include "catalog/pg_inherits.h"
28 #include "catalog/pg_namespace.h"
29 #include "catalog/pg_proc.h"
30 #include "catalog/pg_shadow.h"
31 #include "miscadmin.h"
32 #include "nodes/makefuncs.h"
33 #include "storage/backendid.h"
34 #include "utils/builtins.h"
35 #include "utils/fmgroids.h"
36 #include "utils/guc.h"
37 #include "utils/catcache.h"
38 #include "utils/lsyscache.h"
39 #include "utils/syscache.h"
40
41
42 /*
43  * The namespace search path is a possibly-empty list of namespace OIDs.
44  * In addition to the explicit list, the TEMP table namespace is always
45  * implicitly searched first (if it's been initialized).  Also, the system
46  * catalog namespace is always searched.  If the system namespace is
47  * explicitly present in the path then it will be searched in the specified
48  * order; otherwise it will be searched after TEMP tables and *before* the
49  * explicit list.  (It might seem that the system namespace should be
50  * implicitly last, but this behavior appears to be required by SQL99.
51  * Also, this provides a way to search the system namespace first without
52  * thereby making it the default creation target namespace.)
53  *
54  * The default creation target namespace is kept equal to the first element
55  * of the explicit list, or is the system namespace if the list is empty.
56  *
57  * In bootstrap mode or a standalone backend, the default search path is
58  * empty, so that the system namespace is the only one searched or inserted
59  * into.  In multiuser mode, the default search path contains the PG_PUBLIC
60  * namespace, preceded by the user's own namespace if one exists.
61  */
62
63 static List *namespaceSearchPath = NIL;
64
65 /* this flag must be updated correctly when namespaceSearchPath is changed */
66 static bool pathContainsSystemNamespace = false;
67
68 /* default place to create stuff */
69 static Oid      defaultCreationNamespace = PG_CATALOG_NAMESPACE;
70
71 /*
72  * myTempNamespace is InvalidOid until and unless a TEMP namespace is set up
73  * in a particular backend session (this happens when a CREATE TEMP TABLE
74  * command is first executed).  Thereafter it's the OID of the temp namespace.
75  */
76 static Oid      myTempNamespace = InvalidOid;
77
78 /*
79  * This is the text equivalent of the search path --- it's the value
80  * of the GUC variable 'search_path'.
81  */
82 char *namespace_search_path = NULL;
83
84
85 /*
86  * Deletion ordering constraint item.
87  */
88 typedef struct DelConstraint
89 {
90         Oid                     referencer;             /* table to delete first */
91         Oid                     referencee;             /* table to delete second */
92         int                     pred;                   /* workspace for TopoSortRels */
93         struct DelConstraint *link;     /* workspace for TopoSortRels */
94 } DelConstraint;
95
96
97 /* Local functions */
98 static Oid      GetTempTableNamespace(void);
99 static void RemoveTempRelations(Oid tempNamespaceId);
100 static List *FindTempRelations(Oid tempNamespaceId);
101 static List *FindDeletionConstraints(List *relOids);
102 static List *TopoSortRels(List *relOids, List *constraintList);
103 static void RemoveTempRelationsCallback(void);
104
105
106 /*
107  * RangeVarGetRelid
108  *              Given a RangeVar describing an existing relation,
109  *              select the proper namespace and look up the relation OID.
110  *
111  * If the relation is not found, return InvalidOid if failOK = true,
112  * otherwise raise an error.
113  */
114 Oid
115 RangeVarGetRelid(const RangeVar *relation, bool failOK)
116 {
117         Oid                     namespaceId;
118         Oid                     relId;
119
120         /*
121          * We check the catalog name and then ignore it.
122          */
123         if (relation->catalogname)
124         {
125                 if (strcmp(relation->catalogname, DatabaseName) != 0)
126                         elog(ERROR, "Cross-database references are not implemented");
127         }
128
129         if (relation->schemaname)
130         {
131                 /* use exact schema given */
132                 namespaceId = GetSysCacheOid(NAMESPACENAME,
133                                                                          CStringGetDatum(relation->schemaname),
134                                                                          0, 0, 0);
135                 if (!OidIsValid(namespaceId))
136                         elog(ERROR, "Namespace \"%s\" does not exist",
137                                  relation->schemaname);
138                 relId = get_relname_relid(relation->relname, namespaceId);
139         }
140         else
141         {
142                 /* search the namespace path */
143                 relId = RelnameGetRelid(relation->relname);
144         }
145
146         if (!OidIsValid(relId) && !failOK)
147         {
148                 if (relation->schemaname)
149                         elog(ERROR, "Relation \"%s\".\"%s\" does not exist",
150                                  relation->schemaname, relation->relname);
151                 else
152                         elog(ERROR, "Relation \"%s\" does not exist",
153                                  relation->relname);
154         }
155         return relId;
156 }
157
158 /*
159  * RangeVarGetCreationNamespace
160  *              Given a RangeVar describing a to-be-created relation,
161  *              choose which namespace to create it in.
162  *
163  * Note: calling this may result in a CommandCounterIncrement operation.
164  * That will happen on the first request for a temp table in any particular
165  * backend run; we will need to either create or clean out the temp schema.
166  */
167 Oid
168 RangeVarGetCreationNamespace(const RangeVar *newRelation)
169 {
170         Oid                     namespaceId;
171
172         /*
173          * We check the catalog name and then ignore it.
174          */
175         if (newRelation->catalogname)
176         {
177                 if (strcmp(newRelation->catalogname, DatabaseName) != 0)
178                         elog(ERROR, "Cross-database references are not implemented");
179         }
180
181         if (newRelation->istemp)
182         {
183                 /* TEMP tables are created in our backend-local temp namespace */
184                 if (newRelation->schemaname)
185                         elog(ERROR, "TEMP tables may not specify a namespace");
186                 /* Initialize temp namespace if first time through */
187                 if (!OidIsValid(myTempNamespace))
188                         myTempNamespace = GetTempTableNamespace();
189                 return myTempNamespace;
190         }
191
192         if (newRelation->schemaname)
193         {
194                 /* use exact schema given */
195                 namespaceId = GetSysCacheOid(NAMESPACENAME,
196                                                                          CStringGetDatum(newRelation->schemaname),
197                                                                          0, 0, 0);
198                 if (!OidIsValid(namespaceId))
199                         elog(ERROR, "Namespace \"%s\" does not exist",
200                                  newRelation->schemaname);
201         }
202         else
203         {
204                 /* use the default creation namespace */
205                 namespaceId = defaultCreationNamespace;
206         }
207
208         return namespaceId;
209 }
210
211 /*
212  * RelnameGetRelid
213  *              Try to resolve an unqualified relation name.
214  *              Returns OID if relation found in search path, else InvalidOid.
215  */
216 Oid
217 RelnameGetRelid(const char *relname)
218 {
219         Oid                     relid;
220         List       *lptr;
221
222         /*
223          * If a TEMP-table namespace has been set up, it is implicitly first
224          * in the search path.
225          */
226         if (OidIsValid(myTempNamespace))
227         {
228                 relid = get_relname_relid(relname, myTempNamespace);
229                 if (OidIsValid(relid))
230                         return relid;
231         }
232
233         /*
234          * If system namespace is not in path, implicitly search it before path
235          */
236         if (!pathContainsSystemNamespace)
237         {
238                 relid = get_relname_relid(relname, PG_CATALOG_NAMESPACE);
239                 if (OidIsValid(relid))
240                         return relid;
241         }
242
243         /*
244          * Else search the path
245          */
246         foreach(lptr, namespaceSearchPath)
247         {
248                 Oid                     namespaceId = (Oid) lfirsti(lptr);
249
250                 relid = get_relname_relid(relname, namespaceId);
251                 if (OidIsValid(relid))
252                         return relid;
253         }
254
255         /* Not found in path */
256         return InvalidOid;
257 }
258
259 /*
260  * TypenameGetTypid
261  *              Try to resolve an unqualified datatype name.
262  *              Returns OID if type found in search path, else InvalidOid.
263  *
264  * This is essentially the same as RelnameGetRelid, but we never search
265  * the TEMP table namespace --- there is no reason to refer to the types
266  * of temp tables, AFAICS.
267  */
268 Oid
269 TypenameGetTypid(const char *typname)
270 {
271         Oid                     typid;
272         List       *lptr;
273
274         /*
275          * If system namespace is not in path, implicitly search it before path
276          */
277         if (!pathContainsSystemNamespace)
278         {
279                 typid = GetSysCacheOid(TYPENAMENSP,
280                                                            PointerGetDatum(typname),
281                                                            ObjectIdGetDatum(PG_CATALOG_NAMESPACE),
282                                                            0, 0);
283                 if (OidIsValid(typid))
284                         return typid;
285         }
286
287         /*
288          * Else search the path
289          */
290         foreach(lptr, namespaceSearchPath)
291         {
292                 Oid                     namespaceId = (Oid) lfirsti(lptr);
293
294                 typid = GetSysCacheOid(TYPENAMENSP,
295                                                            PointerGetDatum(typname),
296                                                            ObjectIdGetDatum(namespaceId),
297                                                            0, 0);
298                 if (OidIsValid(typid))
299                         return typid;
300         }
301
302         /* Not found in path */
303         return InvalidOid;
304 }
305
306 /*
307  * FuncnameGetCandidates
308  *              Given a possibly-qualified function name and argument count,
309  *              retrieve a list of the possible matches.
310  *
311  * We search a single namespace if the function name is qualified, else
312  * all namespaces in the search path.  The return list will never contain
313  * multiple entries with identical argument types --- in the multiple-
314  * namespace case, we arrange for entries in earlier namespaces to mask
315  * identical entries in later namespaces.
316  */
317 FuncCandidateList
318 FuncnameGetCandidates(List *names, int nargs)
319 {
320         FuncCandidateList resultList = NULL;
321         char       *catalogname;
322         char       *schemaname = NULL;
323         char       *funcname = NULL;
324         Oid                     namespaceId;
325         CatCList   *catlist;
326         int                     i;
327
328         /* deconstruct the name list */
329         switch (length(names))
330         {
331                 case 1:
332                         funcname = strVal(lfirst(names));
333                         break;
334                 case 2:
335                         schemaname = strVal(lfirst(names));
336                         funcname = strVal(lsecond(names));
337                         break;
338                 case 3:
339                         catalogname = strVal(lfirst(names));
340                         schemaname = strVal(lsecond(names));
341                         funcname = strVal(lfirst(lnext(lnext(names))));
342                         /*
343                          * We check the catalog name and then ignore it.
344                          */
345                         if (strcmp(catalogname, DatabaseName) != 0)
346                                 elog(ERROR, "Cross-database references are not implemented");
347                         break;
348                 default:
349                         elog(ERROR, "Improper qualified name (too many dotted names)");
350                         break;
351         }
352
353         if (schemaname)
354         {
355                 /* use exact schema given */
356                 namespaceId = GetSysCacheOid(NAMESPACENAME,
357                                                                          CStringGetDatum(schemaname),
358                                                                          0, 0, 0);
359                 if (!OidIsValid(namespaceId))
360                         elog(ERROR, "Namespace \"%s\" does not exist",
361                                  schemaname);
362         }
363         else
364         {
365                 /* flag to indicate we need namespace search */
366                 namespaceId = InvalidOid;
367         }
368
369         /* Search syscache by name and nargs only */
370         catlist = SearchSysCacheList(PROCNAME, 2,
371                                                                  CStringGetDatum(funcname),
372                                                                  Int16GetDatum(nargs),
373                                                                  0, 0);
374
375         for (i = 0; i < catlist->n_members; i++)
376         {
377                 HeapTuple       proctup = &catlist->members[i]->tuple;
378                 Form_pg_proc procform = (Form_pg_proc) GETSTRUCT(proctup);
379                 int                     pathpos = 0;
380                 FuncCandidateList newResult;
381
382                 if (OidIsValid(namespaceId))
383                 {
384                         /* Consider only procs in specified namespace */
385                         if (procform->pronamespace != namespaceId)
386                                 continue;
387                         /* No need to check args, they must all be different */
388                 }
389                 else
390                 {
391                         /* Consider only procs that are in the search path */
392                         if (pathContainsSystemNamespace ||
393                                 procform->pronamespace != PG_CATALOG_NAMESPACE)
394                         {
395                                 List       *nsp;
396
397                                 foreach(nsp, namespaceSearchPath)
398                                 {
399                                         pathpos++;
400                                         if (procform->pronamespace == (Oid) lfirsti(nsp))
401                                                 break;
402                                 }
403                                 if (nsp == NIL)
404                                         continue;       /* proc is not in search path */
405                         }
406
407                         /*
408                          * Okay, it's in the search path, but does it have the same
409                          * arguments as something we already accepted?  If so, keep
410                          * only the one that appears earlier in the search path.
411                          *
412                          * If we have an ordered list from SearchSysCacheList (the
413                          * normal case), then any conflicting proc must immediately
414                          * adjoin this one in the list, so we only need to look at
415                          * the newest result item.  If we have an unordered list,
416                          * we have to scan the whole result list.
417                          */
418                         if (resultList)
419                         {
420                                 FuncCandidateList       prevResult;
421
422                                 if (catlist->ordered)
423                                 {
424                                         if (memcmp(procform->proargtypes, resultList->args,
425                                                            nargs * sizeof(Oid)) == 0)
426                                                 prevResult = resultList;
427                                         else
428                                                 prevResult = NULL;
429                                 }
430                                 else
431                                 {
432                                         for (prevResult = resultList;
433                                                  prevResult;
434                                                  prevResult = prevResult->next)
435                                         {
436                                                 if (memcmp(procform->proargtypes, prevResult->args,
437                                                                    nargs * sizeof(Oid)) == 0)
438                                                         break;
439                                         }
440                                 }
441                                 if (prevResult)
442                                 {
443                                         /* We have a match with a previous result */
444                                         Assert(pathpos != prevResult->pathpos);
445                                         if (pathpos > prevResult->pathpos)
446                                                 continue; /* keep previous result */
447                                         /* replace previous result */
448                                         prevResult->pathpos = pathpos;
449                                         prevResult->oid = proctup->t_data->t_oid;
450                                         continue;       /* args are same, of course */
451                                 }
452                         }
453                 }
454
455                 /*
456                  * Okay to add it to result list
457                  */
458                 newResult = (FuncCandidateList)
459                         palloc(sizeof(struct _FuncCandidateList) - sizeof(Oid)
460                                    + nargs * sizeof(Oid));
461                 newResult->pathpos = pathpos;
462                 newResult->oid = proctup->t_data->t_oid;
463                 memcpy(newResult->args, procform->proargtypes, nargs * sizeof(Oid));
464
465                 newResult->next = resultList;
466                 resultList = newResult;
467         }
468
469         ReleaseSysCacheList(catlist);
470
471         return resultList;
472 }
473
474 /*
475  * QualifiedNameGetCreationNamespace
476  *              Given a possibly-qualified name for an object (in List-of-Values
477  *              format), determine what namespace the object should be created in.
478  *              Also extract and return the object name (last component of list).
479  *
480  * This is *not* used for tables.  Hence, the TEMP table namespace is
481  * never selected as the creation target.
482  */
483 Oid
484 QualifiedNameGetCreationNamespace(List *names, char **objname_p)
485 {
486         char       *catalogname;
487         char       *schemaname = NULL;
488         char       *objname = NULL;
489         Oid                     namespaceId;
490
491         /* deconstruct the name list */
492         switch (length(names))
493         {
494                 case 1:
495                         objname = strVal(lfirst(names));
496                         break;
497                 case 2:
498                         schemaname = strVal(lfirst(names));
499                         objname = strVal(lsecond(names));
500                         break;
501                 case 3:
502                         catalogname = strVal(lfirst(names));
503                         schemaname = strVal(lsecond(names));
504                         objname = strVal(lfirst(lnext(lnext(names))));
505                         /*
506                          * We check the catalog name and then ignore it.
507                          */
508                         if (strcmp(catalogname, DatabaseName) != 0)
509                                 elog(ERROR, "Cross-database references are not implemented");
510                         break;
511                 default:
512                         elog(ERROR, "Improper qualified name (too many dotted names)");
513                         break;
514         }
515
516         if (schemaname)
517         {
518                 /* use exact schema given */
519                 namespaceId = GetSysCacheOid(NAMESPACENAME,
520                                                                          CStringGetDatum(schemaname),
521                                                                          0, 0, 0);
522                 if (!OidIsValid(namespaceId))
523                         elog(ERROR, "Namespace \"%s\" does not exist",
524                                  schemaname);
525         }
526         else
527         {
528                 /* use the default creation namespace */
529                 namespaceId = defaultCreationNamespace;
530         }
531
532         *objname_p = objname;
533         return namespaceId;
534 }
535
536 /*
537  * makeRangeVarFromNameList
538  *              Utility routine to convert a qualified-name list into RangeVar form.
539  */
540 RangeVar *
541 makeRangeVarFromNameList(List *names)
542 {
543         RangeVar   *rel = makeRangeVar(NULL, NULL);
544
545         switch (length(names))
546         {
547                 case 1:
548                         rel->relname = strVal(lfirst(names));
549                         break;
550                 case 2:
551                         rel->schemaname = strVal(lfirst(names));
552                         rel->relname = strVal(lsecond(names));
553                         break;
554                 case 3:
555                         rel->catalogname = strVal(lfirst(names));
556                         rel->schemaname = strVal(lsecond(names));
557                         rel->relname = strVal(lfirst(lnext(lnext(names))));
558                         break;
559                 default:
560                         elog(ERROR, "Improper relation name (too many dotted names)");
561                         break;
562         }
563
564         return rel;
565 }
566
567 /*
568  * isTempNamespace - is the given namespace my temporary-table namespace?
569  */
570 bool
571 isTempNamespace(Oid namespaceId)
572 {
573         if (OidIsValid(myTempNamespace) && myTempNamespace == namespaceId)
574                 return true;
575         return false;
576 }
577
578 /*
579  * GetTempTableNamespace
580  *              Initialize temp table namespace on first use in a particular backend
581  */
582 static Oid
583 GetTempTableNamespace(void)
584 {
585         char            namespaceName[NAMEDATALEN];
586         Oid                     namespaceId;
587
588         snprintf(namespaceName, NAMEDATALEN, "pg_temp_%d", MyBackendId);
589
590         namespaceId = GetSysCacheOid(NAMESPACENAME,
591                                                                  CStringGetDatum(namespaceName),
592                                                                  0, 0, 0);
593         if (!OidIsValid(namespaceId))
594         {
595                 /*
596                  * First use of this temp namespace in this database; create it.
597                  * The temp namespaces are always owned by the superuser.
598                  */
599                 namespaceId = NamespaceCreate(namespaceName, BOOTSTRAP_USESYSID);
600                 /* Advance command counter to make namespace visible */
601                 CommandCounterIncrement();
602         }
603         else
604         {
605                 /*
606                  * If the namespace already exists, clean it out (in case the
607                  * former owner crashed without doing so).
608                  */
609                 RemoveTempRelations(namespaceId);
610         }
611
612         /*
613          * Register exit callback to clean out temp tables at backend shutdown.
614          */
615         on_shmem_exit(RemoveTempRelationsCallback, 0);
616
617         return namespaceId;
618 }
619
620 /*
621  * Remove all relations in the specified temp namespace.
622  *
623  * This is called at backend shutdown (if we made any temp relations).
624  * It is also called when we begin using a pre-existing temp namespace,
625  * in order to clean out any relations that might have been created by
626  * a crashed backend.
627  */
628 static void
629 RemoveTempRelations(Oid tempNamespaceId)
630 {
631         List       *tempRelList;
632         List       *constraintList;
633         List       *lptr;
634
635         /* Get a list of relations to delete */
636         tempRelList = FindTempRelations(tempNamespaceId);
637
638         if (tempRelList == NIL)
639                 return;                                 /* nothing to do */
640
641         /* If more than one, sort them to respect any deletion-order constraints */
642         if (length(tempRelList) > 1)
643         {
644                 constraintList = FindDeletionConstraints(tempRelList);
645                 if (constraintList != NIL)
646                         tempRelList = TopoSortRels(tempRelList, constraintList);
647         }
648
649         /* Scan the list and delete all entries */
650         foreach(lptr, tempRelList)
651         {
652                 Oid                     reloid = (Oid) lfirsti(lptr);
653
654                 heap_drop_with_catalog(reloid, true);
655                 /*
656                  * Advance cmd counter to make catalog changes visible, in case
657                  * a later entry depends on this one.
658                  */
659                 CommandCounterIncrement();
660         }
661 }
662
663 /*
664  * Find all relations in the specified temp namespace.
665  *
666  * Returns a list of relation OIDs.
667  */
668 static List *
669 FindTempRelations(Oid tempNamespaceId)
670 {
671         List       *tempRelList = NIL;
672         Relation        pgclass;
673         HeapScanDesc scan;
674         HeapTuple       tuple;
675         ScanKeyData key;
676
677         /*
678          * Scan pg_class to find all the relations in the target namespace.
679          * Ignore indexes, though, on the assumption that they'll go away
680          * when their tables are deleted.
681          */
682         ScanKeyEntryInitialize(&key, 0x0,
683                                                    Anum_pg_class_relnamespace,
684                                                    F_OIDEQ,
685                                                    ObjectIdGetDatum(tempNamespaceId));
686
687         pgclass = heap_openr(RelationRelationName, AccessShareLock);
688         scan = heap_beginscan(pgclass, false, SnapshotNow, 1, &key);
689
690         while (HeapTupleIsValid(tuple = heap_getnext(scan, 0)))
691         {
692                 switch (((Form_pg_class) GETSTRUCT(tuple))->relkind)
693                 {
694                         case RELKIND_RELATION:
695                         case RELKIND_SEQUENCE:
696                         case RELKIND_VIEW:
697                                 tempRelList = lconsi(tuple->t_data->t_oid, tempRelList);
698                                 break;
699                         default:
700                                 break;
701                 }
702         }
703
704         heap_endscan(scan);
705         heap_close(pgclass, AccessShareLock);
706
707         return tempRelList;
708 }
709
710 /*
711  * Find deletion-order constraints involving the given relation OIDs.
712  *
713  * Returns a list of DelConstraint objects.
714  */
715 static List *
716 FindDeletionConstraints(List *relOids)
717 {
718         List       *constraintList = NIL;
719         Relation        inheritsrel;
720         HeapScanDesc scan;
721         HeapTuple       tuple;
722
723         /*
724          * Scan pg_inherits to find parents and children that are in the list.
725          */
726         inheritsrel = heap_openr(InheritsRelationName, AccessShareLock);
727         scan = heap_beginscan(inheritsrel, 0, SnapshotNow, 0, NULL);
728
729         while (HeapTupleIsValid(tuple = heap_getnext(scan, 0)))
730         {
731                 Oid             inhrelid = ((Form_pg_inherits) GETSTRUCT(tuple))->inhrelid;
732                 Oid             inhparent = ((Form_pg_inherits) GETSTRUCT(tuple))->inhparent;
733
734                 if (intMember(inhrelid, relOids) && intMember(inhparent, relOids))
735                 {
736                         DelConstraint  *item;
737
738                         item = (DelConstraint *) palloc(sizeof(DelConstraint));
739                         item->referencer = inhrelid;
740                         item->referencee = inhparent;
741                         constraintList = lcons(item, constraintList);
742                 }
743         }
744
745         heap_endscan(scan);
746         heap_close(inheritsrel, AccessShareLock);
747
748         return constraintList;
749 }
750
751 /*
752  * TopoSortRels -- topological sort of a list of rels to delete
753  *
754  * This is a lot simpler and slower than, for example, the topological sort
755  * algorithm shown in Knuth's Volume 1.  However, we are not likely to be
756  * working with more than a few constraints, so the apparent slowness of the
757  * algorithm won't really matter.
758  */
759 static List *
760 TopoSortRels(List *relOids, List *constraintList)
761 {
762         int                     queue_size = length(relOids);
763         Oid                *rels;
764         int                *beforeConstraints;
765         DelConstraint **afterConstraints;
766         List       *resultList = NIL;
767         List       *lptr;
768         int                     i,
769                                 j,
770                                 k,
771                                 last;
772
773         /* Allocate workspace */
774         rels = (Oid *) palloc(queue_size * sizeof(Oid));
775         beforeConstraints = (int *) palloc(queue_size * sizeof(int));
776         afterConstraints = (DelConstraint **)
777                 palloc(queue_size * sizeof(DelConstraint*));
778
779         /* Build an array of the target relation OIDs */
780         i = 0;
781         foreach(lptr, relOids)
782         {
783                 rels[i++] = (Oid) lfirsti(lptr);
784         }
785
786         /*
787          * Scan the constraints, and for each rel in the array, generate a
788          * count of the number of constraints that say it must be before
789          * something else, plus a list of the constraints that say it must be
790          * after something else. The count for the j'th rel is stored in
791          * beforeConstraints[j], and the head of its list in
792          * afterConstraints[j].  Each constraint stores its list link in
793          * its link field (note any constraint will be in just one list).
794          * The array index for the before-rel of each constraint is
795          * remembered in the constraint's pred field.
796          */
797         MemSet(beforeConstraints, 0, queue_size * sizeof(int));
798         MemSet(afterConstraints, 0, queue_size * sizeof(DelConstraint*));
799         foreach(lptr, constraintList)
800         {
801                 DelConstraint  *constraint = (DelConstraint *) lfirst(lptr);
802                 Oid                     rel;
803
804                 /* Find the referencer rel in the array */
805                 rel = constraint->referencer;
806                 for (j = queue_size; --j >= 0;)
807                 {
808                         if (rels[j] == rel)
809                                 break;
810                 }
811                 Assert(j >= 0);                 /* should have found a match */
812                 /* Find the referencee rel in the array */
813                 rel = constraint->referencee;
814                 for (k = queue_size; --k >= 0;)
815                 {
816                         if (rels[k] == rel)
817                                 break;
818                 }
819                 Assert(k >= 0);                 /* should have found a match */
820                 beforeConstraints[j]++; /* referencer must come before */
821                 /* add this constraint to list of after-constraints for referencee */
822                 constraint->pred = j;
823                 constraint->link = afterConstraints[k];
824                 afterConstraints[k] = constraint;
825         }
826         /*--------------------
827          * Now scan the rels array backwards.   At each step, output the
828          * last rel that has no remaining before-constraints, and decrease
829          * the beforeConstraints count of each of the rels it was constrained
830          * against.  (This is the right order since we are building the result
831          * list back-to-front.)
832          * i = counter for number of rels left to output
833          * j = search index for rels[]
834          * dc = temp for scanning constraint list for rel j
835          * last = last valid index in rels (avoid redundant searches)
836          *--------------------
837          */
838         last = queue_size - 1;
839         for (i = queue_size; --i >= 0;)
840         {
841                 DelConstraint  *dc;
842
843                 /* Find next candidate to output */
844                 while (rels[last] == InvalidOid)
845                         last--;
846                 for (j = last; j >= 0; j--)
847                 {
848                         if (rels[j] != InvalidOid && beforeConstraints[j] == 0)
849                                 break;
850                 }
851                 /* If no available candidate, topological sort fails */
852                 if (j < 0)
853                         elog(ERROR, "TopoSortRels: failed to find a workable deletion ordering");
854                 /* Output candidate, and mark it done by zeroing rels[] entry */
855                 resultList = lconsi(rels[j], resultList);
856                 rels[j] = InvalidOid;
857                 /* Update beforeConstraints counts of its predecessors */
858                 for (dc = afterConstraints[j]; dc; dc = dc->link)
859                         beforeConstraints[dc->pred]--;
860         }
861
862         /* Done */
863         return resultList;
864 }
865
866 /*
867  * Callback to remove temp relations at backend exit.
868  */
869 static void
870 RemoveTempRelationsCallback(void)
871 {
872         if (OidIsValid(myTempNamespace)) /* should always be true */
873         {
874                 /* Need to ensure we have a usable transaction. */
875                 AbortOutOfAnyTransaction();
876                 StartTransactionCommand();
877
878                 RemoveTempRelations(myTempNamespace);
879
880                 CommitTransactionCommand();
881         }
882 }
883
884 /*
885  * Routines for handling the GUC variable 'search_path'.
886  */
887
888 /* parse_hook: is proposed value valid? */
889 bool
890 check_search_path(const char *proposed)
891 {
892         char       *rawname;
893         List       *namelist;
894         List       *l;
895
896         /* Need a modifiable copy of string */
897         rawname = pstrdup(proposed);
898
899         /* Parse string into list of identifiers */
900         if (!SplitIdentifierString(rawname, ',', &namelist))
901         {
902                 /* syntax error in name list */
903                 pfree(rawname);
904                 freeList(namelist);
905                 return false;
906         }
907
908         /*
909          * If we aren't inside a transaction, we cannot do database access so
910          * cannot verify the individual names.  Must accept the list on faith.
911          * (This case can happen, for example, when the postmaster reads a
912          * search_path setting from postgresql.conf.)
913          */
914         if (!IsTransactionState())
915         {
916                 pfree(rawname);
917                 freeList(namelist);
918                 return true;
919         }
920
921         /*
922          * Verify that all the names are either valid namespace names or "$user".
923          * (We do not require $user to correspond to a valid namespace; should we?)
924          */
925         foreach(l, namelist)
926         {
927                 char   *curname = (char *) lfirst(l);
928
929                 if (strcmp(curname, "$user") == 0)
930                         continue;
931                 if (!SearchSysCacheExists(NAMESPACENAME,
932                                                                   CStringGetDatum(curname),
933                                                                   0, 0, 0))
934                 {
935                         pfree(rawname);
936                         freeList(namelist);
937                         return false;
938                 }
939         }
940
941         pfree(rawname);
942         freeList(namelist);
943
944         return true;
945 }
946
947 /* assign_hook: do extra actions needed when assigning to search_path */
948 void
949 assign_search_path(const char *newval)
950 {
951         char       *rawname;
952         List       *namelist;
953         List       *oidlist;
954         List       *newpath;
955         List       *l;
956         MemoryContext oldcxt;
957
958         /*
959          * If we aren't inside a transaction, we cannot do database access so
960          * cannot look up the names.  In this case, do nothing; the internal
961          * search path will be fixed later by InitializeSearchPath.  (We assume
962          * this situation can only happen in the postmaster or early in backend
963          * startup.)
964          */
965         if (!IsTransactionState())
966                 return;
967
968         /* Need a modifiable copy of string */
969         rawname = pstrdup(newval);
970
971         /* Parse string into list of identifiers */
972         if (!SplitIdentifierString(rawname, ',', &namelist))
973         {
974                 /* syntax error in name list */
975                 /* this should not happen if GUC checked check_search_path */
976                 elog(ERROR, "assign_search_path: invalid list syntax");
977         }
978
979         /*
980          * Convert the list of names to a list of OIDs.  If any names are not
981          * recognizable, just leave them out of the list.  (This is our only
982          * reasonable recourse when the already-accepted default is bogus.)
983          */
984         oidlist = NIL;
985         foreach(l, namelist)
986         {
987                 char   *curname = (char *) lfirst(l);
988                 Oid             namespaceId;
989
990                 if (strcmp(curname, "$user") == 0)
991                 {
992                         /* $user --- substitute namespace matching user name, if any */
993                         HeapTuple       tuple;
994
995                         tuple = SearchSysCache(SHADOWSYSID,
996                                                                    ObjectIdGetDatum(GetSessionUserId()),
997                                                                    0, 0, 0);
998                         if (HeapTupleIsValid(tuple))
999                         {
1000                                 char   *uname;
1001
1002                                 uname = NameStr(((Form_pg_shadow) GETSTRUCT(tuple))->usename);
1003                                 namespaceId = GetSysCacheOid(NAMESPACENAME,
1004                                                                                          CStringGetDatum(uname),
1005                                                                                          0, 0, 0);
1006                                 if (OidIsValid(namespaceId))
1007                                         oidlist = lappendi(oidlist, namespaceId);
1008                                 ReleaseSysCache(tuple);
1009                         }
1010                 }
1011                 else
1012                 {
1013                         /* normal namespace reference */
1014                         namespaceId = GetSysCacheOid(NAMESPACENAME,
1015                                                                                  CStringGetDatum(curname),
1016                                                                                  0, 0, 0);
1017                         if (OidIsValid(namespaceId))
1018                                 oidlist = lappendi(oidlist, namespaceId);
1019                 }
1020         }
1021
1022         /*
1023          * Now that we've successfully built the new list of namespace OIDs,
1024          * save it in permanent storage.
1025          */
1026         oldcxt = MemoryContextSwitchTo(TopMemoryContext);
1027         newpath = listCopy(oidlist);
1028         MemoryContextSwitchTo(oldcxt);
1029
1030         /* Now safe to assign to state variable. */
1031         freeList(namespaceSearchPath);
1032         namespaceSearchPath = newpath;
1033
1034         /*
1035          * Update info derived from search path.
1036          */
1037         pathContainsSystemNamespace = intMember(PG_CATALOG_NAMESPACE,
1038                                                                                         namespaceSearchPath);
1039
1040         if (namespaceSearchPath == NIL)
1041                 defaultCreationNamespace = PG_CATALOG_NAMESPACE;
1042         else
1043                 defaultCreationNamespace = (Oid) lfirsti(namespaceSearchPath);
1044
1045         /* Clean up. */
1046         pfree(rawname);
1047         freeList(namelist);
1048         freeList(oidlist);
1049 }
1050
1051 /*
1052  * InitializeSearchPath: initialize module during InitPostgres.
1053  *
1054  * This is called after we are up enough to be able to do catalog lookups.
1055  */
1056 void
1057 InitializeSearchPath(void)
1058 {
1059         /*
1060          * In normal multi-user mode, we want the default search path to be
1061          * '$user,public' (or as much of that as exists, anyway; see the
1062          * error handling in assign_search_path); which is what guc.c has as
1063          * the wired-in default value.  But in bootstrap or standalone-backend
1064          * mode, the default search path must be empty so that initdb correctly
1065          * creates everything in PG_CATALOG_NAMESPACE.  Accordingly, adjust the
1066          * default setting if we are not running under postmaster.  (If a
1067          * non-default setting has been supplied, this will not overwrite it.)
1068          */
1069         if (!IsUnderPostmaster)
1070         {
1071                 SetConfigOption("search_path", "",
1072                                                 PGC_POSTMASTER, PGC_S_DEFAULT);
1073         }
1074         /*
1075          * If a search path setting was provided before we were able to execute
1076          * lookups, establish the internal search path now.
1077          */
1078         if (namespace_search_path && *namespace_search_path &&
1079                 namespaceSearchPath == NIL)
1080                 assign_search_path(namespace_search_path);
1081 }