#author: James Henstridge #adapted to _mysql by Andy Dustman """This is a class that implements an interface to mySQL databases, conforming to the API published by the Python db-sig at http://www.python.org/sigs/db-sig/DatabaseAPI.html It is really just a wrapper for an older python interface to mySQL databases called mySQL, which I modified to facilitate use of a cursor. That module was Joseph Skinner's port of the mSQL module by David Gibson, which was a modified version of Anthony Baxter's msql module. As an example, to add some extra (unprivelledged) users to your database system, and delete them again: >>> import Mysqldb >>> conn = Mysqldb.mysqldb('mysql@localhost root rootpasswd') >>> curs = conn.cursor() >>> curs.execute("insert into user (host, user) values ('%s', '%s')", ... [('localhost', 'linus'), ('somewhere.com.au', 'james')]) 2 >>> curs.execute("select * from user") >>> curs.fetchall() -- record listing -- >>> curs.execute("delete from user where host = 'somewhere.com.au' or user = 'linus'") 2 >>> curs.close() >>> conn.close() The argument to mysqldb.mysqldb is of the form 'db@host user pass', 'db@host user', 'db@host', 'db', 'db user pass' or 'db user'. As always, the source is a good manual :-) James Henstridge """ import _mysql MySQL = _mysql from string import upper, split, join error = 'mysqldb.error' _type = {} for a in ('char', 'varchar', 'string', 'unhandled', '????'): _type[a] = 'STRING' for a in ('tiny blob', 'medium blob', 'long blob', 'blob'): _type[a] = 'RAW' for a in ('short', 'long', 'float', 'double', 'decimal'): _type[a] = 'NUMBER' for a in ('date', 'time', 'datetime', 'timestamp'): _type[a] = 'DATE' del a from _mysql import FIELD_TYPE _type_conv = { FIELD_TYPE.TINY: int, FIELD_TYPE.SHORT: int, FIELD_TYPE.LONG: int, FIELD_TYPE.FLOAT: float, FIELD_TYPE.DOUBLE: float, FIELD_TYPE.LONGLONG: long, FIELD_TYPE.INT24: int, FIELD_TYPE.YEAR: int } def isDDL(q): return upper(split(q)[0]) in ('CREATE', 'ALTER', 'GRANT', 'REVOKE', 'DROP', 'SET') def isDML(q): return upper(split(q)[0]) in ('DELETE', 'INSERT', 'UPDATE', 'LOAD') def isDQL(q): return upper(split(q)[0]) in ('SELECT', 'SHOW', 'DESC', 'DESCRIBE') class Connection: """This is the connection object for the mySQL database interface.""" def __init__(self, host, user, passwd, db): kwargs = {} kwargs['conv'] = _type_conv if host: kwargs['host'] = host if user: kwargs['user'] = user if passwd: kwargs['passwd'] = passwd if db: kwargs['db'] = db try: self.__conn = apply(MySQL.connect, (), kwargs) except MySQL.Error, msg: raise error, msg self.__curs = Cursor(self.__conn) self._server_info = i = self.__conn.get_server_info() self._server_version = int(i[0])*10000 + int(i[2:4])*100 + int(i[5:7]) def __del__(self): self.close() def __getattr__(self, key): return getattr(self.__curs, key) def __setattr__(self, key, val): if key in ('arraysize', 'description', 'insert_id'): setattr(self.__curs, key, val) else: self.__dict__[key] = val def close(self): self.__conn = None def cursor(self): if self.__conn == None: raise error, "Connection is closed." return Cursor(self.__conn) def commit(self): """Commit the current transaction.""" if self._server_version > 32315: self.__conn.query("COMMIT") def rollback(self): """Rollback the current transaction.""" if self._server_version > 32315: self.__conn.query("ROLLBACK") else: raise error, "Not supported by server" def callproc(self, params=None): pass # These functions are just here so that every action that is # covered by mySQL is covered by mysqldb. They are not standard # DB API. The list* methods are not included, since they can be # done with the SQL SHOW command. def create(self, dbname): """This is not a standard part of Python DB API.""" self.__conn.query("CREATE DATABASE %s" % dbname) self.__conn.store_result() return None def drop(self, dbname): """This is not a standard part of Python DB API.""" self.__conn.query("DROP DATABASE %s" % dbname) self.__conn.store_result() return None def reload(self): """This is not a standard part of Python DB API.""" self.__conn.query("RELOAD TABLES") self.__conn.store_result() return None def shutdown(self): """This is not a standard part of Python DB API.""" return self.__conn.shutdown() class Cursor: """A cursor object for use with connecting to mySQL databases.""" def __init__(self, conn): self.__conn = conn self.__res = None self.arraysize = 1 self.__dict__['description'] = None self.__open = 1 self.insert_id = 0 def __del__(self): self.close() def __setattr__(self, key, val): if key == 'description': raise error, "description is a read-only attribute." else: self.__dict__[key] = val def __delattr__(self, key): if key in ('description', 'arraysize', 'insert_id'): raise error, "%s can't be deleted." % (key,) else: del self.__dict__[key] def close(self): self.__conn = None self.__res = None self.__open = 0 def execute(self, op, params=None): if not self.__open: raise error, "Cursor has been closed." if params: if type(params[0]) not in (type(()), type([])): params = [params] if isDDL(op): self.__dict__['description'] = None try: for x in params: self.__res = \ self.__conn.query(op % x) self.insert_id = self.__res.insert_id() except MySQL.Error, msg: raise error, msg return 1 if isDML(op): self.__dict__['description'] = None af = 0 try: for x in params: self.__res = \ self.__conn.query(op % x) af =af+self.__res.affectedrows() self.insert_id = self.__res.insert_id() except MySQL.Error, msg: raise error, msg return af if isDQL(op): try: self.__res = self.__conn.query( op % params[-1]) self.insert_id = self.__res.insert_id() f = self.__res.fields() except MySQL.Error, msg: raise error, msg self.__dict__['description'] = tuple(map( lambda x: (x[0], _type[x[2]], x[3], x[3]), f)) return None else: try: self.__conn.query(op) self.__res = self.__conn.store_result() self.insert_id = self.__conn.insert_id() except MySQL.Error, msg: raise error, msg self.__dict__['description'] = None if isDDL(op): return 1 elif self.__conn.affected_rows() != -1: return self.__conn.affected_rows() else: self.__dict__['description'] = self.__res.describe() return None def fetchone(self): if not self.__res: raise error, "no query made yet." try: return self.__res.fetch_row(1)[0] except MySQL.Error, msg: raise error, msg def fetchmany(self, size=None): if not self.__res: raise error, "no query made yet." try: return self.__res.fetch_row(size or self.arraysize) except MySQL.Error, msg: raise error, msg def fetchall(self): if not self.__res: raise error, "no query made yet." try: rows = r = list(self.__res.fetch_row(self.arraysize)) while 1: rows = list(self.__res.fetch_row(self.arraysize)) if not rows: break r.extend(rows) return r except MySQL.Error, msg: raise error, msg def fetchoneDict(self): """This is not a standard part of Python DB API.""" if not self.__res: raise error, "no query made yet." try: return self.__res.fetch_row(1, 2)[0] except MySQL.Error, msg: raise error, msg def fetchmanyDict(self, size=None): """This is not a standard part of Python DB API.""" if not self.__res: raise error, "no query made yet." try: return self.__res.fetch_row(size or self.arraysize, 2) except MySQL.Error, msg: raise error, msg def fetchallDict(self): """This is not a standard part of Python DB API.""" if not self.__res: raise error, "no query made yet." try: rows = r = list(self.__res.fetch_row(self.arraysize, 2)) while 1: rows = list(self.__res.fetch_row(self.arraysize, 2)) if not rows: break r.extend(rows) return r except MySQL.Error, msg: raise error, msg def setinputsizes(self, sizes): pass def setoutputsize(self, size, col=None): pass def mysqldb(connect_string): """Makes a connection to the MySQL server. The Argument should be of the form 'db@host user pass' or 'db@host user' or 'db@host' or 'db' or 'db user pass' or 'db user', where db is the database name, host is the server's host name, user is your user name, and pass is your password.""" val = split(connect_string) if len(val) == 0: raise error, "no database specified" while len(val) < 3: val.append('') dh = split(val[0], '@') if len(dh) == 0: raise error, "no database specified" while len(dh) < 2: dh.append('') return Connection(dh[1], val[1], val[2], dh[0])