пятница, 3 декабря 2010 г.

[prog] OTL4, MSSQL, bulk insert, триггер на вставку и ошибка 24000

Нужно было сделать быструю вставку записей в табличку MSSQL. Задействовал нестандартную SQL-ную фичу с конструкцией insert into … select … union all … select … union all. Работал через OTL4 и сделал пул otl_nocommit_stream-ом (с разным количеством строк внутри). Например, если в пуле были otl-потоки для 20, 10, 5, 2 и 1-ой строки, а нужно было вставить 59 строк, то два раза использовался поток на 20 записей, один раз поток для 10 строк, один раз поток для 5 строк и два раза поток для 2-х строк.

Все работало хорошо до тех пор, пока в базе не появился триггер на вставку в эту таблицу. Триггер должен был срабатывать на insert и добавлять строки в другую таблицу. Но после того, как он срабатывал, у меня при выборе следующего otl-потока из пула возникала ошибка 24000: [Microsoft][SQL Server Native Client 10.0]Invalid cursor state.

Поиск по Интернету показал, что такие вещи при работе через ODBC уже случались. И в качестве workaround-а рекомендовали после каждого SQLExecute для вставки в основную таблицу делать обращение к SQLMoreResults.

Легко это сделать когда ты сам осуществляешь вызовы SQLExecute. Но за меня это делает OTL. Да еще делает это неявно для меня, внутри своих otl-потоков. Так что как применить данную рекомендацию поначалу было непонятно.

К счастью, OTL доступен в исходных текстах (более того, весь OTL – это один большой заголовочный файл). Посредством поиска удалось выяснить, что обращение к SQLMoreResults в OTL есть. Происходит оно внутри метода sql_row_count(), а sql_row_count() вызывается после обращения к SQLExecute.

Но sql_row_count() использует SQLMoreResults не всегда, а только когда определен макрос OTL_ODBC_ALTERNATE_RPC. И в документации к OTL_ODBC_ALTERNATE_RPC сказано, что он предназначен для PostgreSQL:

This #define should be used with the PostgreSQL ODBC driver. The driver returns as many row counts via SQLRowCount() calls as there are rows in a batch INSERT statement. The #define enables a loop that fetches all individual row counts and sums them up (+=). As a result, otl_stream::get_rpc() returns the total, which is correct for PostgreSQL. Normally, commercially available ODBC drivers return a single row count on a batch INSERT.

Тем не менее, определил макрос OTL_ODBC_ALTERNATE_RPC и все заработало!

Мораль сей басни – доступные исходники сторонних библиотек – это must have. :)

Комментариев нет: