| # $Id: isql.py 6639 2009-08-10 17:06:51Z fwierzbicki $ |
| |
| import dbexts, cmd, sys, os |
| |
| """ |
| Isql works in conjunction with dbexts to provide an interactive environment |
| for database work. |
| """ |
| |
| __version__ = "$Revision: 6639 $"[11:-2] |
| |
| class IsqlExit(Exception): pass |
| |
| class Prompt: |
| """ |
| This class fixes a problem with the cmd.Cmd class since it uses an ivar 'prompt' |
| as opposed to a method 'prompt()'. To get around this, this class is plugged in |
| as a 'prompt' attribute and when invoked the '__str__' method is called which |
| figures out the appropriate prompt to display. I still think, even though this |
| is clever, the attribute version of 'prompt' is poor design. |
| """ |
| def __init__(self, isql): |
| self.isql = isql |
| def __str__(self): |
| prompt = "%s> " % (self.isql.db.dbname) |
| if len(self.isql.sqlbuffer) > 0: |
| prompt = "... " |
| return prompt |
| if os.name == 'java': |
| def __tojava__(self, cls): |
| import java.lang.String |
| if cls == java.lang.String: |
| return self.__str__() |
| return False |
| |
| class IsqlCmd(cmd.Cmd): |
| |
| def __init__(self, db=None, delimiter=";", comment=('#', '--')): |
| cmd.Cmd.__init__(self, completekey=None) |
| if db is None or type(db) == type(""): |
| self.db = dbexts.dbexts(db) |
| else: |
| self.db = db |
| self.kw = {} |
| self.sqlbuffer = [] |
| self.comment = comment |
| self.delimiter = delimiter |
| self.prompt = Prompt(self) |
| |
| def parseline(self, line): |
| command, arg, line = cmd.Cmd.parseline(self, line) |
| if command and command <> "EOF": |
| command = command.lower() |
| return command, arg, line |
| |
| def do_which(self, arg): |
| """\nPrints the current db connection parameters.\n""" |
| print self.db |
| return False |
| |
| def do_EOF(self, arg): |
| return False |
| |
| def do_p(self, arg): |
| """\nExecute a python expression.\n""" |
| try: |
| exec arg.strip() in globals() |
| except: |
| print sys.exc_info()[1] |
| return False |
| |
| def do_column(self, arg): |
| """\nInstructions for column display.\n""" |
| return False |
| |
| def do_use(self, arg): |
| """\nUse a new database connection.\n""" |
| # this allows custom dbexts |
| self.db = self.db.__class__(arg.strip()) |
| return False |
| |
| def do_table(self, arg): |
| """\nPrints table meta-data. If no table name, prints all tables.\n""" |
| if len(arg.strip()): |
| self.db.table(arg, **self.kw) |
| else: |
| self.db.table(None, **self.kw) |
| return False |
| |
| def do_proc(self, arg): |
| """\nPrints store procedure meta-data.\n""" |
| if len(arg.strip()): |
| self.db.proc(arg, **self.kw) |
| else: |
| self.db.proc(None, **self.kw) |
| return False |
| |
| def do_schema(self, arg): |
| """\nPrints schema information.\n""" |
| print |
| self.db.schema(arg) |
| print |
| return False |
| |
| def do_delimiter(self, arg): |
| """\nChange the delimiter.\n""" |
| delimiter = arg.strip() |
| if len(delimiter) > 0: |
| self.delimiter = delimiter |
| |
| def do_o(self, arg): |
| """\nSet the output.\n""" |
| if not arg: |
| fp = self.db.out |
| try: |
| if fp: |
| fp.close() |
| finally: |
| self.db.out = None |
| else: |
| fp = open(arg, "w") |
| self.db.out = fp |
| |
| def do_q(self, arg): |
| """\nQuit.\n""" |
| try: |
| if self.db.out: |
| self.db.out.close() |
| finally: |
| return True |
| |
| def do_set(self, arg): |
| """\nSet a parameter. Some examples:\n set owner = 'informix'\n set types = ['VIEW', 'TABLE']\nThe right hand side is evaluated using `eval()`\n""" |
| if len(arg.strip()) == 0: |
| items = self.kw.items() |
| if len(items): |
| print |
| # format the results but don't include how many rows affected |
| for a in dbexts.console(items, ("key", "value"))[:-1]: |
| print a |
| print |
| return False |
| d = filter(lambda x: len(x) > 0, map(lambda x: x.strip(), arg.split("="))) |
| if len(d) == 1: |
| if self.kw.has_key(d[0]): |
| del self.kw[d[0]] |
| else: |
| self.kw[d[0]] = eval(d[1]) |
| |
| def do_i(self, arg): |
| fp = open(arg) |
| try: |
| print |
| for line in fp.readlines(): |
| line = self.precmd(line) |
| stop = self.onecmd(line) |
| stop = self.postcmd(stop, line) |
| finally: |
| fp.close() |
| return False |
| |
| def default(self, arg): |
| try: |
| token = arg.strip() |
| if not token: |
| return False |
| comment = [token.startswith(x) for x in self.comment] |
| if reduce(lambda x,y: x or y, comment): |
| return False |
| if token[0] == '\\': |
| token = token[1:] |
| # is it possible the line contains the delimiter |
| if len(token) >= len(self.delimiter): |
| # does the line end with the delimiter |
| if token[-1 * len(self.delimiter):] == self.delimiter: |
| # now add all up to the delimiter |
| self.sqlbuffer.append(token[:-1 * len(self.delimiter)]) |
| if self.sqlbuffer: |
| q = " ".join(self.sqlbuffer) |
| print q |
| self.db.isql(q, **self.kw) |
| self.sqlbuffer = [] |
| if self.db.updatecount: |
| print |
| if self.db.updatecount == 1: |
| print "1 row affected" |
| else: |
| print "%d rows affected" % (self.db.updatecount) |
| print |
| return False |
| if token: |
| self.sqlbuffer.append(token) |
| except: |
| self.sqlbuffer = [] |
| print |
| print sys.exc_info()[1] |
| print |
| return False |
| |
| def emptyline(self): |
| return False |
| |
| def postloop(self): |
| raise IsqlExit() |
| |
| def cmdloop(self, intro=None): |
| while 1: |
| try: |
| cmd.Cmd.cmdloop(self, intro) |
| except IsqlExit, e: |
| break |
| except Exception, e: |
| print |
| print e |
| print |
| intro = None |
| |
| if __name__ == '__main__': |
| import getopt |
| |
| try: |
| opts, args = getopt.getopt(sys.argv[1:], "b:", []) |
| except getopt.error, msg: |
| print |
| print msg |
| print "Try `%s --help` for more information." % (sys.argv[0]) |
| sys.exit(0) |
| |
| dbname = None |
| for opt, arg in opts: |
| if opt == '-b': |
| dbname = arg |
| |
| intro = "\nisql - interactive sql (%s)\n" % (__version__) |
| |
| isql = IsqlCmd(dbname) |
| isql.cmdloop() |