IRowsetFastLoad and nvarchar (1) fields



I'm using IRowsetFastLoad to bulk load a SQL Server 2000
database. I've hit a problem where populating fields defined
as:

nvarchar (1) null

doesn't work. In fact it looks like the loader is cutting my
strings in half. The workaround is to double the field length
definition in my DDL - I wonder if nvarchar (1) really does
mean characters and not bytes!?!

Here's some sample code that exhibits the problem. Once it's
run you can select * from irowsetfastloadunicode and you'll see
that none of the rows inserted have data.

Ideas/suggestions welcomed.



Chris.


// irowsetfastloadunicode.cpp
//

// This program demonstrates a problem populating 1-character
long
// Unicode fields using IRowsetFastLoad.
//
// The program is compiled with Microsoft Visual Studio .NET
2003.
//
// You will need a test database and a test table. The table
can be
// created with this DDL:
//
// CREATE TABLE irowsetfastloadunicode (
// text nvarchar (1) NULL
// )
//
// Search the code below for <username>, <password>, <server>,
and
// <catalogue> and substitute values appropriate for your
setup.

#include "stdafx.h"

#include <sqloledb.h>

// Globals.

// Controls the number of rows inserted during the test.
const int ROWS_TO_INSERT = 10;

const wchar_t* server = L"<server>";
const wchar_t* database = L"<database>";
const wchar_t* username = L"<username>";
const wchar_t* password = L"<password>";

CDataSource g_DataSource;
CSession g_Session;
CDynamicAccessor g_DynamicAccessor;
DBID g_TableId;
CComPtr<IRowsetFastLoad> g_RowsetFastLoad;

// Simple COM initializer/uninitializer

class ComInitializer
{
public:
ComInitializer()
{
::CoInitialize(NULL);
}

~ComInitializer()
{
::CoUninitialize();
}
};

ComInitializer g_OneAndOnlyComInitializer;

// Local functions.

HRESULT OpenDataSource();
HRESULT CloseDataSource();
HRESULT OpenRowsetFastLoad();
HRESULT CloseRowsetFastLoad();
HRESULT InsertData();

int _tmain(int argc, _TCHAR* argv[])
{
OpenDataSource();
OpenRowsetFastLoad();
InsertData();
CloseRowsetFastLoad();
CloseDataSource();

return (0);
}

HRESULT OpenDataSource()
{
HRESULT hr;
CDBPropSet ps;

ps.SetGUID(DBPROPSET_DBINIT);

#ifdef WINDOWS_AUTHENTICATION

ps.AddProperty(DBPROP_AUTH_INTEGRATED, OLESTR("SSPI"));

#else // SQL Server Authentication

ps.AddProperty(DBPROP_AUTH_USERID, username);
ps.AddProperty(DBPROP_AUTH_PASSWORD, password);

#endif // WINDOWS_AUTHENTICATION

ps.AddProperty(DBPROP_INIT_DATASOURCE, server);
ps.AddProperty(DBPROP_INIT_CATALOG, database);

hr = g_DataSource.Open(_T("SQLOLEDB.1"), &ps);

return (hr);
}

HRESULT CloseDataSource()
{
g_Session.Close();
g_DataSource.Close();

if (NULL != g_TableId.uName.pwszName)
{
delete [] g_TableId.uName.pwszName;
g_TableId.uName.pwszName = NULL;
}

return (S_OK);
}

HRESULT OpenRowsetFastLoad()
{
HRESULT hr;
CComPtr<IDBProperties> properties;
CDBPropSet ps;

ps.SetGUID(DBPROPSET_SQLSERVERDATASOURCE);
ps.AddProperty(SSPROP_ENABLEFASTLOAD, true);

hr = g_DataSource.m_spInit->QueryInterface(
IID_IDBProperties,
(LPVOID*) &properties);

if (FAILED(hr))
return (hr);

hr = properties->SetProperties(1, &ps);

if (FAILED(hr))
return (hr);

hr = g_Session.Open(g_DataSource);

if (FAILED(hr))
return (hr);

// This is the table we'll use for the test. See the
comment at
// the top of this file for the DDL to create it.
wchar_t tn[] = L"irowsetfastloadunicode";

g_TableId.eKind = DBKIND_NAME;
g_TableId.uName.pwszName = new WCHAR[wcslen(tn) + sizeof
(wchar_t)];

wcscpy(g_TableId.uName.pwszName, tn);

hr = g_Session.m_spOpenRowset->OpenRowset(
NULL,
&g_TableId,
NULL,
IID_IRowsetFastLoad,
0,
NULL,
(LPUNKNOWN*) &g_RowsetFastLoad);

if (FAILED(hr))
return (hr);

hr = g_DynamicAccessor.BindColumns(g_RowsetFastLoad);

return (hr);
}

HRESULT CloseRowsetFastLoad()
{
if (g_RowsetFastLoad)
{
g_DynamicAccessor.ReleaseAccessors
(g_RowsetFastLoad);
g_DynamicAccessor.Close();

g_RowsetFastLoad = NULL;
}

return (S_OK);
}

HRESULT InsertData()
{
CComQIPtr<IRowset> rs = g_RowsetFastLoad;
DBORDINAL cc = 0;
DBCOLUMNINFO* ci = NULL;
OLECHAR* sb = NULL;
HRESULT hr;

hr = g_DynamicAccessor.GetColumnInfo(
rs,
&cc,
&ci,
&sb);

if (FAILED(hr))
return (hr);

for (DBORDINAL iii = 0; iii < cc; ++iii)
{
// Note: column ordinals are 1-based. We set the
size once for
// the insert. If we were loading NULL for any
field, we'd
// leave the size at 0.
g_DynamicAccessor.SetLength(iii + 1, ci
[iii].ulColumnSize);
}

CoTaskMemFree(ci); ci = NULL;
CoTaskMemFree(sb); sb = NULL;

for (int jjj = 0; jjj < ROWS_TO_INSERT; ++jjj)
{
// The table's "text" field is a 1 character length
nvarchar.
// This string should fit and fill it completely.
wchar_t text[] = L"A";
wchar_t* p = NULL;

// Setting string values is more involved that
other types.
// First we must get a pointer into the buffer
where the string
// value should be stored.

p = (wchar_t*) g_DynamicAccessor.GetValue(1);
wcscpy(p, text);

hr = g_RowsetFastLoad->InsertRow(
g_DynamicAccessor.GetHAccessor(0),
g_DynamicAccessor.GetBuffer());

if (FAILED(hr))
return (hr);
}

hr = g_RowsetFastLoad->Commit(TRUE);

return (hr);
}
.


Loading