From d480ad766a88960a9369e12636873c3065fdeb5e Mon Sep 17 00:00:00 2001 From: adustman Date: Sat, 27 Jan 2001 02:32:00 +0000 Subject: [PATCH] Update docs. result.fetch_row(0,how) returns all rows of the rsult set as a tuple. --- mysql/CompatMysqldb.py | 80 ++++++++++++++++---------------- mysql/MySQLdb-FAQ.sgml | 39 +++++++--------- mysql/MySQLdb.py | 50 ++++++++++---------- mysql/MySQLdb.sgml | 37 ++++++++------- mysql/_mysqlmodule.c | 102 +++++++++++++++++++++++++++++------------ mysql/setup.py | 2 +- 6 files changed, 175 insertions(+), 135 deletions(-) diff --git a/mysql/CompatMysqldb.py b/mysql/CompatMysqldb.py index fb3ec85..58aa843 100755 --- a/mysql/CompatMysqldb.py +++ b/mysql/CompatMysqldb.py @@ -1,5 +1,6 @@ #author: James Henstridge -#adapted to _mysql by Andy Dustman +#adapted to _mysql by Andy Dustman +#under no circumstances should you bug James about this!!! """This is a class that implements an interface to mySQL databases, conforming to the API published by the Python db-sig at @@ -41,21 +42,10 @@ from string import upper, split, join error = 'mysqldb.error' -_type = {} -for a in ('char', 'varchar', 'string', 'unhandled', '????'): - _type[a] = 'STRING' -for a in ('tiny blob', 'medium blob', 'long blob', 'blob'): - _type[a] = 'RAW' -for a in ('short', 'long', 'float', 'double', 'decimal'): - _type[a] = 'NUMBER' -for a in ('date', 'time', 'datetime', 'timestamp'): - _type[a] = 'DATE' -del a - from _mysql import FIELD_TYPE _type_conv = { FIELD_TYPE.TINY: int, FIELD_TYPE.SHORT: int, - FIELD_TYPE.LONG: int, + FIELD_TYPE.LONG: long, FIELD_TYPE.FLOAT: float, FIELD_TYPE.DOUBLE: float, FIELD_TYPE.LONGLONG: long, @@ -70,6 +60,33 @@ def isDML(q): 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): @@ -84,9 +101,10 @@ class Connection: except MySQL.Error, msg: raise error, msg self.__curs = Cursor(self.__conn) - self._server_info = i = self.__conn.get_server_info() - self._server_version = int(i[0])*10000 + int(i[2:4])*100 + int(i[5:7]) - + self.__conn.query("SHOW VARIABLES") + self.__vars = {} + for k, v in self.__conn.store_result().fetch_row(0): + self.__vars[k] = v def __del__(self): self.close() @@ -109,14 +127,15 @@ class Connection: def commit(self): """Commit the current transaction.""" - if self._server_version > 32315: self.__conn.query("COMMIT") + if self.__vars.get('have_bdb', 'NO') == 'YES': + self.__conn.query("COMMIT") def rollback(self): """Rollback the current transaction.""" - if self._server_version > 32315: self.__conn.query("ROLLBACK") + if self.__vars.get('have_bdb', 'NO') == 'YES': + 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 @@ -205,12 +224,9 @@ class Cursor: self.__res = self.__conn.query( op % params[-1]) self.insert_id = self.__res.insert_id() - f = self.__res.fields() except MySQL.Error, msg: raise error, msg - self.__dict__['description'] = tuple(map( - lambda x: (x[0], _type[x[2]], x[3], - x[3]), f)) + self.__dict__['description'] = self.__res.describe() return None else: try: @@ -244,17 +260,10 @@ class Cursor: def fetchall(self): if not self.__res: raise error, "no query made yet." try: - rows = r = list(self.__res.fetch_row(self.arraysize)) - while 1: - rows = list(self.__res.fetch_row(self.arraysize)) - if not rows: break - r.extend(rows) - return r + 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." @@ -275,16 +284,9 @@ class Cursor: """This is not a standard part of Python DB API.""" if not self.__res: raise error, "no query made yet." try: - rows = r = list(self.__res.fetch_row(self.arraysize, 2)) - while 1: - rows = list(self.__res.fetch_row(self.arraysize, 2)) - if not rows: break - r.extend(rows) - return r + 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 diff --git a/mysql/MySQLdb-FAQ.sgml b/mysql/MySQLdb-FAQ.sgml index 023155d..574c779 100644 --- a/mysql/MySQLdb-FAQ.sgml +++ b/mysql/MySQLdb-FAQ.sgml @@ -19,9 +19,7 @@ You have two basic options:

-Modify the compiler flags in Setup so that it links against the static -library. Probably Change your system environment so that the MySQL libraries are on your loader path. With Linux, you can modify /etc/ld.so.conf (see @@ -38,13 +36,13 @@ linking against an earlier version. You may also have more than one version installed. Get Python 1.5.2 from your vendor or python.org. ImportError: ./_mysqlmodule.so: undefined symbol: uncompress -./_mysqlmodule.c:33: mysql.h: No such file or directory

It seems that MySQL-3.23 client libraries require libz for gzip -compression. Add -lz to the link line in Setup. +compression. setup.py should add this automatically. +./_mysqlmodule.c:33: mysql.h: No such file or directory

The include path (-I) to your MySQL include files is wrong; modify -Setup. OR: You don't have the MySQL development stuff loaded. If you +setup.py. OR: You don't have the MySQL development stuff loaded. If you are using the Red Hat RPMs, you need the I'm using Windows...

Say no more. -

There is a I don't use Windows. setup.py is supposed to work for building. +There may also be a link to some user-contributed binaries on the web site. Trouble with ZMySQLDA -

Not supported. By me, at least. There is a new version on - -that supports MySQLdb without any patching. Use that. +

What? ZMySQLDA never fails! Well, actually, I just don't have any +good questions yet. Except: Install MySQLdb first, and then untar +the ZMySQLDA source into your Zope home, and restart Zope. Using MySQLdb

MySQLdb is a database module, so you should be familiar with the spec. Deviations from the spec are documented in the -. What do I do if I am completely clueless?

Get a clue. Clues have been provided in the But MySQL doesn't have cursors!

True enough. MySQLdb fakes it, though, because the spec requires it. -cursor.rollback() is missing! -

MySQL doesn't do transactions. cursor.rollback() always fails! +

MySQL now supports transactions using BDB tables. +If your server doesn't support them, rollbacks will always fail, as they should, +because it can't do what you asked. +Even if your server does support them, rollbacks will fail if you +modified any non-BDB tables.

OTOH, MySQL-3.23 will, sometime in the near future, support transactions. -When this happens, How do I use some of the special MySQL features?

Short answer: Don't, if you can avoid it. Your program will not be portable to other databases. @@ -119,7 +114,7 @@ argument (the connection handle in the C API). So let's say you want to use I still wanna use _mysql directly. -

Well, it Well, it diff --git a/mysql/MySQLdb.py b/mysql/MySQLdb.py index 580f19a..a631630 100644 --- a/mysql/MySQLdb.py +++ b/mysql/MySQLdb.py @@ -53,7 +53,7 @@ quote_conv = { types.IntType: Thing2Str, type_conv = { FIELD_TYPE.TINY: int, FIELD_TYPE.SHORT: int, - FIELD_TYPE.LONG: int, + FIELD_TYPE.LONG: long, FIELD_TYPE.FLOAT: float, FIELD_TYPE.DOUBLE: float, FIELD_TYPE.LONGLONG: long, @@ -144,14 +144,6 @@ def Binary(x): return str(x) insert_values = re.compile(r'values\s(\(.+\))', re.IGNORECASE) -def _fetchall(result, *args): - rows = r = list(apply(result.fetch_row, args)) - while 1: - rows = apply(result.fetch_row, args) - if not rows: break - r.extend(list(rows)) - return r - class BaseCursor: """A base for Cursor classes. Useful attributes: @@ -256,6 +248,14 @@ class BaseCursor: def nextset(self): return None + def _fetch_row(self): return self._result.fetch_row(1, self._fetch_type)[0] + + def _fetch_rows(self, size): + return self._result.fetch_row(size, self._fetch_type) + + def _fetch_all_rows(self): + return self._result.fetch_row(0, self._fetch_type) + class CursorWarningMixIn: @@ -363,16 +363,8 @@ class CursorTupleRowsMixIn: _fetch_type = 0 - def _fetch_row(self): return self._result.fetch_row(1, self._fetch_type)[0] - def _fetch_rows(self, size): - return self._result.fetch_row(size, self._fetch_type) - - def _fetch_all_rows(self): - return _fetchall(self._result, self.arraysize, self._fetch_type) - - -class CursorDictRowsMixIn(CursorTupleRowsMixIn): +class CursorDictRowsMixIn: _fetch_type = 1 @@ -453,9 +445,11 @@ class Connection: self.cursorclass = Cursor self.db = apply(connect, (), kwargs) self.quote_conv[types.StringType] = self.Thing2Literal - self._server_info = self.db.get_server_info() - i = map(int, split(split(self._server_info, '-')[0],'.')) - self._server_version = i[0]*10000 + i[1]*100 + i[2] + self.db.query('show variables') + r = self.db.store_result() + vars = r.fetch_row(0) + self._server_vars = {} + for k,v in vars: self._server_vars[k] = v if _threading: self.__lock = _threading.Lock() if _threading: @@ -473,11 +467,13 @@ class Connection: def commit(self): """Commit the current transaction.""" - if self._server_version > 32315: self.db.query("COMMIT") + if self._server_vars.get('have_bdb','NO') == 'YES': + self.db.query("COMMIT") def rollback(self): """Rollback the current transaction.""" - if self._server_version > 32315: self.db.query("ROLLBACK") + if self._server_vars.get('have_bdb','NO') == 'YES': + self.db.query("ROLLBACK") else: raise NotSupportedError, "Not supported by server" def cursor(self, cursorclass=None): @@ -496,10 +492,10 @@ class Connection: 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 _fetchall(self.db.list_dbs()) - def list_fields(self, table): return _fetchall(self.db.list_fields(table)) - def list_processes(self): return _fetchall(self.db.list_processes()) - def list_tables(self, db): return _fetchall(self.db.list_tables(db)) + 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() diff --git a/mysql/MySQLdb.sgml b/mysql/MySQLdb.sgml index 3962b8e..5d9b2b7 100644 --- a/mysql/MySQLdb.sgml +++ b/mysql/MySQLdb.sgml @@ -23,14 +23,21 @@ charge under a license derived from the Python license.

Linux/UNIX

-This module is developed on RedHat Linux 5.2 for Intel. It should build without -much trouble on most UNIX-like platforms by using the . Windows (3.11, 95, 98, NT, 2000, CE, BSOD, XYZ, etc.)

Windows is Python

@@ -57,19 +64,19 @@ type. MySQL-3.23

-MySQLdb has only been lightly tested. -MySQL-3.23 is presently in beta (testing) release. Several API +MySQL-3.23 is presently in gamma (stable prerelease). Several API additions have been made so far. These will be incorporated into -MySQLdb as work progresses. As of 3.23.15, transactions are supported -in MySQL using BDB tables. MySQLdb (0.2.2 and up) detects the -server version upon connection and will issue DateTime

If you have the module installed (recommended), MySQLdb will use it for date-related objects. Otherwise, these will be returned to Python as strings. You can also modify the type conversion @@ -109,14 +116,12 @@ with the same name; use SQL db.commit() and db.rollback() both exist and silently do nothing | db.commit() and db.rollback() work if the MySQL -client library can perform transactions; otherwise db.rollback() -is not defined +server can perform transactions; otherwise db.rollback() +always fails Mysqldb to MySQLdb changes Zope and ZMySQLDA -

I'm not distributing a ZMySQLDA patch any more. Somebody else -has written a ZMySQLDA that works with MySQLdb. Find it at -. +

I wrote a for use with MySQLdb. Documentation

The web page documentation may be slightly ahead of the latest release and may reflect features of the next release. @@ -132,7 +137,7 @@ information, see the MySQL documentation. The documentation for this module is intentionally weak because you probably should use the higher-level module. If you really need it, use the standard MySQL docs and transliterate as necessary. -

Compatibility note: As of 0.2.2, the various fetch_rowXXX() cursor +

Compatibility note: As of 0.3.0, the various fetch_rowXXX() cursor methods have been combined into a single fetch_row([n=1[,how=0]]) method. See the built-in module documentation for more details.

diff --git a/mysql/_mysqlmodule.c b/mysql/_mysqlmodule.c index 0c66640..02043a2 100644 --- a/mysql/_mysqlmodule.c +++ b/mysql/_mysqlmodule.c @@ -880,12 +880,14 @@ _mysql_row_to_dict_old( v = _mysql_field_to_python(c, row[i], length[i]); if (!v) goto error; { - int len; - char buf[256]; - strncpy(buf, fields[i].table, 256); - len = strlen(buf); - strncat(buf, ".", 256-len); - len = strlen(buf); + 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); } @@ -897,6 +899,45 @@ _mysql_row_to_dict_old( 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(self->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, @@ -912,9 +953,8 @@ _mysql_ResultObject_fetch_row( _mysql_row_to_dict_old }; _PYFUNC *convert_row; - unsigned int maxrows=1, how=0, i; - PyObject *r; - MYSQL_ROW row; + unsigned int maxrows=1, how=0, skiprows=0, rowsadded; + PyObject *r=NULL; if (!PyArg_ParseTupleAndKeywords(args, kwargs, "|ii:fetch_row", kwlist, &maxrows, &how)) @@ -924,28 +964,30 @@ _mysql_ResultObject_fetch_row( return NULL; } convert_row = row_converters[how]; - - if (!(r = PyTuple_New(maxrows))) return NULL; - for (i = 0; iuse) - row = mysql_fetch_row(self->result); - else { - Py_BEGIN_ALLOW_THREADS; - row = mysql_fetch_row(self->result); - Py_END_ALLOW_THREADS; + 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; } - if (!row && mysql_errno(self->connection)) { - Py_XDECREF(r); - return _mysql_Exception((_mysql_ConnectionObject *)self->conn); - } - 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 r; error: diff --git a/mysql/setup.py b/mysql/setup.py index 300fbc1..bbde487 100644 --- a/mysql/setup.py +++ b/mysql/setup.py @@ -69,7 +69,7 @@ derived from the Python license. setup (# Distribution meta-data name = "MySQL-python", - version = "0.3.0", + version = "0.3.2", description = "An interface to MySQL", long_description=long_description, author = "Andy Dustman",