Опубликован: 20.12.2005 | Уровень: специалист | Доступ: платный | ВУЗ: Московский государственный университет имени М.В.Ломоносова
Лекция 8:

Именованные курсоры

< Лекция 7 || Лекция 8: 12 || Лекция 9 >

Асинхронное выполнение функций

По умолчанию ODBC-драйверы применяют синхронное выполнение функций, когда приложение ожидает завершения выполнения функции. При асинхронном выполнении функций ODBC-драйвер возвращает управление приложению до окончательного завершения выполнения функции. В этом случае приложение продолжает выполняться дальше и имеет возможность синхронного или асинхронного вызова других функций.

В зависимости от используемого источника данных асинхронное выполнение осуществляется на основе оператора или соединения. Режим асинхронного выполнения можно определить при вызове функции SQLGetInfo с параметром, равным SQL_ASYNC_MODE: для асинхронного выполнения уровня соединения возвращается значение SQL_AM_CONNECTION, а для асинхронного выполнения уровня оператора - значение SQL_AM_STATEMENT.

Для определения того, что функция будет выполняться асинхронно на уровне оператора, следует вызовом функции SQLSetStmtAttr установить значение атрибута SQL_ATTR_ASYNC_ENABLE равным SQL_ASYNC_ENABLE_ON. Для определения асинхронного выполнения на уровне соединения следует вызовом функции SQLSetConnectAttr устанавливать значение атрибута соединения SQL_ATTR_ASYNC_ENABLE равным SQL_ASYNC_ENABLE_ON. При использовании асинхронного режима уровня соединения все операторы, ассоциируемые с данным соединением, могут выполняться в асинхронном режиме.

Количество максимально допустимых параллельно и асинхронно выполняемых операторов для конкретного драйвера определяется вызовом функции SQLGetInfo с параметром, равным SQL_MAX_ASYNC_CONCURRENT_STATEMENTS.

При асинхронном выполнении функции до завершения ее выполнения каждый повторный вызов этой функции с теми же параметрами возвращает код ответа SQL_STILL_EXECUTING. Так, повторяя вызовы через некоторый интервал времени, можно определять момент, когда будет завершено асинхронное выполнение функции.

Например:

SQLHSTMT  hstmt1;         // Дескриптор оператора
SQLRETURN rc;

// Устанавливаем режим асинхронного выполнения
SQLSetStmtAttr(hstmt1, SQL_ATTR_ASYNC_ENABLE, 
               SQL_ASYNC_ENABLE_ON, 0);
// Выполняем асинхронно оператор SELECT
while ((rc=SQLExecDirect(hstmt1,
        "SELECT * FROM TBL1",SQL_NTS)) == 
                              SQL_STILL_EXECUTING) 
 {
   // Действия, осуществляемые во время асинхронного
   // выполнения оператора SELECT (нельзя использовать
   // дескриптор hstmt1)
}
// Асинхронное выполнение завершено

Во время асинхронного выполнения оператора приложение может осуществлять синхронный или асинхронный вызов функций для других дескрипторов оператора. Для самого дескриптора оператора в период асинхронного выполнения может осуществляться только повторный вызов выполняемой функции или функций SQLCancel (прерывание вызова), SQLGetDiagField и SQLGetDiagRec (но только заголовки полей).

Для соединения, ассоциированного с асинхронно выполняемым оператором, возможен вызов следующих функций: SQLAllocHandle (для размещения дескриптора оператора), SQLGetDiagField, SQLGetDiagRec, SQLGetFunctions.

Пример:

SQLHDBC       hdbc1, hdbc2;  // Дескрипторы соединения
SQLHSTMT      hstmt1, hstmt2, hstmt3;
SQLCHAR *     SQLStatement = "SELECT * FROM TBL1";
SQLUINTEGER   InfoValue;
SQLRETURN     rc;
// Создание дескрипторов операторов
  SQLAllocHandle(SQL_HANDLE_STMT, hdbc1, &hstmt1);
  SQLAllocHandle(SQL_HANDLE_STMT, hdbc1, &hstmt2);
  SQLAllocHandle(SQL_HANDLE_STMT, hdbc2, &hstmt3);
// Устанавливаем для hstmt1 режим асинхронного 
// выполнения
SQLSetStmtAttr(hstmt1, SQL_ATTR_ASYNC_ENABLE, 
               SQL_ASYNC_ENABLE_ON, 0);
// Асинхронное выполнение для дескриптора hstmt1
while ((rc = SQLExecDirect(hstmt1, SQLStatement, 
        SQL_NTS)) == SQL_STILL_EXECUTING) {
   // Следующий вызов вернет код ответа HY010, 
   // т.к. дескриптор оператора hstmt1 пока " занят",
   // а второй вызов использует дескриптор hdbc1, в 
   // котором размещен дескриптор оператора hstmt1 
   SQLExecDirect(hstmt1, SQLStatement, SQL_NTS);      
   SQLGetInfo(hdbc1, SQL_UNION, 
               (SQLPOINTER) &InfoValue, 
               0, NULL);   // Ошибка с кодом HY010
  // Следующие операторы будут выполнены без ошибки,
  // т.к для hdbc1 применяются дескрипторы оператора, 
  //не используемые асинхронно в настоящий момент
   SQLExecDirect(hstmt2, SQLStatement, SQL_NTS); 
   SQLTables(hstmt3,NULL,0,NULL,0,NULL,0,NULL,0);   
   SQLGetInfo(hdbc2, SQL_UNION, 
              (SQLPOINTER) &InfoValue, 0, NULL);   
}

Для отключения режима асинхронного выполнения операторов следует вызвать функцию SQLSetStmtAttr со значением атрибута SQL_ATTR_ASYNC_ENABLE, равным SQL_ASYNC_ENABLE_OFF (для уровня оператора) или функцию SQLSetConnectAttr со значением атрибута SQL_ATTR_ASYNC_ENABLE, равным SQL_ASYNC_ENABLE_OFF (для уровня соединения).

При вызове функции SQLGetDiagField, применяемой для получения диагностической информации, с дескриптором оператора, используемого асинхронно, возвращаются следующие значения:

  • поля SQL_DIAG_CURSOR_ROW_COUNT, SQL_DIAG_DYNAMIC_FUNCTION, SQL_DIAG_DYNAMIC_FUNCTION_CODE, SQL_DIAG_ROW_COUNT возвращают неопределенное значение;
  • поле SQL_DIAG_NUMBER возвращает значение 0;
  • поле SQL_DIAG_RETURNCODE возвращает значение SQL_STILL_EXECUTING ;
  • все поля записи возвращают SQL_NO_DATA.

Следующий пример иллюстрирует процесс получения диагностической информации:

SQLCHAR       SqlState[6], SQLStmt[100], Msg[SQL_MAX_MESSAGE_LENGTH];
SQLINTEGER    NativeError;
SQLSMALLINT   i, MsgLen;
SQLRETURN     rc1, rc2;
SQLHSTMT      hstmt;
// В SQLStmt содержится SQL-оператор
// Выполнение оператора и получение информации
// о возникающих ошибках или предупреждениях
rc1 = SQLExecDirect(hstmt, SQLStmt, SQL_NTS);
if ((rc1 == SQL_SUCCESS_WITH_INFO) || 
     (rc1 == SQL_ERROR)) {
   // Получение записей об ошибках
   i = 1;
   while ((rc2 = SQLGetDiagRec(SQL_HANDLE_STMT, 
                 hstmt, i, SqlState, 
                 &NativeError,
                 Msg, sizeof(Msg), 
                 &MsgLen)) != SQL_NO_DATA) {
                cout<< SqlState<<" "
                    <<NativeError<<" "
                   <<Msg<<" "
                    <<MsgLen;
      i++;
   }
}

if ((rc1 == SQL_SUCCESS) || 
     (rc1 == SQL_SUCCESS_WITH_INFO)) {
   // Оператор выполнен успешно
}
< Лекция 7 || Лекция 8: 12 || Лекция 9 >