1
2
3
4
5
6
7
8
9 __version__ = "$Revision: 1.10 $"
10 __author__ = "Ian Haywood"
11 __license__ = "GPL (details at http://www.gnu.org)"
12
13
14 import sys, os, string, re, urllib2, logging
15
16
17 _log = logging.getLogger('gm.bootstrapper')
18 _log.info(__version__)
19
21 """
22 runs the shell command and returns a string
23 """
24 stdin, stdout = os.popen4 (cmd.group (1))
25 r = stdout.read ()
26 stdout.close()
27 stdin.close()
28 return r
29
31 """
32 performs backtick shell extension in a string
33 """
34 return re.sub (r"`(.*)`", shellrun, str)
35
37
39 """
40 db : the interpreter to connect to, must be a DBAPI compliant interface
41 """
42 self.conn = conn
43 self.vars = {'ON_ERROR_STOP':None}
44
46 match = re.match (str, self.line)
47 if match is None:
48 ret = 0
49 else:
50 ret = 1
51 self.groups = match.groups ()
52 return ret
53
55 tmp = string.replace("%s:%d: %s" % (self.filename, self.lineno-1, aMsg), '\r', '')
56 return string.replace(tmp, '\n', '')
57
58 - def log (self, level, str):
59 _log.Log (level, "%s: line %d: %s" % (self.filename, self.lineno-1, str))
60
61 - def run (self, filename):
62 """
63 filename: a file, containg semicolon-separated SQL commands
64 """
65 if re.match ("http://.*", filename) or re.match ("ftp://.*", filename) or re.match ("gopher://.*", filename):
66 try:
67 self.file = urllib2.urlopen (filename)
68 except URLError:
69 _log.error("cannot access %s" % filename)
70 return 1
71 else:
72 if os.access (filename, os.R_OK):
73 self.file = open(filename)
74 else:
75 _log.error("cannot open file [%s]" % filename)
76 return 1
77
78 self.lineno = 0
79 self.filename = filename
80 in_string = False
81 bracketlevel = 0
82 curr_cmd = ''
83 curs = self.conn.cursor ()
84
85 for self.line in self.file.readlines():
86 self.lineno += 1
87 if len(self.line.strip()) == 0:
88 continue
89
90
91 if self.match (r"^\\echo (.*)"):
92 _log.info(self.fmt_msg(shell(self.groups[0])))
93 continue
94
95 if self.match (r"^\\qecho (.*)"):
96 _log.info(self.fmt_msg(shell (self.groups[0])))
97 continue
98
99 if self.match (r"^\\q"):
100 _log.warning(self.fmt_msg("script terminated by \\q"))
101 return 0
102
103 if self.match (r"^\\set (\S+) (\S+)"):
104 self.vars[self.groups[0]] = shell (self.groups[1])
105 if self.groups[0] == 'ON_ERROR_STOP':
106 self.vars['ON_ERROR_STOP'] = int (self.vars['ON_ERROR_STOP'])
107 continue
108
109 if self.match (r"^\\unset (\S+)"):
110 self.vars[self.groups[0]] = None
111 continue
112
113 if self.match (r"^\\connect.*"):
114 _log.error(self.fmt_msg("\\connect not yet supported in scripts"))
115 continue
116
117 if self.match (r"^\\lo_import.*"):
118 _log.error(self.fmt_msg("\\lo_import not yet supported"))
119
120 return 1
121
122 if self.match (r"^\\copy .* to '(\S+)' .*"):
123 _log.error(self.fmt_msg("\\copy to not implemented"))
124 return 1
125
126 if self.match (r"^\\copy .* from '(\S+)' .*"):
127 copyfile = self.groups[0]
128 try:
129 copyfd = file (os.path.join (os.path.dirname (self.filename), copyfile))
130 except error:
131 _log.error(self.fmt_msg(error))
132 return 1
133 self.line = self.line[1:].strip()
134 self.line.replace ("'%s'" % copyfile, 'stdin')
135
136 copyline = 0
137 try:
138 curs = self.conn.cursor ()
139
140 curs.execute (self.line)
141
142 for i in copyfd.readlines ():
143 curs.execute (i)
144 copyline += 1
145 self.conn.commit ()
146 curs.close ()
147 except StandardError, error:
148 _log.error("%s: %d: %s" % (copyfile, copyline, error))
149 if self.vars['ON_ERROR_STOP']:
150 return 1
151 continue
152
153
154 if self.match (r"^\\i (\S+)"):
155
156 Psql(self.conn).run (os.path.join (os.path.dirname (self.filename), self.groups[0]))
157 continue
158
159
160 if self.match (r"^\\encoding.*"):
161 _log.error(self.fmt_msg("\\encoding not yet supported"))
162 continue
163
164
165 if self.match (r"^\\(.*)") and not in_string:
166
167
168 _log.warning(self.fmt_msg("psql command \"\\%s\" being ignored " % self.groups[0]))
169 continue
170
171
172 this_char = self.line[0]
173
174 for next_char in self.line[1:] + ' ':
175
176
177 if this_char == "'":
178 in_string = not in_string
179
180
181 if this_char == '-' and next_char == '-' and not in_string:
182 break
183
184
185 if this_char == '(' and not in_string:
186 bracketlevel += 1
187 if this_char == ')' and not in_string:
188 bracketlevel -= 1
189
190
191 if not (not in_string and (bracketlevel == 0) and (this_char == ';')):
192 curr_cmd += this_char
193 else:
194 try:
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213 if curr_cmd.strip() != '':
214 if curr_cmd.find('vacuum'):
215 self.conn.commit();
216 curs.close()
217 old_iso_level = self.conn.isolation_level
218 self.conn.set_isolation_level(0)
219 curs = self.conn.cursor()
220 curs.execute (curr_cmd)
221 self.conn.set_isolation_level(old_iso_level)
222 else:
223 curs.execute (curr_cmd)
224
225 except StandardError, error:
226 _log.debug(curr_cmd)
227 if re.match (r"^NOTICE:.*", str(error)):
228 _log.warning(self.fmt_msg(error))
229 else:
230 if self.vars['ON_ERROR_STOP']:
231 _log.error(self.fmt_msg(error))
232 return 1
233 else:
234 _log.debug(self.fmt_msg(error))
235
236 self.conn.commit()
237 curs.close()
238 curs = self.conn.cursor()
239 curr_cmd = ''
240
241 this_char = next_char
242
243
244
245
246 self.conn.commit()
247 curs.close()
248 return 0
249
250
251 if __name__ == '__main__':
252 from pyPgSQL import PgSQL
253 conn = PgSQL.connect (user='gm-dbo', database = 'gnumed')
254 psql = Psql (conn)
255 psql.run (sys.argv[1])
256 conn.close ()
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285