]> granicus.if.org Git - postgresql/blob - src/backend/commands/command.c
1746eefbe3ec08ecca6fd4afa42f42675c500f05
[postgresql] / src / backend / commands / command.c
1 /*-------------------------------------------------------------------------
2  *
3  * command.c
4  *        random postgres portal and utility support code
5  *
6  * Copyright (c) 1994, Regents of the University of California
7  *
8  *
9  * IDENTIFICATION
10  *        $Header: /cvsroot/pgsql/src/backend/commands/Attic/command.c,v 1.57 1999/11/22 17:56:00 momjian Exp $
11  *
12  * NOTES
13  *        The PortalExecutorHeapMemory crap needs to be eliminated
14  *        by designing a better executor / portal processing memory
15  *        interface.
16  *
17  *        The PerformAddAttribute() code, like most of the relation
18  *        manipulating code in the commands/ directory, should go
19  *        someplace closer to the lib/catalog code.
20  *
21  *-------------------------------------------------------------------------
22  */
23 #include "postgres.h"
24
25 #include "access/heapam.h"
26 #include "catalog/catalog.h"
27 #include "catalog/catname.h"
28 #include "catalog/indexing.h"
29 #include "catalog/pg_type.h"
30 #include "commands/command.h"
31 #include "executor/execdefs.h"
32 #include "executor/executor.h"
33 #include "miscadmin.h"
34 #include "optimizer/prep.h"
35 #include "utils/acl.h"
36 #include "utils/builtins.h"
37 #include "utils/syscache.h"
38 #include "utils/temprel.h"
39
40 /* ----------------
41  *              PortalExecutorHeapMemory stuff
42  *
43  *              This is where the XXXSuperDuperHacky code was. -cim 3/15/90
44  * ----------------
45  */
46 MemoryContext PortalExecutorHeapMemory = NULL;
47
48 /* --------------------------------
49  *              PortalCleanup
50  * --------------------------------
51  */
52 void
53 PortalCleanup(Portal portal)
54 {
55         MemoryContext context;
56
57         /* ----------------
58          *      sanity checks
59          * ----------------
60          */
61         AssertArg(PortalIsValid(portal));
62         AssertArg(portal->cleanup == PortalCleanup);
63
64         /* ----------------
65          *      set proper portal-executor context before calling ExecMain.
66          * ----------------
67          */
68         context = MemoryContextSwitchTo((MemoryContext) PortalGetHeapMemory(portal));
69         PortalExecutorHeapMemory = (MemoryContext) PortalGetHeapMemory(portal);
70
71         /* ----------------
72          *      tell the executor to shutdown the query
73          * ----------------
74          */
75         ExecutorEnd(PortalGetQueryDesc(portal), PortalGetState(portal));
76
77         /* ----------------
78          *      switch back to previous context
79          * ----------------
80          */
81         MemoryContextSwitchTo(context);
82         PortalExecutorHeapMemory = (MemoryContext) NULL;
83 }
84
85 /* --------------------------------
86  *              PerformPortalFetch
87  * --------------------------------
88  */
89 void
90 PerformPortalFetch(char *name,
91                                    bool forward,
92                                    int count,
93                                    char *tag,
94                                    CommandDest dest)
95 {
96         Portal          portal;
97         int                     feature;
98         QueryDesc  *queryDesc;
99         MemoryContext context;
100         Const           limcount;
101
102         /* ----------------
103          *      sanity checks
104          * ----------------
105          */
106         if (name == NULL)
107         {
108                 elog(NOTICE, "PerformPortalFetch: blank portal unsupported");
109                 return;
110         }
111
112         /* ----------------
113          *      Create a const node from the given count value
114          * ----------------
115          */
116         memset(&limcount, 0, sizeof(limcount));
117         limcount.type = T_Const;
118         limcount.consttype = INT4OID;
119         limcount.constlen = sizeof(int4);
120         limcount.constvalue = (Datum) count;
121         limcount.constisnull = FALSE;
122         limcount.constbyval = TRUE;
123         limcount.constisset = FALSE;
124         limcount.constiscast = FALSE;
125
126
127         /* ----------------
128          *      get the portal from the portal name
129          * ----------------
130          */
131         portal = GetPortalByName(name);
132         if (!PortalIsValid(portal))
133         {
134                 elog(NOTICE, "PerformPortalFetch: portal \"%s\" not found",
135                          name);
136                 return;
137         }
138
139         /* ----------------
140          *      switch into the portal context
141          * ----------------
142          */
143         context = MemoryContextSwitchTo((MemoryContext) PortalGetHeapMemory(portal));
144
145         AssertState(context == (MemoryContext) PortalGetHeapMemory(GetPortalByName(NULL)));
146
147         /* ----------------
148          *      setup "feature" to tell the executor what direction and
149          *      how many tuples to fetch.
150          * ----------------
151          */
152         if (forward)
153                 feature = EXEC_FOR;
154         else
155                 feature = EXEC_BACK;
156
157         /* ----------------
158          *      tell the destination to prepare to recieve some tuples
159          * ----------------
160          */
161         queryDesc = PortalGetQueryDesc(portal);
162
163         if (dest == None)                       /* MOVE */
164         {
165                 QueryDesc  *qdesc = (QueryDesc *) palloc(sizeof(QueryDesc));
166
167                 memcpy(qdesc, queryDesc, sizeof(QueryDesc));
168                 qdesc->dest = dest;
169                 queryDesc = qdesc;
170         }
171
172         BeginCommand(name,
173                                  queryDesc->operation,
174                                  portal->attinfo,               /* QueryDescGetTypeInfo(queryDesc),
175                                                                                  * */
176                                  false,                 /* portal fetches don't end up in
177                                                                  * relations */
178                                  false,                 /* this is a portal fetch, not a "retrieve
179                                                                  * portal" */
180                                  tag,
181                                  dest);
182
183         /* ----------------
184          *      execute the portal fetch operation
185          * ----------------
186          */
187         PortalExecutorHeapMemory = (MemoryContext) PortalGetHeapMemory(portal);
188
189         ExecutorRun(queryDesc, PortalGetState(portal), feature,
190                                 (Node *) NULL, (Node *) &limcount);
191
192         if (dest == None)                       /* MOVE */
193                 pfree(queryDesc);
194
195         /* ----------------
196          * Note: the "end-of-command" tag is returned by higher-level
197          *               utility code
198          *
199          * Return blank portal for now.
200          * Otherwise, this named portal will be cleaned.
201          * Note: portals will only be supported within a BEGIN...END
202          * block in the near future.  Later, someone will fix it to
203          * do what is possible across transaction boundries.
204          * ----------------
205          */
206         MemoryContextSwitchTo(
207                          (MemoryContext) PortalGetHeapMemory(GetPortalByName(NULL)));
208 }
209
210 /* --------------------------------
211  *              PerformPortalClose
212  * --------------------------------
213  */
214 void
215 PerformPortalClose(char *name, CommandDest dest)
216 {
217         Portal          portal;
218
219         /* ----------------
220          *      sanity checks
221          * ----------------
222          */
223         if (name == NULL)
224         {
225                 elog(NOTICE, "PerformPortalClose: blank portal unsupported");
226                 return;
227         }
228
229         /* ----------------
230          *      get the portal from the portal name
231          * ----------------
232          */
233         portal = GetPortalByName(name);
234         if (!PortalIsValid(portal))
235         {
236                 elog(NOTICE, "PerformPortalClose: portal \"%s\" not found",
237                          name);
238                 return;
239         }
240
241         /* ----------------
242          *      Note: PortalCleanup is called as a side-effect
243          * ----------------
244          */
245         PortalDestroy(&portal);
246 }
247
248 /* ----------------
249  *              PerformAddAttribute
250  *
251  *              adds an additional attribute to a relation
252  *
253  *              Adds attribute field(s) to a relation.  Each new attribute
254  *              is given attnums in sequential order and is added to the
255  *              ATTRIBUTE relation.  If the AMI fails, defunct tuples will
256  *              remain in the ATTRIBUTE relation for later vacuuming.
257  *              Later, there may be some reserved attribute names???
258  *
259  *              (If needed, can instead use elog to handle exceptions.)
260  *
261  *              Note:
262  *                              Initial idea of ordering the tuple attributes so that all
263  *              the variable length domains occured last was scratched.  Doing
264  *              so would not speed access too much (in general) and would create
265  *              many complications in formtuple, amgetattr, and addattribute.
266  *
267  *              scan attribute catalog for name conflict (within rel)
268  *              scan type catalog for absence of data type (if not arg)
269  *              create attnum magically???
270  *              create attribute tuple
271  *              insert attribute in attribute catalog
272  *              modify reldesc
273  *              create new relation tuple
274  *              insert new relation in relation catalog
275  *              delete original relation from relation catalog
276  * ----------------
277  */
278 void
279 PerformAddAttribute(char *relationName,
280                                         char *userName,
281                                         bool inherits,
282                                         ColumnDef *colDef)
283 {
284         Relation        rel,
285                                 attrdesc;
286         Oid                     myrelid;
287         HeapTuple       reltup;
288         HeapTuple       attributeTuple;
289         Form_pg_attribute attribute;
290         FormData_pg_attribute attributeD;
291         int                     i;
292         int                     minattnum,
293                                 maxatts;
294         HeapTuple       tup;
295         Relation        idescs[Num_pg_attr_indices];
296         Relation        ridescs[Num_pg_class_indices];
297         bool            hasindex;
298
299         /*
300          * permissions checking.  this would normally be done in utility.c,
301          * but this particular routine is recursive.
302          *
303          * normally, only the owner of a class can change its schema.
304          */
305         if (!allowSystemTableMods && IsSystemRelationName(relationName))
306                 elog(ERROR, "PerformAddAttribute: class \"%s\" is a system catalog",
307                          relationName);
308 #ifndef NO_SECURITY
309         if (!pg_ownercheck(userName, relationName, RELNAME))
310                 elog(ERROR, "PerformAddAttribute: you do not own class \"%s\"",
311                          relationName);
312 #endif
313
314         /*
315          * Grab an exclusive lock on the target table, which we will NOT release
316          * until end of transaction.
317          */
318         rel = heap_openr(relationName, AccessExclusiveLock);
319         myrelid = RelationGetRelid(rel);
320         heap_close(rel, NoLock);        /* close rel but keep lock! */
321
322         /*
323          * we can't add a not null attribute
324          */
325         if (colDef->is_not_null)
326                 elog(ERROR, "Can't add a NOT NULL attribute to an existing relation");
327         if (colDef->raw_default || colDef->cooked_default)
328                 elog(ERROR, "ADD ATTRIBUTE: DEFAULT not yet implemented");
329
330         /*
331          * if the first element in the 'schema' list is a "*" then we are
332          * supposed to add this attribute to all classes that inherit from
333          * 'relationName' (as well as to 'relationName').
334          *
335          * any permissions or problems with duplicate attributes will cause the
336          * whole transaction to abort, which is what we want -- all or
337          * nothing.
338          */
339         if (colDef != NULL)
340         {
341                 if (inherits)
342                 {
343                         Oid                     childrelid;
344                         List       *child,
345                                            *children;
346
347                         /* this routine is actually in the planner */
348                         children = find_all_inheritors(lconsi(myrelid, NIL), NIL);
349
350                         /*
351                          * find_all_inheritors does the recursive search of the
352                          * inheritance hierarchy, so all we have to do is process all
353                          * of the relids in the list that it returns.
354                          */
355                         foreach(child, children)
356                         {
357                                 childrelid = lfirsti(child);
358                                 if (childrelid == myrelid)
359                                         continue;
360                                 rel = heap_open(childrelid, AccessExclusiveLock);
361                                 PerformAddAttribute(RelationGetRelationName(rel),
362                                                                         userName, false, colDef);
363                                 heap_close(rel, AccessExclusiveLock);
364                         }
365                 }
366         }
367
368         rel = heap_openr(RelationRelationName, RowExclusiveLock);
369
370         reltup = SearchSysCacheTupleCopy(RELNAME,
371                                                                          PointerGetDatum(relationName),
372                                                                          0, 0, 0);
373
374         if (!HeapTupleIsValid(reltup))
375                 elog(ERROR, "PerformAddAttribute: relation \"%s\" not found",
376                          relationName);
377
378         /*
379          * XXX is the following check sufficient?
380          */
381         if (((Form_pg_class) GETSTRUCT(reltup))->relkind == RELKIND_INDEX)
382         {
383                 elog(ERROR, "PerformAddAttribute: index relation \"%s\" not changed",
384                          relationName);
385         }
386
387         minattnum = ((Form_pg_class) GETSTRUCT(reltup))->relnatts;
388         maxatts = minattnum + 1;
389         if (maxatts > MaxHeapAttributeNumber)
390                 elog(ERROR, "PerformAddAttribute: relations limited to %d attributes",
391                          MaxHeapAttributeNumber);
392
393         attrdesc = heap_openr(AttributeRelationName, RowExclusiveLock);
394
395         /*
396          * Open all (if any) pg_attribute indices
397          */
398         hasindex = RelationGetForm(attrdesc)->relhasindex;
399         if (hasindex)
400                 CatalogOpenIndices(Num_pg_attr_indices, Name_pg_attr_indices, idescs);
401
402         attributeD.attrelid = reltup->t_data->t_oid;
403
404         attributeTuple = heap_addheader(Natts_pg_attribute,
405                                                                         sizeof attributeD,
406                                                                         (char *) &attributeD);
407
408         attribute = (Form_pg_attribute) GETSTRUCT(attributeTuple);
409
410         i = 1 + minattnum;
411
412         {
413                 HeapTuple       typeTuple;
414                 Form_pg_type tform;
415                 char       *typename;
416                 int                     attnelems;
417
418                 tup = SearchSysCacheTuple(ATTNAME,
419                                                                   ObjectIdGetDatum(reltup->t_data->t_oid),
420                                                                   PointerGetDatum(colDef->colname),
421                                                                   0, 0);
422
423                 if (HeapTupleIsValid(tup))
424                         elog(ERROR, "PerformAddAttribute: attribute \"%s\" already exists in class \"%s\"",
425                                  colDef->colname, relationName);
426
427                 /*
428                  * check to see if it is an array attribute.
429                  */
430
431                 typename = colDef->typename->name;
432
433                 if (colDef->typename->arrayBounds)
434                 {
435                         attnelems = length(colDef->typename->arrayBounds);
436                         typename = makeArrayTypeName(colDef->typename->name);
437                 }
438                 else
439                         attnelems = 0;
440
441                 typeTuple = SearchSysCacheTuple(TYPENAME,
442                                                                                 PointerGetDatum(typename),
443                                                                                 0, 0, 0);
444                 tform = (Form_pg_type) GETSTRUCT(typeTuple);
445
446                 if (!HeapTupleIsValid(typeTuple))
447                         elog(ERROR, "Add: type \"%s\" nonexistent", typename);
448                 namestrcpy(&(attribute->attname), colDef->colname);
449                 attribute->atttypid = typeTuple->t_data->t_oid;
450                 attribute->attlen = tform->typlen;
451                 attribute->attdisbursion = 0;
452                 attribute->attcacheoff = -1;
453                 attribute->atttypmod = colDef->typename->typmod;
454                 attribute->attnum = i;
455                 attribute->attbyval = tform->typbyval;
456                 attribute->attnelems = attnelems;
457                 attribute->attisset = (bool) (tform->typtype == 'c');
458                 attribute->attalign = tform->typalign;
459                 attribute->attnotnull = false;
460                 attribute->atthasdef = (colDef->raw_default != NULL ||
461                                                                 colDef->cooked_default != NULL);
462
463                 heap_insert(attrdesc, attributeTuple);
464                 if (hasindex)
465                         CatalogIndexInsert(idescs,
466                                                            Num_pg_attr_indices,
467                                                            attrdesc,
468                                                            attributeTuple);
469         }
470
471         if (hasindex)
472                 CatalogCloseIndices(Num_pg_attr_indices, idescs);
473
474         heap_close(attrdesc, RowExclusiveLock);
475
476         ((Form_pg_class) GETSTRUCT(reltup))->relnatts = maxatts;
477         heap_replace(rel, &reltup->t_self, reltup, NULL);
478
479         /* keep catalog indices current */
480         CatalogOpenIndices(Num_pg_class_indices, Name_pg_class_indices, ridescs);
481         CatalogIndexInsert(ridescs, Num_pg_class_indices, rel, reltup);
482         CatalogCloseIndices(Num_pg_class_indices, ridescs);
483
484         pfree(reltup);
485         heap_close(rel, RowExclusiveLock);
486 }
487
488 void
489 LockTableCommand(LockStmt *lockstmt)
490 {
491         Relation        rel;
492         int                     aclresult;
493
494         rel = heap_openr(lockstmt->relname, NoLock);
495         if (! RelationIsValid(rel))
496                 elog(ERROR, "Relation '%s' does not exist", lockstmt->relname);
497
498         if (lockstmt->mode == AccessShareLock)
499                 aclresult = pg_aclcheck(lockstmt->relname, GetPgUserName(), ACL_RD);
500         else
501                 aclresult = pg_aclcheck(lockstmt->relname, GetPgUserName(), ACL_WR);
502
503         if (aclresult != ACLCHECK_OK)
504                 elog(ERROR, "LOCK TABLE: permission denied");
505
506         LockRelation(rel, lockstmt->mode);
507
508         heap_close(rel, NoLock);        /* close rel, keep lock */
509 }