]> granicus.if.org Git - postgresql/blob - src/backend/commands/rename.c
Do table renaming in a sane order: physical file rename must happen
[postgresql] / src / backend / commands / rename.c
1 /*-------------------------------------------------------------------------
2  *
3  * rename.c
4  *        renameatt() and renamerel() reside here.
5  *
6  * Portions Copyright (c) 1996-2000, PostgreSQL, Inc
7  * Portions Copyright (c) 1994, Regents of the University of California
8  *
9  *
10  * IDENTIFICATION
11  *        $Header: /cvsroot/pgsql/src/backend/commands/Attic/rename.c,v 1.45 2000/05/25 21:30:20 tgl Exp $
12  *
13  *-------------------------------------------------------------------------
14  */
15 #include "postgres.h"
16
17 #include <errno.h>
18
19 #include "access/heapam.h"
20 #include "catalog/catname.h"
21 #include "catalog/pg_type.h"
22 #include "catalog/heap.h"
23 #include "catalog/indexing.h"
24 #include "catalog/catalog.h"
25 #include "commands/rename.h"
26 #include "miscadmin.h"
27 #include "storage/smgr.h"
28 #include "optimizer/prep.h"
29 #include "utils/acl.h"
30 #include "utils/relcache.h"
31 #include "utils/syscache.h"
32
33
34 /*
35  *              renameatt               - changes the name of a attribute in a relation
36  *
37  *              Attname attribute is changed in attribute catalog.
38  *              No record of the previous attname is kept (correct?).
39  *
40  *              get proper relrelation from relation catalog (if not arg)
41  *              scan attribute catalog
42  *                              for name conflict (within rel)
43  *                              for original attribute (if not arg)
44  *              modify attname in attribute tuple
45  *              insert modified attribute in attribute catalog
46  *              delete original attribute from attribute catalog
47  *
48  *              XXX Renaming an indexed attribute must (eventually) also change
49  *                              the attribute name in the associated indexes.
50  */
51 void
52 renameatt(char *relname,
53                   char *oldattname,
54                   char *newattname,
55                   char *userName,
56                   int recurse)
57 {
58         Relation        targetrelation;
59         Relation        attrelation;
60         HeapTuple       reltup,
61                                 oldatttup,
62                                 newatttup;
63         Oid                     relid;
64
65         /*
66          * permissions checking.  this would normally be done in utility.c,
67          * but this particular routine is recursive.
68          *
69          * normally, only the owner of a class can change its schema.
70          */
71         if (!allowSystemTableMods && IsSystemRelationName(relname))
72                 elog(ERROR, "renameatt: class \"%s\" is a system catalog",
73                          relname);
74 #ifndef NO_SECURITY
75         if (!IsBootstrapProcessingMode() &&
76                 !pg_ownercheck(userName, relname, RELNAME))
77                 elog(ERROR, "renameatt: you do not own class \"%s\"",
78                          relname);
79 #endif
80
81         /*
82          * Grab an exclusive lock on the target table, which we will NOT
83          * release until end of transaction.
84          */
85         targetrelation = heap_openr(relname, AccessExclusiveLock);
86         relid = RelationGetRelid(targetrelation);
87         heap_close(targetrelation, NoLock); /* close rel but keep lock! */
88
89         /*
90          * if the 'recurse' flag is set then we are supposed to rename this
91          * attribute in all classes that inherit from 'relname' (as well as in
92          * 'relname').
93          *
94          * any permissions or problems with duplicate attributes will cause the
95          * whole transaction to abort, which is what we want -- all or
96          * nothing.
97          */
98         if (recurse)
99         {
100                 List       *child,
101                                    *children;
102
103                 /* this routine is actually in the planner */
104                 children = find_all_inheritors(relid);
105
106                 /*
107                  * find_all_inheritors does the recursive search of the
108                  * inheritance hierarchy, so all we have to do is process all of
109                  * the relids in the list that it returns.
110                  */
111                 foreach(child, children)
112                 {
113                         Oid                     childrelid = lfirsti(child);
114                         char            childname[NAMEDATALEN];
115
116                         if (childrelid == relid)
117                                 continue;
118                         reltup = SearchSysCacheTuple(RELOID,
119                                                                                  ObjectIdGetDatum(childrelid),
120                                                                                  0, 0, 0);
121                         if (!HeapTupleIsValid(reltup))
122                         {
123                                 elog(ERROR, "renameatt: can't find catalog entry for inheriting class with oid %u",
124                                          childrelid);
125                         }
126                         /* make copy of cache value, could disappear in call */
127                         StrNCpy(childname,
128                                         NameStr(((Form_pg_class) GETSTRUCT(reltup))->relname),
129                                         NAMEDATALEN);
130                         /* note we need not recurse again! */
131                         renameatt(childname, oldattname, newattname, userName, 0);
132                 }
133         }
134
135         attrelation = heap_openr(AttributeRelationName, RowExclusiveLock);
136
137         oldatttup = SearchSysCacheTupleCopy(ATTNAME,
138                                                                                 ObjectIdGetDatum(relid),
139                                                                                 PointerGetDatum(oldattname),
140                                                                                 0, 0);
141         if (!HeapTupleIsValid(oldatttup))
142                 elog(ERROR, "renameatt: attribute \"%s\" nonexistent", oldattname);
143
144         if (((Form_pg_attribute) GETSTRUCT(oldatttup))->attnum < 0)
145                 elog(ERROR, "renameatt: system attribute \"%s\" not renamed", oldattname);
146
147         newatttup = SearchSysCacheTuple(ATTNAME,
148                                                                         ObjectIdGetDatum(relid),
149                                                                         PointerGetDatum(newattname),
150                                                                         0, 0);
151         /* should not already exist */
152         if (HeapTupleIsValid(newatttup))
153         {
154                 heap_freetuple(oldatttup);
155                 elog(ERROR, "renameatt: attribute \"%s\" exists", newattname);
156         }
157
158         StrNCpy(NameStr(((Form_pg_attribute) GETSTRUCT(oldatttup))->attname),
159                         newattname, NAMEDATALEN);
160
161         heap_update(attrelation, &oldatttup->t_self, oldatttup, NULL);
162
163         /* keep system catalog indices current */
164         {
165                 Relation        irelations[Num_pg_attr_indices];
166
167                 CatalogOpenIndices(Num_pg_attr_indices, Name_pg_attr_indices, irelations);
168                 CatalogIndexInsert(irelations, Num_pg_attr_indices, attrelation, oldatttup);
169                 CatalogCloseIndices(Num_pg_attr_indices, irelations);
170         }
171
172         heap_freetuple(oldatttup);
173         heap_close(attrelation, RowExclusiveLock);
174 }
175
176 /*
177  *              renamerel               - change the name of a relation
178  */
179 void
180 renamerel(const char *oldrelname, const char *newrelname)
181 {
182         int                     i;
183         Relation        targetrelation;
184         Relation        relrelation;    /* for RELATION relation */
185         HeapTuple       oldreltup;
186         Oid                     reloid;
187         char            relkind;
188         char            oldpath[MAXPGPATH],
189                                 newpath[MAXPGPATH],
190                                 toldpath[MAXPGPATH + 10],
191                                 tnewpath[MAXPGPATH + 10];
192         Relation        irelations[Num_pg_class_indices];
193
194         if (!allowSystemTableMods && IsSystemRelationName(oldrelname))
195                 elog(ERROR, "renamerel: system relation \"%s\" not renamed",
196                          oldrelname);
197
198         if (!allowSystemTableMods && IsSystemRelationName(newrelname))
199                 elog(ERROR, "renamerel: Illegal class name: \"%s\" -- pg_ is reserved for system catalogs",
200                          newrelname);
201
202         /*
203          * Instead of using heap_openr(), do it the hard way, so that we
204          * can rename indexes as well as regular relations.
205          */
206         targetrelation = RelationNameGetRelation(oldrelname);
207
208         if (!RelationIsValid(targetrelation))
209                 elog(ERROR, "Relation '%s' does not exist", oldrelname);
210
211         /*
212          * Grab an exclusive lock on the target table, which we will NOT
213          * release until end of transaction.
214          */
215         LockRelation(targetrelation, AccessExclusiveLock);
216
217         /* ----------------
218          *      RENAME TABLE within a transaction block is dangerous, because
219          *      if the transaction is later rolled back we have no way to
220          *      undo the rename of the relation's physical file.  For now, allow it
221          *      but emit a warning message.
222          *      Someday we might want to consider postponing the physical rename
223          *      until transaction commit, but that's a lot of work...
224          *      The only case that actually works right is for relations created
225          *      in the current transaction, since the post-abort state would be that
226          *      they don't exist anyway.  So, no warning in that case.
227          * ----------------
228          */
229         if (IsTransactionBlock() && !targetrelation->rd_myxactonly)
230                 elog(NOTICE, "Caution: RENAME TABLE cannot be rolled back, so don't abort now");
231
232         reloid = RelationGetRelid(targetrelation);
233         relkind = targetrelation->rd_rel->relkind;
234
235         /*
236          * Flush all blocks of the relation out of the buffer pool.  We need
237          * this because the blocks are marked with the relation's name as well
238          * as OID. If some backend tries to write a dirty buffer with
239          * mdblindwrt after we've renamed the physical file, we'll be in big
240          * trouble.
241          *
242          * Since we hold the exclusive lock on the relation, we don't have to
243          * worry about more blocks being read in while we finish the rename.
244          */
245         if (FlushRelationBuffers(targetrelation, (BlockNumber) 0) < 0)
246                 elog(ERROR, "renamerel: unable to flush relation from buffer pool");
247
248         /*
249          * Make sure smgr and lower levels close the relation's files. (Next
250          * access to rel will reopen them.)
251          *
252          * Note: we rely on shared cache invalidation message to make other
253          * backends close and re-open the files.
254          */
255         smgrclose(DEFAULT_SMGR, targetrelation);
256
257         /*
258          * Close rel, but keep exclusive lock!
259          */
260         heap_close(targetrelation, NoLock);
261
262         /*
263          * Flush the relcache entry (easier than trying to change it at exactly
264          * the right instant).  It'll get rebuilt on next access to relation.
265          *
266          * XXX What if relation is myxactonly?
267          */
268         targetrelation = NULL;          /* make sure I don't touch it again */
269         RelationIdInvalidateRelationCacheByRelationId(reloid);
270
271         /*
272          * Find relation's pg_class tuple, and make sure newrelname isn't in
273          * use.
274          */
275         relrelation = heap_openr(RelationRelationName, RowExclusiveLock);
276
277         oldreltup = SearchSysCacheTupleCopy(RELNAME,
278                                                                                 PointerGetDatum(oldrelname),
279                                                                                 0, 0, 0);
280         if (!HeapTupleIsValid(oldreltup))
281                 elog(ERROR, "renamerel: relation \"%s\" does not exist", oldrelname);
282
283         if (RelnameFindRelid(newrelname) != InvalidOid)
284                 elog(ERROR, "renamerel: relation \"%s\" exists", newrelname);
285
286         /*
287          * Update pg_class tuple with new relname.
288          */
289         StrNCpy(NameStr(((Form_pg_class) GETSTRUCT(oldreltup))->relname),
290                         newrelname, NAMEDATALEN);
291
292         heap_update(relrelation, &oldreltup->t_self, oldreltup, NULL);
293
294         /* keep the system catalog indices current */
295         CatalogOpenIndices(Num_pg_class_indices, Name_pg_class_indices, irelations);
296         CatalogIndexInsert(irelations, Num_pg_class_indices, relrelation, oldreltup);
297         CatalogCloseIndices(Num_pg_class_indices, irelations);
298
299         heap_close(relrelation, NoLock);
300
301         /*
302          * Also rename the associated type, if any.
303          */
304         if (relkind != RELKIND_INDEX)
305                 TypeRename(oldrelname, newrelname);
306
307         /*
308          * Perform physical rename of files.  If this fails, we haven't yet
309          * done anything irreversible.  NOTE that this MUST be the last step;
310          * an error occurring afterwards would leave the relation hosed!
311          *
312          * XXX smgr.c ought to provide an interface for this; doing it directly
313          * is bletcherous.
314          */
315         strcpy(oldpath, relpath(oldrelname));
316         strcpy(newpath, relpath(newrelname));
317         if (rename(oldpath, newpath) < 0)
318                 elog(ERROR, "renamerel: unable to rename %s to %s: %m",
319                          oldpath, newpath);
320
321         /* rename additional segments of relation, too */
322         for (i = 1;; i++)
323         {
324                 sprintf(toldpath, "%s.%d", oldpath, i);
325                 sprintf(tnewpath, "%s.%d", newpath, i);
326                 if (rename(toldpath, tnewpath) < 0)
327                 {
328                         /* expected case is that there's not another segment file */
329                         if (errno == ENOENT)
330                                 break;
331                         /* otherwise we're up the creek... */
332                         elog(ERROR, "renamerel: unable to rename %s to %s: %m",
333                                  toldpath, tnewpath);
334                 }
335         }
336 }