dil: (Default)
dil ([personal profile] dil) wrote2011-06-05 10:44 pm

Грабли DBIные, заботливо присыпанные листвой

Ну конечно, я на них наступил.

Типичная последовательность действий при работе с базой данных: prepare(), execute(), если надо прочитать результат – fetch(), ну и в конце finish(). И до сих пор всё работало.

И тут ВНЕЗАПНО оказалось, что если выполняется не обычный SQLный оператор, а хранимая процедура (посредством EXECUTE PROCEDURE), то возникшая при выполнении ошибка после execute() никак не проявляется. Типа, всё нормально выполнилось. А вот когда потом делаешь fetch(), тут-то ошибка и вылезает. Более того, если процедура ничего не возвращает, то ошибка не проявляется вовсе. И процедура, кажется, вообще не выполняется, пока не сделаешь fetch(). Да-да, fetch(), который заведомо ничего вернуть не может.

В документации, естественно, эта замечательная особенность не описана. И я, как обычно, на эти грабли наступил и потратил полдня, чтоб понять, какого хрена оно молча не работает.

Оригинал этой записи. Комментировать можно тут или там.

Любые материалы из этого блога запрещается использовать на сайте livejournal.ru в любой форме и любом объёме

это баг DBI ?

[identity profile] kranov.livejournal.com 2011-06-06 04:56 am (UTC)(link)
http://dil.livejournal.com/913707.html

Re: это баг DBI ?

[identity profile] dil.livejournal.com 2011-06-06 05:25 am (UTC)(link)
Проклятый склероз :( Спасибо за напоминание, я про тот случай совершенно забыл. И как я его тогда обходил, тоже. Только судя по моему описанию, тогда исключения из процедуры таки возвращались сразу, а теперь только после fetch().

Ошибка, скорее, в конкретном DBD. В DBI написано, что "Calling stored procedures is currently not defined by the DBI. Some drivers, such as DBD::Oracle, support it in non-portable ways. See driver documentation for more details." А в driver documentation про это ничего нет.

Re: это баг DBI ?

[identity profile] kranov.livejournal.com 2011-06-06 07:04 am (UTC)(link)
Я посмотрел сейчас несколько фреймворков, абсолютно самостоятельно делают первый фетч, если запрос вернул курсор. А пользователю предлагают делать :

sql.execute
while not sql.eof
  begin
    A = sql.field0
    sql.fetch
  end


поэтому никто твоих граблей не видит.

Re: это баг DBI ?

[identity profile] kranov.livejournal.com 2011-06-06 07:04 am (UTC)(link)
абсолютно все

Re: это баг DBI ?

[identity profile] dil.livejournal.com 2011-06-06 07:12 am (UTC)(link)
А если не вернул, как в случае ничего не возвращающей процедуры?

Re: это баг DBI ?

[identity profile] kranov.livejournal.com 2011-06-06 07:18 am (UTC)(link)
если не вернул, ничего не делают. В этом случае и фетч нельзя сделать.

В общем-то им даже проще, предполагается что есть два типа sql, возвращающие курсор и не возвращающие, поэтому первые вызываются sql.open а вторые sql.exec. Первые всегда фетчатся вторые никогда, правда они проверяют все равно вернулся курсор или нет.

Re: это баг DBI ?

[identity profile] dil.livejournal.com 2011-06-06 07:29 am (UTC)(link)
А вот у меня получается, что ошибку становится видно только после вызова fetch(), который заведомо ничего вернуть не может.

Re: это баг DBI ?

[identity profile] kranov.livejournal.com 2011-06-06 07:31 am (UTC)(link)
В описании процедуры слово RETURNING есть или нет?

Re: это баг DBI ?

[identity profile] dil.livejournal.com 2011-06-06 08:09 am (UTC)(link)
Нет, а то б она была функцией.
В моём случае возвращать ей нечего, она добавляет запись в таблицу, предварительно проводя некоторые логические проверки, не реализованные на уровне constraint'ов. Если какие-то проверки не прошли, генерирует ошибку.

попробовал на esql.c написать

[identity profile] kranov.livejournal.com 2011-06-06 10:38 am (UTC)(link)
не получается отфетчить и процедуры

    EXEC SQL execute procedure tmp_proc(0);
    chk("exec proc");
    printf("\n");


    EXEC SQL declare c1 cursor for execute procedure tmp_proc(0);
    chk("DECLARE");
    EXEC SQL open c1;
    chk("OPEN");
    EXEC SQL fetch c1 into $pa;
    chk("FETCH");
    if (sqlca.sqlcode == 0)
        printf("Value selected from 'c' = %d.\n", pa);
    printf("\n");



exec proc:
	SQLCODE =    -746: tmp_proc-raised exception

DECLARE was successful

OPEN:
	SQLCODE =    -685: Function () returns too few values.

FETCH:
	SQLCODE =    -400: Fetch attempted on unopen cursor.

и на самом деле ошибка идет только при фетче

[identity profile] kranov.livejournal.com 2011-06-06 10:40 am (UTC)(link)
    EXEC SQL execute procedure tmp_func(0);
    chk("exec");
    printf("\n");


    EXEC SQL declare c cursor for execute procedure tmp_func(0);
    chk("DECLARE");
    EXEC SQL open c;
    chk("OPEN");
    EXEC SQL fetch c into $pa;
    chk("FETCH");
    if (sqlca.sqlcode == 0)
        printf("Value selected from 'c' = %d.\n", pa);
    printf("\n");



exec:
	SQLCODE =    -684: Function (informix.tmp_func) returns too many values.

DECLARE was successful
OPEN was successful

FETCH:
	SQLCODE =    -746: tmp_func-raised exception

Re: и на самом деле ошибка идет только при фетче

[identity profile] dil.livejournal.com 2011-06-06 09:26 pm (UTC)(link)
вот...