diff --git a/MySQLdb/CompatMysqldb.py b/MySQLdb/CompatMysqldb.py new file mode 100755 index 0000000..a20c832 --- /dev/null +++ b/MySQLdb/CompatMysqldb.py @@ -0,0 +1,318 @@ +""" +Original author: James Henstridge +Adapted by: Andy Dustman + +This is the original Mysqldb.py module which came with MySQLmodule-1.4, +only it has been adapted to use _mysql instead MySQL. It is intended +for backwards compatibility purposes only. But as a bonus, transactions +will work if your database server and table types support them. It is +called CompatMysqldb instead of Mysqldb so as not to interfere with an +existing Mysqldb, or MySQLdb on case-insensitive brain-dead operating +systems. + +Under no circumstances should you bug James Henstridge about this!!! + +----- + +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' + +from MySQLdb.constants import FIELD_TYPE +_type_conv = { FIELD_TYPE.TINY: int, + FIELD_TYPE.SHORT: int, + FIELD_TYPE.LONG: long, + 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 DBAPITypeObject: + + def __init__(self,*values): + self.values = values + + def __cmp__(self,other): + if other in self.values: + return 0 + if other < self.values: + return 1 + else: + return -1 + +_Set = DBAPITypeObject + +STRING = _Set(FIELD_TYPE.CHAR, FIELD_TYPE.ENUM, FIELD_TYPE.INTERVAL, + FIELD_TYPE.SET, FIELD_TYPE.STRING, FIELD_TYPE.VAR_STRING) +BINARY = _Set(FIELD_TYPE.BLOB, FIELD_TYPE.LONG_BLOB, FIELD_TYPE.MEDIUM_BLOB, + FIELD_TYPE.TINY_BLOB) +NUMBER = _Set(FIELD_TYPE.DECIMAL, FIELD_TYPE.DOUBLE, FIELD_TYPE.FLOAT, + FIELD_TYPE.INT24, FIELD_TYPE.LONG, FIELD_TYPE.LONGLONG, + FIELD_TYPE.TINY, FIELD_TYPE.YEAR) +DATE = _Set(FIELD_TYPE.DATE, FIELD_TYPE.NEWDATE) +TIME = _Set(FIELD_TYPE.TIME) +TIMESTAMP = _Set(FIELD_TYPE.TIMESTAMP, FIELD_TYPE.DATETIME) +ROWID = _Set() + +class Connection: + """This is the connection object for the mySQL database interface.""" + def __init__(self, host, user, passwd, db): + from MySQLdb.constants import CLIENT + 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.__transactional = self.__conn.server_capabilities & CLIENT.TRANSACTIONS + + 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.__transactional: + self.__conn.query("COMMIT") + + def rollback(self): + """Rollback the current transaction.""" + if self.__transactional: + 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() + except MySQL.Error, msg: + raise error, msg + self.__dict__['description'] = self.__res.describe() + 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: + return self.__res.fetch_row(0) + 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: + return self.__res.fetch_row(0,2) + 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]) + diff --git a/MySQLdb/MANIFEST.in b/MySQLdb/MANIFEST.in new file mode 100644 index 0000000..d889af2 --- /dev/null +++ b/MySQLdb/MANIFEST.in @@ -0,0 +1,9 @@ +prune CVS +recursive-include doc * +recursive-include examples * +include _mysql_version.h +include README.MySQLmodule +include license.py +include MANIFEST.in +include MANIFEST +include GPL diff --git a/MySQLdb/MySQLdb/__init__.py b/MySQLdb/MySQLdb/__init__.py new file mode 100644 index 0000000..192a5f7 --- /dev/null +++ b/MySQLdb/MySQLdb/__init__.py @@ -0,0 +1,82 @@ +"""MySQLdb - A DB API v2.0 compatible interface to MySQL. + +This package is a wrapper around _mysql, which mostly implements the +MySQL C API. + +connect() -- connects to server + +See the C API specification and the MySQL documentation for more info +on other items. + +For information on how MySQLdb handles type conversion, see the +MySQLdb.converters module. + +""" + +__author__ = "Andy Dustman " +__revision__ = """$Revision$"""[11:-2] +version_info = ( + 0, + 9, + 0, + "beta", + 1) +if version_info[3] == "final": __version__ = "%d.%d.%d" % version_info[:3] +else: __version__ = "%d.%d.%d%1.1s%d" % version_info[:5] + +import _mysql + +if __version__ != getattr(_mysql, '__version__', None): + raise ImportError, "this is MySQLdb version %s, but _mysql is version %s" %\ + (__version__, _mysql.__version__) + + +threadsafety = 2 +apilevel = "2.0" +paramstyle = "format" + + +class DBAPITypeObject: + + """Helper class for determining column types; required by DB API.""" + + def __init__(self,*values): + self.values = values + + def __cmp__(self,other): + if other in self.values: + return 0 + if other < self.values: + return 1 + else: + return -1 + + +_Set = DBAPITypeObject +from constants import FIELD_TYPE + +STRING = _Set(FIELD_TYPE.CHAR, FIELD_TYPE.ENUM, FIELD_TYPE.INTERVAL, + FIELD_TYPE.SET, FIELD_TYPE.STRING, FIELD_TYPE.VAR_STRING) +BINARY = _Set(FIELD_TYPE.BLOB, FIELD_TYPE.LONG_BLOB, FIELD_TYPE.MEDIUM_BLOB, + FIELD_TYPE.TINY_BLOB) +NUMBER = _Set(FIELD_TYPE.DECIMAL, FIELD_TYPE.DOUBLE, FIELD_TYPE.FLOAT, + FIELD_TYPE.INT24, FIELD_TYPE.LONG, FIELD_TYPE.LONGLONG, + FIELD_TYPE.TINY, FIELD_TYPE.YEAR) +DATE = _Set(FIELD_TYPE.DATE, FIELD_TYPE.NEWDATE) +TIME = _Set(FIELD_TYPE.TIME) +TIMESTAMP = _Set(FIELD_TYPE.TIMESTAMP, FIELD_TYPE.DATETIME) +ROWID = _Set() + +def Binary(x): return str(x) + +from _mysql import * +from connections import Connection + +def Connect(*args, **kwargs): + """Factory function for connections.Connection.""" + return apply(Connection, args, kwargs) + +connect = Connect + +__all__ = ['BINARY', 'Binary', 'Connect', 'Connection', 'DATE', 'DataError', 'DatabaseError', 'Error', 'FIELD_TYPE', 'IntegrityError', 'InterfaceError', 'InternalError', 'MySQLError', 'NULL', 'NUMBER', 'NotSupportedError', 'OperationalError', 'ProgrammingError', 'ROWID', 'STRING', 'TIME', 'TIMESTAMP', 'Warning', 'apilevel', 'connect', 'connections', 'constants', 'cursors', 'debug', 'escape', 'escape_dict', 'escape_sequence', 'escape_string', 'get_client_info', 'paramstyle', 'string_literal', 'threadsafety', 'version_info'] + diff --git a/MySQLdb/MySQLdb/connections.py b/MySQLdb/MySQLdb/connections.py new file mode 100644 index 0000000..dc04054 --- /dev/null +++ b/MySQLdb/MySQLdb/connections.py @@ -0,0 +1,209 @@ +"""MySQLdb Connections Module + +This module implements connections for MySQLdb. Presently there is +only one class: Connection. Others are unlikely. However, you might +want to make your own subclasses. In most cases, you will probably +override Connection.default_cursor with a non-standard Cursor class. + +""" +import cursors +from _mysql_exceptions import NotSupportedError, ProgrammingError + +class Connection: + + """Create a connection to the database. It is strongly recommended + that you only use keyword parameters. "NULL pointer" indicates that + NULL will be passed to mysql_real_connect(); the value in parenthesis + indicates how MySQL interprets the NULL. Consult the MySQL C API + documentation for more information. + + host -- string, host to connect to or NULL pointer (localhost) + user -- string, user to connect as or NULL pointer (your username) + passwd -- string, password to use or NULL pointer (no password) + db -- string, database to use or NULL (no DB selected) + port -- integer, TCP/IP port to connect to or default MySQL port + unix_socket -- string, location of unix_socket to use or use TCP + client_flags -- integer, flags to use or 0 (see MySQL docs) + conv -- conversion dictionary, see MySQLdb.converters + connect_time -- number of seconds to wait before the connection + attempt fails. + compress -- if set, compression is enabled + init_command -- command which is run once the connection is created + read_default_file -- see the MySQL documentation for mysql_options() + read_default_group -- see the MySQL documentation for mysql_options() + cursorclass -- class object, used to create cursors or cursors.Cursor. + This parameter MUST be specified as a keyword parameter. + threads -- boolean, if false threading is disabled, otherwise threads + are enabled by default. (MUST be a keyword parameter.) + + Returns a Connection object. + + There are a number of undocumented, non-standard methods. See the + documentation for the MySQL C API for some hints on what they do. + """ + + default_cursor = cursors.Cursor + + def __init__(self, *args, **kwargs): + from _mysql import connect + from constants import CLIENT + from converters import conversions + import types + kwargs2 = kwargs.copy() + self.__threads = kwargs.get('threads',1) + if kwargs.has_key('threads'): + del kwargs2['threads'] + if not kwargs.has_key('conv'): + kwargs2['conv'] = conversions.copy() + if kwargs.has_key('cursorclass'): + self.cursorclass = kwargs['cursorclass'] + del kwargs2['cursorclass'] + else: + self.cursorclass = self.default_cursor + self._db = apply(connect, args, kwargs2) + self._db.converter[types.StringType] = self._db.string_literal + self._transactional = self._db.server_capabilities & CLIENT.TRANSACTIONS + self._autocommit = 1 + if self.__threads: + # __c_lock: connection lock. Cursors must always obtain the + # connection lock before doing any queries. A blocking + # call is used. If the same thread tries to acquire the + # lock, produce an error. + # + # _t_lock: transaction lock. If operating transactionally, + # Cursors must acquire the transaction lock on the first + # query. The same thread may acquire the lock more than + # once. commit() or rollback() or an error releases this + # lock. + import threading + self.__c_lock = threading.Lock() + self.__c_locker = None + self.__t_lock = threading.Lock() + self.__t_locker = None + + def __del__(self): + self.close() + + def _begin(self): + """Obtain the transaction lock. A thread may try to obtain this + lock multiple times.""" + if not self.__threads: return + import threading + me = threading.currentThread() + if self.__t_locker == me: return + self.__t_lock.acquire() + self.__t_locker = me + + def _end(self): + """Release the transaction lock. If a thread tries to release this + lock when it is not currently locking it, it does nothing.""" + if not self.__threads: return + import threading + me = threading.currentThread() + if self.__t_locker != me: return + self.__t_locker = None + self.__t_lock.release() + + def _acquire(self): + """Acquire the connection. ProgrammingError is raised if the + thread has already acquired the connection.""" + if not self.__threads: return + import threading + me = threading.currentThread() + if self.__c_locker == me: + raise ProgrammingError, "would produce deadlock" + self.__c_lock.acquire() + self.__c_locker = me + + def _release(self): + """Release the connection. If a thread tries to release this + lock when it is not currently locking it, ProgrammingError + is raised (this shouldn't happen).""" + if not self.__threads: return + import threading + me = threading.currentThread() + if self.__c_locker != me: + if not self.__c_locker: return + raise ProgrammingError, "tried to release another %s's lock" % self.__c_locker + self.__c_locker = None + self.__c_lock.release() + + def close(self): + """Close the connection. No further activity possible.""" + self._db.close() + + def autocommit(self, v): + """Turn autocommit on or off.""" + self._db.query("SET AUTOCOMMIT=%d"%v) + self._transactional = not v + self._autocommit = v + + def begin(self): + """Explicitly begin a transaction. Non-standard.""" + self._db.query("BEGIN") + self._transactional = 1 + + def commit(self): + """Commit the current transaction.""" + try: + if self._transactional: + self._db.query("COMMIT") + finally: + self._end() + self._transactional = not self._autocommit + + def rollback(self): + """Rollback the current transaction.""" + try: + if self._transactional: + self._db.query("ROLLBACK") + else: + raise NotSupportedError, "Not supported by server" + finally: + self._end() + self._transactional = not self._autocommit + + def cursor(self, cursorclass=None): + + """Create a cursor on which queries may be performed. The + optional cursorclass parameter is used to create the + Cursor. By default, self.cursorclass=cursors.Cursor is + used.""" + + return (cursorclass or self.cursorclass)(self) + + # Non-portable MySQL-specific stuff + # Methods not included on purpose (use Cursors instead): + # query, store_result, use_result + + def affected_rows(self): return self._db.affected_rows() + def dump_debug_info(self): return self._db.dump_debug_info() + def escape_string(self, s): return self._db.escape_string(s) + def get_host_info(self): return self._db.get_host_info() + def get_proto_info(self): return self._db.get_proto_info() + def get_server_info(self): return self._db.get_server_info() + def info(self): return self._db.info() + def kill(self, p): return self._db.kill(p) + def list_dbs(self): return self._db.list_dbs().fetch_row(0) + def list_fields(self, table): return self._db.list_fields(table).fetch_row(0) + def list_processes(self): return self._db.list_processes().fetch_row(0) + def list_tables(self, db): return self._db.list_tables(db).fetch_row(0) + def field_count(self): return self._db.field_count() + num_fields = field_count # used prior to MySQL-3.22.24 + def ping(self): return self._db.ping() + def row_tell(self): return self._db.row_tell() + def select_db(self, db): return self._db.select_db(db) + def shutdown(self): return self._db.shutdown() + def stat(self): return self._db.stat() + def string_literal(self, s): return self._db.string_literal(s) + def thread_id(self): return self._db.thread_id() + + def _try_feature(self, feature, *args, **kwargs): + try: + return apply(getattr(self._db, feature), args, kwargs) + except AttributeError: + raise NotSupportedError, "not supported by MySQL library" + def character_set_name(self): + return self._try_feature('character_set_name') + def change_user(self, *args, **kwargs): + return apply(self._try_feature, ('change_user',)+args, kwargs) diff --git a/MySQLdb/MySQLdb/constants/CLIENT.py b/MySQLdb/MySQLdb/constants/CLIENT.py new file mode 100644 index 0000000..d9e0b56 --- /dev/null +++ b/MySQLdb/MySQLdb/constants/CLIENT.py @@ -0,0 +1,23 @@ +"""MySQL CLIENT constants + +These constants are used when creating the connection. Use bitwise-OR +(|) to combine options together, and pass them as the client_flags +parameter to MySQLdb.Connection. For more information on these flags, +see the MySQL C API documentation for mysql_real_connect(). + +""" + +LONG_PASSWORD = 1 +FOUND_ROWS = 2 +LONG_FLAG = 4 +CONNECT_WITH_DB = 8 +NO_SCHEMA = 16 +COMPRESS = 32 +ODBC = 64 +LOCAL_FILES = 128 +IGNORE_SPACE = 256 +CHANGE_USER = 512 +INTERACTIVE = 1024 +SSL = 2048 +IGNORE_SIGPIPE = 4096 +TRANSACTIONS = 8192 # mysql_com.h was WRONG prior to 3.23.35 diff --git a/MySQLdb/MySQLdb/constants/CR.py b/MySQLdb/MySQLdb/constants/CR.py new file mode 100644 index 0000000..249dfec --- /dev/null +++ b/MySQLdb/MySQLdb/constants/CR.py @@ -0,0 +1,30 @@ +"""MySQL Connection Errors + +Nearly all of these raise OperationalError. COMMANDS_OUT_OF_SYNC +raises ProgrammingError. + +""" + +MIN_ERROR = 2000 +MAX_ERROR = 2999 +UNKNOWN_ERROR = 2000 +SOCKET_CREATE_ERROR = 2001 +CONNECTION_ERROR = 2002 +CONN_HOST_ERROR = 2003 +IPSOCK_ERROR = 2004 +UNKNOWN_HOST = 2005 +SERVER_GONE_ERROR = 2006 +VERSION_ERROR = 2007 +OUT_OF_MEMORY = 2008 +WRONG_HOST_INFO = 2009 +LOCALHOST_CONNECTION = 2010 +TCP_CONNECTION = 2011 +SERVER_HANDSHAKE_ERR = 2012 +SERVER_LOST = 2013 +COMMANDS_OUT_OF_SYNC = 2014 +NAMEDPIPE_CONNECTION = 2015 +NAMEDPIPEWAIT_ERROR = 2016 +NAMEDPIPEOPEN_ERROR = 2017 +NAMEDPIPESETSTATE_ERROR = 2018 +CANT_READ_CHARSET = 2019 +NET_PACKET_TOO_LARGE = 2020 diff --git a/MySQLdb/MySQLdb/constants/ER.py b/MySQLdb/MySQLdb/constants/ER.py new file mode 100644 index 0000000..262bfce --- /dev/null +++ b/MySQLdb/MySQLdb/constants/ER.py @@ -0,0 +1,211 @@ +"""MySQL ER Constants + +These constants are error codes for the bulk of the error conditions +that may occur. + +""" + +HASHCHK = 1000 +NISAMCHK = 1001 +NO = 1002 +YES = 1003 +CANT_CREATE_FILE = 1004 +CANT_CREATE_TABLE = 1005 +CANT_CREATE_DB = 1006 +DB_CREATE_EXISTS = 1007 +DB_DROP_EXISTS = 1008 +DB_DROP_DELETE = 1009 +DB_DROP_RMDIR = 1010 +CANT_DELETE_FILE = 1011 +CANT_FIND_SYSTEM_REC = 1012 +CANT_GET_STAT = 1013 +CANT_GET_WD = 1014 +CANT_LOCK = 1015 +CANT_OPEN_FILE = 1016 +FILE_NOT_FOUND = 1017 +CANT_READ_DIR = 1018 +CANT_SET_WD = 1019 +CHECKREAD = 1020 +DISK_FULL = 1021 +DUP_KEY = 1022 +ERROR_ON_CLOSE = 1023 +ERROR_ON_READ = 1024 +ERROR_ON_RENAME = 1025 +ERROR_ON_WRITE = 1026 +FILE_USED = 1027 +FILSORT_ABORT = 1028 +FORM_NOT_FOUND = 1029 +GET_ERRNO = 1030 +ILLEGAL_HA = 1031 +KEY_NOT_FOUND = 1032 +NOT_FORM_FILE = 1033 +NOT_KEYFILE = 1034 +OLD_KEYFILE = 1035 +OPEN_AS_READONLY = 1036 +OUTOFMEMORY = 1037 +OUT_OF_SORTMEMORY = 1038 +UNEXPECTED_EOF = 1039 +CON_COUNT_ERROR = 1040 +OUT_OF_RESOURCES = 1041 +BAD_HOST_ERROR = 1042 +HANDSHAKE_ERROR = 1043 +DBACCESS_DENIED_ERROR = 1044 +ACCESS_DENIED_ERROR = 1045 +NO_DB_ERROR = 1046 +UNKNOWN_COM_ERROR = 1047 +BAD_NULL_ERROR = 1048 +BAD_DB_ERROR = 1049 +TABLE_EXISTS_ERROR = 1050 +BAD_TABLE_ERROR = 1051 +NON_UNIQ_ERROR = 1052 +SERVER_SHUTDOWN = 1053 +BAD_FIELD_ERROR = 1054 +WRONG_FIELD_WITH_GROUP = 1055 +WRONG_GROUP_FIELD = 1056 +WRONG_SUM_SELECT = 1057 +WRONG_VALUE_COUNT = 1058 +TOO_LONG_IDENT = 1059 +DUP_FIELDNAME = 1060 +DUP_KEYNAME = 1061 +DUP_ENTRY = 1062 +WRONG_FIELD_SPEC = 1063 +PARSE_ERROR = 1064 +EMPTY_QUERY = 1065 +NONUNIQ_TABLE = 1066 +INVALID_DEFAULT = 1067 +MULTIPLE_PRI_KEY = 1068 +TOO_MANY_KEYS = 1069 +TOO_MANY_KEY_PARTS = 1070 +TOO_LONG_KEY = 1071 +KEY_COLUMN_DOES_NOT_EXITS = 1072 +BLOB_USED_AS_KEY = 1073 +TOO_BIG_FIELDLENGTH = 1074 +WRONG_AUTO_KEY = 1075 +READY = 1076 +NORMAL_SHUTDOWN = 1077 +GOT_SIGNAL = 1078 +SHUTDOWN_COMPLETE = 1079 +FORCING_CLOSE = 1080 +IPSOCK_ERROR = 1081 +NO_SUCH_INDEX = 1082 +WRONG_FIELD_TERMINATORS = 1083 +BLOBS_AND_NO_TERMINATED = 1084 +TEXTFILE_NOT_READABLE = 1085 +FILE_EXISTS_ERROR = 1086 +LOAD_INFO = 1087 +ALTER_INFO = 1088 +WRONG_SUB_KEY = 1089 +CANT_REMOVE_ALL_FIELDS = 1090 +CANT_DROP_FIELD_OR_KEY = 1091 +INSERT_INFO = 1092 +INSERT_TABLE_USED = 1093 +NO_SUCH_THREAD = 1094 +KILL_DENIED_ERROR = 1095 +NO_TABLES_USED = 1096 +TOO_BIG_SET = 1097 +NO_UNIQUE_LOGFILE = 1098 +TABLE_NOT_LOCKED_FOR_WRITE = 1099 +TABLE_NOT_LOCKED = 1100 +BLOB_CANT_HAVE_DEFAULT = 1101 +WRONG_DB_NAME = 1102 +WRONG_TABLE_NAME = 1103 +TOO_BIG_SELECT = 1104 +UNKNOWN_ERROR = 1105 +UNKNOWN_PROCEDURE = 1106 +WRONG_PARAMCOUNT_TO_PROCEDURE = 1107 +WRONG_PARAMETERS_TO_PROCEDURE = 1108 +UNKNOWN_TABLE = 1109 +FIELD_SPECIFIED_TWICE = 1110 +INVALID_GROUP_FUNC_USE = 1111 +UNSUPPORTED_EXTENSION = 1112 +TABLE_MUST_HAVE_COLUMNS = 1113 +RECORD_FILE_FULL = 1114 +UNKNOWN_CHARACTER_SET = 1115 +TOO_MANY_TABLES = 1116 +TOO_MANY_FIELDS = 1117 +TOO_BIG_ROWSIZE = 1118 +STACK_OVERRUN = 1119 +WRONG_OUTER_JOIN = 1120 +NULL_COLUMN_IN_INDEX = 1121 +CANT_FIND_UDF = 1122 +CANT_INITIALIZE_UDF = 1123 +UDF_NO_PATHS = 1124 +UDF_EXISTS = 1125 +CANT_OPEN_LIBRARY = 1126 +CANT_FIND_DL_ENTRY = 1127 +FUNCTION_NOT_DEFINED = 1128 +HOST_IS_BLOCKED = 1129 +HOST_NOT_PRIVILEGED = 1130 +PASSWORD_ANONYMOUS_USER = 1131 +PASSWORD_NOT_ALLOWED = 1132 +PASSWORD_NO_MATCH = 1133 +UPDATE_INFO = 1134 +CANT_CREATE_THREAD = 1135 +WRONG_VALUE_COUNT_ON_ROW = 1136 +CANT_REOPEN_TABLE = 1137 +INVALID_USE_OF_NULL = 1138 +REGEXP_ERROR = 1139 +MIX_OF_GROUP_FUNC_AND_FIELDS = 1140 +NONEXISTING_GRANT = 1141 +TABLEACCESS_DENIED_ERROR = 1142 +COLUMNACCESS_DENIED_ERROR = 1143 +ILLEGAL_GRANT_FOR_TABLE = 1144 +GRANT_WRONG_HOST_OR_USER = 1145 +NO_SUCH_TABLE = 1146 +NONEXISTING_TABLE_GRANT = 1147 +NOT_ALLOWED_COMMAND = 1148 +SYNTAX_ERROR = 1149 +DELAYED_CANT_CHANGE_LOCK = 1150 +TOO_MANY_DELAYED_THREADS = 1151 +ABORTING_CONNECTION = 1152 +NET_PACKET_TOO_LARGE = 1153 +NET_READ_ERROR_FROM_PIPE = 1154 +NET_FCNTL_ERROR = 1155 +NET_PACKETS_OUT_OF_ORDER = 1156 +NET_UNCOMPRESS_ERROR = 1157 +NET_READ_ERROR = 1158 +NET_READ_INTERRUPTED = 1159 +NET_ERROR_ON_WRITE = 1160 +NET_WRITE_INTERRUPTED = 1161 +TOO_LONG_STRING = 1162 +TABLE_CANT_HANDLE_BLOB = 1163 +TABLE_CANT_HANDLE_AUTO_INCREMENT = 1164 +DELAYED_INSERT_TABLE_LOCKED = 1165 +WRONG_COLUMN_NAME = 1166 +WRONG_KEY_COLUMN = 1167 +WRONG_MRG_TABLE = 1168 +DUP_UNIQUE = 1169 +BLOB_KEY_WITHOUT_LENGTH = 1170 +PRIMARY_CANT_HAVE_NULL = 1171 +TOO_MANY_ROWS = 1172 +REQUIRES_PRIMARY_KEY = 1173 +NO_RAID_COMPILED = 1174 +UPDATE_WITHOUT_KEY_IN_SAFE_MODE = 1175 +KEY_DOES_NOT_EXITS = 1176 +CHECK_NO_SUCH_TABLE = 1177 +CHECK_NOT_IMPLEMENTED = 1178 +CANT_DO_THIS_DURING_AN_TRANSACTION = 1179 +ERROR_DURING_COMMIT = 1180 +ERROR_DURING_ROLLBACK = 1181 +ERROR_DURING_FLUSH_LOGS = 1182 +ERROR_DURING_CHECKPOINT = 1183 +NEW_ABORTING_CONNECTION = 1184 +DUMP_NOT_IMPLEMENTED = 1185 +FLUSH_MASTER_BINLOG_CLOSED = 1186 +INDEX_REBUILD = 1187 +MASTER = 1188 +MASTER_NET_READ = 1189 +MASTER_NET_WRITE = 1190 +FT_MATCHING_KEY_NOT_FOUND = 1191 +LOCK_OR_ACTIVE_TRANSACTION = 1192 +UNKNOWN_SYSTEM_VARIABLE = 1193 +CRASHED_ON_USAGE = 1194 +CRASHED_ON_REPAIR = 1195 +WARNING_NOT_COMPLETE_ROLLBACK = 1196 +TRANS_CACHE_FULL = 1197 +SLAVE_MUST_STOP = 1198 +SLAVE_NOT_RUNNING = 1199 +BAD_SLAVE = 1200 +MASTER_INFO = 1201 +SLAVE_THREAD = 1202 +ERROR_MESSAGES = 203 diff --git a/MySQLdb/MySQLdb/constants/FIELD_TYPE.py b/MySQLdb/MySQLdb/constants/FIELD_TYPE.py new file mode 100644 index 0000000..c010bae --- /dev/null +++ b/MySQLdb/MySQLdb/constants/FIELD_TYPE.py @@ -0,0 +1,33 @@ +"""MySQL FIELD_TYPE Constants + +These constants represent the various column (field) types that are +supported by MySQL. + +""" + +DECIMAL = 0 +TINY = 1 +SHORT = 2 +LONG = 3 +FLOAT = 4 +DOUBLE = 5 +NULL = 6 +TIMESTAMP = 7 +LONGLONG = 8 +INT24 = 9 +DATE = 10 +TIME = 11 +DATETIME = 12 +YEAR = 13 +NEWDATE = 14 +ENUM = 247 +SET = 248 +TINY_BLOB = 249 +MEDIUM_BLOB = 250 +LONG_BLOB = 251 +BLOB = 252 +VAR_STRING = 253 +STRING = 254 + +CHAR = TINY +INTERVAL = ENUM diff --git a/MySQLdb/MySQLdb/constants/FLAG.py b/MySQLdb/MySQLdb/constants/FLAG.py new file mode 100644 index 0000000..00e6c7c --- /dev/null +++ b/MySQLdb/MySQLdb/constants/FLAG.py @@ -0,0 +1,23 @@ +"""MySQL FLAG Constants + +These flags are used along with the FIELD_TYPE to indicate various +properties of columns in a result set. + +""" + +NOT_NULL = 1 +PRI_KEY = 2 +UNIQUE_KEY = 4 +MULTIPLE_KEY = 8 +BLOB = 16 +UNSIGNED = 32 +ZEROFILL = 64 +BINARY = 128 +ENUM = 256 +AUTO_INCREMENT = 512 +TIMESTAMP = 1024 +SET = 2048 +NUM = 32768 +PART_KEY = 16384 +GROUP = 32768 +UNIQUE = 65536 diff --git a/MySQLdb/MySQLdb/constants/REFRESH.py b/MySQLdb/MySQLdb/constants/REFRESH.py new file mode 100644 index 0000000..4a08b94 --- /dev/null +++ b/MySQLdb/MySQLdb/constants/REFRESH.py @@ -0,0 +1,17 @@ +"""MySQL REFRESH Constants + +These constants seem to mostly deal with things internal to the +MySQL server. Forget you saw this. + +""" + +GRANT = 1 +LOG = 2 +TABLES = 4 +HOSTS = 8 +STATUS = 16 +THREADS = 32 +SLAVE = 64 +MASTER = 128 +READ_LOCK = 16384 +FAST = 32768 diff --git a/MySQLdb/MySQLdb/constants/__init__.py b/MySQLdb/MySQLdb/constants/__init__.py new file mode 100644 index 0000000..3da4a0e --- /dev/null +++ b/MySQLdb/MySQLdb/constants/__init__.py @@ -0,0 +1 @@ +__all__ = ['CR', 'FIELD_TYPE','CLIENT','REFRESH','ER','FLAG'] diff --git a/MySQLdb/MySQLdb/converters.py b/MySQLdb/MySQLdb/converters.py new file mode 100644 index 0000000..8f8a044 --- /dev/null +++ b/MySQLdb/MySQLdb/converters.py @@ -0,0 +1,187 @@ +"""MySQLdb type conversion module + +This module handles all the type conversions for MySQL. If the default +type conversions aren't what you need, you can make your own. The +dictionary conversions maps some kind of type to a conversion function +which returns the corresponding value: + +Key: FIELD_TYPE.* (from MySQLdb.constants) +Conversion function: + Arguments: string + Returns: Python object + +Key: Python type object (from types) or class +Conversion function: + Arguments: Python object of indicated type or class AND + conversion dictionary + Returns: SQL literal value + Notes: Most conversion functions can ignore the dictionary, but + it is a required parameter. It is necessary for converting + things like sequences and instances. + +Don't modify conversions if you can avoid it. Instead, make copies +(with the copy() method), modify the copies, and then pass them to +MySQL.connect(). + +""" + +from _mysql import string_literal, escape_sequence, escape_dict, escape, NULL +from constants import FIELD_TYPE +from time import localtime, strftime +import types + +def Thing2Str(s, d): + """Convert something into a string via str().""" + return str(s) + +# Python 1.5.2 compatibility hack +if str(0L)[-1]=='L': + def Long2Int(l, d): + """Convert a long integer to a string, chopping the L.""" + return str(l)[:-1] +else: + Long2Int = Thing2Str + +def None2NULL(o, d): + """Convert None to NULL.""" + return NULL # duh + +def Thing2Literal(o, d): + + """Convert something into a SQL string literal. If using + MySQL-3.23 or newer, string_literal() is a method of the + _mysql.MYSQL object, and this function will be overridden with + that method when the connection is created.""" + + return string_literal(o, d) + +def Instance2Str(o, d): + + """Convert an Instance to a string representation. If the + __str__() method produces acceptable output, then you don't need + to add the class to conversions; it will be handled by the default + converter. If the exact class is not found in d, it will use the + first class it can find for which o is an instance.""" + + if d.has_key(o.__class__): return + d[o.__class__](o, d) + cl = filter(lambda x,o=o: + type(x)==types.ClassType and isinstance(o,x), d.keys()) + if not cl: + return d[types.StringType](o,d) + d[o.__class__] = d[cl[0]] + return d[cl[0]](o, d) + +conversions = { + types.IntType: Thing2Str, + types.LongType: Long2Int, + types.FloatType: Thing2Str, + types.NoneType: None2NULL, + types.TupleType: escape_sequence, + types.ListType: escape_sequence, + types.DictType: escape_dict, + types.InstanceType: Instance2Str, + types.StringType: Thing2Literal, # default + FIELD_TYPE.TINY: int, + FIELD_TYPE.SHORT: int, + FIELD_TYPE.LONG: long, + FIELD_TYPE.FLOAT: float, + FIELD_TYPE.DOUBLE: float, + FIELD_TYPE.LONGLONG: long, + FIELD_TYPE.INT24: int, + FIELD_TYPE.YEAR: int + } + +try: + try: + # new packaging + from mx.DateTime import Date, Time, Timestamp, ISO, \ + DateTimeType, DateTimeDeltaType + except ImportError: + # old packaging, deprecated + from DateTime import Date, Time, Timestamp, ISO, \ + DateTimeType, DateTimeDeltaType + + def DateFromTicks(ticks): + """Convert UNIX ticks into a mx.DateTime.Date.""" + return apply(Date, localtime(ticks)[:3]) + + def TimeFromTicks(ticks): + """Convert UNIX ticks into a mx.DateTime.Time.""" + return apply(Time, localtime(ticks)[3:6]) + + def TimestampFromTicks(ticks): + """Convert UNIX ticks into a mx.DateTime.Timestamp.""" + return apply(Timestamp, localtime(ticks)[:6]) + + def format_DATE(d): + """Format a DateTime object as an ISO date.""" + return d.strftime("%Y-%m-%d") + + def format_TIME(d): + """Format a DateTime object as a time value.""" + return d.strftime("%H:%M:%S") + + def format_TIMESTAMP(d): + """Format a DateTime object as an ISO timestamp.""" + return d.strftime("%Y-%m-%d %H:%M:%S") + + def mysql_timestamp_converter(s): + """Convert a MySQL TIMESTAMP to a mx.DateTime.Timestamp.""" + parts = map(int, filter(None, (s[:4],s[4:6],s[6:8], + s[8:10],s[10:12],s[12:14]))) + try: return apply(Timestamp, tuple(parts)) + except: return None + + def DateTime_or_None(s): + try: return ISO.ParseDateTime(s) + except: return None + + def TimeDelta_or_None(s): + try: return ISO.ParseTimeDelta(s) + except: return None + + def Date_or_None(s): + try: return ISO.ParseDate(s) + except: return None + + conversions[FIELD_TYPE.TIMESTAMP] = mysql_timestamp_converter + conversions[FIELD_TYPE.DATETIME] = DateTime_or_None + conversions[FIELD_TYPE.TIME] = TimeDelta_or_None + conversions[FIELD_TYPE.DATE] = Date_or_None + + def DateTime2literal(d, c): + """Format a DateTime object as an ISO timestamp.""" + return escape(format_TIMESTAMP(d),c) + + def DateTimeDelta2literal(d, c): + """Format a DateTimeDelta object as a time.""" + return escape(format_TIME(d),c) + + conversions[DateTimeType] = DateTime2literal + conversions[DateTimeDeltaType] = DateTimeDelta2literal + +except ImportError: + # no DateTime? We'll muddle through somehow. + + def DateFromTicks(ticks): + """Convert UNIX ticks to ISO date format.""" + return strftime("%Y-%m-%d", localtime(ticks)) + + def TimeFromTicks(ticks): + """Convert UNIX ticks to time format.""" + return strftime("%H:%M:%S", localtime(ticks)) + + def TimestampFromTicks(ticks): + """Convert UNIX ticks to ISO timestamp format.""" + return strftime("%Y-%m-%d %H:%M:%S", localtime(ticks)) + + def format_DATE(d): + """Format a date as a date (does nothing, you don't have mx.DateTime).""" + return d + + format_TIME = format_TIMESTAMP = format_DATE + +__all__ = [ 'conversions', 'DateFromTicks', 'TimeFromTicks', + 'TimestampFromTicks', 'format_DATE', 'format_TIME', + 'format_TIMESTAMP' ] diff --git a/MySQLdb/MySQLdb/cursors.py b/MySQLdb/MySQLdb/cursors.py new file mode 100644 index 0000000..f250190 --- /dev/null +++ b/MySQLdb/MySQLdb/cursors.py @@ -0,0 +1,390 @@ +"""MySQLdb Cursors + +This module implements Cursors of various types for MySQLdb. By +default, MySQLdb uses the Cursor class. + +""" + +import re +insert_values = re.compile(r'values\s(\(.+\))', re.IGNORECASE) +from _mysql import escape, ProgrammingError, Warning + +class BaseCursor: + + """A base for Cursor classes. Useful attributes: + + description -- DB API 7-tuple describing columns in last query + arraysize -- default number of rows fetchmany() will fetch + + See the MySQL docs for more information.""" + + def __init__(self, connection): + self.__conn = connection + self.description = None + self.rowcount = -1 + self.arraysize = 100 + self._executed = None + self._transaction = 0 + self.__c_locked = 0 + + def __del__(self): + self.close() + + def close(self): + """Close the cursor. No further queries will be possible.""" + if not self.__conn: return + self._end() + self.__conn = None + self._executed = None + self._transaction = None + + def _check_executed(self): + if not self._executed: + raise ProgrammingError, "execute() first" + + def setinputsizes(self, *args): + """Does nothing, required by DB API.""" + + def setoutputsizes(self, *args): + """Does nothing, required by DB API.""" + + def _get_db(self): + if not self.__conn: + raise ProgrammingError, "cursor closed" + return self.__conn._db + + def execute(self, query, args=None): + + """Execute a query. + + query -- string, query to execute on server + args -- sequence or mapping, parameters to use with query. + returns long integer rows affected, if any""" + + from types import ListType, TupleType + qc = self._get_db().converter + if not args: + r = self._query(query) + elif type(args) is ListType and type(args[0]) is TupleType: + r = self.executemany(query, args) # deprecated + else: + try: + r = self._query(query % escape(args, qc)) + except TypeError, m: + if m.args[0] in ("not enough arguments for format string", + "not all arguments converted"): + raise ProgrammingError, m.args[0] + else: + raise + self._executed = query + return r + + def executemany(self, query, args): + + """Execute a multi-row query. + + query -- string, query to execute on server + args -- sequence of sequences or mappings, parameters to use with + query. The query must contain the clause "values ( ... )". + The parenthetical portion will be repeated once for each + item in the sequence. + returns long integer rows affected, if any + + This method performs multiple-row inserts and similar queries.""" + + from string import join + qc = self._get_db().converter + m = insert_values.search(query) + if not m: raise ProgrammingError, "can't find values" + p = m.start(1) + qv = query[p:] + qargs = escape(args, qc) + try: + q = [ query % qargs[0] ] + for a in qargs[1:]: q.append( qv % a ) + except TypeError, msg: + if msg.args[0] in ("not enough arguments for format string", + "not all arguments converted"): + raise ProgrammingError, (0, msg.args[0]) + else: + raise + r = self._query(join(q,',\n')) + self._executed = query + return r + + def __do_query(self, q): + from string import split, atoi + db = self._get_db() + if self._transaction: self._begin() + try: + db.query(q) + self._result = self._get_result() + self.rowcount = db.affected_rows() + self.description = self._result and self._result.describe() or None + self._insert_id = db.insert_id() + self._info = db.info() + self._check_for_warnings() + except: + self._end() + raise + return self.rowcount + + def _check_for_warnings(self): pass + + _query = __do_query + + def info(self): + """Return some information about the last query (db.info())""" + self._check_executed() + return self._info + + def insert_id(self): + """Return the last inserted ID on an AUTO_INCREMENT columns.""" + self._check_executed() + return self._insert_id + + def nextset(self): + """Does nothing. Required by DB API.""" + self._check_executed() + return None + + def _fetch_row(self, size=1): + return self._result.fetch_row(size, self._fetch_type) + + def _begin(self): + self.__conn._begin() + self._transaction = 1 + + def _end(self): + self._transaction = 0 + self.__conn._end() + + def _acquire(self): + if self.__c_locked: return + self.__conn._acquire() + self.__c_locked = 1 + + def _release(self): + if not self.__conn: return + self.__conn._release() + self.__c_locked = 0 + + def _is_transactional(self): + return self.__conn._transactional + + +class CursorWarningMixIn: + + """This is a MixIn class that provides the capability of raising + the Warning exception when something went slightly wrong with your + query.""" + + def _check_for_warnings(self): + from string import atoi, split + if self._info: + warnings = atoi(split(self._info)[-1]) + if warnings: + raise Warning, self._info + + +class CursorStoreResultMixIn: + + """This is a MixIn class which causes the entire result set to be + stored on the client side, i.e. it uses mysql_store_result(). If the + result set can be very large, consider adding a LIMIT clause to your + query, or using CursorUseResultMixIn instead.""" + + def __init__(self, connection): + BaseCursor.__init__(self, connection) + self._acquire() + + def _get_result(self): return self._get_db().store_result() + + def close(self): + """Close the cursor. Further queries will not be possible.""" + self._rows = () + BaseCursor.close(self) + + def _query(self, q): + self._acquire() + try: + rowcount = self._BaseCursor__do_query(q) + self._rows = self._result and self._fetch_row(0) or () + self._pos = 0 + del self._result + if not self._is_transactional: self._end() + return rowcount + finally: + self._release() + + def fetchone(self): + """Fetches a single row from the cursor.""" + self._check_executed() + if self._pos >= len(self._rows): return None + result = self._rows[self._pos] + self._pos = self._pos+1 + return result + + def fetchmany(self, size=None): + """Fetch up to size rows from the cursor. Result set may be smaller + than size. If size is not defined, cursor.arraysize is used.""" + self._check_executed() + end = self._pos + size or self.arraysize + result = self._rows[self._pos:end] + self._pos = end + return result + + def fetchall(self): + """Fetchs all available rows from the cursor.""" + self._check_executed() + result = self._pos and self._rows[self._pos:] or self._rows + self._pos = len(self._rows) + return result + + def seek(self, row, whence=0): + """seek to a given row of the result set analogously to file.seek(). + This is non-standard extension.""" + self._check_executed() + if whence == 0: + self._pos = row + elif whence == 1: + self._pos = self._pos + row + elif whence == 2: + self._pos = len(self._rows) + row + + def tell(self): + """Return the current position in the result set analogously to + file.tell(). This is a non-standard extension.""" + self._check_executed() + return self._pos + + +class CursorUseResultMixIn: + + """This is a MixIn class which causes the result set to be stored + in the server and sent row-by-row to client side, i.e. it uses + mysql_use_result(). You MUST retrieve the entire result set and + close() the cursor before additional queries can be peformed on + the connection.""" + + def close(self): + """Close the cursor. No further queries can be executed.""" + self._release() + self._result = None + BaseCursor.close(self) + + def _get_result(self): return self._get_db().use_result() + + def fetchone(self): + """Fetches a single row from the cursor.""" + self._check_executed() + r = self._fetch_row(1) + return r and r[0] or None + + def fetchmany(self, size=None): + """Fetch up to size rows from the cursor. Result set may be smaller + than size. If size is not defined, cursor.arraysize is used.""" + self._check_executed() + return self._fetch_row(size or self.arraysize) + + def fetchall(self): + """Fetchs all available rows from the cursor.""" + self._check_open() + return self._fetch_row(0) + + +class CursorTupleRowsMixIn: + + """This is a MixIn class that causes all rows to be returned as tuples, + which is the standard form required by DB API.""" + + _fetch_type = 0 + + +class CursorDictRowsMixIn: + + """This is a MixIn class that causes all rows to be returned as + dictionaries. This is a non-standard feature.""" + + _fetch_type = 1 + + def fetchoneDict(self): + """Fetch a single row as a dictionary. Deprecated: + Use fetchone() instead.""" + return self.fetchone() + + def fetchmanyDict(self, size=None): + """Fetch several rows as a list of dictionaries. Deprecated: + Use fetchmany() instead.""" + return self.fetchmany(size) + + def fetchallDict(self): + """Fetch all available rows as a list of dictionaries. Deprecated: + Use fetchall() instead.""" + return self.fetchall() + + +class CursorOldDictRowsMixIn(CursorDictRowsMixIn): + + """This is a MixIn class that returns rows as dictionaries with + the same key convention as the old Mysqldb (MySQLmodule). Don't + use this.""" + + _fetch_type = 2 + + +class CursorNW(CursorStoreResultMixIn, CursorTupleRowsMixIn, + BaseCursor): + + """This is a basic Cursor class that returns rows as tuples and + stores the result set in the client. Warnings are not raised.""" + + +class Cursor(CursorWarningMixIn, CursorNW): + + """This is the standard Cursor class that returns rows as tuples + and stores the result set in the client. Warnings are raised as + necessary.""" + + +class DictCursorNW(CursorStoreResultMixIn, CursorDictRowsMixIn, + BaseCursor): + + """This is a Cursor class that returns rows as dictionaries and + stores the result set in the client. Warnings are not raised.""" + + +class DictCursor(CursorWarningMixIn, DictCursorNW): + + """This is a Cursor class that returns rows as dictionaries and + stores the result set in the client. Warnings are raised as + necessary.""" + + +class SSCursorNW(CursorUseResultMixIn, CursorTupleRowsMixIn, + BaseCursor): + + """This is a basic Cursor class that returns rows as tuples and + stores the result set in the server. Warnings are not raised.""" + + +class SSCursor(CursorWarningMixIn, SSCursorNW): + + """This is a Cursor class that returns rows as tuples and stores + the result set in the server. Warnings are raised as necessary.""" + + +class SSDictCursorNW(CursorUseResultMixIn, CursorDictRowsMixIn, + BaseCursor): + + """This is a Cursor class that returns rows as dictionaries and + stores the result set in the server. Warnings are not raised.""" + + +class SSDictCursor(CursorWarningMixIn, SSDictCursorNW): + + """This is a Cursor class that returns rows as dictionaries and + stores the result set in the server. Warnings are raised as + necessary.""" + + diff --git a/MySQLdb/PKG-INFO b/MySQLdb/PKG-INFO new file mode 100644 index 0000000..ae6cb87 --- /dev/null +++ b/MySQLdb/PKG-INFO @@ -0,0 +1,26 @@ +Metadata-Version: 1.0 +Name: MySQL-python +Version: 0.9.0a3 +Summary: An interface to MySQL +Home-page: http://dustman.net/andy/python/MySQLdb +Author: Andy Dustman +Author-email: andy@dustman.net +License: UNKNOWN +Description: Python interface to MySQL-3.23 + + MySQLdb is an interface to the popular MySQL database server for Python. + The design goals are: + + - Compliance with Python database API version 2.0 + - Thread-safety + - Thread-friendliness (threads will not block each other) + - Compatibility with MySQL-3.23 and later + + This module should be mostly compatible with an older interface + written by Joe Skinner and others. However, the older version is + a) not thread-friendly, b) written for MySQL 3.21, c) apparently + not actively maintained. No code from that version is used in + MySQLdb. MySQLdb is free software. + + +Platform: UNKNOWN diff --git a/MySQLdb/README b/MySQLdb/README new file mode 100644 index 0000000..8e39782 --- /dev/null +++ b/MySQLdb/README @@ -0,0 +1,57 @@ +Prerequisites: + +Python 1.5.2 or higher +-- http://www.pythonlabs.com/products/python2.0/download_python2.0.html +-- http://www.python.org/1.6/ +-- http://www.python.org/1.5/ +-- Versions lower than 1.5.2 WON'T WORK. +-- If you have Red Hat Linux or a similar packaging system, make sure + you have the Python development headers and libraries (python-devel). + +Distutils 1.0 or higher +-- comes with Python 1.6 and 2.0 +-- http://www.python.org/sigs/distutils-sig/download.html + +MySQL 3.22.19 or higher +-- http://www.mysql.com/downloads/ +-- Versions lower than 3.22 definitely WON'T WORK. +-- Versions lower than 3.22.19 might not work. +-- The current (recommended) 3.22 release is 3.22.32. +-- MySQL-3.23 is beta at present but supported. +-- If you have Red Hat Linux or a similar packaging system, make sure + you have the MySQL development headers and libraries (MySQL-devel). + +First thing to do is edit setup.py. There are some variables towards the +beginning that tell it where your MySQL include files and libraries are. +The values are right for MySQL's standard Red Hat Linux (6.2) RPMs. If +you have another platform, you'll have to figure out the right values +yourself. + +Note that recent binary distributions include two sets of client +libraries: mysqlclient and mysqlclient_r. The latter are the +"thread-safe" libraries, so use those if you can, and if threading is +important to you. + +If you have the dynamic client libraries (on Linux, .so vs. .a), those +will be used by default. If they are not on your standard loader path, +you will have to set or adjust the LD_LIBRARY_PATH environment variable +(on Linux) or whatever your platform requires. Otherwise, you can adjust +setup.py to link against the static library. + +Finally, putting it together: + +$ python setup.py build +# python setup.py install + +TIP: If you are using a binary package of Zope, you need run setup.py +with Zope's python executable. Otherwise, Zope (ZMySQLDA) will not +be able to find _mysql. + +If you prefer RPMs, you can use the bdist_rpm command with setup.py. + +Thanks go to Brian Fordham for cooking up an early version of setup.py. + +License: GPL or the original license based on Python 1.5.2's license. + +Andy Dustman +2000-11-30 diff --git a/MySQLdb/_mysql.c b/MySQLdb/_mysql.c new file mode 100644 index 0000000..7117f8a --- /dev/null +++ b/MySQLdb/_mysql.c @@ -0,0 +1,1613 @@ +#include "_mysql_version.h" +/* +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. Alternatively, you may use the original license +reproduced below. + +Copyright 1999 by Comstar.net, Inc., Atlanta, GA, US. + + All Rights Reserved + +Permission to use, copy, modify, and distribute this software and its +documentation for any purpose and without fee is hereby granted, +provided that the above copyright notice appear in all copies and that +both that copyright notice and this permission notice appear in +supporting documentation, and that the name of Comstar.net, Inc. +or COMSTAR not be used in advertising or publicity pertaining to +distribution of the software without specific, written prior permission. + +COMSTAR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, +INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO +EVENT SHALL COMSTAR BE LIABLE FOR ANY SPECIAL, INDIRECT OR +CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF +USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR +OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR +PERFORMANCE OF THIS SOFTWARE. +*/ + +#include "Python.h" + +#ifdef MS_WIN32 +#include +#ifndef uint +#define uint unsigned int +#endif +#endif /* MS_WIN32 */ + +#include "structmember.h" +#include "mysql.h" +#include "mysqld_error.h" +#include "errmsg.h" + +static PyObject *_mysql_MySQLError; +static PyObject *_mysql_Warning; +static PyObject *_mysql_Error; +static PyObject *_mysql_DatabaseError; +static PyObject *_mysql_InterfaceError; +static PyObject *_mysql_DataError; +static PyObject *_mysql_OperationalError; +static PyObject *_mysql_IntegrityError; +static PyObject *_mysql_InternalError; +static PyObject *_mysql_ProgrammingError; +static PyObject *_mysql_NotSupportedError; + +typedef struct { + PyObject_HEAD + MYSQL connection; + int open; + PyObject *converter; +} _mysql_ConnectionObject; + +#define check_connection(c) if (!(c->open)) _mysql_Exception(c) +#define result_connection(r) ((_mysql_ConnectionObject *)r->conn) +#define check_result_connection(r) if (!(result_connection(r)->open)) _mysql_Exception(result_connection(r)) + +extern PyTypeObject _mysql_ConnectionObject_Type; + +typedef struct { + PyObject_HEAD + PyObject *conn; + MYSQL_RES *result; + int nfields; + int use; + PyObject *converter; +} _mysql_ResultObject; + +extern PyTypeObject _mysql_ResultObject_Type; + +PyObject * +_mysql_Exception(_mysql_ConnectionObject *c) +{ + PyObject *t, *e; + int merr; + + if (!(t = PyTuple_New(2))) return NULL; + if (!(c->open)) { + e = _mysql_InternalError; + PyTuple_SET_ITEM(t, 0, PyInt_FromLong(-1L)); + PyTuple_SET_ITEM(t, 1, PyString_FromString("connection is closed")); + PyErr_SetObject(e, t); + Py_DECREF(t); + return NULL; + } + merr = mysql_errno(&(c->connection)); + if (!merr) + e = _mysql_InterfaceError; + else if (merr > CR_MAX_ERROR) { + PyTuple_SET_ITEM(t, 0, PyInt_FromLong(-1L)); + PyTuple_SET_ITEM(t, 1, PyString_FromString("error totally whack")); + PyErr_SetObject(_mysql_InterfaceError, t); + Py_DECREF(t); + return NULL; + } + else switch (merr) { + case CR_COMMANDS_OUT_OF_SYNC: + case ER_DB_CREATE_EXISTS: + case ER_SYNTAX_ERROR: + case ER_NO_SUCH_TABLE: + case ER_WRONG_DB_NAME: + case ER_WRONG_TABLE_NAME: + case ER_FIELD_SPECIFIED_TWICE: + case ER_INVALID_GROUP_FUNC_USE: + case ER_UNSUPPORTED_EXTENSION: + case ER_TABLE_MUST_HAVE_COLUMNS: +#ifdef ER_CANT_DO_THIS_DURING_AN_TRANSACTION + case ER_CANT_DO_THIS_DURING_AN_TRANSACTION: +#endif + e = _mysql_ProgrammingError; + break; + case ER_DUP_ENTRY: + case ER_DUP_UNIQUE: + case ER_PRIMARY_CANT_HAVE_NULL: + e = _mysql_IntegrityError; + break; +#ifdef ER_WARNING_NOT_COMPLETE_ROLLBACK + case ER_WARNING_NOT_COMPLETE_ROLLBACK: + e = _mysql_NotSupportedError; + break; +#endif + default: + if (merr < 1000) + e = _mysql_InternalError; + else + e = _mysql_OperationalError; + break; + } + PyTuple_SET_ITEM(t, 0, PyInt_FromLong((long)merr)); + PyTuple_SET_ITEM(t, 1, PyString_FromString(mysql_error(&(c->connection)))); + PyErr_SetObject(e, t); + Py_DECREF(t); + return NULL; +} + + +static _mysql_ResultObject* +_mysql_ResultObject_New( + _mysql_ConnectionObject *conn, + MYSQL_RES *result, + int use, + PyObject *conv) +{ + int n, i; + MYSQL_FIELD *fields; + _mysql_ResultObject *r; + if (!(r = PyObject_NEW(_mysql_ResultObject, &_mysql_ResultObject_Type))) + return NULL; + r->conn = (PyObject *) conn; + r->converter = NULL; + r->use = use; + Py_INCREF(conn); + Py_INCREF(conv); + r->result = result; + n = mysql_num_fields(result); + r->nfields = n; + if (n) { + if (!(r->converter = PyTuple_New(n))) { + Py_DECREF(conv); + Py_DECREF(conn); + return NULL; + } + fields = mysql_fetch_fields(result); + for (i=0; iconverter, i, fun); + } + } + Py_DECREF(conv); + return r; +} + +static char _mysql_connect__doc__[] = +"connect() -- returns a MYSQL connection object. Exclusive use of\n\ + keyword parameters strongly recommended. Consult the + MySQL C API documentation for more details.\n\ +\n\ +host -- string, host to connect to or NULL pointer (localhost)\n\ +user -- string, user to connect as or NULL (your username)\n\ +passwd -- string, password to use or NULL (no password)\n\ +db -- string, database to use or NULL (no DB selected)\n\ +port -- integer, TCP/IP port to connect to or default MySQL port\n\ +unix_socket -- string, location of unix_socket to use or use TCP\n\ +client_flags -- integer, flags to use or 0 (see MySQL docs)\n\ +conv -- dictionary, maps MySQL FIELD_TYPE.* to Python functions which\n\ + convert a string to the appropriate Python type\n\ +connect_time -- number of seconds to wait before the connection\n\ + attempt fails.\n\ +compress -- if set, compression is enabled\n\ +init_command -- command which is run once the connection is created\n\ +read_default_file -- see the MySQL documentation for mysql_options()\n\ +read_default_group -- see the MySQL documentation for mysql_options()\n\ +"; + +static PyObject * +_mysql_connect( + PyObject *self, + PyObject *args, + PyObject *kwargs) +{ + MYSQL *conn; + PyObject *conv = NULL; + char *host = NULL, *user = NULL, *passwd = NULL, + *db = NULL, *unix_socket = NULL; + uint port = MYSQL_PORT; + uint client_flag = 0; + static char *kwlist[] = { "host", "user", "passwd", "db", "port", + "unix_socket", "conv", + "connect_timeout", "compress", + "named_pipe", "init_command", + "read_default_file", "read_default_group", + NULL } ; + int connect_timeout = 0; + int compress = -1, named_pipe = -1; + char *init_command=NULL, + *read_default_file=NULL, + *read_default_group=NULL; + _mysql_ConnectionObject *c = PyObject_NEW(_mysql_ConnectionObject, + &_mysql_ConnectionObject_Type); + if (c == NULL) return NULL; + if (!PyArg_ParseTupleAndKeywords(args, kwargs, "|ssssisOiiisss:connect", + kwlist, + &host, &user, &passwd, &db, + &port, &unix_socket, &conv, + &connect_timeout, + &compress, &named_pipe, + &init_command, &read_default_file, + &read_default_group)) + return NULL; + if (conv) { + c->converter = conv; + Py_INCREF(conv); + } else { + if (!(c->converter = PyDict_New())) { + Py_DECREF(c); + return NULL; + } + } + Py_BEGIN_ALLOW_THREADS ; + conn = mysql_init(&(c->connection)); + if (connect_timeout) { + unsigned int timeout = connect_timeout; + mysql_options(&(c->connection), MYSQL_OPT_CONNECT_TIMEOUT, + (char *)&timeout); + } + if (compress != -1) { + mysql_options(&(c->connection), MYSQL_OPT_COMPRESS, 0); + client_flag |= CLIENT_COMPRESS; + } + if (named_pipe != -1) + mysql_options(&(c->connection), MYSQL_OPT_NAMED_PIPE, 0); + if (init_command != NULL) + mysql_options(&(c->connection), MYSQL_INIT_COMMAND, init_command); + if (read_default_file != NULL) + mysql_options(&(c->connection), MYSQL_READ_DEFAULT_FILE, read_default_file); + if (read_default_group != NULL) + mysql_options(&(c->connection), MYSQL_READ_DEFAULT_GROUP, read_default_group); + conn = mysql_real_connect(&(c->connection), host, user, passwd, db, + port, unix_socket, client_flag); + Py_END_ALLOW_THREADS ; + c->open = 1; + if (!conn) { + _mysql_Exception(c); + c->open = 0; + Py_DECREF(c); + return NULL; + } + return (PyObject *) c; +} + +static PyObject * +_mysql_ConnectionObject_close( + _mysql_ConnectionObject *self, + PyObject *args) +{ + if (!PyArg_NoArgs(args)) return NULL; + if (self->open) { + Py_BEGIN_ALLOW_THREADS + mysql_close(&(self->connection)); + Py_END_ALLOW_THREADS + self->open = 0; + } + Py_XDECREF(self->converter); + self->converter = NULL; + Py_INCREF(Py_None); + return Py_None; +} + +static PyObject * +_mysql_ConnectionObject_affected_rows( + _mysql_ConnectionObject *self, + PyObject *args) +{ + if (!PyArg_NoArgs(args)) return NULL; + check_connection(self); + return PyLong_FromUnsignedLongLong(mysql_affected_rows(&(self->connection))); +} + +static char _mysql_debug__doc__[] = +"debug(s) -- Does a DBUG_PUSH with the given string.\n\ +mysql_debug() uses the Fred Fish debug library.\n\ +To use this function, you must compile the client library to\n\ +support debugging.\n\ +"; +static PyObject * +_mysql_debug( + PyObject *self, + PyObject *args) +{ + char *debug; + if (!PyArg_ParseTuple(args, "s", &debug)) return NULL; + mysql_debug(debug); + Py_INCREF(Py_None); + return Py_None; +} + +static PyObject * +_mysql_ConnectionObject_dump_debug_info( + _mysql_ConnectionObject *self, + PyObject *args) +{ + int err; + if (!PyArg_NoArgs(args)) return NULL; + check_connection(self); + Py_BEGIN_ALLOW_THREADS + err = mysql_dump_debug_info(&(self->connection)); + Py_END_ALLOW_THREADS + if (err) return _mysql_Exception(self); + Py_INCREF(Py_None); + return Py_None; +} + +static PyObject * +_mysql_ConnectionObject_errno( + _mysql_ConnectionObject *self, + PyObject *args) +{ + if (!PyArg_NoArgs(args)) return NULL; + check_connection(self); + return PyInt_FromLong((long)mysql_errno(&(self->connection))); +} + +static PyObject * +_mysql_ConnectionObject_error( + _mysql_ConnectionObject *self, + PyObject *args) +{ + if (!PyArg_NoArgs(args)) return NULL; + check_connection(self); + return PyString_FromString(mysql_error(&(self->connection))); +} + +static char _mysql_escape_string__doc__[] = +"escape_string(s) -- quote any SQL-interpreted characters in string s.\n\ +\n\ +This function is somewhat deprecated. mysql_real_escape_string() was\n\ +introduced in MySQL-3.23. The new version handles character sets\n\ +correctly, but requires a connection object to work. Therefore,\n\ +escape_string() has become a method of the connection object. It is\n\ +retained as a module function for compatibility reasons. Use the\n\ +method version of this function if at all possible."; +static PyObject * +_mysql_escape_string( + _mysql_ConnectionObject *self, + PyObject *args) +{ + PyObject *str; + char *in, *out; + int len, size; + if (!PyArg_ParseTuple(args, "s#:escape_string", &in, &size)) return NULL; + str = PyString_FromStringAndSize((char *) NULL, size*2+1); + if (!str) return PyErr_NoMemory(); + out = PyString_AS_STRING(str); +#if MYSQL_VERSION_ID < 32321 + len = mysql_escape_string(out, in, size); +#else + if (self) { + check_connection(self); + len = mysql_real_escape_string(&(self->connection), out, in, size); + } + else + len = mysql_escape_string(out, in, size); +#endif + if (_PyString_Resize(&str, len) < 0) return NULL; + return (str); +} + +static char _mysql_string_literal__doc__[] = +"string_literal(obj) -- converts object obj into a SQL string literal.\n\ +This means, any special SQL characters are escaped, and it is enclosed\n\ +within single quotes. In other words, it performs:\n\ +\n\ +\"'%s'\" % escape_string(str(obj))\n\ +\n\ +This function is somewhat deprecated. mysql_real_escape_string() was\n\ +introduced in MySQL-3.23. The new version handles character sets\n\ +correctly, but requires a connection object to work. Therefore,\n\ +string_literal() has become a method of the connection object. It is\n\ +retained as a module function for compatibility reasons. Use the\n\ +method version of this function if at all possible."; +static PyObject * +_mysql_string_literal( + _mysql_ConnectionObject *self, + PyObject *args) +{ + PyObject *str, *s, *o, *d; + char *in, *out; + int len, size; + if (!PyArg_ParseTuple(args, "O|O:string_literal", &o, &d)) return NULL; + s = PyObject_Str(o); + in = PyString_AsString(s); + size = PyString_GET_SIZE(s); + str = PyString_FromStringAndSize((char *) NULL, size*2+3); + if (!str) return PyErr_NoMemory(); + out = PyString_AS_STRING(str); +#if MYSQL_VERSION_ID < 32321 + len = mysql_escape_string(out+1, in, size); +#else + if (self) { + check_connection(self); + len = mysql_real_escape_string(&(self->connection), out+1, in, size); + } + else + len = mysql_escape_string(out+1, in, size); +#endif + *out = *(out+len+1) = '\''; + if (_PyString_Resize(&str, len+2) < 0) return NULL; + Py_DECREF(s); + return (str); +} + +static PyObject *_mysql_NULL; + +static PyObject * +_escape_item( + PyObject *item, + PyObject *d) +{ + PyObject *quoted=NULL, *itemtype, *itemconv; + if (!(itemtype = PyObject_Type(item))) + goto error; + itemconv = PyObject_GetItem(d, itemtype); + Py_DECREF(itemtype); + if (!itemconv) { + PyErr_Clear(); + itemconv = PyObject_GetItem(d, + (PyObject *) &PyString_Type); + } + if (!itemconv) { + PyErr_SetString(PyExc_TypeError, + "no default type converter defined"); + goto error; + } + quoted = PyObject_CallFunction(itemconv, "OO", item, d); + Py_DECREF(itemconv); +error: + return quoted; +} + +static char _mysql_escape__doc__[] = +"escape(obj, dict) -- escape any special characters in object obj\n\ +using mapping dict to provide quoting functions for each type.\n\ +Returns a SQL literal string."; +static PyObject * +_mysql_escape( + PyObject *self, + PyObject *args) +{ + PyObject *o=NULL, *d=NULL; + if (!PyArg_ParseTuple(args, "O|O:escape", &o, &d)) + return NULL; + if (d) { + if (!PyMapping_Check(d)) { + PyErr_SetString(PyExc_TypeError, + "argument 2 must be a mapping"); + return NULL; + } + return _escape_item(o, d); + } else { + if (!self) { + PyErr_SetString(PyExc_TypeError, + "argument 2 must be a mapping"); + return NULL; + } + return _escape_item(o, + ((_mysql_ConnectionObject *) self)->converter); + } +} + +static char _mysql_escape_sequence__doc__[] = +"escape_sequence(seq, dict) -- escape any special characters in sequence\n\ +seq using mapping dict to provide quoting functions for each type.\n\ +Returns a tuple of escaped items."; +static PyObject * +_mysql_escape_sequence( + PyObject *self, + PyObject *args) +{ + PyObject *o=NULL, *d=NULL, *r=NULL, *item, *quoted; + int i, n; + if (!PyArg_ParseTuple(args, "OO:escape_sequence", &o, &d)) + goto error; + if (!PyMapping_Check(d)) { + PyErr_SetString(PyExc_TypeError, + "argument 2 must be a mapping"); + return NULL; + } + if (!(n = PyObject_Length(o))) goto error; + if (!(r = PyTuple_New(n))) goto error; + for (i=0; iresult); + fields = mysql_fetch_fields(self->result); + if (!(d = PyTuple_New(n))) return NULL; + for (i=0; iresult); + fields = mysql_fetch_fields(self->result); + if (!(d = PyTuple_New(n))) return NULL; + for (i=0; iresult); + if (!(r = PyTuple_New(n))) return NULL; + length = mysql_fetch_lengths(self->result); + for (i=0; iconverter, i); + v = _mysql_field_to_python(c, row[i], length[i]); + if (!v) goto error; + PyTuple_SET_ITEM(r, i, v); + } + return r; + error: + Py_XDECREF(r); + return NULL; +} + +static PyObject * +_mysql_row_to_dict( + _mysql_ResultObject *self, + MYSQL_ROW row) +{ + unsigned int n, i; + unsigned long *length; + PyObject *r, *c; + MYSQL_FIELD *fields; + + n = mysql_num_fields(self->result); + if (!(r = PyDict_New())) return NULL; + length = mysql_fetch_lengths(self->result); + fields = mysql_fetch_fields(self->result); + for (i=0; iconverter, i); + v = _mysql_field_to_python(c, row[i], length[i]); + if (!v) goto error; + if (!PyMapping_HasKeyString(r, fields[i].name)) { + PyMapping_SetItemString(r, fields[i].name, v); + } else { + int len; + char buf[256]; + strncpy(buf, fields[i].table, 256); + len = strlen(buf); + strncat(buf, ".", 256-len); + len = strlen(buf); + strncat(buf, fields[i].name, 256-len); + PyMapping_SetItemString(r, buf, v); + } + Py_DECREF(v); + } + return r; + error: + Py_XDECREF(r); + return NULL; +} + +static PyObject * +_mysql_row_to_dict_old( + _mysql_ResultObject *self, + MYSQL_ROW row) +{ + unsigned int n, i; + unsigned long *length; + PyObject *r, *c; + MYSQL_FIELD *fields; + + n = mysql_num_fields(self->result); + if (!(r = PyDict_New())) return NULL; + length = mysql_fetch_lengths(self->result); + fields = mysql_fetch_fields(self->result); + for (i=0; iconverter, i); + v = _mysql_field_to_python(c, row[i], length[i]); + if (!v) goto error; + { + int len=0; + char buf[256]=""; + if (strlen(fields[i].table)) { + strncpy(buf, fields[i].table, 256); + len = strlen(buf); + strncat(buf, ".", 256-len); + len = strlen(buf); + } + strncat(buf, fields[i].name, 256-len); + PyMapping_SetItemString(r, buf, v); + } + Py_DECREF(v); + } + return r; + error: + Py_XDECREF(r); + return NULL; +} + +typedef PyObject *_PYFUNC(_mysql_ResultObject *, MYSQL_ROW); + +int +_mysql__fetch_row( + _mysql_ResultObject *self, + PyObject **r, + int skiprows, + int maxrows, + _PYFUNC *convert_row) +{ + unsigned int i; + MYSQL_ROW row; + + for (i = skiprows; i<(skiprows+maxrows); i++) { + PyObject *v; + if (!self->use) + row = mysql_fetch_row(self->result); + else { + Py_BEGIN_ALLOW_THREADS; + row = mysql_fetch_row(self->result); + Py_END_ALLOW_THREADS; + } + if (!row && mysql_errno(&(((_mysql_ConnectionObject *)(self->conn))->connection))) { + _mysql_Exception((_mysql_ConnectionObject *)self->conn); + goto error; + } + if (!row) { + if (_PyTuple_Resize(r, i, 0) == -1) goto error; + break; + } + v = convert_row(self, row); + if (!v) goto error; + PyTuple_SET_ITEM(*r, i, v); + } + return i-skiprows; + error: + return -1; +} + +static PyObject * +_mysql_ResultObject_fetch_row( + _mysql_ResultObject *self, + PyObject *args, + PyObject *kwargs) +{ + typedef PyObject *_PYFUNC(_mysql_ResultObject *, MYSQL_ROW); + static char *kwlist[] = { "maxrows", "how", NULL }; + static _PYFUNC *row_converters[] = + { + _mysql_row_to_tuple, + _mysql_row_to_dict, + _mysql_row_to_dict_old + }; + _PYFUNC *convert_row; + unsigned int maxrows=1, how=0, skiprows=0, rowsadded; + PyObject *r=NULL; + + if (!PyArg_ParseTupleAndKeywords(args, kwargs, "|ii:fetch_row", kwlist, + &maxrows, &how)) + return NULL; + check_result_connection(self); + if (how < 0 || how >= sizeof(row_converters)) { + PyErr_SetString(PyExc_ValueError, "how out of range"); + return NULL; + } + convert_row = row_converters[how]; + if (maxrows) { + if (!(r = PyTuple_New(maxrows))) goto error; + rowsadded = _mysql__fetch_row(self, &r, skiprows, maxrows, + convert_row); + if (rowsadded == -1) goto error; + } else { + if (self->use) { + maxrows = 1000; + if (!(r = PyTuple_New(maxrows))) goto error; + while (1) { + rowsadded = _mysql__fetch_row(self, &r, skiprows, + maxrows, convert_row); + if (rowsadded == -1) goto error; + skiprows += rowsadded; + if (rowsadded < maxrows) break; + } + } else { + /* XXX if overflow, maxrows<0? */ + maxrows = (int) mysql_num_rows(self->result); + if (!(r = PyTuple_New(maxrows))) goto error; + rowsadded = _mysql__fetch_row(self, &r, 0, + maxrows, convert_row); + if (rowsadded == -1) goto error; + } + } + return r; + error: + Py_XDECREF(r); + return NULL; +} + +#if MYSQL_VERSION_ID >= 32303 +static PyObject * +_mysql_ConnectionObject_change_user( + _mysql_ConnectionObject *self, + PyObject *args, + PyObject *kwargs) +{ + char *user, *pwd=NULL, *db=NULL; + int r; + static char *kwlist[] = { "user", "passwd", "db", NULL } ; + + if (!PyArg_ParseTupleAndKeywords(args, kwargs, "s|ss:change_user", + kwlist, &user, &pwd, &db)) + return NULL; + check_connection(self); + Py_BEGIN_ALLOW_THREADS + r = mysql_change_user(&(self->connection), user, pwd, db); + Py_END_ALLOW_THREADS + if (r) return _mysql_Exception(self); + Py_INCREF(Py_None); + return Py_None; +} +#endif + +#if MYSQL_VERSION_ID >= 32321 +static PyObject * +_mysql_ConnectionObject_character_set_name( + _mysql_ConnectionObject *self, + PyObject *args) +{ + const char *s; + if (!PyArg_NoArgs(args)) return NULL; + check_connection(self); + s = mysql_character_set_name(&(self->connection)); + return PyString_FromString(s); +} +#endif + +static char _mysql_get_client_info__doc__[] = +"get_client_info() -- Returns a string that represents\n\ +the client library version."; +static PyObject * +_mysql_get_client_info( + PyObject *self, + PyObject *args) +{ + if (!PyArg_NoArgs(args)) return NULL; + return PyString_FromString(mysql_get_client_info()); +} + +static PyObject * +_mysql_ConnectionObject_get_host_info( + _mysql_ConnectionObject *self, + PyObject *args) +{ + if (!PyArg_NoArgs(args)) return NULL; + check_connection(self); + return PyString_FromString(mysql_get_host_info(&(self->connection))); +} + +static PyObject * +_mysql_ConnectionObject_get_proto_info( + _mysql_ConnectionObject *self, + PyObject *args) +{ + if (!PyArg_NoArgs(args)) return NULL; + check_connection(self); + return PyInt_FromLong((long)mysql_get_proto_info(&(self->connection))); +} + +static PyObject * +_mysql_ConnectionObject_get_server_info( + _mysql_ConnectionObject *self, + PyObject *args) +{ + if (!PyArg_NoArgs(args)) return NULL; + check_connection(self); + return PyString_FromString(mysql_get_server_info(&(self->connection))); +} + +static PyObject * +_mysql_ConnectionObject_info( + _mysql_ConnectionObject *self, + PyObject *args) +{ + char *s; + if (!PyArg_NoArgs(args)) return NULL; + check_connection(self); + s = mysql_info(&(self->connection)); + if (s) return PyString_FromString(s); + Py_INCREF(Py_None); + return Py_None; +} + +static PyObject * +_mysql_ConnectionObject_insert_id( + _mysql_ConnectionObject *self, + PyObject *args) +{ + my_ulonglong r; + if (!PyArg_NoArgs(args)) return NULL; + check_connection(self); + Py_BEGIN_ALLOW_THREADS + r = mysql_insert_id(&(self->connection)); + Py_END_ALLOW_THREADS + return PyLong_FromUnsignedLongLong(r); +} + +static PyObject * +_mysql_ConnectionObject_kill( + _mysql_ConnectionObject *self, + PyObject *args) +{ + unsigned long pid; + int r; + if (!PyArg_ParseTuple(args, "i:kill", &pid)) return NULL; + check_connection(self); + Py_BEGIN_ALLOW_THREADS + r = mysql_kill(&(self->connection), pid); + Py_END_ALLOW_THREADS + if (r) return _mysql_Exception(self); + Py_INCREF(Py_None); + return Py_None; +} + +static PyObject * +_mysql_ConnectionObject_list_dbs( + _mysql_ConnectionObject *self, + PyObject *args) +{ + MYSQL_RES *result; + char *wild = NULL; + + if (!PyArg_ParseTuple(args, "|s:list_dbs", &wild)) return NULL; + check_connection(self); + Py_BEGIN_ALLOW_THREADS + result = mysql_list_dbs(&(self->connection), wild); + Py_END_ALLOW_THREADS + if (!result) return _mysql_Exception(self); + return (PyObject *) _mysql_ResultObject_New(self, result, 0, + self->converter); +} + +static PyObject * +_mysql_ConnectionObject_list_fields( + _mysql_ConnectionObject *self, + PyObject *args) +{ + MYSQL_RES *result; + char *wild = NULL, *table; + + if (!PyArg_ParseTuple(args, "s|s:list_fields", &table, &wild)) return NULL; + check_connection(self); + Py_BEGIN_ALLOW_THREADS + result = mysql_list_fields(&(self->connection), table, wild); + Py_END_ALLOW_THREADS + if (!result) return _mysql_Exception(self); + return (PyObject *) _mysql_ResultObject_New(self, result, 0, + self->converter); +} + +static PyObject * +_mysql_ConnectionObject_list_processes( + _mysql_ConnectionObject *self, + PyObject *args) +{ + MYSQL_RES *result; + + if (!PyArg_NoArgs(args)) return NULL; + check_connection(self); + Py_BEGIN_ALLOW_THREADS + result = mysql_list_processes(&(self->connection)); + Py_END_ALLOW_THREADS + if (!result) return _mysql_Exception(self); + return (PyObject *) _mysql_ResultObject_New(self, result, 0, + self->converter); +} + +static PyObject * +_mysql_ConnectionObject_list_tables( + _mysql_ConnectionObject *self, + PyObject *args) +{ + MYSQL_RES *result; + char *wild = NULL; + + if (!PyArg_ParseTuple(args, "|s:list_tables", &wild)) return NULL; + check_connection(self); + Py_BEGIN_ALLOW_THREADS + result = mysql_list_tables(&(self->connection), wild); + Py_END_ALLOW_THREADS + if (!result) return _mysql_Exception(self); + return (PyObject *) _mysql_ResultObject_New(self, result, 0, + self->converter); +} + +static PyObject * +_mysql_ConnectionObject_field_count( + _mysql_ConnectionObject *self, + PyObject *args) +{ + if (!PyArg_NoArgs(args)) return NULL; + check_connection(self); +#if MYSQL_VERSION_ID < 32224 + return PyInt_FromLong((long)mysql_num_fields(&(self->connection))); +#else + return PyInt_FromLong((long)mysql_field_count(&(self->connection))); +#endif +} + +static PyObject * +_mysql_ResultObject_num_fields( + _mysql_ResultObject *self, + PyObject *args) +{ + if (!PyArg_NoArgs(args)) return NULL; + check_result_connection(self); + return PyInt_FromLong((long)mysql_num_fields(self->result)); +} + +static PyObject * +_mysql_ResultObject_num_rows( + _mysql_ResultObject *self, + PyObject *args) +{ + if (!PyArg_NoArgs(args)) return NULL; + check_result_connection(self); + return PyLong_FromUnsignedLongLong(mysql_num_rows(self->result)); +} + +static PyObject * +_mysql_ConnectionObject_ping( + _mysql_ConnectionObject *self, + PyObject *args) +{ + int r; + if (!PyArg_NoArgs(args)) return NULL; + check_connection(self); + Py_BEGIN_ALLOW_THREADS + r = mysql_ping(&(self->connection)); + Py_END_ALLOW_THREADS + if (r) return _mysql_Exception(self); + Py_INCREF(Py_None); + return Py_None; +} + +static PyObject * +_mysql_ConnectionObject_query( + _mysql_ConnectionObject *self, + PyObject *args) +{ + char *query; + int len, r; + if (!PyArg_ParseTuple(args, "s#:query", &query, &len)) return NULL; + check_connection(self); + Py_BEGIN_ALLOW_THREADS + r = mysql_real_query(&(self->connection), query, len); + Py_END_ALLOW_THREADS + if (r) return _mysql_Exception(self); + Py_INCREF(Py_None); + return Py_None; +} + +static PyObject * +_mysql_ConnectionObject_select_db( + _mysql_ConnectionObject *self, + PyObject *args) +{ + char *db; + int r; + if (!PyArg_ParseTuple(args, "s:select_db", &db)) return NULL; + check_connection(self); + Py_BEGIN_ALLOW_THREADS + r = mysql_select_db(&(self->connection), db); + Py_END_ALLOW_THREADS + if (r) return _mysql_Exception(self); + Py_INCREF(Py_None); + return Py_None; +} + +static PyObject * +_mysql_ConnectionObject_shutdown( + _mysql_ConnectionObject *self, + PyObject *args) +{ + int r; + if (!PyArg_NoArgs(args)) return NULL; + check_connection(self); + Py_BEGIN_ALLOW_THREADS + r = mysql_shutdown(&(self->connection)); + Py_END_ALLOW_THREADS + if (r) return _mysql_Exception(self); + Py_INCREF(Py_None); + return Py_None; +} + +static PyObject * +_mysql_ConnectionObject_stat( + _mysql_ConnectionObject *self, + PyObject *args) +{ + char *s; + if (!PyArg_NoArgs(args)) return NULL; + check_connection(self); + Py_BEGIN_ALLOW_THREADS + s = mysql_stat(&(self->connection)); + Py_END_ALLOW_THREADS + if (!s) return _mysql_Exception(self); + return PyString_FromString(s); +} + +static PyObject * +_mysql_ConnectionObject_store_result( + _mysql_ConnectionObject *self, + PyObject *args) +{ + MYSQL_RES *result; + + if (!PyArg_NoArgs(args)) return NULL; + check_connection(self); + Py_BEGIN_ALLOW_THREADS + result = mysql_store_result(&(self->connection)); + Py_END_ALLOW_THREADS + if (!result) { + Py_INCREF(Py_None); + return Py_None; + } + return (PyObject *) _mysql_ResultObject_New(self, result, 0, + self->converter); +} + +static PyObject * +_mysql_ConnectionObject_thread_id( + _mysql_ConnectionObject *self, + PyObject *args) +{ + unsigned long pid; + if (!PyArg_NoArgs(args)) return NULL; + check_connection(self); + Py_BEGIN_ALLOW_THREADS + pid = mysql_thread_id(&(self->connection)); + Py_END_ALLOW_THREADS + return PyInt_FromLong((long)pid); +} + +static PyObject * +_mysql_ConnectionObject_use_result( + _mysql_ConnectionObject *self, + PyObject *args) +{ + MYSQL_RES *result; + + if (!PyArg_NoArgs(args)) return NULL; + check_connection(self); + Py_BEGIN_ALLOW_THREADS + result = mysql_use_result(&(self->connection)); + Py_END_ALLOW_THREADS + if (!result) { + Py_INCREF(Py_None); + return Py_None; + } + return (PyObject *) _mysql_ResultObject_New(self, result, 1, + self->converter); +} + +static void +_mysql_ConnectionObject_dealloc( + _mysql_ConnectionObject *self) +{ + PyObject *o; + + if (self->open) { + o = _mysql_ConnectionObject_close(self, NULL); + Py_XDECREF(o); + } + Py_XDECREF(self->converter); + PyMem_Free((char *) self); +} + +static PyObject * +_mysql_ConnectionObject_repr( + _mysql_ConnectionObject *self) +{ + char buf[300]; + if (self->open) + sprintf(buf, "", + self->connection.host, + (long)self); + else + sprintf(buf, "", + (long)self); + return PyString_FromString(buf); +} + +static char _mysql_ResultObject_data_seek__doc__[] = +"data_seek(n) -- seek to row n of result set"; +static PyObject * +_mysql_ResultObject_data_seek( + _mysql_ResultObject *self, + PyObject *args) +{ + unsigned int row; + if (!PyArg_ParseTuple(args, "i:data_seek", &row)) return NULL; + check_result_connection(self); + mysql_data_seek(self->result, row); + Py_INCREF(Py_None); + return Py_None; +} + +static char _mysql_ResultObject_row_seek__doc__[] = +"row_seek(n) -- seek by offset n rows of result set"; +static PyObject * +_mysql_ResultObject_row_seek( + _mysql_ResultObject *self, + PyObject *args) +{ + int offset; + MYSQL_ROW_OFFSET r; + if (!PyArg_ParseTuple(args, "i:row_seek", &offset)) return NULL; + check_result_connection(self); + r = mysql_row_tell(self->result); + mysql_row_seek(self->result, r+offset); + Py_INCREF(Py_None); + return Py_None; +} + +static char _mysql_ResultObject_row_tell__doc__[] = +"row_tell() -- return the current row number of the result set."; +static PyObject * +_mysql_ResultObject_row_tell( + _mysql_ResultObject *self, + PyObject *args) +{ + MYSQL_ROW_OFFSET r; + if (!PyArg_NoArgs(args)) return NULL; + check_result_connection(self); + r = mysql_row_tell(self->result); + return PyInt_FromLong(r-self->result->data->data); +} + +static void +_mysql_ResultObject_dealloc( + _mysql_ResultObject *self) +{ + mysql_free_result(self->result); + Py_DECREF(self->conn); + Py_DECREF(self->converter); + PyMem_Free((char *) self); +} + +static PyObject * +_mysql_ResultObject_repr( + _mysql_ResultObject *self) +{ + char buf[300]; + sprintf(buf, "", + (long)self); + return PyString_FromString(buf); +} + +static PyMethodDef _mysql_ConnectionObject_methods[] = { + {"affected_rows", (PyCFunction)_mysql_ConnectionObject_affected_rows, 0}, +#if MYSQL_VERSION_ID >= 32303 + {"change_user", (PyCFunction)_mysql_ConnectionObject_change_user, METH_VARARGS | METH_KEYWORDS}, +#endif +#if MYSQL_VERSION_ID >= 32321 + {"character_set_name", (PyCFunction)_mysql_ConnectionObject_character_set_name, 0}, +#endif + {"close", (PyCFunction)_mysql_ConnectionObject_close, 0}, + {"dump_debug_info", (PyCFunction)_mysql_ConnectionObject_dump_debug_info, 0}, + {"escape", (PyCFunction)_mysql_escape, 1}, + {"escape_string", (PyCFunction)_mysql_escape_string, 1}, + {"error", (PyCFunction)_mysql_ConnectionObject_error, 0}, + {"errno", (PyCFunction)_mysql_ConnectionObject_errno, 0}, + {"field_count", (PyCFunction)_mysql_ConnectionObject_field_count, 0}, + {"get_host_info", (PyCFunction)_mysql_ConnectionObject_get_host_info, 0}, + {"get_proto_info", (PyCFunction)_mysql_ConnectionObject_get_proto_info, 0}, + {"get_server_info", (PyCFunction)_mysql_ConnectionObject_get_server_info, 0}, + {"info", (PyCFunction)_mysql_ConnectionObject_info, 0}, + {"insert_id", (PyCFunction)_mysql_ConnectionObject_insert_id, 0}, + {"kill", (PyCFunction)_mysql_ConnectionObject_kill, 1}, + {"list_dbs", (PyCFunction)_mysql_ConnectionObject_list_dbs, 1}, + {"list_fields", (PyCFunction)_mysql_ConnectionObject_list_fields, 1}, + {"list_processes", (PyCFunction)_mysql_ConnectionObject_list_processes, 0}, + {"list_tables", (PyCFunction)_mysql_ConnectionObject_list_tables, 1}, + {"ping", (PyCFunction)_mysql_ConnectionObject_ping, 0}, + {"query", (PyCFunction)_mysql_ConnectionObject_query, 1}, + {"select_db", (PyCFunction)_mysql_ConnectionObject_select_db, 1}, + {"shutdown", (PyCFunction)_mysql_ConnectionObject_shutdown, 0}, + {"stat", (PyCFunction)_mysql_ConnectionObject_stat, 0}, + {"store_result", (PyCFunction)_mysql_ConnectionObject_store_result, 0}, + {"string_literal", (PyCFunction)_mysql_string_literal, 1}, + {"thread_id", (PyCFunction)_mysql_ConnectionObject_thread_id, 0}, + {"use_result", (PyCFunction)_mysql_ConnectionObject_use_result, 0}, + {NULL, NULL} /* sentinel */ +}; + +static struct memberlist _mysql_ConnectionObject_memberlist[] = { + {"open", T_INT, 0, RO}, + {"closed", T_INT, 0, RO}, + {"converter", T_OBJECT, offsetof(_mysql_ConnectionObject,converter)}, + {NULL} /* Sentinel */ +}; + +static PyMethodDef _mysql_ResultObject_methods[] = { + {"data_seek", (PyCFunction)_mysql_ResultObject_data_seek, + METH_VARARGS, _mysql_ResultObject_data_seek__doc__}, + {"row_seek", (PyCFunction)_mysql_ResultObject_row_seek, + METH_VARARGS, _mysql_ResultObject_row_seek__doc__}, + {"row_tell", (PyCFunction)_mysql_ResultObject_row_tell, + METH_VARARGS, _mysql_ResultObject_row_tell__doc__}, + {"describe", (PyCFunction)_mysql_ResultObject_describe, 0}, + {"fetch_row", (PyCFunction)_mysql_ResultObject_fetch_row, METH_VARARGS | METH_KEYWORDS}, + {"field_flags", (PyCFunction)_mysql_ResultObject_field_flags, 0}, + {"num_fields", (PyCFunction)_mysql_ResultObject_num_fields, 0}, + {"num_rows", (PyCFunction)_mysql_ResultObject_num_rows, 0}, + {NULL, NULL} /* sentinel */ +}; + +static struct memberlist _mysql_ResultObject_memberlist[] = { + {"converter", T_OBJECT, offsetof(_mysql_ResultObject,converter), RO}, + {NULL} /* Sentinel */ +}; + +static PyObject * +_mysql_ConnectionObject_getattr( + _mysql_ConnectionObject *self, + char *name) +{ + PyObject *res; + + res = Py_FindMethod(_mysql_ConnectionObject_methods, (PyObject *)self, name); + if (res != NULL) + return res; + PyErr_Clear(); + if (strcmp(name, "open") == 0) + return PyInt_FromLong((long)(self->open)); + if (strcmp(name, "closed") == 0) + return PyInt_FromLong((long)!(self->open)); + if (strcmp(name, "server_capabilities") == 0) { + check_connection(self); + return PyInt_FromLong((long)(self->connection.server_capabilities)); + } + return PyMember_Get((char *)self, _mysql_ConnectionObject_memberlist, name); +} + +static PyObject * +_mysql_ResultObject_getattr( + _mysql_ResultObject *self, + char *name) +{ + PyObject *res; + + res = Py_FindMethod(_mysql_ResultObject_methods, (PyObject *)self, name); + if (res != NULL) + return res; + PyErr_Clear(); + return PyMember_Get((char *)self, _mysql_ResultObject_memberlist, name); +} + +static int +_mysql_ConnectionObject_setattr( + _mysql_ConnectionObject *c, + char *name, + PyObject *v) +{ + if (v == NULL) { + PyErr_SetString(PyExc_AttributeError, + "can't delete connection attributes"); + return -1; + } + return PyMember_Set((char *)c, _mysql_ConnectionObject_memberlist, name, v); +} + +static int +_mysql_ResultObject_setattr( + _mysql_ResultObject *c, + char *name, + PyObject *v) +{ + if (v == NULL) { + PyErr_SetString(PyExc_AttributeError, + "can't delete connection attributes"); + return -1; + } + return PyMember_Set((char *)c, _mysql_ResultObject_memberlist, name, v); +} + +PyTypeObject _mysql_ConnectionObject_Type = { +#ifndef MS_WIN32 + PyObject_HEAD_INIT(&PyType_Type) +#else + PyObject_HEAD_INIT(NULL) +#endif + 0, + "connection", + sizeof(_mysql_ConnectionObject), + 0, + (destructor)_mysql_ConnectionObject_dealloc, /* tp_dealloc */ + 0, /*tp_print*/ + (getattrfunc)_mysql_ConnectionObject_getattr, /* tp_getattr */ + (setattrfunc)_mysql_ConnectionObject_setattr, /* tp_setattr */ + 0, /*tp_compare*/ + (reprfunc)_mysql_ConnectionObject_repr, /* tp_repr */ +}; + +PyTypeObject _mysql_ResultObject_Type = { +#ifndef MS_WIN32 + PyObject_HEAD_INIT(&PyType_Type) +#else + PyObject_HEAD_INIT(NULL) +#endif + 0, + "result", + sizeof(_mysql_ResultObject), + 0, + (destructor)_mysql_ResultObject_dealloc, /* tp_dealloc */ + 0, /*tp_print*/ + (getattrfunc)_mysql_ResultObject_getattr, /* tp_getattr */ + (setattrfunc)_mysql_ResultObject_setattr, /* tp_setattr */ + 0, /*tp_compare*/ + (reprfunc)_mysql_ResultObject_repr, /* tp_repr */ +}; + +static PyMethodDef +_mysql_methods[] = { + { "connect", + (PyCFunction)_mysql_connect, + METH_VARARGS | METH_KEYWORDS, + _mysql_connect__doc__ + }, + { "debug", + (PyCFunction)_mysql_debug, + METH_VARARGS, + _mysql_debug__doc__ + }, + { "escape", + (PyCFunction)_mysql_escape, + METH_VARARGS, + _mysql_escape__doc__ + }, + { "escape_sequence", + (PyCFunction)_mysql_escape_sequence, + METH_VARARGS, + _mysql_escape_sequence__doc__ + }, + { "escape_dict", + (PyCFunction)_mysql_escape_dict, + METH_VARARGS, + _mysql_escape_dict__doc__ + }, + { "escape_string", + (PyCFunction)_mysql_escape_string, + METH_VARARGS, + _mysql_escape_string__doc__ + }, + { "string_literal", + (PyCFunction)_mysql_string_literal, + METH_VARARGS, + _mysql_string_literal__doc__ + }, + { "get_client_info", + (PyCFunction)_mysql_get_client_info, + 0, + _mysql_get_client_info__doc__ + }, + {NULL, NULL} /* sentinel */ +}; + +static PyObject * +_mysql_NewException( + PyObject *dict, + PyObject *edict, + char *name) +{ + PyObject *e; + + if (!(e = PyDict_GetItemString(edict, name))) + return NULL; + if (PyDict_SetItemString(dict, name, e)) return NULL; + return e; +} + +static char _mysql___doc__[] = +"_mysql: an adaptation of the MySQL C API (mostly)\n\ +\n\ +You probably are better off using MySQLdb instead of using this\n\ +module directly.\n\ +\n\ +In general, renaming goes from mysql_* to _mysql.*. _mysql.connect()\n\ +returns a connection object (MYSQL). Functions which expect MYSQL * as\n\ +an argument are now methods of the connection object. A number of things\n\ +return result objects (MYSQL_RES). Functions which expect MYSQL_RES * as\n\ +an argument are now methods of the result object. The mysql_real_*\n\ +functions are the ones used in place of not-real ones. The various\n\ +FLAG_*, CLIENT_*, FIELD_TYPE_*, etc. constants are renamed to FLAG.*,\n\ +CLIENT.*, FIELD_TYPE.*, etc. Deprecated functions (as of 3.22) are NOT\n\ +implemented.\n\ +"; + +DL_EXPORT(void) +init_mysql(void) +{ + PyObject *dict, *module, *emod, *edict; + module = Py_InitModule3("_mysql", _mysql_methods, _mysql___doc__); +#ifdef MS_WIN32 + _mysql_ConnectionObject_Type.ob_type = &PyType_Type; + _mysql_ResultObject_Type.ob_type = &PyType_Type; +#endif + if (!(dict = PyModule_GetDict(module))) goto error; + if (PyDict_SetItemString(dict, "__version__", + PyString_FromString(_mysql__version__))) + goto error; + if (!(emod = PyImport_ImportModule("_mysql_exceptions"))) + goto error; + if (!(edict = PyModule_GetDict(emod))) goto error; + if (!(_mysql_MySQLError = + _mysql_NewException(dict, edict, "MySQLError"))) + goto error; + if (!(_mysql_Warning = + _mysql_NewException(dict, edict, "Warning"))) + goto error; + if (!(_mysql_Error = + _mysql_NewException(dict, edict, "Error"))) + goto error; + if (!(_mysql_InterfaceError = + _mysql_NewException(dict, edict, "InterfaceError"))) + goto error; + if (!(_mysql_DatabaseError = + _mysql_NewException(dict, edict, "DatabaseError"))) + goto error; + if (!(_mysql_DataError = + _mysql_NewException(dict, edict, "DataError"))) + goto error; + if (!(_mysql_OperationalError = + _mysql_NewException(dict, edict, "OperationalError"))) + goto error; + if (!(_mysql_IntegrityError = + _mysql_NewException(dict, edict, "IntegrityError"))) + goto error; + if (!(_mysql_InternalError = + _mysql_NewException(dict, edict, "InternalError"))) + goto error; + if (!(_mysql_ProgrammingError = + _mysql_NewException(dict, edict, "ProgrammingError"))) + goto error; + if (!(_mysql_NotSupportedError = + _mysql_NewException(dict, edict, "NotSupportedError"))) + goto error; + Py_DECREF(emod); + if (!(_mysql_NULL = PyString_FromString("NULL"))) + goto error; + if (PyDict_SetItemString(dict, "NULL", _mysql_NULL)) goto error; + error: + if (PyErr_Occurred()) + PyErr_SetString(PyExc_ImportError, + "_mysql: init failed"); + return; +} + + diff --git a/MySQLdb/_mysql_exceptions.py b/MySQLdb/_mysql_exceptions.py new file mode 100644 index 0000000..9614121 --- /dev/null +++ b/MySQLdb/_mysql_exceptions.py @@ -0,0 +1,83 @@ +"""_mysql_exceptions: Exception classes for _mysql and MySQLdb. + +These classes are dictated by the DB API v2.0: + + http://www.python.org/topics/database/DatabaseAPI-2.0.html +""" + +from exceptions import Exception, StandardError + +class MySQLError(StandardError): + + """Exception related to operation with MySQL.""" + + +class Warning(MySQLError): + + """Exception raised for important warnings like data truncations + while inserting, etc.""" + +class Error(MySQLError): + + """Exception that is the base class of all other error exceptions + (not Warning).""" + + +class InterfaceError(Error): + + """Exception raised for errors that are related to the database + interface rather than the database itself.""" + + +class DatabaseError(Error): + + """Exception raised for errors that are related to the + database.""" + + +class DataError(DatabaseError): + + """Exception raised for errors that are due to problems with the + processed data like division by zero, numeric value out of range, + etc.""" + + +class OperationalError(DatabaseError): + + """Exception raised for errors that are related to the database's + operation and not necessarily under the control of the programmer, + e.g. an unexpected disconnect occurs, the data source name is not + found, a transaction could not be processed, a memory allocation + error occurred during processing, etc.""" + + +class IntegrityError(DatabaseError): + + """Exception raised when the relational integrity of the database + is affected, e.g. a foreign key check fails, duplicate key, + etc.""" + + +class InternalError(DatabaseError): + + """Exception raised when the database encounters an internal + error, e.g. the cursor is not valid anymore, the transaction is + out of sync, etc.""" + + +class ProgrammingError(DatabaseError): + + """Exception raised for programming errors, e.g. table not found + or already exists, syntax error in the SQL statement, wrong number + of parameters specified, etc.""" + + +class NotSupportedError(DatabaseError): + + """Exception raised in case a method or database API was used + which is not supported by the database, e.g. requesting a + .rollback() on a connection that does not support transaction or + has transactions turned off.""" + + +del Exception, StandardError diff --git a/MySQLdb/_mysql_version.h b/MySQLdb/_mysql_version.h new file mode 100644 index 0000000..45cf1f2 --- /dev/null +++ b/MySQLdb/_mysql_version.h @@ -0,0 +1 @@ +static char _mysql__version__[] = "0.9.0b1"; diff --git a/MySQLdb/setup.py b/MySQLdb/setup.py new file mode 100644 index 0000000..8b4a4d2 --- /dev/null +++ b/MySQLdb/setup.py @@ -0,0 +1,109 @@ +#!/usr/bin/env python + +"""Setup script for the MySQLdb module distribution.""" + +import os, sys +from distutils.core import setup +from distutils.extension import Extension + +YES = 1 +NO = 0 + +# set this to 1 if you have the thread-safe mysqlclient library +thread_safe_library = NO + +# You probably don't have to do anything past this point. If you +# do, please mail me the configuration for your platform. Don't +# forget to include the value of sys.platform and os.name. + +mysqlclient = thread_safe_library and "mysqlclient_r" or "mysqlclient" + +if sys.platform == "linux-i386": # Red Hat + include_dirs = ['/usr/include/mysql'] + library_dirs = ['/usr/lib/mysql'] + libraries = [mysqlclient, "z"] + runtime_library_dirs = [] + extra_objects = [] +elif sys.platform in ("freebsd4", "openbsd2"): + include_dirs = ['/usr/local/include/mysql'] + library_dirs = ['/usr/local/lib/mysql'] + libraries = [mysqlclient, "z"] + runtime_library_dirs = [] + extra_objects = [] +elif sys.platform == "win32": + include_dirs = [r'c:\mysql\include'] + library_dirs = [r'c:\mysql\lib\opt'] + libraries = [mysqlclient, 'zlib', 'msvcrt', 'libcmt', + 'wsock32', 'advapi32'] + runtime_library_dirs = [] + extra_objects = [r'c:\mysql\lib\opt\mysqlclient.lib'] +elif os.name == "posix": # most Linux/UNIX platforms + include_dirs = ['/usr/include/mysql'] + library_dirs = ['/usr/lib/mysql'] + # MySQL-3.23 seems to need libz + libraries = [mysqlclient, "z"] + # On some platorms, this can be used to find the shared libraries + # at runtime, if they are in a non-standard location. Doesn't + # work for Linux gcc. + ## runtime_library_dirs = library_dirs + runtime_library_dirs = [] + # This can be used on Linux to force use of static mysqlclient lib + ## extra_objects = ['/usr/lib/mysql/libmysqlclient.a'] + extra_objects = [] +else: + raise "UnknownPlatform", "sys.platform=%s, os.name=%s" % \ + (sys.platform, os.name) + +long_description = \ +"""Python interface to MySQL-3.23 + +MySQLdb is an interface to the popular MySQL database server for Python. +The design goals are: + +- Compliance with Python database API version 2.0 +- Thread-safety +- Thread-friendliness (threads will not block each other) +- Compatibility with MySQL-3.23 and later + +This module should be mostly compatible with an older interface +written by Joe Skinner and others. However, the older version is +a) not thread-friendly, b) written for MySQL 3.21, c) apparently +not actively maintained. No code from that version is used in +MySQLdb. MySQLdb is free software. + +""" + +setup (# Distribution meta-data + name = "MySQL-python", + version = "0.9.0b1", + description = "An interface to MySQL", + long_description=long_description, + author = "Andy Dustman", + author_email = "andy@dustman.net", + url = "http://dustman.net/andy/python/MySQLdb", + + # Description of the modules and packages in the distribution + + py_modules = ["CompatMysqldb", + "_mysql_exceptions", + "MySQLdb.converters", + "MySQLdb.connections", + "MySQLdb.cursors", + "MySQLdb.constants.CR", + "MySQLdb.constants.FIELD_TYPE", + "MySQLdb.constants.ER", + "MySQLdb.constants.FLAG", + "MySQLdb.constants.REFRESH", + "MySQLdb.constants.CLIENT", + ], + + ext_modules = [Extension( + name='_mysql', + sources=['_mysql.c'], + include_dirs=include_dirs, + library_dirs=library_dirs, + runtime_library_dirs=runtime_library_dirs, + libraries=libraries, + extra_objects=extra_objects, + )], +)