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