EnglishУкраїнськаmRussian
Login/New
Topic with no new replies

Сохранённые процедуры MySQL в OpenSCADA


Author Message
Written on: 28. 11. 2013 [13:16]
adsum
Andrew S
Topic creator
registered since: 03.10.2013
Posts: 31
Есть интерес запрашивать данные из MySQL через сохранённые процедуры.
Сделал процедуру, которая возвращает данные в виде одной таблички (собственно, с учётом возможностей MBD::sqlReq()). При запросе через закладку SQL модуля MySQL и при запросе через функцию dbReqSQL корректно получаю результат, но получаю и ошибку "Ошибка 2014 запроса к БД: Commands out of sync; you can't run this command now". Для удобства я поменял в функции MBD::sqlReq строку формирования сообщения, что бы возвращался код ошибки MySQL:
throw TError(TSYS::DBRequest,nodePath().c_str(),_("Query to DB error %d: %s"),mysql_errno(&connect),mysql_error(&connect));
После этого никакие запросы не работают и этот модуль надо отключить и подключить к БД.
Теоретически это логично, т.к. в mysql_real_connect не установлен флаг CLIENT_MULTI_RESULTS, а в документации написано, что для запроса CALL он нужен, но я написал небольшую программку, которая отправляет запрос к MySQL и печатает ответ (во вложении), У неё так же флаг 0. Эта программа корректно выполняет и SELECT и CALL, т.е. в irez = 0.
Не могу понять, почему при одном наборе команд столь разный реззультат. Нет ли у кого-нибудь опыта в по данной теме?

[This article was edited 1 times, at last 28.11.2013 at 13:17.]
Attachment

mysqltestoscada.c (File type: text/x-csrc, Size: 1.29 kilobytes) — 2127 downloads
Written on: 28. 11. 2013 [15:51]
adsum
Andrew S
Topic creator
registered since: 03.10.2013
Posts: 31
Появилось некоторое понимание ситуации после того, как переделал свою программку на возможность отправить несколько запросов в рамках одного подключения (во вложении).
Хотя CALL и возвращает корректно данные после запроса, ошибка 2014 появляется при отправке следующего запроса, В случае модуля, это происходит при переходе на закладку "Базы данных", которая отправляет запрос SHOW TABLES FROM `oscada`, ну а для функции - при следующем запросе.
Насколько я понял, это связано с тем, что после mysql_query() не выполнена mysql_next_result()
Непонятна только роль флага CLIENT_MULTI_RESULTS, а точнее его отсутствия.
Attachment

mysqltestoscada.c (File type: text/x-csrc, Size: 1.45 kilobytes) — 2197 downloads
Written on: 28. 11. 2013 [16:48]
roman
Roman Savochenko
Moderator
Contributor
Developer
registered since: 12.12.2007
Posts: 3750
"adsum" wrote:

Насколько я понял, это связано с тем, что после mysql_query() не выполнена mysql_next_result()

После mysql_real_query()!
Не выполняется mysql_next_result() поскольку не нужен ибо не включен режим отправки множества запросов в одном сеансе.

"adsum" wrote:

Непонятна только роль флага CLIENT_MULTI_RESULTS, а точнее его отсутствия.

Изначально по причине ненадобности, а в последствии по причине того, что поддержка отправки множества запросов в одном сеансе требует усложнения обработки результата, та-же mysql_next_result(), и как следствие менее стройного и даже запутанного представления этого результата для последующей обработки в скриптах OpenSCADA.

Конечно можно просто включить этот режим, а обрабатывать только первый результат, что как минимум откроет возможность отправлять множество незапрашивающих данные запросов, ну и в Вашем случае.

P.S. Уже третий раз задаётся вопрос на эту тему! Вы бы рядом поглядывали!

Learn, learn and learn better than work, work and work.
Written on: 29. 11. 2013 [11:35]
adsum
Andrew S
Topic creator
registered since: 03.10.2013
Posts: 31
После mysql_real_query()!
очепятка :)
Изначально по причине ненадобности, а в последствии по причине того, что поддержка отправки множества запросов в одном сеансе требует усложнения обработки результата, та-же mysql_next_result(), и как следствие менее стройного и даже запутанного представления этого результата для последующей обработки в скриптах OpenSCADA.
Написав "Непонятна только роль флага CLIENT_MULTI_RESULTS, а точнее его отсутствия", я имел ввиду, что несмотря на отсутствие флага CLIENT_MULTI_RESULTS установленная у меня версия MySQL всё равно позволяет выполнить запросы, которые требуют наличие этого флага. Я в своей программке проверил: при флаге 0 Multiple Statement Execution вполне себе выполняются.
Конечно можно просто включить этот режим, а обрабатывать только первый результат, что как минимум откроет возможность отправлять множество незапрашивающих данные запросов, ну и в Вашем случае.
Да, я так и сделал. добавление в конце функции следующего кода решило проблему:
void MBD::sqlReq( const string &ireq, vector< vector<string> > *tbl, char intoTrans )
{
...
mysql_free_result(res);
while((irez = mysql_next_result(&connect)) == 0) mysql_free_result(mysql_store_result(&connect));
if(irez > 0) throw TError(TSYS::DBRequest,nodePath().c_str(),_("Next result error: %s"),mysql_error(&connect));
}

Результат тестирования:
функция dbReqSQL - для сохранённой процедуры, в которой есть несколько запросов, ожидаемо возвращается результат первого. если в сохранённой процедуре есть ошибка - возвращается пустой массив (независимо от того, в каком из запросов была ошибка).
модуль MySQL - для сохранённой процедуры, в которой есть несколько запросов, возвращается результат первого. если в сохранённой процедуре есть ошибка - выводится сообщение об ошибке, поле "Результат" не обновляется. При любом действии, которое вызывает обновления поля (смена закладки, редактирование поля "Запрос" и т.п.), в него выводится результат первого запроса процедуры (если, конечно, ошибка была не в первом запросе).
Written on: 29. 11. 2013 [14:26]
roman
Roman Savochenko
Moderator
Contributor
Developer
registered since: 12.12.2007
Posts: 3750
"adsum" wrote:

Конечно можно просто включить этот режим, а обрабатывать только первый результат, что как минимум откроет возможность отправлять множество незапрашивающих данные запросов, ну и в Вашем случае.
Да, я так и сделал. добавление в конце функции следующего кода решило проблему:

Решает, но не полностью и только в случае если первый-же запрос возвращает результат.

В общем включил режим множественности запросов и выгрузил в репозиторий!

"adsum" wrote:

...поле "Результат" не обновляется.

Да, по ошибкам запроса конфигуратор остальные поля не обновляет.

Learn, learn and learn better than work, work and work.
Written on: 29. 11. 2013 [15:22]
adsum
Andrew S
Topic creator
registered since: 03.10.2013
Posts: 31
Решает, но не полностью и только в случае если первый-же запрос возвращает результат.
Думал как сохранить совместимость и получать несколько запросов. Есть такая мысль. Оставить всё тот-же двумерный массив, в котором между результатами запроса помещать пустой массив. Например:
<TArrayObj>
<TArrayObj> <str>1</str> <str>Device 1</str> </TArrayObj>
<TArrayObj> <str>TypeCode</str> <str>Code</str> <str>Status</str> <str>Value</str> </TArrayObj>
<TArrayObj> <str>Device</str> <str>DeviceAlarm</str> <str>1</str> <str>Normal</str> </TArrayObj>
<TArrayObj> <str>Device</str> <str>DeviceAlarm</str> <str>2</str> <str>Warn</str> </TArrayObj>
<TArrayObj> <str>Device</str> <str>DeviceAlarm</str> <str>3</str> <str>Error</str> </TArrayObj>
<TArrayObj> <str>Device</str> <str>DeviceAlarm</str> <str>4</str> <str>Alarm</str> </TArrayObj>
<TArrayObj> </TArrayObj>
<TArrayObj> <str>ID</str> <str>Name</str> </TArrayObj>
<TArrayObj> <str>1</str> <str>Device 1</str> </TArrayObj>
<TArrayObj> <str>2</str> <str>Device 2</str> </TArrayObj>
<TArrayObj> <str>3</str> <str>Device 3</str> </TArrayObj>
</TArrayObj>
Соответственно по цветам: результат первого запроса и результат второго запроса, а между - разделитель.
У ранее созданного кода будет только один результат запроса, и совместимость сохранится. Кто захочет множественные запросы, должен при обработке проверять длину массива.
Сначала собирался именно так и делать, но остановило то, что ещё не в достаточной мере изучил код (в частности tbds.cpp)

[This article was edited 1 times, at last 29.11.2013 at 15:23.]
Written on: 30. 11. 2013 [16:37]
roman
Roman Savochenko
Moderator
Contributor
Developer
registered since: 12.12.2007
Posts: 3750
"adsum" wrote:

Решает, но не полностью и только в случае если первый-же запрос возвращает результат.
Думал как сохранить совместимость и получать несколько запросов. Есть такая мысль. Оставить всё тот-же двумерный массив, в котором между результатами запроса помещать пустой массив.
...
Сначала собирался именно так и делать, но остановило то, что ещё не в достаточной мере изучил код (в частности tbds.cpp)

Как изучите поймёте, что SYS.sqlReq() это вершина айсберга, а под ней виртуальная функция БД TBD::SQLReq(), которая возвращает массив в виде "vector< vector<string> >", но проблема даже не в этом, поскольку можно тоже пустой массив строк как разделитель использовать, а больше в том, что те кто эту функцию использует, кроме "SYS.sqlReq()" ожидают соблюдение размерности массивов всех строк, как, например, вывод результата в той же вкладке "SQL". Если конкретно вкладки "SQL" то там определённо возможен вывод только одного результата, а добавить формирователю результата для неё проверку на пустую строку можно.

В общем подумаю и погляжу где там TBD::SQLReq() ещё используется и насколько такое поведение реально.

Learn, learn and learn better than work, work and work.



7043