За основу возьмем базу 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); } }
Для более подробного ознакомления с этими и другими возможностями пожалуйста, рассмотрите примеры.