Использование событий Firebird базы данных

   За основу возьмем базу employee.fdb и в ней таблицу SALES. Для этой таблицы есть триггер post_new_order, в котором вызывается команда

    Следует уточнить, что триггер срабатывает после вставки записи в таблицу. Теперь попробуем решить следующую задачу, нам нужно после вставки новой записи в таблицу SALES изменить поле статуса записи с состояния 'new' в 'open'. Предположим что запись была успешно вставлена и сервер выполнил генерацию события '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++;
которая является примитивным механизмом синхронизации для рабочего потока, который собственно будет выполнять нашу основную задачу. Схему его работы мы рассмотрим ниже.
    - в строке соединения или при формировании DNS нужно устанавливать опцию NOWAIT в положение OFF.
    - обязательно нужно указать следующее утверждение:
    // 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);
	}
    }	

Для более подробного ознакомления с этими и другими возможностями пожалуйста, рассмотрите примеры.