Столкнулся с проблемой,
АИБС "МАРК-SQL" для школьных библиотек 1.5.4 не работала с
MySQL, а разработчики заверяли, что программа не совместима с
MySQL.
Решил проанализировать запросы, которые ходят между программой и сервером при помощи
MySQL-Proxy [
2].
И сразу увидел некорректный запрос с использованием зарезервированного слова
"SEPARATOR":
SELECT TAG,SUBTAG,FLAGS,SEPARATOR,CAPTION FROM TAG
После добавления обратных кавычек к зарезервированному слову
SEPARATOR [
1] запрос выполнился в
MySQL без проблем:
SELECT TAG,SUBTAG,FLAGS,`SEPARATOR`,CAPTION FROM TAG
Значит для нормальной работы программы
АИБС "МАРК-SQL" для школьных библиотек 1.5.4 нужно ловить каждый запрос, и если требуется, экранировать зарезервированные слова
MySQL на лету. С этой задачей вполне успешно справится
MySQL-Proxy [
2] с поддержкой
lua [
4] скриптов.
После установки, запуска
MySQL-Proxy и подключения
lua скрипта:
function MyDebug(str)
-- enable\disable debug messages print to stdout.
local dbg = 0
if dbg == 1 then
print('[DEBUG] ' .. str)
end
end
function read_query(packet)
local file = io.open('/var/log/mysql-proxy.log', 'a')
local q = string.sub(packet, 2)
MyDebug('Orig. Query: ' .. q)
file:write('Orig. Query: ' .. q .. '\n')
-- Таблица зарезервированных слов MySQL.
-- http://dev.mysql.com/doc/mysqld-version-reference/en/mysqld-version-reference-reservedwords-5-7.html
q = string.gsub(q, '(SEPARATOR)', '`%1`')
q = string.gsub(q, '(INT%d)', '`%1`')
-- Исправление поискового запроса (захардкоженного в marcp.exe) и регистронезависимый поиск.
local _,_,str1,str2,str3 = string.find(q,"SELECT TERM,CNT FROM (.-) WHERE TERM%s*>=%s*('.-')%s(.*)") -- TODO: проверить всегда ли передаются кавычки в поле WHERE TERM >= ''.
if str1 ~= nil and str2 ~= nil and str3 ~= nil then
MyDebug('str1: ' .. str1); MyDebug('str2: ' .. str2); MyDebug('str3: ' .. str3)
str2 = string.gsub(str2, "^'", '') -- Delete start '.
str2 = string.gsub(str2, "'$", '') -- Delete end '.
MyDebug('Fixed str2: ' .. str2)
q = string.format("SELECT TERM,CNT FROM %s WHERE LOWER(TERM) LIKE LOWER('%s%%') %s",str1,str2,str3)
end
-- Исправляем кавычки в запросе.
q = string.gsub(q, 'SELECT CODE,NAME FROM%s+"(.+)"%s*(.*)', 'SELECT CODE,NAME FROM `%1` %2') -- Del start '.
MyDebug('Fixed query: ' .. q .. '\n')
file:write('Fixed query: ' .. q .. '\n')
file:close()
proxy.queries:append(2, string.char(proxy.COM_QUERY) .. q, {resultset_is_needed = true})
return proxy.PROXY_SEND_QUERY
end
function read_query_result(inj)
if inj.id == 1 and inj.resultset.rows ~= nil then
local file = io.open('/var/log/mysql-proxy.log', 'a')
for row in inj.resultset.rows do
local i = 1
local fields = {}
while row[i] do
if row[i] == row then break end
file:write('Response field: ' .. inj.resultset.fields[i].name .. ' => ' .. row[i] .. '\n')
i = i + 1
end
end
file:close()
return proxy.PROXY_IGNORE_RESULT
end
end
В ходе экспериментов для подключения к БД был получен такой конфиг
dns.ini для работы с
MySQL:
Универс|DRIVER=MySQL ODBC 5.2 Unicode Driver;NO_SSPS=1;MULTI_STATEMENTS=1;AUTO_RECONNECT=1;LOG_QUERY=0;IGNORE_SPACE=1;COMPRESSED_PROTO=1;NO_PROMPT=1;CHARSET=cp1251;DATABASE=marcsql;SERVER=192.168.4.26;PORT=4040;UID=marcsql;PASSWORD=password;
Описание некоторых опций:
- NO_SSPS=1; - Включение подстановок на стороне клиента, а не сервера (т.е. запросы в которых есть знаки "?").
- COMPRESSED_PROTO=1; - Включение сжатия (немного повышается скорость работы с большими результатами запросов).
- NO_PROMPT=1; - Отключение окна запроса, при переподключении к БД.
- CHARSET=cp1251; - Кодировка в которой будет идти обмен между сервером и клиентом (программа для Windows и работает в cp1251).
- BASE=marcsql; - Имя базы.
- SERVER=192.168.4.26; - Адрес сервера MySQL-Proxy.
- PORT=4040; - Порт на котором работает MySQL-Proxy.
- UID=marcsql; - Логин, для подключения к БД.
- PASSWORD=password; - Пароль, для подключения к БД.
Но на этом рано останавливаться, нужно еще привести в порядок запросы в файлах программы да и самой программе объяснить, что мы будем использовать MySQL, а не MS Access:
Добавляем в конец информацию о
MySQL в
bin/db.ini:
[Mysql]
ScriptPath=..\sql\Mysql
CreateTempTable=CREATE TABLE &Table&TabSuf(DOC_ID INTEGER PRIMARY KEY)
DictQuery=SELECT TERM,CNT FROM &Table
В файле
DbmsParams.ini:
[Mysql]
$VARCHAR=VARCHAR
$COUNTER=INT AUTO_INCREMENT
$DOUBLE=FLOAT
$LONGTEXT=TEXT
$LONGBINARY=TEXT
$MIDTEXT=TEXT
$TEXT=TEXT
$DBPREF_=marcsql.
$COLUMN=
$TOP1000=
$ALTERCOLUMN=ALTER COLUMN
Добавляем информацию о возрастном маркере в
EditMap.ini:
[333a]
EditForm=TECombo
ComboValues=0+ Для дошкольного возраста,6+ Для младшего школьного возраста,12+ Для среднего школьного возраста,16+ Для старшего школьного возраста,18+ Для взрослых
TagValues=Для дошкольного возраста,Для младшего школьного возраста,Для среднего школьного возраста,Для старшего школьного возраста,Для взрослых
Separator=,
OnlyFormEdit=Yes
DefaultMenu=NO
[200e]
EditForm=TECombo
ComboValues=[0+],[6+],[12+],[16+],[18+]
TagValues=[0+],[6+],[12+],[16+],[18+]
Separator=,
OnlyFormEdit=Yes
DefaultMenu=NO
Добавляем редактор
LibreOffice в файле
marc.ini в секцию
[Editors]:
[Editors]
LibreOffice=swriter.exe
Изменяем файл
phase.ini под потребности нашей школы:
[Книги]
CreateTags=245anpbco,100ae,700ae,653a,520a,020ac,090ax,084a,110a,260abc,440a,650a,200e,300ab,333a,952be,526cde,250a,998а,773b,773d,773t,773g,013ddee
EditTags=245,100,700,653,520,020,090,084,110,200,260,440,650,300,333,952,526,250,998,773
ViewTags=100a,100e,700a,700e,245a,245b,245o,245p,245n,260a,260b,260c,300a,300b,333a,440a,020a,020c,650a,090a,090x,084a
ShowAllTags=YES
RecType=a
BibLevel=m
В файле
RdrData.ini, в секции
[Common] исправляем запрос
LastReaderQuery:
LastReaderQuery=SELECT RDR_ID FROM &ReadersPrefREADERS ORDER BY LEN(RDR_ID) DESC, RDR_ID DESC LIMIT 1
Теперь
SQL файлы, заходим в каталог
sql:
Была проблема при удалении записей из таблиц словарей, исправляется в файле
delidx.sql:
Ищем строку:
DELETE FROM &Table WHERE CNT=0;
Заменяем её на:
DELETE FROM &Table WHERE CNT<=0;
Исправляем проблему с удалением данных из таблицы
METAIDX ищем в файле
dropdict.sql строку:
DELETE FROM METAIDX WHERE NAME='&Table.TERM'
Заменяем её на:
DELETE FROM `METAIDX` WHERE `NAME` = '&Table.`TERM`';
DELETE FROM `METAIDX` WHERE `NAME` = '&Table.TERM';
Теперь создаем каталог
Mysql и копируем все файлы из каталога
Access в созданный каталог
Mysql (напоминаю, мы должны быть в каталоге
sql). Другими словами мы должны получить копию папки
sql/Acess, но по адресу
sql/Mysql.
И приводим в порядок файлы в каталоге
sql/Mysql:
Файл
crdict.sql приводим к такому виду:
INSERT INTO METAIDX(`NAME`,`TYPE`,`MAXLEN`,`TAGS`,`CAPTION`,`SEP`) VALUES('&Table.`TERM`','&Type',&MaxLen,'&Taglist','&Caption','&Sep');
CREATE TABLE &Table (
`IDX_ID` INT AUTO_INCREMENT NOT NULL PRIMARY KEY,
`TERM` VARCHAR(&MaxLen),
`CNT` INTEGER);
CREATE TABLE `&TableX` (
`IDX_ID` INTEGER NOT NULL,
`DOC_ID` INTEGER NOT NULL,
FOREIGN KEY(`IDX_ID`) REFERENCES `&Table`(`IDX_ID`));
CREATE INDEX &TableXB ON &TableX(`DOC_ID`);
CREATE INDEX &TableXC ON &TableX(`IDX_ID`);
Файл
rebuild.sql приводим к такому виду:
DROP TABLE IF EXISTS `IDXTEMP`;
CREATE TABLE `IDXTEMP` (`DOC_ID` INTEGER, &Term Text);
$BUILD;
$Инициализация таблицы индексов;
DELETE FROM &TableX;
DELETE FROM &Table;
$Заполнение таблицы индексов;
INSERT INTO &Table(&Term,CNT) SELECT &Term, Count(&Term) FROM IDXTEMP GROUP BY &Term;
$Заполнение таблицы перекрестных ссылок;
INSERT INTO &TableX(IDX_ID,DOC_ID) SELECT &Table.IDX_ID,IDXTEMP.DOC_ID FROM &Table,IDXTEMP
WHERE IDXTEMP.&Term=&Table.&Term;
$Удаление временной таблицы;
DROP TABLE IDXTEMP;
На этом изменения в файлах программы окончены. Пришло время конвертировать базу, я конвертировал при помощи
BullZip Access To MySQL[
6], на выходе получил
.sql файл, который был скопирован на сервер и залит в БД. На этом все.
1.
Reserved Words in MySQL 5.0
2.
Download MySQL Proxy
3.
Download Connector/ODBC
4.
20.1 – Pattern-Matching Functions
5.
MySQL On air. Мониторим SQL запросы
6.
Access To MySQL
7.
MySQL и MarcSQL
8.
Версия АИБС "МАРК-SQL" для школьных библиотек находится в свободном доступе для российских школ.