非同期実行 (ポーリング メソッド)

ODBC 3.8 および Windows 7 SDK より前では、非同期操作はステートメント関数でのみ許可されていました。 詳細については、このトピックで後述する 「ステートメント操作の非同期実行」を参照してください。

Windows 7 SDK の ODBC 3.8 では、接続関連の操作で非同期実行が導入されました。 詳細については、このトピックで後述する 「接続操作の非同期実行 」セクションを参照してください。

Windows 7 SDK では、非同期ステートメントまたは接続操作の場合、アプリケーションはポーリング メソッドを使用して非同期操作が完了したと判断しました。 Windows 8 SDK 以降では、通知メソッドを使用して非同期操作が完了したことを確認できます。 詳細については、「非同期実行 (通知メソッド)」を参照してください。

既定では、ドライバーは ODBC 関数を同期的に実行します。つまり、アプリケーションは関数を呼び出し、ドライバーは関数の実行が完了するまでアプリケーションに制御を返しません。 ただし、一部の関数は非同期的に実行できます。つまり、アプリケーションは関数を呼び出し、ドライバーは最小限の処理の後、アプリケーションに制御を返します。 その後、アプリケーションは、最初の関数の実行中に他の関数を呼び出すことができます。

非同期実行は、接続の確立、SQL ステートメントの準備と実行、メタデータの取得、データのフェッチ、トランザクションのコミットなどの、データ ソースで主に実行されるほとんどの関数でサポートされています。 これは、ログイン プロセスや大規模なデータベースに対する複雑なクエリなど、データ ソースで実行されるタスクに長い時間がかかる場合に最も役立ちます。

非同期処理が有効になっているステートメントまたは接続を使用してアプリケーションが関数を実行すると、ドライバーは最小限量の処理 (エラーの引数のチェックなど) を実行し、データ ソースに処理を渡し、SQL_STILL_EXECUTING リターン コードを使用して制御をアプリケーションに返します。 アプリケーションでは他の作業を実行します。 非同期関数がいつ終了したかを判断するために、アプリケーションは、最初に使用したのと同じ引数を持つ関数を呼び出すことによって、一定の間隔でドライバーをポーリングします。 関数がまだ実行中の場合は、SQL_STILL_EXECUTING を返します。実行が完了すると、SQL_SUCCESS、SQL_ERROR、SQL_NEED_DATA など、同期的に実行された場合に返されたコードが返されます。

関数が同期的に実行されるか非同期的に実行されるかは、ドライバー固有です。 たとえば、結果セットのメタデータがドライバーにキャッシュされるとします。 この場合、SQLDescribeCol の実行にかかる時間は非常に少なく、ドライバーは人為的に実行を遅らせるのではなく、単に関数を実行する必要があります。 一方、ドライバーがデータ ソースからメタデータを取得する必要がある場合は、この実行中にアプリケーションに制御を返す必要があります。 そのため、アプリケーションは、最初に関数を非同期的に実行するときに、SQL_STILL_EXECUTING 以外のリターン コードを処理できる必要があります。

ステートメント操作の非同期実行

次のステートメント関数はデータ ソースに対して動作し、非同期的に実行できます。

非同期ステートメントの実行は、データ ソースに応じて、ステートメントごとまたは接続ごとに制御されます。 つまり、アプリケーションは、特定の関数を非同期的に実行するのではなく、特定のステートメントで実行されるすべての関数を非同期的に実行することを指定します。 サポートされているアプリケーションを調べるには、SQL_ASYNC_MODE のオプションを使用して SQLGetInfo を呼び出します。 接続レベルの非同期実行 (ステートメント ハンドルの場合) がサポートされている場合、SQL_AM_CONNECTION が返されます。ステートメント レベルの非同期実行がサポートされている場合は、SQL_AM_STATEMENT が返されます。

特定のステートメントで実行される関数を非同期的に実行するように指定するために、アプリケーションは SQL_ATTR_ASYNC_ENABLE 属性を使用して SQLSetStmtAttr を呼び出し、SQL_ASYNC_ENABLE_ON に設定します。 接続レベルの非同期処理がサポートされている場合、SQL_ATTR_ASYNC_ENABLE ステートメント属性は読み取り専用であり、その値はステートメントが割り当てられた接続の接続属性と同じです。 ステートメント属性の値がステートメントの割り当て時以降に設定されるかどうかは、ドライバー固有です。 設定しようとすると、SQL_ERROR と SQLSTATE HYC00 が返されます (オプションの機能は実装されていません)。

特定の接続で実行される関数を非同期的に実行するように指定するために、アプリケーションは SQL_ATTR_ASYNC_ENABLE 属性を使用して SQLSetConnectAttr を呼び出し、SQL_ASYNC_ENABLE_ON に設定します。 接続に割り当てられた将来のステートメント ハンドルはすべて、非同期実行に対して有効になります。この操作によって既存のステートメント ハンドルが有効になるかどうかは、ドライバーによって定義されます。 SQL_ATTR_ASYNC_ENABLE が SQL_ASYNC_ENABLE_OFF に設定されている場合、接続上のすべてのステートメントは同期モードになります。 接続にアクティブ ステートメントがある間に非同期実行が有効になっている場合、エラーが返されます。

ドライバーが特定の接続でサポートできる非同期モードでアクティブな同時実行ステートメントの最大数を確認するには、アプリケーションは、SQL_MAX_ASYNC_CONCURRENT_STATEMENTS オプションを使用して SQLGetInfo を呼び出します。

次のコードは、ポーリング モデルのしくみを示しています。

SQLHSTMT  hstmt1;  
SQLRETURN rc;  
  
// Specify that the statement is to be executed asynchronously.  
SQLSetStmtAttr(hstmt1, SQL_ATTR_ASYNC_ENABLE, SQL_ASYNC_ENABLE_ON, 0);  
  
// Execute a SELECT statement asynchronously.  
while ((rc=SQLExecDirect(hstmt1,"SELECT * FROM Orders",SQL_NTS))==SQL_STILL_EXECUTING) {  
   // While the statement is still executing, do something else.  
   // Do not use hstmt1, because it is being used asynchronously.  
}  
  
// When the statement has finished executing, retrieve the results.  

関数が非同期的に実行されている間、アプリケーションは他のステートメントで関数を呼び出すことができます。 アプリケーションは、非同期ステートメントに関連付けられている接続を除き、任意の接続で関数を呼び出すこともできます。 ただし、ステートメント操作が SQL_STILL_EXECUTING を返した後、アプリケーションは元の関数と次の関数 (ステートメント ハンドルまたはその関連する接続、環境ハンドル) のみを呼び出すことができます。

アプリケーションが非同期ステートメントを使用するか、そのステートメントに関連付けられている接続を使用して他の関数を呼び出した場合、関数は SQLSTATE HY010 (関数シーケンス エラー) などを返します。

SQLHDBC       hdbc1, hdbc2;  
SQLHSTMT      hstmt1, hstmt2, hstmt3;  
SQLCHAR *     SQLStatement = "SELECT * FROM Orders";  
SQLUINTEGER   InfoValue;  
SQLRETURN     rc;  
  
SQLAllocHandle(SQL_HANDLE_STMT, hdbc1, &hstmt1);  
SQLAllocHandle(SQL_HANDLE_STMT, hdbc1, &hstmt2);  
SQLAllocHandle(SQL_HANDLE_STMT, hdbc2, &hstmt3);  
  
// Specify that hstmt1 is to be executed asynchronously.  
SQLSetStmtAttr(hstmt1, SQL_ATTR_ASYNC_ENABLE, SQL_ASYNC_ENABLE_ON, 0);  
  
// Execute hstmt1 asynchronously.  
while ((rc = SQLExecDirect(hstmt1, SQLStatement, SQL_NTS)) == SQL_STILL_EXECUTING) {  
   // The following calls return HY010 because the previous call to  
   // SQLExecDirect is still executing asynchronously on hstmt1. The  
   // first call uses hstmt1 and the second call uses hdbc1, on which  
   // hstmt1 is allocated.  
   SQLExecDirect(hstmt1, SQLStatement, SQL_NTS);   // Error!  
   SQLGetInfo(hdbc1, SQL_UNION, (SQLPOINTER) &InfoValue, 0, NULL);   // Error!  
  
   // The following calls do not return errors. They use a statement  
   // handle other than hstmt1 or a connection handle other than hdbc1.  
   SQLExecDirect(hstmt2, SQLStatement, SQL_NTS);   // OK  
   SQLTables(hstmt3, NULL, 0, NULL, 0, NULL, 0, NULL, 0);   // OK  
   SQLGetInfo(hdbc2, SQL_UNION, (SQLPOINTER) &InfoValue, 0, NULL);   // OK  
}  

アプリケーションが関数を呼び出して、まだ非同期的に実行されているかどうかを判断する場合は、元のステートメント ハンドルを使用する必要があります。 これは、非同期実行がステートメントごとに追跡されるためです。 アプリケーションでは、ドライバー マネージャーで過去のエラー チェックを取得するために、他の引数 (元の引数が実行する) の有効な値も供給する必要があります。 ただし、ドライバーはステートメント ハンドルを確認し、ステートメントが非同期的に実行されていることを判断した後、他のすべての引数を無視します。

関数が非同期的に実行されている間(つまり、SQL_STILL_EXECUTING を返した後、別のコードを返す前)、アプリケーションは同じステートメント ハンドルで SQLCancel または SQLCancelHandle を呼び出すことによってそれを取り消すことができます。 これは、関数の実行を取り消す保証はありません。 たとえば、関数は既に完了している可能性があります。 さらに、SQLCancel または SQLCancelHandle によって返されるコードは、関数の取り消しが実際に関数を取り消したかどうかではなく、成功したかどうかを示すだけです。 関数が取り消されたかどうかを判断するために、アプリケーションは関数を再度呼び出します。 関数が取り消された場合は、SQL_ERROR および SQLSTATE HY008 (操作が取り消されました) が返されます。 関数が取り消されなかった場合は、別の SQLSTATE で SQL_SUCCESS、 SQL_STILL_EXECUTING、SQL_ERRORなど、別のコードが返されます。

ドライバーがステートメント レベルの非同期処理をサポートしているときに特定のステートメントの非同期実行を無効にするには、アプリケーションは、SQL_ATTR_ASYNC_ENABLE属性を使用して SQLSetStmtAttr を呼び出し、SQL_ASYNC_ENABLE_OFF に設定します。 ドライバーが接続レベルの非同期処理をサポートしている場合、アプリケーションは SQLSetConnectAttr を呼び出して SQL_ATTR_ASYNC_ENABLE を SQL_ASYNC_ENABLE_OFF に設定します。これによって、接続上のすべてのステートメントの非同期実行が無効になります。

アプリケーションは、元の関数の繰り返しループで診断レコードを処理する必要があります。 非同期関数の実行時に SQLGetDiagField または SQLGetDiagRec が呼び出されると、診断レコードの現在のリストが返されます。 元の関数呼び出しが繰り返されるたびに、以前の診断レコードが消去されます。

接続操作を非同期的に実行する

ODBC 3.8 より前では、準備、実行、フェッチなどのステートメント関連の操作やカタログ メタデータ操作に対して非同期実行が許可されていました。 ODBC 3.8 以降では、接続、切断、コミット、ロールバックなどの接続関連の操作でも非同期実行が可能です。

ODBC 3.8 の詳細については、「ODBC 3.8 の新機能」を参照してください。

接続操作を非同期的に実行すると、次のシナリオで役立ちます。

  • 少数のスレッドが、データ レートが非常に高い多数のデバイスを管理する場合。 応答性とスケーラビリティを最大化するには、すべての操作を非同期にすることが望ましいです。

  • 複数の接続でデータベース操作を重複させ、転送の経過時間を短縮したい場合。

  • 効率的な非同期 ODBC 呼び出しと接続操作を取り消す機能により、ユーザーは、アプリケーションでタイムアウトを待たずに低速操作を取り消すことができます。

接続ハンドルで動作する次の関数を非同期的に実行できるようになりました。

ドライバーがこれらの関数に対する非同期操作をサポートしているかどうかを判断するために、アプリケーションは SQLGetInfo をSQL_ASYNC_DBC_FUNCTIONS で呼び出します。 非同期操作がサポートされている場合、SQL_ASYNC_DBC_CAPABLE が返されます。 非同期操作がサポートされていない場合、SQL_ASYNC_DBC_NOT_CAPABLE が返されます。

特定の接続で実行される関数を非同期的に実行するように指定するために、アプリケーションは SQLSetConnectAttr を呼び出し、SQL_ATTR_ASYNC_DBC_FUNCTIONS_ENABLE 属性を SQL_ASYNC_DBC_ENABLE_ON に設定します。 接続を確立する前に接続属性を設定すると、常に同期的に実行されます。 また、SQLSetConnectAttr でSQL_ATTR_ASYNC_DBC_FUNCTIONS_ENABLE 接続属性を設定する操作は、常に同期的に実行されます。

アプリケーションは、接続を行う前に非同期操作を有効にすることができます。 ドライバー マネージャーは接続を行う前に使用するドライバーを決定できないため、ドライバー マネージャーは常に SQLSetConnectAttr で成功を返します。 ただし、ODBC ドライバーが非同期操作をサポートしていない場合は、接続に失敗することがあります。

一般に、特定の接続ハンドルまたはステートメント ハンドルに関連付けられた非同期実行関数は、最大でも 1 つ存在できます。 ただし、接続ハンドルには、複数のステートメント ハンドルを関連付けることができます。 接続ハンドルで非同期操作が実行されていない場合は、関連付けられているステートメント ハンドルで非同期操作を実行できます。 同様に、関連付けられているステートメント ハンドルで進行中の非同期操作がない場合は、接続ハンドルに対して非同期操作を実行できます。 現在非同期操作を実行しているハンドルを使用して非同期操作を実行しようとすると、HY010 "関数シーケンス エラー" が返されます。

接続操作で SQL_STILL_EXECUTING が返された場合、アプリケーションは元の関数とその接続ハンドルに対して次の関数のみを呼び出すことができます。

  • SQLCancelHandle (接続ハンドル上)

  • SQLGetDiagField

  • SQLGetDiagRec

  • SQLAllocHandle (ENV/DBC の割り当て)

  • SQLAllocHandleStd (ENV/DBC の割り当て)

  • SQLGetEnvAttr

  • SQLGetConnectAttr

  • SQLDataSources

  • SQLDrivers

  • SQLGetInfo

  • SQLGetFunctions

アプリケーションは、元の関数の繰り返しループで診断レコードを処理する必要があります。 非同期関数の実行時に SQLGetDiagField または SQLGetDiagRec が呼び出されると、診断レコードの現在のリストが返されます。 元の関数呼び出しが繰り返されるたびに、以前の診断レコードが消去されます。

接続を非同期的に開いたり閉じたりしている場合、アプリケーションが元の関数呼び出しで SQL_SUCCESS または SQL_SUCCESS_WITH_INFO を受け取ると、操作は完了します。

ODBC 3.8に新しい関数 SQLCancelHandle が追加されました。 この関数は、6 つの接続関数 (SQLBrowseConnectSQLConnectSQLDisconnectSQLDriverConnectSQLEndTranSQLSetConnectAttr) を取り消します。 アプリケーションは、SQLGetFunctions を呼び出して、ドライバーが SQLCancelHandle をサポートしているかどうかを判断する必要があります。 SQLCancel と同様に、SQLCancelHandle が成功を返した場合、操作が取り消されたという意味ではありません。 アプリケーションは元の関数をもう一度呼び出して、操作が取り消されたかどうかを判断する必要があります。 SQLCancelHandle を使用すると、接続ハンドルまたはステートメント ハンドルに対する非同期操作を取り消すことができます。 SQLCancelHandle を使用してステートメント ハンドルに対する操作を取り消すことは、SQLCancel の呼び出しと同じです。

SQLCancelHandle と非同期接続操作の両方を同時にサポートする必要はありません。 ドライバーは非同期接続操作をサポートできますが、SQLCancelHandle はサポートできません。その逆もまた同様です。

ODBC 3.x および ODBC 2.x アプリケーションでは、ODBC 3.8 ドライバーと ODBC 3.8 ドライバー マネージャーを使用して、非同期接続操作と SQLCancelHandle を使用することもできます。 旧バージョンのアプリケーションで新しい ODBC バージョンの新機能を使用できるようにする方法については、「互換性マトリックス」を参照してください。

接続プール

接続プールが有効になっている場合、非同期操作は、接続の確立 (SQLConnectSQLDriverConnect を使用) と SQLDisconnect との接続の終了に対してのみ最小限にサポートされます。 ただし、アプリケーションは引き続き SQLConnectSQLDriverConnect、および SQLDisconnect からのSQL_STILL_EXECUTING 戻り値を処理できる必要があります。

接続プールが有効になっている場合、 SQLEndTranSQLSetConnectAttr は非同期操作でサポートされます。

A. 接続関数の非同期実行を有効にする

次の例は、SQLSetConnectAttr を使用して、接続関連の関数の非同期実行を有効にする方法を示しています。

BOOL AsyncConnect (SQLHANDLE hdbc)   
{  
   SQLRETURN r;  
   SQLHANDLE hdbc;  
  
   // Enable asynchronous execution of connection functions.  
   // This must be executed synchronously, that is r != SQL_STILL_EXECUTING  
   r = SQLSetConnectAttr(  
         hdbc,   
         SQL_ATTR_ASYNC_DBC_FUNCTIONS_ENABLE,  
         reinterpret_cast<SQLPOINTER> (SQL_ASYNC_DBC_ENABLE_ON),  
         0);  
   if (r != SQL_SUCCESS && r != SQL_SUCCESS_WITH_INFO)   
   {  
      return FALSE;  
   }  
  
   TCHAR szConnStrIn[256] = _T("DSN=AsyncDemo");  
  
   r = SQLDriverConnect(hdbc, NULL, (SQLTCHAR *) szConnStrIn, SQL_NTS, NULL, 0, NULL, SQL_DRIVER_NOPROMPT);  
  
   if (r == SQL_ERROR)   
   {  
      // Use SQLGetDiagRec to process the error.  
      // If SQLState is HY114, the driver does not support asynchronous execution.  
      return FALSE;  
   }  
  
   while (r == SQL_STILL_EXECUTING)   
   {  
      // Do something else.  
  
      // Check for completion, with the same set of arguments.  
      r = SQLDriverConnect(hdbc, NULL, (SQLTCHAR *) szConnStrIn, SQL_NTS, NULL, 0, NULL, SQL_DRIVER_NOPROMPT);  
   }  
  
   if (r != SQL_SUCCESS && r != SQL_SUCCESS_WITH_INFO)   
   {  
      return FALSE;  
   }  
  
   return TRUE;  
}  
  

B. 非同期コミット操作

この例では、非同期コミット操作を示します。 ロールバック操作は、この方法でも実行できます。

BOOL AsyncCommit ()   
{  
   SQLRETURN r;   
  
   // Assume that SQL_ATTR_ASYNC_DBC_FUNCTIONS_ENABLE is SQL_ASYNC_DBC_ENABLE_ON.  
  
   r = SQLEndTran(SQL_HANDLE_DBC, hdbc, SQL_COMMIT);  
   while (r == SQL_STILL_EXECUTING)   
   {  
      // Do something else.  
  
      // Check for completion with the same set of arguments.  
      r = SQLEndTran(SQL_HANDLE_DBC, hdbc, SQL_COMMIT);  
   }  
  
   if (r != SQL_SUCCESS && r != SQL_SUCCESS_WITH_INFO)   
   {  
      return FALSE;  
   }  
   return TRUE;  
}  

参照

ステートメントの実行 (ODBC)