~launchpad-pqm/launchpad/devel

10637.3.1 by Guilherme Salgado
Use the default python version instead of a hard-coded version
1
#!/usr/bin/python
8452.3.3 by Karl Fogel
* utilities/: Add copyright header block to source files that were
2
#
8687.15.2 by Karl Fogel
In files modified by r8688, change "<YEARS>" to "2009", as per
3
# Copyright 2009 Canonical Ltd.  This software is licensed under the
8687.15.3 by Karl Fogel
Shorten the copyright header block to two lines.
4
# GNU Affero General Public License version 3 (see the file LICENSE).
8452.3.3 by Karl Fogel
* utilities/: Add copyright header block to source files that were
5
1745 by Canonical.com Patch Queue Manager
[r=jamesh] Simple database restoration script and sampledata fixes
6
"""
7
Restore a full database dump. This script should become unnecessary
8
when we are running PostgreSQL 8 as it will correctly order its dumps.
9
"""
10
11
from optparse import OptionParser
14612.2.6 by William Grant
utilities
12
import os
1876 by Canonical.com Patch Queue Manager
[r=jamesh] database dump script
13
from signal import SIGTERM
14612.2.6 by William Grant
utilities
14
from subprocess import (
15
    PIPE,
16
    Popen,
17
    )
18
import sys
19
import tempfile
20
1876 by Canonical.com Patch Queue Manager
[r=jamesh] database dump script
21
22
class DumpFile(object):
23
    """File-like object wrapping a normal or compressed file on disk"""
24
    process = None
25
26
    def __init__(self, dumpname):
27
28
        if dumpname.endswith('.bz2'):
29
            self.process = Popen(
30
                    ["bunzip2", "-c", dumpname], stdin=PIPE, stdout=PIPE
31
                    )
32
        elif dumpname.endswith('.gz'):
33
            self.process = Popen(
34
                    ["gunzip", "-c", dumpname], stdin=PIPE, stdout=PIPE
35
                    )
36
        else:
37
            self.out = open(dumpname, 'r')
38
39
        if self.process is not None:
40
            self.process.stdin.close()
41
            self.out = self.process.stdout
42
43
    def read(self, bytes=None):
44
        return self.out.read(bytes)
45
46
    def close(self):
47
        if self.process is None:
48
            return self.out.close()
49
50
        if self.process.poll() is not None:
51
            if self.process.returncode != 0:
52
                print >> sys.stderr, "ERROR: Uncompressor returned %d" % (
53
                        self.process.returncode
54
                        )
55
            return
56
57
        os.kill(self.process.pid, SIGTERM)
58
59
    def fileno(self):
60
        return self.out.fileno()
61
1745 by Canonical.com Patch Queue Manager
[r=jamesh] Simple database restoration script and sampledata fixes
62
63
def generate_order(dumpname):
64
    """Generate a correctly order dump listing"""
65
   
1876 by Canonical.com Patch Queue Manager
[r=jamesh] database dump script
66
    dump_input = DumpFile(dumpname)
67
    cmd = Popen('pg_restore -l', shell=True, stdout=PIPE, stdin=dump_input)
1745 by Canonical.com Patch Queue Manager
[r=jamesh] Simple database restoration script and sampledata fixes
68
    (stdout, stderr) = cmd.communicate()
69
    if cmd.returncode != 0:
70
        raise RuntimeError('pg_restore returned %d' % rv)
1876 by Canonical.com Patch Queue Manager
[r=jamesh] database dump script
71
    dump_input.close()
1745 by Canonical.com Patch Queue Manager
[r=jamesh] Simple database restoration script and sampledata fixes
72
73
    full_listing = [l for l in stdout.split('\n') if l.strip()]
74
75
    full_listing.sort(listing_cmp)
76
77
    return '\n'.join(full_listing)
78
1876 by Canonical.com Patch Queue Manager
[r=jamesh] database dump script
79
1745 by Canonical.com Patch Queue Manager
[r=jamesh] Simple database restoration script and sampledata fixes
80
def listing_cmp(a, b):
2936.1.1 by Stuart Bishop
Update pgrestore.py to resture PostgreSQL 7.4 dumps under PostgreSQL 8.0
81
    if POSTGRESQL7:
82
        idx = 2
83
    else:
84
        idx = 3
1745 by Canonical.com Patch Queue Manager
[r=jamesh] Simple database restoration script and sampledata fixes
85
    if a.startswith(';'):
86
        atype = ';'
87
    else:
2936.1.1 by Stuart Bishop
Update pgrestore.py to resture PostgreSQL 7.4 dumps under PostgreSQL 8.0
88
        atype = a.split()[idx]
1745 by Canonical.com Patch Queue Manager
[r=jamesh] Simple database restoration script and sampledata fixes
89
90
    if b.startswith(';'):
91
        btype = ';'
92
    else:
2936.1.1 by Stuart Bishop
Update pgrestore.py to resture PostgreSQL 7.4 dumps under PostgreSQL 8.0
93
        btype = b.split()[idx]
1745 by Canonical.com Patch Queue Manager
[r=jamesh] Simple database restoration script and sampledata fixes
94
2936.1.3 by Stuart Bishop
pgrestore helper should create INDEXes earlier so they can be used by contraints checks
95
    # Build indexes in same parse as tables, so that tables with constraints
96
    # that need to reference other tables (eg. CHECK is_person() columns)
97
    # load in a reasonable time. This is more fragile, but should last
98
    # until postgresql 8 migration makes this script irrelevant.
1745 by Canonical.com Patch Queue Manager
[r=jamesh] Simple database restoration script and sampledata fixes
99
    scores = {
100
        ';': 0,
101
        'SCHEMA': 1,
102
        'TYPE': 10,
103
        'FUNC': 10,
104
        'PROCEDURAL': 10,
105
        'FUNCTION': 10,
106
        'OPERATOR': 20,
107
        'TABLE': 30,
108
        'SEQUENCE': 35,
109
        'BLOBS': 38,
110
        'VIEW': 40,
111
        'TRIGGER': 90,
112
        'FK': 95,
113
        'CONSTRAINT': 95,
2936.1.3 by Stuart Bishop
pgrestore helper should create INDEXes earlier so they can be used by contraints checks
114
        'INDEX': 30,
1745 by Canonical.com Patch Queue Manager
[r=jamesh] Simple database restoration script and sampledata fixes
115
        'COMMENT': 200,
116
        'ACL': 1000,
117
        }
118
119
    # Will fail if we get an unknown type in the listing, which is by design
120
    # at the moment. Might want a default value though instead?
121
    return cmp(scores[atype], scores[btype])
122
1876 by Canonical.com Patch Queue Manager
[r=jamesh] database dump script
123
1745 by Canonical.com Patch Queue Manager
[r=jamesh] Simple database restoration script and sampledata fixes
124
def createdb(options):
125
    args = ['createdb', '--encoding=UNICODE']
126
127
    if options.user:
128
        args.append('--username=%s' % options.user)
129
130
    if options.host:
131
        args.append('--host=%s' % options.host)
132
133
    args.append(options.dbname)
134
135
    if options.verbose:
136
        cmd = ' '.join(args)
137
        print >> sys.stderr, 'Running %s' % cmd
1876 by Canonical.com Patch Queue Manager
[r=jamesh] database dump script
138
        createdb = Popen(args)
1745 by Canonical.com Patch Queue Manager
[r=jamesh] Simple database restoration script and sampledata fixes
139
    else:
1876 by Canonical.com Patch Queue Manager
[r=jamesh] database dump script
140
        createdb = Popen(args, stdout=PIPE, stderr=PIPE)
1745 by Canonical.com Patch Queue Manager
[r=jamesh] Simple database restoration script and sampledata fixes
141
142
    (out, err) = createdb.communicate()
143
144
    if createdb.returncode != 0:
145
        print >> sys.stderr, err
146
        print >> sys.stderr, out
147
        print >> sys.stderr, 'ERROR: %d' % createdb.returncode
148
        sys.exit(createdb.returncode)
149
150
151
if __name__ == "__main__":
152
153
    parser = OptionParser("usage: %prog [options] [DUMPFILE | -]")
154
    parser.add_option(
155
            "-d", "--dbname", dest="dbname",
156
            help="Create the database DBNAME and restore the dump into it",
157
            metavar="DBNAME"
158
            )
159
    parser.add_option(
160
            "-H", "--host", dest="host", default=None,
161
            help="Connect to PostgreSQL running on HOST",
162
            metavar="HOST"
163
            )
164
    parser.add_option(
165
            "--no-acl", dest="perms", action="store_false",
166
            default=True, help="Do not restore ownership or permissions",
167
            )
168
    parser.add_option(
169
            "-U", "--user", dest="user",
170
            help="Connect as superuser USER", metavar="USER", default=None
171
            )
172
    parser.add_option(
173
            "-v", "--verbose", dest="verbose",
174
            action="store_true", default=False
175
            )
2936.1.1 by Stuart Bishop
Update pgrestore.py to resture PostgreSQL 7.4 dumps under PostgreSQL 8.0
176
    parser.add_option(
177
            "-7", dest="postgres7",
178
            action="store_true", default=False,
179
            help="Restore into a PostgreSQL 7.4 database"
180
            )
1745 by Canonical.com Patch Queue Manager
[r=jamesh] Simple database restoration script and sampledata fixes
181
182
    (options, args) = parser.parse_args()
183
2936.1.1 by Stuart Bishop
Update pgrestore.py to resture PostgreSQL 7.4 dumps under PostgreSQL 8.0
184
    if options.postgres7:
185
        POSTGRESQL7 = True
186
    else:
187
        POSTGRESQL7 = False
188
1745 by Canonical.com Patch Queue Manager
[r=jamesh] Simple database restoration script and sampledata fixes
189
    if len(args) > 1:
190
        parser.error("Too many arguments")
191
192
    if len(args) == 0:
1876 by Canonical.com Patch Queue Manager
[r=jamesh] database dump script
193
        parser.error("Must specify dump file name.")
1745 by Canonical.com Patch Queue Manager
[r=jamesh] Simple database restoration script and sampledata fixes
194
195
    dumpname = args[0]
196
197
    if not os.path.exists(dumpname):
198
        parser.error("%s does not exist" % dumpname)
199
200
    handle, listingfilename = tempfile.mkstemp()
201
    try:
202
        os.close(handle)
203
        listingfile = open(listingfilename, 'w')
204
        listingfile.write(generate_order(dumpname))
205
        listingfile.close()
206
207
        pg_restore_args = ["pg_restore", "--use-list=%s" % listingfilename]
208
209
        if options.dbname:
210
            createdb(options)
211
            pg_restore_args.append("--dbname=%s" % options.dbname)
212
213
        if not options.perms:
214
            pg_restore_args.append("--no-owner")
215
            pg_restore_args.append("--no-acl")
216
217
        if options.user:
218
            pg_restore_args.append("--user=%s" % options.user)
219
220
        if options.host:
221
            pg_restore_args.append("--host=%s" % options.host)
222
223
        if options.verbose:
224
            pg_restore_args.append("--verbose")
225
1876 by Canonical.com Patch Queue Manager
[r=jamesh] database dump script
226
        dump_input = DumpFile(dumpname)
1745 by Canonical.com Patch Queue Manager
[r=jamesh] Simple database restoration script and sampledata fixes
227
228
        if options.verbose:
229
            cmd = ' '.join(pg_restore_args)
230
            print >> sys.stderr, "Running %s" % cmd
1876 by Canonical.com Patch Queue Manager
[r=jamesh] database dump script
231
            rest = Popen(pg_restore_args, stdin=dump_input)
1745 by Canonical.com Patch Queue Manager
[r=jamesh] Simple database restoration script and sampledata fixes
232
        else:
1876 by Canonical.com Patch Queue Manager
[r=jamesh] database dump script
233
            rest = Popen(pg_restore_args, stderr=PIPE, stdin=dump_input)
1745 by Canonical.com Patch Queue Manager
[r=jamesh] Simple database restoration script and sampledata fixes
234
235
        (out,err) = rest.communicate()
236
        if rest.returncode != 0:
237
            print >> sys.stderr, err
238
            print >> sys.stderr, 'ERROR: %d' % rest.returncode
239
            sys.exit(rest.returncode)
240
241
    finally:
242
        os.unlink(listingfilename)
243
244