5.6. MySQLmodule-1.4
Этот модуль был разработан под Linux (RH50), MySQL 3.21.30 и Python 1.5.1.
MySQLmodule-1.x основан на mySQLmodule-0.1.4, разработанном:
mySQLmodule-0.1.4 в свою очередь основан на mSQLmodule, разработанном:
- Portions copyright (C) 1995 Thawte Consulting, cc
- Portions copyright (C) 1994 Anthony Baxter
5.6.1 Почему другой интерфейс Python/MySQL?
Была обнаружена проблема при сохранении строк, содержащих ASCII-ноль
(\0) в базе данных MySQL. После расследования в языке Python (который
создал первоначальную ошибку), был проверен код mySQLmodule и там найден ряд
проблем. Некоторые подпрограммы не освобождают распределенную память, некоторые
функции MySQL API не были доступны, и многих разработчиков совершенно не
устраивал тот факт, что mySQLmodule возвратит различные структуры данных в
зависимости от метода. Так что я переделал код. Так как эти изменения включают
изменение в методах Python и значениях возврата, был повышен код версии и
изменено имя модуля на MySQL, чтобы оно не столкнулось с оригиналом (mySQL).
Рационально возвратить список списков вместо набора или списка наборов. Не
стоит создавать ситуацию, когда подпрограммы доступа к DB занимаются обработкой
типа возврата, и стоит предусмотреть возможность изменить данные в возвращенной
таблице напрямую.
Data = DBH['select * from MyTable']
if Data:
rows = len(Data)
cols = len(Data[0])
for i in range(rows):
for j in range(cols):
if not Data[i][j]: Data[i][j] = DefaultElement()
Do_Something(Data)
Никакой потребности усложнять это при наличии наборов внутри внешнего списка
нет. Для тех, кто предпочитает словари, метод STH fetchdict() возвратит
список словарей. Ключи словаря квалифицированы с соответствующим именем
(возможно, не одним) обрабатываемой таблицы.
5.6.2 Компиляция и установка модуля MySQL
- Скопируйте MySQLmodule.c в подкаталог Modules дерева
исходников Python.
- Добавьте следующие строки в Ваш файл Setup в этом каталоге:
MySQL MySQLmodule.c -L/usr/local/lib/mysql/ -lmysqlclient \
-I/usr/local/include/mysql
Обратите внимание, что расположение библиотеки MySQL и include-каталога
может быть иным на Вашей специфической системе. Вы можете формировать
разделяемый модуль (вставкой ниже индикатора shared в файле
Setup).
- Если Вы формировали Python ранее, просто выполните make в Вашем
основном каталоге Python. Если нет, следуйте командам о том, как компилировать
и установить Python.
Чтобы формировать динамически загружаемый модуль без доступа к дереву
исходников python, используйте:
gcc -shared -I/usr/include/python1.5 -I/usr/local/include/mysql \
MySQLmodule.c -lmysqlclient -L/usr/lib/python1.5/config \
-lpython1.5 -o MySQLmodule.so
Переместите возникающий в результате файл MySQLmodule.so в
PYTHONPATH. Замените соответствующие расположения Ваших библиотек и
include-файлов.
Обратите внимание, что модуль чувствителен к регистру, и что имя было
изменено на MySQL, чтобы не разорвать любой существующий код, который использует
старый модуль mySQL.
Экспортируемые типы, функции и классы
Модуль MySQL экспортирует следующее:
Методы для операторных дескрипторов (STH):
- Table = STH.fetchrows([n])
Возвращает результаты запроса к DB.
Если n<0, все строки будут выбраны. Иначе будут возвращены только
следующие n строк. Значение по умолчанию должно возвратить все строки.
- Table = STH.fetchdict([n])
Аналогично STH.fetchrows(), за
исключением того, что список словарей возвращен с парами
tablename.fieldname:data.
- Table = STH.fields()
Возвращает описания поля результата запроса
STH. В настоящее время MySQLmodule понимает "pri", "notnull", "auto_inc",
"ukey" и "mkey" .
- STH.seek(n)
Двигает курсор к строке n (0
определяет первую строку). Доступен только, если результат хранится на стороне
пользователя (выбрано в DBH.selectdb). Иначе создает исключительную
ситуацию.
- Integer = STH.numrows()
Возвращает, сколько строк находятся в
результате запроса STH. Предупреждение: в действительности это число
отражает сколько записей получил клиент. Для методов хранения на стороне
сервера, это начинается с 0 и увеличивается по мере того, как
пользователь выбирает строки. Для методов хранения на стороне клиента это
число немедленно дает общее количество строк для этого запроса.
- Integer = STH.numfields()
Возвращает, сколько столбцов находятся
в результате запроса STH.
- Integer = STH.affectedrows()
Возвращает данные на сколько строк
воздействовал последний запрос. Обратите внимание, что MySQL не всегда
возвращает правильное значение.
- Integer = STH.insert_id()
Возвращает значение
auto_increment из STH-запроса insert. Обратите внимание,
что это число постоянно, пока STH существует.
- Integer = STH.eof()
Возвратит 1, если последняя строка
читалась, иначе 0. Всегда 1, если выбрано хранение данных на
стороне пользователя (значение по умолчанию).
Использование модуля MySQL
import MySQL
DBH = MySQL.connect() # localhost
print DBH.listdbs()
DBH.selectdb('test')
print DBH.serverinfo()
print DBH.stat()
DBH["create table pytest (x int, y int, s char(20))"]
DBH["insert into pytest values (1,2,'abc')"]
DBH.do("insert into pytest values (3,4,'def')")
STH = DBH.query("insert into pytest values (5,6,'ghi')")
print STH.affectedrows()
print DBH['select * from pytest']
STH = DBH.query("select * from pytest")
print STH.numrows()
print STH.fields()
print STH.fetchrows(-1)
STH.seek(0)
print STH.fetchrows(1)
print STH.fetchrows(1)
STH.seek(0)
print STH.fetchrows(2)
print STH.fetchrows(2)
print STH.numfields()
STH.seek(0)
print STH.fetchdict(1)
print STH.fetchdict()
STH = DBH.query("select * from pytest",1)
print STH.fetchdict(1)
print STH.fetchdict() # compare to previous dicts
STH = DBH.query("select * from pytest",1)
print STH.fetchrows(1)
print STH.eof()
print STH.fetchrows()
print STH.eof()
DBH['drop table pytest']
5.6.3 Примечания относительно хранения на сервере
MySQL предлагает два немного различных пути доступа к данным из базы данных.
Заданный по умолчанию метод в MySQLmodule состоит в том, чтобы использовать
хранение на стороне пользователя, то есть все запросы, включая методы курсора
(STH), выбирают все данные с сервера. К строкам обращаются через
STH.fetchrows(n) или STH.fetchdict(n) индивидуально (n=1),
блочно (n>1), все строки забирают одной порцией (n<0) или не
обращаются вообще (n=0). STH.numrows() может сообщать после
запроса сколько строк находится в результате. STH.seek(k) может
использоваться, чтобы обратиться к строкам в произвольном порядке. Недостаток
хранения на стороне пользователя состоит в том, что это использует память
пользователя, чтобы сохранить все строки. Этот способ учитывает конструкции
типа:
STH = DBH.query("select * from Foo")
N = STH.numrows()
if N > 1000: raise Hell,"You must be joking!"
for i in xrange(N):
[Data] = STH.fetchdict(1)
Так как пользователь действительно переместил к себе все строки, которые
подготовил сервер, все транзакции на этом канале прекратились, и сервер готов
принять новые команды. Это также означает, что STH.eof() всегда true
(1) для хранения данных на стороне клиента.
Хранение на стороне сервера не требует, чтобы у пользователя было много
памяти. Все записи будут перемещены на основании запроса. Однако, этот способ
имеет несколько недостатков. Так как теперь возникает возможность того, что
пользователь не забрал все строки, каждая новая команда должна проверить,
является ли свободным, то есть готов ли он принять новую команду. Если нет,
команда должна очистить канал команды, выдавая запросы на чтение в количестве,
достаточном, чтобы получить оставшиеся строки. MySQL API 3.21 не предлагает
команды abort(). STH.numrows() не знает ничего относительно того,
сколько строк были выбраны запросом, так что вышеупомянутый код примера будет
падать. STH.numrows() будет, однако, модифицироваться, поскольку строки
все-таки читаются, например:
STH = DBH.query("select * from Foo")
Data = STH.fetchrows(-1)
print "Got",STH.numrows(),"rows." # len(Data) is the same
STH.eof() имеет смысл только при хранении данных на сервере, но даже
здесь такой код полезен не всегда:
STH = DBH.query("select 1")
print STH.eof() # will print 0
Data = STH.fetchrows(1) # retrieve the row
print STH.eof() # still 0 :-(
Data = STH.fetchrows(1) # must repeat. Data will be []
print STH.eof() # now we get 1, but we already
# knew that we've hit the end
Можно было рассматривать это, как ошибку. STH.seek(k) больше не
доступен и создает исключение ("cannot seek on server"), то есть строки
должны теперь читаться последовательно.
Хранение на стороне сервера также создает дополнительную нагрузку на сервер.
В частности, он должен остаться в контакте с пользователем, пока все строки не
будут прочитаны. Так что лучше бы быстро получить все строки и не делать длинную
обработку или, что еще хуже, позволять пользователю останавливать поиск
(например, нажимая клавиши Ctrl-S в интерактивном интерфейсе).
Для тех, кто не может решить, который метод является более подходящим для
прикладной программы, MySQLmodule позволяет смешивать оба метода свободно.
Заданное по умолчанию поведение может быть установлено через
DBH.selectdb() и может быть изменено для индивидуального курсора (STH).
Обратите внимание, что незавершенные запросы будут отменены последующими
командами:
STH = DBH.query("select * from Foo",1) # use server side storage
Tables = DBH.listtables() # stomp on previous results
Data = STH.fetchrows() # nothing here anymore vs.
STH = DBH.query("select * from Foo",0) # use client side storage
Tables = DBH.listtables() # won't interfere
Data = STH.fetchrows() # no problem...
Хранение на стороне сервера также создает более сложный код в MySQLmodule.
Обычно при хранении на стороне клиента STH-курсоры независимы от дескриптора
базы данных. Немедленно после запроса все данные будут перекачаны клиенту, и
пользователь может делать все, что сочтет необходимым с дескриптором DBH. Но при
хранении на стороне сервера это становится сложным. С курсорами стороны сервера
указатель на дескриптор сохранен в дескрипторе STH. MySQLmodule удостоверится,
что этот кусок памяти не освобожден прежде, чем закроются все ожидающие
обработки курсоры. Это означает, что mysql_close() не обязательно вызван,
если база данных, обрабатываемая DBH, разрушена:
DBH = MySQL.connect() # get a DB handle
STH = DBH.query("select 1",1) # server side cursor
del DBH # mysql_close() *not* called
STH.fetchrows() # will succeed!
del STH # now mysql_close() will be called
# in DBH_dealloc()
Если Вы должны закрыть дескриптор DB немедленно, используйте
DBH.close(). Все дальнейшие попытки работать на этом дескрипторе (даже
ожидающие обработки курсоры стороны сервера) получат исключительную ситуацию
"... server has gone away". Начиная с того момента, как mySQL не может
принимать команды из последовательности, все DBH-методы должны проверить
незавершенные STH-курсоры. Чтобы обращаться к ним, дескриптор DBH хранит
указатель на STH-курсор.
5.6.4 Установка MySQLmodule-1.4 под Windows NT
Следующие инструкции основаны на информации, предоставленной Nigel Head
(nhead@houbits.com).
- Вы должны получить файл libMySQL.dll из клиентского пакета
win32-mysql версии 3.22.8 или выше, более ранние версии не будут
работать. Это доступно на www.tcx.se (или зеркалах) для свободной загрузки. Вы
должны, вероятно, переместить файл в то же самое расположение, что и Ваша
библиотека python15.dll.
- Скомпилируйте MySQLmodule-1.4 с включенным переключателем
WIN32. VC5 и файлы проекта MySQL.dsp и MySQL.dsw
включены (но, вероятно, их придется изменять, чтобы удовлетворить требованиям
Вашей среды).
- Переместите возникающий в результате файл MySQL.pyd в Ваш каталог
Python DLL.
Если что-то пойдет не так, помощь лучше всего поискать в конференции
comp.lang.python.
содержание назад вперед