~vcs-imports/mammoth-replicator/trunk

« back to all changes in this revision

Viewing changes to contrib/pg_upgrade/pg_upgrade

  • Committer: alvherre
  • Date: 2005-12-16 21:24:52 UTC
  • Revision ID: svn-v4:db760fc0-0f08-0410-9d63-cc6633f64896:trunk:1
Initial import of the REL8_0_3 sources from the Pgsql CVS repository.

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
#!/bin/sh
 
2
#
 
3
# pg_upgrade: update a database without needing a full dump/reload cycle.
 
4
# CAUTION: Read the manual page before trying to use this!
 
5
 
 
6
# $PostgreSQL: pgsql/contrib/pg_upgrade/pg_upgrade,v 1.16 2003-11-29 19:51:35 pgsql Exp $
 
7
#
 
8
# To migrate this to newer versions of PostgreSQL:  
 
9
#       1)  Update the version numbers at the top of the file
 
10
#       2)  Search for specific version mentions in the script and update
 
11
#           accordingly.
 
12
#       3)  Add changes for next version.
 
13
 
 
14
#set -x
 
15
 
 
16
# UPGRADE_VERSION is the expected old database version
 
17
UPGRADE_VERSION="7.3"
 
18
CUR_VERSION="7.3"
 
19
 
 
20
# Set this to "Y" to enable this program
 
21
ENABLE="Y"
 
22
 
 
23
if [ "$ENABLE" != "Y" ]
 
24
then
 
25
        echo "Sorry, $0 cannot upgrade database version $SRC_VERSION to $DST_VERSION." 1>&2
 
26
        echo "The on-disk structure of tables has changed." 1>&2
 
27
        echo "You will need to dump and restore using pg_dumpall." 1>&2
 
28
        exit 1
 
29
fi
 
30
 
 
31
 
 
32
trap "rm -f /tmp/$$.*" 0 1 2 3 15
 
33
 
 
34
BASENAME=`basename "$0"`
 
35
PHASE=""
 
36
 
 
37
while [ "$#" -ne 0 ]
 
38
do
 
39
        if [ "X$1" = "X-1" ]
 
40
        then    PHASE="1"
 
41
                shift
 
42
        elif [ "X$1" = "X-2" ]
 
43
        then    PHASE="2"
 
44
                shift
 
45
        elif [ "X$1" = "X-D" ]
 
46
        then    PGDATA="$2"
 
47
                shift 2
 
48
        fi
 
49
done
 
50
 
 
51
if [ "$PHASE" = "" ]
 
52
then    echo "You must run $BASENAME in either mode 1 or mode 2." 1>&2
 
53
        echo "Usage:  $BASENAME [-D datadir] -1 | -2" 1>&2
 
54
        exit 1
 
55
fi
 
56
 
 
57
if [ "$PGDATA" = "" ]
 
58
then    echo "You must set the PGDATA environment variable or specify it with -D." 1>&2
 
59
        echo "Usage:  $BASENAME [-D datadir] -1 | -2" 1>&2
 
60
        exit 1
 
61
fi
 
62
 
 
63
if [ ! -d "$PGDATA" ]
 
64
then    echo "$PGDATA does not exist.  Exiting." 1>&2
 
65
        if [ "$PHASE" -eq 2 ]
 
66
        then    echo "Perhaps you didn't run initdb." 1>&2
 
67
        fi
 
68
        exit 1
 
69
fi
 
70
 
 
71
if [ "$USER" = "root" -o ! -r "$PGDATA"/PG_VERSION ]
 
72
then    echo "You must run this as the PostgreSQL superuser.  Exiting." 1>&2
 
73
        exit 1
 
74
fi
 
75
 
 
76
# Strip off the trailing directory name and store our data there
 
77
# in the hope we are in the same filesystem so 'mv 'works.
 
78
 
 
79
INFODIR=`dirname "$PGDATA"`/pg_upgrade_info
 
80
SAVEDATA="$INFODIR"/data
 
81
 
 
82
make_dbobjoidmap()
 
83
{
 
84
        psql -d template1 -At -c "SELECT datname FROM pg_database" |
 
85
        grep -v '^template0$' |
 
86
        while read DB
 
87
        do      
 
88
                QUERY="SELECT   relname, oid
 
89
                        FROM    pg_class
 
90
                        WHERE   relkind = 'r' OR
 
91
                                relkind = 'i' OR
 
92
                                relkind = 'S' OR
 
93
                                relkind = 't';"
 
94
 
 
95
                psql -d "$DB" -At -F'   ' -c "$QUERY" |
 
96
                while read RELNAME_OID
 
97
                do      
 
98
                        echo "$DB       $RELNAME_OID"
 
99
                done
 
100
        done
 
101
}
 
102
 
 
103
 
 
104
make_dboidmap()
 
105
{
 
106
        psql -d template1 -At -F'       ' -c \
 
107
                'SELECT datname, oid FROM pg_database;' |
 
108
        grep -v '^template0$'
 
109
}
 
110
 
 
111
 
 
112
move_objfiles()
 
113
{
 
114
        # Test to make sure there is a matching file in each place
 
115
 
 
116
        if [ ! -f "$SAVEDATA"/base/"$SRC_DBOID"/"$SRC_OID" -a \
 
117
             ! -h "$SAVEDATA"/base/"$SRC_DBOID"/"$SRC_OID" ]
 
118
        then    echo "Moving of database $DB, OID $SRC_OID, object $OBJ failed." 1>&2
 
119
                echo "File not found.  Exiting." 1>&2
 
120
                return 1
 
121
        fi
 
122
 
 
123
        if [ ! -f "$PGDATA"/base/"$DST_DBOID"/"$DST_OID" -a \
 
124
             ! -h "$PGDATA"/base/"$DST_DBOID"/"$DST_OID" ]
 
125
        then    echo "Moving of database $DB, OID $DST_OID, object $OBJ failed." 1>&2
 
126
                echo "File not found.  Exiting." 1>&2
 
127
                return 1
 
128
        fi
 
129
 
 
130
        # Move files
 
131
 
 
132
        mv -f "$SAVEDATA"/base/"$SRC_DBOID"/"$SRC_OID" "$PGDATA"/base/"$DST_DBOID"/"$DST_OID"
 
133
        if [ "$?" -ne 0 ]
 
134
        then    echo "Moving of database $DB, OID $SRC_OID, object $OBJ" 1>&2
 
135
                echo "to $DST_OID failed.  Exiting" 1>&2
 
136
                return 1
 
137
        fi
 
138
 
 
139
        # handle table extents
 
140
 
 
141
        ls "$SAVEDATA"/base/"$SRC_DBOID"/"$SRC_OID".* 2> /dev/null | while read FILE
 
142
        do
 
143
                EXT=`basename "$FILE" | sed 's/^.*\.\(.*\)$/\1/'`
 
144
                mv -f "$FILE" "$PGDATA"/base/"$DST_DBOID"/"$DST_OID"."$EXT"
 
145
                if [ "$?" -ne 0 ]
 
146
                then    echo "Moving of database $DB, OID $SRC_OID, object $OBJ" 1>&2
 
147
                        echo "to $DST_OID failed.  Exiting." 1>&2
 
148
                        return 1
 
149
                fi
 
150
        done
 
151
}
 
152
 
 
153
if [ "$PHASE" -eq 1 ]
 
154
then
 
155
 
 
156
        ##########################
 
157
        #  Phase 1 starts here   #
 
158
        ##########################
 
159
 
 
160
 
 
161
        if [ ! -d "$PGDATA"/base/1 ]
 
162
        then    echo "There is no database template1 in $PGDATA/base." 1>&2
 
163
                exit 1
 
164
        fi
 
165
 
 
166
        # get version
 
167
        SRC_VERSION=`cat "$PGDATA"/PG_VERSION`
 
168
        if [ "$SRC_VERSION" = "" ]
 
169
        then    echo "$BASENAME can not find the PostgreSQL version file" 1>&2
 
170
                echo "'$PGDATA/PG_VERSION'.  Exiting." 1>&2
 
171
                exit 1
 
172
        fi
 
173
 
 
174
        if [    "$SRC_VERSION" != "$CUR_VERSION" -a \
 
175
                "$SRC_VERSION" != "$UPGRADE_VERSION" ]
 
176
        then    echo "$BASENAME supports versions $UPGRADE_VERSION and $CUR_VERSION only." 1>&2
 
177
                echo "However, your database is version $SRC_VERSION." 1>&2
 
178
                echo "You will need to dump and restore using pg_dumpall.  Exiting." 1>&2
 
179
                exit 1
 
180
        fi
 
181
 
 
182
        # If server is down, start it so we can do some work.
 
183
        if ! pg_ctl status | sed -n '1p' | grep "is running" > /dev/null 2>&1
 
184
        then    pg_ctl -w start
 
185
                if [ $? -ne 0 ]
 
186
                then    echo "Can not start server.  Exiting." 1>&2
 
187
                        exit 1
 
188
                fi
 
189
        fi
 
190
 
 
191
        # create directory for our data
 
192
        if ! rm -rf "$INFODIR" 
 
193
        then    echo "Deletion of old pg_upgrade_info directory $INFODIR failed." 1>&2
 
194
                echo "Exiting." 1>&2
 
195
                exit 1
 
196
        fi
 
197
        if ! mkdir "$INFODIR"
 
198
        then    echo "Creation of new pg_upgrade_info directory $INFODIR failed." 1>&2
 
199
                echo "Exiting." 1>&2
 
200
                exit 1
 
201
        fi
 
202
 
 
203
        if ! chmod og-rwx "$INFODIR"
 
204
        then    echo "Permission change on new pg_upgrade_info directory $INFODIR failed." 1>&2
 
205
                echo "Exiting." 1>&2
 
206
                exit 1
 
207
        fi
 
208
 
 
209
 
 
210
        # Dump schema
 
211
        pg_dumpall -s > "$INFODIR"/schema
 
212
        if [ $? -ne 0 ]
 
213
        then    echo "Can not dump schema.  Exiting." 1>&2
 
214
                exit 1
 
215
        fi
 
216
        
 
217
        # Generate mappings for database
 
218
        make_dboidmap > "$INFODIR"/dboidmap || exit "$?"
 
219
        make_dbobjoidmap > "$INFODIR"/dbobjoidmap || exit "$?"
 
220
 
 
221
        # Vacuum all databases to remove exipired rows.
 
222
        # We will lose our transaction log file during the upgrade so we
 
223
        # have to do this.
 
224
 
 
225
        vacuumdb -a
 
226
        if [ $? -ne 0 ]
 
227
        then    echo "Can not vacuum server.  Exiting." 1>&2
 
228
                exit 1
 
229
        fi
 
230
 
 
231
        # Stop server so we can move the directory.
 
232
        pg_ctl -w stop
 
233
        if [ $? -ne 0 ]
 
234
        then    echo "Can not stop server.  Exiting." 1>&2
 
235
                exit 1
 
236
        fi
 
237
        
 
238
        # No matter what the directory name, call it data
 
239
        mv "$PGDATA" "$INFODIR"/data
 
240
        if [ $? -ne 0 ]
 
241
        then    echo "Can not move old $PGDATA out of the way.  Exiting." 1>&2
 
242
                exit 1
 
243
        fi
 
244
        echo
 
245
        echo
 
246
        echo "$BASENAME phase 1 completed."
 
247
        echo "Continue with the steps outlined in the $BASENAME manual page."
 
248
        exit 0
 
249
fi
 
250
 
 
251
 
 
252
        ##########################
 
253
        #  Phase 2 starts here   #
 
254
        ##########################
 
255
 
 
256
# check things
 
257
 
 
258
if [ ! -d "$INFODIR" ]
 
259
then    echo "There is no '$INFODIR' directory from the phase 1 run of $BASENAME." 1>&2
 
260
        exit 1  
 
261
fi
 
262
 
 
263
if [ ! -d "$SAVEDATA" ]
 
264
then    echo "There is no '$SAVEDATA' directory from the phase 1 run of $BASENAME." 1>&2
 
265
        exit 1  
 
266
fi
 
267
 
 
268
if [ ! -f "$SAVEDATA/PG_VERSION" ]
 
269
then    echo "Cannot read '$SAVEDATA/PG_VERSION' --- something is wrong." 1>&2
 
270
        exit 1
 
271
fi
 
272
 
 
273
if [ ! -f "$PGDATA/PG_VERSION" ]
 
274
then    echo "Cannot read '$PGDATA/PG_VERSION' --- something is wrong." 1>&2
 
275
        exit 1
 
276
fi
 
277
 
 
278
if [ ! -d "$PGDATA/base/1" ]
 
279
then    echo "Cannot find database template1 in '$PGDATA/base'." 1>&2
 
280
        echo "Are you running $BASENAME as the postgres superuser?" 1>&2
 
281
        exit 1
 
282
fi
 
283
 
 
284
# Get the actual versions seen in the data dirs.
 
285
 
 
286
SRC_VERSION=`cat "$SAVEDATA"/PG_VERSION`
 
287
DST_VERSION=`cat "$PGDATA"/PG_VERSION`
 
288
 
 
289
# Check for version compatibility.
 
290
# This code will need to be updated/reviewed for each new PostgreSQL release.
 
291
 
 
292
if [ "$DST_VERSION" != "$CUR_VERSION" ]
 
293
then    echo "$BASENAME is for PostgreSQL version $CUR_VERSION" 1>&2
 
294
        echo "but $PGDATA/PG_VERSION contains $DST_VERSION." 1>&2
 
295
        echo "Did you run initdb for version $UPGRADE_VERSION by mistake?" 1>&2
 
296
        exit 1
 
297
fi
 
298
 
 
299
# Stop server for pg_resetxlog use
 
300
 
 
301
if pg_ctl status | sed -n '1p' | grep "is running" > /dev/null 2>&1
 
302
then    pg_ctl -w stop
 
303
        if [ $? -ne 0 ]
 
304
        then    echo "Can not start server.  Exiting." 1>&2
 
305
                exit 1
 
306
        fi
 
307
fi
 
308
 
 
309
# check for proper pg_resetxlog version
 
310
 
 
311
pg_resetxlog 2> /dev/null
 
312
# file not found status is normally 127, not 1
 
313
if [ "$?" -ne 1 ]
 
314
then    echo "Unable to find pg_resetxlog in your path." 1>&2
 
315
        echo "Install it from pgsql/contrib/pg_resetxlog and continue.  Exiting." 1>&2
 
316
        exit 1
 
317
fi
 
318
 
 
319
if ! pg_resetxlog -x 2>&1 | grep 'xid' > /dev/null 2>&1
 
320
then    echo "Old version of pg_resetxlog found in path." 1>&2
 
321
        echo "Install a newer version of pg_resetxlog from pgsql/contrib/pg_resetxlog." 1>&2
 
322
        echo "Exiting." 1>&2
 
323
        exit 1
 
324
fi
 
325
 
 
326
SRC_XID=`pg_resetxlog -n "$SAVEDATA" | grep "NextXID" | awk -F'  *' '{print $4}'`
 
327
DST_XID=`pg_resetxlog -n "$PGDATA" | grep "NextXID" | awk -F'  *' '{print $4}'`
 
328
 
 
329
# compare locales to make sure they match
 
330
 
 
331
pg_resetxlog -n "$SAVEDATA" | grep "^LC_" > /tmp/$$.0
 
332
pg_resetxlog -n "$PGDATA" | grep "^LC_" > /tmp/$$.1
 
333
if ! diff /tmp/$$.0 /tmp/$$.1 > /dev/null
 
334
then    echo "Locales do not match between the two versions.  Exiting." 1>&2
 
335
        exit 1
 
336
fi
 
337
 
 
338
# Restart postmaster
 
339
 
 
340
pg_ctl -w start
 
341
if [ $? -ne 0 ]
 
342
then    echo "Can not start server.  Exiting." 1>&2
 
343
        exit 1
 
344
fi
 
345
 
 
346
 
 
347
###################################
 
348
# Checking done.  Ready to proceed.
 
349
###################################
 
350
 
 
351
 
 
352
# Execute the schema script to create everything
 
353
 
 
354
psql template1 < "$INFODIR"/schema
 
355
if [ $? -ne 0 ]
 
356
then    echo "There were errors in the input script.  Exiting." 1>&2
 
357
        exit 1
 
358
fi
 
359
 
 
360
echo "Input script completed, fixing row commit statuses..."
 
361
 
 
362
# Generate mappings for new database
 
363
make_dboidmap > /tmp/$$.dboidmap || exit "$?"
 
364
make_dbobjoidmap > /tmp/$$.dbobjoidmap || exit "$?"
 
365
 
 
366
# we are done with SQL database access
 
367
# shutdown forces buffers to disk
 
368
 
 
369
pg_ctl -w stop
 
370
if [ "$?" -ne 0 ]
 
371
then    echo "Unable to stop database server.  Exiting." 1>&2
 
372
        exit 1
 
373
fi
 
374
 
 
375
echo "Commit fixes complete, moving data files..."
 
376
 
 
377
# Move table/index/sequence files
 
378
 
 
379
cat "$INFODIR"/dbobjoidmap | while read LINE
 
380
do
 
381
        DB=`echo "$LINE" | awk '{print $1}'`
 
382
        OBJ=`echo "$LINE" | awk '{print $2}'`
 
383
 
 
384
        # Skip system tables, except for pg_largeobject
 
385
        # pg_toast tables are handled later as part of the
 
386
        # base table move
 
387
        if [ `expr X"$OBJ" : X'pg_'` -eq 4 -a \
 
388
             `expr X"$OBJ" : X'pg_largeobject'` -ne 15 ]
 
389
        then    continue
 
390
        fi
 
391
 
 
392
        SRC_OID=`echo "$LINE" | awk '{print $3}'`
 
393
        SRC_DBOID=`grep "^$DB   " "$INFODIR"/dboidmap | awk '{print $2}'`
 
394
        DST_DBOID=`grep "^$DB   " /tmp/$$.dboidmap | awk '{print $2}'`
 
395
        DST_OID=`grep "^$DB     $OBJ    " /tmp/$$.dbobjoidmap | awk '{print $3}'`
 
396
 
 
397
        move_objfiles
 
398
 
 
399
        # Handle TOAST files if they exist
 
400
        if grep "^$DB   pg_toast_$SRC_OID       " "$INFODIR"/dbobjoidmap \
 
401
                > /dev/null 2>&1
 
402
        then    # toast heap
 
403
                SAVE_SRC_OID="$SRC_OID"
 
404
                SAVE_DST_OID="$DST_OID"
 
405
                SRC_OID=`grep "^$DB     pg_toast_$SAVE_SRC_OID  " \
 
406
                        "$INFODIR"/dbobjoidmap | awk '{print $3}'`
 
407
                DST_OID=`grep "^$DB     pg_toast_$SAVE_DST_OID  " \
 
408
                        /tmp/$$.dbobjoidmap | awk '{print $3}'`
 
409
                move_objfiles
 
410
                # toast index
 
411
                SRC_OID=`grep "^$DB     pg_toast_${SAVE_SRC_OID}_idx    " \
 
412
                        "$INFODIR"/dbobjoidmap | awk '{print $3}'`
 
413
                DST_OID=`grep "^$DB     pg_toast_${SAVE_DST_OID}_idx    " \
 
414
                        /tmp/$$.dbobjoidmap | awk '{print $3}'`
 
415
                move_objfiles
 
416
        fi
 
417
done
 
418
                                                                   
 
419
 
 
420
# Set this so future backends don't think these tuples are their own
 
421
# because it matches their own XID.
 
422
# Commit status already updated by vacuum above
 
423
# Set to maximum XID just in case SRC wrapped around recently and
 
424
# is lower than DST's database
 
425
 
 
426
if [ "$SRC_XID" -gt "$DST_XID" ]
 
427
then    MAX_XID="$SRC_XID"
 
428
else    MAX_XID="$DST_XID"
 
429
fi
 
430
 
 
431
pg_resetxlog -x "$MAX_XID" "$PGDATA"
 
432
if [ "$?" -ne 0 ]
 
433
then    echo "Unable to set new XID.  Exiting." 1>&2
 
434
        exit 1
 
435
fi
 
436
 
 
437
# Move over old WAL
 
438
 
 
439
rm -r "$PGDATA"/pg_xlog
 
440
mv -f "$SAVEDATA"/pg_xlog "$PGDATA"
 
441
 
 
442
# Move over old clog
 
443
 
 
444
rm -r "$PGDATA"/pg_clog
 
445
mv -f "$SAVEDATA"/pg_clog "$PGDATA"
 
446
 
 
447
# Set last log file id and segment from old database
 
448
 
 
449
LOG_ID=`pg_resetxlog -n "$SAVEDATA" | grep "Current log file id:" |
 
450
        awk -F'  *' '{print $5}'`
 
451
if [ "$LOG_ID" = "" ]
 
452
then    echo "Unable to get old log file id.  Exiting." 1>&2
 
453
        exit 1
 
454
fi
 
455
SEG_ID=`pg_resetxlog -n "$SAVEDATA" | grep "Next log file segment:" |
 
456
        awk -F'  *' '{print $5}'`
 
457
if [ "$SEG_ID" = "" ]
 
458
then    echo "Unable to get old log segment id.  Exiting." 1>&2
 
459
        exit 1
 
460
fi
 
461
 
 
462
# Set checkpoint location of new database
 
463
 
 
464
pg_resetxlog -l "$LOG_ID","$SEG_ID" "$PGDATA"
 
465
if [ "$?" -ne 0 ]
 
466
then    echo "Unable to set new log file/segment id.  Exiting." 1>&2
 
467
        exit 1
 
468
fi
 
469
 
 
470
# Restart server with moved data
 
471
 
 
472
pg_ctl -w start
 
473
if [ "$?" -ne 0 ]
 
474
then    echo "Unable to restart database server.  Exiting." 1>&2
 
475
        exit 1
 
476
fi
 
477
 
 
478
# Now that we have moved the WAL/transaction log files, vacuum again to
 
479
# mark install rows with fixed transaction ids to prevent problems on xid
 
480
# wraparound.
 
481
 
 
482
vacuumdb -a
 
483
if [ $? -ne 0 ]
 
484
then    echo "There were errors during VACUUM.  Exiting." 1>&2
 
485
        exit 1
 
486
fi
 
487
 
 
488
echo
 
489
echo
 
490
echo "$BASENAME phase 2 completed."
 
491
echo "You may remove the old database files with 'rm -r $INFODIR'."
 
492
exit 0