FILESTREAM アプリケーションでのデータベース操作との競合の回避

適用対象:SQL Server

SqlOpenFilestream() を使用して WIN32 ファイル ハンドルを開いて FILESTREAM BLOB データの読み取りまたは書き込みを行うアプリケーションでは、一般的なトランザクションで管理されている Transact-SQL ステートメントとの競合エラーが発生する可能性があります。 これには、実行の完了に長い時間がかかる Transact-SQL または MARS クエリが含まれます。 アプリケーションは、このような競合を回避できるように注意深く設計する必要があります。

SQL Server データベース エンジン またはアプリケーションが FILESTREAM BLOB を開こうとする場合、 データベース エンジン によって、関連付けられたトランザクション コンテキストがチェックされます。 データベース エンジン は、DDL ステートメント、DML ステートメント、データの取得、トランザクションの管理のうち、どれが開く操作の対象になっているかに基づいて、要求を許可または拒否します。 次の表は、トランザクションで開かれているファイルの種類に基づいて、Transact-SQL ステートメントを許可または拒否するかどうかをデータベース エンジンが判断する方法を示しています。

Transact-SQL ステートメント 読み取り用に開く 書き込み用に開く
CREATE TABLE、CREATE INDEX、DROP TABLE、ALTER TABLE などの、データベース メタデータを処理する DDL ステートメント 許可 ブロックされてタイムアウトで失敗
UPDATE、DELETE、INSERT などの、データベース内に格納されているデータを処理する DML ステートメント 許可 拒否
SELECT 許可 許可
COMMIT TRANSACTION 拒否* 拒否*
SAVE TRANSACTION 拒否* 拒否*
ROLLBACK 許可* 許可*

* トランザクションは取り消され、トランザクション コンテキストで開かれたハンドルは無効になります。 アプリケーションは、開いているハンドルをすべて閉じる必要があります。

次の例は、Transact-SQL ステートメントと FILESTREAM Win32 アクセスが競合を引き起こす可能性がある方法を示しています。

A. 書き込みアクセス用に FILESTREAM BLOB を開く

次の例は、書き込み専用アクセスでファイルを開いた場合の影響を示しています。

dstHandle =  OpenSqlFilestream(dstFilePath, Write, 0,  
    transactionToken, cbTransactionToken, 0);  
  
//Write some date to the FILESTREAM BLOB.  
WriteFile(dstHandle, updateData, ...);  
  
//DDL statements will be denied.  
//DML statements will be denied.  
//SELECT statements will be allowed. The FILESTREAM BLOB is  
//returned without the modifications that are made by  
//WriteFile(dstHandle, updateData, ...).  
CloseHandle(dstHandle);  
  
//DDL statements will be allowed.  
//DML statements will be allowed.  
//SELECT statements will be allowed. The FILESTREAM BLOB  
//is returned with the updateData applied.  

B. 読み取りアクセス用に FILESTREAM BLOB を開く

次の例は、読み取り専用アクセスでファイルを開いた場合の影響を示しています。

dstHandle =  OpenSqlFilestream(dstFilePath, Read, 0,  
    transactionToken, cbTransactionToken, 0);  
//DDL statements will be denied.  
//DML statements will be allowed. Any changes that are  
//made to the FILESTREAM BLOB will not be returned until  
//the dstHandle is closed.  
//SELECT statements will be allowed.  
CloseHandle(dstHandle);  
  
//DDL statements will be allowed.  
//DML statements will be allowed.  
//SELECT statements will be allowed.  

C. 複数の FILESTREAM BLOB ファイルを開いて閉じる

複数のファイルが開いている場合は、最も厳しい規則が使用されます。 2 つのファイルを開く例を次に示します。 最初のファイルは読み取り用に、2 番目のファイルは書き込み用に開かれます。 DML ステートメントは、2 番目のファイルが開かれるまで拒否されます。

dstHandle =  OpenSqlFilestream(dstFilePath, Read, 0,  
    transactionToken, cbTransactionToken, 0);  
//DDL statements will be denied.  
//DML statements will be allowed.  
//SELECT statements will be allowed.  
  
dstHandle1 =  OpenSqlFilestream(dstFilePath1, Write, 0,  
    transactionToken, cbTransactionToken, 0);  
  
//DDL statements will be denied.  
//DML statements will be denied.  
//SELECT statements will be allowed.  
  
//Close the read handle. The write handle is still open.  
CloseHandle(dstHandle);  
//DML statements are still denied because the write handle is open.  
  
//DDL statements will be denied.  
//DML statements will be denied.  
//SELECT statements will be allowed.  
  
CloseHandle(dstHandle1);  
//DDL statements will be allowed.  
//DML statements will be allowed.  
//SELECT statements will be allowed.  

D. カーソルが閉じられていない

次の例では、ステートメントのカーソルが閉じられていないために、 OpenSqlFilestream() で書き込みアクセス用に BLOB を開くことができない場合を示しています。

TCHAR *sqlDBQuery =  
TEXT("SELECT GET_FILESTREAM_TRANSACTION_CONTEXT(),")  
TEXT("Chart.PathName() FROM Archive.dbo.Records");  
  
//Execute a long-running Transact-SQL statement. Do not allow  
//the statement to complete before trying to  
//open the file.  
  
SQLExecDirect(hstmt, sqlDBQuery, SQL_NTS);  
  
//Before you call OpenSqlFilestream() any open files  
//that the Cursor the Transact-SQL statement is using  
// must be closed. In this example,  
//SQLCloseCursor(hstmt) is not called so that  
//the transaction will indicate that there is a file  
//open for reading. This will cause the call to  
//OpenSqlFilestream() to fail because the file is  
//still open.  
  
HANDLE srcHandle =  OpenSqlFilestream(srcFilePath,  
     Write, 0,  transactionToken,  cbTransactionToken,  0);  
  
//srcHandle will == INVALID_HANDLE_VALUE because the  
//cursor is still open.  

参照

OpenSqlFilestream による FILESTREAM データへのアクセス
複数のアクティブな結果セット (MARS) の使用