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