Mercurial > sqlpython
annotate sqlpython.py @ 21:8b55aaa52ce9
working on load, and preserving stdin/out
author | devlinjs@FA7CZA6N1254998.wrightpatterson.afmc.ds.af.mil |
---|---|
date | Wed, 19 Dec 2007 13:40:39 -0500 |
parents | 2776755a3a7e |
children | 1fb9f7dee7d8 |
rev | line source |
---|---|
0 | 1 # |
2 # SqlPython V1.3 | |
3 # Author: Luca.Canali@cern.ch, Apr 2006 | |
4 # Rev 18-Oct-07 | |
5 # | |
6 # A python module to reproduce Oracle's command line 'sqlplus-like' within python | |
7 # Intended to allow easy customizations and extentions | |
8 # Best used with the companion modules sqlpyPlus and mysqlpy | |
9 # See also http://twiki.cern.ch/twiki/bin/view/PSSGroup/SqlPython | |
10 | |
16
2776755a3a7e
beginning separation of cmd2
devlinjs@FA7CZA6N1254998.wrightpatterson.afmc.ds.af.mil
parents:
9
diff
changeset
|
11 import cmd2,getpass,binascii,cx_Oracle,re |
0 | 12 import pexpecter |
13 | |
14 # complication! separate sessions -> | |
15 # separate transactions !!!!! | |
16 # also: timeouts, other session failures | |
17 | |
16
2776755a3a7e
beginning separation of cmd2
devlinjs@FA7CZA6N1254998.wrightpatterson.afmc.ds.af.mil
parents:
9
diff
changeset
|
18 class sqlpython(cmd2.Cmd): |
0 | 19 '''A python module to reproduce Oracle's command line with focus on customization and extention''' |
20 | |
21 def __init__(self): | |
16
2776755a3a7e
beginning separation of cmd2
devlinjs@FA7CZA6N1254998.wrightpatterson.afmc.ds.af.mil
parents:
9
diff
changeset
|
22 cmd2.Cmd.__init__(self) |
0 | 23 self.prompt = 'SQL.No_Connection> ' |
24 self.maxfetch = 1000 | |
25 self.failoverSessions = [] | |
26 self.terminator = ';' | |
27 self.timeout = 30 | |
28 | |
29 def do_connect(self, arg): | |
30 '''Opens the DB connection''' | |
31 try: | |
32 if arg.find('/') == -1: | |
33 orapass = getpass.getpass('Password: ') | |
34 orauser = arg.split('@')[0] | |
35 oraserv = arg.split('@')[1] | |
36 self.orcl = cx_Oracle.connect(orauser,orapass,oraserv) | |
37 arg = '%s/%s@%s' % (orauser, orapass, oraserv) | |
38 else: | |
39 self.orcl = cx_Oracle.connect(arg) | |
40 self.curs = self.orcl.cursor() | |
41 self.prompt = 'SQL.'+self.orcl.tnsentry+'> ' | |
42 self.failoverSessions = [f for f in [fbs(arg) for fbs in pexpecter.available] if f.available] | |
43 | |
44 except Exception, e: | |
45 print e | |
46 | |
47 def emptyline(self): | |
48 pass | |
49 | |
50 def do_quit(self, arg): | |
51 return 1 | |
52 | |
53 def fail(self, arg, do_everywhere=False): | |
54 if self.failover: | |
55 success, result = False, '' | |
56 for fbs in self.failoverSessions: | |
57 success, result = fbs.attempt(arg) | |
58 if success: | |
59 print result | |
60 if not do_everywhere: | |
61 return True | |
62 print result | |
63 return False | |
64 | |
65 def designated_session(self, arg, sesstype): | |
66 for fbs in self.failoverSessions: | |
67 if fbs.valid and fbs.__class__ == sesstype: | |
68 success, result = fbs.attempt(arg) | |
69 print result | |
70 return | |
71 print 'Valid %s not found' % (sesstype.__name__) | |
72 | |
73 def do_terminators(self, arg): | |
74 """; standard Oracle format | |
75 \\c CSV (with headings) | |
76 \\C CSV (no headings) | |
77 \\g list | |
78 \\G aligned list | |
79 \\h HTML table | |
80 \\i INSERT statements | |
81 \\s CSV (with headings) | |
82 \\S CSV (no headings) | |
83 \\t transposed | |
84 \\x XML""" | |
85 print self.do_terminators.__doc__ | |
86 | |
87 terminatorSearchString = '|'.join('\\' + d.split()[0] for d in do_terminators.__doc__.splitlines()) | |
88 | |
89 def do_yasql(self, arg): | |
90 '''Sends a command to a YASQL session (http://sourceforge.net/projects/yasql/)''' | |
91 self.designated_session(arg, pexpecter.YASQLSession) | |
92 do_y = do_yasql | |
93 def do_sqlplus(self, arg): | |
94 '''Sends a command to a SQL*Plus session''' | |
95 self.designated_session(arg, pexpecter.SqlPlusSession) | |
96 do_sqlp = do_sqlplus | |
97 def do_senora(self, arg): | |
98 '''Sends a command to a Senora session (http://senora.sourceforge.net/)''' | |
99 self.designated_session(arg, pexpecter.SenoraSession) | |
100 do_sen = do_senora | |
9
f70cc3be6377
reporting rowcount
devlinjs@FA7CZA6N1254998.wrightpatterson.afmc.ds.af.mil
parents:
3
diff
changeset
|
101 |
0 | 102 def default(self, arg, do_everywhere = False): |
9
f70cc3be6377
reporting rowcount
devlinjs@FA7CZA6N1254998.wrightpatterson.afmc.ds.af.mil
parents:
3
diff
changeset
|
103 self.query = finishStatement(arg).strip().rstrip(';') |
0 | 104 try: |
105 self.curs.execute(self.query) | |
9
f70cc3be6377
reporting rowcount
devlinjs@FA7CZA6N1254998.wrightpatterson.afmc.ds.af.mil
parents:
3
diff
changeset
|
106 print '\nExecuted%s\n' % ((self.curs.rowcount > 0) and ' (%d rows)' % self.curs.rowcount or '') |
0 | 107 if do_everywhere: |
108 self.fail(arg, do_everywhere = True ) | |
109 except Exception, e: | |
110 result = self.fail(arg) | |
111 if not result: | |
112 print str(e) | |
113 | |
114 def do_commit(self, arg): | |
115 self.default('commit %s' % (arg), do_everywhere=True) | |
116 def do_rollback(self, arg): | |
117 self.default('rollback %s' % (arg), do_everywhere=True) | |
118 | |
119 # shortcuts | |
120 do_q = do_quit | |
121 do_exit = do_quit | |
122 | |
2
59903dcaf327
working on history
devlinjs@FA7CZA6N1254998.wrightpatterson.afmc.ds.af.mil
parents:
1
diff
changeset
|
123 stmtEndSearchString = r'(.*)(%s)\s*(\d+)?\s*$' % sqlpython.terminatorSearchString |
1
8fa146b9a2d7
reworking multiline
devlinjs@FA7CZA6N1254998.wrightpatterson.afmc.ds.af.mil
parents:
0
diff
changeset
|
124 stmtEndFinder = re.compile(stmtEndSearchString, re.MULTILINE | re.DOTALL) |
8fa146b9a2d7
reworking multiline
devlinjs@FA7CZA6N1254998.wrightpatterson.afmc.ds.af.mil
parents:
0
diff
changeset
|
125 prompt2 = ' > ' |
8fa146b9a2d7
reworking multiline
devlinjs@FA7CZA6N1254998.wrightpatterson.afmc.ds.af.mil
parents:
0
diff
changeset
|
126 |
8fa146b9a2d7
reworking multiline
devlinjs@FA7CZA6N1254998.wrightpatterson.afmc.ds.af.mil
parents:
0
diff
changeset
|
127 def finishStatement(firstline): |
8fa146b9a2d7
reworking multiline
devlinjs@FA7CZA6N1254998.wrightpatterson.afmc.ds.af.mil
parents:
0
diff
changeset
|
128 lines = [firstline] |
8fa146b9a2d7
reworking multiline
devlinjs@FA7CZA6N1254998.wrightpatterson.afmc.ds.af.mil
parents:
0
diff
changeset
|
129 while 1: |
8fa146b9a2d7
reworking multiline
devlinjs@FA7CZA6N1254998.wrightpatterson.afmc.ds.af.mil
parents:
0
diff
changeset
|
130 m = stmtEndFinder.search(lines[-1]) |
8fa146b9a2d7
reworking multiline
devlinjs@FA7CZA6N1254998.wrightpatterson.afmc.ds.af.mil
parents:
0
diff
changeset
|
131 if m: |
8fa146b9a2d7
reworking multiline
devlinjs@FA7CZA6N1254998.wrightpatterson.afmc.ds.af.mil
parents:
0
diff
changeset
|
132 return '\n'.join(lines) |
8fa146b9a2d7
reworking multiline
devlinjs@FA7CZA6N1254998.wrightpatterson.afmc.ds.af.mil
parents:
0
diff
changeset
|
133 lines.append(raw_input(prompt2)) |
8fa146b9a2d7
reworking multiline
devlinjs@FA7CZA6N1254998.wrightpatterson.afmc.ds.af.mil
parents:
0
diff
changeset
|
134 |
8fa146b9a2d7
reworking multiline
devlinjs@FA7CZA6N1254998.wrightpatterson.afmc.ds.af.mil
parents:
0
diff
changeset
|
135 def findTerminator(statement): |
8fa146b9a2d7
reworking multiline
devlinjs@FA7CZA6N1254998.wrightpatterson.afmc.ds.af.mil
parents:
0
diff
changeset
|
136 m = stmtEndFinder.search(statement) |
3
cd23cd62de3c
history working pretty well
devlinjs@FA7CZA6N1254998.wrightpatterson.afmc.ds.af.mil
parents:
2
diff
changeset
|
137 if m: |
cd23cd62de3c
history working pretty well
devlinjs@FA7CZA6N1254998.wrightpatterson.afmc.ds.af.mil
parents:
2
diff
changeset
|
138 return m.groups() |
cd23cd62de3c
history working pretty well
devlinjs@FA7CZA6N1254998.wrightpatterson.afmc.ds.af.mil
parents:
2
diff
changeset
|
139 else: |
cd23cd62de3c
history working pretty well
devlinjs@FA7CZA6N1254998.wrightpatterson.afmc.ds.af.mil
parents:
2
diff
changeset
|
140 return statement, None, None |
0 | 141 |
142 def pmatrix(rows,desc,maxlen=30): | |
143 '''prints a matrix, used by sqlpython to print queries' result sets''' | |
144 names = [] | |
145 maxen = [] | |
146 toprint = [] | |
147 for d in desc: | |
148 n = d[0] | |
149 names.append(n) # list col names | |
150 maxen.append(len(n)) # col length | |
151 rcols = range(len(desc)) | |
152 rrows = range(len(rows)) | |
153 for i in rrows: # loops for all rows | |
154 rowsi = map(str, rows[i]) # current row to process | |
155 split = [] # service var is row split is needed | |
156 mustsplit = 0 # flag | |
157 for j in rcols: | |
158 if str(desc[j][1]) == "<type 'cx_Oracle.BINARY'>": # handles RAW columns | |
159 rowsi[j] = binascii.b2a_hex(rowsi[j]) | |
160 maxen[j] = max(maxen[j], len(rowsi[j])) # computes max field length | |
161 if maxen[j] <= maxlen: | |
162 split.append('') | |
163 else: # split the line is 2 because field is too long | |
164 mustsplit = 1 | |
165 maxen[j] = maxlen | |
166 split.append(rowsi[j][maxlen-1:2*maxlen-1]) | |
167 rowsi[j] = rowsi[j][0:maxlen-1] # this implem. truncates after maxlen*2 | |
168 toprint.append(rowsi) # 'toprint' is a printable copy of rows | |
169 if mustsplit != 0: | |
170 toprint.append(split) | |
171 sepcols = [] | |
172 for i in rcols: | |
173 maxcol = maxen[i] | |
174 name = names[i] | |
175 sepcols.append("-" * maxcol) # formats column names (header) | |
176 names[i] = name + (" " * (maxcol-len(name))) # formats separ line with -- | |
177 rrows2 = range(len(toprint)) | |
178 for j in rrows2: | |
179 val = toprint[j][i] | |
180 if str(desc[i][1]) == "<type 'cx_Oracle.NUMBER'>": # right align numbers | |
181 toprint[j][i] = (" " * (maxcol-len(val))) + val | |
182 else: | |
183 toprint[j][i] = val + (" " * (maxcol-len(val))) | |
184 for j in rrows2: | |
185 toprint[j] = ' '.join(toprint[j]) | |
186 names = ' '.join(names) | |
187 sepcols = ' '.join(sepcols) | |
188 toprint.insert(0, sepcols) | |
189 toprint.insert(0, names) | |
190 return '\n'.join(toprint) | |
191 |