За основу возьмем базу employee.fdb и в ней таблицу SALES.
Для этой таблицы есть триггер post_new_order, в котором вызывается
команда
- подключить файл описания интерфейса ODBC, обеспечивающего обработку событий Firebird базы данных.
#include "OdbcUserEvents.h"- указать в таблице eventInfo какие события нас интересуют. В нашем случае нас будет интересовать только событие 'new_order'. Событие 'change_order' указано только с целью подчеркнуть, что можно контролировать одновременно не одно событие, а много.
ODBC_EVENT_INFO eventInfo[] =
{
INIT_ODBC_EVENT("new_order"),
INIT_ODBC_EVENT("change_order")
};
- создать структуру MyUniqueData хранения данных
необходимых для решения задачи. В нашем случае поле
event_flag будет сигнализировать, что событие от сервера
доставлено и мы можем приступать к работе.
struct MyUniqueData
{
int event_flag;
//... other define for use into astRoutine
};
- создать callback функцию astRoutine, которая будет
активизироваться в случае возникновения событий, которые
мы определили для контроля в таблице eventInfo.
void astRoutine( void *userEventsInterfase, short length, char * updated )
{
PODBC_USER_EVENTS_INTERFASE userInterfase = (PODBC_USER_EVENTS_INTERFASE)userEventsInterfase;
SQLSetConnectAttr( userInterfase->hdbc, SQL_FB_UPDATECOUNT_EVENTS, (SQLPOINTER)updated, SQL_LEN_BINARY_ATTR( length ) );
MyUniqueData &myData = *(MyUniqueData*)userInterfase->userData;
myData.event_flag++;
printf( "ast routine was called\n" );
}
Функция обязательно должна иметь вызов:
SQLSetConnectAttr( userInterfase->hdbc,
SQL_FB_UPDATECOUNT_EVENTS,
(SQLPOINTER)updated,
SQL_LEN_BINARY_ATTR( length ) );
Он необходим для обновления состояния событий в нашей структуре
eventInfo. Которая, имеет поле countEvents указывающее
на общее количество срабатываний события и поле bool changed;
принимающее состояние true когда при обновлении состояния
событий счетчик countEvents до обновления и после отличается.
В нашем случае, нам интересен сам факт регистрации события, по этому
мы выполняем команду:
myData.event_flag++;
которая является примитивным механизмом синхронизации для
рабочего потока, который собственно будет выполнять нашу основную
задачу. Схему его работы мы рассмотрим ниже.
// Specify that the Firebird ODBC Cursor is always used, then connect.
SQLSetConnectAttr( hdbc, SQL_ATTR_ODBC_CURSORS, (SQLPOINTER)SQL_CUR_USE_DRIVER, 0 );
SQLConnect( hdbc, (UCHAR*)connectString, SQL_NTS, NULL, 0, NULL, 0 );
- выполнить подготовку SQL запроса курсора, он нам необходим для
демонстрации механизма событий. В Вашем случае он будет другим.
SQLPrepare( stmtSel, (UCHAR*)
"SELECT po_number"
" FROM sales"
" WHERE order_status = 'new'"
" FOR UPDATE",
SQL_NTS );
- установить курсору имя 'C', это имя будет
использоваться для выполнения SQL запроса на модификацию.
char *cursor = "C";
SQLSetCursorName( stmtSel, (UCHAR*)cursor, sizeof( cursor ) );
- выполнить подготовку SQL запроса на модификацию, он нам
необходим для демонстрации механизма событий. В Вашем случае
он будет другим.
SQLPrepare( stmtUpd, (UCHAR*)
"UPDATE sales"
" SET order_status = 'open'"
" WHERE CURRENT OF C",
SQL_NTS );
- выполнить инициализацию структуры ODBC_EVENTS_BLOCK_INFO,
которая обеспечит работу интерфейса событий и передать ее драйверу.
myData.event_flag = 0;
ODBC_EVENTS_BLOCK_INFO eventsBlockInfo = INIT_EVENTS_BLOCK_INFO( hdbc, eventInfo, astRoutine, &myData );
SQLSetConnectAttr( hdbc, SQL_FB_INIT_EVENTS, (SQLPOINTER)&eventsBlockInfo, SQL_LEN_BINARY_ATTR((int)sizeof( eventsBlockInfo )) );
- сообщить соединению, что мы готовы принимать события.
SQLSetConnectAttr( hdbc, SQL_FB_REQUEUE_EVENTS, (SQLPOINTER)NULL, 0 );
- запустить обработчик событий.
while ( !iret )
{
// If the event was triggered, reset the buffer and re-queue
if ( myData.event_flag )
{
myData.event_flag = 0;
// Check for first ast_call. isc_que_events fires
// each event to get processing started
if ( first )
first = 0;
else
{
// Select query to look at triggered events
ret = SQLExecute( stmtSel );
for (;;)
{
ret = SQLFetch( stmtSel );
if ( ret == SQL_NO_DATA_FOUND )
break;
ret = SQLExecute( stmtUpd );
}
/* Re-queue for the next event */
SQLSetConnectAttr( hdbc, SQL_FB_REQUEUE_EVENTS, (SQLPOINTER)NULL, 0 );
/* This does not block, but as a sample program there is nothing
** else for us to do, so we will take a nap
*/
Sleep(1000);
}
}
Для более подробного ознакомления с этими и другими возможностями пожалуйста, рассмотрите примеры.