Конфликты Win32 и Transact-SQL
Приложения, использующие функцию SqlOpenFilestream() для открытия дескрипторов файлов Win32 для считывания или записи данных FILESTREAM BLOB, могут столкнуться с конфликтами при работе с инструкциями Transact-SQL, использованными в общей транзакции. Это также относится и к запросам Transact-SQL или MARS, выполнение которых занимает много времени. При разработке приложений надо уделить особое внимание предотвращению данных типов конфликтов.
Если компонент SQL Server Database Engine или приложения пытаются открыть объекты FILESTREAM BLOB, то компонент Database Engine выполнит проверку контекста ассоциированной транзакции. Компонент Database Engine разрешит или запретит запрос в зависимости от того, работает ли операция по открытию с инструкциями DDL, инструкциями DML, получает ли она данные и управляет ли она транзакциями. В следующей таблице показывается, как компонент Database Engine определяет, следует ли разрешить или запретить инструкцию Transact-SQL, основываясь на типе файлов, открытых в транзакции.
Инструкции Transact-SQL |
Открыты для чтения |
Открыты для записи |
---|---|---|
Инструкции DDL, работающие с метаданными базы данных, например CREATE TABLE, CREATE INDEX, DROP TABLE и ALTER TABLE. |
Разрешены |
Заблокированы и завершаются со сбоем по истечении времени ожидания. |
Инструкции DML, работающие с данными, хранящимися в базе данных, например UPDATE, DELETE и INSERT. |
Разрешены |
Запрещены |
SELECT |
Разрешены |
Разрешены |
COMMIT TRANSACTION |
Запрещены* |
Запрещены* |
SAVE TRANSACTION |
Запрещены* |
Запрещены* |
ROLLBACK |
Разрешены |
Разрешены* |
* Транзакция отменяется, а открытые дескрипторы для контекста транзакции становятся недействительными. Приложение должно закрыть все открытые дескрипторы.
Примеры
В следующих примерах показывается, как могут возникать конфликты при совместном использовании инструкций Transact-SQL и доступа к FILESTREAM с помощью функций Win32.
А. Открытие объекта 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.
Б. Открытие объекта 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.
В. Открытие и закрытие нескольких файлов FILESTREAM BLOB
Если открыто несколько файлов, то используется правило с наибольшими ограничениями. В следующем примере открываются два файла. Первый файл открывается для чтения, а второй — для записи. Инструкции DML будут запрещены, пока не будет открыт второй файл.
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.
Г. Сбой при закрытии курсора
В следующем примере показывается, как незакрытый курсор инструкции может помешать функции 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.