Mercurial > sqlpython
comparison sqlpyPlus.py @ 80:83de0cb04f12
prevent crash on lone backslash
author | catherine@localhost |
---|---|
date | Wed, 14 May 2008 17:23:52 -0400 |
parents | 01d578f4e6e7 |
children | 32c868fca272 |
comparison
equal
deleted
inserted
replaced
79:01d578f4e6e7 | 80:83de0cb04f12 |
---|---|
10 - SQL*Plus-style describe, spool | 10 - SQL*Plus-style describe, spool |
11 - write sends query result directly to file | 11 - write sends query result directly to file |
12 - comments shows table and column comments | 12 - comments shows table and column comments |
13 - compare ... to ... graphically compares results of two queries | 13 - compare ... to ... graphically compares results of two queries |
14 - commands are case-insensitive | 14 - commands are case-insensitive |
15 | 15 |
16 Use 'help' within sqlpython for details. | 16 Use 'help' within sqlpython for details. |
17 | 17 |
18 Compatible with sqlpython v1.3 | 18 Compatible with sqlpython v1.3 |
19 | 19 |
20 Set bind variables the hard (SQL*Plus) way | 20 Set bind variables the hard (SQL*Plus) way |
26 """ | 26 """ |
27 # note in cmd.cmd about supporting emacs commands? | 27 # note in cmd.cmd about supporting emacs commands? |
28 | 28 |
29 descQueries = { | 29 descQueries = { |
30 'TABLE': (""" | 30 'TABLE': (""" |
31 atc.column_name, | 31 atc.column_name, |
32 CASE atc.nullable WHEN 'Y' THEN 'NULL' ELSE 'NOT NULL' END "Null?", | 32 CASE atc.nullable WHEN 'Y' THEN 'NULL' ELSE 'NOT NULL' END "Null?", |
33 atc.data_type || | 33 atc.data_type || |
34 CASE atc.data_type WHEN 'DATE' THEN '' | 34 CASE atc.data_type WHEN 'DATE' THEN '' |
35 ELSE '(' || | 35 ELSE '(' || |
36 CASE atc.data_type WHEN 'NUMBER' THEN TO_CHAR(atc.data_precision) || | 36 CASE atc.data_type WHEN 'NUMBER' THEN TO_CHAR(atc.data_precision) || |
37 CASE atc.data_scale WHEN 0 THEN '' | 37 CASE atc.data_scale WHEN 0 THEN '' |
38 ELSE ',' || TO_CHAR(atc.data_scale) END | 38 ELSE ',' || TO_CHAR(atc.data_scale) END |
39 ELSE TO_CHAR(atc.data_length) END | 39 ELSE TO_CHAR(atc.data_length) END |
40 END || | 40 END || |
41 CASE atc.data_type WHEN 'DATE' THEN '' ELSE ')' END | 41 CASE atc.data_type WHEN 'DATE' THEN '' ELSE ')' END |
42 data_type | 42 data_type |
43 FROM all_tab_columns atc | 43 FROM all_tab_columns atc |
44 WHERE atc.table_name = :object_name | 44 WHERE atc.table_name = :object_name |
45 AND atc.owner = :owner | 45 AND atc.owner = :owner |
46 ORDER BY atc.column_id;""",), | 46 ORDER BY atc.column_id;""",), |
47 'PROCEDURE': (""" | 47 'PROCEDURE': (""" |
48 argument_name, | 48 argument_name, |
49 data_type, | 49 data_type, |
50 in_out, | 50 in_out, |
51 default_value | 51 default_value |
52 FROM all_arguments | 52 FROM all_arguments |
53 WHERE object_name = :object_name | 53 WHERE object_name = :object_name |
54 AND owner = :owner | 54 AND owner = :owner |
55 AND package_name IS NULL | 55 AND package_name IS NULL |
56 AND argument_name IS NOT NULL | 56 AND argument_name IS NOT NULL |
59 SELECT DISTINCT object_name | 59 SELECT DISTINCT object_name |
60 FROM all_arguments | 60 FROM all_arguments |
61 WHERE package_name = :package_name | 61 WHERE package_name = :package_name |
62 AND owner = :owner""",), | 62 AND owner = :owner""",), |
63 'PackageObjArgs':(""" | 63 'PackageObjArgs':(""" |
64 object_name, | 64 object_name, |
65 argument_name, | 65 argument_name, |
66 data_type, | 66 data_type, |
67 in_out, | 67 in_out, |
68 default_value | 68 default_value |
69 FROM all_arguments | 69 FROM all_arguments |
70 WHERE package_name = :package_name | 70 WHERE package_name = :package_name |
71 AND object_name = :object_name | 71 AND object_name = :object_name |
72 AND owner = :owner | 72 AND owner = :owner |
73 AND argument_name IS NOT NULL | 73 AND argument_name IS NOT NULL |
74 ORDER BY sequence""",), | 74 ORDER BY sequence""",), |
75 'TRIGGER':(""" | 75 'TRIGGER':(""" |
76 description | 76 description |
77 FROM all_triggers | 77 FROM all_triggers |
78 WHERE owner = :owner | 78 WHERE owner = :owner |
79 AND trigger_name = :object_name | 79 AND trigger_name = :object_name |
80 """, | 80 """, |
81 """ | 81 """ |
82 table_owner, | 82 table_owner, |
83 base_object_type, | 83 base_object_type, |
84 table_name, | 84 table_name, |
85 column_name, | 85 column_name, |
86 when_clause, | 86 when_clause, |
87 status, | 87 status, |
88 action_type, | 88 action_type, |
89 crossedition | 89 crossedition |
90 FROM all_triggers | 90 FROM all_triggers |
91 WHERE owner = :owner | 91 WHERE owner = :owner |
92 AND trigger_name = :object_name | 92 AND trigger_name = :object_name |
93 \\t | 93 \\t |
94 """, | 94 """, |
109 FROM all_indexes | 109 FROM all_indexes |
110 WHERE owner = :owner | 110 WHERE owner = :owner |
111 AND index_name = :object_name | 111 AND index_name = :object_name |
112 \\t | 112 \\t |
113 """,) | 113 """,) |
114 } | 114 } |
115 descQueries['VIEW'] = descQueries['TABLE'] | 115 descQueries['VIEW'] = descQueries['TABLE'] |
116 descQueries['FUNCTION'] = descQueries['PROCEDURE'] | 116 descQueries['FUNCTION'] = descQueries['PROCEDURE'] |
117 | 117 |
118 queries = { | 118 queries = { |
119 'resolve': """ | 119 'resolve': """ |
120 SELECT object_type, object_name, owner FROM ( | 120 SELECT object_type, object_name, owner FROM ( |
121 SELECT object_type, object_name, user owner, 1 priority | 121 SELECT object_type, object_name, user owner, 1 priority |
122 FROM user_objects | 122 FROM user_objects |
123 WHERE object_name = :objName | 123 WHERE object_name = :objName |
124 UNION ALL | 124 UNION ALL |
125 SELECT ao.object_type, ao.object_name, ao.owner, 2 priority | 125 SELECT ao.object_type, ao.object_name, ao.owner, 2 priority |
126 FROM all_objects ao | 126 FROM all_objects ao |
127 JOIN user_synonyms us ON (us.table_owner = ao.owner AND us.table_name = ao.object_name) | 127 JOIN user_synonyms us ON (us.table_owner = ao.owner AND us.table_name = ao.object_name) |
128 WHERE us.synonym_name = :objName | 128 WHERE us.synonym_name = :objName |
129 AND ao.object_type != 'SYNONYM' | 129 AND ao.object_type != 'SYNONYM' |
130 UNION ALL | 130 UNION ALL |
131 SELECT ao.object_type, ao.object_name, ao.owner, 3 priority | 131 SELECT ao.object_type, ao.object_name, ao.owner, 3 priority |
132 FROM all_objects ao | 132 FROM all_objects ao |
133 JOIN all_synonyms asyn ON (asyn.table_owner = ao.owner AND asyn.table_name = ao.object_name) | 133 JOIN all_synonyms asyn ON (asyn.table_owner = ao.owner AND asyn.table_name = ao.object_name) |
134 WHERE asyn.synonym_name = :objName | 134 WHERE asyn.synonym_name = :objName |
135 AND ao.object_type != 'SYNONYM' | 135 AND ao.object_type != 'SYNONYM' |
136 AND asyn.owner = 'PUBLIC' | 136 AND asyn.owner = 'PUBLIC' |
137 UNION ALL | 137 UNION ALL |
138 SELECT 'DIRECTORY' object_type, dir.directory_name, dir.owner, 6 priority | 138 SELECT 'DIRECTORY' object_type, dir.directory_name, dir.owner, 6 priority |
139 FROM all_directories dir | 139 FROM all_directories dir |
140 WHERE dir.directory_name = :objName | 140 WHERE dir.directory_name = :objName |
141 UNION ALL | 141 UNION ALL |
142 SELECT 'DATABASE LINK' object_type, db_link, owner, 7 priority | 142 SELECT 'DATABASE LINK' object_type, db_link, owner, 7 priority |
143 FROM all_db_links dbl | 143 FROM all_db_links dbl |
144 WHERE dbl.db_link = :objName | 144 WHERE dbl.db_link = :objName |
145 ) ORDER BY priority ASC""", | 145 ) ORDER BY priority ASC""", |
146 'tabComments': """ | 146 'tabComments': """ |
147 SELECT comments | 147 SELECT comments |
148 FROM all_tab_comments | 148 FROM all_tab_comments |
149 WHERE owner = :owner | 149 WHERE owner = :owner |
150 AND table_name = :table_name""", | 150 AND table_name = :table_name""", |
151 'colComments': """ | 151 'colComments': """ |
152 atc.column_name, | 152 atc.column_name, |
153 acc.comments | 153 acc.comments |
154 FROM all_tab_columns atc | 154 FROM all_tab_columns atc |
155 JOIN all_col_comments acc ON (atc.owner = acc.owner and atc.table_name = acc.table_name and atc.column_name = acc.column_name) | 155 JOIN all_col_comments acc ON (atc.owner = acc.owner and atc.table_name = acc.table_name and atc.column_name = acc.column_name) |
156 WHERE atc.table_name = :object_name | 156 WHERE atc.table_name = :object_name |
157 AND atc.owner = :owner | 157 AND atc.owner = :owner |
158 ORDER BY atc.column_id;""", | 158 ORDER BY atc.column_id;""", |
159 #thanks to Senora.pm for "refs" | 159 #thanks to Senora.pm for "refs" |
160 'refs': """ | 160 'refs': """ |
161 NULL referenced_by, | 161 NULL referenced_by, |
162 c2.table_name references, | 162 c2.table_name references, |
163 c1.constraint_name constraint | 163 c1.constraint_name constraint |
164 FROM | 164 FROM |
165 user_constraints c1, | 165 user_constraints c1, |
166 user_constraints c2 | 166 user_constraints c2 |
167 WHERE | 167 WHERE |
168 c1.table_name = :object_name | 168 c1.table_name = :object_name |
169 and c1.constraint_type ='R' | 169 and c1.constraint_type ='R' |
170 and c1.r_constraint_name = c2.constraint_name | 170 and c1.r_constraint_name = c2.constraint_name |
171 and c1.r_owner = c2.owner | 171 and c1.r_owner = c2.owner |
172 and c1.owner = :owner | 172 and c1.owner = :owner |
173 UNION | 173 UNION |
174 SELECT c1.table_name referenced_by, | 174 SELECT c1.table_name referenced_by, |
175 NULL references, | 175 NULL references, |
176 c1.constraint_name constraint | 176 c1.constraint_name constraint |
177 FROM | 177 FROM |
178 user_constraints c1, | 178 user_constraints c1, |
179 user_constraints c2 | 179 user_constraints c2 |
180 WHERE | 180 WHERE |
181 c2.table_name = :object_name | 181 c2.table_name = :object_name |
182 and c1.constraint_type ='R' | 182 and c1.constraint_type ='R' |
183 and c1.r_constraint_name = c2.constraint_name | 183 and c1.r_constraint_name = c2.constraint_name |
184 and c1.r_owner = c2.owner | 184 and c1.r_owner = c2.owner |
185 and c1.owner = :owner | 185 and c1.owner = :owner |
186 """ | 186 """ |
187 } | 187 } |
188 | 188 |
189 import sys, os, re, sqlpython, cx_Oracle, pyparsing | 189 import sys, os, re, sqlpython, cx_Oracle, pyparsing |
190 from cmd2 import flagReader, Cmd | 190 from cmd2 import flagReader, Cmd |
191 | 191 |
192 if float(sys.version[:3]) < 2.3: | 192 if float(sys.version[:3]) < 2.3: |
193 def enumerate(lst): | 193 def enumerate(lst): |
194 return zip(range(len(lst)), lst) | 194 return zip(range(len(lst)), lst) |
195 | 195 |
196 class SoftwareSearcher(object): | 196 class SoftwareSearcher(object): |
197 def __init__(self, softwareList, purpose): | 197 def __init__(self, softwareList, purpose): |
198 self.softwareList = softwareList | 198 self.softwareList = softwareList |
199 self.purpose = purpose | 199 self.purpose = purpose |
200 self.software = None | 200 self.software = None |
210 return (software, '%s %s') | 210 return (software, '%s %s') |
211 for (n, (software, invokeString)) in enumerate(self.softwareList): | 211 for (n, (software, invokeString)) in enumerate(self.softwareList): |
212 if os.path.exists(software): | 212 if os.path.exists(software): |
213 if n > (len(self.softwareList) * 0.7): | 213 if n > (len(self.softwareList) * 0.7): |
214 print """ | 214 print """ |
215 | 215 |
216 Using %s. Note that there are better options available for %s, | 216 Using %s. Note that there are better options available for %s, |
217 but %s couldn't find a better one in your PATH. | 217 but %s couldn't find a better one in your PATH. |
218 Feel free to open up %s | 218 Feel free to open up %s |
219 and customize it to find your favorite %s program. | 219 and customize it to find your favorite %s program. |
220 | 220 |
221 """ % (software, self.purpose, __file__, __file__, self.purpose) | 221 """ % (software, self.purpose, __file__, __file__, self.purpose) |
222 return (software, invokeString) | 222 return (software, invokeString) |
223 stem = os.path.split(software)[1] | 223 stem = os.path.split(software)[1] |
224 for p in os.environ['PATH'].split(os.pathsep): | 224 for p in os.environ['PATH'].split(os.pathsep): |
225 if os.path.exists(os.sep.join([p, stem])): | 225 if os.path.exists(os.sep.join([p, stem])): |
226 return (stem, invokeString) | 226 return (stem, invokeString) |
227 raise (OSError, """Could not find any %s programs. You will need to install one, | 227 raise (OSError, """Could not find any %s programs. You will need to install one, |
228 or customize %s to make it aware of yours. | 228 or customize %s to make it aware of yours. |
229 Looked for these programs: | 229 Looked for these programs: |
230 %s""" % (self.purpose, __file__, "\n".join([s[0] for s in self.softwareList]))) | 230 %s""" % (self.purpose, __file__, "\n".join([s[0] for s in self.softwareList]))) |
231 #v2.4: %s""" % (self.purpose, __file__, "\n".join(s[0] for s in self.softwareList))) | 231 #v2.4: %s""" % (self.purpose, __file__, "\n".join(s[0] for s in self.softwareList))) |
232 | 232 |
233 softwareLists = { | 233 softwareLists = { |
234 'diff/merge': [ | 234 'diff/merge': [ |
235 ('/usr/bin/meld',"%s %s %s"), | 235 ('/usr/bin/meld',"%s %s %s"), |
236 ('/usr/bin/kdiff3',"%s %s %s"), | 236 ('/usr/bin/kdiff3',"%s %s %s"), |
237 (r'C:\Program Files\Araxis\Araxis Merge v6.5\Merge.exe','"%s" %s %s'), | 237 (r'C:\Program Files\Araxis\Araxis Merge v6.5\Merge.exe','"%s" %s %s'), |
238 (r'C:\Program Files\TortoiseSVN\bin\TortoiseMerge.exe', '"%s" /base:"%s" /mine:"%s"'), | 238 (r'C:\Program Files\TortoiseSVN\bin\TortoiseMerge.exe', '"%s" /base:"%s" /mine:"%s"'), |
239 ('FileMerge','%s %s %s'), | 239 ('FileMerge','%s %s %s'), |
240 ('kompare','%s %s %s'), | 240 ('kompare','%s %s %s'), |
241 ('WinMerge','%s %s %s'), | 241 ('WinMerge','%s %s %s'), |
242 ('xxdiff','%s %s %s'), | 242 ('xxdiff','%s %s %s'), |
243 ('fldiff','%s %s %s'), | 243 ('fldiff','%s %s %s'), |
244 ('gtkdiff','%s %s %s'), | 244 ('gtkdiff','%s %s %s'), |
245 ('tkdiff','%s %s %s'), | 245 ('tkdiff','%s %s %s'), |
246 ('gvimdiff','%s %s %s'), | 246 ('gvimdiff','%s %s %s'), |
247 ('diff',"%s %s %s"), | 247 ('diff',"%s %s %s"), |
248 (r'c:\windows\system32\comp.exe',"%s %s %s")], | 248 (r'c:\windows\system32\comp.exe',"%s %s %s")], |
249 'text editor': [ | 249 'text editor': [ |
250 ('gedit', '%s %s'), | 250 ('gedit', '%s %s'), |
251 ('textpad', '%s %s'), | 251 ('textpad', '%s %s'), |
252 ('notepad.exe', '%s %s'), | 252 ('notepad.exe', '%s %s'), |
253 ('pico', '%s %s'), | 253 ('pico', '%s %s'), |
254 ('emacs', '%s %s'), | 254 ('emacs', '%s %s'), |
255 ('vim', '%s %s'), | 255 ('vim', '%s %s'), |
256 ('vi', '%s %s'), | 256 ('vi', '%s %s'), |
257 ('ed', '%s %s'), | 257 ('ed', '%s %s'), |
258 ('edlin', '%s %s') | 258 ('edlin', '%s %s') |
259 ] | 259 ] |
260 } | 260 } |
261 | 261 |
262 diffMergeSearcher = SoftwareSearcher(softwareLists['diff/merge'],'diff/merge') | 262 diffMergeSearcher = SoftwareSearcher(softwareLists['diff/merge'],'diff/merge') |
263 editSearcher = SoftwareSearcher(softwareLists['text editor'], 'text editor') | 263 editSearcher = SoftwareSearcher(softwareLists['text editor'], 'text editor') |
264 editor = os.environ.get('EDITOR') | 264 editor = os.environ.get('EDITOR') |
336 result[varname] = existingBinds[varname] | 336 result[varname] = existingBinds[varname] |
337 except KeyError: | 337 except KeyError: |
338 if not givenBindVars.has_key(varname): | 338 if not givenBindVars.has_key(varname): |
339 print 'Bind variable %s not defined.' % (varname) | 339 print 'Bind variable %s not defined.' % (varname) |
340 return result | 340 return result |
341 | 341 |
342 class sqlpyPlus(sqlpython.sqlpython): | 342 class sqlpyPlus(sqlpython.sqlpython): |
343 defaultExtension = 'sql' | 343 defaultExtension = 'sql' |
344 shortcuts = {'?': 'help', '@': 'getrun', '!': 'shell', ':': 'setbind', '\\': 'psql'} | 344 shortcuts = {'?': 'help', '@': 'getrun', '!': 'shell', ':': 'setbind', '\\': 'psql'} |
345 multilineCommands = '''select insert update delete tselect | 345 multilineCommands = '''select insert update delete tselect |
346 create drop alter'''.split() | 346 create drop alter'''.split() |
347 defaultFileName = 'afiedt.buf' | 347 defaultFileName = 'afiedt.buf' |
348 def __init__(self): | 348 def __init__(self): |
349 sqlpython.sqlpython.__init__(self) | 349 sqlpython.sqlpython.__init__(self) |
350 self.binds = CaselessDict() | 350 self.binds = CaselessDict() |
351 self.sqlBuffer = [] | 351 self.sqlBuffer = [] |
362 def parseline(self, line): | 362 def parseline(self, line): |
363 """Parse the line into a command name and a string containing | 363 """Parse the line into a command name and a string containing |
364 the arguments. Returns a tuple containing (command, args, line). | 364 the arguments. Returns a tuple containing (command, args, line). |
365 'command' and 'args' may be None if the line couldn't be parsed. | 365 'command' and 'args' may be None if the line couldn't be parsed. |
366 Overrides cmd.cmd.parseline to accept variety of shortcuts..""" | 366 Overrides cmd.cmd.parseline to accept variety of shortcuts..""" |
367 | 367 |
368 cmd, arg, line = sqlpython.sqlpython.parseline(self, line) | 368 cmd, arg, line = sqlpython.sqlpython.parseline(self, line) |
369 if cmd in ('select', 'sleect', 'insert', 'update', 'delete', 'describe', | 369 if cmd in ('select', 'sleect', 'insert', 'update', 'delete', 'describe', |
370 'desc', 'comments', 'pull', 'refs', 'desc', 'triggers', 'find') \ | 370 'desc', 'comments', 'pull', 'refs', 'desc', 'triggers', 'find') \ |
371 and not hasattr(self, 'curs'): | 371 and not hasattr(self, 'curs'): |
372 print 'Not connected.' | 372 print 'Not connected.' |
373 return '', '', '' | 373 return '', '', '' |
374 return cmd, arg, line | 374 return cmd, arg, line |
375 | 375 |
376 def onecmd_plus_hooks(self, line): | 376 def onecmd_plus_hooks(self, line): |
377 line = self.precmd(line) | 377 line = self.precmd(line) |
378 stop = self.onecmd(line) | 378 stop = self.onecmd(line) |
379 stop = self.postcmd(stop, line) | 379 stop = self.postcmd(stop, line) |
380 | 380 |
400 for (itm, useq) in zip(row, usequotes)) | 400 for (itm, useq) in zip(row, usequotes)) |
401 result = ['INSERT INTO %s (%s) VALUES (%s);' % | 401 result = ['INSERT INTO %s (%s) VALUES (%s);' % |
402 (self.tblname, ','.join(self.colnames), formatRow(row)) | 402 (self.tblname, ','.join(self.colnames), formatRow(row)) |
403 for row in self.rows] | 403 for row in self.rows] |
404 return '\n'.join(result) | 404 return '\n'.join(result) |
405 | 405 |
406 def output_row_as_xml(self, row): | 406 def output_row_as_xml(self, row): |
407 result = [' <%s>\n %s\n </%s>' % | 407 result = [' <%s>\n %s\n </%s>' % |
408 (colname.lower(), str('' if (itm is None) else itm), colname.lower()) | 408 (colname.lower(), str('' if (itm is None) else itm), colname.lower()) |
409 for (itm, colname) in zip(row, self.colnames)] | 409 for (itm, colname) in zip(row, self.colnames)] |
410 return '\n'.join(result) | 410 return '\n'.join(result) |
419 result = [' <tr>\n %s\n </tr>' % result] | 419 result = [' <tr>\n %s\n </tr>' % result] |
420 for row in self.rows: | 420 for row in self.rows: |
421 result.append(' <tr>\n %s\n </tr>' % | 421 result.append(' <tr>\n %s\n </tr>' % |
422 (''.join('<td>%s</td>' % | 422 (''.join('<td>%s</td>' % |
423 str('' if (itm is None) else itm) | 423 str('' if (itm is None) else itm) |
424 for itm in row))) | 424 for itm in row))) |
425 result = '''<table id="%s"> | 425 result = '''<table id="%s"> |
426 %s | 426 %s |
427 </table>''' % (self.tblname, '\n'.join(result)) | 427 </table>''' % (self.tblname, '\n'.join(result)) |
428 return result | 428 return result |
429 | 429 |
472 newdesc[0][0] = 'COLUMN NAME' | 472 newdesc[0][0] = 'COLUMN NAME' |
473 result = '\n' + sqlpython.pmatrix(transpr,newdesc) | 473 result = '\n' + sqlpython.pmatrix(transpr,newdesc) |
474 else: | 474 else: |
475 result = sqlpython.pmatrix(self.rows, self.curs.description, self.maxfetch) | 475 result = sqlpython.pmatrix(self.rows, self.curs.description, self.maxfetch) |
476 return result | 476 return result |
477 | 477 |
478 statementEndPattern = re.compile(r'(.*)(;|\\[gGhtxicCsS])\s*(\d*)$', re.DOTALL | re.MULTILINE) | 478 statementEndPattern = re.compile(r'(.*)(;|\\[gGhtxicCsS])\s*(\d*)$', re.DOTALL | re.MULTILINE) |
479 # what about quote-enclosed? | 479 # what about quote-enclosed? |
480 | 480 |
481 def findTerminator(self, statement): | 481 def findTerminator(self, statement): |
482 m = self.statementEndPattern.search(statement) | 482 m = self.statementEndPattern.search(statement) |
483 if m: | 483 if m: |
484 return m.groups() | 484 return m.groups() |
485 else: | 485 else: |
486 return statement, None, None | 486 return statement, None, None |
487 | 487 |
488 legalOracle = re.compile('[a-zA-Z_$#]') | 488 legalOracle = re.compile('[a-zA-Z_$#]') |
489 | 489 |
490 def do_select(self, arg, bindVarsIn=None, override_terminator=None): | 490 def do_select(self, arg, bindVarsIn=None, override_terminator=None): |
491 """Fetch rows from a table. | 491 """Fetch rows from a table. |
492 | 492 |
493 Limit the number of rows retrieved by appending | 493 Limit the number of rows retrieved by appending |
494 an integer after the terminator | 494 an integer after the terminator |
495 (example: SELECT * FROM mytable;10 ) | 495 (example: SELECT * FROM mytable;10 ) |
496 | 496 |
497 Output may be formatted by choosing an alternative terminator | 497 Output may be formatted by choosing an alternative terminator |
498 ("help terminators" for details) | 498 ("help terminators" for details) |
499 """ | 499 """ |
500 bindVarsIn = bindVarsIn or {} | 500 bindVarsIn = bindVarsIn or {} |
501 self.query = 'select ' + arg | 501 self.query = 'select ' + arg |
530 self.sqlBuffer.append(self.query) | 530 self.sqlBuffer.append(self.query) |
531 | 531 |
532 pullflags = flagReader.FlagSet([flagReader.Flag('full')]) | 532 pullflags = flagReader.FlagSet([flagReader.Flag('full')]) |
533 def do_pull(self, arg): | 533 def do_pull(self, arg): |
534 """Displays source code. | 534 """Displays source code. |
535 | 535 |
536 --full, -f: get dependent objects as well""" | 536 --full, -f: get dependent objects as well""" |
537 | 537 |
538 options, arg = self.pullflags.parse(arg) | 538 options, arg = self.pullflags.parse(arg) |
539 object_type, owner, object_name = self.resolve(arg.strip(self.terminator).upper()) | 539 object_type, owner, object_name = self.resolve(arg.strip(self.terminator).upper()) |
540 if not object_type: | 540 if not object_type: |
541 return | 541 return |
542 self.stdout.write("%s %s.%s\n" % (object_type, owner, object_name)) | 542 self.stdout.write("%s %s.%s\n" % (object_type, owner, object_name)) |
543 self.stdout.write(str(self.curs.callfunc('DBMS_METADATA.GET_DDL', cx_Oracle.CLOB, | 543 self.stdout.write(str(self.curs.callfunc('DBMS_METADATA.GET_DDL', cx_Oracle.CLOB, |
544 [object_type, object_name, owner]))) | 544 [object_type, object_name, owner]))) |
545 if options.has_key('full'): | 545 if options.has_key('full'): |
546 for dependent_type in ('OBJECT_GRANT', 'CONSTRAINT', 'TRIGGER'): | 546 for dependent_type in ('OBJECT_GRANT', 'CONSTRAINT', 'TRIGGER'): |
547 try: | 547 try: |
548 self.stdout.write(str(self.curs.callfunc('DBMS_METADATA.GET_DEPENDENT_DDL', cx_Oracle.CLOB, | 548 self.stdout.write(str(self.curs.callfunc('DBMS_METADATA.GET_DEPENDENT_DDL', cx_Oracle.CLOB, |
549 [dependent_type, object_name, owner]))) | 549 [dependent_type, object_name, owner]))) |
550 except cx_Oracle.DatabaseError: | 550 except cx_Oracle.DatabaseError: |
551 pass | 551 pass |
552 | 552 |
553 findflags = flagReader.FlagSet([flagReader.Flag('insensitive')]) | 553 findflags = flagReader.FlagSet([flagReader.Flag('insensitive')]) |
554 def do_find(self, arg): | 554 def do_find(self, arg): |
555 """Finds argument in source code. | 555 """Finds argument in source code. |
556 | 556 |
557 --insensitive, -i: case-insensitive search""" | 557 --insensitive, -i: case-insensitive search""" |
558 | 558 |
559 options, arg = self.findflags.parse(arg) | 559 options, arg = self.findflags.parse(arg) |
560 if options.has_key('insensitive'): | 560 if options.has_key('insensitive'): |
561 searchfor = "LOWER(text)" | 561 searchfor = "LOWER(text)" |
562 arg = arg.lower() | 562 arg = arg.lower() |
563 else: | 563 else: |
564 searchfor = "text" | 564 searchfor = "text" |
565 self.do_select("* from all_source where %s like '%%%s%%'" % (searchfor, arg)) | 565 self.do_select("* from all_source where %s like '%%%s%%'" % (searchfor, arg)) |
566 | 566 |
567 def do_describe(self, arg): | 567 def do_describe(self, arg): |
568 "emulates SQL*Plus's DESCRIBE" | 568 "emulates SQL*Plus's DESCRIBE" |
569 | 569 |
570 if not arg: | 570 if not arg: |
571 self.do_select("""object_name, object_type FROM all_objects WHERE object_type IN ('TABLE','VIEW','INDEX') ORDER BY object_name""") | 571 self.do_select("""object_name, object_type, owner FROM all_objects WHERE object_type IN ('TABLE','VIEW','INDEX') ORDER BY object_name""") |
572 return | 572 return |
573 object_type, owner, object_name = self.resolve(arg.strip(self.terminator).upper()) | 573 object_type, owner, object_name = self.resolve(arg.strip(self.terminator).upper()) |
574 if not object_type: | 574 if not object_type: |
575 self.do_select("""object_name, object_type FROM all_objects | 575 self.do_select("""object_name, object_type, owner FROM all_objects |
576 WHERE object_type IN ('TABLE','VIEW','INDEX') | 576 WHERE object_type IN ('TABLE','VIEW','INDEX') |
577 AND object_name LIKE '%%%s%%' | 577 AND object_name LIKE '%%%s%%' |
578 ORDER BY object_name""" % arg.upper() ) | 578 ORDER BY object_name""" % arg.upper() ) |
579 return | 579 return |
580 self.stdout.write("%s %s.%s\n" % (object_type, owner, object_name)) | 580 self.stdout.write("%s %s.%s\n" % (object_type, owner, object_name)) |
581 descQ = descQueries.get(object_type) | 581 descQ = descQueries.get(object_type) |
582 if descQ: | 582 if descQ: |
583 for q in descQ: | 583 for q in descQ: |
587 packageContents = self.curs.fetchall() | 587 packageContents = self.curs.fetchall() |
588 for (packageObj_name,) in packageContents: | 588 for (packageObj_name,) in packageContents: |
589 print packageObj_name | 589 print packageObj_name |
590 self.do_select(descQueries['PackageObjArgs'][0],bindVarsIn={'package_name':object_name, 'owner':owner, 'object_name':packageObj_name}) | 590 self.do_select(descQueries['PackageObjArgs'][0],bindVarsIn={'package_name':object_name, 'owner':owner, 'object_name':packageObj_name}) |
591 do_desc = do_describe | 591 do_desc = do_describe |
592 | 592 |
593 def do_deps(self, arg): | |
594 object_type, owner, object_name = self.resolve(arg.strip(self.terminator).upper()) | |
595 if object_type == 'PACKAGE BODY': | |
596 q = "and (type != 'PACKAGE BODY' or name != :object_name)'" | |
597 object_type = 'PACKAGE' | |
598 else: | |
599 q = "" | |
600 q = """ name, | |
601 type | |
602 from user_dependencies | |
603 where | |
604 referenced_name like :object_name | |
605 and referenced_type like :object_type | |
606 and referenced_owner like :owner | |
607 %s""" % (q) | |
608 self.do_select(q, {'object_name':object_name, 'object_type':object_type, 'owner':owner}) | |
609 | |
593 def do_comments(self, arg): | 610 def do_comments(self, arg): |
594 'Prints comments on a table and its columns.' | 611 'Prints comments on a table and its columns.' |
595 object_type, owner, object_name = self.resolve(arg.strip(self.terminator).upper()) | 612 object_type, owner, object_name = self.resolve(arg.strip(self.terminator).upper()) |
596 if object_type: | 613 if object_type: |
597 self.curs.execute(queries['tabComments'],{'table_name':object_name, 'owner':owner}) | 614 self.curs.execute(queries['tabComments'],{'table_name':object_name, 'owner':owner}) |
617 object_type, owner, object_name = '', '', '' | 634 object_type, owner, object_name = '', '', '' |
618 return object_type, owner, object_name | 635 return object_type, owner, object_name |
619 | 636 |
620 def do_resolve(self, arg): | 637 def do_resolve(self, arg): |
621 self.stdout.write(self.resolve(arg)+'\n') | 638 self.stdout.write(self.resolve(arg)+'\n') |
622 | 639 |
623 def spoolstop(self): | 640 def spoolstop(self): |
624 if self.spoolFile: | 641 if self.spoolFile: |
625 sys.stdout = self.stdoutBeforeSpool | 642 sys.stdout = self.stdoutBeforeSpool |
626 print 'Finished spooling to ', self.spoolFile.name | 643 print 'Finished spooling to ', self.spoolFile.name |
627 self.spoolFile.close() | 644 self.spoolFile.close() |
628 self.spoolFile = None | 645 self.spoolFile = None |
629 | 646 |
630 def do_spool(self, arg): | 647 def do_spool(self, arg): |
631 """spool [filename] - begins redirecting output to FILENAME.""" | 648 """spool [filename] - begins redirecting output to FILENAME.""" |
632 self.spoolstop() | 649 self.spoolstop() |
633 arg = arg.strip() | 650 arg = arg.strip() |
634 if not arg: | 651 if not arg: |
645 f = open(fname, 'w') | 662 f = open(fname, 'w') |
646 sys.stdout = f | 663 sys.stdout = f |
647 self.onecmd_plus_hooks(arg) | 664 self.onecmd_plus_hooks(arg) |
648 f.close() | 665 f.close() |
649 sys.stdout = originalOut | 666 sys.stdout = originalOut |
650 | 667 |
651 def do_write(self, args): | 668 def do_write(self, args): |
652 'write [filename.extension] query - writes result to a file' | 669 'write [filename.extension] query - writes result to a file' |
653 words = args.split(None, 1) | 670 words = args.split(None, 1) |
654 if len(words) > 1 and '.' in words[0]: | 671 if len(words) > 1 and '.' in words[0]: |
655 fname, command = words | 672 fname, command = words |
656 else: | 673 else: |
657 fname, command = 'output.txt', args | 674 fname, command = 'output.txt', args |
658 self.write(command, fname) | 675 self.write(command, fname) |
659 print 'Results written to %s' % os.path.join(os.getcwd(), fname) | 676 print 'Results written to %s' % os.path.join(os.getcwd(), fname) |
660 | 677 |
661 def do_compare(self, args): | 678 def do_compare(self, args): |
662 """COMPARE query1 TO query2 - uses external tool to display differences. | 679 """COMPARE query1 TO query2 - uses external tool to display differences. |
663 | 680 |
664 Sorting is recommended to avoid false hits. | 681 Sorting is recommended to avoid false hits. |
665 Will attempt to use a graphical diff/merge tool like kdiff3, meld, or Araxis Merge, | 682 Will attempt to use a graphical diff/merge tool like kdiff3, meld, or Araxis Merge, |
666 if they are installed.""" | 683 if they are installed.""" |
667 fnames = [] | 684 fnames = [] |
668 args2 = args.split(' to ') | 685 args2 = args.split(' to ') |
680 def do_getrun(self, fname): | 697 def do_getrun(self, fname): |
681 'Brings SQL commands from a file to the in-memory SQL buffer, and executes them.' | 698 'Brings SQL commands from a file to the in-memory SQL buffer, and executes them.' |
682 Cmd.do_load(self, fname) | 699 Cmd.do_load(self, fname) |
683 def do_psql(self, arg): | 700 def do_psql(self, arg): |
684 '''Shortcut commands emulating psql's backslash commands. | 701 '''Shortcut commands emulating psql's backslash commands. |
685 | 702 |
686 \c connect | 703 \c connect |
687 \d desc | 704 \d desc |
688 \e edit | 705 \e edit |
689 \g run | 706 \g run |
690 \h help | 707 \h help |
703 commands = {} | 720 commands = {} |
704 for c in self.do_psql.__doc__.splitlines()[2:]: | 721 for c in self.do_psql.__doc__.splitlines()[2:]: |
705 (abbrev, command) = c.split(None, 1) | 722 (abbrev, command) = c.split(None, 1) |
706 commands[abbrev[1:]] = command | 723 commands[abbrev[1:]] = command |
707 words = arg.split(None,1) | 724 words = arg.split(None,1) |
708 abbrev = words[0] | 725 try: |
726 abbrev = words[0] | |
727 except IndexError: | |
728 return | |
709 try: | 729 try: |
710 args = words[1] | 730 args = words[1] |
711 except IndexError: | 731 except IndexError: |
712 args = '' | 732 args = '' |
713 try: | 733 try: |
715 except KeyError: | 735 except KeyError: |
716 print 'psql command \%s not yet supported.' % abbrev | 736 print 'psql command \%s not yet supported.' % abbrev |
717 | 737 |
718 def do__dir_tables(self, arg): | 738 def do__dir_tables(self, arg): |
719 self.do_select("""table_name, 'TABLE' as type, owner FROM all_tables WHERE table_name LIKE '%%%s%%'""" % arg.upper()) | 739 self.do_select("""table_name, 'TABLE' as type, owner FROM all_tables WHERE table_name LIKE '%%%s%%'""" % arg.upper()) |
720 | 740 |
721 def do__dir_views(self, arg): | 741 def do__dir_views(self, arg): |
722 self.do_select("""view_name, 'VIEW' as type, owner FROM all_views WHERE view_name LIKE '%%%s%%'""" % arg.upper()) | 742 self.do_select("""view_name, 'VIEW' as type, owner FROM all_views WHERE view_name LIKE '%%%s%%'""" % arg.upper()) |
723 | 743 |
724 def do__dir_indexes(self, arg): | 744 def do__dir_indexes(self, arg): |
725 self.do_select("""index_name, index_type, owner FROM all_indexes WHERE index_name LIKE '%%%s%%' OR table_name LIKE '%%%s%%'""" % (arg.upper(), arg.upper())) | 745 self.do_select("""index_name, index_type, owner FROM all_indexes WHERE index_name LIKE '%%%s%%' OR table_name LIKE '%%%s%%'""" % (arg.upper(), arg.upper())) |
727 def do__dir_tablespaces(self, arg): | 747 def do__dir_tablespaces(self, arg): |
728 self.do_select("""tablespace_name, file_name from dba_data_files""") | 748 self.do_select("""tablespace_name, file_name from dba_data_files""") |
729 | 749 |
730 def do__dir_schemas(self, arg): | 750 def do__dir_schemas(self, arg): |
731 self.do_select("""owner, count(*) AS objects FROM all_objects GROUP BY owner ORDER BY owner""") | 751 self.do_select("""owner, count(*) AS objects FROM all_objects GROUP BY owner ORDER BY owner""") |
732 | 752 |
733 def do_head(self, arg): | 753 def do_head(self, arg): |
734 nrows = 10 | 754 nrows = 10 |
735 args = arg.split() | 755 args = arg.split() |
736 if len(args) > 1: | 756 if len(args) > 1: |
737 for a in args: | 757 for a in args: |
741 args.remove(a) | 761 args.remove(a) |
742 except: | 762 except: |
743 pass | 763 pass |
744 arg = ' '.join(args) | 764 arg = ' '.join(args) |
745 self.do_select('* from %s;%d' % (arg, nrows)) | 765 self.do_select('* from %s;%d' % (arg, nrows)) |
746 | 766 |
747 def do_print(self, arg): | 767 def do_print(self, arg): |
748 'print VARNAME: Show current value of bind variable VARNAME.' | 768 'print VARNAME: Show current value of bind variable VARNAME.' |
749 if arg: | 769 if arg: |
750 if arg[0] == ':': | 770 if arg[0] == ':': |
751 arg = arg[1:] | 771 arg = arg[1:] |
754 except KeyError: | 774 except KeyError: |
755 self.stdout.write('No bind variable %s\n' % arg) | 775 self.stdout.write('No bind variable %s\n' % arg) |
756 else: | 776 else: |
757 for (var, val) in self.binds.items(): | 777 for (var, val) in self.binds.items(): |
758 print ':%s = %s' % (var, val) | 778 print ':%s = %s' % (var, val) |
759 | 779 |
760 def do_setbind(self, arg): | 780 def do_setbind(self, arg): |
761 args = arg.split(None, 2) | 781 args = arg.split(None, 2) |
762 if len(args) < 2: | 782 if len(args) < 2: |
763 self.do_print(arg) | 783 self.do_print(arg) |
764 elif len(args) > 2 and args[1] in ('=',':='): | 784 elif len(args) > 2 and args[1] in ('=',':='): |
765 var, val = args[0], args[2] | 785 var, val = args[0], args[2] |
766 if val[0] == val[-1] == "'" and len(val) > 1: | 786 if val[0] == val[-1] == "'" and len(val) > 1: |
767 val = val[1:-1] | 787 self.binds[var] = val[1:-1] |
788 return | |
768 try: | 789 try: |
769 val = int(val) | 790 self.binds[var] = int(val) |
791 return | |
770 except ValueError: | 792 except ValueError: |
771 try: | 793 try: |
772 val = float(val) | 794 self.binds[var] = float(val) |
795 return | |
773 except ValueError: | 796 except ValueError: |
774 val = self.curs.callfunc(val, []) | 797 try: |
775 # submit to sql | 798 self.binds[var] = self.curs.callfunc(val, []) |
776 | 799 return |
777 self.binds[var] = val # but what if val is a function call? | 800 except: |
778 else: | 801 pass |
779 print 'Could not parse ', args | 802 # TODO: fix |
780 | 803 |
804 print 'Could not parse ', args | |
805 | |
781 def do_exec(self, arg): | 806 def do_exec(self, arg): |
782 if arg[0] == ':': | 807 if arg[0] == ':': |
783 self.do_setbind(arg[1:]) | 808 self.do_setbind(arg[1:]) |
784 else: | 809 else: |
785 try: | 810 try: |
800 self.curs.execute('\n'.join(lines)) | 825 self.curs.execute('\n'.join(lines)) |
801 except Exception, e: | 826 except Exception, e: |
802 print e | 827 print e |
803 return | 828 return |
804 lines.append(line) | 829 lines.append(line) |
805 | 830 |
806 def do_begin(self, arg): | 831 def do_begin(self, arg): |
807 self.anon_plsql('begin ' + arg) | 832 self.anon_plsql('begin ' + arg) |
808 | 833 |
809 def do_declare(self, arg): | 834 def do_declare(self, arg): |
810 self.anon_plsql('declare ' + arg) | 835 self.anon_plsql('declare ' + arg) |
811 | 836 |
812 def do_create(self, arg): | 837 def do_create(self, arg): |
813 self.anon_plsql('create ' + arg) | 838 self.anon_plsql('create ' + arg) |
814 | 839 |
815 lsflags = flagReader.FlagSet([flagReader.Flag('long')]) | 840 lsflags = flagReader.FlagSet([flagReader.Flag('long')]) |
816 def do_ls(self, arg): | 841 def do_ls(self, arg): |
817 options, arg = self.lsflags.parse(arg) | 842 options, arg = self.lsflags.parse(arg) |
818 where = '' | 843 where = '' |
819 if arg: | 844 if arg: |
820 where = """\nWHERE object_type || '/' || object_name | 845 where = """\nWHERE object_type || '/' || object_name |
821 LIKE '%%%s%%'""" % (arg.upper().replace('*','%')) | 846 LIKE '%%%s%%'""" % (arg.upper().replace('*','%')) |
822 else: | 847 else: |
823 where = '' | 848 where = '' |
824 result = [] | 849 result = [] |
825 statement = '''SELECT object_type, object_name, | 850 statement = '''SELECT object_type, object_name, |
826 status, last_ddl_time | 851 status, last_ddl_time |
827 FROM user_objects %s | 852 FROM user_objects %s |
828 ORDER BY object_type, object_name''' % (where) | 853 ORDER BY object_type, object_name''' % (where) |
829 self.curs.execute(statement) | 854 self.curs.execute(statement) |
830 for (object_type, object_name, status, last_ddl_time) in self.curs.fetchall(): | 855 for (object_type, object_name, status, last_ddl_time) in self.curs.fetchall(): |
831 if options.has_key('long'): | 856 if options.has_key('long'): |
832 result.append('%s\t%s\t%s/%s' % (status, last_ddl_time, object_type, object_name)) | 857 result.append('%s\t%s\t%s/%s' % (status, last_ddl_time, object_type, object_name)) |
833 else: | 858 else: |
834 result.append('%s/%s' % (object_type, object_name)) | 859 result.append('%s/%s' % (object_type, object_name)) |
835 self.stdout.write('\n'.join(result) + '\n') | 860 self.stdout.write('\n'.join(result) + '\n') |
836 | 861 |
837 | 862 |
838 if options.has_key('insensitive'): | 863 if options.has_key('insensitive'): |
839 searchfor = "LOWER(text)" | 864 searchfor = "LOWER(text)" |
840 arg = arg.lower() | 865 arg = arg.lower() |
841 | 866 |
842 | 867 |
843 def do_cat(self, arg): | 868 def do_cat(self, arg): |
844 targets = arg.split() | 869 targets = arg.split() |
845 for target in targets: | 870 for target in targets: |
846 self.do_select('* from %s' % target) | 871 self.do_select('* from %s' % target) |
847 | 872 |
848 def do_grep(self, arg): | 873 def do_grep(self, arg): |
849 """grep PATTERN TABLE - search for term in any of TABLE's fields""" | 874 """grep PATTERN TABLE - search for term in any of TABLE's fields""" |
850 targets = arg.split() | 875 targets = arg.split() |
851 pattern = targets.pop(0) | 876 pattern = targets.pop(0) |
852 for target in targets: | 877 for target in targets: |
868 self.do_select(queries['refs'],bindVarsIn={'object_name':object_name, 'owner':owner}) | 893 self.do_select(queries['refs'],bindVarsIn={'object_name':object_name, 'owner':owner}) |
869 | 894 |
870 def _test(): | 895 def _test(): |
871 import doctest | 896 import doctest |
872 doctest.testmod() | 897 doctest.testmod() |
873 | 898 |
874 if __name__ == "__main__": | 899 if __name__ == "__main__": |
875 "Silent return implies that all unit tests succeeded. Use -v to see details." | 900 "Silent return implies that all unit tests succeeded. Use -v to see details." |
876 _test() | 901 _test() |