Поддержка FILESTREAM (ODBC)
Начиная с SQL Server 2008 и SQL Server собственный клиент 10.0, ODBC поддерживает усовершенствованную функцию FILESTREAM. Дополнительные сведения об этой возможности см. в разделе Поддержка FILESTREAM.
Для передачи и получения значений varbinary(max), превышающих 2 Гбайт, приложение должно осуществлять привязку параметров с помощью SQLBindParameter. Значение ColumnSize должно быть равным SQL_SS_LENGTH_UNLIMITED, а содержимое StrLen_or_IndPtr – равным SQL_DATA_AT_EXEC до SQLExecDirect или SQLExecute.
Как и все прочие параметры времени выполнения функции, эти данные предоставляются с SQLParamData и SQLPutData.
Можно вызвать SQLGetData для выборки данных по фрагментам для столбца FILESTREAM, если столбец не привязан к SQLBindCol.
Данные FILESTREAM можно обновлять, если они связаны с SQLBindCol.
Если в случае вызова SQLFetch для связанного столбца величина буфера будет недостаточной для размещения всего значения, будет получено предупреждение «данные усечены». Проигнорируйте это предупреждение и обновите данные в этом связанном столбце с помощью вызовов SQLParamData и SQLPutData. Данные FILESTREAM можно обновлять с помощью SQLSetPos, если они связаны с SQLBindCol.
Примеры
Столбцы FILESTREAM ведут себя точно так же, как столбцы varbinary(max), однако они не имеют ограничений по размерам. Они связываются как SQL_VARBINARY. (SQL_LONGVARBINARY используется со столбцами типа image, и на этот тип накладываются определенные ограничения. Так, SQL_LONGVARBINARY нельзя использовать в качестве выходного параметра). Следующие примеры показывают прямой доступ к столбцам FILESTREAM в системе NTFS. Эти примеры основываются на той посылке, что следующий код Transact-SQL был выполнен в базе данных:
CREATE TABLE fileStreamDocs(id uniqueidentifier ROWGUIDCOL NOT NULL UNIQUE,author varchar(64),document VARBINARY(MAX) FILESTREAM NULL)
Чтение
void selectFilestream (LPCWSTR dstFilePath) {SQLRETURN r;SQLCHAR transactionToken[1024];SQLWCHAR srcFilePath[1024];SQLINTEGER cbTransactionToken, cbsrcFilePath;// The GUID columns must be visible to the query, // even if it is not usedr = SQLExecDirect(hstmt, (SQLTCHAR *) _T("select GET_FILESTREAM_TRANSACTION_CONTEXT(); \select TOP(1) id, document.PathName() \from fileStreamDocs WHERE author = 'Chris Lee'"), SQL_NTS);if (r != SQL_SUCCESS && r!=SQL_SUCCESS_WITH_INFO) {ODBCError(henv, hdbc, hstmt, NULL, true); exit(-1);}r = SQLFetch(hstmt);if (r != SQL_SUCCESS && r!=SQL_SUCCESS_WITH_INFO) {ODBCError(henv, hdbc, hstmt, NULL, true); exit(-1);}r = SQLGetData(hstmt, 1, SQL_C_BINARY, transactionToken, sizeof(transactionToken), &cbTransactionToken);if (r != SQL_SUCCESS && r!=SQL_SUCCESS_WITH_INFO) {ODBCError(henv, hdbc, hstmt, NULL, true); exit(-1);}r = SQLMoreResults(hstmt);if (r != SQL_SUCCESS && r!=SQL_SUCCESS_WITH_INFO) {ODBCError(henv, hdbc, hstmt, NULL, true); exit(-1);}if (r != SQL_SUCCESS && r!=SQL_SUCCESS_WITH_INFO) {ODBCError(henv, hdbc, hstmt, NULL, true); exit(-1);}r = SQLFetch(hstmt);r = SQLGetData(hstmt, 2, SQL_C_TCHAR, srcFilePath, sizeof(srcFilePath), &cbsrcFilePath);if (r != SQL_SUCCESS && r!=SQL_SUCCESS_WITH_INFO) {ODBCError(henv, hdbc, hstmt, NULL, true); exit(-1);}if (!copyFileFromSql(srcFilePath, dstFilePath, transactionToken, cbTransactionToken)) {DeleteFile(dstFilePath);}r = SQLTransact(henv, hdbc, SQL_ROLLBACK);if (r != SQL_SUCCESS && r!=SQL_SUCCESS_WITH_INFO) {ODBCError(henv, hdbc, hstmt, NULL, true); exit(-1);}}
Вставка
void insertFilestream(LPCWSTR srcFilePath) {SQLRETURN r;SQLCHAR transactionToken[64];SQLWCHAR dstFilePath[1024];SQLINTEGER cbTransactionToken, cbDstFilePath;SQLUSMALLINT mode;r = SQLExecDirect(hstmt, (SQLTCHAR *) _T("insert into fileStreamDocs (id, author, document)\ output Get_Filestream_Transaction_Context(), inserted.document.PathName() \ values (newid(), 'Chris Lee', convert(varbinary, '**Temp**')) "), SQL_NTS);if (r != SQL_SUCCESS && r!=SQL_SUCCESS_WITH_INFO) {ODBCError(henv, hdbc, hstmt, NULL, true); exit(-1);}r = SQLFetch(hstmt);if (r != SQL_SUCCESS && r!=SQL_SUCCESS_WITH_INFO) {ODBCError(henv, hdbc, hstmt, NULL, true); exit(-1);}r = SQLGetData(hstmt, 1, SQL_C_BINARY,transactionToken, sizeof(transactionToken), &cbTransactionToken);if (r != SQL_SUCCESS && r!=SQL_SUCCESS_WITH_INFO) {ODBCError(henv, hdbc, hstmt, NULL, true); exit(-1);}r = SQLGetData(hstmt, 2, SQL_C_TCHAR,dstFilePath, sizeof(dstFilePath), &cbDstFilePath);if (r != SQL_SUCCESS && r!=SQL_SUCCESS_WITH_INFO) {ODBCError(henv, hdbc, hstmt, NULL, true); exit(-1);}r = SQLCloseCursor(hstmt);if (r != SQL_SUCCESS && r!=SQL_SUCCESS_WITH_INFO) {ODBCError(henv, hdbc, hstmt, NULL, true); exit(-1);}if (copyFileToSql(srcFilePath, dstFilePath, transactionToken, cbTransactionToken)) {mode = SQL_COMMIT;}else {mode = SQL_ROLLBACK;}r = SQLTransact(henv, hdbc, mode);if (r != SQL_SUCCESS && r!=SQL_SUCCESS_WITH_INFO) {ODBCError(henv, hdbc, hstmt, NULL, true); exit(-1);}}
Вспомогательные подпрограммы
#define COPYBUFFERSIZE 4096BOOL copyFileContents (HANDLE srcHandle, HANDLE dstHandle) {BYTE buffer[COPYBUFFERSIZE];DWORD bytesRead, bytesWritten;BOOL r;do {r = ReadFile(srcHandle, buffer, COPYBUFFERSIZE, &bytesRead,NULL);if (bytesRead == 0) {return r;}r = WriteFile(dstHandle, buffer, bytesRead, &bytesWritten, NULL);if (bytesWritten == 0) {return r;}} while (TRUE);}BOOL copyFileToSql(LPCWSTR srcFilePath, LPCWSTR dstFilePath, LPBYTE transactionToken, SQLINTEGER cbTransactionToken) {BOOL r;HANDLE srcHandle, dstHandle;unsigned int NtStatus;srcHandle = CreateFile( srcFilePath, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_FLAG_SEQUENTIAL_SCAN, NULL);if (srcHandle == INVALID_HANDLE_VALUE) {return FALSE;}dstHandle = OpenSqlFilestream( dstFilePath, Write, 0, transactionToken, cbTransactionToken, 0);if (dstHandle == INVALID_HANDLE_VALUE) {NtStatus = GetLastError();r = CloseHandle(srcHandle);return FALSE;}//copy filer = copyFileContents(srcHandle, dstHandle);CloseHandle(srcHandle);CloseHandle(dstHandle);return r;}BOOL copyFileFromSql(LPCWSTR srcFilePath, LPCWSTR dstFilePath, LPBYTE transactionToken, SQLINTEGER cbTransactionToken) {BOOL r;HANDLE srcHandle, dstHandle;unsigned int NtStatus;srcHandle = OpenSqlFilestream( srcFilePath, Read, 0, transactionToken, cbTransactionToken, 0);if (srcHandle == INVALID_HANDLE_VALUE) {return FALSE;}dstHandle = CreateFile( dstFilePath, GENERIC_WRITE, 0, NULL, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);if (dstHandle == INVALID_HANDLE_VALUE) {CloseHandle(srcHandle);return FALSE;}r = copyFileContents(srcHandle, dstHandle);CloseHandle(srcHandle);CloseHandle(dstHandle);return r;}