Ambil Kolom di Klien Asli SQL Server Menggunakan IRow::GetColumns (OLE DB)
Berlaku untuk: SQL Server Azure SQL Database Azure SQL Managed Instance Azure Synapse Analytics Analytics Platform System (PDW)
Antarmuka IRow memungkinkan akses langsung ke kolom satu baris dalam tataan hasil. Dengan demikian, IRow adalah cara yang efisien untuk mengambil kolom dari tataan hasil dengan satu baris.
Sampel kode tersedia yang menunjukkan cara mengambil satu baris menggunakan IRow. Dalam sampel ini, satu kolom pada satu waktu diambil dari baris. Sampel menunjukkan:
Cara mengambil sekelompok kolom (secara berurutan).
Cara mengakses kolom dua kali. Pertama kali lebar kolom aktual diperoleh, dan kemudian data aktual diakses. Dalam struktur DBCOLUMNACCESS, jika pData adalah NULL dan cbMaxLen adalah 0, panggilan ke IRow>-GetColumns() hanya mengembalikan panjang kolom aktual. Dalam hal ini, IRow-GetColumns>() dapat dipanggil lagi pada kolom yang sama untuk mengambil data aktual.
Penting
Jika memungkinkan, gunakan Autentikasi Windows. Jika Autentikasi Windows tidak tersedia, minta pengguna untuk memasukkan kredensial mereka pada waktu proses. Hindari menyimpan kredensial dalam file. Jika Anda harus mempertahankan kredensial, Anda harus mengenkripsinya dengan API kripto Win32.
Buat koneksi ke sumber data.
Jalankan perintah (dalam contoh berikut, ICommandExecute::Execute() dipanggil dengan IID_IRow).
Jalankan IRow::GetColumns() untuk mengambil satu atau beberapa kolom di baris yang dihasilkan. Jika Anda ingin menemukan ukuran kolom aktual sebelum mengambil data, atur pData di DBCOLUMNACCESS ke NULL. Panggilan ke IRow::GetColumns() hanya mengembalikan lebar kolom. Panggilan lain IRow::GetColumns() akan mengambil data.
Jalankan IRow::GetColumns() hingga semua kolom yang Anda butuhkan diakses. Kolom harus diakses secara berurutan.
Sampel ini menunjukkan cara menggunakan antarmuka IRow untuk memungkinkan akses langsung ke kolom satu baris dalam tataan hasil. Contoh menunjukkan:
Cara mengambil sekelompok kolom secara berurutan.
Cara mengakses kolom dua kali - pertama kali lebar kolom aktual diperoleh lalu kemudian data aktual diakses.
Dalam struktur DBCOLUMNACCESS, jika pData adalah NULL dan cbMaxLen adalah 0, panggilan ke IRow-GetColumns> hanya mengembalikan panjang kolom aktual. Dalam hal ini IRow-GetColumns> dapat dipanggil lagi pada kolom yang sama untuk mengambil data aktual. Sampel ini tidak didukung pada IA64.
Sampel kode Transact-SQL dalam artikel ini menggunakan AdventureWorks2022
database sampel atau AdventureWorksDW2022
, yang dapat Anda unduh dari halaman beranda Sampel Microsoft SQL Server dan Proyek Komunitas.
Daftar kode pertama (Transact-SQL) membuat tabel yang digunakan oleh sampel.
Kompilasi dengan ole32.lib oleaut32.lib dan jalankan daftar kode kedua (C++). Aplikasi ini tersambung ke instans SQL Server default komputer Anda. Pada beberapa sistem operasi Windows, Anda harus mengubah (localhost) atau (lokal) ke nama instans SQL Server Anda. Untuk menyambungkan ke instans bernama, ubah string koneksi dari L"(local)" ke L"(local)\\name", di mana nama adalah instans bernama. Secara default, SQL Server Express menginstal ke instans bernama. Pastikan variabel lingkungan INCLUDE Anda menyertakan direktori yang berisi sqlncli.h.
Daftar kode ketiga (Transact-SQL) menghapus tabel yang digunakan oleh sampel.
USE AdventureWorks2022;
GO
if exists (select name from sysobjects where name = 'MyTable')
drop table MyTable
go
create table MyTable
(
col1 int,
col2 varchar(50),
col3 char(50),
col4 datetime,
col5 float,
col6 money,
col7 sql_variant,
col8 binary(50),
col9 text,
col10 image
)
go
insert into MyTable
values
(
10,
'abcdefghijklmnopqrstuvwxyz',
'ABCDEFGHIJKLMNOPQRSTUVWXYZ',
'11/1/1999 11:52 AM',
3.14,
99.95,
convert(nchar(50), N'AbCdEfGhIjKlMnOpQrStUvWxYz'),
0x123456789,
replicate('AAAAABBBBB', 500),
replicate(0x123456789, 500)
)
Go
// compile with: ole32.lib oleaut32.lib
#define DBINITCONSTANTS
#define OLEDBVER 0x0250 // to include correct interfaces
#include <stdio.h>
#include <windows.h>
#include <iostream>
#include <oledb.h>
#include <sqlncli.h>
using namespace std;
int InitializeAndEstablishConnection();
HRESULT GetColumnSize(IRow* pUnkRow, ULONG iCol);
ULONG PrintData(ULONG iCols, ULONG iStart, DBCOLUMNINFO* prgInfo, DBCOLUMNACCESS* prgColumns);
HRESULT GetColumns(IRow* pUnkRow, ULONG iStart, ULONG iEnd);
IDBInitialize* pIDBInitialize = NULL;
IDBProperties* pIDBProperties = NULL;
IDBCreateSession* pIDBCreateSession = NULL;
IDBCreateCommand* pIDBCreateCommand = NULL;
ICommandText* pICommandText = NULL;
IRow * pIRow = NULL;
DBCOLUMNINFO* pDBColumnInfo = NULL;
IAccessor* pIAccessor = NULL;
DBPROP InitProperties[4];
DBPROPSET rgInitPropSet[1];
ULONG i, j;
HRESULT hresult;
DBROWCOUNT cNumRows = 0;
ULONG lNumCols;
WCHAR* pStringsBuffer;
DBBINDING* pBindings;
ULONG ConsumerBufColOffset = 0;
HACCESSOR hAccessor;
ULONG lNumRowsRetrieved;
HROW hRows[10];
HROW* pRows = &hRows[0];
int main() {
ULONG iidx = 0;
WCHAR* wCmdString = OLESTR(" SELECT * FROM MyTable");
// Call a function to initialize and establish connection.
if (InitializeAndEstablishConnection() == -1) {
cout << "Failed to initialize and establish connection.\n";
// Insert your code for cleanup and error handling.
return -1;
}
// Create a session object.
if (FAILED(pIDBInitialize->QueryInterface (
IID_IDBCreateSession, (void**) &pIDBCreateSession))) {
cout << "Failed to obtain IDBCreateSession interface.\n";
// Insert your code for cleanup and error handling.
return -1;
}
if (FAILED(pIDBCreateSession->CreateSession( NULL, IID_IDBCreateCommand, (IUnknown**) &pIDBCreateCommand))) {
cout << "pIDBCreateSession->CreateSession failed.\n";
// Insert your code for cleanup and error handling.
return -1;
}
// Access the ICommandText interface.
if (FAILED(pIDBCreateCommand->CreateCommand( NULL, IID_ICommandText, (IUnknown**) &pICommandText))) {
cout << "Failed to access ICommand interface.\n";
// Insert your code for cleanup and error handling.
return -1;
}
// Use SetCommandText() to specify the command text.
if (FAILED(pICommandText->SetCommandText(DBGUID_DBSQL, wCmdString))) {
cout << "Failed to set command text.\n";
// Insert your code for cleanup and error handling.
return -1;
}
// Fetch columns 1-5 and then 6-10 and display the contents
if (FAILED(hresult = pICommandText->Execute(NULL, IID_IRow, NULL, &cNumRows, (IUnknown **) &pIRow))) {
cout << "Failed to execute command.\n";
// Insert your code for cleanup and error handling.
return -1;
}
hresult = GetColumns(pIRow, 1, 5);
hresult = GetColumns(pIRow, 6, 10);
hresult = pIRow->Release();
// Execute the command.
if (FAILED(hresult = pICommandText->Execute(NULL, IID_IRow, NULL, &cNumRows, (IUnknown **) &pIRow))) {
cout << "Failed to execute command.\n";
// Insert your code for cleanup and error handling.
return -1;
}
// Get columns
for ( iidx = 1 ; iidx <= 10 ; iidx++ ) {
if (FAILED(hresult = GetColumnSize(pIRow, iidx))) {
cout << "Failed to get column size.\n";
// Insert your code for cleanup and error handling.
return -1;
}
hresult = GetColumns(pIRow, iidx, iidx);
}
pIRow->Release();
// Release memory.
pICommandText->Release();
pIDBCreateCommand->Release();
pIDBCreateSession->Release();
if (FAILED(pIDBInitialize->Uninitialize())) {
// Uninitialize not required, but fails if an interface has not been released. Can be used for debugging.
cout << "Problem uninitializing.\n";
}
pIDBInitialize->Release();
// Release the COM library.
CoUninitialize();
return 0;
};
//--------------------------------------------------------------------
BOOL InitColumn(DBCOLUMNACCESS* pCol, DBCOLUMNINFO* pInfo) {
// If text or image column is being read,in which case the max possible length of a value is
// the column is hugh,we will limit that size to 512 bytes (for illustration purposes).
DBLENGTH ulSize = (pInfo->ulColumnSize < 0x7fffffff) ? pInfo->ulColumnSize : 512;
// Verify dta buffer is large enough.
if (pCol->cbMaxLen < (ulSize + 1)) {
if (pCol->pData) {
delete [] pCol->pData;
pCol->pData = NULL;
}
// Allocate data buffer
void * p = pCol->pData = new WCHAR[ulSize + 1];
if (!(p /* pCol->pData = new WCHAR[ulSize + 1] */ ))
return FALSE;
// set the max length of caller-initialized memory.
pCol->cbMaxLen = sizeof(WCHAR) * (ulSize + 1);
// In the above 2 steps, pData is pointing to memory (it is not NULL) and cbMaxLen has a value
// (not 0), so next call to IRow->GetData() will read the data from the column.
}
// Clear memory buffer
ZeroMemory((void*) pCol->pData, pCol->cbMaxLen);
// Set properties.
pCol->wType = DBTYPE_WSTR;
pCol->columnid = pInfo->columnid;
pCol->cbDataLen = 0;
pCol->dwStatus = 0;
pCol->dwReserved = 0;
pCol->bPrecision = 0;
pCol->bScale = 0;
return TRUE;
}
//--------------------------------------------------------------------
HRESULT GetColumns(IRow* pUnkRow, ULONG iStart, ULONG iEnd) {
// Start and end are same. Thus, get only one column.
HRESULT hr;
ULONG iidx; // loop counter
DBORDINAL cColumns; // Count of columns
ULONG cUserCols; // Count of user columns
DBCOLUMNINFO* prgInfo; // Column of info. array
OLECHAR* pColNames; // Array of column names
DBCOLUMNACCESS* prgColumns; // Ptr to column access structures array
DBCOLUMNINFO* pCurrInfo;
DBCOLUMNACCESS* pCurrCol;
IColumnsInfo* pIColumnsInfo = NULL;
// Initialize
cColumns = 0;
prgInfo = NULL;
pColNames = NULL;
prgColumns = NULL;
printf("Retrieving data\n");
// Get column info to build column access array
hr = pUnkRow->QueryInterface(IID_IColumnsInfo, (void**)&pIColumnsInfo);
if (FAILED(hr))
goto CLEANUP;
hr = pIColumnsInfo->GetColumnInfo(&cColumns, &prgInfo, &pColNames);
if (FAILED(hr))
goto CLEANUP;
printf("In GetColumns(), Columns= %d\n", cColumns);
// Determine no. of columns to retrieve. Since iEnd and iStart is same, this is redundent step.
// cUserCols will always be 1.
cUserCols = iEnd - iStart + 1;
// Walk list of columns and setup a DBCOLUMNACCESS structure
if (!(prgColumns= new DBCOLUMNACCESS[cUserCols])) { // cUserCols is only 1
hr = E_FAIL;
goto CLEANUP;
}
ZeroMemory((void*) prgColumns, sizeof(DBCOLUMNACCESS) * cUserCols);
for ( iidx = 0 ; iidx < cUserCols ; iidx++ ) {
pCurrInfo = prgInfo + iidx + iStart - 1;
pCurrCol = prgColumns + iidx;
// Here the values of DBCOLUMNACCESS elements is set (pData and cbMaxLen)Thus IRow->GetColumns()
// will return actual data.
if (InitColumn(pCurrCol, pCurrInfo) == FALSE)
goto CLEANUP;
}
hr = pUnkRow->GetColumns(cUserCols, prgColumns); // cUserCols = 1
if (FAILED(hr))
printf("Error occurred\n");
// Show data.
PrintData(cUserCols, iStart, prgInfo, prgColumns);
CLEANUP:
if (pIColumnsInfo)
pIColumnsInfo->Release();
if (prgColumns)
delete [] prgColumns;
return hr;
}
// This function returns the actual width of the data in the column (not the columnwidth in
// DBCOLUMNFO structure which is the width of the column)
HRESULT GetColumnSize(IRow* pUnkRow, ULONG iCol) {
HRESULT hr = NOERROR;
DBORDINAL cColumns = 0; // Count the columns
DBCOLUMNINFO* prgInfo; // Column info array
OLECHAR* pColNames;
DBCOLUMNACCESS column;
DBCOLUMNINFO* pCurrInfo;
IColumnsInfo* pIColumnsInfo = NULL;
// Initialize
prgInfo = NULL;
pColNames = NULL;
printf("Checking column size\n");
// Get column info to build column access array
hr = pUnkRow->QueryInterface(IID_IColumnsInfo, (void**) &pIColumnsInfo);
if (FAILED(hr))
goto CLEANUP;
hr = pIColumnsInfo->GetColumnInfo(&cColumns, &prgInfo, &pColNames);
if (FAILED(hr))
goto CLEANUP;
printf("Value of cColumns is %d\n", cColumns);
// Setup a DBCOLUMNACCESS structure: Here pData is set to NULL and cbMaxLen is set to 0. Thus
// IRow->GetColumns() returns only the actual column length in cbDataLen member of DBCOLUMNACCESS
// structure. In this case you can call IRow->GetColumns() again for the same column to retrieve
// actual data in the second call.
ZeroMemory((void*) &column, sizeof(DBCOLUMNACCESS));
column.pData = NULL;
pCurrInfo = prgInfo + iCol - 1;
// Get the column id in DBCOLUMNACCESS structure. It is then used in GetColumn().
column.columnid = pCurrInfo->columnid;
printf("column.columnid value is %d\n", column.columnid);
// We know which column to get. The column.columnid gives the column no.
hr = pUnkRow->GetColumns(1, &column);
if (FAILED(hr))
printf("Errors occurred\n");
// Show data
PrintData(1, iCol, prgInfo, &column);
CLEANUP:
if (pIColumnsInfo)
pIColumnsInfo->Release();
return hr;
}
BOOL GetStatus(DWORD dwStatus, WCHAR* pwszStatus) {
switch (dwStatus) {
case DBSTATUS_S_OK:
wcscpy_s(pwszStatus, 255, L"DBSTATUS_S_OK");
break;
case DBSTATUS_E_UNAVAILABLE:
wcscpy_s(pwszStatus, 255, L"DBSTATUS_E_UNAVAILABLE");
break;
case DBSTATUS_S_TRUNCATED:
wcscpy_s(pwszStatus, 255, L"DBSTATUS_S_TRUNCATED");
break;
}
return TRUE;
}
ULONG PrintData(ULONG iCols, ULONG iStart, DBCOLUMNINFO* prgInfo, DBCOLUMNACCESS* prgColumns) {
WCHAR wszStatus[255];
DBCOLUMNINFO* pCurrInfo;
DBCOLUMNACCESS* pCurrCol;
ULONG iidx;
printf("No. Name Status Length Max Data\n");
for ( iidx = 0 ; iidx < iCols ; iidx++ ) {
pCurrInfo = prgInfo + iidx + iStart - 1;
pCurrCol = prgColumns + iidx;
GetStatus(pCurrCol->dwStatus, wszStatus);
// was the data successfully retrieved?
wprintf(L"%-3d %-*s %-20s %-3d %-3d %-20s\n", iStart+iidx,
10,
pCurrInfo->pwszName,
wszStatus,
pCurrCol->cbDataLen,
pCurrCol->cbMaxLen,
(WCHAR*) pCurrCol->pData);
}
wprintf(L"\n");
return iidx;
}
int InitializeAndEstablishConnection() {
// Initialize the COM library.
CoInitialize(NULL);
// Obtain access to the SQLNCLI provider.
hresult = CoCreateInstance(CLSID_SQLNCLI11,
NULL,
CLSCTX_INPROC_SERVER,
IID_IDBInitialize,
(void **) &pIDBInitialize);
if (FAILED(hresult)) {
printf("Failed to get IDBInitialize interface.\n");
// Insert your code for cleanup and error handling.
return -1;
}
// Initialize the property values needed to establish the connection.
for ( i = 0 ; i < 4 ; i++ )
VariantInit(&InitProperties[i].vValue);
// Server name.
InitProperties[0].dwPropertyID = DBPROP_INIT_DATASOURCE;
InitProperties[0].vValue.vt = VT_BSTR;
InitProperties[0].vValue.bstrVal= SysAllocString(L"(local)");
InitProperties[0].dwOptions = DBPROPOPTIONS_REQUIRED;
InitProperties[0].colid = DB_NULLID;
// Database.
InitProperties[1].dwPropertyID = DBPROP_INIT_CATALOG;
InitProperties[1].vValue.vt = VT_BSTR;
InitProperties[1].vValue.bstrVal= SysAllocString(L"AdventureWorks");
InitProperties[1].dwOptions = DBPROPOPTIONS_REQUIRED;
InitProperties[1].colid = DB_NULLID;
InitProperties[2].dwPropertyID = DBPROP_AUTH_INTEGRATED;
InitProperties[2].vValue.vt = VT_BSTR;
InitProperties[2].vValue.bstrVal= SysAllocString(L"SSPI");
InitProperties[2].dwOptions = DBPROPOPTIONS_REQUIRED;
InitProperties[2].colid = DB_NULLID;
// Now that the properties are set, construct the DBPROPSET structure (rgInitPropSet). The DBPROPSET
// structure is used to pass an array of DBPROP structures (InitProperties) to the SetProperties method.
rgInitPropSet[0].guidPropertySet= DBPROPSET_DBINIT;
rgInitPropSet[0].cProperties = 4;
rgInitPropSet[0].rgProperties = InitProperties;
// Set initialization properties.
hresult = pIDBInitialize->QueryInterface(IID_IDBProperties, (void **)&pIDBProperties);
if (FAILED(hresult)) {
cout << "Failed to get IDBProperties interface.\n";
// Insert your code for cleanup and error handling.
return -1;
}
hresult = pIDBProperties->SetProperties(1, rgInitPropSet);
if (FAILED(hresult)) {
cout << "Failed to set initialization properties.\n";
// Insert your code for cleanup and error handling.
return -1;
}
pIDBProperties->Release();
// Now establish the connection to the data source.
if (FAILED(pIDBInitialize->Initialize()))
cout << "Problem establishing connection to the data source.\n";
return 0;
}
USE AdventureWorks2022;
GO
if exists (select name from sysobjects where name = 'MyTable')
drop table MyTable
go