TABLE.CPP

//----------------------------------------------------------------------------- 
// Microsoft OLE DB TABLECOPY Sample
// Copyright (C) 1996 By Microsoft Corporation.
//
// @doc
//
// @module TABLE.H
//
//-----------------------------------------------------------------------------

/////////////////////////////////////////////////////////////////////
// Includes
//
/////////////////////////////////////////////////////////////////////
#include "winmain.h"
#include "common.h"
#include "tablecopy.h"
#include "table.h"
#include "wizard.h"
#include "progress.h"


//////////////////////////////////////////////////////////////////////////////
// Defines / Macros
//
//////////////////////////////////////////////////////////////////////////////
#define NO_MATCH0x0000
#define MATCH_EXACT0x0001
#define MATCH_TYPE0x0002
#define MATCH_SIZE0x0004
#define MATCH_DEFAULT0x0008



/////////////////////////////////////////////////////////////////
// CTable::CTable
//
/////////////////////////////////////////////////////////////////
CTable::CTable(CWizard* pCWizard)
{
ASSERT(pCWizard);

m_pCDataSource = new CDataSource;

m_wszIDQuote[0]= EOL;
m_wszIDSeperator[0]= EOL;

m_wszSchemaName[0]= EOL;
m_wszTableName[0]= EOL;
m_wszQualTableName[0]= EOL;

//IndexInfo
m_cIndexes= 0;// Count of indexes
m_rgIndexInfo= NULL;// Index information

//ColumnInfo
m_cColumns= 0;// Count of columns
m_rgColDesc= NULL;// Column information
m_cBlobs= 0;// 0 Blob Columns

m_pCWizard= pCWizard;// Back pointer to Windowing class
}


/////////////////////////////////////////////////////////////////
// CTable::~CTable
//
/////////////////////////////////////////////////////////////////
CTable::~CTable()
{
delete m_pCDataSource;

SAFE_FREE(m_rgIndexInfo);
SAFE_FREE(m_rgColDesc);
}


/////////////////////////////////////////////////////////////////
// BOOL CTable::Connect
//
/////////////////////////////////////////////////////////////////
BOOL CTable::Connect(HWND hWnd)
{
ASSERT(m_pCDataSource);
if(m_pCDataSource->Connect(hWnd))
{
//Get LiteralInfo for this table
GetLiteralInfo();
}

return m_pCDataSource->IsConnected();
}


/////////////////////////////////////////////////////////////////
// BOOL CTable::IsConnected
//
/////////////////////////////////////////////////////////////////
BOOL CTable::IsConnected()
{
ASSERT(m_pCDataSource);
return m_pCDataSource->IsConnected();
}


/////////////////////////////////////////////////////////////////
// BOOL CTable::TranslateNames
//
/////////////////////////////////////////////////////////////////
BOOL CTable::TranslateNames()
{
//Converts all DSN dependents names to allowable chars
MakeAlphaNumeric(m_wszTableName, UNDERSCORE);

for(ULONG i=0; i<m_cColumns; i++)
{
ASSERT(m_rgColDesc);
MakeAlphaNumeric(m_rgColDesc[i].wszColName, UNDERSCORE);
}

for(i=0; i<m_cIndexes; i++)
{
ASSERT(m_rgIndexInfo);
MakeAlphaNumeric(m_rgIndexInfo[i].wszIndexName, UNDERSCORE);
MakeAlphaNumeric(m_rgIndexInfo[i].wszColName, UNDERSCORE);
}

return TRUE;
}


/////////////////////////////////////////////////////////////////
// HRESULT CTable::GetLiteralInfo
//
/////////////////////////////////////////////////////////////////
HRESULT CTable::GetLiteralInfo()
{
ASSERT(m_pCDataSource);
ASSERT(m_pCDataSource->m_pIDBInitialize);

HRESULT hr;

const ULONG cLiterals = 2;
DBLITERAL rgLiterals[cLiterals] = {DBLITERAL_QUOTE, DBLITERAL_CATALOG_SEPARATOR};

IDBInfo* pIDBInfo = NULL;

ULONGcLiteralInfo = 0;
DBLITERALINFO* rgLiteralInfo = NULL;
WCHAR* pwszCharBuffer = NULL;

//Obtain IDBInfo interface
XTESTC(hr = m_pCDataSource->m_pIDBInitialize->QueryInterface(IID_IDBInfo, (void **)&pIDBInfo));

//GetLiteralInfo
XTESTC(hr = pIDBInfo->GetLiteralInfo(cLiterals, rgLiterals, &cLiteralInfo,&rgLiteralInfo, &pwszCharBuffer));

//DBLITERAL_QUOTE
if(rgLiteralInfo[0].fSupported)
wcscpy(m_wszIDQuote, rgLiteralInfo[0].pwszLiteralValue);

//DBLITERAL_CATALOG_SEPARATOR
if(rgLiteralInfo[1].fSupported)
wcscpy(m_wszIDSeperator, rgLiteralInfo[1].pwszLiteralValue);


CLEANUP:
SAFE_RELEASE(pIDBInfo);
SAFE_FREE(rgLiteralInfo);
SAFE_FREE(pwszCharBuffer);
return hr;
}



/////////////////////////////////////////////////////////////////
// BOOL CTable::GetQuotedID
//
/////////////////////////////////////////////////////////////////
BOOL CTable::GetQuotedID(WCHAR* pwszOutBuff, WCHAR* pwszInBuff)
{
return ::GetQuotedID(pwszOutBuff, pwszInBuff, m_wszIDQuote, m_wszIDSeperator);
}



/////////////////////////////////////////////////////////////////
// HRESULT CTable::GetColInfo
//
/////////////////////////////////////////////////////////////////
HRESULT CTable::GetColInfo()
{
ASSERT(m_pCDataSource);
ASSERT(m_pCDataSource->m_pICommandText);
HRESULT hr;

WCHAR wszSqlStmt[MAX_QUERY_LEN]; // from SELECT statement
IColumnsInfo* pIColumnsInfo = NULL;

ULONG cColumns = 0;
DBCOLUMNINFO* rgColInfo = NULL;
WCHAR*rgStringBuffer = NULL;

IRowset* pIRowset = NULL;
LONG cRowsAffected = 0;

ULONG i;
//Create the SELECT statment, BOGUS WHERE Clause
CreateSQLStmt(ESQL_SELECT_BOGUS, wszSqlStmt, FALSE);

//Set the command text
XTESTC(hr = m_pCDataSource->m_pICommandText->SetCommandText(DBGUID_DBSQL, wszSqlStmt));

// Execute the query and retrieve the results
XTESTC(hr = m_pCDataSource->m_pICommandText->Execute(NULL, IID_IRowset, NULL, &cRowsAffected, (IUnknown **)&pIRowset));

//Now finally GetColInfo
XTESTC(hr = pIRowset->QueryInterface(IID_IColumnsInfo, (void **)&pIColumnsInfo));
XTESTC(hr = pIColumnsInfo->GetColumnInfo(&cColumns, &rgColInfo, &rgStringBuffer));

//Loop through the ColInfo and Copy to our ColDesc
m_cColumns = 0;
m_cBlobs = 0;

for(i=0; i<cColumns; i++)
{
//ignore Bookmark columns
if(rgColInfo[i].iOrdinal == 0)
continue;

//Don't need to alloc the rgColDesc struct, since that should have already
//been done in the selection phase of Step1, and the ColName
//Should have already been copied as well, but make sure
ASSERT(m_rgColDesc);
ASSERT(wcscmp(m_rgColDesc[m_cColumns].wszColName, rgColInfo[i].pwszName)==0);

//Now copy the rest of the info
//DBCOLUMNINFO
m_rgColDesc[m_cColumns].iOrdinal= rgColInfo[i].iOrdinal;
m_rgColDesc[m_cColumns].ulColumnSize= rgColInfo[i].ulColumnSize;
m_rgColDesc[m_cColumns].wType= rgColInfo[i].wType;
m_rgColDesc[m_cColumns].dwFlags= rgColInfo[i].dwFlags;
m_rgColDesc[m_cColumns].bPrecision= rgColInfo[i].bPrecision;
m_rgColDesc[m_cColumns].bScale= rgColInfo[i].bScale;

//BLOB Columns
if(rgColInfo[i].dwFlags & DBCOLUMNFLAGS_ISLONG)
m_cBlobs++;

m_cColumns++;
}


CLEANUP:
SAFE_RELEASE(pIRowset);
SAFE_RELEASE(pIColumnsInfo);

SAFE_FREE(rgColInfo);
SAFE_FREE(rgStringBuffer);
return hr;
}



/////////////////////////////////////////////////////////////////
// HRESULT CTable::GetTypeInfo
//
/////////////////////////////////////////////////////////////////
HRESULT CTable::GetTypeInfo()
{
ASSERT(m_pCDataSource);
HRESULT hr;

IRowset*pIRowset = NULL;
IAccessor*pIAccessor = NULL;
HACCESSORhAccessor = DB_NULL_HACCESSOR;

ULONGi;
ULONGcRowsObtained = 0;
HROW*rghRows = NULL;

//Current SchemaInfo for each type, until we find the correct match
TYPEINFO TypeInfo;
TYPEINFO* rgTypeInfo = NULL;
ULONG* rgMatch = NULL;

//Arrays to store best TypeInfo
SAFE_ALLOC(rgTypeInfo, TYPEINFO, m_cColumns);
SAFE_ALLOC(rgMatch, ULONG, m_cColumns);
memset(rgMatch, NO_MATCH, m_cColumns*sizeof(ULONG));

//Get ProviderTypes rowset IDBSchemaRowset
TESTC(hr = GetTypeInfoRowset(&pIAccessor, &hAccessor, &pIRowset));

//Loop over all the Schema TypeInfo rowset
//And match up with ColInfo
while(TRUE)
{
hr = pIRowset->GetNextRows(NULL,0,MAX_BLOCK_SIZE,&cRowsObtained, &rghRows);
if(FAILED(hr) || cRowsObtained==0)
break;

//Loop over the BLOCK of rows obtained
for (i=0; i<cRowsObtained; i++)
{
//Reset all the TypeInfo fields
memset(&TypeInfo, 0, sizeof(TYPEINFO));

//Put the data for one type into the TypeInfo Struct
XTESTC(hr = pIRowset->GetData(rghRows[i],hAccessor, (void*)&TypeInfo));

//Loop over all the columns and see if they match this type
for(ULONG iCol=0; iCol<m_cColumns; iCol++)
{
ASSERT(m_rgColDesc);

//Only try matching if this is the correct type and
//the column doesn't already have a perfect match
if(TypeInfo.wType != m_rgColDesc[iCol].wType || (rgMatch[iCol] & MATCH_EXACT))
continue;

//A Nullable type cannot be mapped to a non-Nullable type
if(m_rgColDesc[iCol].fIsNullable && !TypeInfo.fIsNullable)
continue;

//If never matched before, we at least know they match by type
if(!rgMatch[iCol])
{
rgMatch[iCol] |= MATCH_TYPE;
rgTypeInfo[iCol] = TypeInfo;
}

// Exact type/size matches take precedence
if(COLINFO_SIZE(m_rgColDesc[iCol]) == TypeInfo.ulColumnSize)
{
rgMatch[iCol] |= MATCH_EXACT;
rgTypeInfo[iCol] = TypeInfo;
}

// Otherwise try best fit size
if(COLINFO_SIZE(m_rgColDesc[iCol]) < TypeInfo.ulColumnSize &&
TypeInfo.ulColumnSize < rgTypeInfo[iCol].ulColumnSize)
{
rgMatch[iCol] |= MATCH_SIZE;
rgTypeInfo[iCol] = TypeInfo;
}
}
}

//Release this group of rows
XTESTC(hr = pIRowset->ReleaseRows(cRowsObtained,rghRows,NULL,NULL,NULL));
SAFE_FREE(rghRows);
}

//Now that we have the TypeInfo matched, fill in our ColDesc struct
for(i=0; i<m_cColumns; i++)
{
ASSERT(m_rgColDesc);
if(rgMatch[i])
{
//TYPEINFO
wcscpy(m_rgColDesc[i].wszTypeName, rgTypeInfo[i].wszTypeName);
m_rgColDesc[i].ulCreateParams= GetCreateParams(rgTypeInfo[i].wszCreateParams);
m_rgColDesc[i].fIsNullable= rgTypeInfo[i].fIsNullable == VARIANT_TRUE;
m_rgColDesc[i].guidType= rgTypeInfo[i].guidType;
}
else
{
wMessageBox(NULL, MB_ICONEXCLAMATION | MB_OK, wsz_ERROR,
wsz_NO_TYPE_FOUND_, GetDBTypeName(m_rgColDesc[i].wType));
}
}


CLEANUP:
if(hAccessor && pIAccessor)
XTEST(pIAccessor->ReleaseAccessor(hAccessor,NULL));

SAFE_RELEASE(pIAccessor);
SAFE_RELEASE(pIRowset);

SAFE_FREE(rgMatch);
SAFE_FREE(rgTypeInfo);

SAFE_FREE(rghRows);
return hr;
}


/////////////////////////////////////////////////////////////////
// HRESULT CTable::MapTableInfo
//
/////////////////////////////////////////////////////////////////
HRESULT CTable::MapTableInfo(CTable* pCSourceTable)
{
ASSERT(pCSourceTable);
ASSERT(m_pCDataSource);
HRESULT hr;

IAccessor* pIAccessor = NULL;
HACCESSOR hAccessor = DB_NULL_HACCESSOR;

IRowset* pIRowset = NULL;

ULONGcRowsObtained = 0;
HROW*rghRows = NULL;

//Match bitmask, indicating what type of match was found
BOOL fMatchedAll = FALSE;

TYPEINFO TypeInfo;
TYPEINFO* rgTypeInfo = NULL;
ULONG* rgMatch = NULL;

//ColumnInfo
m_cColumns= pCSourceTable->m_cColumns;
SAFE_FREE(m_rgColDesc);
SAFE_ALLOC(m_rgColDesc, COLDESC, m_cColumns);
memcpy(m_rgColDesc,pCSourceTable->m_rgColDesc, m_cColumns * sizeof(COLDESC));

//IndexInfo
m_cIndexes= pCSourceTable->m_cIndexes;
SAFE_FREE(m_rgIndexInfo);
SAFE_ALLOC(m_rgIndexInfo, INDEXINFO, m_cIndexes);
memcpy(m_rgIndexInfo, pCSourceTable->m_rgIndexInfo, m_cIndexes * sizeof(INDEXINFO));

//Arrays to store best TypeInfo
SAFE_ALLOC(rgTypeInfo, TYPEINFO, m_cColumns);
SAFE_ALLOC(rgMatch, ULONG, m_cColumns);
memset(rgMatch, NO_MATCH, m_cColumns*sizeof(ULONG));

//Get ProviderTypes rowset IDBSchemaRowset
TESTC(hr = GetTypeInfoRowset(&pIAccessor, &hAccessor, &pIRowset));

//Loop until all types are matched.
//We may not find a match, which promotes the type to the next higher
//type, which will require another cycle to match that type...
while(!fMatchedAll)
{
//Get data for each row in rowset
XTESTC(hr = pIRowset->RestartPosition(NULL));

//Loops over the entire SchemaRowset
while(TRUE)
{
hr = pIRowset->GetNextRows(NULL, 0, MAX_BLOCK_SIZE, &cRowsObtained, &rghRows);
if(FAILED(hr) || cRowsObtained == 0)
break;

//Loop over the BLOCK of rows obtained
for (ULONG i=0; i<cRowsObtained; i++)
{
ASSERT(m_rgColDesc);

//Reset all the TypeInfo fields
memset(&TypeInfo, 0, sizeof(TYPEINFO));

//Put the data for one type into the TypeInfo Struct
XTESTC(hr = pIRowset->GetData(rghRows[i], hAccessor, (void *)&TypeInfo));

//Loop over the columns and get TypeInfo
for(ULONG iCol=0; iCol<m_cColumns; iCol++)
{
//Only try matching if this is the correct type and
//the column doesn't already have a perfect match
if(TypeInfo.wType != m_rgColDesc[iCol].wType || (rgMatch[iCol] & MATCH_EXACT))
continue;

//A Nullable type cannot be mapped to a non-Nullable type
if(m_rgColDesc[iCol].fIsNullable && !TypeInfo.fIsNullable)
continue;

//If never matched before, we at least know they match by type
if(!rgMatch[iCol])
{
rgMatch[iCol] |= MATCH_TYPE;
rgTypeInfo[iCol] = TypeInfo;
}

// Exact type/size matches take precedence
if(COLINFO_SIZE(m_rgColDesc[iCol]) == TypeInfo.ulColumnSize)
{
rgMatch[iCol] |= MATCH_EXACT;
rgTypeInfo[iCol] = TypeInfo;
}

// Otherwise try best fit size
if(COLINFO_SIZE(m_rgColDesc[iCol]) < TypeInfo.ulColumnSize &&
TypeInfo.ulColumnSize < rgTypeInfo[iCol].ulColumnSize)
{
rgMatch[iCol] |= MATCH_SIZE;
rgTypeInfo[iCol] = TypeInfo;
}
}
}

//Release this group of rows
XTESTC(hr = pIRowset->ReleaseRows(cRowsObtained, rghRows, NULL, NULL, NULL));
SAFE_FREE(rghRows);
}

// See if every type has a match
fMatchedAll = TRUE;
for(ULONG i=0; i<m_cColumns; i++)
{
ASSERT(m_rgColDesc);

// If not we will have to promote a type and try again
if(rgMatch[i])
{
//If found a match fill in the TypeInfo fileds of our ColDesc
wcscpy(m_rgColDesc[i].wszTypeName, rgTypeInfo[i].wszTypeName);
m_rgColDesc[i].ulCreateParams= GetCreateParams(rgTypeInfo[i].wszCreateParams);
m_rgColDesc[i].fIsNullable= rgTypeInfo[i].fIsNullable == VARIANT_TRUE;
}
else
{
fMatchedAll = FALSE;

//Try to promote it to the next largest type
if(!GetPromotedType(&m_rgColDesc[i].wType))
{
//If unable to promote, we are out of luck
wMessageBox(NULL, MB_ICONEXCLAMATION | MB_OK, wsz_ERROR,
wsz_NO_TYPE_MATCH_, GetDBTypeName(m_rgColDesc[i].wType));
goto CLEANUP;
}
}
}
}


CLEANUP:
if(hAccessor && pIAccessor)
XTEST(pIAccessor->ReleaseAccessor(hAccessor, NULL));

SAFE_RELEASE(pIAccessor);
SAFE_RELEASE(pIRowset);

SAFE_FREE(rgMatch);
SAFE_FREE(rgTypeInfo);

SAFE_FREE(rghRows);
return hr;
}




/////////////////////////////////////////////////////////////////
// HRESULT CTable::CreateTable
//
/////////////////////////////////////////////////////////////////
HRESULT CTable::CreateTable()
{
ASSERT(m_pCDataSource);
ASSERT(m_pCDataSource->m_pICommandText);
HRESULT hr;

BSTRbstrSqlState = NULL;
WCHARwszSqlStmt[MAX_QUERY_LEN];// Create table statement

ULONG cRecords = 0;
IErrorRecords* pIErrorRecords = NULL;
ICommandText* pICommandText = NULL;

Busy();

// Setup the initialize CREATE TABLE '<CTableName>'
CreateSQLStmt(ESQL_CREATE_TABLE, wszSqlStmt);

// Set the Command Text
pICommandText = m_pCDataSource->m_pICommandText;
XTESTC(hr = pICommandText->SetCommandText(DBGUID_DBSQL, wszSqlStmt));

// Execute the command
hr = pICommandText->Execute(NULL, IID_NULL, NULL, NULL, NULL);

// If this didn't work, then we need to display messages, and if the
// error was a duplicate table, offer to drop.
if(FAILED(hr))
{
//Get the Error Records, need to save them, since every call
//cleans the previous error objects
TESTC(GetErrorRecords(&cRecords, &pIErrorRecords));

//If Error was due to an existing table, just ask to drop it
if(GetSqlErrorInfo(0, pIErrorRecords, &bstrSqlState)==S_OK &&
bstrSqlState && wcscmp(bstrSqlState, L"S0001")==0)
{
WCHAR wszBuffer[MAX_QUERY_LEN];

//If the user doesn't wants to drop it, exit
if(IDNO == wMessageBox(NULL, MB_ICONQUESTION | MB_YESNO, wsz_ERROR,
wsz_ASK_DROP_TABLE_, m_wszQualTableName))
goto CLEANUP;

//Otherwise drop that table and continue
CreateSQLStmt(ESQL_DROP_TABLE, wszBuffer);

//Drop the existing Table
XTESTC(hr = pICommandText->SetCommandText(DBGUID_DBSQL, wszBuffer));
XTESTC(hr = pICommandText->Execute(NULL, IID_NULL, NULL, NULL, NULL));

//Now reset the CreateTable text to the SqlStmt and Execute
XTESTC(hr = pICommandText->SetCommandText(DBGUID_DBSQL, wszSqlStmt));
XTESTC(hr = pICommandText->Execute(NULL, IID_NULL, NULL, NULL, NULL));
}
else
{
//Otherwsie unknown error, just display it to the user
DisplayErrorRecords(NULL, cRecords, pIErrorRecords);
}
}

CLEANUP:
Busy();
SAFE_SYSFREE(bstrSqlState);
SAFE_RELEASE(pIErrorRecords);
return hr;
}




/////////////////////////////////////////////////////////////////
// HRESULT CTable::CopyIndexes
//
/////////////////////////////////////////////////////////////////
HRESULT CTable::CopyIndexes(CTable* pCTable)
{
ASSERT(pCTable);
HRESULT hr;

WCHARwszBuffer[MAX_NAME_LEN];
WCHARwszSqlStmt[MAX_QUERY_LEN];
ULONG i;

//Copy Index Info from Source table
m_cIndexes= pCTable->m_cIndexes;

//Array to indicate which index/columns we have used
ULONG* rgIndexUsed = NULL;
SAFE_ALLOC(rgIndexUsed, ULONG, m_cIndexes);
memset(rgIndexUsed, 0, m_cIndexes * sizeof(ULONG));

SAFE_FREE(m_rgIndexInfo);
SAFE_ALLOC(m_rgIndexInfo, INDEXINFO, m_cIndexes);
memcpy(m_rgIndexInfo, pCTable->m_rgIndexInfo, m_cIndexes * sizeof(INDEXINFO));

// Loop around each index that is valid. See if any are to be created
for(i=0; i<m_cIndexes; i++)
{
//If this index has already been created, skip
//might have been used in another index creation
if(rgIndexUsed[i])
continue;

//If this index is used as a primary key, skip
//PrimaryKeys are taken care of differently
if(m_rgIndexInfo[i].fIsPrimaryKey)
continue;

//"CREATE <UNIQUE> INDEX "
swprintf(wszSqlStmt, wsz_CREATE_INDEX_, (m_rgIndexInfo[i].fUnique) ? wsz_UNIQUE_INDEX : wsz_SPACE);

//Add IndexName
GetQuotedID(wszBuffer, m_rgIndexInfo[i].wszIndexName);
wcscat(wszSqlStmt, wszBuffer);

//Add CTableName
wcscat(wszSqlStmt, L" ON ");
GetQuotedID(wszBuffer, m_wszQualTableName);
wcscat(wszSqlStmt, wszBuffer);

wcscat(wszSqlStmt, wsz_LPAREN);

// Now loop through the indexes and find all columns that
// belong to this index
for(ULONG iCol=i; iCol<m_cIndexes; iCol++)
{
//If not the same index skip
if(wcscmp(m_rgIndexInfo[i].wszIndexName, m_rgIndexInfo[iCol].wszIndexName)!=0)
continue;

//mark this Index as used
rgIndexUsed[iCol] = TRUE;

// Add the column to the list
GetQuotedID(wszBuffer, m_rgIndexInfo[iCol].wszColName);
wcscat(wszSqlStmt, wszBuffer);

// Indicate Asending or Decending
if(m_rgIndexInfo[iCol].dwCollation == DB_COLLATION_DESC)
wcscat(wszSqlStmt, wsz_INDEX_DESC);

//Add trailing "," between col names
wcscat(wszSqlStmt, wsz_COMMA);
}

//Replace last trailing "," with a ")"
wcscpy(&wszSqlStmt[wcslen(wszSqlStmt)-wcslen(wsz_COMMA)], wsz_RPAREN);

// If user wants to see the statement, show it to them
if(m_pCWizard->m_pCTableCopy->m_fShowQuery)
wMessageBox(NULL, MB_OK | MB_ICONINFORMATION, wsz_SHOW_SQL_TITLE,
wsz_SHOW_SQL_, m_pCDataSource->m_pwszDataSource, wszSqlStmt);

//Set the command text
XTESTC(hr = m_pCDataSource->m_pICommandText->SetCommandText(DBGUID_DBSQL, wszSqlStmt));

//Execute the command
//Don't exit yet, the user might want to continue even though this index failed
XTEST(hr = m_pCDataSource->m_pICommandText->Execute(NULL, IID_NULL, NULL, NULL, NULL));

//If INDEX Failed
if(FAILED(hr))
{
//Index Failed, Do you want to Continue?
if(IDNO == wMessageBox(NULL, MB_ICONINFORMATION | MB_YESNO, wsz_ERROR,
wsz_INDEX_FAILED_, m_rgIndexInfo[i].wszIndexName))
goto CLEANUP;
}

//Since the User didn't exit, continue as normal
hr = S_OK;
}

CLEANUP:
Busy();
SAFE_FREE(rgIndexUsed);
return hr;
}


/////////////////////////////////////////////////////////////////
// HRESULT CTable::CreateSQLStmt
//
/////////////////////////////////////////////////////////////////
HRESULT CTable::CreateSQLStmt(ESQL_STMT eSqlStmt, WCHAR* pwszSqlStmt, BOOL fShowSql)
{
ASSERT(pwszSqlStmt);
HRESULT hr = S_OK;
WCHAR wszBuffer[MAX_NAME_LEN*2]; // Buffer

switch(eSqlStmt)
{
//SELECT <ColumnList> FROM <QualifiedTableName>
case ESQL_SELECT:
{
//Create the SELECT statment
wcscpy(pwszSqlStmt, wsz_SELECT);

// Loop through each column
for(ULONG i=0; i<m_cColumns; i++)
{
// Add the column to the list
GetQuotedID(wszBuffer, m_rgColDesc[i].wszColName);
wcscat(pwszSqlStmt, wszBuffer);

if(i<m_cColumns-1)
wcscat(pwszSqlStmt, wsz_COMMA);
}

// Add the Table Name
wcscat(pwszSqlStmt, wsz_FROM);
GetQuotedID(wszBuffer, m_wszQualTableName);
wcscat(pwszSqlStmt, wszBuffer);
}
break;

case ESQL_SELECT_BOGUS:
{
//Create the column list first in standard order
wcscpy(pwszSqlStmt, wsz_SELECT);

// Loop through each column
for(ULONG i=0; i<m_cColumns; i++)
{
GetQuotedID(wszBuffer, m_rgColDesc[i].wszColName);
wcscat(pwszSqlStmt, wszBuffer);

if(i<m_cColumns-1)
wcscat(pwszSqlStmt, wsz_COMMA);
}

// Add the table name
wcscat(pwszSqlStmt, wsz_FROM);
GetQuotedID(wszBuffer, m_wszQualTableName);
wcscat(pwszSqlStmt, wszBuffer);

// Finally, add a bogus WHERE clause. Because we want this
// statement to get only metadata, we'll try to tell the
// server not to actually get rows.
wcscat(pwszSqlStmt, wsz_BOGUS_WHERE);
}
break;

case ESQL_INSERT:
{
wcscpy(pwszSqlStmt, wsz_INSERT_INTO);

//Add the Table Name
GetQuotedID(wszBuffer, m_wszQualTableName);
wcscat(pwszSqlStmt, wszBuffer);
wcscat(pwszSqlStmt, wsz_LPAREN);

// Add the column list
for (ULONG i=0; i<m_cColumns; i++)
{
//Only Bind updatable columns
if(m_rgColDesc[i].dwFlags & DBCOLUMNFLAGS_WRITE || m_rgColDesc[i].dwFlags & DBCOLUMNFLAGS_WRITEUNKNOWN)
{
// Quote each column name if required
GetQuotedID(wszBuffer, m_rgColDesc[i].wszColName);
wcscat(pwszSqlStmt, wszBuffer);

// Now add ", "
if(i<m_cColumns-1)
wcscat(pwszSqlStmt, wsz_COMMA);
}
}

// And finally the values list, which will just be parameter markers
wcscat(pwszSqlStmt, wsz_VALUES_CLAUSE);

// Loop through each column
for(i=0; i<m_cColumns; i++)
{
//Only Bind those columns that are updatable
if(m_rgColDesc[i].dwFlags & DBCOLUMNFLAGS_WRITE || m_rgColDesc[i].dwFlags & DBCOLUMNFLAGS_WRITEUNKNOWN)
{
wcscat(pwszSqlStmt, wsz_PARAM);
if(i<m_cColumns-1)
wcscat(pwszSqlStmt, wsz_COMMA);
}
}

// Finish off the string
wcscat(pwszSqlStmt, wsz_RPAREN);
}
break;

case ESQL_CREATE_TABLE:
{

// Setup the initialize CREATE TABLE '<CTableName>'
wcscpy(pwszSqlStmt, wsz_CREATE_TABLE);
GetQuotedID(wszBuffer, m_wszQualTableName);
wcscat(pwszSqlStmt, wszBuffer);

// Setup (<ColList>)
wcscat(pwszSqlStmt, wsz_LPAREN);

// Loop through each column and format the column name, type, and precision
for(ULONG i=0; i<m_cColumns; i++)
{
// Add the ColumnName
GetQuotedID(wszBuffer, m_rgColDesc[i].wszColName);
wcscat(pwszSqlStmt, wszBuffer);

// Add the ColumnType
wcscat(pwszSqlStmt, wsz_SPACE);
wcscat(pwszSqlStmt, m_rgColDesc[i].wszTypeName);

// If required, add the precision and scale information
if(m_rgColDesc[i].ulCreateParams & CP_LENGTH)
{
swprintf(wszBuffer, L"(%lu)", COLINFO_SIZE(m_rgColDesc[i]));
wcscat(pwszSqlStmt, wszBuffer);
}
else if(m_rgColDesc[i].ulCreateParams & CP_PRECISION &&
m_rgColDesc[i].ulCreateParams & CP_SCALE)
{
swprintf(wszBuffer, L"(%lu,%lu)", COLINFO_SIZE(m_rgColDesc[i]), m_rgColDesc[i].bScale);
wcscat(pwszSqlStmt, wszBuffer);
}

//Add PRIMARY KEY if Supported and a PrimaryKey Column
if(m_pCDataSource->m_fPrimaryKeysSupported)
{
BOOL fPrimaryKey = FALSE;

//Loop through the index info and see if this Column
//is supposed to be a primary key of the table
for(ULONG iIndex=0; iIndex<m_cIndexes; iIndex++)
if(m_rgIndexInfo[iIndex].fIsPrimaryKey && wcscmp(m_rgIndexInfo[iIndex].wszColName, m_rgColDesc[i].wszColName)==0)
fPrimaryKey = TRUE;

//If PrimaryKey, add PRIMARY KEY info
if(fPrimaryKey)
wcscat(pwszSqlStmt, wsz_PRIMARY_KEY);
}

if(i<m_cColumns-1)
wcscat(pwszSqlStmt, wsz_COMMA);
}

//Add trailing ")"
wcscat(pwszSqlStmt, wsz_RPAREN);
}
break;

case ESQL_DROP_TABLE:
{
swprintf(pwszSqlStmt, wsz_DROP_TABLE_, m_wszQualTableName);
}
break;

default:
ASSERT(!"Unhandled Case!");
break;
};

// If user wants to see the statement, show it to them
if(m_pCWizard->m_pCTableCopy->m_fShowQuery && fShowSql)
wMessageBox(NULL, MB_OK | MB_ICONINFORMATION, wsz_SHOW_SQL_TITLE,
wsz_SHOW_SQL_, m_pCDataSource->m_pwszDataSource, pwszSqlStmt);

return hr;
}


/////////////////////////////////////////////////////////////////
// HRESULT CTable::GetRowset
//
/////////////////////////////////////////////////////////////////
HRESULT CTable::GetRowset(REFIID riid, IUnknown** ppIRowset, HACCESSOR* phAccessor, ULONG* pRowSize, ULONG ulBlobSize)
{
ASSERT(ppIRowset);
ASSERT(m_pCDataSource);
ASSERT(m_pCDataSource->m_pICommandText);

HRESULT hr;

WCHAR*pwszCol = NULL;// String manipulation
WCHARwszSqlStmt[MAX_QUERY_LEN];// Format the select statement

ULONGi;// Column index

ULONG ulOffset = 0;
ULONG cBindings = 0;
DBBINDING* rgBindings = NULL;

const ULONG cPropSets = 1;
DBPROPSET rgPropSets[cPropSets];
const ULONG cProperties = 3;
DBPROP rgProperties[cProperties];

rgPropSets[0].cProperties = cProperties;
rgPropSets[0].rgProperties = rgProperties;
rgPropSets[0].guidPropertySet = DBPROPSET_ROWSET;

Busy();

ICommandProperties* pICommandProperties = NULL;
IAccessor* pIAccessor = NULL;

//Alloc the space to hold the Bindings
SAFE_ALLOC(rgBindings, DBBINDING, m_cColumns);

//Create the SELECT statment
CreateSQLStmt(ESQL_SELECT, wszSqlStmt);

// Now execute the select statement
LONGcRowsAffected;

rgPropSets[0].cProperties = 0;
if(m_cBlobs)
{
//Kagera's Implementation requires IID_RowsetLocate for BLOB Support
rgPropSets[0].rgProperties[rgPropSets[0].cProperties].dwPropertyID= DBPROP_IRowsetLocate;
rgPropSets[0].rgProperties[rgPropSets[0].cProperties].dwOptions= DBPROPOPTIONS_REQUIRED;
rgPropSets[0].rgProperties[rgPropSets[0].cProperties].dwStatus= DBPROPSTATUS_OK;
rgPropSets[0].rgProperties[rgPropSets[0].cProperties].colid= DB_NULLID;
rgPropSets[0].rgProperties[rgPropSets[0].cProperties].vValue.vt= VT_BOOL;
V_BOOL(&rgPropSets[0].rgProperties[rgPropSets[0].cProperties].vValue) = VARIANT_TRUE;
rgPropSets[0].cProperties++;
}

if(riid == IID_IRowsetChange)
{
//Set UPDATABILITY for IID_IRowsetChange
rgPropSets[0].rgProperties[rgPropSets[0].cProperties].dwPropertyID= DBPROP_UPDATABILITY;
rgPropSets[0].rgProperties[rgPropSets[0].cProperties].dwOptions= DBPROPOPTIONS_REQUIRED;
rgPropSets[0].rgProperties[rgPropSets[0].cProperties].dwStatus= DBPROPSTATUS_OK;
rgPropSets[0].rgProperties[rgPropSets[0].cProperties].colid= DB_NULLID;
rgPropSets[0].rgProperties[rgPropSets[0].cProperties].vValue.vt= VT_I4;
V_I4(&rgPropSets[0].rgProperties[rgPropSets[0].cProperties].vValue) = DBPROPVAL_UP_CHANGE | DBPROPVAL_UP_DELETE | DBPROPVAL_UP_INSERT;
rgPropSets[0].cProperties++;
}

//Set any properties

XTESTC(hr = m_pCDataSource->m_pICommandText->QueryInterface(IID_ICommandProperties, (void**)&pICommandProperties)); 
XTESTC(hr = pICommandProperties->SetProperties(cPropSets, rgPropSets));

//Set the command text
XTESTC(hr = m_pCDataSource->m_pICommandText->SetCommandText(DBGUID_DBSQL, wszSqlStmt));

//Execute the Command
XTESTC(hr = m_pCDataSource->m_pICommandText->Execute(NULL, riid, NULL, &cRowsAffected, (IUnknown**)ppIRowset));

// Prepare the bindings and create the accessor for each column
XTESTC(hr = (*ppIRowset)->QueryInterface(IID_IAccessor, (void **)&pIAccessor));

ulOffset = 0;
cBindings = 0;
for(i=0; i<m_cColumns; i++)
{
//SetUp the Bindings
rgBindings[cBindings].iOrdinal= i+1;
rgBindings[cBindings].obStatus= ulOffset;
rgBindings[cBindings].obLength= ulOffset + sizeof(DBSTATUS);
rgBindings[cBindings].obValue= ulOffset + sizeof(DBSTATUS) + sizeof(ULONG);

rgBindings[cBindings].pTypeInfo = NULL;
rgBindings[cBindings].pBindExt = NULL;

rgBindings[cBindings].dwPart= DBPART_VALUE|DBPART_LENGTH|DBPART_STATUS;
rgBindings[cBindings].dwMemOwner= DBMEMOWNER_CLIENTOWNED;
rgBindings[cBindings].eParamIO= DBPARAMIO_NOTPARAM;

rgBindings[cBindings].dwFlags= 0;
rgBindings[cBindings].bPrecision= m_rgColDesc[i].bPrecision;
rgBindings[cBindings].bScale= m_rgColDesc[i].bScale;

rgBindings[cBindings].pObject= NULL;
rgBindings[cBindings].wType= m_rgColDesc[i].wType;
rgBindings[cBindings].cbMaxLen= m_rgColDesc[i].ulColumnSize;

//Adjust ISLONG Columns if not bound as ISeqStream
if(m_rgColDesc[i].dwFlags & DBCOLUMNFLAGS_ISLONG)
ADJUST_SIZE(rgBindings[cBindings].cbMaxLen, ulBlobSize);

ulOffset = ROUNDUP(rgBindings[cBindings].obValue + rgBindings[cBindings].cbMaxLen);
cBindings++;
}

//Size for pData
if(pRowSize)
*pRowSize = ulOffset;

//Create the accessor for the entire row
if(phAccessor)
XTESTC(hr = pIAccessor->CreateAccessor(DBACCESSOR_ROWDATA, cBindings, rgBindings, 0, phAccessor, NULL));


CLEANUP:
Busy();
FreeBindings(cBindings, rgBindings);
SAFE_RELEASE(pICommandProperties);
SAFE_RELEASE(pIAccessor);
return hr;
}



/////////////////////////////////////////////////////////////////
// HRESULT CTable::CopyData
//
/////////////////////////////////////////////////////////////////
HRESULT CTable::CopyData(CTable* pCTable, ULONG* pcRows, ULONG ulParamSets, ULONG ulBlobSize)
{
ASSERT(pCTable && pcRows);
ASSERT(m_pCDataSource);
ASSERT(m_pCDataSource->m_pICommandText);
HRESULT hr;

WCHARwszSqlStmt[MAX_QUERY_LEN];// Format the select statement
WCHARwszBuffer[MAX_NAME_LEN];

ULONGi,iCol;// Column index
ULONGulOffset = 0;
ULONGcBindings = 0;
DBBINDING*rgBindings = NULL;

IAccessor* pIAccessor = NULL;
HACCESSOR hAccessor = DB_NULL_HACCESSOR;

ULONG cRowSize = 0;

IRowsetChange* pIRowsetChange = NULL;

ULONG cRowsObtained = 0;
HROW* rghRows = NULL;

DBPARAMS DBParams;

void* pData = NULL;
ULONG cRows = 0;
ULONG cMaxRows = *pcRows;

Busy();

HACCESSOR hFromAccessor = DB_NULL_HACCESSOR;
IRowset* pIFromRowset = NULL;

//Get the Rowset from the FromCTable
TESTC(hr = pCTable->GetRowset(IID_IRowset, (IUnknown**)&pIFromRowset, &hFromAccessor, &cRowSize, ulBlobSize));

//Obtain the Accessor
XTESTC(hr = m_pCDataSource->m_pICommandText->QueryInterface(IID_IAccessor, (void **)&pIAccessor));
SAFE_ALLOC(rgBindings, DBBINDING, m_cColumns);

cBindings = 0;
for(i=0; i<m_cColumns; i++)
{
rgBindings[cBindings].iOrdinal= ulParamSets ? cBindings+1 : i+1;
rgBindings[cBindings].obStatus = ulOffset;
rgBindings[cBindings].obLength = ulOffset + sizeof(DBSTATUS);
rgBindings[cBindings].obValue = ulOffset + sizeof(ULONG) + sizeof(DBSTATUS);

rgBindings[cBindings].pTypeInfo = NULL;
rgBindings[cBindings].pBindExt = NULL;

rgBindings[cBindings].dwPart= DBPART_VALUE | DBPART_LENGTH | DBPART_STATUS;
rgBindings[cBindings].dwMemOwner= DBMEMOWNER_CLIENTOWNED;
rgBindings[cBindings].eParamIO= ulParamSets ? DBPARAMIO_INPUT : DBPARAMIO_NOTPARAM;
rgBindings[cBindings].dwFlags= 0;

rgBindings[cBindings].bPrecision= pCTable->m_rgColDesc[i].bPrecision;
rgBindings[cBindings].bScale= pCTable->m_rgColDesc[i].bScale;

rgBindings[cBindings].pObject= NULL;
rgBindings[cBindings].wType= m_rgColDesc[i].wType;
rgBindings[cBindings].cbMaxLen= pCTable->m_rgColDesc[i].ulColumnSize;

//Adjust ISLONG Columns if not bound as ISeqStream
if(m_rgColDesc[i].dwFlags & DBCOLUMNFLAGS_ISLONG)
ADJUST_SIZE(rgBindings[cBindings].cbMaxLen, ulBlobSize);

ulOffset = ROUNDUP(rgBindings[cBindings].obValue + rgBindings[cBindings].cbMaxLen);

//Only Bind Updatable columns
//Note, we are not using the Source info here, since in the process
//of adjusting columns to a different DSN, the columns may have become writeable
if(m_rgColDesc[i].dwFlags & DBCOLUMNFLAGS_WRITE || m_rgColDesc[i].dwFlags & DBCOLUMNFLAGS_WRITEUNKNOWN)
cBindings++;
}

if (cBindings)
XTESTC(hr = pIAccessor->CreateAccessor(ulParamSets ? DBACCESSOR_PARAMETERDATA : DBACCESSOR_ROWDATA, cBindings,rgBindings, ulOffset, &hAccessor, NULL));


//If using Parameters to INSERT the Data
if(ulParamSets)
{
// Now create the INSERT INTO statment
CreateSQLStmt(ESQL_INSERT, wszSqlStmt, ulParamSets);

//Set the command text
XTESTC(hr = m_pCDataSource->m_pICommandText->SetCommandText(DBGUID_DBSQL, wszSqlStmt));
}
//Oterwise were using InsertRow
else
{
//Create the TargetRowset
TESTC(hr = GetRowset(IID_IRowsetChange, (IUnknown**)&pIRowsetChange, NULL, NULL, ulBlobSize));
}


// Display the progress dialog
ASSERT(m_pCWizard && m_pCWizard->m_pCProgress);
m_pCWizard->m_pCProgress->Display();
m_pCWizard->m_pCProgress->SetWorkingItem(wsz_COPYING);

//Alloc room for pData
SAFE_ALLOC(pData, BYTE, max(ulParamSets, MAX_BLOCK_SIZE) * cRowSize);

//Setup DBPARAMS Struct
DBParams.cParamSets = 1;//Numer of Parameter sets
DBParams.hAccessor= hAccessor;//Target Param Accessor
DBParams.pData= pData;//Source Data

while(cRows < cMaxRows)
{
hr = pIFromRowset->GetNextRows(NULL, 0, (ulParamSets > 1) ? ulParamSets : MAX_BLOCK_SIZE, &cRowsObtained, &rghRows);
if(FAILED(hr) || cRowsObtained==0)
break;

//Determine the number of rows that are actually needed to retrieve
//The user might have specfified the number of rows to retrieve which could
//be smaller than the number in the block size
ULONG cRowsNeeded = min(cRowsObtained, cMaxRows-cRows);

//Use Parameters to INSERT the data, use MultipleParamSets
if(ulParamSets > 1)
{
//GetData
for (i=0; i<cRowsNeeded; i++)
XTESTC(hr = pIFromRowset->GetData(rghRows[i], hFromAccessor, (BYTE*)pData + (i*cRowSize)));

//Adjust the paramerer sets
DBParams.cParamSets = cRowsNeeded;

//Execute the INSERT (multiple param sets)
XTESTC(hr = m_pCDataSource->m_pICommandText->Execute(NULL, IID_NULL, &DBParams, NULL, NULL));

// Update insert progress
swprintf(wszBuffer, wsz_COPIED_RECORDS, (cRows += cRowsNeeded));
if(!m_pCWizard->m_pCProgress->UpdateProgress(wsz_COPYING, wszBuffer))
goto CLEANUP;
}
//Use Paramseters to INSERT the data, but only 1 ParamSet (not multiple)
else if(ulParamSets == 1)
{
for (i=0; i<cRowsNeeded; i++)
{
//GetData
XTESTC(hr = pIFromRowset->GetData(rghRows[i], hFromAccessor, pData));

//Execute the INSERT
XTESTC(hr = m_pCDataSource->m_pICommandText->Execute(NULL, IID_NULL, &DBParams, NULL, NULL));

// Update insert progress
swprintf(wszBuffer, wsz_COPIED_RECORDS, cRows++);
if(!m_pCWizard->m_pCProgress->UpdateProgress(wsz_COPYING, wszBuffer))
goto CLEANUP;
}
}
//Use IRowsetChange::InsertRow to INSERT the Data
else
{
for (i=0; i<cRowsNeeded; i++)
{
//GetData from the Source
XTESTC(hr = pIFromRowset->GetData(rghRows[i], hFromAccessor, pData));

//Adjust the Status fields to be DBSTATUS_S_OK | DBSTATUS_S_ISNULL
//GetData mihgt have reported DB_S_TRUNCATED or other minor problems
for(iCol = 0; iCol < cBindings; iCol++)
if(STATUS_IS_BOUND(rgBindings[iCol]) && BINDING_STATUS(rgBindings[iCol], pData) != DBSTATUS_S_ISNULL)
BINDING_STATUS(rgBindings[iCol],pData) = DBSTATUS_S_OK;

//InsertRow to the Target
XTESTC(hr = pIRowsetChange->InsertRow(NULL, hAccessor, pData, NULL));

// Update insert progress
swprintf(wszBuffer, wsz_COPIED_RECORDS, cRows++);
if(!m_pCWizard->m_pCProgress->UpdateProgress(wsz_COPYING, wszBuffer))
goto CLEANUP;
}
}

//Release the group of rows
XTESTC(hr = pIFromRowset->ReleaseRows(cRowsObtained, rghRows, NULL, NULL, NULL));
SAFE_FREE(rghRows);
}

CLEANUP:
//Stop the propgress
m_pCWizard->m_pCProgress->StopProgress();

Busy();
*pcRows = cRows;

//Release Accessors
if(m_pCDataSource->m_pICommandText && hAccessor)
XTEST(pIAccessor->ReleaseAccessor(hAccessor, NULL));
SAFE_RELEASE(pIAccessor);

//Release Accessors
if(pIFromRowset && hFromAccessor)
{
XTEST(pIFromRowset->QueryInterface(IID_IAccessor, (void **)&pIAccessor));
XTEST(pIAccessor->ReleaseAccessor(hFromAccessor, NULL));
}
SAFE_RELEASE(pIAccessor);
SAFE_RELEASE(pIFromRowset);
SAFE_RELEASE(pIRowsetChange);

FreeBindings(cBindings, rgBindings);
SAFE_FREE(rghRows);
SAFE_FREE(pData);
return hr;
}




/////////////////////////////////////////////////////////////////////////////
// HRESULT CTable::GetTypeInfoRowset
//
/////////////////////////////////////////////////////////////////////////////
HRESULT CTable::GetTypeInfoRowset(IAccessor** ppIAccessor, HACCESSOR* phAccessor, IRowset** ppIRowset)
{
ASSERT(ppIAccessor && phAccessor && ppIRowset);
ASSERT(m_pCDataSource && m_pCDataSource->m_pIDBCreateCommand);
HRESULT hr;

IDBSchemaRowset* pIDBSchemaRowset = NULL;

//Bind all the columns from types rowset:
const ULONG cBindings = 6;
const DBBINDING rgBindings[cBindings] =
{
1,
offsetof(TYPEINFO, wszTypeName),// offset of value in consumers buffer
0,// offset of length
0,// offset of status
NULL,// reserved
NULL,// for ole object
NULL,// reserved
DBPART_VALUE,// specifies Value is bound only
DBMEMOWNER_CLIENTOWNED,// memory is client owned
DBPARAMIO_NOTPARAM,//
MAX_NAME_LEN,// size in bytes of the value part in the consumers buffer
0, // reserved
DBTYPE_WSTR, // data type indicator
0,// precision
0, // scale

2,
offsetof(TYPEINFO, wType),// offset of value in consumers buffer
0,// offset of length
0,// offset of status
NULL,// reserved
NULL,// for ole object
NULL,// reserved
DBPART_VALUE,// specifies Value is bound only
DBMEMOWNER_CLIENTOWNED,// memory is client owned
DBPARAMIO_NOTPARAM,//
sizeof(USHORT),// size in bytes of the value part in the consumers buffer
0, // reserved
DBTYPE_UI2, // data type indicator
0,// precision
0, // scale

3,
offsetof(TYPEINFO, ulColumnSize),// offset of value in consumers buffer
0,// offset of length
0,// offset of status
NULL,// reserved
NULL,// for ole object
NULL,// reserved
DBPART_VALUE,// specifies Value is bound only
DBMEMOWNER_CLIENTOWNED,// memory is client owned
DBPARAMIO_NOTPARAM,//
sizeof(ULONG),// size in bytes of the value part in the consumers buffer
0, // reserved
DBTYPE_UI4, // data type indicator
0,// precision
0, // scale

6,
offsetof(TYPEINFO, wszCreateParams),// offset of value in consumers buffer
0,// offset of length
0,// offset of status
NULL,// reserved
NULL,// for ole object
NULL,// reserved
DBPART_VALUE,// specifies Value is bound only
DBMEMOWNER_CLIENTOWNED,// memory is client owned
DBPARAMIO_NOTPARAM,//
MAX_NAME_LEN,// size in bytes of the value part in the consumers buffer
0, // reserved
DBTYPE_WSTR, // data type indicator
0,// precision
0, // scale

7,
offsetof(TYPEINFO, fIsNullable),// offset of value in consumers buffer
0,// offset of length
0,// offset of status
NULL,// reserved
NULL,// for ole object
NULL,// reserved
DBPART_VALUE,// specifies Value is bound only
DBMEMOWNER_CLIENTOWNED,// memory is client owned
DBPARAMIO_NOTPARAM,//
sizeof(VARIANT_BOOL),// size in bytes of the value part in the consumers buffer
0, // reserved
DBTYPE_BOOL, // data type indicator
0,// precision
0, // scale

16,
offsetof(TYPEINFO, guidType),// offset of value in consumers buffer
0,// offset of length
0,// offset of status
NULL,// reserved
NULL,// for ole object
NULL,// reserved
DBPART_VALUE,// specifies Value is bound only
DBMEMOWNER_CLIENTOWNED,// memory is client owned
DBPARAMIO_NOTPARAM,//
sizeof(GUID),// size in bytes of the value part in the consumers buffer
0, // reserved
DBTYPE_GUID, // data type indicator
0,// precision
0, // scale
};

//Return if this interface is not supported
XTESTC(hr = m_pCDataSource->m_pIDBCreateCommand->QueryInterface(IID_IDBSchemaRowset, (void **)&pIDBSchemaRowset));

//GetRowset
//DBSCHEMA_PROVIDER_TYPES is required a SCHEMA
XTESTC(hr = pIDBSchemaRowset->GetRowset(NULL, DBSCHEMA_PROVIDER_TYPES, 0, NULL, IID_IRowset,0, NULL, (IUnknown**)ppIRowset));

//Create the the Accessor
XTESTC(hr = (*ppIRowset)->QueryInterface(IID_IAccessor, (void **)ppIAccessor));
XTESTC(hr = (*ppIAccessor)->CreateAccessor(DBACCESSOR_ROWDATA, cBindings, rgBindings, 0, phAccessor, NULL));


CLEANUP:
SAFE_RELEASE(pIDBSchemaRowset);
return hr;
}