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