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