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()