Використання подій 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" );
}
Функція astRoutine обовязково повинна мати виклик:

    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.
    - обовязково потрібно вказати команду SQLSetConnectAttr:
    // 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);
	}
    }	

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