| Home | Trees | Indices | Help |
|
|---|
|
|
1 # A Python class to replace the PSQL command-line interpreter
2 # NOTE: this is not a full replacement for the interpeter, merely
3 # enough functionality to run gnumed installation scripts
4 #
5 # Copyright (C) 2003, 2004 - 2010 GNUmed developers
6 # Licence: GPL
7 #===================================================================
8 __version__ = "$Revision: 1.10 $"
9 __author__ = "Ian Haywood"
10 __license__ = "GPL (details at http://www.gnu.org)"
11
12 # stdlib
13 import sys, os, string, re, urllib2, logging
14
15
16 _log = logging.getLogger('gm.bootstrapper')
17 _log.info(__version__)
18
19 unformattable_error_id = 12345
20 #===================================================================
22 """
23 runs the shell command and returns a string
24 """
25 stdin, stdout = os.popen4 (cmd.group (1))
26 r = stdout.read ()
27 stdout.close()
28 stdin.close()
29 return r
30 #-------------------------------------------------------------------
32 """
33 performs backtick shell extension in a string
34 """
35 return re.sub (r"`(.*)`", shellrun, str)
36 #===================================================================
38
40 """
41 db : the interpreter to connect to, must be a DBAPI compliant interface
42 """
43 self.conn = conn
44 self.vars = {'ON_ERROR_STOP':None}
45 #---------------------------------------------------------------
47 match = re.match (str, self.line)
48 if match is None:
49 ret = 0
50 else:
51 ret = 1
52 self.groups = match.groups ()
53 return ret
54 #---------------------------------------------------------------
56 try:
57 tmp = u"%s:%d: %s" % (self.filename, self.lineno-1, aMsg)
58 tmp = tmp.replace(u'\r', u'')
59 tmp = tmp.replace(u'\n', u'')
60 except UnicodeDecodeError:
61 global unformattable_error_id
62 tmp = u"%s:%d: <cannot unicode(msg), printing on console with ID [#%d]>" % (self.filename, self.lineno-1, unformattable_error_id)
63 try:
64 print 'ERROR: GNUmed bootstrap #%d:' % unformattable_error_id
65 print aMsg
66 except: pass
67 unformattable_error_id += 1
68 return tmp
69 #---------------------------------------------------------------
71 """
72 filename: a file, containg semicolon-separated SQL commands
73 """
74 if re.match ("http://.*", filename) or re.match ("ftp://.*", filename) or re.match ("gopher://.*", filename):
75 try:
76 self.file = urllib2.urlopen (filename)
77 except URLError:
78 _log.error(u"cannot access %s" % filename)
79 return 1
80 else:
81 if os.access (filename, os.R_OK):
82 self.file = open(filename)
83 else:
84 _log.error(u"cannot open file [%s]" % filename)
85 return 1
86
87 self.lineno = 0
88 self.filename = filename
89 in_string = False
90 bracketlevel = 0
91 curr_cmd = ''
92 curs = self.conn.cursor ()
93 # transaction_started = False
94 for self.line in self.file.readlines():
95 self.lineno += 1
96 if len(self.line.strip()) == 0:
97 continue
98
99 # \echo
100 if self.match (r"^\\echo (.*)"):
101 _log.info(self.fmt_msg(shell(self.groups[0])))
102 continue
103 # \qecho
104 if self.match (r"^\\qecho (.*)"):
105 _log.info(self.fmt_msg(shell (self.groups[0])))
106 continue
107 # \q
108 if self.match (r"^\\q"):
109 _log.warning(self.fmt_msg(u"script terminated by \\q"))
110 return 0
111 # \set
112 if self.match (r"^\\set (\S+) (\S+)"):
113 self.vars[self.groups[0]] = shell (self.groups[1])
114 if self.groups[0] == 'ON_ERROR_STOP':
115 self.vars['ON_ERROR_STOP'] = int (self.vars['ON_ERROR_STOP'])
116 continue
117 # \unset
118 if self.match (r"^\\unset (\S+)"):
119 self.vars[self.groups[0]] = None
120 continue
121 # \connect
122 if self.match (r"^\\connect.*"):
123 _log.error(self.fmt_msg(u"\\connect not yet supported in scripts"))
124 continue
125 # \lo_import
126 if self.match (r"^\\lo_import.*"):
127 _log.error(self.fmt_msg(u"\\lo_import not yet supported"))
128 # no sense to continue here
129 return 1
130 # \copy ... to ...
131 if self.match (r"^\\copy .* to '(\S+)' .*"):
132 _log.error(self.fmt_msg(u"\\copy to not implemented"))
133 return 1
134 # \copy ... from ...
135 if self.match (r"^\\copy .* from '(\S+)' .*"):
136 copyfile = self.groups[0]
137 try:
138 copyfd = file (os.path.join (os.path.dirname (self.filename), copyfile))
139 except error:
140 _log.error(self.fmt_msg(error))
141 return 1
142 self.line = self.line[1:].strip() # lop off leading slash
143 self.line.replace ("'%s'" % copyfile, 'stdin')
144 # now we have a command that the backend understands
145 copyline = 0
146 try:
147 curs = self.conn.cursor ()
148 # send the COPY command
149 curs.execute (self.line)
150 # send the data
151 for i in copyfd.readlines ():
152 curs.execute (i)
153 copyline += 1
154 self.conn.commit ()
155 curs.close ()
156 except StandardError, error:
157 _log.error(u"%s: %d: %s" % (copyfile, copyline, error))
158 if self.vars['ON_ERROR_STOP']:
159 return 1
160 continue
161
162 # \i
163 if self.match (r"^\\i (\S+)"):
164 # create another interpreter instance in same connection
165 Psql(self.conn).run (os.path.join (os.path.dirname (self.filename), self.groups[0]))
166 continue
167
168 # \encoding
169 if self.match (r"^\\encoding.*"):
170 _log.error(self.fmt_msg(u"\\encoding not yet supported"))
171 continue
172
173 # other '\' commands
174 if self.match (r"^\\(.*)") and not in_string:
175 # most other \ commands are for controlling output formats, don't make
176 # much sense in an installation script, so we gently ignore them
177 _log.warning(self.fmt_msg(u"psql command \"\\%s\" being ignored " % self.groups[0]))
178 continue
179
180 # non-'\' commands
181 this_char = self.line[0]
182 # loop over characters in line
183 for next_char in self.line[1:] + ' ':
184
185 # start/end of string detected
186 if this_char == "'":
187 in_string = not in_string
188
189 # detect -- style comments
190 if this_char == '-' and next_char == '-' and not in_string:
191 break
192
193 # detect bracketing
194 if this_char == '(' and not in_string:
195 bracketlevel += 1
196 if this_char == ')' and not in_string:
197 bracketlevel -= 1
198
199 # found end of command, not inside string, not inside bracket ?
200 if not (not in_string and (bracketlevel == 0) and (this_char == ';')):
201 curr_cmd += this_char
202 else:
203 try:
204 # if curr_cmd.strip ().upper () == 'COMMIT':
205 # if transaction_started:
206 # self.conn.commit ()
207 # curs.close ()
208 # curs = self.conn.cursor ()
209 # _log.debug(self.fmt_msg ("transaction committed"))
210 # else:
211 # _log.warning(self.fmt_msg ("COMMIT without BEGIN: no actual transaction happened!"))
212 # transaction_started = False
213
214 # elif curr_cmd.strip ().upper () == 'BEGIN':
215 # if transaction_started:
216 # _log.warning(self.fmt_msg ("BEGIN inside transaction"))
217 # else:
218 # transaction_started = True
219 # _log.debug(self.fmt_msg ("starting transaction"))
220
221 # else:
222 if curr_cmd.strip() != '':
223 if curr_cmd.find('vacuum'):
224 self.conn.commit();
225 curs.close()
226 old_iso_level = self.conn.isolation_level
227 self.conn.set_isolation_level(0)
228 curs = self.conn.cursor()
229 curs.execute (curr_cmd)
230 self.conn.set_isolation_level(old_iso_level)
231 else:
232 curs.execute (curr_cmd)
233 # if not transaction_started:
234 except StandardError, error:
235 _log.debug(curr_cmd)
236 if re.match (r"^NOTICE:.*", str(error)):
237 _log.warning(self.fmt_msg(error))
238 else:
239 if self.vars['ON_ERROR_STOP']:
240 _log.error(self.fmt_msg(error))
241 return 1
242 else:
243 _log.debug(self.fmt_msg(error))
244
245 self.conn.commit()
246 curs.close()
247 curs = self.conn.cursor()
248 curr_cmd = ''
249
250 this_char = next_char
251
252 # end of loop over chars
253
254 # end of loop over lines
255 self.conn.commit()
256 curs.close()
257 return 0
258 #===================================================================
259 # testing code
260 if __name__ == '__main__':
261 from pyPgSQL import PgSQL
262 conn = PgSQL.connect (user='gm-dbo', database = 'gnumed')
263 psql = Psql (conn)
264 psql.run (sys.argv[1])
265 conn.close ()
266 #===================================================================
267
| Home | Trees | Indices | Help |
|
|---|
| Generated by Epydoc 3.0.1 on Mon Nov 29 04:05:29 2010 | http://epydoc.sourceforge.net |