Поддержка FILESTREAM (ODBC)

Драйвер ODBC собственного клиента SQL Server поддерживает улучшенную функциональность 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 used
r = 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 4096
BOOL 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 file
r = 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;
}