comparison sqlpyPlus.py @ 132:2baecb3d5356

improving tab completion
author catherine@Elli.myhome.westell.com
date Wed, 27 Aug 2008 21:49:26 -0400
parents 2b7ce838120d
children 25cbb45b190d
comparison
equal deleted inserted replaced
131:2b7ce838120d 132:2baecb3d5356
183 and c1.r_owner = c2.owner 183 and c1.r_owner = c2.owner
184 and c1.owner = :owner 184 and c1.owner = :owner
185 """ 185 """
186 } 186 }
187 187
188 import sys, os, re, sqlpython, cx_Oracle, pyparsing 188 import sys, os, re, sqlpython, cx_Oracle, pyparsing, re, completion
189 from cmd2 import Cmd, make_option, options, Statekeeper 189 from cmd2 import Cmd, make_option, options, Statekeeper
190 190
191 if float(sys.version[:3]) < 2.3: 191 if float(sys.version[:3]) < 2.3:
192 def enumerate(lst): 192 def enumerate(lst):
193 return zip(range(len(lst)), lst) 193 return zip(range(len(lst)), lst)
504 result = sqlpython.pmatrix(self.rows, self.curs.description, self.maxfetch) 504 result = sqlpython.pmatrix(self.rows, self.curs.description, self.maxfetch)
505 return result 505 return result
506 506
507 legalOracle = re.compile('[a-zA-Z_$#]') 507 legalOracle = re.compile('[a-zA-Z_$#]')
508 508
509 def complete_select(self, text, line, begidx, endidx): 509 def select_list(self, sql):
510 self.curs.execute(sql)
511 return [r[0] for r in self.curs.fetchall()]
512
513 tableNameRegex = re.compile(
514 r'(from|update)\s+(([a-zA-Z0-9_#$]+)\.)?([a-zA-Z0-9_#$]+)\s+',
515 re.IGNORECASE | re.DOTALL | re.MULTILINE)
516 columnNameRegex = re.compile(
517 r'select\s+(.*)from',
518 re.IGNORECASE | re.DOTALL | re.MULTILINE)
519 def completedefault(self, text, line, begidx, endidx):
520 segment = completion.whichSegment(line)
521 text = text.upper()
510 completions = [] 522 completions = []
511 for statement in """SELECT column_name FROM all_tab_columns WHERE column_name LIKE '%s%%' 523 if segment == 'select':
512 SELECT table_name FROM all_tables WHERE table_name LIKE '%s%%' 524 stmt = "SELECT column_name FROM user_tab_columns WHERE column_name LIKE '%s%%'"
513 SELECT DISTINCT owner FROM all_tables WHERE owner LIKE '%%%s'""".splitlines(): 525 completions = self.select_list(stmt % (text))
514 self.curs.execute(statement % text.upper()) 526 if not completions:
515 completions.extend([r[0] for r in self.curs.fetchall()]) 527 stmt = "SELECT column_name FROM all_tab_columns WHERE column_name LIKE '%s%%'"
528 completions = self.select_list(stmt % (text))
529 elif segment in ('from', 'update'):
530 stmt = "SELECT table_name FROM user_tables WHERE table_name LIKE '%s%%'"
531 completions = self.select_list(stmt % (text))
532 if not completions:
533 stmt = """SELECT table_name FROM user_tables WHERE table_name LIKE '%s%%'
534 UNION
535 SELECT DISTINCT owner FROM all_tables WHERE owner LIKE '%%%s'"""
536 completions = self.select_list(stmt % (text, text))
537 elif segment in ('where', 'group by', 'order by'):
538 try:
539 owner, tableName = self.tableNameRegex.search(line).groups()[2:4]
540 except AttributeError:
541 return []
542 if owner:
543 stmt = "SELECT column_name FROM all_tab_columns WHERE owner = '%s' AND table_name = '%s'" \
544 % (owner.upper(), tableName.upper())
545 else:
546 stmt = "SELECT column_name FROM all_tab_columns WHERE table_name = '%s'" \
547 % (tableName.upper())
548 completions = self.select_list(stmt)
549
516 return completions 550 return completions
517 551
518 rowlimitPattern = pyparsing.Word(pyparsing.nums)('rowlimit') 552 rowlimitPattern = pyparsing.Word(pyparsing.nums)('rowlimit')
519 terminatorPattern = (pyparsing.oneOf('; \\s \\S \\c \\C \\t \\x \\h') 553 terminatorPattern = (pyparsing.oneOf('; \\s \\S \\c \\C \\t \\x \\h')
520 ^ pyparsing.Literal('\n/') ^ \ 554 ^ pyparsing.Literal('\n/') ^ \
975 def do_refs(self, arg): 1009 def do_refs(self, arg):
976 arg = self.parsed(arg).unterminated.upper() 1010 arg = self.parsed(arg).unterminated.upper()
977 object_type, owner, object_name = self.resolve(arg) 1011 object_type, owner, object_name = self.resolve(arg)
978 if object_type == 'TABLE': 1012 if object_type == 'TABLE':
979 self.do_select(queries['refs'],bindVarsIn={'object_name':object_name, 'owner':owner}) 1013 self.do_select(queries['refs'],bindVarsIn={'object_name':object_name, 'owner':owner})
980
981 sqlStyleComment = pyparsing.Literal("--") + pyparsing.ZeroOrMore(pyparsing.CharsNotIn("\n"))
982 keywords = {'order by': pyparsing.Keyword('order', caseless=True) +
983 pyparsing.Keyword('by', caseless=True),
984 'select': pyparsing.Keyword('select', caseless=True),
985 'from': pyparsing.Keyword('from', caseless=True),
986 'having': pyparsing.Keyword('having', caseless=True),
987 'update': pyparsing.Keyword('update', caseless=True),
988 'set': pyparsing.Keyword('set', caseless=True),
989 'delete': pyparsing.Keyword('delete', caseless=True),
990 'insert into': pyparsing.Keyword('insert', caseless=True) +
991 pyparsing.Keyword('into', caseless=True),
992 'values': pyparsing.Keyword('values', caseless=True),
993 'group by': pyparsing.Keyword('group', caseless=True) +
994 pyparsing.Keyword('by', caseless=True),
995 'where': pyparsing.Keyword('where', caseless=True)}
996 for (name, parser) in keywords.items():
997 parser.ignore(pyparsing.sglQuotedString)
998 parser.ignore(pyparsing.dblQuotedString)
999 parser.ignore(pyparsing.cStyleComment)
1000 parser.ignore(sqlStyleComment)
1001 parser.name = name
1002
1003 def orderedParseResults(parsers, statement):
1004 results = []
1005 for parser in parsers:
1006 results.extend(parser.scanString(statement))
1007 results.sort(cmp=lambda x,y:cmp(x[1],y[1]))
1008 return results
1009
1010 def whichSqlPhrase(statement):
1011 results = orderedParseResults(keywords.values(), statement)
1012 if results:
1013 return ' '.join(results[-1][0])
1014 else:
1015 return None
1016
1017 oracleIdentifierCharacters = pyparsing.alphanums + '_#$'
1018 def wordInProgress(statement):
1019 result = []
1020 letters = list(statement)
1021 letters.reverse()
1022 for letter in letters:
1023 if letter not in oracleIdentifierCharacters:
1024 result.reverse()
1025 return ''.join(result)
1026 result.append(letter)
1027 result.reverse()
1028 return ''.join(result)
1029 1014
1030 def _test(): 1015 def _test():
1031 import doctest 1016 import doctest
1032 doctest.testmod() 1017 doctest.testmod()
1033 1018