2025-11-28 00:35:46 +09:00

22269 lines
686 KiB
C++

//--------------------------------------------------------------------
// Microsoft OLE DB Test
//
// Copyright 1996-2000 Microsoft Corporation.
//
// @doc
//
// @module ICmdWParams.CPP | ICmdWParams source file
//
//--------------------------------------------------------------------
#include "MODStandard.hpp"
#define DBINITCONSTANTS // Must be defined to initialize constants in oledb.h
#define INITGUID // For IID_ITransactionOptions, etc.
#include "ICmdWPar.h"
#include "msdasql.h"
#include "extralib.h"
#include <time.h>
#include <malloc.h> // _heapchk()
#define FREE_DATA(pData) PROVIDER_FREE(pData)
#define TEST_CHECK(exp, hres) { if (!CHECK ((exp), (hres))) { goto CLEANUP; } }
#define TEST_COMPARE(exp, val) { if (!COMPARE ((exp), (val))) { goto CLEANUP; } }
#define RELEASE(ptr) SAFE_RELEASE(ptr)
#define TEST_PTR(ptr) {if (!ptr) {odtLog << L"Out of memory!\n"; goto CLEANUP;}}
#define ABORT_PTR(ptr) {if (!ptr) {odtLog << L"Out of memory!\n"; fResult = FALSE; goto CLEANUP;}}
#define TEST_ALLOC(ptype, ptr, fill, cb) { ptr = (ptype *)PROVIDER_ALLOC(cb); TEST_PTR(ptr); memset(ptr, fill, cb); }
#define ABORT_ALLOC(ptype, ptr, fill, cb) { ptr = (ptype *)PROVIDER_ALLOC(cb); ABORT_PTR(ptr); memset(ptr, fill, cb); }
#define FAIL_VAR(exp, hres) if (!(exp == hres)) fResult = FALSE
#define FAIL_CHECK(exp, hres) if (!CHECK((exp), (hres))) fResult = FALSE
#define IF_CHECK(exp, hres) if (!CHECK((exp), (hres))) fResult = FALSE; if ((exp) == (hres))
#define FAIL_COMPARE(exp, val) if (!COMPARE((exp), (val))) fResult = FALSE
#define IF_COMPARE(exp, val) if (!COMPARE((exp), (val))) fResult = FALSE; if ((exp) == (val))
#define ABORT_CHECK(exp, hres) { if (!CHECK ((exp), (hres))) { fResult = FALSE; goto CLEANUP; } }
#define ABORT_COMPARE(exp, val) { if (!COMPARE ((exp), (val))) { fResult = FALSE; goto CLEANUP; } }
#define SAFE_RELEASE_ACCESSOR(pIAcc, hAcc) {if ((pIAcc) && (hAcc) && \
CHECK((pIAcc)->ReleaseAccessor((hAcc), NULL), S_OK)) (hAcc) = DB_NULL_HACCESSOR;}
#define DEBUGVAR odtLog << L"***********************************************************************\n";\
odtLog << L"Currently using customized code to track down bugs.\n";\
odtLog << L"Remove debugging code.\n";\
odtLog << L"***********************************************************************\n";
const CLSID CLSID_ConfProv = {0xb2a233c1, 0x5b20, 0x11d0, {0x84, 0x18, 0x0, 0xaa, 0x00, 0x3f, 0xd, 0xd4}};
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
// Module Values
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
// {{ TCW_MODULE_GLOBALS
DECLARE_MODULE_CLSID = { 0x26638211, 0x7c75, 0x11cf, { 0xac, 0xa3, 0x00, 0xaa, 0x00, 0x4a, 0x99, 0xe0 }};
DECLARE_MODULE_NAME("ICommandWithParameters");
DECLARE_MODULE_OWNER("Microsoft");
DECLARE_MODULE_DESCRIP("Test for ICommandWithParameters");
DECLARE_MODULE_VERSION(831765099);
// TCW_WizardVersion(2)
// TCW_Automation(True)
// }} TCW_MODULE_GLOBALS_END
// Globals
typedef struct tagParamStruct
{
DBORDINAL ulColIndex;
DBPARAMIO eParamIO;
WCHAR * pwszParamName;
WCHAR wszDataSourceType[SP_MAX_PARAMNAME_LENGTH];
enum COMPARE_OP eCompareOp;
} ParamStruct;
// Valid storage flags, plus one invalid one
#define STGM_INVALID 0x80000000L // Not currently valid
struct tagStorageModes {
DWORD dwMode;
WCHAR wszMode[30];
};
typedef struct tagStorageModes StorageModes;
const StorageModes g_StorageFlags[]={
EXPAND(STGM_READ),
EXPAND(STGM_WRITE),
EXPAND(STGM_READWRITE),
EXPAND(STGM_SHARE_DENY_NONE),
EXPAND(STGM_SHARE_DENY_READ),
EXPAND(STGM_SHARE_DENY_WRITE),
EXPAND(STGM_SHARE_EXCLUSIVE),
// EXPAND(STGM_DIRECT), // Same as STGM_READ!
EXPAND(STGM_TRANSACTED),
EXPAND(STGM_CREATE),
EXPAND(STGM_CONVERT),
// EXPAND(STGM_FAILIFTHERE), // Same as STGM_READ!
EXPAND(STGM_PRIORITY),
EXPAND(STGM_DELETEONRELEASE),
EXPAND(STGM_INVALID)
};
WCHAR * g_pwszProcedureName;
WCHAR * g_pwszProcedureName2;
BOOL g_bMultipleParamSets;
BOOL g_bSqlServer;
BOOL g_bKagera;
BOOL g_bOracle;
BOOL g_bLuxor;
BOOL g_bConfProv;
ULONG_PTR g_ulOutParamsSupported;
BOOL g_fRowsetTest;
BOOL g_fRowObj;
//--------------------------------------------------------------------
// @func Module level initialization routine
//
// @rdesc Success or Failure
// @flag TRUE | Successful initialization
// @flag FALSE | Initialization problems
//
BOOL ModuleInit(CThisTestModule * pThisTestModule)
{
g_pwszProcedureName = NULL;
g_pwszProcedureName2 = NULL;
g_bMultipleParamSets = FALSE;
g_bSqlServer = FALSE;
g_bKagera = FALSE;
g_bOracle = FALSE;
g_bLuxor = FALSE;
g_bConfProv = FALSE;
g_ulOutParamsSupported = DBPROPVAL_OA_NOTSUPPORTED;
g_fRowsetTest = TRUE;
g_fRowObj = FALSE;
if (ModuleCreateDBSession(pThisTestModule))
{
HRESULT hr=E_FAIL;
IDBCreateCommand *pIDBCreateCommand=NULL;
IDBInitialize * pIDBInitialize = NULL;
CTable * pTable = NULL;
LPWSTR pwszDBMSName = NULL;
LPWSTR pwszName = NULL;
// Fail gracefully and quit module if we don't support Commands
if (SUCCEEDED(hr = pThisTestModule->m_pIUnknown2->QueryInterface(
IID_IDBCreateCommand, (void **)&pIDBCreateCommand)))
{
ICommandWithParameters * pICmdWPar = NULL;
hr = pIDBCreateCommand->CreateCommand(NULL, IID_ICommandWithParameters, (IUnknown **)&pICmdWPar);
pIDBCreateCommand->Release();
SAFE_RELEASE(pICmdWPar);
if (hr == E_NOINTERFACE)
{
odtLog << L"ICommandWithParameters is not supported.\n";
return TEST_SKIPPED;
}
// Fail if we got an error.
if (!pThisTestModule->m_pError->Validate(hr,
LONGSTRING(__FILE__), __LINE__, S_OK))
return FALSE;
}
else
{
// Make sure we returned E_NOINTERFACE if we've failed
if (pThisTestModule->m_pError->Validate(hr,
LONGSTRING(__FILE__), __LINE__, E_NOINTERFACE))
odtLog <<L"Commands are not supported.\n";
return TEST_SKIPPED;
}
// This test doesn't support using an ini file, make sure we're not
if(GetModInfo()->GetFileName())
{
odtLog << L"INFO: Test does not support using fixed table from ini file, resetting...\n";
GetModInfo()->ResetIniFile();
}
// This test doesn't support using INSERT_ROWSETCHANGE because it needs the command text back
if (GetModInfo()->GetInsert() == INSERT_ROWSETCHANGE)
{
odtLog << L"INFO: Test does not support using INSERT_ROWSETCHANGE, resetting to INSERT_COMMAND\n";
GetModInfo()->SetInsert(INSERT_COMMAND);
}
//Create a table we'll use for the whole test module,
//store it in pVoid for now
if (!(pTable = new CTable((IUnknown *)pThisTestModule->m_pIUnknown2,
(LPWSTR)gwszModuleName)))
// (LPWSTR)gwszModuleName, NONULLS)))
{
PRVTRACE (wszMemoryAllocationError);
return FALSE;
}
// Save the table
pThisTestModule->m_pVoid = pTable;
if(!CHECK(pTable->CreateTable(TOTAL_NUMBER_OF_ROWS), S_OK))
return FALSE;
// Create procedure names for default procedures
g_pwszProcedureName = MakeObjectName(L"ICmdProc", wcslen(pTable->GetTableName()));
g_pwszProcedureName2 = MakeObjectName(L"ICmdProc", wcslen(pTable->GetTableName()));
if (!g_pwszProcedureName || !g_pwszProcedureName2)
return FALSE;
if (!VerifyInterface(pThisTestModule->m_pIUnknown, IID_IDBInitialize, DATASOURCE_INTERFACE, (IUnknown**)&pIDBInitialize))
return FALSE;
// Get output param support
GetProperty(DBPROP_OUTPUTPARAMETERAVAILABILITY,
DBPROPSET_DATASOURCEINFO,pIDBInitialize, &g_ulOutParamsSupported);
// Get multiple paramsets support
g_bMultipleParamSets = GetProperty(DBPROP_MULTIPLEPARAMSETS,
DBPROPSET_DATASOURCEINFO,pIDBInitialize, VARIANT_TRUE);
if (pThisTestModule->m_ProviderClsid == CLSID_ConfProv)
g_bConfProv = TRUE;
if(GetProperty(DBPROP_PROVIDERNAME, DBPROPSET_DATASOURCEINFO, pIDBInitialize, &pwszName))
{
if (!wcscmp(pwszName, L"MSDASQL.DLL"))
g_bKagera = TRUE;
if (!wcscmp(pwszName, L"sqloledb.dll"))
g_bLuxor = TRUE;
SAFE_FREE(pwszName);
}
if(GetProperty(DBPROP_DBMSNAME, DBPROPSET_DATASOURCEINFO, pIDBInitialize, &pwszDBMSName))
{
if (!wcscmp(pwszDBMSName, L"Microsoft SQL Server"))
g_bSqlServer = TRUE;
if (!wcscmp(pwszDBMSName, L"Oracle"))
g_bOracle = TRUE;
}
// Hack to keep from pumping out the same failures over and over for known issues.
// Access doesn't support output params or multiple paramsets, but Kagera doesn't know that
// Remoting doesn't support output params or multiple paramsets either, but doesn't report correctly
if (!wcscmp (pwszDBMSName, L"ACCESS"))
{
if (!COMPARE(g_ulOutParamsSupported, DBPROPVAL_OA_NOTSUPPORTED))
{
odtLog << L"Provider doesn't support output params, resetting to DBPROPVAL_OA_NOTSUPPORTED\n";
g_ulOutParamsSupported = DBPROPVAL_OA_NOTSUPPORTED;
}
else
odtLog << L"Provider now returns proper output param support, remove fixup code.\n";
}
if (!wcscmp (pwszDBMSName, L"ACCESS") || GetModInfo()->GetClassContext() == CLSCTX_LOCAL_SERVER)
{
if (!COMPARE(g_bMultipleParamSets, FALSE))
{
odtLog << L"Provider does not support multiple paramsets, resetting to FALSE.\n";
g_bMultipleParamSets = FALSE;
}
else
odtLog << L"Provider now returns proper multipleparamset support, remove fixup code.\n";
}
if (GetModInfo()->GetClassContext() == CLSCTX_LOCAL_SERVER)
{
odtLog << L"Provider does not allow passing valid ppRowset to Execute for output params, IID_NULL will be used.\n";
g_fRowsetTest = FALSE; // Interface Remoting does not yet support a rowset pointer if output params
}
// See if row objects are supported
if (SupportedProperty(DBPROP_IRow, DBPROPSET_ROWSET, pIDBInitialize, ROWSET_INTERFACE))
g_fRowObj = TRUE;
SAFE_RELEASE(pIDBInitialize);
SAFE_FREE(pwszDBMSName);
//If we made it this far, everything has succeeded
return TRUE;
}
return FALSE;
}
//--------------------------------------------------------------------
// @func Module level termination routine
//
// @rdesc Success or Failure
// @flag TRUE | Successful initialization
// @flag FALSE | Initialization problems
//
BOOL ModuleTerminate(CThisTestModule * pThisTestModule)
{
SAFE_FREE(g_pwszProcedureName);
SAFE_FREE(g_pwszProcedureName2);
if (pThisTestModule->m_pVoid)
{
((CTable *)pThisTestModule->m_pVoid)->DropTable();
delete (CTable *)pThisTestModule->m_pVoid;
pThisTestModule->m_pVoid = NULL;
}
return ModuleReleaseDBSession(pThisTestModule);
}
ULONG wcschcount(LPWSTR pwsz, WCHAR ch);
BOOL IsErrorStatus(DBSTATUS sStatus);
DBLENGTH DisplaySize(CCol ColInfo);
size_t FormatString(WCHAR ** ppwszDest, WCHAR * pwszFmt, ULONG cArgs, ...);
size_t FormatStringFromArray(WCHAR ** ppwszDest, WCHAR * pwszFmt, ULONG cArgs, WCHAR ** ppInsertArray);
BOOL AddParam(
DB_UPARAMS iParam,
DBORDINAL iCol,
DBPARAMIO eParamIO,
WCHAR * pwszParamName,
BOOL fBindByName,
DBLENGTH * pcbRowSize,
DBBINDING * pDBBINDINFO,
DBPARAMBINDINFO * pDBPARAMBINDINFO,
ParamStruct * pParamAll,
CTable * pTable,
BOOL fReturnParam = FALSE
);
void FreeParameterNames(DBCOUNTITEM cParams, ParamStruct * pParamInfo);
// Returns the size of a type, including variable length types, since
// WSTR2DBTYE ignores length output for fixed length types.
DBLENGTH GetDataSize(DBTYPE wType, DBLENGTH cbData)
{
if (IsFixedLength(wType))
return GetDBTypeSize(wType);
else
return cbData;
}
LPWSTR GetStandardTypeName(DBTYPE wType)
{
for (ULONG iTypeName=0; iTypeName < g_cStdParams; iTypeName++)
{
if (g_rgStdParamBindInfo[iTypeName].wType == wType)
return g_rgStdParamBindInfo[iTypeName].wszStdTypeName;
}
return NULL;
}
//--------------------------------------------------------------------------
// AddParam
// Add a parameter to the bind information arrays
//
// Return parameter is assumed if pParamInfo is NULL, in which case wType
// and cbParam are used. If pParamInfo is not NULL then this information
// is obtained from the associated table column.
//--------------------------------------------------------------------------
BOOL AddParam(
DB_UPARAMS iParam,
DBORDINAL iCol,
DBPARAMIO eParamIO,
WCHAR * pwszParamName,
BOOL fBindByName,
DBLENGTH * pcbRowSize,
DBBINDING * pDBBINDINFO,
DBPARAMBINDINFO * pDBPARAMBINDINFO,
ParamStruct * pParamAll,
CTable * pTable,
BOOL fReturnParam
)
{
CCol TempCol;
BOOL fIsVariableLength = FALSE;
DBTYPE wType;
DBLENGTH cbParam;
DBLENGTH cbOutParam;
DBLENGTH cbRowSize=0;
WCHAR * pTypeName=NULL;
BYTE bPrecision;
BYTE bScale;
WCHAR * pwszStrip = NULL;
ASSERT(eParamIO != 0);
if (!pcbRowSize)
pcbRowSize = &cbRowSize;
// Get the associated column information
if (!CHECK(pTable->GetColInfo(iCol, TempCol), S_OK))
return FALSE;
// Always use the native type
wType = TempCol.GetProviderType();
bPrecision = (BYTE) TempCol.GetPrecision();
bScale = (BYTE)TempCol.GetScale();
// The CCol information matches GetColumnInfo, which is different for bScale than the spec
// for bScale for GetParameterInfo. Fix it up here.
if (IsNumericType(wType))
{
switch(wType)
{
case DBTYPE_DECIMAL:
case DBTYPE_NUMERIC:
case DBTYPE_VARNUMERIC:
case DBTYPE_R4:
case DBTYPE_R8:
// CCol value is correct
break;
case DBTYPE_CY:
// Scale for money is always 4 per spec
bScale = 4;
break;
default:
// All other numeric types are integers with scale 0. CCol matches
// GetColInfo, which doesn't match spec for GetParameterInfo.
bScale = 0;
break;
}
}
cbParam = TempCol.GetColumnSize();
// For string params we need to have one more space in our bindings for the NULL term.
cbOutParam = TempCol.GetMaxColumnSize();
if (wType == DBTYPE_STR)
cbOutParam+=sizeof(CHAR);
if (wType == DBTYPE_WSTR)
cbOutParam= 2 * cbOutParam + sizeof(WCHAR);
// Save the data source type until SetParameterInfo time
pTypeName=TempCol.GetProviderTypeName();
if (!pTypeName || !pTypeName[0])
{
// We need to use a standard type name
if (!(pTypeName=GetStandardTypeName(wType)))
return FALSE;
}
// Create the DBBINDING info
if (pDBBINDINFO)
{
pDBBINDINFO[iParam].iOrdinal=iParam+1;
pDBBINDINFO[iParam].obLength=*pcbRowSize;
*pcbRowSize+=sizeof(DBLENGTH);
pDBBINDINFO[iParam].obStatus=*pcbRowSize;
*pcbRowSize+=sizeof(DBSTATUS);
// Adjust value for proper alignment
*pcbRowSize = ROUND_UP(*pcbRowSize, ROUND_UP_AMOUNT);
pDBBINDINFO[iParam].obValue=*pcbRowSize;
pDBBINDINFO[iParam].cbMaxLen = cbOutParam;
*pcbRowSize+=cbOutParam;
// Adjust row size for proper alignment
*pcbRowSize = ROUND_UP(*pcbRowSize, ROUND_UP_AMOUNT);
pDBBINDINFO[iParam].pTypeInfo=NULL;
pDBBINDINFO[iParam].pObject=NULL; // We don't handle stream objects here
pDBBINDINFO[iParam].pBindExt=NULL;
pDBBINDINFO[iParam].dwPart=DBPART_VALUE | DBPART_LENGTH | DBPART_STATUS;
pDBBINDINFO[iParam].dwMemOwner=DBMEMOWNER_CLIENTOWNED;
pDBBINDINFO[iParam].eParamIO = eParamIO;
pDBBINDINFO[iParam].dwFlags = 0; // TODO: Handle DBBINDFLAG_HTML?
pDBBINDINFO[iParam].wType = wType;
pDBBINDINFO[iParam].bPrecision = bPrecision;
pDBBINDINFO[iParam].bScale = bScale;
}
// Populate the ParamStruct information for this clause to the one for the whole stmt
if (pParamAll)
{
pParamAll[iParam].ulColIndex = iCol;
pParamAll[iParam].eParamIO = eParamIO;
pParamAll[iParam].pwszParamName = pwszParamName;
wcscpy(pParamAll[iParam].wszDataSourceType, pTypeName);
// Sql Server specific: If the type name contains a left paren, then strip it off
if (pwszStrip = wcsstr(pParamAll[iParam].wszDataSourceType, L"("))
*pwszStrip = L'\0';
// Sql Server specific: If the type name contains "identity, then strip it off
if (pwszStrip = wcsstr(pParamAll[iParam].wszDataSourceType, L"identity"))
*pwszStrip = L'\0';
// We assume the comparison operator will be '=' for this parameter
pParamAll[iParam].eCompareOp = CP_EQ;
}
// Create the DBPARAMBINDINFO information
if (pDBPARAMBINDINFO)
{
pDBPARAMBINDINFO[iParam].pwszDataSourceType = pParamAll[iParam].wszDataSourceType;
pDBPARAMBINDINFO[iParam].pwszName = (fBindByName) ? pwszParamName : NULL;
pDBPARAMBINDINFO[iParam].ulParamSize = cbParam;
// Set the appropriate flags
pDBPARAMBINDINFO[iParam].dwFlags=0;
if (eParamIO & DBPARAMIO_OUTPUT)
pDBPARAMBINDINFO[iParam].dwFlags |= DBPARAMFLAGS_ISOUTPUT;
if (eParamIO & DBPARAMIO_INPUT)
pDBPARAMBINDINFO[iParam].dwFlags |= DBPARAMFLAGS_ISINPUT;
if (TempCol.GetIsLong())
pDBPARAMBINDINFO[iParam].dwFlags |= DBPARAMFLAGS_ISLONG;
// For SQL Server, return params are not nullable, otherwise expect nullability
// to match the associated column
if (TempCol.GetNullable())
{
if (!fReturnParam || !g_bSqlServer)
pDBPARAMBINDINFO[iParam].dwFlags |= DBPARAMFLAGS_ISNULLABLE;
}
if (!TempCol.GetUnsigned())
pDBPARAMBINDINFO[iParam].dwFlags |= DBPARAMFLAGS_ISSIGNED;
pDBPARAMBINDINFO[iParam].bPrecision = bPrecision;
pDBPARAMBINDINFO[iParam].bScale = bScale;
}
return TRUE;
}
// Adjust obStatus, obLength, and obValue due to changes in binding structure.
// Recompute cbRowSize also.
void Repack(DBCOUNTITEM cBind, DBBINDING * pBIND, DBLENGTH * pcbRowSize)
{
ULONG iBind =0;
DBLENGTH ulOffset = 0;
for (iBind=0; iBind < cBind; iBind++)
{
pBIND[iBind].obStatus = ulOffset;
ulOffset+=sizeof(DBSTATUS);
// Adjust for alignment of Length
ulOffset = ROUND_UP(ulOffset,ROUND_UP_AMOUNT);
pBIND[iBind].obLength = ulOffset;
ulOffset+=sizeof(DBLENGTH);
pBIND[iBind].obValue = ulOffset;
// Note we assume cbMaxLen is already set appropriately for variable length types.
pBIND[iBind].cbMaxLen = GetDataSize(pBIND[iBind].wType, pBIND[iBind].cbMaxLen);
ulOffset+=GetDataSize(pBIND[iBind].wType, pBIND[iBind].cbMaxLen);
// Adjust for alignment
ulOffset = ROUND_UP(ulOffset,ROUND_UP_AMOUNT);
}
if (pcbRowSize)
*pcbRowSize = ulOffset;
}
void FreeParameterNames(DBCOUNTITEM cParams, ParamStruct * pParamInfo)
{
if (pParamInfo)
{
for (ULONG iParam = 0; iParam < cParams; iParam++)
SAFE_FREE(pParamInfo[iParam].pwszParamName);
}
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
// CErrorCache
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
#define CCHECK(ErrObj, hrAct, hrExp, ulErr, pwszMsg, fWarn) (ErrObj.CCheck((hrAct), \
(hrExp), (ulErr), (pwszMsg), (fWarn), LONGSTRING(__FILE__), (__LINE__)))
#define CCOMPARE(ErrObj, fResult, ulErr, pwszMsg, fWarn) (ErrObj.CCompare((fResult), \
(ulErr), (pwszMsg), (fWarn), LONGSTRING(__FILE__), (__LINE__)))
enum ERROR_CACHE_ENUM
{
// GetParameterInfo cached errors
EC_INVALID_PRECISION = 1,
EC_INVALID_SCALE,
EC_INVALID_ISSIGNED,
EC_INVALID_ISNULLABLE,
EC_INVALID_ISINPUT,
EC_MISSING_ISINPUT_AND_ISOUTPUT_FLAGS,
EC_UNEXPECTED_S_OK,
EC_INVALID_PARAM_NAME,
EC_MAX_ERROR_NUMBER // Must always be last enum
};
LPWSTR g_ppwszErrorStrings[] =
{
// GetParameterInfo cached errors
L"EC_INVALID_PRECISION",
L"EC_INVALID_SCALE",
L"EC_INVALID_ISSIGNED",
L"EC_INVALID_ISNULLABLE",
L"EC_INVALID_ISINPUT",
L"EC_MISSING_ISINPUT_AND_ISOUTPUT_FLAGS",
L"EC_UNEXPECTED_S_OK",
L"EC_INVALID_PARAM_NAME",
};
class CErrorCache
{
private:
ULONG m_ulDebugMode;
ULONG m_ulMaxCachedErrors;
ULONG * m_pulErrorCache;
ULONG m_cErrorsCached;
BOOL IsErrorCached(ULONG ulError);
public:
CErrorCache(void);
~CErrorCache(void);
BOOL Init(ULONG ulDebugMode);
BOOL CCheck(HRESULT hrActual, HRESULT hrExpected, ULONG ulError = 0,
LPWSTR pwszMessage = NULL, BOOL fWarning = FALSE, LPWSTR pwszFile = NULL,
ULONG ulLine = 0);
BOOL CCompare(BOOL fResult, ULONG ulError = 0, LPWSTR pwszMessage = NULL,
BOOL fWarning = FALSE, LPWSTR pwszFile = NULL, ULONG ulLine = 0);
};
CErrorCache::CErrorCache(void)
{
m_ulDebugMode = 0;
m_ulMaxCachedErrors = EC_MAX_ERROR_NUMBER-1;
m_pulErrorCache = NULL;
m_cErrorsCached = 0;
}
CErrorCache::~CErrorCache(void)
{
// Print summary of errors cached if there is at least one
// error cached.
if (m_cErrorsCached)
{
odtLog << L"\n\nThe following duplicate errors were supressed:\n\n";
for (ULONG iErr = 0; iErr < m_ulMaxCachedErrors; iErr++)
if (m_pulErrorCache[iErr])
odtLog << L"\t" << g_ppwszErrorStrings[iErr] <<
L"\t\t" << m_pulErrorCache[iErr] << L"\n";
odtLog << L"\n\n";
odtLog << L"To see all error details instead of this summary please add 'DEBUGMODE=FULL;' to the init string.\n\n";
}
SAFE_FREE(m_pulErrorCache);
}
BOOL CErrorCache::Init(ULONG ulDebugMode)
{
BOOL fResult = FALSE;
m_ulDebugMode = ulDebugMode;
// Allocate memory for error cache. Since we don't expect this to be more than
// a few 10's of items just use a static array.
SAFE_ALLOC(m_pulErrorCache, ULONG, m_ulMaxCachedErrors);
// Init all cache locations to 0, no error cached
memset(m_pulErrorCache, 0, m_ulMaxCachedErrors*sizeof(ULONG));
fResult = TRUE;
CLEANUP:
return fResult;
}
BOOL CErrorCache::IsErrorCached(ULONG ulError)
{
ASSERT(ulError <= m_ulMaxCachedErrors);
// In case this gets called before init.
if (!m_pulErrorCache)
return FALSE;
// If the cache has a value other than 0 then it's been cached
return m_pulErrorCache[ulError-1];
}
BOOL CErrorCache::CCheck(HRESULT hrActual, HRESULT hrExpected, ULONG ulError,
LPWSTR pwszMessage, BOOL fWarning, LPWSTR pwszFile,
ULONG ulLine)
{
BOOL fReturn = hrActual == hrExpected;
// Check for valid error number
if (ulError == 0 || ulError > m_ulMaxCachedErrors)
return FALSE;
// If this error is already cached then we allow it to pass unless debugmode is full.
if (IsErrorCached(ulError) && !(m_ulDebugMode & DEBUGMODE_FULL))
{
if (!fReturn)
// Update the cache
m_pulErrorCache[ulError-1]++;
return fReturn;
}
// Otherwise we have to perform the comparison. Note we can't just return the
// value from PrivLibValidate because on warning it always returns TRUE even on
// a miscompare
PrivlibValidate(hrActual, hrExpected, fWarning, pwszFile, ulLine);
// If the comparison failed, then print failure message
if (!fReturn)
{
// Update the cache
if (!(m_ulDebugMode & DEBUGMODE_FULL))
{
m_pulErrorCache[ulError-1]++;
m_cErrorsCached++;
}
if (pwszMessage)
odtLog << pwszMessage << L"\n";
}
return fReturn;
}
BOOL CErrorCache::CCompare(BOOL fResult, ULONG ulError, LPWSTR pwszMessage,
BOOL fWarning, LPWSTR pwszFile, ULONG ulLine)
{
// Check for valid error number
if (ulError == 0 || ulError > m_ulMaxCachedErrors)
return FALSE;
// If this error is already cached then we allow it to pass unless debugmode is full.
if (IsErrorCached(ulError) && !(m_ulDebugMode & DEBUGMODE_FULL))
{
if (!fResult)
// Update the cache
m_pulErrorCache[ulError-1]++;
return fResult;
}
// Otherwise we have to perform the comparison. Note we can't just return the
// value from PrivLibValidate because on warning it always returns TRUE even on
// a miscompare
PrivlibCompare(fResult, fWarning, pwszFile, ulLine);
// If the comparison failed, then print failure message
if (!fResult)
{
if (!(m_ulDebugMode & DEBUGMODE_FULL))
{
m_pulErrorCache[ulError-1]++;
m_cErrorsCached++;
}
if (pwszMessage)
odtLog << pwszMessage << L"\n";
}
return fResult;
}
class ICmdWParSequentialStream : public ISequentialStream {
private:
ULONG m_ulRefCount;
ULONG m_ulBufSize;
ULONG m_ulReadSize;
ULONG m_ulBytesLeft;
ULONG m_ulReadPos;
BYTE * m_pSrcData;
BYTE * m_pReadPtr;
BOOL m_fWasRead;
BOOL m_fFree;
public:
ICmdWParSequentialStream(void)
{
m_ulRefCount=1;
m_ulBufSize=0;
m_ulReadSize=0;
m_ulBytesLeft=0;
m_ulReadPos=0;
m_pSrcData=NULL;
m_pReadPtr=NULL;
m_fWasRead = FALSE;
m_fFree = FALSE;
}
~ICmdWParSequentialStream(void)
{
// If user requested stream object to free the source buffer
if (m_fFree)
SAFE_FREE(m_pSrcData);
}
virtual ULONG STDMETHODCALLTYPE AddRef(void) {return ++m_ulRefCount;}
virtual ULONG STDMETHODCALLTYPE Release(void)
{
--m_ulRefCount;
if (m_ulRefCount == 0)
{
delete this;
return 0;
}
return m_ulRefCount;
}
virtual HRESULT STDMETHODCALLTYPE QueryInterface(REFIID riid, void ** ppvObj)
{
HRESULT hr=E_FAIL;
if (!ppvObj)
return E_INVALIDARG;
else
*ppvObj = NULL;
if (riid != IID_ISequentialStream && riid != IID_IUnknown)
return E_NOINTERFACE;
AddRef();
*ppvObj = this;
return S_OK;
}
HRESULT Init(const void * pSrcData, const ULONG ulBufSize, const ULONG ulReadSize, BOOL fFree = FALSE)
{
// Must have a source
if (NULL == pSrcData)
return E_INVALIDARG;
// Data length must be non-zero
if (0 == ulBufSize)
return E_INVALIDARG;
m_ulBufSize=ulBufSize;
m_ulReadSize=ulReadSize;
m_pSrcData = (BYTE *)pSrcData;
m_pReadPtr = m_pSrcData;
m_ulBytesLeft=m_ulReadSize;
m_ulReadPos=0;
m_fWasRead = FALSE;
m_fFree = fFree;
return S_OK;
}
virtual HRESULT STDMETHODCALLTYPE Write(const void * pv, ULONG cb, ULONG * pcbWritten)
{
return E_NOTIMPL;
}
virtual HRESULT STDMETHODCALLTYPE Read(void * pv, ULONG cb, ULONG * pcbRead)
{
ULONG ulBytesWritten=0;
ULONG ulCBToWrite=cb;
ULONG ulCBToCopy;
BYTE * pvb = (BYTE *)pv;
m_fWasRead = TRUE;
if (NULL == m_pSrcData)
return E_FAIL;
if (NULL == pv)
return STG_E_INVALIDPOINTER;
while (ulBytesWritten < ulCBToWrite && m_ulBytesLeft)
{
// Make sure we don't write more than our max read size or the size they asked for
ulCBToCopy=min(m_ulBytesLeft, cb);
// Make sure we don't read past the end of the internal buffer
ulCBToCopy=min(m_ulBufSize-m_ulReadPos, ulCBToCopy);
memcpy(pvb, m_pReadPtr+m_ulReadPos, ulCBToCopy);
pvb+=ulCBToCopy;
ulBytesWritten+=ulCBToCopy;
m_ulBytesLeft-=ulCBToCopy;
cb-=ulCBToCopy;
// Wrap reads around the src buffer
m_ulReadPos+=ulCBToCopy;
if (m_ulReadPos >= m_ulBufSize)
m_ulReadPos = 0;
}
if (pcbRead)
*pcbRead=ulBytesWritten;
return S_OK;
}
BOOL WasRead(void)
{
return m_fWasRead;
}
LPBYTE GetSrcBuffer(void)
{
return m_pSrcData;
}
ULONG GetSrcBufferSize(void)
{
return m_ulBufSize;
}
ULONG GetReadSize(void)
{
return m_ulReadSize;
}
void Rewind(void)
{
m_pReadPtr = m_pSrcData;
m_ulBytesLeft=m_ulReadSize;
m_ulReadPos=0;
}
};
//--------------------------------------------------------------------
// Utility class to return provider specific syntax for procedures
//
class CSyntax {
private:
WCHAR * m_pwszProviderName;
WCHAR * m_pwszDBMSName;
WCHAR * m_pwszProcName;
enum PROVIDER_ENUM m_eProvider; // The provider
enum DBMS_ENUM m_eDBMS; // The DBMS
enum DIALECT_ENUM m_eDialect; // The dialect
const ProviderList * m_pProviderList; // List of providers
const DBMSList * m_pDBMSList; // List of DBMS's
ULONG m_cDialectTokens; // Total dialect tokens in list
const DialectTokens * m_pDialectTokens; // List of dialect tokens
const Dialects * m_pDialectList; // List of dialects given provider and DBMS
CTable * m_pTable; // Table to use to build column name, parm name lists from
DBBINDING * m_pDBBINDING; // User's bindings
DBCOUNTITEM m_cParams; // Count of parameters
ParamStruct * m_pColmap; // Mapping of parameters to columns
ULONG m_iCurrentParam; // Current parameter to build syntax for
DBCOUNTITEM m_iCurrentRow; // Current table row to build syntax for
BOOL m_fColCounts; // Whether column count and mappings are needed for this syntax element
BOOL m_fHasReturnParam; // Whether procedure has a return parameter
ULONG m_ulMaxParamName; // Maximum length of a parameter for boundary testing.
ULONG m_ulCreateFlags; // Flags to control parameter creation.
enum TOKEN_ENUM m_eProcType;
// Given the provider name find the provider in our known provider list
void SetProvider(void)
{
for (ULONG idx=0; idx < UNKNOWN_PROVIDER; idx++)
{
if (!wcscmp(m_pwszProviderName, m_pProviderList[idx].wszProviderName))
{
m_eProvider=m_pProviderList[idx].eProvider;
break;
}
}
}
// Given the DBMS name find the DBMS in our known DBMS list
void SetDBMS(void)
{
for (ULONG idx=0; idx < UNKNOWN_DBMS; idx++)
{
if (!wcscmp(m_pwszDBMSName, m_pDBMSList[idx].wszDBMSName))
{
m_eDBMS=m_pDBMSList[idx].eDBMS;
break;
}
}
}
// Given the Provider and DBMS set the dialect used
void SetDialect(void)
{
for (ULONG idx=0;
m_pDialectList[idx].eProvider != UNKNOWN_PROVIDER || m_pDialectList[idx].eDBMS != UNKNOWN_DBMS;
idx++)
{
if ((m_pDialectList[idx].eProvider == m_eProvider && m_pDialectList[idx].eDBMS == m_eDBMS) ||
(m_pDialectList[idx].eProvider == UNKNOWN_PROVIDER && m_pDialectList[idx].eDBMS == UNKNOWN_DBMS))
{
// We found the provider/dbms combo or they're unknown, set the dialect
m_eDialect = m_pDialectList[idx].eDialect;
break;
}
}
}
void SetCurrentParam(ULONG iParam)
{
m_iCurrentParam = iParam;
}
// Creates a parameter definition string for a given parameter
WCHAR * MakeParamDef(ULONG iParam, BOOL fDefaultVal)
{
CCol TempCol;
enum TOKEN_ENUM eParamDef=T_PARM_DEF_IN;
WCHAR * pwszParamDef = NULL;
// Get the information about the column
if (FAILED(m_pTable->GetColInfo(m_pColmap[iParam].ulColIndex, TempCol)))
goto CLEANUP;
if ((m_pColmap[iParam].eParamIO & DBPARAMIO_INPUT) &&
(m_pColmap[iParam].eParamIO & DBPARAMIO_OUTPUT))
eParamDef = T_PARM_DEF_INOUT;
else if (m_pColmap[iParam].eParamIO & DBPARAMIO_INPUT)
eParamDef = T_PARM_DEF_IN;
else if (m_pColmap[iParam].eParamIO & DBPARAMIO_OUTPUT)
eParamDef = T_PARM_DEF_OUT;
else
goto CLEANUP;
// Tell the syntax builder what parameter we're working with
SetCurrentParam(iParam);
// Retrieve parameter definition format
pwszParamDef = GetSyntax(eParamDef);
CLEANUP:
return pwszParamDef;
}
// Builds special token strings based on table/param info
WCHAR * BuildTokenString(enum TOKEN_ENUM eToken, DBORDINAL * pcColsAdded, DB_LORDINAL ** prgColsAdded)
{
CCol TempCol;
ULONG iCol, iStartCol;
DBORDINAL cCols;
WCHAR * pwszStr=NULL;
WCHAR * pwszName=NULL;
WCHAR * pwszParamName = NULL;
WCHAR * pwszParamNameFmt = NULL;
WCHAR * pwszParamDef = NULL;
WCHAR * pwszColDef = NULL;
WCHAR * pwszSize = NULL;
WCHAR * pwszParamMarker = NULL;
WCHAR * pwszStrip = NULL;
ULONG iParam=0;
BOOL fError=TRUE;
DBORDINAL cColsAdded = 0;
DB_LORDINAL * pColsAdded = NULL;
const ULONG ulColNameMax = m_ulMaxParamName;
cCols = m_pTable->CountColumnsOnTable();
TEST_ALLOC(DB_LORDINAL, pColsAdded, 0, (size_t)(cCols * sizeof(DB_LORDINAL)));
// We know how to build certain strings from the table and/or col map
switch(eToken)
{
case T_ORDER_COL:
// Find the numeric col
if (SUCCEEDED(m_pTable->GetOrderByCol(&TempCol)))
pwszStr = wcsDuplicate(TempCol.GetColName());
break;
case T_FIRST_PARM:
// We MUST have information about the number of params and
// we can't make a name for a non-existent param
if (!m_cParams || !m_pColmap || m_iCurrentParam+1 > m_cParams)
goto CLEANUP;
pwszStr = wcsDuplicate(m_pColmap[0].pwszParamName);
break;
case T_PARM_NAME:
// We MUST have information about the number of params and
// we can't make a name for a non-existent param
if (!m_cParams || !m_pColmap || m_iCurrentParam+1 > m_cParams)
goto CLEANUP;
pwszStr = wcsDuplicate(m_pColmap[m_iCurrentParam].pwszParamName);
break;
case T_RET_TYPE:
// We MUST have information about the number of params and
// we can't make a name for a non-existent param
if (!m_cParams || !m_pColmap)
goto CLEANUP;
pwszStr = wcsDuplicate(m_pColmap[0].wszDataSourceType);
break;
case T_RET_DEF:
// We MUST have information about the mapping of the parameter to column
// so we can look up the proper create params based on the column.
if (!m_cParams || !m_pColmap)
goto CLEANUP;
// Return param is always the first parameter
pwszStr = MakeParamDef(0, FALSE);
break;
case T_RET_VAL:
// Return parameter is always the first one
m_iCurrentParam = 0;
// Fall through
case T_DATA_VAL:
{
WCHAR * pwszPrefix = NULL;
WCHAR * pwszSuffix = NULL;
WCHAR wszData[DATA_SIZE] = L"";
size_t ulLiteral = 0;
HRESULT hr;
// We always just return a literal that matches the data type requested
// for the return value. There must be a column of that type in the table
// for output value verification.
// We MUST have information about the number of params and
// we can't make a literal for a non-existent param
if (!m_cParams || !m_pColmap || m_iCurrentParam+1 > m_cParams || !m_iCurrentRow)
goto CLEANUP;
// Get the information about the associated column.
if (FAILED(m_pTable->GetColInfo(m_pColmap[m_iCurrentParam].ulColIndex, TempCol)))
goto CLEANUP;
// Get the literal prefix
if (pwszPrefix = TempCol.GetPrefix())
ulLiteral+=wcslen(pwszPrefix);
// Get the literal suffix
if (pwszSuffix = TempCol.GetSuffix())
ulLiteral+=wcslen(pwszSuffix);
// Create a wchar data value
hr=m_pTable->MakeData(wszData, m_iCurrentRow, m_pColmap[m_iCurrentParam].ulColIndex, PRIMARY, TRUE);
// If makedata failed we're hosed
if (FAILED(hr))
goto CLEANUP;
// If the data is supposed to be NULL, then that's the literal
if (S_FALSE == hr)
pwszStr = wcsDuplicate(L"NULL");
else
{
//Special case Data Types...
switch(TempCol.GetProviderType())
{
case DBTYPE_BOOL:
{
//MakeData may return "0","1" or "False","True" (if using INI File)
//Out INI File has it in the format from DBTYPE_BOOL -> DBTYPE_WSTR
//Which should always be in "False","True" according to the spec
//But since we maybe formulating Literals, they must be "0","1" for Boolean literals...
if(wcscmp(wszData, L"True")==0)
wcscpy(wszData, L"1");
else if(wcscmp(wszData, L"False")==0)
wcscpy(wszData, L"0");
break;
}
}
// Need to add one more for null term
ulLiteral+=wcslen(wszData)+1;
// Allocate space for the literal from the combined size of each
TEST_ALLOC(WCHAR, pwszStr, 0, ulLiteral*sizeof(WCHAR));
pwszStr[0] = L'\0';
// Copy the pieces into the literal
if (pwszPrefix)
wcscat(pwszStr, pwszPrefix);
wcscat(pwszStr, wszData);
if (pwszSuffix)
wcscat(pwszStr, pwszSuffix);
}
}
break;
case T_PARM_SIZE:
// We MUST have information about the number of params and
// we can't make a name for a non-existent param
if (!m_cParams || !m_pColmap || m_iCurrentParam+1 > m_cParams)
goto CLEANUP;
// Get the information about the column
if (FAILED(m_pTable->GetColInfo(m_pColmap[m_iCurrentParam].ulColIndex, TempCol)))
goto CLEANUP;
TempCol.CreateColDef(&pwszColDef);
// Sql Server specific: If the col def contains "identity", then strip it off
if (pwszColDef && (pwszStrip = wcsstr(pwszColDef, L"identity")))
*pwszStrip = L'\0';
// If we have a size specification return it
if (pwszColDef && (pwszSize = wcsstr(pwszColDef, L"(")))
pwszStr = wcsDuplicate(pwszSize);
else
// Otherwise return an empty string
pwszStr = wcsDuplicate(L"");
PROVIDER_FREE(pwszColDef);
break;
case T_PARM_TYPE:
// We MUST have information about the number of params and
// we can't make a name for a non-existent param
if (!m_cParams || !m_pColmap || m_iCurrentParam+1 > m_cParams)
goto CLEANUP;
pwszStr = wcsDuplicate(m_pColmap[m_iCurrentParam].wszDataSourceType);
break;
case T_OFFSET_NO_LONG_PARM_MARKER_EQ_LIST:
if (!(pwszParamMarker = GetSyntax(T_PARM_MARKER)))
goto CLEANUP;
// Fall through
case T_OFFSET_NO_LONG_PARM_EQ_LIST:
// In this case the number of params in the limit clause should match
// the number in the output list.
// We MUST have information about the number of params
if (!m_cParams || !m_pColmap)
goto CLEANUP;
// We assume space for column name=param name+comma+space
// will never be greater than 2 + 2*columnsize.
TEST_ALLOC(WCHAR, pwszStr, 0, (size_t)m_cParams * (3 + 2*ulColNameMax) * sizeof(WCHAR));
// Go through each parameter and add to our lists
for (iParam=0; iParam < m_cParams; iParam++)
{
// We'll get the column name from the next parameter, not this one
ULONG iColParam = iParam+1;
// Wrap back to first param
if (iParam == m_cParams-1)
iColParam = 0;
// Get the information about the column
if (FAILED(m_pTable->GetColInfo(m_pColmap[iColParam].ulColIndex, TempCol)))
goto CLEANUP;
if (pwszStr[0])
wcscat(pwszStr, L",");
if (eToken == T_OFFSET_NO_LONG_PARM_EQ_LIST)
wcscat(pwszStr, m_pColmap[iParam].pwszParamName);
else
wcscat(pwszStr, pwszParamMarker);
wcscat(pwszStr, L"=");
wcscat(pwszStr, TempCol.GetColName());
}
PROVIDER_FREE(pwszParamMarker);
break;
case T_PARM_MARKER_LIST:
case T_PARM_MARKER_LIST_DFLT:
case T_PARM_MARKER_LIST_RET:
// We MUST have information about the number of params
if (!m_cParams || !m_pColmap)
goto CLEANUP;
if (!(pwszParamMarker = GetSyntax(T_PARM_MARKER)))
goto CLEANUP;
// Allocate space for L"?, " for each param (marker list)
TEST_ALLOC(WCHAR, pwszStr, 0, (size_t)m_cParams * (wcslen(pwszParamMarker) + sizeof(L",")));
iParam = 0;
// Skip creation of the first marker character for these types of marker lists
if (eToken == T_PARM_MARKER_LIST_RET ||
eToken == T_PARM_MARKER_LIST_DFLT)
iParam++;
for (; iParam < m_cParams; iParam++)
{
if (!((m_pColmap[iParam].eCompareOp == CP_ISNULL ||
m_pColmap[iParam].eCompareOp == CP_ISNOTNULL) &&
!(m_pColmap[iParam].eParamIO & DBPARAMIO_OUTPUT)))
{
if (pwszStr[0])
wcscat(pwszStr, L",");
wcscat(pwszStr, pwszParamMarker);
}
}
break;
case T_NO_LONG_PARM_DEF_LIST:
case T_UPDATABLE_PARM_DEF_LIST:
case T_NO_LONG_PARM_DEF_LIST_DFLT:
{
BOOL fDefault = FALSE; // We only create a default value for the first param
DBCOUNTITEM cbMem = m_cParams * (2 + 3*ulColNameMax) * sizeof(WCHAR);
if (eToken == T_NO_LONG_PARM_DEF_LIST_DFLT)
fDefault = TRUE;
// parm1 inout type1(createparams)
// We MUST have information about the mapping of the parameter to column
// so we can look up the proper create params based on the column.
if (!m_cParams || !m_pColmap)
goto CLEANUP;
// We assume space for column/param name+space+inputoutput+space+typenamemax+(p, s)
// will never be greater than 2 + 3*columnsize.
TEST_ALLOC(WCHAR, pwszStr, 0, (size_t)cbMem);
// Go through each parameter and add to our lists
for (iParam=(m_fHasReturnParam ? 1 : 0); iParam < m_cParams; iParam++)
{
BOOL fAdd = TRUE;
// If the parameter is using the IS NULL syntax it's not really
// a parameter, so leave it off
if (m_pColmap[iParam].eCompareOp == CP_ISNULL ||
m_pColmap[iParam].eCompareOp == CP_ISNOTNULL)
fAdd = FALSE;
// But output only params need to be left in
if (m_pColmap[iParam].eParamIO & DBPARAMIO_OUTPUT)
fAdd = TRUE;
// And we need to add it anyway if it's an insert sproc
if (m_eProcType == T_EXEC_PROC_INSERT_INPUT)
fAdd = TRUE;
// If the parameter is using the IS NULL syntax it's not really
// a parameter, so leave it off
/*
if (!((m_pColmap[iParam].eCompareOp == CP_ISNULL ||
m_pColmap[iParam].eCompareOp == CP_ISNOTNULL) &&
!(m_pColmap[iParam].eParamIO & DBPARAMIO_OUTPUT)))
*/
if (fAdd)
{
if (!(pwszParamDef = MakeParamDef(iParam, fDefault)))
goto CLEANUP;
// Need space for pwszParamDef, ", ", and null terminator.
if ((wcslen(pwszStr)+wcslen(pwszParamDef)+3)*sizeof(WCHAR) > cbMem)
{
// Realloc twice as much as needed to allow fewer reallocations
cbMem= (wcslen(pwszStr)+wcslen(pwszParamDef)+3)*sizeof(WCHAR)*2;
SAFE_REALLOC(pwszStr, WCHAR, cbMem)
}
if (pwszStr[0])
wcscat(pwszStr, L", ");
wcscat(pwszStr, pwszParamDef);
PROVIDER_FREE(pwszParamDef);
fDefault = FALSE;
}
}
}
break;
case T_UNIQUE_SEARCHABLE_COL_EQ_PARM_MARKER:
case T_SEARCHABLE_COL_EQ_PARM_MARKER:
case T_NO_LONG_PARM_MARKER_EQ_LIST:
if (!(pwszParamMarker = GetSyntax(T_PARM_MARKER)))
goto CLEANUP;
// Fall through
case T_SEARCHABLE_COL_EQ_PARM:
case T_OFFSET_SEARCHABLE_COL_EQ_PARM:
case T_UNIQUE_SEARCHABLE_COL_EQ_PARM:
case T_NO_LONG_PARM_LIST:
case T_NO_LONG_PARM_EQ_LIST:
case T_NULL_PARM_EQ_LIST:
case T_FIRST_COL_EQ_PARM:
case T_LOOKUP_PARM_EQ_LIST:
case T_UPDATABLE_PARM_LIST:
// Retrieve the syntax for parameter names
if (!(pwszParamNameFmt = GetSyntax(T_PARM_NAME_FMT)))
goto CLEANUP;
// We need to know the param mapping
ASSERT(m_cParams && m_pColmap);
// Fall through
case T_SEARCHABLE_COL_EQ:
case T_UPDATABLE_COL_LIST:
case T_NO_LONG_COL_LIST:
case T_NO_LONG_COL_ORDER_LIST:
case T_OFFSET_NO_LONG_COL_LIST:
case T_COL_LIST:
case T_SECOND_COL:
case T_NULL_COL_LIST:
// L"colname, colname, ..." or L"parmname, parmname..." or L"parmname=colname
TEST_ALLOC(WCHAR, pwszStr, 0, (size_t)(cCols * (3 + 2 * ulColNameMax) * sizeof(WCHAR)));
// For creating an order list for the order by clause we're stuck with a limit
// on the number of columns. For some providers this is 16, others may have
// a different limit. We'll use the min we are aware of.
if (eToken == T_NO_LONG_COL_ORDER_LIST && cCols > MAX_ORDER_LIST)
cCols = MAX_ORDER_LIST;
iStartCol = 1;
// For lookup syntax only one column is desired, the second one
if (eToken == T_SECOND_COL)
{
iStartCol = 2;
cCols = 2;
}
// For lookup syntax only one param is desired, the first one
if (eToken == T_FIRST_COL_EQ_PARM || eToken == T_LOOKUP_PARM_EQ_LIST)
cCols = 1;
// Go through each column in the table and add to our lists
for (iCol=iStartCol; iCol<=cCols; iCol++)
{
ULONG iNewCol = iCol;
WCHAR wchFirst;
// Adjust retrieved column if making offset col list
if (eToken == T_OFFSET_NO_LONG_COL_LIST)
{
iNewCol++;
if (iNewCol > cCols)
iNewCol = 1;
}
// Get the information about the column
if (FAILED(m_pTable->GetColInfo(iNewCol, TempCol)))
goto CLEANUP;
// If the column is wanted add to the list
if (bAddColumn(eToken, TempCol))
{
ULONG fIsNull = FALSE;
pColsAdded[cColsAdded++] = iNewCol;
if (eToken == T_NULL_COL_LIST)
pwszName = wcsDuplicate(L"NULL");
else
pwszName = wcsDuplicate(TempCol.GetColName());
if (!pwszName)
goto CLEANUP;
// For most tokens we need to add a comma separator
if (eToken != T_SEARCHABLE_COL_EQ_PARM &&
eToken != T_OFFSET_SEARCHABLE_COL_EQ_PARM &&
eToken != T_UNIQUE_SEARCHABLE_COL_EQ_PARM &&
eToken != T_SEARCHABLE_COL_EQ_PARM_MARKER &&
eToken != T_UNIQUE_SEARCHABLE_COL_EQ_PARM_MARKER &&
eToken != T_FIRST_COL_EQ_PARM &&
pwszStr[0])
wcscat(pwszStr, L", ");
if (eToken == T_NO_LONG_PARM_LIST ||
eToken == T_SEARCHABLE_COL_EQ_PARM ||
eToken == T_OFFSET_SEARCHABLE_COL_EQ_PARM ||
eToken == T_UNIQUE_SEARCHABLE_COL_EQ_PARM ||
eToken == T_SEARCHABLE_COL_EQ_PARM_MARKER ||
eToken == T_UNIQUE_SEARCHABLE_COL_EQ_PARM_MARKER ||
eToken == T_NO_LONG_PARM_EQ_LIST ||
eToken == T_NULL_PARM_EQ_LIST ||
eToken == T_NO_LONG_PARM_MARKER_EQ_LIST ||
eToken == T_FIRST_COL_EQ_PARM ||
eToken == T_LOOKUP_PARM_EQ_LIST ||
eToken == T_UPDATABLE_PARM_LIST)
{
// Build param name
wchFirst = L'P';
if (eToken == T_SEARCHABLE_COL_EQ_PARM ||
eToken == T_UNIQUE_SEARCHABLE_COL_EQ_PARM ||
eToken == T_OFFSET_SEARCHABLE_COL_EQ_PARM ||
eToken == T_SEARCHABLE_COL_EQ_PARM_MARKER ||
eToken == T_UNIQUE_SEARCHABLE_COL_EQ_PARM_MARKER ||
eToken == T_FIRST_COL_EQ_PARM)
{
// When building the offset list for in/out params we need the
// param name in the limit clause to match the one in the output
// list.
if (eToken != T_OFFSET_SEARCHABLE_COL_EQ_PARM &&
eToken != T_UNIQUE_SEARCHABLE_COL_EQ_PARM &&
eToken != T_UNIQUE_SEARCHABLE_COL_EQ_PARM_MARKER &&
eToken != T_FIRST_COL_EQ_PARM)
// Change the param name to start with 'L' for the limit clause
wchFirst = L'L';
if (pwszStr[0])
wcscat(pwszStr, L" and ");
// Tack the column name onto the string
wcscat(pwszStr, TempCol.GetColName());
if (!(pwszParamName = MakeParamName(TempCol, wchFirst)))
goto CLEANUP;
for (iParam = 0; iParam < m_cParams; iParam++)
if (!wcscmp(m_pColmap[iParam].pwszParamName, pwszParamName))
break;
// We should always find a matching parameter name.
ASSERT(iParam < m_cParams);
// Now put in the operator based on the param info
switch (m_pColmap[iParam].eCompareOp)
{
case CP_EQ:
wcscat(pwszStr, L"=");
break;
case CP_GT:
wcscat(pwszStr, L">");
break;
case CP_LT:
wcscat(pwszStr, L"<");
break;
case CP_ISNULL:
wcscat(pwszStr, L" IS NULL");
fIsNull = TRUE;
break;
case CP_ISNOTNULL:
wcscat(pwszStr, L" IS NOT NULL");
fIsNull = TRUE;
break;
default:
ASSERT(!L"Unknown operator found.");
}
}
if (!pwszParamName && !(pwszParamName = MakeParamName(TempCol, wchFirst)))
goto CLEANUP;
PROVIDER_FREE(pwszName);
pwszName = pwszParamName;
pwszParamName = NULL;
if (eToken == T_SEARCHABLE_COL_EQ_PARM_MARKER ||
eToken == T_UNIQUE_SEARCHABLE_COL_EQ_PARM_MARKER ||
eToken == T_NO_LONG_PARM_MARKER_EQ_LIST)
wcscpy(pwszName, pwszParamMarker);
}
// We always want to tack on the parameter name unless
// using IS NULL or IS NOT NULL syntax.
if (!fIsNull)
wcscat(pwszStr, pwszName);
if (eToken == T_NO_LONG_PARM_EQ_LIST ||
eToken == T_NULL_PARM_EQ_LIST ||
eToken == T_NO_LONG_PARM_MARKER_EQ_LIST ||
eToken == T_LOOKUP_PARM_EQ_LIST)
{
wcscat(pwszStr, L"=");
if (eToken == T_NULL_PARM_EQ_LIST)
wcscat(pwszStr, L"NULL");
else
wcscat(pwszStr, TempCol.GetColName());
}
PROVIDER_FREE(pwszName);
}
}
break;
case T_TABLE_NAME:
// We just duplicate the table name
if (!(pwszStr = wcsDuplicate(m_pTable->GetTableName())))
goto CLEANUP;
break;
case T_PROC_NAME:
if (!m_pwszProcName)
{
// We make a unique proc name by calling MakeObjectName
if (!(m_pwszProcName = MakeObjectName(L"ICmdProc", wcslen(m_pTable->GetTableName())+sizeof(WCHAR))))
goto CLEANUP;
}
// Return a copy of the name
pwszStr = wcsDuplicate(m_pwszProcName);
break;
case T_SEARCHABLE_PARM_MARKER_LIST:
case T_UPDATABLE_PARM_MARKER_LIST:
case T_NO_LONG_PARM_MARKER_LIST:
// L"?,?,?"
if (!(pwszParamMarker = GetSyntax(T_PARM_MARKER)))
goto CLEANUP;
TEST_ALLOC(WCHAR, pwszStr, 0, (size_t)(cCols * (ulColNameMax+wcslen(pwszParamMarker)+sizeof(L","))));
// Go through each column in the table and add to our lists
for (iCol=1; iCol<=cCols; iCol++)
{
// Get the information about the column
if (FAILED(m_pTable->GetColInfo(iCol, TempCol)))
goto CLEANUP;
// If the column is wanted add to the list
if (bAddColumn(eToken, TempCol))
{
if (pwszStr[0])
wcscat(pwszStr, L", ");
wcscat(pwszStr, pwszParamMarker);
PROVIDER_FREE(pwszName);
}
}
break;
default:
return NULL;
}
fError=FALSE;
CLEANUP:
if (fError)
PROVIDER_FREE(pwszStr);
PROVIDER_FREE(pwszParamMarker);
PROVIDER_FREE(pwszName)
PROVIDER_FREE(pwszParamName);
PROVIDER_FREE(pwszParamNameFmt);
if (pcColsAdded)
*pcColsAdded = cColsAdded;
if (prgColsAdded)
*prgColsAdded = pColsAdded;
else
PROVIDER_FREE(pColsAdded);
return pwszStr;
}
public:
// Constructor
CSyntax(void)
{
m_pwszProviderName = NULL;
m_pwszDBMSName = NULL;
m_cDialectTokens=0;
m_pDialectTokens=NULL;
m_pProviderList=NULL;
m_pDBMSList=NULL;
m_pDialectList = NULL;
m_pTable = NULL;
m_eProvider=UNKNOWN_PROVIDER;
m_eDBMS=UNKNOWN_DBMS;
m_eDialect=ORA_SQL;
m_pDBBINDING = NULL;
m_cParams=0;
m_pColmap = NULL;
m_pwszProcName = NULL;
m_iCurrentParam = 0;
m_iCurrentRow = 0;
m_fColCounts = FALSE;
m_fHasReturnParam = FALSE;
m_ulMaxParamName = SP_MAX_PARAMNAME_LENGTH;
m_ulCreateFlags = 0;
m_eProcType = T_EXEC_PROC_SELECT_OUT;
}
void SetProcType(enum TOKEN_ENUM eProcType)
{
m_eProcType = eProcType;
}
BOOL Init(WCHAR * pwszProviderName,
WCHAR * pwszDBMSName,
ULONG cDialectTokens,
const DialectTokens * pDialectTokens,
const ProviderList * pProviderList,
const DBMSList * pDBMSList,
const Dialects * pDialectList,
CTable * pTable,
ULONG ulMaxParamName)
{
m_pwszProviderName = pwszProviderName;
m_pwszDBMSName = pwszDBMSName;
m_cDialectTokens=cDialectTokens;
m_pDialectTokens=pDialectTokens;
m_pProviderList=pProviderList;
m_pDBMSList=pDBMSList;
m_pDialectList = pDialectList;
m_eDialect=ORA_SQL;
m_pTable = pTable;
m_ulMaxParamName = ulMaxParamName;
// Validate member vars. We need all these.
// m_pTable might be NULL if we never need to build specialized strings
// from the table
if (!m_pwszProviderName ||
!m_pwszDBMSName ||
!m_cDialectTokens ||
!m_pDialectTokens ||
!m_pProviderList ||
!m_pDBMSList ||
!m_pDialectList)
return FALSE;
SetProvider();
SetDBMS();
SetDialect();
return TRUE;
}
BOOL bHasColCounts(enum TOKEN_ENUM eToken, ULONG * pcColCounts, ULONG ** prgColCounts)
{
ASSERT(pcColCounts);
ASSERT(prgColCounts);
*pcColCounts = 0;
*prgColCounts = NULL;
switch (eToken)
{
case T_SELECT_IN:
// Only one token returns col counts
TEST_ALLOC(ULONG, *prgColCounts, 0, sizeof(ULONG));
(*prgColCounts)[0] = 2; // Token 3 needs to return col counts
*pcColCounts = 1;
return TRUE;
break;
default:
return FALSE;
}
CLEANUP:
return FALSE;
}
BOOL bNeedsLimitClause(enum TOKEN_ENUM eToken)
{
switch(eToken)
{
case T_EXEC_PROC_SELECT_IN:
case T_EXEC_PROC_SELECT_INOUT:
case T_EXEC_PROC_SELECT_INOUT_RET:
case T_EXEC_PROC_SELECT_OUT:
case T_EXEC_PROC_SELECT_OUT_DFLT:
case T_EXEC_PROC_SELECT_OUT_NULL:
case T_EXEC_PROC_SELECT_OUT_RET:
case T_EXEC_PROC_UPDATE_INPUT:
case T_EXEC_PROC_UPDATE_INPUT_RET:
return TRUE;
case T_EXEC_PROC_INSERT_INPUT:
case T_EXEC_PROC_INSERT_INPUT_RET:
default:
return FALSE;
}
}
BOOL bAddColumn(enum TOKEN_ENUM eToken, CCol ColInfo)
{
// We want certain columns from the table based on the proc type
switch(eToken)
{
case T_SEARCHABLE_COL_EQ:
case T_SEARCHABLE_COL_EQ_PARM:
case T_SEARCHABLE_PARM_MARKER_LIST:
case T_SEARCHABLE_COL_EQ_PARM_MARKER:
case T_UNIQUE_SEARCHABLE_COL_EQ_PARM_MARKER:
case T_EXEC_PROC_SELECT_INOUT:
case T_EXEC_PROC_SELECT_INOUT_RET:
case T_EXEC_PROC_SELECT_OUT:
case T_EXEC_PROC_SELECT_OUT_DFLT:
case T_EXEC_PROC_SELECT_OUT_NULL:
case T_EXEC_PROC_SELECT_OUT_RET:
case T_NO_LONG_PARM_EQ_LIST:
case T_NULL_PARM_EQ_LIST:
case T_NO_LONG_PARM_MARKER_EQ_LIST:
case T_OFFSET_NO_LONG_PARM_EQ_LIST:
case T_OFFSET_NO_LONG_COL_LIST:
case T_OFFSET_NO_LONG_PARM_MARKER_EQ_LIST:
case T_OFFSET_SEARCHABLE_COL_EQ_PARM:
case T_UNIQUE_SEARCHABLE_COL_EQ_PARM:
case T_NO_LONG_COL_LIST:
case T_NULL_COL_LIST:
case T_NO_LONG_COL_ORDER_LIST:
return (ColInfo.GetSearchable() && // Must be a searchable column for use in where
!ColInfo.GetIsLong() && // Long columns require 'LIKE', which we don't support yet
ColInfo.GetUpdateable() && // If we didn't insert the value we don't know it
!ColInfo.GetAutoInc()); // Even if updatable we never insert autoincs
break;
case T_UPDATABLE_COL_LIST:
case T_UPDATABLE_PARM_MARKER_LIST:
case T_EXEC_PROC_UPDATE_INPUT:
case T_EXEC_PROC_UPDATE_INPUT_RET:
case T_EXEC_PROC_INSERT_INPUT:
case T_EXEC_PROC_INSERT_INPUT_RET:
case T_UPDATABLE_PARM_LIST:
return ColInfo.GetUpdateable();
break;
case T_NO_LONG_PARM_LIST:
case T_NO_LONG_PARM_MARKER_LIST:
return !ColInfo.GetIsLong();
break;
case T_COL_LIST:
case T_SECOND_COL:
case T_FIRST_COL_EQ_PARM:
case T_LOOKUP_PARM_EQ_LIST:
return TRUE;
default:
// The delete and select in proc will hit the default and not add any columns except
// in the limit clause.
return FALSE;
}
}
void SetProcName(WCHAR * pwszProcName)
{
m_pwszProcName = pwszProcName;
}
void SetCreateFlags(ULONG ulCreateFlags)
{
m_ulCreateFlags = ulCreateFlags;
}
void SetCurrentRow(DBCOUNTITEM iRow)
{
m_iCurrentRow = iRow;
}
void SetReturnParam(BOOL fRetParam)
{
m_fHasReturnParam = fRetParam;
}
void SetMaxParamName(ULONG ulMaxParamName)
{
m_ulMaxParamName = ulMaxParamName;
}
// Build the string for a given syntax
WCHAR * GetSyntax(enum TOKEN_ENUM eToken, DBORDINAL * pcColsWanted = NULL, DB_LORDINAL ** prgColsWanted = NULL)
{
ULONG iSyntax=0;
ULONG iANSISyntax=0;
ULONG iToken=0;
ULONG cTokens=0;
BOOL fFoundANSISyntax = FALSE;
BOOL fFoundProviderSyntax = FALSE;
WCHAR ** ppwszTokenStrings = NULL;
WCHAR * pwszSyntax = NULL;
ULONG crgColCounts = 0;
ULONG * prgColCounts = NULL;
// Find the syntax for this token
for (iToken = 0; iToken < m_cDialectTokens; iToken++)
{
// Look for provider syntax
if (m_pDialectTokens[iToken].eToken == eToken &&
m_pDialectTokens[iToken].eDialect == m_eDialect)
{
// We found the provider's syntax
fFoundProviderSyntax = TRUE;
iSyntax = iToken;
break;
}
// Look for default syntax (Oracle, since Oracle seems more close
// to ANSI.
if (m_pDialectTokens[iToken].eToken == eToken &&
m_pDialectTokens[iToken].eDialect == ORA_SQL)
{
iANSISyntax=iToken;
fFoundANSISyntax = TRUE;
}
}
// Make sure we actually found the provider syntax otherwise use ANSI
if (!fFoundProviderSyntax)
{
if (fFoundANSISyntax)
iSyntax = iANSISyntax;
else
{
// We didn't find the provider syntax or the ansi syntax
odtLog << L"Couldn't find proper syntax to use for this token.\n";
goto CLEANUP;
}
}
cTokens = m_pDialectTokens[iSyntax].cTokens;
// Find out if this token is expected to receive column count information
// prgColCounts is an array of token indexes that should return count info.
if (bHasColCounts(eToken, &crgColCounts, &prgColCounts))
m_fColCounts=TRUE;
// We need new code to sum the col counts and reallocate the array
// if this is > 1.
ASSERT(crgColCounts < 2);
// Check to see if it's a root token and return the string
if (m_pDialectTokens[iSyntax].rgeTokens[0] == T_ROOT)
{
// It claims to be a root token, but if the token count isn't zero it's bogus
if (cTokens)
{
cTokens = 0;
goto CLEANUP;
}
// If it's a special root token that's one we think we know how to build then the string
// pointer is NULL;
if (!m_pDialectTokens[iSyntax].pwszTokenString)
{
if (!(pwszSyntax = BuildTokenString(eToken, pcColsWanted, prgColsWanted)))
goto CLEANUP;
}
// Otherwise just return the string
else if (!(pwszSyntax = wcsDuplicate(m_pDialectTokens[iSyntax].pwszTokenString)))
goto CLEANUP;
}
else
{
// Not a root token, we have to assemble the token from the pieces specified
// Allocate space for a pointer to each piece of the string
TEST_ALLOC(LPWSTR, ppwszTokenStrings, 0, cTokens * sizeof(LPWSTR));
// Find out the size of each piece and get the pointer to each
for (iToken = 0; iToken < cTokens; iToken++)
{
ULONG iColCount = 0;
DBORDINAL cCols = 0;
DB_LORDINAL * rgCols = NULL;
DBORDINAL * pcCols = &cCols;
DB_LORDINAL ** prgCols = &rgCols;
// Get the syntax for this token
if (!(ppwszTokenStrings[iToken]=GetSyntax(m_pDialectTokens[iSyntax].rgeTokens[iToken], pcCols, prgCols)))
goto CLEANUP;
// If this token needs to return col counts set up for it.
// Currently only coded for 1 col count being returned so only the first one is
if ((m_fColCounts && iToken == prgColCounts[iColCount]) ||
(!m_fColCounts && pcColsWanted && !*pcColsWanted))
{
if (pcColsWanted && prgColsWanted)
{
*pcColsWanted = cCols;
SAFE_FREE(*prgColsWanted);
*prgColsWanted = rgCols;
}
iColCount++;
if (iColCount = crgColCounts)
{
PROVIDER_FREE(prgColCounts);
m_fColCounts = FALSE;
}
}
}
// Now assemble the combined string from the pieces. First piece is the format.
if (!FormatStringFromArray(&pwszSyntax, ppwszTokenStrings[0], cTokens-1, &ppwszTokenStrings[1]))
goto CLEANUP;
}
CLEANUP:
// Free any temporary strings we allocated
for (iToken = 0; iToken < cTokens; iToken++)
PROVIDER_FREE(ppwszTokenStrings[iToken]);
PROVIDER_FREE(ppwszTokenStrings);
ASSERT(pwszSyntax);
return pwszSyntax;
}
WCHAR * MakeParamName(CCol ColInfo, WCHAR wchFirst)
{
WCHAR * pwszParamName = NULL;
WCHAR * pwszParamRoot = NULL;
WCHAR * pwszNameFmt = NULL;
ULONG ulMaxParamName = m_ulMaxParamName;
if (!(pwszParamRoot = wcsDuplicate(ColInfo.GetColName())))
goto CLEANUP;
// Make the last character of the param name different so it doesn't match col name.
// Use last char so it doesn't conflict with localization testing.
pwszParamRoot[wcslen(pwszParamRoot)-1] = wchFirst;
if (!(pwszNameFmt = GetSyntax(T_PARM_NAME_FMT)))
goto CLEANUP;
if (!FormatString(&pwszParamName, pwszNameFmt, 1, pwszParamRoot))
goto CLEANUP;
// If we're making long names, make name one larger than max
if (m_ulCreateFlags & CREATE_LONG_NAMES)
ulMaxParamName++;
// If we're testing boundary on max name size, reallocate and fill name
if (m_ulCreateFlags & CREATE_LONG_NAMES || m_ulCreateFlags & CREATE_MAX_NAMES)
{
SAFE_REALLOC(pwszParamName, WCHAR, ulMaxParamName+1);
if (GetModInfo()->GetLocaleInfo())
{
// We have to create a consistent value for parameter name each time, so we must set the
// Unicode Seed consistently. Use column number.
GetModInfo()->GetLocaleInfo()->SetUnicodeSeed((INT)ColInfo.GetColNum());
if (!GetModInfo()->GetLocaleInfo()->MakeUnicodeIntlString(pwszParamName+wcslen(pwszParamName),
ulMaxParamName-(ULONG)wcslen(pwszParamName)+1))
goto CLEANUP;
}
else
{
for (size_t ichar=wcslen(pwszParamName); ichar < ulMaxParamName; ichar++)
pwszParamName[ichar] = L'A';
pwszParamName[ulMaxParamName] = L'\0';
}
}
CLEANUP:
PROVIDER_FREE(pwszParamRoot);
PROVIDER_FREE(pwszNameFmt);
return pwszParamName;
}
inline BOOL IsKnown(void) {return m_eProvider != UNKNOWN_PROVIDER && m_eDBMS != UNKNOWN_DBMS;}
inline void SetBinding(DBBINDING * pBinding) {m_pDBBINDING = pBinding;}
inline void SetColMap(DBCOUNTITEM cParams, ParamStruct * pColMap)
{
m_cParams = cParams;
m_pColmap = pColMap;
}
inline void SetTable(CTable * pTable)
{
m_pTable = pTable;
}
inline enum DBMS_ENUM GetDBMSType(void) {return m_eDBMS;}
};
//--------------------------------------------------------------------
// Utility class to allow printing a custom string when an error occurs
//
class CMyError {
public:
//@cmember Checks the value of fEqual increments error count if it's FALSE
inline BOOL Compare(
BOOL fEqual,
wchar_t * strFile,
UDWORD udwLine,
CError * m_pCError,
WCHAR * wszMsg=NULL)
{
ASSERT(m_pCError);
if (wszMsg && !fEqual)
odtLog << wszMsg;
return m_pCError->Compare(fEqual, strFile, udwLine);
}
};
#define MYCOMPARE(obj1,obj2,msg) m_MyError.Compare((obj1)==(obj2), LONGSTRING(__FILE__), __LINE__, m_pError, msg)
class CIVerifyRow : public CSessionObject
{
private:
IID m_iidExec;
BOOL m_fLiteralSelect;
public:
//@cmember CTOR
CIVerifyRow(LPWSTR wszTestCaseName)
:CSessionObject(wszTestCaseName)
{
m_iidExec = IID_IRowset;
m_fLiteralSelect = FALSE;
}
//@cmember DTOR
~CIVerifyRow(){ }
//@cmember function to find whether a row exists in the table or not.
BOOL FindRow (DBCOUNTITEM ulRowNum, CTable *pTable=NULL, ICommand * pICommand = NULL, IRowset ** ppIRowset=NULL, HROW ** pphRows=NULL,
DBORDINAL * pcCols = NULL, DB_LORDINAL ** ppCols = NULL, BOOL fFetchRow = TRUE);
LPBYTE GetUpdatableCols(DBCOUNTITEM ulRowNum, DBCOUNTITEM cBindings, DBBINDING * pBinding,
DBLENGTH cbRowSize, ICommand * pICommand, CTable *pTable = NULL);
void SetIID(REFIID riid) {m_iidExec = riid;}
CMyError m_MyError;
};
//--------------------------------------------------------------------
// @mfunc FindRow
// Checks to see whether a specified row exists in the table or not.
// uses a parameterized query to (select) to verify.
//--------------------------------------------------------------------
BOOL
CIVerifyRow::FindRow(DBCOUNTITEM ulRowNum, CTable *pTable, ICommand * pICommand, IRowset ** ppIRowset, HROW ** pphRows,
DBORDINAL * pcCols, DB_LORDINAL ** ppCols, BOOL fFetchRow)
{
const ULONG cRows = 1; // Should be 1 because we are expecting 1 row back from select.
HROW * phRows = NULL;
BOOL fFound = FALSE;
IRowset * pIRowset = NULL;
DBCOUNTITEM cRowsObtained = 0;
DB_LORDINAL * rgSearchableCols = NULL;
WCHAR * pwszSqlStmt = NULL;
ICommandText * pICommandText = NULL;
DBORDINAL iCol, cCols = 0;
DB_LORDINAL * pCols = NULL;
IAccessor * pCmdIAccessor = NULL;
DBBINDING * rgBindings = NULL;
DBCOUNTITEM cBindings = 0;
DBLENGTH cbRowSize = 0;
DBPARAMS Param;
DBPARAMS * pParam = NULL;
BYTE * pData = NULL;
HACCESSOR hExecAccessor=DB_NULL_HACCESSOR;
DBPARAMBINDINFO * pParamBindInfo = NULL;
ParamStruct * pParamStruct = NULL;
DB_UPARAMS * prgParamOrdinals = NULL;
ICommandWithParameters * pICmdWParams = NULL;
// Init out params
if (pcCols)
*pcCols = 0;
if (ppCols)
*ppCols = NULL;
if (ppIRowset)
*ppIRowset = NULL;
if (pphRows)
*pphRows = NULL;
SAFE_ALLOC(phRows, HROW, sizeof(HROW) * cRows);
// if we are passed a table pointer set the table to that one.
if (!pTable)
pTable = m_pTable;
// Get a command object to do a sql query.
if (!pICommand)
{
TEST_CHECK(m_pIDBCreateCommand->CreateCommand(NULL, IID_ICommand,
(IUnknown **)&pICommand), S_OK);
}
else
pICommand->AddRef();
TEST_COMPARE(VerifyInterface(pICommand, IID_ICommandText, COMMAND_INTERFACE, (IUnknown**)&pICommandText),TRUE);
// While it is better to use literals to validate the inserted row, there are cases where literals will not match
// the inserted data, mainly in the case of variant date data, because the date inside a variant does not contain
// a fractional seconds component. So this is now changed to use parameters instead for most cases.
if (m_fLiteralSelect)
{
TEST_CHECK(pTable->CreateSQLStmt(SELECT_ROW_WITH_LITERALS, NULL, &pwszSqlStmt,&cCols, &pCols, ulRowNum), S_OK);
// Set command text
TEST_CHECK(pICommandText->SetCommandText(DBGUID_DBSQL , pwszSqlStmt), S_OK);
}
else
{
HRESULT hrSetParam = E_FAIL;
TEST_CHECK(pTable->CreateSQLStmt(SELECT_ALL_WITH_SEARCHABLE_AND_UPDATEABLE, NULL, &pwszSqlStmt,&cCols, &pCols, ulRowNum), S_OK);
// Set command text
TEST_CHECK(pICommandText->SetCommandText(DBGUID_DBSQL , pwszSqlStmt), S_OK);
TEST_COMPARE(VerifyInterface(pICommand, IID_IAccessor, COMMAND_INTERFACE, (IUnknown **)&pCmdIAccessor), TRUE)
// Create the accessor.
TEST_CHECK(GetAccessorAndBindings(pCmdIAccessor, DBACCESSOR_PARAMETERDATA,
&hExecAccessor, &rgBindings, &cBindings, &cbRowSize,
DBPART_VALUE | DBPART_STATUS | DBPART_LENGTH, ALL_COLS_BOUND,
FORWARD, NO_COLS_BY_REF, NULL, NULL, NULL, DBTYPE_EMPTY,
0, NULL, NULL, NO_COLS_OWNED_BY_PROV,
DBPARAMIO_INPUT, BLOB_LONG), S_OK);
//Set up parameter input values for selecting row 1
TEST_CHECK (FillInputBindings(pTable, DBACCESSOR_PARAMETERDATA, cBindings,
rgBindings, &pData, ulRowNum, cCols, pCols, PRIMARY), S_OK);
// Allocate information for SetParameterInfo
TEST_ALLOC(DBPARAMBINDINFO, pParamBindInfo, 0, (size_t)(sizeof(DBPARAMBINDINFO) * cCols));
TEST_ALLOC(ParamStruct, pParamStruct, 0, (size_t)(sizeof(ParamStruct) * cCols));
TEST_ALLOC(DB_UPARAMS, prgParamOrdinals, 0, (size_t)(sizeof(DB_UPARAMS) * cCols));
// Compare the given Parameter information against the columns information
// gotten by ExecuteCommand. In this case cParams should match cColumns.
for (iCol = 0; iCol < cBindings; iCol++)
{
prgParamOrdinals[iCol] = iCol+1;
AddParam(iCol, pCols[iCol], DBPARAMIO_INPUT, NULL, FALSE, &cbRowSize, NULL, pParamBindInfo, pParamStruct, pTable);
}
TEST_COMPARE(VerifyInterface(pICommand, IID_ICommandWithParameters,
COMMAND_INTERFACE,(IUnknown **)&pICmdWParams), TRUE);
// Remove any old parameter information that might be hanging around.
TEST_CHECK(pICmdWParams->SetParameterInfo(0, NULL, NULL), S_OK);
pParam = &Param;
Param.cParamSets = 1;
Param.hAccessor = hExecAccessor;
Param.pData = pData;
// Now reset the statement to retrieve all columns using parameters derived from the
// searchable and updatable columns
cCols = 0;
SAFE_FREE(pCols);
SAFE_FREE(pwszSqlStmt);
TEST_CHECK(pTable->CreateSQLStmt(SELECT_FROMTBLWITHPARAMS, NULL, &pwszSqlStmt,&cCols, &pCols, ulRowNum), S_OK);
TEST_CHECK(pICommandText->SetCommandText(DBGUID_DBSQL , pwszSqlStmt), S_OK);
hrSetParam = pICmdWParams->SetParameterInfo(cBindings, prgParamOrdinals, pParamBindInfo);
if (hrSetParam != DB_S_TYPEINFOOVERRIDDEN)
TEST_CHECK(hrSetParam, S_OK);
}
TEST_CHECK(pICommand->Execute(NULL, m_iidExec, pParam, NULL, (IUnknown **)&pIRowset), S_OK);
if (m_iidExec == IID_IRowset && fFetchRow)
{
TEST_CHECK (pIRowset->GetNextRows(NULL, 0, cRows, &cRowsObtained, &phRows), S_OK);
TEST_COMPARE(cRowsObtained, 1);
TEST_COMPARE(phRows != NULL, TRUE);
}
fFound = TRUE;
CLEANUP:
if (fFound)
{
if (pcCols)
*pcCols = cCols;
if (ppCols)
*ppCols = pCols;
else
SAFE_FREE(pCols);
}
// If the rowset and hrows were requested, pass them back
if (pphRows && fFound)
*pphRows = phRows;
else
{
//Free all the rows for this GetNextRows call
if (pIRowset && phRows && m_iidExec == IID_IRowset)
CHECK(pIRowset->ReleaseRows(cRowsObtained, phRows, NULL, NULL, NULL), S_OK);
SAFE_FREE(phRows);
}
if (ppIRowset && fFound)
*ppIRowset = pIRowset;
else
SAFE_RELEASE (pIRowset);
SAFE_RELEASE_ACCESSOR(pCmdIAccessor, hExecAccessor);
SAFE_RELEASE (pICommand);
SAFE_RELEASE (pICommandText);
SAFE_RELEASE (pCmdIAccessor);
SAFE_RELEASE (pICmdWParams);
if (pData)
CHECK(ReleaseInputBindingsMemory(cBindings, rgBindings, pData, TRUE), S_OK);
SAFE_FREE(prgParamOrdinals);
SAFE_FREE(pParamBindInfo);
FreeParameterNames(cBindings, pParamStruct);
SAFE_FREE(pParamStruct);
SAFE_FREE(rgBindings);
SAFE_FREE(pwszSqlStmt);
return fFound;
}
//--------------------------------------------------------------------
// @mfunc FindRow
// Checks to see whether a specified row exists in the table or not.
// and returns a pData buffer with the updatable columns (user must free)
//--------------------------------------------------------------------
LPBYTE CIVerifyRow::GetUpdatableCols(DBCOUNTITEM ulRowNum, DBCOUNTITEM cBindings, DBBINDING * pBinding,
DBLENGTH cbRowSize, ICommand * pICommand, CTable *pTable)
{
LPBYTE pData = NULL;
IRowset * pIRowset = NULL;
IAccessor * pIAccessor = NULL;
HROW * phRows = NULL;
HACCESSOR hAccessor = DB_NULL_HACCESSOR;
DBCOUNTITEM cBindRowset = 0;
DBLENGTH cbRowsizeRowset= 0;
ULONG iBind;
DBBINDING * pBindRowset = NULL;
DBBINDING * pBindNew = NULL;
// Since we're munging the bindings and therefore need IAccessor we can't use a row
// object here;
IID iid = m_iidExec;
m_iidExec = IID_IRowset;
if (!pTable)
pTable = m_pTable;
SAFE_ALLOC(pBindNew, DBBINDING, cBindings);
memcpy(pBindNew, pBinding, (size_t)(cBindings*sizeof(DBBINDING)));
TESTC(pICommand != NULL);
TESTC(FindRow(ulRowNum, pTable, pICommand, &pIRowset, &phRows));
TESTC(pIRowset!=NULL);
TESTC(phRows!=NULL);
TESTC(VerifyInterface(pIRowset, IID_IAccessor,
ROWSET_INTERFACE, (IUnknown **)&pIAccessor));
TESTC_(GetAccessorAndBindings(pIRowset, DBACCESSOR_ROWDATA,
&hAccessor, &pBindRowset, &cBindRowset, &cbRowsizeRowset,
DBPART_VALUE | DBPART_STATUS | DBPART_LENGTH,
UPDATEABLE_COLS_BOUND, FORWARD, NO_COLS_BY_REF,
NULL, NULL,
NULL, DBTYPE_EMPTY, 0, NULL,
NULL ,NO_COLS_OWNED_BY_PROV, DBPARAMIO_NOTPARAM, BLOB_LONG ), S_OK);
TESTC(cBindings == cBindRowset);
// We really only wanted the ordinal values required for the updatable cols
// so we can retrieve the right columns.
SAFE_RELEASE_ACCESSOR(pIAccessor, hAccessor);
for (iBind = 0; iBind < cBindings; iBind++)
pBindNew[iBind].iOrdinal = pBindRowset[iBind].iOrdinal;
SAFE_FREE(pBindRowset);
// Now create an accessor with bindings matching the parameter data
TEST_CHECK(pIAccessor->CreateAccessor(DBACCESSOR_ROWDATA, cBindings,
pBindNew, cbRowSize, &hAccessor, NULL), S_OK);
SAFE_ALLOC(pData, BYTE, cbRowSize);
// Retrieve the row
TESTC_(pIRowset->GetData(*phRows, hAccessor, pData), S_OK);
TESTC_(pIRowset->ReleaseRows(1, phRows, NULL, NULL, NULL), S_OK);
CLEANUP:
// Restore the proper iid
m_iidExec = iid;
m_fLiteralSelect = FALSE;
SAFE_RELEASE(pIRowset);
SAFE_RELEASE(pIAccessor);
SAFE_FREE(pBindRowset);
SAFE_FREE(phRows);
return pData;
}
//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
//Base Class definition for ICommandWithParameters.
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
// @class CICmdWParams: Base class for all ICommandWithParameter test cases.
class CICmdWParams : public CSessionObject
{
public:
//@cmember Constructor
CICmdWParams(LPWSTR wszTestCaseName);
virtual void SetTestCaseParam(ETESTCASE eTestCase = TC_Rowset)
{
m_eTestCase = eTestCase;
switch(eTestCase)
{
case TC_Rowset:
break;
case TC_Row:
break;
default:
ASSERT(!L"Unhandled Type...");
break;
};
}
//data
ETESTCASE m_eTestCase;
//@cmember CompareDbParamInfo
//Compares Data information in rgParamInfo against rgColInfo.
//Assumes that rgColInfo is initialized to the column information
HRESULT CompareDBParamInfo(
DBCOUNTITEM cCols, // [in] Number of columns in rgColumnsInfo. This is expected to match cParams
DBORDINAL * prgParamColMap, // [in] Parameter to column mapping array
DB_UPARAMS cParams, // [in] Number of parameter information to compare
DBPARAMINFO * rgParamInfo, // [in] Array of DBPARAMINFO containing parameter info
LPOLESTR pNamesBuffer // [in] Names buffer from GetParameterInfo.
);
void DumpCommandProps(IUnknown * pIUnknown, BOOL fPropertiesInError,
ULONG cPropSets=0, DBPROPSET * pPropSets=NULL);
// @cmember copies CTestCase public data to local objects
void TestCaseInfoToObjs(void);
// @cmember Create pData for both commands.
BOOL MakeDataForCommand(DBCOUNTITEM cRowNum);
// @cmember MakeTempTableName to create a table name based on existing table name.
WCHAR * MakeTempTableName(WCHAR wch);
// @cmember Generate a row number to be inserted.
DBCOUNTITEM NextInsertRowNum();
// @cmember Release pdata for both commands.
void ReleaseDataForCommand();
// @cmember True if a provider type is DateTime type.
BOOL IsColDateTime(DBTYPE ProviderType);
// @cmember TRUE if a provider type is Numeric.
BOOL IsColNumeric ( DBTYPE ProviderType );
// @cmember TRUE if a provider type is I2
BOOL IsColI2 ( DBTYPE ProviderType );
// Gets all the relevent property values (Sqlserver, Outparameters, multipleparameteres)
BOOL GetReleventProperties();
// @cmember Release rowdata information.
inline void ReleaseRowsetPtr(IRowset ** ppRowset)
{
if (*ppRowset)
{
(*ppRowset)->Release();
(*ppRowset) = NULL;
}
}
//@cmember Execute after calling Setparameterinfo.
// For different combination of variable length columns.
BOOL ExecuteWithSetParamInfo(
LPOLESTR pwszDataSourceType, // SetParamInfo with this Data type.
DBTYPE wType // DbBind for Execute with this Data type.
);
//@cmember Create the Sql statement for the wType.
BOOL GetSqlBindInfo(
LPOLESTR pwszDataSourceType, // SetParamInfo with this data type
DBTYPE wType, // DbBind for Execute with this data type
WCHAR *pwszCreateStatement, // Create table statement
WCHAR *pwszDropStatement, // Drop table statement
WCHAR *pwszInsertStatement, // Insert row statement
DBBINDING **prgDbBindInfo, // DbBinding structure generated.
DBPARAMBINDINFO **prgDbParamBindInfo, // ParamBindInfo generated for SetParam.
DBORDINAL **rgParamColumnMap,
HACCESSOR *hAccessor
);
//@cmember Fillinputbindings for multiple parameter sets.
// most of the parameters are for FillInputBindings call(MiscFunc.cpp).
HRESULT FillInputBindingsForArray(
CTable * pTable,
DBACCESSORFLAGS dwFlags,
DBLENGTH cbRowSize,
DBCOUNTITEM cBindings,
DBBINDING * rgBindings,
BYTE ** ppData,
ULONG cNumRows, // Number of Rows for which data has to be generated
DBCOUNTITEM * rgRowNums, // Array of rownumbers for which data has to be generated.
DBORDINAL cParamColMap,
DB_LORDINAL * rgParamColMap // Array of Column number mappings for the bindings.
);
//@cmember ReleaseInputBindings for multiple parameter sets.
// Most of the parameters are for ReleaseInputBindins call (MiscFunc.cpp).
HRESULT ReleaseInputBindingsForArray(
ULONG cNumRows,
DBLENGTH cbRowSize,
DBCOUNTITEM cBindings,
DBBINDING *rgBindings,
BYTE *pData);
BOOL bHasReturnParam(enum TOKEN_ENUM eProcType);
HRESULT CreateStoredProc(ICommandText * pICommandText, WCHAR * pwszProcName, WCHAR * pwszCreateStmt, BOOL fIsFunction = FALSE);
HRESULT SetParameterInfoIfNeeded(DB_UPARAMS cParams, DB_UPARAMS * pParamOrdinals, DBPARAMBINDINFO * pParamBindInfo,
ICommand * pICommand = NULL);
HRESULT PrepareForExecute(WCHAR * pwszExecuteStmt, ULONG cParams, DB_UPARAMS * pParamOrdinals,
DBPARAMBINDINFO * pPARAMBIND, BOOL * pfCanDerive, WCHAR * pwszProcName=NULL,
WCHAR * pwszCreateProcStmt=NULL, BOOL fIsFunction = FALSE);
HRESULT ValidateGetParameterInfo(ULONG cExpParams, ULONG cParamSets, DBCOUNTITEM ulRowNum, DB_UPARAMS * pExpOrdinals,
DBPARAMBINDINFO * pExpParamBind, DBBINDING * pBINDING, DBLENGTH cbRowSize, ParamStruct * pParamAll,
BYTE * pData, enum ROWSET_ENUM eRowset, DBORDINAL cColumns, DB_LORDINAL * rgColumns,BOOL fCanDerive,
enum VERIFY_ENUM eVerifyMethod = VERIFY_USE_TABLE, ICommand * pICommand = NULL);
HRESULT ExecuteAndVerify(ULONG cParams, ULONG cParamSets, ParamStruct * pParamAll,
DBCOUNTITEM ulRowNum, DBBINDING * pBINDING, DBLENGTH cbRowSize, BYTE * pData, enum ROWSET_ENUM eRowset,
DBORDINAL cColumns, DB_LORDINAL * rgColumns, enum VERIFY_ENUM eVerifyMethod, BOOL fRelease,
ICommand * pICommand = NULL, HRESULT hrExpected = S_OK, DBBINDING * pBindMatch=NULL,
BYTE * pDataMatch = NULL, DBCOUNTITEM * pcRowsExpected = NULL);
HRESULT VerifyObj(REFIID riidRowset, IUnknown * pUnkRowset, DBCOUNTITEM ulStartingRow,
DBORDINAL cRowsetCols, DB_LORDINAL * rgTableColOrds, BOOL fRelease, BOOL fRestart = FALSE, CTable * pTable = NULL,
DBCOUNTITEM * pcRows = NULL);
HRESULT VerifyRowObj(REFIID riidRow, IUnknown * pUnkRow, DBCOUNTITEM ulStartingRow,
DBORDINAL cRowsetCols, DB_LORDINAL * rgTableColOrds, BOOL fRelease, CTable * pTable);
HRESULT VerifyRowset(REFIID riidRowset, IUnknown * pUnkRowset, DBCOUNTITEM ulStartingRow,
DBORDINAL cRowsetCols, DB_LORDINAL * rgTableColOrds, BOOL fRelease, BOOL fRestart = FALSE,
CTable * pTable = NULL, DBCOUNTITEM * pcRowsExpected=NULL);
HRESULT VerifyOutParams(ULONG cParams, ULONG cParamSets, DBCOUNTITEM ulRowNum, DBBINDING * pBinding,
DBLENGTH cbRowSize, BYTE * pData, ParamStruct * pParamAll, enum VERIFY_ENUM eVerifyMethod,
DBBINDING * pBindMatch=NULL, BYTE * pDataMatch = NULL);
BOOL CreateProcBindings(
enum TOKEN_ENUM eProcType, // [IN] Proc type, regular proc or function (has return value)
BOOL fBindByName, // [IN] If TRUE then we add parameter names to the rgParamBindInfo
ULONG cParamSets, // [IN] Number of sets of parameters to be created
DBTYPE wReturnType, // [IN] Return parameter type
DBCOUNTITEM ulTableRow, // [IN] Row number in table to select, insert, or update
ULONG * pcParams, // [OUT] Count of params created
DBLENGTH * pcbRowSize, // [OUT] Count of bytes for a single row of parameters
DBBINDING ** ppDBBINDINFO, // [OUT] Binding array for CreateAccessor
DB_UPARAMS ** ppParamOrdinals, // [OUT] Array of cParams ordinals
DBPARAMBINDINFO ** ppDBPARAMBINDINFO,// [OUT] rgParamBindInfo for SetParameterInfo
WCHAR ** ppwszCreateStmt, // [OUT] SQL stmt to create the stored proc
WCHAR ** ppwszExecProcStmt, // [OUT] SQL stmt to execute the stored proc
WCHAR ** ppwszExecStmt, // [OUT] SQL stmt to execute without stored proc
WCHAR ** ppwszProcName, // [OUT] Name of stored proc created
BYTE ** ppData, // [OUT] Pointer to data for the parameters
ParamStruct ** ppParamAll, // [OUT] Pointer to array of param structs to save name, column
DBORDINAL * pcColumns = NULL, // [OUT] Column count for rowset returned by proc or exec
DB_LORDINAL ** prgColumnsOrd = NULL,// [OUT] Array of mappings of rowset columns to underlying table columns
DB_LORDINAL ** prgParamColOrd = NULL,// [OUT] Array of mappings of parameters to underlying table columns
ULONG ulCreateFlags = 0 // [IN] Flags used to control creation of parameters
);
//@cmember Executes the stored procedure.
HRESULT ExecuteStoredProcedure(ICommandText *pICommandText, DBPARAMS *pDbParams,
DBCOUNTITEM ulRowNum, ULONG DataValues, ULONG BindingType);
// Drop the procedure created.
BOOL DropStoredProcedure(ICommandText *pICommandText, WCHAR * pwszProcedureName=NULL, BOOL fIsFunction = FALSE);
// @cmember Speciality functions to store the Byref pointers to free them later.
BOOL StorePassByRefPointers(DB_UPARAMS cRows, BYTE * pData);
void ReleaseInputBindingsMemoryByRef(ULONG cRows, BYTE * pData, BOOL fFreeProviderMemory);
// @cmember Swap the ordinal information for a parameter
void SwapOrdinal(ULONG ulBindIndex);
//@cmemmber Destructor
~CICmdWParams(){};
CErrorCache m_EC; // Error cache to suppress duplicate errors
// @cmember Command object for select statement with parameters for
// columns only.
ICommand *m_pICommand;
// @cmember Command object for no particulare command. Basicaly a scratch command object.
ICommand *m_pIEmptyCommand;
// @cmember Rownumber for which to generate pData for using FillInputBindings.
ULONG m_cSDVRowNum;
// @cmember Class to verify the insertion of the row.
CIVerifyRow cRow;
// @cmember Rownumber used to create the stored procedure.
ULONG m_ulSPRowNum;
// @cmember Rownumber guarenteed to be there. (usually the first row number).
ULONG m_ulFixedRowNum;
protected:
IID m_iidExec;
//@cmember Common base class initialization
BOOL Init();
//@cmember Common base class termination
BOOL Terminate();
//@cmember Interface pointer for ICommandWithParameters(select for ).
ICommandWithParameters *m_pICmdWParams;
//@cmember Interface pointer for ICommandWithParameters(Select for ).
ICommandPrepare *m_pICommandPrepare;
//@cmember Interface pointer for ICommandWithParameters(Select for ).
ICommandText *m_pICommandText;
//@cmember Handle to a created valid accessor.
HACCESSOR m_hAccessor;
//@cmember Handle to a create valid accessor for the stored procedures.
HACCESSOR m_hStoredProcAccessor;
//@cmember pData size for the stored proc.
ULONG m_cbStoredProcRowSize;
// Type information for the stored precedure parameters.
DBCOLUMNINFO *m_rgStoredProcColInfo;
// General accessors for error checking.
HACCESSOR m_hGeneralAccessor;
HACCESSOR m_hNullAccessor;
HACCESSOR m_hRowdataAccessor;
HACCESSOR m_hDupParamAccessor;
//@cmember IAccessor on Command Object
IAccessor * m_pCmdIAccessor;
IAccessor * m_pGeneralCmdIAccessor;
// @cmember Rowset pointer for Stored procedure
IRowset *m_pIStoredProcRowset;
//@cmember Count of bindings in m_rgBindings.
DBCOUNTITEM m_cBindings;
//@cmember Array of DBBINDINGS used to create an accessor.
DBBINDING * m_rgBindings;
//@cmember Count of bindings in m_rgBindings.
ULONG m_cStoredProcBindings;
//@cmember Array of DBBINDINGS used to create an accessor.
DBBINDING * m_rgStoredProcBindings;
//@cmember Array of DBCOLUMNSINFO got from GetAccessorAndBinding call
// for columns.
DBCOLUMNINFO * m_rgColInfo;
//@cmember count of elements in DBPARAMBINDINFO structure for SetParameterInfo.
DB_UPARAMS m_cDbParamBindInfo;
//@cmember Array of BSTR for DBPARAMBINDINFO for corresponding column types.
ParamStruct *m_pParamStruct;
// @cmember array of DBPARAMBINDINFO structures.
DBPARAMBINDINFO *m_rgDbParamBindInfo;
// @cmember array of DBPARAMBINDINFO structures for stored proc parameters.
DBPARAMBINDINFO *m_rgStoredProcParamBindInfo;
//@cmember Array of BSTR for DBPARAMBINDINFO for corresponding data types in Stored proc
//parameters.
WCHAR **m_rgwszStoredProcDataSourceTypes;
// @cmember array of DBPARAMBINDINFO structures.
DB_UPARAMS *m_rgParamOrdinals;
// @cmember number of rows affected by the stored procedure query.
DBROWCOUNT m_cStoredProcRowsAffected;
// @cmember Number of parameters in the stored procedure
ULONG m_cStoredProcParamColMap;
// @cmember Range of parameter to COlumn mapping.
DB_LORDINAL *m_rgStoredProcParamColMap;
// @cmember Range of parameter ordinals for the stored procedure.
ULONG *m_rgStoredProcParamOrdinals;
// @cmember BOOL to specify whether SetParameterInfo was set or not.
BOOL m_bSetParameterInfo;
// @cmember BOOL to Specify if default values for columns are supported (DBPROP_COL_DEFFAULT)
BOOL m_fDefaultVal;
// @cmember BOOL to Specify case of identifiers
ULONG_PTR m_ulIdentifierCase;
// @cmember BOOL to Specify case of quoted identifiers
ULONG_PTR m_ulQuotedIdentifierCase;
//@cmember Row size used to allocate buffer pointed to by m_pData.
DBLENGTH m_cbRowSize;
//@cmember Pointer to consumers's buffer.
BYTE * m_pData;
//@cmember DBPARAMINFO structure for
DBPARAMS m_DbParamsAll;
//@cmember Count of columns in rowset, used for
//@cmember for parameterized queries.
ULONG m_cParamColMap;
//@cmember Array of ordinals of columns in rowset, used
//@cmember for parameterized queries.
DBORDINAL * m_rgParamColMap;
//@cmember Sql statement for Select with parameters.
WCHAR *m_pwszSqlInsertAllWithParams;
// @cmember Table pointer for an extra table.
CTable *m_pExtraTable;
// @cmember Number of Parameters returned by GetParameterInfo
DB_UPARAMS m_cParams;
// @cmember Array of parameter information.
DBPARAMINFO *m_rgParamInfo;
// @cmember Buffer to store name information for m_rgParamInfo.
WCHAR *m_pNamesBuffer;
// @cmember Create procedure string
WCHAR *m_pwszCreateProcedureString;
// @cmember Execute procedure string
WCHAR *m_pwszExecuteProcedureString;
// @cmember Buffer containing parameter names
WCHAR ** m_pwszParameterNames;
// @cmember Base addresss of buffer containing parameter names
LPOLESTR * m_prgwszParameterNamesBase;
// Range of pointers allocated by FillInputBindings for ByRef types.
// We have to store them because kagera overwrites them when it generates
// Output parameter values.
void **m_rgvpByRefPointers;
void FreeDescParams();
CSyntax m_Syntax;
HRESULT VerifyParamInfo(DBCOUNTITEM cParamsExp, DB_UPARAMS * rgParamOrdinals, DBPARAMBINDINFO * rgDbParamBindInfo,
DB_UPARAMS cParams, DBPARAMINFO * rgParamInfo, LPOLESTR pNamesBuffer, ULONG idxStart=0, BOOL fDerived = FALSE);
BOOL ReverseArray(void * rgArray, DBCOUNTITEM cElements, ULONG ulElementSize);
BOOL ScrambleArray(void * rgArray, DB_UPARAMS cElements, ULONG ulElementSize);
WCHAR ** CreateParameterNames(WCHAR ** ppwszParameterNames, enum NAME_ENUM eNameType, DB_UPARAMS * pcParamNames=NULL,
ULONG fColTypes = NO_LONG_COLS);
void FreeParameterNames(WCHAR ** pwszParameterNames);
void SetParameterNames(WCHAR ** pwszParameterNames);
HRESULT SetRowsetPropertyDefault(DBPROPID DBPropID, ICommand * pICommand = NULL);
CMyError m_MyError;
DWORD m_dwDriverODBCVer;
DB_UPARAMS * m_prgParamOrdinals;
WCHAR ** m_prgwszParameterNames;
static DBCOUNTITEM m_cInsertRowNum;
BOOL m_fProcedureSupport;
// @cmember Result of test
TESTRESULT m_TestResult;
private:
};
DBCOUNTITEM CICmdWParams::m_cInsertRowNum = TOTAL_NUMBER_OF_ROWS + 1;
//---------------------------------------------------------------------
//@mfunc Constructor for the base class;
//---------------------------------------------------------------------
CICmdWParams::CICmdWParams(LPWSTR wszTestCaseName)
: CSessionObject (wszTestCaseName),
cRow(wszTestCaseName)
{
m_hAccessor = DB_NULL_HACCESSOR;
m_pICommand = NULL;
m_pIEmptyCommand = NULL;
m_hGeneralAccessor = DB_NULL_HACCESSOR;
m_hNullAccessor = DB_NULL_HACCESSOR;
m_hRowdataAccessor = DB_NULL_HACCESSOR;
m_hDupParamAccessor = DB_NULL_HACCESSOR;
m_hStoredProcAccessor = DB_NULL_HACCESSOR;
m_cbStoredProcRowSize = 0;
m_rgStoredProcColInfo = NULL;
m_cBindings = 0;
m_cStoredProcRowsAffected = 0;
m_cStoredProcParamColMap = 0;
m_rgBindings = NULL;
m_rgStoredProcParamColMap = NULL;
m_rgStoredProcParamOrdinals = NULL;
m_bSetParameterInfo = FALSE;
m_ulIdentifierCase = DBPROPVAL_IC_UPPER;
m_ulQuotedIdentifierCase = DBPROPVAL_IC_UPPER;
m_cStoredProcBindings = 0;
m_rgStoredProcBindings = 0;
m_cDbParamBindInfo = 0;
m_rgDbParamBindInfo = NULL;
m_rgParamOrdinals = NULL;
m_pParamStruct = NULL;
m_rgColInfo = NULL;
m_pData = NULL;
m_cSDVRowNum = 0;
m_iidExec = IID_IRowset;
// Since So many rows were inserted into the table starting
// from row 1. This numbered row would have been inserted.
m_ulFixedRowNum = TOTAL_NUMBER_OF_ROWS - 1;
m_cbRowSize = 0;
m_pCmdIAccessor = NULL;
m_pGeneralCmdIAccessor = NULL;
m_pICommandPrepare = NULL;
m_pICmdWParams = NULL;
m_pICommandText = NULL;
m_pIStoredProcRowset = NULL;
m_cParamColMap = 0;
m_rgParamColMap = NULL;
m_rgStoredProcParamBindInfo = NULL;
m_rgwszStoredProcDataSourceTypes = NULL;
m_pwszSqlInsertAllWithParams = NULL;
m_pwszCreateProcedureString = NULL;
m_pwszExecuteProcedureString = NULL;
m_pExtraTable = NULL;
m_cParams = 0;
m_rgParamInfo = NULL;
m_pNamesBuffer = NULL;
m_rgvpByRefPointers = NULL;
m_prgParamOrdinals = NULL;
m_prgwszParameterNames = NULL;
m_pwszParameterNames = NULL;
m_fProcedureSupport=FALSE; // We assume no stored proc support
// By default we're testing rowsets being returned from Execute
SetTestCaseParam(TC_Rowset);
}
void CICmdWParams::DumpCommandProps(IUnknown * pIUnknown, BOOL fPropertiesInError,
ULONG cPropSets, DBPROPSET * pPropSets)
{
DBPROPIDSET rgPropertyIDSets[1];
ICommandProperties * pICmdProp = NULL;
IRowsetInfo * pIRowsetInfo = NULL;
ULONG cPropertySets = 0;
DBPROPSET * pPropertySets = NULL;
HRESULT hrGetProp = E_FAIL;
rgPropertyIDSets[0].rgPropertyIDs = NULL;
rgPropertyIDSets[0].cPropertyIDs = 0;
rgPropertyIDSets[0].guidPropertySet = DBPROPSET_PROPERTIESINERROR;
if (cPropSets && pPropSets)
{
cPropertySets = cPropSets;
pPropertySets = pPropSets;
}
else
{
if (SUCCEEDED(pIUnknown->QueryInterface(IID_ICommandProperties, (void **)&pICmdProp)))
{
ULONG cPropZero = 0;
ULONG cPropZeroNULL = 0;
if (!fPropertiesInError)
{
odtLog << L"Dumping command properties:\n";
pICmdProp->GetProperties(0, NULL, &cPropertySets, &pPropertySets);
}
else if (fPropertiesInError)
{
odtLog << L"Dumping command properties in error:\n";
pICmdProp->GetProperties(1, rgPropertyIDSets, &cPropertySets, &pPropertySets);
}
else
odtLog << L"Invalid option:\n";
}
else if (SUCCEEDED(pIUnknown->QueryInterface(IID_IRowsetInfo, (void **)&pIRowsetInfo)))
{
pIRowsetInfo->GetProperties(0, NULL, &cPropertySets, &pPropertySets);
odtLog << L"Dumping rowset properties:\n";
}
else
odtLog << L"Not a command or rowset interface.\n";
}
if (pPropertySets)
{
for (ULONG iPropSet = 0; iPropSet < cPropertySets; iPropSet++)
{
for (ULONG iProp = 0; iProp < pPropertySets[iPropSet].cProperties; iProp++)
{
const LPWSTR pwszTrue = L"VARIANT_TRUE";
const LPWSTR pwszFalse = L"VARIANT_FALSE";
const LPWSTR pwszEmpty = L"VT_EMPTY";
const LPWSTR pwszUnexpected = L"UNEXPECTED";
WCHAR wszBuff[30] = L"";
const LPWSTR ppwszStatus[] = {
L"DBPROPSTATUS_OK",
L"DBPROPSTATUS_NOTSUPPORTED",
L"DBPROPSTATUS_BADVALUE",
L"DBPROPSTATUS_BADOPTION",
L"DBPROPSTATUS_BADCOLUMN",
L"DBPROPSTATUS_NOTALLSETTABLE",
L"DBPROPSTATUS_NOTSETTABLE",
L"DBPROPSTATUS_NOTSET",
L"DBPROPSTATUS_CONFLICTING",
L"DBPROPSTATUS_NOTAVAILABLE",
};
LPWSTR pwszValue = pwszUnexpected;
switch(V_VT(&pPropertySets[iPropSet].rgProperties[iProp].vValue))
{
case VT_EMPTY:
pwszValue = pwszEmpty;
break;
case VT_BOOL:
if (V_BOOL(&pPropertySets[iPropSet].rgProperties[iProp].vValue) == VARIANT_TRUE)
pwszValue = pwszTrue;
else if (V_BOOL(&pPropertySets[iPropSet].rgProperties[iProp].vValue) == VARIANT_FALSE)
pwszValue = pwszFalse;
break;
case VT_I4:
swprintf(wszBuff, L"%d", V_I4(&pPropertySets[iPropSet].rgProperties[iProp].vValue));
pwszValue = (LPWSTR)wszBuff;
break;
case VT_BSTR:
pwszValue = V_BSTR(&pPropertySets[iPropSet].rgProperties[iProp].vValue);
break;
}
{
DBPROPINFO * pPropInfo = NULL;
pPropInfo = GetPropInfo(pPropertySets[iPropSet].rgProperties[iProp].dwPropertyID, pPropertySets[iPropSet].guidPropertySet,
m_pThisTestModule->m_pIUnknown, SESSION_INTERFACE);
if (pPropInfo && pPropInfo->pwszDescription)
{
odtLog << L"\t" << iPropSet << L" " << iProp << L" Property " << pPropInfo->pwszDescription << L" " << pwszValue;
PROVIDER_FREE(pPropInfo->pwszDescription);
}
else
odtLog << L"\t" << iPropSet << L" " << iProp << L" Property " << pPropertySets[iPropSet].rgProperties[iProp].dwPropertyID << L" " << pwszValue;
if (pPropertySets[iPropSet].rgProperties[iProp].dwOptions == DBPROPOPTIONS_REQUIRED)
odtLog << L" REQUIRED";
else
odtLog << L" OPTIONAL";
if (pPropInfo->dwFlags & DBPROPFLAGS_READ)
odtLog << L" READ";
if (pPropInfo->dwFlags & DBPROPFLAGS_WRITE)
odtLog << L" WRITE";
odtLog << L" " << ppwszStatus[pPropertySets[iPropSet].rgProperties[iProp].dwStatus];
odtLog << L"\n";
PROVIDER_FREE(pPropInfo);
}
}
odtLog << L"\n";
}
// Only free the properties if we obtained them.
if (!(cPropSets && pPropSets))
FreeProperties(&cPropertySets, &pPropertySets);
}
SAFE_RELEASE(pICmdProp);
SAFE_RELEASE(pIRowsetInfo);
}
//---------------------------------------------------------------------
//@mfunc Copy testcase information from the base class to member
// command objects. As all use common test case information.
//
void
CICmdWParams::TestCaseInfoToObjs(void)
{
cRow.SetOwningMod(0, this->m_pThisTestModule);
}
//--------------------------------------------------------------------
//@mfunc Routine to Free parameters for GetParameterInfo.
//@rdesc void
//
void
CICmdWParams::FreeDescParams()
{
FREE_DATA (m_pNamesBuffer);
FREE_DATA (m_rgParamInfo);
}
//--------------------------------------------------------------------
//@mfunc Gets relevent properties and sets member variables.
BOOL
CICmdWParams::GetReleventProperties()
{
WCHAR * pwszDriverODBCVer;
// Needed for testing case of named parameters
if (!GetProperty(DBPROP_IDENTIFIERCASE,
DBPROPSET_DATASOURCEINFO,m_pIDBInitialize, &m_ulIdentifierCase))
m_ulIdentifierCase = DBPROPVAL_IC_UPPER;
// Needed for testing case of named parameters
if (!GetProperty(DBPROP_QUOTEDIDENTIFIERCASE,
DBPROPSET_DATASOURCEINFO,m_pIDBInitialize, &m_ulQuotedIdentifierCase))
m_ulQuotedIdentifierCase = DBPROPVAL_IC_UPPER;
if (GetProperty(KAGPROP_DRIVERODBCVER, DBPROPSET_PROVIDERDATASOURCEINFO,
m_pIDBInitialize,&pwszDriverODBCVer))
m_dwDriverODBCVer=_wtoi(pwszDriverODBCVer);
else
m_dwDriverODBCVer=0; // Unknown/not Kagera provider
m_fDefaultVal = SupportedProperty(DBPROP_COL_DEFAULT, DBPROPSET_COLUMN, m_pThisTestModule->m_pIUnknown, SESSION_INTERFACE);
// Needed for DBPROP_DBMSNAME
ULONG cPropertyIDSets = 1;
DBPROPIDSET rgPropertyIDSets[1];
DBPROPID rgPropertyIDs[]=
{
DBPROP_DBMSNAME,
DBPROP_PROVIDERNAME,
DBPROP_PROCEDURETERM
};
ULONG cPropertySets = 0;
ULONG ulMaxParamNameLen = SP_MAX_PARAMNAME_LENGTH;
DBPROPSET * rgPropertySets = NULL;
IDBProperties * pIDBProp = NULL;
IDBInfo * pIDBInfo = NULL;
DBLITERALINFO * pLiteralInfo = NULL;
ULONG cLiterals;
WCHAR * pwszCharBuffer = NULL;
DBLITERAL dbliteral = DBLITERAL_COLUMN_NAME;
rgPropertyIDSets[0].guidPropertySet = DBPROPSET_DATASOURCEINFO;
rgPropertyIDSets[0].cPropertyIDs = sizeof(rgPropertyIDs)/sizeof(DBPROPID);
rgPropertyIDSets[0].rgPropertyIDs=&rgPropertyIDs[0];
if(FAILED(m_pIDBInitialize->QueryInterface(IID_IDBProperties,(void **)&pIDBProp)))
return FALSE;
if (SUCCEEDED(m_pIDBInitialize->QueryInterface(IID_IDBInfo,(void **)&pIDBInfo)))
{
if (S_OK == pIDBInfo->GetLiteralInfo(1, &dbliteral, &cLiterals, &pLiteralInfo, &pwszCharBuffer))
{
ulMaxParamNameLen = pLiteralInfo->cchMaxLen;
// Per spec the value may have no maximum or the max may be unknown, so use our default value.
if (ulMaxParamNameLen == ~0)
ulMaxParamNameLen = SP_MAX_PARAMNAME_LENGTH;
// Set the maximum parameter name length to 30 for now due to spec issues.
ulMaxParamNameLen = 29;
odtLog << L"Maximum parameter name length hard-coded to 29 due to spec issues.\n";
SAFE_FREE(pLiteralInfo);
SAFE_FREE(pwszCharBuffer);
}
}
if (pIDBProp->GetProperties(cPropertyIDSets, rgPropertyIDSets, &cPropertySets, &rgPropertySets) == S_OK)
{
// Initialize string lookups
m_Syntax.Init(V_BSTR(&(rgPropertySets[0].rgProperties[1].vValue)), // Provider name
V_BSTR(&(rgPropertySets[0].rgProperties[0].vValue)), // DBMS name
NUMELEM(g_DialectTokens), // Size of dialect list
g_DialectTokens, // List of dialect tokens
g_ProviderList, // List of providers
g_DBMSList, // List of DBMS's
g_Dialects, // List of dialects
m_pTable, // Table to use
ulMaxParamNameLen); // Maximum size of a parameter name
// If the DBPROP_PROCEDURETERM isn't NULL, then the provider/dbms supports procedures
if (V_BSTR(&(rgPropertySets[0].rgProperties[2].vValue)))
{
m_fProcedureSupport=TRUE;
if (!m_Syntax.IsKnown())
{
odtLog <<L"Procedures are supported but the dialect is unknown. Assuming ANSI SQL. \n";
}
}
}
//Free rgPropertySets
SAFE_RELEASE(pIDBProp);
SAFE_RELEASE(pIDBInfo);
FreeProperties(&cPropertySets, &rgPropertySets);
return TRUE;
}
//--------------------------------------------------------------------
//@mfunc FillinputBindings variant to fill pData for a number of rows.
// Assumes the *ppData is already allocated.
//--------------------------------------------------------------------
HRESULT
CICmdWParams::FillInputBindingsForArray(
CTable *pTable,
DBACCESSORFLAGS dwFlags,
DBLENGTH cbRowSize,
DBCOUNTITEM cBindings,
DBBINDING *rgBindings,
BYTE **ppData,
ULONG cNumRows,
DBCOUNTITEM *rgRowNums,
DBORDINAL cParamColMap,
DB_LORDINAL *rgParamColMap
)
{
ULONG i = 0;
HRESULT hResult = S_OK;
BYTE *pDataPtr=NULL;
ASSERT (*ppData);
for (i = 0; i < cNumRows; i++)
{
// Get to the next row.
pDataPtr = *ppData + (i * cbRowSize);
hResult = FillInputBindings(pTable, dwFlags, cBindings,
rgBindings, &pDataPtr, rgRowNums[i], cParamColMap, rgParamColMap);
if (FAILED(hResult)) // If we fail return from here. no need to do any further.
break;
}
return hResult;
}
//--------------------------------------------------------------------
//@mfunc ReleaseInputBindings variant to release pData allocated by
// FillInputBindingsForArray.
// Calls ReleaseInputBindings and Frees memory pointed by pData;
//--------------------------------------------------------------------
HRESULT
CICmdWParams::ReleaseInputBindingsForArray(
ULONG cNumRows,
DBLENGTH cbRowSize,
DBCOUNTITEM cBindings,
DBBINDING *rgBindings,
BYTE *pData)
{
ULONG i = 0;
BYTE *pDataPtr = NULL;
HRESULT hResult = S_OK;
if (!pData)
return E_FAIL;
for (i = 0; i < cNumRows; i++)
{
// Get to the next row.
pDataPtr = pData + ( i * cbRowSize );
hResult = ReleaseInputBindingsMemory(cBindings, rgBindings, pDataPtr, FALSE);
}
PROVIDER_FREE(pData);
return hResult;
}
//--------------------------------------------------------------------
//@mfunc Base class Initialization Routine
//@rdesc TRUE or FALSE
//
BOOL
CICmdWParams::Init()
{
BOOL fResult = FALSE;
CCol TempCol(m_pIMalloc);
DB_UPARAMS cParams;
DBPARAMINFO *rgLocalParamInfo = NULL;
DBORDINAL cColsOut;
UINT i=0;
BOOL fLocalPrepare = FALSE;
IRowset *pIRowset= NULL;
// Initialize error cache
m_EC.Init(GetModInfo()->GetDebugMode());
if (COLEDB::Init())
{
if (m_pThisTestModule && m_pThisTestModule->m_pVoid)
m_cInsertRowNum = ((CTable *)(m_pThisTestModule->m_pVoid))->CountRowsOnTable()+1;
// Get the IDBInitialize interface pointer.
if (!VerifyInterface(m_pThisTestModule->m_pIUnknown, IID_IDBInitialize,
DATASOURCE_INTERFACE,(IUnknown **)&m_pIDBInitialize))
goto CLEANUP;
//Copy the IDBCreateCommand pointer we got at the module level
//down to the testcase level. Note, this increments the ref count,
//So we call ReleaseDbSession in the Terminate, but the DbSession does
//not go away until ModuleTerminate time.
SetDBSession((IDBCreateCommand *)m_pThisTestModule->m_pIUnknown2);
// Set the session for the CRow object.
cRow.SetDBSession((IDBCreateCommand *)m_pThisTestModule->m_pIUnknown2);
//Have this testcase use the table but don't
//let table be deleted, since we'll use it for next test case.
SetTable((CTable *)m_pThisTestModule->m_pVoid, DELETETABLE_NO);
cRow.SetTable((CTable *)m_pThisTestModule->m_pVoid, DELETETABLE_NO);
// Gets the required property values.
GetReleventProperties();
// Create another table.
m_pExtraTable = new CTable((IUnknown *)m_pThisTestModule->m_pIUnknown2,
(LPWSTR)gwszModuleName);
if (!m_pExtraTable)
{
PRVTRACE (wszMemoryAllocationError);
return FALSE;
}
TestCaseInfoToObjs();
// Opening a rowset on Existing command object to stay in FireHose mode.
// and Creating the other command so that they will be generated on a new hdbc.
TEST_CHECK (m_pTable->ExecuteCommand(SELECT_ALLFROMTBL, IID_IRowset, NULL, NULL,
NULL, NULL, EXECUTE_ALWAYS, 0, NULL, NULL, (IUnknown **)&pIRowset, &m_pTable->m_pICommand), S_OK);
// Create Two Command Objects.
if(FAILED(m_pIDBCreateCommand->CreateCommand(NULL, IID_ICommand, (IUnknown **)&m_pICommand)))
{
odtLog << "Create command on m_pICommand Failed\n";
goto CLEANUP;
}
if(FAILED(m_pIDBCreateCommand->CreateCommand(NULL, IID_ICommand, (IUnknown **)&m_pIEmptyCommand)))
{
odtLog << "Create command on m_pIEmptyCommand Failed\n";
goto CLEANUP;
}
// Now we are done with the use of rowset. so release it.
// Since the our command objects are created with a new hdbc because of the existing
// firehose mode. We can release the rowset now.
if (pIRowset) ReleaseRowsetPtr(&pIRowset);
/////////////////////////////////////////////////////////
// Build array containing col ordinals for all able
// cols so we can use them in parameterized queries
/////////////////////////////////////////////////////////
//Get memory to hold array of all col numbers. NOTE: This
//is the max possible, we won't necessarily use them all.
// Mapping array to be used by GetAccessorAndBindings and FillInputBinding
m_rgParamColMap = (DBORDINAL *)m_pIMalloc->Alloc(m_pTable->CountColumnsOnTable() * sizeof(DBORDINAL));
if (m_rgParamColMap == NULL)
{
odtLog << wszMemoryAllocationError;
goto CLEANUP;
}
m_rgParamOrdinals = (DB_UPARAMS *)m_pIMalloc->Alloc(m_pTable->CountColumnsOnTable() * sizeof(DB_UPARAMS));
if (m_rgParamOrdinals == NULL)
{
odtLog << wszMemoryAllocationError;
goto CLEANUP;
}
// Data source type for use in SetParameterInfo.
m_pParamStruct = (ParamStruct *)m_pIMalloc->Alloc(m_pTable->CountColumnsOnTable() * sizeof(ParamStruct));
if (m_pParamStruct == NULL)
{
odtLog << wszMemoryAllocationError;
goto CLEANUP;
}
// Parameter Bind information for use in SetParameterInfo.
m_rgDbParamBindInfo = (DBPARAMBINDINFO *)m_pIMalloc->Alloc(m_pTable->CountColumnsOnTable() * sizeof(DBPARAMBINDINFO));
if (m_rgDbParamBindInfo == NULL)
{
odtLog << wszMemoryAllocationError;
goto CLEANUP;
}
// Set all array entries to NULL so terminate can free the pointers if non-NULL.
memset(m_rgDbParamBindInfo, 0, (size_t)(m_pTable->CountColumnsOnTable() * sizeof(DBPARAMBINDINFO)));
//We'll use this count as the index to the array as we build it
m_cParamColMap = 0;
m_cDbParamBindInfo = 0;
for (i = 1; i <= m_pTable->CountColumnsOnTable(); i++)
{
CHECK(m_pTable->GetColInfo(i, TempCol), S_OK);
//Record the column number in the array if it is updateable.
if (TempCol.GetUpdateable() )
{
m_rgParamColMap[m_cParamColMap] = TempCol.GetColNum();
m_rgParamOrdinals[m_cDbParamBindInfo] = m_cDbParamBindInfo+1; // Parameter ordinal number.
AddParam(m_cDbParamBindInfo, i, DBPARAMIO_INPUT, NULL, FALSE, NULL, NULL, m_rgDbParamBindInfo, m_pParamStruct, m_pTable);
m_cParamColMap++;
m_cDbParamBindInfo++;
}
}
// Get the interface pointer for Accessor.
if (!VerifyInterface(m_pICommand, IID_IAccessor,
COMMAND_INTERFACE,(IUnknown **)&m_pCmdIAccessor))
{
goto CLEANUP;
}
// Get the Interface for a Command Text Object.
if (!VerifyInterface(m_pICommand, IID_ICommandText,
COMMAND_INTERFACE,(IUnknown **)&m_pICommandText))
{
goto CLEANUP;
}
// Get the Interface pointer for ICommandWithparameters Object.
if (!VerifyInterface(m_pICommand, IID_ICommandWithParameters,
COMMAND_INTERFACE,(IUnknown **)&m_pICmdWParams))
{
goto CLEANUP;
}
// Get the Interface pointer for ICommandPrepare Object.
if (!VerifyInterface(m_pICommand, IID_ICommandPrepare,
COMMAND_INTERFACE,(IUnknown **)&m_pICommandPrepare))
{
// Continue;
goto CLEANUP;
}
////////////////////////////////////////////////////////////////////////////////
//Now build the accessor using only the columns in m_rgUpdateableCols
////////////////////////////////////////////////////////////////////////////////
//Create a select for only the updatable columns
TEST_CHECK (m_pTable->ExecuteCommand(SELECT_UPDATEABLE, IID_IUnknown, NULL, NULL,
NULL, NULL, EXECUTE_NEVER, 0, NULL, NULL, NULL, &m_pICommand), S_OK);
// Get the Bindings for the Updateable columns only.
// Passing an array containing the column ordinals for updateable columns only.
TEST_CHECK(GetAccessorAndBindings(m_pCmdIAccessor,
DBACCESSOR_PARAMETERDATA, &m_hAccessor, &m_rgBindings, &m_cBindings,
&m_cbRowSize, DBPART_VALUE | DBPART_STATUS | DBPART_LENGTH, UPDATEABLE_COLS_BOUND,
FORWARD, NO_COLS_BY_REF, &m_rgColInfo, &cColsOut, NULL, DBTYPE_EMPTY, m_cParamColMap,
(LONG_PTR *)m_rgParamColMap, (DBORDINAL *)m_rgParamOrdinals ,NO_COLS_OWNED_BY_PROV,
DBPARAMIO_INPUT, BLOB_LONG ), S_OK);
// Lets restore the text object to insert command.
TEST_CHECK(m_pTable->ExecuteCommand(INSERT_ALLWITHPARAMS, IID_IUnknown, NULL,
&m_pwszSqlInsertAllWithParams, NULL, NULL, EXECUTE_NEVER, 0, NULL, NULL, NULL, &m_pICommand), S_OK);
// Now test GetParameterInfo and if it fails use SetParameterInfo to Build Initial
// list.
m_hr = m_pICmdWParams->GetParameterInfo(&cParams, &rgLocalParamInfo, NULL);
if((m_hr == DB_E_NOTPREPARED))
{
OLECHAR * pNamesBuffer=NULL;
//Have to prepare the statement first.
fLocalPrepare = TRUE;
TEST_CHECK (m_pICommandPrepare->Prepare(1), S_OK);
m_hr = m_pICmdWParams->GetParameterInfo(&cParams, &rgLocalParamInfo, &pNamesBuffer);
PROVIDER_FREE(pNamesBuffer);
}
if (m_hr == DB_E_PARAMUNAVAILABLE || (SUCCEEDED(m_hr) && (cParams == 0 || !rgLocalParamInfo)))
{
if (!CHECK(m_hr, DB_E_PARAMUNAVAILABLE))
{
// This is a bug in the provider, but I don't want to block all testing, so default to calling
// SetParameterInfo but flag as a failure
odtLog << L"Error: Provider claims to derive parameter information but doesn't, defaulting " \
L"to using SetParameterInfo. This may cause errors in some variations. \n\n";
}
m_hr = m_pICmdWParams->SetParameterInfo(m_cDbParamBindInfo, m_rgParamOrdinals, m_rgDbParamBindInfo);
if (m_hr != S_OK && m_hr != DB_S_TYPEINFOOVERRIDDEN )
{
CHECK(m_hr, S_OK);
goto CLEANUP;
}
// SetParameterInfo called successfully.
// Means that provider could not describe parameters and we have to use SetParameterInfo.
m_bSetParameterInfo = TRUE;
}
else if (m_hr != S_OK)
{
// GetParameterInfo returned an unexpected error, fResult is FALSE.
goto CLEANUP;
}
if (fLocalPrepare)
{
fLocalPrepare = FALSE;
TEST_CHECK (m_pICommandPrepare->Unprepare(), S_OK);
}
// Set the IID to use for data retrieval
if (m_eTestCase == TC_Rowset)
m_iidExec = IID_IRowset;
else
m_iidExec = IID_IRow;
// Set the iid for the cRow object to use to match.
cRow.SetIID(m_iidExec);
fResult = TRUE;
}
CLEANUP:
// Cleanup before returning
if (pIRowset)
ReleaseRowsetPtr(&pIRowset);
if (rgLocalParamInfo)
m_pIMalloc->Free(rgLocalParamInfo);
return fResult;
}
//----------------------------------------------
//@mfunc Generate the next row number to insert.
//----------------------------------------------
DBCOUNTITEM CICmdWParams::NextInsertRowNum()
{
return (m_cInsertRowNum++);
}
//-------------------------------------------------------------------------------
//@mfunc Prepare parameter structure for Execute command (parameter information).
// release if previously allocated.
//-------------------------------------------------------------------------------
BOOL
CICmdWParams::MakeDataForCommand(DBCOUNTITEM ulRowNum)
{
// Use the Bindings generated in Init and create RowData.
ReleaseDataForCommand();
if (!CHECK(FillInputBindings((CTable *)m_pThisTestModule->m_pVoid, DBACCESSOR_PARAMETERDATA,
m_cBindings, m_rgBindings, &m_pData, ulRowNum, m_cParamColMap, (DB_LORDINAL *)m_rgParamColMap), S_OK))
{
return FALSE;
}
m_DbParamsAll.cParamSets = 1;
m_DbParamsAll.hAccessor = m_hAccessor;
m_DbParamsAll.pData = m_pData;
return TRUE;
}
//-----------------------------------------------------------------------------
//@mfunc MakeTempTableName (Replace first character of the existing table name.)
//------------------------------------------------------------------------------
WCHAR *
CICmdWParams::MakeTempTableName(WCHAR wch)
{
WCHAR *pwszTableName;
pwszTableName = (WCHAR *)m_pIMalloc->Alloc(wcslen(m_pTable->GetTableName())*sizeof (WCHAR) + sizeof (WCHAR));
// if not null process else return null.
if (pwszTableName)
{
// Now copy table name.
wcscpy(pwszTableName, m_pTable->GetTableName());
// Replace first character with parameter.
pwszTableName[0] = wch;
}
return pwszTableName;
}
//------------------------------------------------------------------------------
//@mfunc Release allocated memory for the parameter structure.
//---------------------------------------------------------------------
void
CICmdWParams::ReleaseDataForCommand()
{
if (m_pData)
{
ReleaseInputBindingsMemory( m_cBindings, m_rgBindings, m_pData, TRUE);
m_pData = NULL;
}
}
enum {
SUBTYPE_LONGVARCHAR,
SUBTYPE_VARCHAR,
SUBTYPE_CHAR,
SUBTYPE_WLONGVARCHAR ,
SUBTYPE_WVARCHAR ,
SUBTYPE_WCHAR,
SUBTYPE_LONGVARBINARY ,
SUBTYPE_VARBINARY,
SUBTYPE_BINARY
};
//--------------------------------------------------------------------
// @mfunc Create sql statements, Binding information, and accessor for
// the types specified by input arguements.
//--------------------------------------------------------------------
BOOL
CICmdWParams::GetSqlBindInfo(
LPOLESTR pwszDataSourceType, // [In]
DBTYPE wType, // [In]
WCHAR *pwszCreateStatement, // [Out]
WCHAR *pwszInsertStatement, // [Out]
WCHAR *pwszDropStatement,
DBBINDING ** prgDbBindInfo, // [Out]
DBPARAMBINDINFO ** prgDbParamBindInfo, // [Out]
DBORDINAL ** prgParamColumnMap, // [Out]
HACCESSOR * phLocalAccessor) // [Out]
{
BOOL fResult = FALSE, fFound = FALSE;
ULONG ulSubType = 0, i = 0;
CCol TempCol;
WCHAR *pwszTableName=NULL;
WCHAR wszCreateFormat[] = L"Create table %s ( %s)";
WCHAR wszInsertFormat[] = L"Insert into %s ( %s ) values ( ? )";
WCHAR wszDropFormat[] = L"Drop table %s";
WCHAR *wszColDef = NULL;
// Initialize the output strings.
pwszCreateStatement[0] = L'\0';
pwszInsertStatement[0] = L'\0';
pwszDropStatement[0] = L'\0';
TEST_ALLOC(DBBINDING, *prgDbBindInfo, 0, sizeof(DBBINDING));
TEST_ALLOC(DBPARAMBINDINFO, *prgDbParamBindInfo, 0, sizeof(DBPARAMBINDINFO));
TEST_ALLOC(DBORDINAL, *prgParamColumnMap, 0, sizeof(DBORDINAL));
switch (wType)
{
case DBTYPE_STR:
if (! wcscmp ((pwszDataSourceType), L"DBTYPE_LONGVARCHAR"))
ulSubType = SUBTYPE_LONGVARCHAR;
else if (! wcscmp ((pwszDataSourceType), L"DBTYPE_VARCHAR"))
ulSubType = SUBTYPE_VARCHAR;
else
ulSubType = SUBTYPE_CHAR;
break;
case DBTYPE_WSTR:
if (! wcscmp ((pwszDataSourceType), L"DBTYPE_WLONGVARCHAR"))
ulSubType = SUBTYPE_WLONGVARCHAR;
else if (! wcscmp ((pwszDataSourceType), L"DBTYPE_WVARCHAR"))
ulSubType = SUBTYPE_WVARCHAR;
else
ulSubType = SUBTYPE_WCHAR;
break;
case DBTYPE_BYTES:
if (! wcscmp ((pwszDataSourceType), L"DBTYPE_LONGVARBINARY"))
ulSubType = SUBTYPE_LONGVARBINARY;
else if (! wcscmp ((pwszDataSourceType), L"DBTYPE_VARBINARY"))
ulSubType = SUBTYPE_VARBINARY;
else
ulSubType = SUBTYPE_BINARY;
break;
default:
// Code might have to be added if it comes here.
ASSERT (TRUE);
break;
}
(*prgDbParamBindInfo)->pwszDataSourceType = NULL;
for (i = 1; i <= m_pTable->CountColumnsOnTable(); i++)
{
if (FAILED(m_pTable->GetColInfo(i, TempCol)))
goto CLEANUP;
//
if (!(TempCol.GetProviderType() == DBTYPE_STR ||
TempCol.GetProviderType() == DBTYPE_WSTR ||
TempCol.GetProviderType() == DBTYPE_BYTES) ||
!TempCol.GetUpdateable())
continue;
switch(ulSubType)
{
case SUBTYPE_LONGVARCHAR:
// Has to be long and Not fixed length type
if ((TempCol.GetProviderType() == DBTYPE_STR) && TempCol.GetIsLong() )
{
LPOLESTR ptr;
ptr = WCSDUP(m_pIMalloc, L"DBTYPE_LONGVARCHAR");
(*prgDbParamBindInfo)->pwszDataSourceType = ptr;
(*prgDbBindInfo)->wType = DBTYPE_STR;
// paramBindInfo.pwszDataSourceType = ptr;
fFound = TRUE;
}
else
continue;
break;
case SUBTYPE_VARCHAR:
if ((TempCol.GetProviderType() == DBTYPE_STR) &&
!(TempCol.GetIsLong()) &&
!(TempCol.GetIsFixedLength())
)
{
(*prgDbParamBindInfo)->pwszDataSourceType = WCSDUP(m_pIMalloc, L"DBTYPE_VARCHAR");
(*prgDbBindInfo)->wType = DBTYPE_STR;
fFound = TRUE;
}
else
continue;
break;
case SUBTYPE_CHAR:
if ((TempCol.GetProviderType() == DBTYPE_STR) &&
!(TempCol.GetIsLong()) &&
TempCol.GetIsFixedLength()
)
{
(*prgDbParamBindInfo)->pwszDataSourceType = WCSDUP(m_pIMalloc, L"DBTYPE_CHAR");
(*prgDbBindInfo)->wType = DBTYPE_STR;
fFound = TRUE;
}
else
continue;
break;
case SUBTYPE_WLONGVARCHAR:
// Has to be long and Not fixed length type
if ((TempCol.GetProviderType() == DBTYPE_WSTR) && TempCol.GetIsLong() )
{
(*prgDbParamBindInfo)->pwszDataSourceType = WCSDUP(m_pIMalloc, L"DBTYPE_WLONGVARCHAR");
(*prgDbBindInfo)->wType = DBTYPE_WSTR;
fFound = TRUE;
}
else
continue;
break;
case SUBTYPE_WVARCHAR:
if ((TempCol.GetProviderType() == DBTYPE_WSTR) &&
!(TempCol.GetIsLong()) &&
!(TempCol.GetIsFixedLength())
)
{
(*prgDbParamBindInfo)->pwszDataSourceType = WCSDUP(m_pIMalloc, L"DBTYPE_WVARCHAR");
(*prgDbBindInfo)->wType = DBTYPE_WSTR;
fFound = TRUE;
}
else
continue;
break;
case SUBTYPE_WCHAR:
if ((TempCol.GetProviderType() == DBTYPE_STR) &&
!(TempCol.GetIsLong()) &&
TempCol.GetIsFixedLength()
)
{
(*prgDbParamBindInfo)->pwszDataSourceType = WCSDUP(m_pIMalloc, L"DBTYPE_WCHAR");
(*prgDbBindInfo)->wType = DBTYPE_WSTR;
fFound = TRUE;
}
else
continue;
break;
case SUBTYPE_LONGVARBINARY:
// Has to be long and Not fixed length type
if ((TempCol.GetProviderType() == DBTYPE_BYTES) && TempCol.GetIsLong() )
{
(*prgDbParamBindInfo)->pwszDataSourceType = WCSDUP(m_pIMalloc, L"DBTYPE_LONGVARBINARY");
(*prgDbBindInfo)->wType = DBTYPE_BYTES;
fFound = TRUE;
}
else
continue;
break;
case SUBTYPE_VARBINARY:
if ((TempCol.GetProviderType() == DBTYPE_BYTES) &&
!(TempCol.GetIsLong()) &&
!(TempCol.GetIsFixedLength())
)
{
(*prgDbParamBindInfo)->pwszDataSourceType = WCSDUP(m_pIMalloc, L"DBTYPE_VARBINARY");
(*prgDbBindInfo)->wType = DBTYPE_BYTES;
fFound = TRUE;
}
else
continue;
break;
case SUBTYPE_BINARY:
if ((TempCol.GetProviderType() == DBTYPE_BYTES) &&
!(TempCol.GetIsLong()) &&
TempCol.GetIsFixedLength()
)
{
(*prgDbParamBindInfo)->pwszDataSourceType = WCSDUP(m_pIMalloc, L"DBTYPE_BINARY");
(*prgDbBindInfo)->wType = DBTYPE_BYTES;
fFound = TRUE;
}
else
continue;
break;
default:
ASSERT (!L"Need to Add more code");
break;
}
if (fFound)
break;
}
if (fFound)
{
TempCol.CreateColDef( &wszColDef);
pwszTableName = MakeTempTableName(L'Z');
// Found the type. Now form the statement and break;
swprintf(pwszCreateStatement, wszCreateFormat, pwszTableName, wszColDef);
swprintf(pwszInsertStatement, wszInsertFormat, pwszTableName, TempCol.GetColName());
swprintf(pwszDropStatement, wszDropFormat, pwszTableName);
// Free wszColDef.
FREE_DATA (wszColDef);
FREE_DATA (pwszTableName);
// ParamBindinfo.
// Build Information for SetParameterInfo.
(*prgDbParamBindInfo)->pwszName = NULL;
(*prgDbParamBindInfo)->dwFlags = DBPARAMFLAGS_ISINPUT;// | DBPARAMFLAGS_ISOUTPUT;
if (TempCol.GetNullable() == TRUE )
(*prgDbParamBindInfo)->dwFlags |= DBPARAMFLAGS_ISNULLABLE;
(*prgDbParamBindInfo)->ulParamSize = TempCol.GetPrecision();
(*prgDbParamBindInfo)->bScale = (BYTE)TempCol.GetScale();
(*prgDbParamBindInfo)->bPrecision = (BYTE)TempCol.GetPrecision();
// DbBinding info.
(*prgDbBindInfo)->dwPart = DBPART_VALUE | DBPART_LENGTH | DBPART_STATUS;
(*prgDbBindInfo)->eParamIO = DBPARAMIO_INPUT ;
(*prgDbBindInfo)->iOrdinal = 1;
(*prgDbBindInfo)->pTypeInfo = NULL;
(*prgDbBindInfo)->obValue = offsetof(DATA, bValue);
(*prgDbBindInfo)->cbMaxLen = TempCol.GetMaxSize();
(*prgDbBindInfo)->obLength = offsetof(DATA, ulLength);
(*prgDbBindInfo)->obStatus = offsetof (DATA, sStatus);
(*prgDbBindInfo)->dwMemOwner = DBMEMOWNER_CLIENTOWNED;
(*prgDbBindInfo)->pBindExt = NULL;
(*prgDbBindInfo)->bPrecision = 0;
(*prgDbBindInfo)->bScale = 0;
// Now Set the column mapping with original table for FillInputBindings.
(*prgParamColumnMap)[0] = TempCol.GetColNum();
// Lets create the accessor.
TEST_CHECK (m_pCmdIAccessor->CreateAccessor(DBACCESSOR_PARAMETERDATA, 1, *prgDbBindInfo, 5000,
phLocalAccessor, NULL), S_OK);
// Everything is OK.
fResult = TRUE;
}
CLEANUP:
return fResult;
}
//---------------------------------------------------------------------
// @mfunc Use SetParameterInfo to Set typeinfo and then execute .
// for all possible variations of DBTYPE_STR, DBTYPE_WSTR, DBTYPE_BYTES
//---------------------------------------------------------------------
BOOL
CICmdWParams::ExecuteWithSetParamInfo(
LPOLESTR pwszDataSourceType,
DBTYPE wType
)
{
WCHAR *wszCreateStatement = NULL;
WCHAR *wszInsertStatement = NULL;
WCHAR *wszDropStatement = NULL;
DB_UPARAMS cParams=1;
HACCESSOR hLocalAccessor = DB_NULL_HACCESSOR;
DB_UPARAMS rgParamOrdinals[] = { 1 };
DBBINDING *rgDbBindInfo = NULL;
DBPARAMBINDINFO *rgDbParamBindInfo = NULL;
DB_LORDINAL *rgParamColumnMap = NULL;
DBROWCOUNT cRowsAffected = 0;
void *pData = NULL;
HRESULT hr;
DBCOUNTITEM ulRowNum=0;
DBPARAMS DbParams;
BOOL fResult = FALSE;
DBLENGTH ulLength = 0;
// Initialize.
DbParams.pData = NULL;
wszCreateStatement = (WCHAR *)m_pIMalloc->Alloc(SP_TEXT_BLOCK_SIZE);
if (!wszCreateStatement)
{
odtLog << wszMemoryAllocationError;
goto CLEANUP;
}
wszInsertStatement = (WCHAR *)m_pIMalloc->Alloc(SP_TEXT_BLOCK_SIZE);
if (!wszInsertStatement)
{
odtLog << wszMemoryAllocationError;
goto CLEANUP;
}
wszDropStatement = (WCHAR *)m_pIMalloc->Alloc(SP_TEXT_BLOCK_SIZE);
if (!wszDropStatement)
{
odtLog << wszMemoryAllocationError;
goto CLEANUP;
}
wszCreateStatement[0] = NULL;
wszInsertStatement[0] = NULL;
wszDropStatement[0] = NULL;
// First Create the Sql statement for the wType.
if (!GetSqlBindInfo(pwszDataSourceType, wType, wszCreateStatement, wszInsertStatement, wszDropStatement,
(DBBINDING **)&rgDbBindInfo, (DBPARAMBINDINFO **)&rgDbParamBindInfo,
(DBORDINAL **)&rgParamColumnMap, &hLocalAccessor))
{
odtLog << L"Couldn't find a matching type in the table for :" << pwszDataSourceType << L" " << wType << L"\n" ;
// Let's not fail the test for that reason.
fResult = TRUE;
goto CLEANUP;
}
// First let's create the table.
TEST_CHECK (m_pICommandText->SetCommandText(DBGUID_DBSQL, wszCreateStatement), S_OK);
TEST_CHECK (m_pICommandText->Execute (NULL, IID_NULL, NULL, NULL, NULL), S_OK);
hr = m_pICmdWParams->SetParameterInfo (cParams, rgParamOrdinals, rgDbParamBindInfo);
if (! SUCCEEDED(hr))
goto CLEANUP;
// Use fill inputbindings to generate the data for insert.
ulRowNum = NextInsertRowNum();
// Fill input bindings.
// We are using m_pTable pointer just to fill pData. The actual table we are using
// different.
TEST_CHECK((hr = FillInputBindings(m_pTable, DBACCESSOR_PARAMETERDATA, cParams,
rgDbBindInfo, (BYTE **)&pData, ulRowNum, cParams, rgParamColumnMap)), S_OK);
// First Set the Insert command.
TEST_CHECK (m_pICommandText->SetCommandText(DBGUID_DBSQL, wszInsertStatement), S_OK);
DbParams.hAccessor = hLocalAccessor;
DbParams.cParamSets = 1;
DbParams.pData = pData;
TESTC( rgDbBindInfo->wType == DBTYPE_STR ||
rgDbBindInfo->wType == DBTYPE_WSTR ||
rgDbBindInfo->wType == DBTYPE_BYTES);
switch(rgDbBindInfo->wType)
{
case DBTYPE_BYTES:
ulLength = 20;
break;
case DBTYPE_STR:
{
DBLENGTH cch = 0;
LPSTR pszStr = (LPSTR)((BYTE *)pData+rgDbBindInfo->obValue);
ulLength = 0;
while(pszStr[ulLength] && cch < 20)
{
if (!IsDBCSLeadByte (pszStr[ulLength]))
ulLength++;
else
ulLength+=2;
cch++;
}
break;
}
case DBTYPE_WSTR:
ulLength = 20*sizeof(WCHAR);
break;
}
// Override the data generated by Private library and insert only 10 chars.
*((DBLENGTH *)((BYTE *)pData + rgDbBindInfo->obLength)) = ulLength;
TEST_CHECK (m_pICommandText->Execute (NULL, IID_NULL, &DbParams, &cRowsAffected, NULL), S_OK);
fResult = TRUE;
CLEANUP:
// First let's drop the table.
if (wszDropStatement && wszDropStatement[0])
{
CHECK (m_pICommandText->SetCommandText(DBGUID_DBSQL, wszDropStatement), S_OK);
CHECK (m_pICommandText->Execute (NULL, IID_NULL, NULL, NULL, NULL), S_OK);
}
if (DbParams.pData)
ReleaseInputBindingsMemory(1, rgDbBindInfo, (BYTE *)DbParams.pData, TRUE);
FREE_DATA (rgDbParamBindInfo->pwszDataSourceType);
FREE_DATA (rgDbParamBindInfo);
FREE_DATA (rgParamColumnMap);
FREE_DATA (rgDbBindInfo);
FREE_DATA (wszCreateStatement);
FREE_DATA (wszInsertStatement);
FREE_DATA (wszDropStatement);
return fResult;
}
//---------------------------------------------------------------------
//@mfunc For GetParameterInfo. Compares information returned by
// GetParameterInfo with that of column information.
//---------------------------------------------------------------------
HRESULT CICmdWParams::CompareDBParamInfo(
DBCOUNTITEM cCols, // [in] Number of columns in rgColumnsInfo. This is expected to match cParams
DBORDINAL * prgParamColMap, // [in] Parameter to column mapping array
DB_UPARAMS cParams, // [in] Number of parameter information to compare
DBPARAMINFO * rgParamInfo, // [in] Array of DBPARAMINFO containing parameter info
LPOLESTR pNamesBuffer // [in] Names buffer from GetParameterInfo.
)
{
ULONG iCol;
DBLENGTH cbRowSize = 0;
BOOL bResult = FALSE;
DBBINDING * pBinding = NULL;
DBPARAMBINDINFO * pParamBindInfo = NULL;
ParamStruct * pParamStruct = NULL;
DB_UPARAMS * prgParamOrdinals = NULL;
HRESULT hr = E_FAIL;
// If no columns are expected, verify null out params
if (!cCols)
{
TESTC(cParams == 0);
TESTC(rgParamInfo == NULL);
TESTC(pNamesBuffer == NULL);
return S_OK;
}
TEST_ALLOC(DBBINDING, pBinding, 0, (size_t)(sizeof(DBBINDING) * cCols));
TEST_ALLOC(DBPARAMBINDINFO, pParamBindInfo, 0, (size_t)(sizeof(DBPARAMBINDINFO) * cCols));
TEST_ALLOC(ParamStruct, pParamStruct, 0, (size_t)(sizeof(ParamStruct) * cCols));
TEST_ALLOC(DB_UPARAMS, prgParamOrdinals, 0, (size_t)(sizeof(DB_UPARAMS) * cCols));
// Compare the given Parameter information against the columns information
// gotten by ExecuteCommand. In this case cParams should match cColumns.
for (iCol = 0; iCol < cCols; iCol++)
{
prgParamOrdinals[iCol] = iCol+1;
TESTC(AddParam(iCol, prgParamColMap[iCol], DBPARAMIO_INPUT, NULL, FALSE, &cbRowSize, pBinding, pParamBindInfo, pParamStruct, m_pTable))
}
// Note that VerifyParamInfo posts it's own failures, so posting a failure here will cause redundant postings
hr = S_OK; // If we made it to here we need to return S_OK since VerifyParamInfo now posts it's own failures
VerifyParamInfo(cCols, prgParamOrdinals, pParamBindInfo, cParams, rgParamInfo, pNamesBuffer);
CLEANUP:
PROVIDER_FREE(pBinding);
PROVIDER_FREE(pParamBindInfo);
PROVIDER_FREE(pParamStruct);
PROVIDER_FREE(prgParamOrdinals);
return hr;
}
//---------------------------------------------------------------------------
// CCommand::IsColDateTime
//
// @mfunc BOOL |
// CCommand |
// IsColDateTime |
// Can the data type hold DateTime values?
//
//
//---------------------------------------------------------------------------
BOOL CICmdWParams::IsColDateTime
(
DBTYPE ProviderType // @parm [IN] provider data type
)
{
if (ProviderType & DBTYPE_BYREF)
ProviderType &= ~DBTYPE_BYREF;
switch(ProviderType)
{
case DBTYPE_DATE: // OLE Auto. Date
case DBTYPE_DBDATE: // Date
case DBTYPE_DBTIME: // Time
case DBTYPE_DBTIMESTAMP: // TimeStamp
return TRUE;
default:
return FALSE; // Compiler needs this
}
}
//---------------------------------------------------------------------------
// CCommand::IsColI2
//
// @mfunc BOOL |
// CCommand |
// IsColI2 |
// Can the data type hold numeric values?
//
//
//---------------------------------------------------------------------------
BOOL CICmdWParams::IsColI2
(
DBTYPE ProviderType // @parm [IN] provider data type
)
{
if (ProviderType & DBTYPE_BYREF)
ProviderType &= ~DBTYPE_BYREF;
switch(ProviderType)
{
case DBTYPE_I2: //I2
return TRUE;
default:
return FALSE; // Compiler needs this
}
}
//---------------------------------------------------------------------------
// CCommand::IsColNumeric
//
// @mfunc BOOL |
// CCommand |
// IsColNumeric |
// Can the data type hold numeric values?
//
//
//---------------------------------------------------------------------------
BOOL CICmdWParams::IsColNumeric
(
DBTYPE ProviderType // @parm [IN] provider data type
)
{
if (ProviderType & DBTYPE_BYREF)
ProviderType &= ~DBTYPE_BYREF;
switch(ProviderType)
{
case DBTYPE_NUMERIC: // Numeric, Decimal
return TRUE;
default:
return FALSE; // Compiler needs this
}
}
// Returns count of character in string
ULONG wcschcount(LPWSTR pwsz, WCHAR ch)
{
ULONG cch = 0;
if (!pwsz)
return 0;
for (; *pwsz; pwsz++)
if (*pwsz == ch)
cch++;
return cch;
}
BOOL IsErrorStatus(DBSTATUS sStatus)
{
switch (sStatus)
{
case S_OK:
case DBSTATUS_S_ISNULL:
case DBSTATUS_S_TRUNCATED:
case DBSTATUS_S_DEFAULT:
return FALSE;
}
return TRUE;
}
// Returns the number of bytes required to display the given type
// as a WSTR.
DBLENGTH DisplaySize(CCol ColInfo)
{
DBLENGTH ulDispSize = ColInfo.GetMaxSize();
switch(ColInfo.GetProviderType())
{
case DBTYPE_DBTIMESTAMP:
ulDispSize = (ulDispSize+6)*sizeof(WCHAR);
break;
case DBTYPE_BYTES:
ulDispSize *= 2*sizeof(WCHAR);
break;
case DBTYPE_R4:
// Precision plus sign plus decimal point plus 'E'+ sign + 2 dig exponent
ulDispSize = (ulDispSize+6) * sizeof(WCHAR);
break;
case DBTYPE_CY:
case DBTYPE_R8:
case DBTYPE_NUMERIC:
case DBTYPE_DECIMAL:
// Precision plus sign plus decimal point plus 'E'+ sign + 3 dig exponent
ulDispSize = (ulDispSize+7) * sizeof(WCHAR);
break;
case DBTYPE_VARNUMERIC:
// Max precision plus sign plus decimal point plus 'E'+ sign + 3 dig exponent
ulDispSize = (255+7) * sizeof(WCHAR);
break;
case DBTYPE_I1:
case DBTYPE_I2:
case DBTYPE_I4:
case DBTYPE_I8:
// Precision plus sign
ulDispSize = (ulDispSize+1) * sizeof(WCHAR);
break;
case DBTYPE_BOOL:
// Per spec Appendix
ulDispSize = wcslen(L"FALSE")*sizeof(WCHAR);
break;
case DBTYPE_GUID:
// Per spec Appendix
ulDispSize = 38 * sizeof(WCHAR);
break;
case DBTYPE_DATE:
// Per spec Appendix
ulDispSize = 20 * sizeof(WCHAR);
break;
case DBTYPE_DBDATE:
// Per spec Appendix
ulDispSize = 10 * sizeof(WCHAR);
break;
case DBTYPE_FILETIME:
// Per spec Appendix
ulDispSize = 24 * sizeof(WCHAR);
break;
case DBTYPE_DBTIME:
// Per spec Appendix
ulDispSize = 8 * sizeof(WCHAR);
break;
default:
ulDispSize *= sizeof(WCHAR);
}
return ulDispSize;
}
// Format a string using replacable %1 parameters, allocating memory required
size_t FormatString(WCHAR ** ppwszDest, WCHAR * pwszFmt, ULONG cArgs, ...)
{
size_t ulStringLen=0;
ULONG iArg;
WCHAR ** ppwszParamArray=NULL;
va_list varargs;
va_start(varargs, cArgs);
if (!pwszFmt || !ppwszDest)
return 0;
if (cArgs)
{
// Allocate an array of wide chars large enough to hold the variable arguments
TEST_ALLOC(WCHAR *, ppwszParamArray, 0, cArgs * sizeof(WCHAR *));
for (iArg = 0; iArg < cArgs; iArg++)
ppwszParamArray[iArg] = va_arg(varargs, WCHAR *);
va_end(varargs);
}
ulStringLen = FormatStringFromArray(ppwszDest, pwszFmt, cArgs, ppwszParamArray);
CLEANUP:
PROVIDER_FREE(ppwszParamArray);
return ulStringLen;
}
// Format a string using replacable %1 parameters, allocating memory required
size_t FormatStringFromArray(WCHAR ** ppwszDest, WCHAR * pwszFmt, ULONG cArgs, WCHAR ** ppInsertArray)
{
size_t ulStringLen = 0, cch=0;
ULONG cInserts=0, iIns, ulInsNo;
WCHAR * pwszIns = NULL;
WCHAR * pwszPart = pwszFmt;
ASSERT(ppwszDest && pwszFmt);
ASSERT(cArgs == 0 || (cArgs > 0 && ppInsertArray));
*ppwszDest = NULL;
if (!pwszFmt )
goto CLEANUP;
pwszIns = wcsstr(pwszFmt, L"%");
// If no arguments or no %n params then we just return the format string
if (cArgs == 0 || !pwszIns)
{
*ppwszDest = wcsDuplicate(pwszFmt);
if (*ppwszDest)
ulStringLen = wcslen(pwszFmt)*sizeof(WCHAR);
return ulStringLen;
}
// Find out space needed for destination string
cch = wcslen(pwszFmt)+1;
for (iIns=0; iIns < cArgs; iIns++)
{
ASSERT(ppInsertArray[iIns]);
cch += wcslen(ppInsertArray[iIns]);
}
// Allocate space
SAFE_ALLOC(*ppwszDest, WCHAR, cch*sizeof(WCHAR));
// Start with an empty string
*ppwszDest[0] = L'\0';
for (iIns = 0; iIns < cArgs && pwszIns; iIns++)
{
// Found a % character, replace with a null term
*pwszIns = L'\0';
// Get the number associated with the %
ulInsNo = _wtol(pwszIns+1);
// If it wasn't a number after the % or it has a leading 0 then ignore it
if (ulInsNo && *(pwszIns+1) != L'0')
{
WCHAR wszInsNo[MAX_LTOW];
cInserts++;
// We try to be nicer than FormatMessage and make sure the count of inserts matches cArgs
ASSERT(cInserts <= cArgs);
if (cInserts > cArgs)
goto CLEANUP;
// Can't have an insert number greater than number of insert array elements
if (ulInsNo > cArgs)
goto CLEANUP;
_ltow(ulInsNo, wszInsNo, 10);
// Copy the part from the format string
wcscat(*ppwszDest, pwszPart);
// Copy the insert string
wcscat(*ppwszDest, ppInsertArray[ulInsNo-1]);
pwszPart = pwszIns+1+wcslen(wszInsNo);
}
// Put back the % to avoid side effects
*pwszIns = L'%';
// Find the next insert point
pwszIns = wcsstr(pwszIns+1, L"%");
}
// Copy any remaining part of the format string
wcscat(*ppwszDest, pwszPart);
ulStringLen = wcslen(*ppwszDest) * sizeof(WCHAR);
CLEANUP:
if (pwszIns)
*pwszIns = L'%';
if (!ulStringLen)
SAFE_FREE(*ppwszDest);
return ulStringLen;
}
BOOL CICmdWParams::bHasReturnParam(enum TOKEN_ENUM eProcType)
{
switch (eProcType)
{
case T_EXEC_PROC_SELECT_OUT_RET:
case T_EXEC_PROC_SELECT_INOUT_RET:
case T_EXEC_PROC_UPDATE_INPUT_RET:
case T_EXEC_PROC_INSERT_INPUT_RET:
case T_EXEC_PROC_DELETE_RET:
return TRUE;
}
return FALSE;
}
HRESULT CICmdWParams::CreateStoredProc(ICommandText * pICommandText, WCHAR * pwszProcName, WCHAR * pwszCreateStmt, BOOL fIsFunction)
{
HRESULT hr = S_OK;
CHAR * pszCreateStmt = NULL;
// Drop the proc if we were passed the name
if (pwszProcName)
DropStoredProcedure(pICommandText, pwszProcName, fIsFunction);
if (CHECK(hr = pICommandText->SetCommandText(DBGUID_DBSQL, pwszCreateStmt), S_OK))
hr = pICommandText->Execute(NULL, IID_NULL, NULL, NULL, NULL);
return hr;
}
HRESULT CICmdWParams::SetParameterInfoIfNeeded(DB_UPARAMS cParams, DB_UPARAMS * pParamOrdinals,
DBPARAMBINDINFO * pParamBindInfo, ICommand * pICommand)
{
ICommandWithParameters * pICmdWParams = NULL;
DBPARAMINFO * pParamInfo = NULL;
DB_UPARAMS cParamsDerived = 0;
WCHAR * pNamesBuffer = NULL;
HRESULT hr = E_FAIL;
// Use the member ICommand if we didn't specify one
if (!pICommand)
pICommand = m_pICommand;
// Get the Interface pointer for ICommandWithparameters Object.
TEST_COMPARE(VerifyInterface(pICommand, IID_ICommandWithParameters,
COMMAND_INTERFACE,(IUnknown **)&pICmdWParams), TRUE);
// Remove any old parameter information that might be hanging around.
TEST_CHECK(pICmdWParams->SetParameterInfo(0, NULL, NULL), S_OK);
// Call GetParameterInfo
hr = pICmdWParams->GetParameterInfo(&cParamsDerived, &pParamInfo, &pNamesBuffer);
if (hr == S_OK)
goto CLEANUP;
else if (hr == DB_E_PARAMUNAVAILABLE)
hr = pICmdWParams->SetParameterInfo(cParams, pParamOrdinals, pParamBindInfo);
else
CHECK(hr, S_OK);
CLEANUP:
PROVIDER_FREE(pParamInfo);
PROVIDER_FREE(pNamesBuffer);
SAFE_RELEASE(pICmdWParams);
return hr;
}
HRESULT CICmdWParams::PrepareForExecute(WCHAR * pwszExecuteStmt, ULONG cParams, DB_UPARAMS * pParamOrdinals,
DBPARAMBINDINFO * pPARAMBIND, BOOL * pfCanDerive, WCHAR * pwszProcName, WCHAR * pwszCreateProcStmt,
BOOL fIsFunction)
{
HRESULT hr;
BOOL fResult = TRUE;
BOOL fCanDerive = FALSE;
if (!pfCanDerive)
pfCanDerive = &fCanDerive;
if (pwszCreateProcStmt)
// Create the stored procedure
ABORT_CHECK(hr = CreateStoredProc(m_pICommandText, pwszProcName, pwszCreateProcStmt, fIsFunction), S_OK);
// Remove any old parameter information
FAIL_CHECK(m_pICmdWParams->SetParameterInfo(0, NULL, NULL), S_OK);
// Set the command text to execute the stored proc
ABORT_CHECK(hr = m_pICommandText->SetCommandText(DBGUID_DBSQL, pwszExecuteStmt), S_OK);
// Prepare if supported
if (m_pICommandPrepare)
{
CHECK(hr = m_pICommandPrepare->Prepare(1), S_OK);
if (FAILED(hr))
{
fResult = FALSE;
goto CLEANUP;
}
}
// Providers that can derive parameter information return DB_S_TYPEINFOOVERRIDDEN here
// Note that some providers can derive information for some types of statements but no
// others, so we check each time.
hr = m_pICmdWParams->SetParameterInfo(cParams, pParamOrdinals, pPARAMBIND);
// If the provider returns DB_S_TYPEINFOOVERRIDDEN then we assume he can derive.
// We also assume he can derive for no parameters at all, since it's a no-op.
if (hr == DB_S_TYPEINFOOVERRIDDEN || !cParams)
*pfCanDerive = TRUE;
else
{
*pfCanDerive = FALSE;
FAIL_CHECK(hr, S_OK);
}
// Now reset the parameter information again so we can let the provider derive (or fail)
FAIL_CHECK(m_pICmdWParams->SetParameterInfo(0, NULL, NULL), S_OK);
CLEANUP:
return fResult ? S_OK : E_FAIL;
}
HRESULT CICmdWParams::ValidateGetParameterInfo(ULONG cExpParams, ULONG cParamSets, DBCOUNTITEM ulRowNum, DB_UPARAMS * pExpOrdinals,
DBPARAMBINDINFO * pExpParamBind, DBBINDING * pBINDING, DBLENGTH cbRowSize, ParamStruct * pParamAll,
BYTE * pData, enum ROWSET_ENUM eRowset, DBORDINAL cColumns, DB_LORDINAL * rgColumns, BOOL fCanDerive,
enum VERIFY_ENUM eVerifyMethod, ICommand * pICommand)
{
HRESULT hrGet = E_FAIL;
HRESULT hrSet = S_OK;
HRESULT hr = E_FAIL;
DB_UPARAMS cParams = 0;
DBPARAMINFO * pParamInfo = NULL;
WCHAR * pNamesBuffer = NULL;
BOOL fResult = TRUE;
ICommandWithParameters * pICmdWParams = NULL;
HRESULT hrGetExpected = DB_E_PARAMUNAVAILABLE; // Assuming provider can't derive parameter info
BYTE * pDataCopy = NULL;
if (fCanDerive)
hrGetExpected = S_OK;
// Use the member command object if we didn't pass in one to use
if (!pICommand)
pICommand = m_pICommand;
// Get the Interface pointer for ICommandWithparameters Object.
ABORT_COMPARE(VerifyInterface(pICommand, IID_ICommandWithParameters,
COMMAND_INTERFACE,(IUnknown **)&pICmdWParams), TRUE);
// Some providers can derive parameter information for stored procs but not statements
// so this call could succeed even though we think parameter derivation isn't supported.
hrGet = pICmdWParams->GetParameterInfo(&cParams, &pParamInfo, &pNamesBuffer);
FAIL_CHECK(hrGet, hrGetExpected);
if (SUCCEEDED(hrGet))
{
// We were able to derive parameter information, see if it looks right
FAIL_VAR(VerifyParamInfo(cExpParams, pExpOrdinals, pExpParamBind,
cParams, pParamInfo, pNamesBuffer, 0, TRUE), S_OK);
// Free the buffers we got from GetParameterInfo
SAFE_FREE(pParamInfo);
SAFE_FREE(pNamesBuffer);
// Save the values in pData so we can replace them after output params are verified.
// For the case of I/O params we expect a second Execute to succeed using the pData
// we passed in, but the Output has overwritten the input values. Also, for non-I/O
// params this puts back the pData values that have the output param buffers NULL'd.
TEST_ALLOC(BYTE, pDataCopy, 0, (size_t)(cbRowSize*cParamSets*sizeof(BYTE)));
memcpy(pDataCopy, pData, (size_t)(cbRowSize*cParamSets*sizeof(BYTE)));
// The final proof is that we can execute with these values
FAIL_VAR(ExecuteAndVerify(cExpParams, cParamSets, pParamAll,ulRowNum, pBINDING, cbRowSize, pData, eRowset,
cColumns, rgColumns, eVerifyMethod, TRUE, pICommand), S_OK);
// Get it again so to make sure the bogus values got set
ABORT_CHECK(pICmdWParams->GetParameterInfo(&cParams, &pParamInfo, &pNamesBuffer), S_OK);
// Check the param info after execute
FAIL_VAR(VerifyParamInfo(cExpParams, pExpOrdinals, pExpParamBind,
cParams, pParamInfo, pNamesBuffer, 0, TRUE), S_OK);
// Put pData back the way it was for further Execute calls
if (eVerifyMethod != VERIFY_NONE)
memcpy(pData, pDataCopy, (size_t)(cbRowSize*cParamSets*sizeof(BYTE)));
PROVIDER_FREE(pDataCopy);
// Free the buffers we got from GetParameterInfo
PROVIDER_FREE(pParamInfo);
PROVIDER_FREE(pNamesBuffer);
// If there are parameters, then the provider will return DB_S_TYPEINFOOVERRIDDEN
// if we set param info.
if (cExpParams)
hrSet = DB_S_TYPEINFOOVERRIDDEN;
}
if (FAILED(hrGet))
{
odtLog << L"Warning: Provider was not able to derive parameter information.\n\n";
hrSet = S_OK;
}
SAFE_FREE(pParamInfo);
SAFE_FREE(pNamesBuffer);
if (cExpParams > 1)
{
HRESULT hr = E_FAIL;
// Set the parameter information to something different so we can verify we can over-ride
// Note some providers goof the DB_S_TYPEINFOOVERRIDDEN and instead return S_OK here.
hr = pICmdWParams->SetParameterInfo(1, &pExpOrdinals[0], &pExpParamBind[1]);
// For this success case we will supress duplicate failures
if (hrSet == DB_S_TYPEINFOOVERRIDDEN && hr == S_OK)
{
CCHECK(m_EC, hr, hrSet,
EC_UNEXPECTED_S_OK,
L"Provider returned S_OK rather than required DB_S_TYPEINFOOVERRIDDEN",
FALSE);
}
// Otherwise we will report a non-suppressed error
else
{
FAIL_CHECK(hr, hrSet);
}
// Get it again so to make sure the bogus values got set
ABORT_CHECK(pICmdWParams->GetParameterInfo(&cParams, &pParamInfo, &pNamesBuffer), S_OK);
// Verify the bogus information
// Note that due to spec change providers are allowed to return all parameters even though
// I set only one, so don't fail them for that.
if (COMPARE(cParams > 0, TRUE))
FAIL_VAR(VerifyParamInfo(1, &pExpOrdinals[0], &pExpParamBind[1],
1, pParamInfo, pNamesBuffer), S_OK);
if (cParams > 1)
FAIL_VAR(VerifyParamInfo(cExpParams-1, &pExpOrdinals[1], &pExpParamBind[1],
cParams-1, &pParamInfo[1], pNamesBuffer, 1), S_OK);
// Since we called SetParameterInfo once above this is now the expected return from any
// other SetParameterInfo calls.
hrSet = DB_S_TYPEINFOOVERRIDDEN;
// Free the buffers we got from GetParameterInfo
PROVIDER_FREE(pParamInfo);
PROVIDER_FREE(pNamesBuffer);
}
// Now set the parameter information correctly
ABORT_CHECK(pICmdWParams->SetParameterInfo(cExpParams, pExpOrdinals, pExpParamBind), hrSet);
// Get it again so we can validate we get back what we set.
ABORT_CHECK(pICmdWParams->GetParameterInfo(&cParams, &pParamInfo, &pNamesBuffer), S_OK);
// Verify results. If we didn't get back what was set it might not be a failure
FAIL_VAR(VerifyParamInfo(cExpParams, pExpOrdinals, pExpParamBind,
cParams, pParamInfo, pNamesBuffer), S_OK);
SAFE_FREE(pParamInfo);
SAFE_FREE(pNamesBuffer);
// The final proof is that we can execute with these values
FAIL_VAR(ExecuteAndVerify(cExpParams, cParamSets, pParamAll, ulRowNum, pBINDING, cbRowSize, pData, eRowset,
cColumns, rgColumns, eVerifyMethod, TRUE, pICommand), S_OK);
// Get it again so we can validate we get back what we set.
ABORT_CHECK(pICmdWParams->GetParameterInfo(&cParams, &pParamInfo, &pNamesBuffer), S_OK);
// Check the param info after execute
FAIL_VAR(VerifyParamInfo(cExpParams, pExpOrdinals, pExpParamBind,
cParams, pParamInfo, pNamesBuffer), S_OK);
CLEANUP:
SAFE_FREE(pParamInfo);
SAFE_FREE(pNamesBuffer);
SAFE_RELEASE(pICmdWParams);
return fResult ? S_OK : E_FAIL;
}
HRESULT CICmdWParams::VerifyOutParams(ULONG cParams, ULONG cParamSets, DBCOUNTITEM ulRowNum, DBBINDING * pBinding,
DBLENGTH cbRowSize, BYTE * pData, ParamStruct * pParamAll, enum VERIFY_ENUM eVerifyMethod, DBBINDING * pBindMatch,
BYTE * pDataMatch)
{
BOOL fResult = TRUE;
ULONG iParam, cOutParams = 0;
DBCOUNTITEM iRow;
DBORDINAL cCols = 0;
DB_LORDINAL * rgColumns = NULL;
DBBINDING * pOutBind = NULL;
// Currently don't support multiple paramsets on output params
// FAIL_COMPARE(cParamSets, 1);
// Now depending on the comparison method we verify the data
if (eVerifyMethod == VERIFY_USE_TABLE)
{
fResult = FALSE; // In case of allocation failure
// If allocation fails we'll return E_FAIL
TEST_ALLOC(DB_LORDINAL, rgColumns, 0, sizeof(DB_LORDINAL)*cParams);
TEST_ALLOC(DBBINDING, pOutBind, 0, sizeof(DBBINDING)*cParams);
// From now on assume success
fResult = TRUE;
// Go through the parameter mapping array and find all the output params
for (iParam = 0; iParam < cParams; iParam++)
{
if (pParamAll[iParam].eParamIO & DBPARAMIO_OUTPUT)
{
// It's an output param, copy the binding information
memcpy(&pOutBind[cOutParams], &pBinding[iParam], sizeof(DBBINDING));
// Copy the column map for CompareData
if (pBinding[iParam].iOrdinal > cCols)
cCols = pBinding[iParam].iOrdinal;
rgColumns[pBinding[iParam].iOrdinal-1]=pParamAll[iParam].ulColIndex;
cOutParams++;
}
}
// If there are no output params then we just return pass
if (!cOutParams)
goto CLEANUP;
// The output param rows match data in the table
for (iRow = ulRowNum; iRow < ulRowNum + cParamSets; iRow++)
{
FAIL_COMPARE(CompareData(
cCols,
rgColumns,
iRow,
pData+(iRow - ulRowNum)*cbRowSize,
cOutParams,
pOutBind,
m_pTable,
m_pIMalloc,
PRIMARY,
COMPARE_ONLY,
COMPARE_ALL,
TRUE
), TRUE);
}
}
else if (eVerifyMethod == VERIFY_USE_PDATA)
{
// By default we assume the second row of data in pData contains the
// expected values and the bindings are identical.
if (!pBindMatch)
pBindMatch = pBinding;
if (!pDataMatch)
pDataMatch = pData+cbRowSize;
// The output param rows match the second row in pdata
for (iParam = 0; iParam < cParams; iParam++)
{
BOOL fCompareData = FALSE;
DBSTATUS ulStatusOut = DBSTATUS_E_BADSTATUS;
DBSTATUS ulStatusExp = DBSTATUS_E_BADSTATUS;
DBLENGTH ulLengthOut = 0;
DBLENGTH ulLengthExp = 0;
BYTE * pValueOut = NULL;
BYTE * pValueExp = NULL;
// Extract status, length, and value
if (pBinding[iParam].dwPart & DBPART_STATUS)
ulStatusOut = *(DBSTATUS *)(pData+pBinding[iParam].obStatus);
if (pBindMatch[iParam].dwPart & DBPART_STATUS)
ulStatusExp = *(DBSTATUS *)(pDataMatch+pBindMatch[iParam].obStatus);
if (pBinding[iParam].dwPart & DBPART_LENGTH)
ulLengthOut = *(DBLENGTH *)(pData+pBinding[iParam].obLength);
if (pBindMatch[iParam].dwPart & DBPART_LENGTH)
ulLengthExp = *(DBLENGTH *)(pDataMatch+pBindMatch[iParam].obLength);
if (pBinding[iParam].dwPart & DBPART_VALUE)
pValueOut = pData+pBinding[iParam].obValue;
if (pBindMatch[iParam].dwPart & DBPART_VALUE)
pValueExp = pDataMatch+pBindMatch[iParam].obValue;
// First make sure the status matches if bound
if ((pBinding[iParam].dwPart & DBPART_STATUS) &&
(pBindMatch[iParam].dwPart & DBPART_STATUS))
FAIL_COMPARE(ulStatusOut, ulStatusExp);
// If either status is bound we can compare the data. If not, we'll fall back
// and use the eCompareOp to decide if it's NULL.
if ((pBinding[iParam].dwPart & DBPART_STATUS) ||
(pBindMatch[iParam].dwPart & DBPART_STATUS))
{
if (((pBinding[iParam].dwPart & DBPART_STATUS) && ulStatusOut == DBSTATUS_S_OK) ||
((pBindMatch[iParam].dwPart & DBPART_STATUS) && ulStatusExp == DBSTATUS_S_OK))
fCompareData = TRUE;
}
else if (pParamAll[iParam].eCompareOp != CP_ISNULL)
fCompareData = TRUE;
if (fCompareData)
{
// We can compare the length, but note length for input
// only params should be ignored.
if ((pBinding[iParam].dwPart & DBPART_LENGTH) &&
(pBindMatch[iParam].dwPart & DBPART_LENGTH) &&
pBinding[iParam].eParamIO & DBPARAMIO_OUTPUT)
FAIL_COMPARE(ulLengthOut, ulLengthExp);
// If the value part was bound then we should be able to compare it.
// If length wasn't bound then we can only compare fixed-length or char
// types.
if ((pBinding[iParam].dwPart & DBPART_VALUE) &&
(pBindMatch[iParam].dwPart & DBPART_VALUE))
{
DBLENGTH ulLength = 0;
DBTYPE wType = pBinding[iParam].wType;
// If we've got a BYREF binding then pValue is a pointer to the value,
// not the value itself.
if (wType & DBTYPE_BYREF)
{
wType &= ~DBTYPE_BYREF;
pValueOut = (BYTE *)*(LPVOID *)pValueOut;
}
if (pBindMatch[iParam].wType & DBTYPE_BYREF)
pValueExp = (BYTE *)*(LPVOID *)pValueExp;
if (pBindMatch[iParam].dwPart & DBPART_LENGTH)
ulLength = ulLengthExp;
else if (pBinding[iParam].dwPart & DBPART_LENGTH)
ulLength = ulLengthOut;
else if (IsFixedLength(pBinding[iParam].wType))
ulLength = GetDBTypeSize(pBinding[iParam].wType);
else if (pBinding[iParam].wType == DBTYPE_STR)
ulLength = strlen((CHAR *)pValueExp);
else if (pBinding[iParam].wType == DBTYPE_WSTR)
ulLength = wcslen((WCHAR *)pValueExp);
// If we know the length, compare the value. We should always know
// the length.
ASSERT(ulLength);
if (ulLength)
FAIL_COMPARE(RelativeCompare(pValueOut, pValueExp, wType, (USHORT)ulLength,
pBinding[iParam].bPrecision, pBinding[iParam].bScale, (ULONG)ulLength), 0);
}
}
}
}
else if (eVerifyMethod == VERIFY_NULL)
{
for (iParam = 0; iParam < cParams; iParam++)
{
if ((pBinding[iParam].eParamIO & DBPARAMIO_OUTPUT) && (pBinding[iParam].dwPart & DBPART_STATUS))
{
DBSTATUS ulStatusOut = *(DBSTATUS *)(pData+pBinding[iParam].obStatus);
COMPARE(ulStatusOut, DBSTATUS_S_ISNULL);
}
}
}
else if (eVerifyMethod == VERIFY_NONE)
NULL; // No verification
else
ASSERT(!L"Invalid verify method in VerifyOutParams.");
CLEANUP:
PROVIDER_FREE(rgColumns);
PROVIDER_FREE(pOutBind);
return fResult ? S_OK : E_FAIL;
}
HRESULT CICmdWParams::ExecuteAndVerify(ULONG cParams, ULONG cParamSets, ParamStruct * pParamAll,
DBCOUNTITEM ulRowNum, DBBINDING * pBINDING, DBLENGTH cbRowSize, BYTE * pData, enum ROWSET_ENUM eRowset,
DBORDINAL cColumns, DB_LORDINAL * rgColumns, enum VERIFY_ENUM eVerifyMethod, BOOL fRelease,
ICommand * pICommand, HRESULT hrExpected,DBBINDING * pBindMatch, BYTE * pDataMatch,
DBCOUNTITEM * pcRowsExpected)
{
BOOL fResult = TRUE;
HACCESSOR hParamAccessor = DB_INVALID_HACCESSOR;
DBPARAMS ExecDbParams;
DBPARAMS * pParams = NULL;
DBROWCOUNT cRowsAffected;
IRowset * pRowset = NULL;
IAccessor * pCmdIAccessor = NULL;
HRESULT hr = E_FAIL;
IRowset ** ppIRowset = &pRowset;
IID iid = m_iidExec;
// Null out DBPARAMS
memset(&ExecDbParams, 0, sizeof(DBPARAMS));
if (!g_fRowsetTest && eRowset == ROWSET_NONE)
{
ppIRowset = NULL;
iid = IID_NULL;
}
// Use the member command object if we didn't pass in one to use
if (!pICommand)
pICommand = m_pICommand;
if (cParams)
{
// Get the interface pointer for Accessor.
if (!VerifyInterface(pICommand, IID_IAccessor,
COMMAND_INTERFACE,(IUnknown **)&pCmdIAccessor))
{
goto CLEANUP;
}
// Now create the parameter accessor
ABORT_CHECK (pCmdIAccessor->CreateAccessor( DBACCESSOR_PARAMETERDATA,
cParams, pBINDING, cbRowSize, &hParamAccessor, NULL), S_OK);
// Set up our param accessor
ExecDbParams.hAccessor = hParamAccessor;
pParams = &ExecDbParams;
}
// The rest of these should be ignored
ExecDbParams.cParamSets = cParamSets;
ExecDbParams.pData = pData;
hr = pICommand->Execute(NULL, iid, pParams,
&cRowsAffected, (IUnknown **)ppIRowset);
if (hr != hrExpected && (hr == DB_E_ERRORSOCCURRED || hr == DB_S_ERRORSOCCURRED))
{
// Dump current command props
DumpCommandProps(pICommand, FALSE);
// Dump properties in error
DumpCommandProps(pICommand, TRUE);
}
// Make sure we got the result we expect, but don't fail a provider if they can execute
// and get the data we expect (bonus functionality)
if (FAILED(hr))
{
// If we failed, then exit. For success codes this will allow
// us to validate the data returned in case it's only one parameter failing.
CHECK(hr, hrExpected);
goto CLEANUP;
}
else
CHECK(hr, S_OK);
// Validate the rowset pointer returned
switch (eRowset)
{
case ROWSET_MAYBE:
if (!pRowset)
odtLog << L"Warning: This command did not produce a rowset. Some providers cannot produce rowsets here.\n\n";
break;
case ROWSET_NONE:
FAIL_COMPARE(pRowset, NULL);
break;
case ROWSET_ALWAYS:
FAIL_COMPARE(pRowset != NULL, TRUE);
break;
default:
ASSERT(!L"Unknown rowset enum value.");
}
// If the provider claims output params are available immediately after Execute verify them here
if (g_ulOutParamsSupported == DBPROPVAL_OA_ATEXECUTE)
FAIL_VAR(VerifyOutParams(cParams, cParamSets, ulRowNum, pBINDING, cbRowSize, pData, pParamAll,
eVerifyMethod, pBindMatch, pDataMatch), S_OK);
// If a rowset was returned validate the data
if (pRowset)
FAIL_VAR(VerifyObj(m_iidExec, pRowset, ulRowNum, cColumns, rgColumns, fRelease, FALSE,
NULL, pcRowsExpected), S_OK);
if (g_ulOutParamsSupported == DBPROPVAL_OA_ATROWRELEASE)
FAIL_VAR(VerifyOutParams(cParams, cParamSets, ulRowNum, pBINDING, cbRowSize, pData, pParamAll,
eVerifyMethod, pBindMatch, pDataMatch), S_OK);
CLEANUP:
if (hParamAccessor != DB_INVALID_HACCESSOR)
FAIL_CHECK(pCmdIAccessor->ReleaseAccessor(hParamAccessor, NULL), S_OK);
SAFE_RELEASE(pCmdIAccessor);
return fResult ? S_OK : E_FAIL;
}
HRESULT CICmdWParams::VerifyObj(REFIID riidRowset, IUnknown * pUnkRowset, DBCOUNTITEM ulStartingRow,
DBORDINAL cRowsetCols, DB_LORDINAL * rgTableColOrds, BOOL fRelease, BOOL fRestart, CTable * pTable,
DBCOUNTITEM * pcRows)
{
if (!pTable)
pTable = m_pTable;
if (riidRowset == IID_IRow)
return VerifyRowObj(riidRowset, pUnkRowset, ulStartingRow,
cRowsetCols, rgTableColOrds, fRelease, pTable);
else
return VerifyRowset(riidRowset, pUnkRowset, ulStartingRow,
cRowsetCols, rgTableColOrds, fRelease, fRestart, pTable,
pcRows);
}
HRESULT CICmdWParams::VerifyRowObj(REFIID riidRow, IUnknown * pUnkRow, DBCOUNTITEM ulStartingRow,
DBORDINAL cRowsetCols, DB_LORDINAL * rgTableColOrds, BOOL fRelease, CTable * pTable)
{
BOOL fResult = FALSE;
CRowObject* pCRow = NULL;
pCRow = new CRowObject();
TESTC(pCRow != NULL)
TESTC_(pCRow->SetRowObject(pUnkRow), S_OK)
//Verify the GetColumns method.
TESTC(pCRow->VerifyGetColumns(ulStartingRow, pTable, ALL_COLS_BOUND, BLOB_LONG, FORWARD,
NO_COLS_BY_REF, DBTYPE_EMPTY, (DBORDINAL)cRowsetCols, (DBORDINAL *)rgTableColOrds))
fResult = TRUE;
CLEANUP:
SAFE_DELETE(pCRow);
if (fRelease)
SAFE_RELEASE(pUnkRow);
return fResult ? S_OK : E_FAIL;
}
HRESULT CICmdWParams::VerifyRowset(REFIID riidRowset, IUnknown * pUnkRowset, DBCOUNTITEM ulStartingRow,
DBORDINAL cRowsetCols, DB_LORDINAL * rgTableColOrds, BOOL fRelease, BOOL fRestart, CTable * pTable,
DBCOUNTITEM * pcRows)
{
BOOL fResult = TRUE;
IRowset * pIRowset = NULL;
IAccessor * pIAccessor = NULL;
HRESULT hr = E_FAIL;
DBCOUNTITEM cRowsObtained=0;
HROW * prghRow = NULL;
HACCESSOR hAccessor = DB_INVALID_HACCESSOR;
DBBINDING * pBindings = NULL;
DBCOUNTITEM cBindings = 0;
DBLENGTH cbRowSize = 0;
BYTE * pData = NULL;
DBCOUNTITEM cRowsInRowset = 0;
// If we didn't get passed the mapping of rowset cols to table cols we can't compare data
// ABORT_COMPARE(cRowsetCols && rgTableColOrds && pUnkRowset, TRUE);
if (!pTable)
pTable = m_pTable;
// Make sure we can get a rowset interface
ABORT_COMPARE(VerifyInterface(pUnkRowset, IID_IRowset,
ROWSET_INTERFACE, (IUnknown **)&pIRowset), TRUE);
// If we were passed in a rowset interface, use the one passed in
if (riidRowset == IID_IRowset)
{
SAFE_RELEASE(pIRowset);
pIRowset = (IRowset *)pUnkRowset;
}
// Make sure we're at the top of the rowset
if (fRestart)
{
hr = pIRowset->RestartPosition(NULL);
if (hr != DB_S_COMMANDREEXECUTED)
ABORT_CHECK(hr, S_OK);
}
// Make sure we can get an accessor interface
ABORT_COMPARE(VerifyInterface(pUnkRowset, IID_IAccessor,
ROWSET_INTERFACE, (IUnknown **)&pIAccessor), TRUE);
ABORT_CHECK(GetAccessorAndBindings(pIAccessor, DBACCESSOR_ROWDATA,
&hAccessor, &pBindings, &cBindings, &cbRowSize,
DBPART_LENGTH | DBPART_STATUS | DBPART_VALUE,
ALL_COLS_BOUND, FORWARD, NO_COLS_BY_REF,
NULL, NULL, NULL, DBTYPE_EMPTY, 0, NULL, NULL,
NO_COLS_OWNED_BY_PROV, DBPARAMIO_NOTPARAM, BLOB_LONG), S_OK);
TEST_ALLOC(BYTE, pData, 0, (size_t)(cbRowSize * sizeof(BYTE)));
// TODO: Use IMultipleResults here if supported in case multiple results are returned??
while (S_OK == (hr = pIRowset->GetNextRows(NULL, 0, 1, &cRowsObtained, &prghRow)))
{
ABORT_COMPARE(cRowsObtained, 1);
cRowsInRowset+=cRowsObtained;
FAIL_CHECK(hr = pIRowset->GetData(*prghRow, hAccessor, pData), S_OK);
if (SUCCEEDED(hr))
{
FAIL_COMPARE(CompareData(
cRowsetCols,
rgTableColOrds,
ulStartingRow,
pData,
cBindings,
pBindings,
pTable,
m_pIMalloc,
PRIMARY,
COMPARE_ONLY,
COMPARE_ALL,
TRUE
), TRUE);
FAIL_CHECK(pIRowset->ReleaseRows(cRowsObtained, prghRow, NULL, NULL, NULL), S_OK);
ulStartingRow++;
}
else
// If this GetData failed it's doubtful further GetData's are of much use
goto CLEANUP;
}
FAIL_CHECK(hr, DB_S_ENDOFROWSET);
if (pcRows)
{
if (*pcRows)
FAIL_COMPARE(cRowsInRowset, *pcRows);
*pcRows = cRowsInRowset;
}
CLEANUP:
if (hAccessor != DB_INVALID_HACCESSOR)
FAIL_CHECK(pIAccessor->ReleaseAccessor(hAccessor, NULL), S_OK);
SAFE_RELEASE(pIAccessor);
// If we passed in a rowset pointer then it gets release below if requested
// otherwise release here.
if (riidRowset != IID_IRowset)
SAFE_RELEASE(pIRowset);
if (fRelease)
SAFE_RELEASE(pUnkRowset);
PROVIDER_FREE(pData);
PROVIDER_FREE(prghRow);
return fResult ? S_OK : E_FAIL;
}
//--------------------------------------------------------------------------
// CreateProcBindings
// Create a stored procedure and set up the member variables necessary
// for executing it
//
// TODO: add support for scalar functions.
// TODO: add support for other limit clauses besides "where" (select, etc.)
// TODO: add support for other comparison types ("in", "between",
// "like", "<", ">")
// TODO: Handle DBBINDFLAG_HTML?
//--------------------------------------------------------------------------
BOOL CICmdWParams::CreateProcBindings(
enum TOKEN_ENUM eProcType, // [IN] Proc type, regular proc or function (has return value)
BOOL fBindByName, // [IN] If TRUE then we add parameter names to the rgParamBindInfo
ULONG cParamSets, // [IN] Number of sets of parameters to be created
DBTYPE wReturnType, // [IN] Return parameter type
DBCOUNTITEM ulTableRow, // [IN] Row number in table or rowset to select, insert, or update
ULONG * pcParams, // [OUT] Count of params created
DBLENGTH * pcbRowSize, // [OUT] Count of bytes for a single row of parameters
DBBINDING ** ppDBBINDINFO, // [OUT] Binding array for CreateAccessor
DB_UPARAMS ** ppParamOrdinals, // [OUT] Array of cParams ordinals
DBPARAMBINDINFO ** ppDBPARAMBINDINFO,// [OUT] rgParamBindInfo for SetParameterInfo
WCHAR ** ppwszCreateStmt, // [OUT] SQL stmt to create the stored proc
WCHAR ** ppwszExecProcStmt, // [OUT] SQL stmt to execute the stored proc
WCHAR ** ppwszExecStmt, // [OUT] SQL stmt to execute without stored proc
WCHAR ** ppwszProcName, // [OUT] Name of stored proc created
BYTE ** ppData, // [OUT] Pointer to data for the parameters
ParamStruct ** ppParamAll, // [OUT] Pointer to array of param structs to save name, column
DBORDINAL * pcColumns, // [OUT] Column count for rowset returned by proc or exec
DB_LORDINAL ** prgColumnsOrd, // [OUT] Array of mappings of rowset columns to underlying table columns
DB_LORDINAL ** prgParamColOrd, // [OUT] Array of mappings of parameters to underlying table columns
ULONG ulCreateFlags // [IN] Flags used to control creation of parameters
)
{
ULONG iCol, cParams=0, cColList=0, cParamList=0, cLimit=0, iParam, iParamList=0,
iLimit=0, iParamRow, iDestParam, cFinalParams, iDefaultParam;
DBLENGTH cbRowSize=0, ulMaxDispSize=0;
DBORDINAL cCols;
CCol TempCol;
BOOL fReturn = FALSE;
enum TOKEN_ENUM eCreateProc, eExecStmt;
WCHAR * pwszSelect = NULL;
IRowset * pIRowset = NULL;
BYTE * pData = NULL;
DBCOUNTITEM cRowsObtained = 0;
DBROWCOUNT cRowsAffected = 0;
HROW * phRows = NULL;
HACCESSOR hAccessor;
LPWSTR pwszParamMarker = NULL;
ParamStruct * pColList=NULL;
ParamStruct * pParamList=NULL;
ParamStruct * pLimit=NULL;
ParamStruct * pReturn=NULL;
ParamStruct * pParamAll=NULL;
ParamStruct ReturnParamStruct;
DB_LORDINAL * pulColOrds=NULL;
if (pcParams)
*pcParams=0;
if (pcbRowSize)
*pcbRowSize=0;
if (ppDBBINDINFO)
*ppDBBINDINFO=NULL;
if (ppParamOrdinals)
*ppParamOrdinals=NULL;
if (ppDBPARAMBINDINFO)
*ppDBPARAMBINDINFO=NULL;
if (ppwszCreateStmt)
*ppwszCreateStmt=NULL;
if (ppwszExecProcStmt)
*ppwszExecProcStmt=NULL;
if (ppwszExecStmt)
*ppwszExecStmt=NULL;
if (ppData)
*ppData=NULL;
if (ppParamAll)
*ppParamAll=NULL;
// It's always an error to not include these output params
if (!pcParams || !pcbRowSize || !ppDBBINDINFO || !ppDBPARAMBINDINFO ||
!ppwszProcName || !ppParamAll || !ppParamOrdinals)
goto CLEANUP;
// Validate the proc type
switch(eProcType)
{
// We only understand this type right now
case T_EXEC_PROC_SELECT_NO_PARM:
case T_EXEC_PROC_SELECT_IN:
case T_EXEC_PROC_SELECT_OUT:
case T_EXEC_PROC_SELECT_OUT_DFLT:
case T_EXEC_PROC_SELECT_OUT_RET:
case T_EXEC_PROC_SELECT_INOUT:
case T_EXEC_PROC_SELECT_OUT_NULL:
case T_EXEC_PROC_INSERT_INPUT:
break;
default:
goto CLEANUP;
}
// It's an error to ask for multiple parameter sets on a select?
if ((eProcType == T_EXEC_PROC_SELECT_OUT_DFLT ||
eProcType == T_EXEC_PROC_SELECT_OUT_RET)
&& cParamSets > 1)
goto CLEANUP;
// If we passed in a proc name to use then set it for the syntax routine
m_Syntax.SetProcName(*ppwszProcName);
m_Syntax.SetCreateFlags(ulCreateFlags);
// If we have a return parameter increment the count of params immediately
// and tell the syntax object
if (bHasReturnParam(eProcType))
{
cParams++;
m_Syntax.SetReturnParam(TRUE);
}
else
m_Syntax.SetReturnParam(FALSE);
// Find out how many columns are in the table
cCols = m_pTable->CountColumnsOnTable();
// Allocate space for arrays of indexes mapping items to columns. We assume there will be no more
// than one entry per table column maximum
TEST_ALLOC(ParamStruct, pColList, 0, (size_t)(cCols*sizeof(ParamStruct)));
TEST_ALLOC(ParamStruct, pParamList, 0, (size_t)(cCols*sizeof(ParamStruct)));
TEST_ALLOC(ParamStruct, pLimit, 0, (size_t)(cCols*sizeof(ParamStruct)));
// Go through each column in the table and add to our lists
for (iCol=1; iCol<=cCols; iCol++)
{
// Get the information about the column
if (!CHECK(m_pTable->GetColInfo(iCol, TempCol), S_OK))
goto CLEANUP;
// If the proc needs a limit clause and the column is useful in a limit clause add it
if (m_Syntax.bNeedsLimitClause(eProcType) && m_Syntax.bAddColumn(T_SEARCHABLE_COL_EQ, TempCol))
{
WCHAR wszDataVal[SP_TEXT_BLOCK_SIZE] = L"\0";
// By default the limit clause has param names starting with L so they
// don't conflict with any output params.
WCHAR wchFirst = L'L';
if (eProcType == T_EXEC_PROC_SELECT_INOUT ||
eProcType == T_EXEC_PROC_SELECT_INOUT_RET)
// For these procs the param names must match
wchFirst = L'P';
// Record the max display size for later use if binding to WCHAR
ulMaxDispSize = max(DisplaySize(TempCol), ulMaxDispSize);
// Create a param name for the limit clause
if (!(pLimit[cLimit].pwszParamName = m_Syntax.MakeParamName(TempCol, wchFirst)))
goto CLEANUP;
// Record the column index and I/O for this limit parameter
pLimit[cLimit].eParamIO=DBPARAMIO_INPUT;
pLimit[cLimit].ulColIndex=iCol;
cLimit++;
}
// Now we build the column list and parameter lists. Either may be empty depending on the stmt type
// For updates or inserts we want only the updatable columns, for output params we can't have LONG cols.
if (m_Syntax.bAddColumn(eProcType, TempCol))
{
// We need a column that matches the return type requested. This parameter doesn't have
// a naturally associated column, but we can pick one to use, otherwise we can't match
// the data.
if (bHasReturnParam(eProcType) && !pReturn && wReturnType == TempCol.GetProviderType())
{
ReturnParamStruct.ulColIndex=iCol;
ReturnParamStruct.eParamIO=DBPARAMIO_OUTPUT;
// Note the provider type name is populated later
pReturn = &ReturnParamStruct;
/*
// Return parameter name will start with 'R'
if (!(pReturn[0].pwszParamName = m_Syntax.MakeParamName(TempCol, 'R')))
goto CLEANUP;
*/
// Due to provider-specific behavior here we will simply look up the return param
// name to use
if (!(pReturn[0].pwszParamName = m_Syntax.GetSyntax(T_RET_NAME)))
goto CLEANUP;
}
// Other parameter names will start with 'P'
if (!(pParamList[cParamList].pwszParamName = m_Syntax.MakeParamName(TempCol, 'P')))
goto CLEANUP;
// Record the column index and I/O
pParamList[cParamList].ulColIndex=iCol;
// Depending on the type of proc we're creating set the I/O
switch (eProcType)
{
case T_EXEC_PROC_SELECT_OUT:
case T_EXEC_PROC_SELECT_OUT_DFLT:
case T_EXEC_PROC_SELECT_OUT_NULL:
case T_EXEC_PROC_SELECT_OUT_RET:
pParamList[cParamList].eParamIO=DBPARAMIO_OUTPUT;
break;
case T_EXEC_PROC_SELECT_INOUT:
case T_EXEC_PROC_SELECT_INOUT_RET:
pParamList[cParamList].eParamIO=DBPARAMIO_OUTPUT | DBPARAMIO_INPUT;
break;
default:
pParamList[cParamList].eParamIO=DBPARAMIO_INPUT;
}
cParamList++;
}
} // for (icol = 1; ...
// Now that we've gone through all the columns we have generated
// 1) The limit clause parameters
// 2) The output parameter list
// 3) The return parameter info if needed
// For IN/OUT params we want to just discard the limit clause infomation since it should match
// the output params
if (eProcType == T_EXEC_PROC_SELECT_INOUT ||
eProcType == T_EXEC_PROC_SELECT_INOUT_RET)
cLimit = 0;
// The total number of parameters is the number in the parameter list plus the limit clause, plus any return
// parameter
cParams+=cParamList+cLimit;
// Allocate space for DBBINDINFO and DBPARAMBINDINFO
TEST_ALLOC(DBBINDING, *ppDBBINDINFO, 0, cParams*sizeof(DBBINDING));
TEST_ALLOC(DBPARAMBINDINFO, *ppDBPARAMBINDINFO, 0, cParams*sizeof(DBPARAMBINDINFO));
TEST_ALLOC(ParamStruct, pParamAll, 0, cParams*sizeof(ParamStruct));
TEST_ALLOC(DB_LORDINAL, pulColOrds,0, cParams*sizeof(DB_LORDINAL));
TEST_ALLOC(DB_UPARAMS, *ppParamOrdinals, 0, cParams*sizeof(DB_UPARAMS));
// Now go through each parameter and build the DBBINDING and DBPARAMBINDINFO
for (iParam=0; iParam < cParams; iParam++)
{
(*ppParamOrdinals)[iParam] = iParam+1;
// The first parameter is the return parameter if it exists.
if (bHasReturnParam(eProcType) && !iParam)
{
// It's a programming error to not populate pReturn at this point.
ASSERT (pReturn);
if (!pReturn)
goto CLEANUP;
if (!AddParam(iParam, pReturn->ulColIndex, pReturn->eParamIO, pReturn->pwszParamName, fBindByName,
&cbRowSize, *ppDBBINDINFO, *ppDBPARAMBINDINFO, pParamAll, m_pTable, TRUE))
goto CLEANUP;
pulColOrds[iParam]=pReturn->ulColIndex;
}
// The next cParamList parameters are the statement parameters
else if (cParamList)
{
if (!AddParam(iParam, pParamList[iParamList].ulColIndex, pParamList[iParamList].eParamIO,
pParamList[iParamList].pwszParamName, fBindByName, &cbRowSize, *ppDBBINDINFO, *ppDBPARAMBINDINFO, pParamAll, m_pTable))
goto CLEANUP;
pulColOrds[iParam]=pParamList[iParamList++].ulColIndex;
cParamList--;
}
// The last cLimit parameters are for the limit clause
else if (cLimit)
{
if (!AddParam(iParam, pLimit[iLimit].ulColIndex, pLimit[iLimit].eParamIO,
pLimit[iLimit].pwszParamName, fBindByName, &cbRowSize, *ppDBBINDINFO, *ppDBPARAMBINDINFO, pParamAll, m_pTable))
goto CLEANUP;
pulColOrds[iParam]=pLimit[iLimit++].ulColIndex;
cLimit--;
}
}
// If creating the indirect select to test in/out params we need to set the comparison
// operator to use in the where clause to select the proper row. By selecting DISTINCT
// using an order by clause we assume we can end up with the first row less than the second
// row such that with a select with second row data values as inputs we can retrieve the first
// row. That way our output params hopefully have different values than the input params.
// This will be absolutely true using automaketable without NULLs, but may not be true if
// using an ini file or if NULLS are included.
if (eProcType == T_EXEC_PROC_SELECT_INOUT)
{
BOOL fSingletonSelect = FALSE;
BYTE * pPrev = NULL;
DBLENGTH cbPrev = 0;
DBSTATUS ulStPrev = DBSTATUS_E_BADSTATUS;
// Get the syntax for doing the same select without params
TEST_COMPARE(((pwszSelect = m_Syntax.GetSyntax(T_SELECT_UNIQUE_ALL)) != NULL), TRUE);
TEST_CHECK(m_pICommandText->SetCommandText(DBGUID_DBSQL, pwszSelect), S_OK);
TEST_CHECK(m_pCmdIAccessor->CreateAccessor(DBACCESSOR_ROWDATA, cParams, *ppDBBINDINFO,
cbRowSize, &hAccessor, NULL), S_OK);
TEST_CHECK(m_pICommand->Execute(NULL, IID_IRowset,
NULL, &cRowsAffected, (IUnknown **)&pIRowset), S_OK);
// Allocate enough memory to hold first three rows
TEST_ALLOC(BYTE, pData, 0, (size_t)(3 * cbRowSize));
// Row number 0 is illegal
TEST_COMPARE(ulTableRow > 0, TRUE);
// Retrieve the row handles. For the first row desired we retrieve rows 1-3 and ignore row 3.
// We use rows 1 & 2 only.
// If row desired > 1 then we retrieve the row before and after also. The row before is used to
// decide if the select will return too many rows.
TEST_CHECK(pIRowset->GetNextRows(NULL, (ulTableRow == 1) ? ulTableRow-1 : ulTableRow-2, 3, &cRowsObtained, &phRows), S_OK);
// Make sure we got 3 rows
TEST_COMPARE(cRowsObtained, 3);
// Retrieve the data from the first three rows
// If everything goes right the row at pData+cbRowSize will match the output
// params, while the row at pData will be the input/output params.
// We use the row at pData + 2 * cbRowSize when ulTableRow is > 1 to verify the select
// will only retrieve one row.
if (ulTableRow == 1)
{
// We want to retrieve the first row, so we use row 2 data in the input params
// The third row is useless to us.
TEST_CHECK(pIRowset->GetData(phRows[0], hAccessor, pData+cbRowSize), S_OK); // Row 1 data for validation
TEST_CHECK(pIRowset->GetData(phRows[1], hAccessor, pData), S_OK); // Input/output params (row 2)
TEST_CHECK(pIRowset->GetData(phRows[2], hAccessor, pData+2*cbRowSize), S_OK); // Ignored
fSingletonSelect = TRUE;
}
else
{
TEST_CHECK(pIRowset->GetData(phRows[1], hAccessor, pData+cbRowSize), S_OK); // Row N data for validation
TEST_CHECK(pIRowset->GetData(phRows[2], hAccessor, pData), S_OK); // Input/output params (row N+1)
TEST_CHECK(pIRowset->GetData(phRows[0], hAccessor, pData+2*cbRowSize), S_OK); // Row N-1
}
// Release the rows and rowset now that we've got the data
CHECK(pIRowset->ReleaseRows(3, phRows, NULL, NULL, NULL), S_OK);
// Our input/output parameter data will be the first row in pData.
if (ppData)
*ppData = pData;
// For each column (parameter) in the result set compare the first row's
// data with the second and set the where operator appropriately (<, = , >).
for (iParam=0; iParam < cParams; iParam++)
{
LONG lCompResult;
BYTE * pFirst, * pSecond;
DBLENGTH cbFirst, cbSecond;
DBSTATUS ulStFirst, ulStSecond;
DBLENGTH * pLengthFirst = (DBLENGTH *)(pData+cbRowSize+(*ppDBBINDINFO)[iParam].obLength);
DBLENGTH * pLengthSecond = (DBLENGTH *)(pData+(*ppDBBINDINFO)[iParam].obLength);
// For STR and WSTR fields, since they were retrieved by GetData, the
// length value includes the null terminator. We want to use this for
// sending parameter data so we can't include the null terminator.
// Oops, no the spec says null terminator is not included in the length
/*
if ((*ppDBBINDINFO)[iParam].wType == DBTYPE_STR)
(*pLengthFirst)--;
if ((*ppDBBINDINFO)[iParam].wType == DBTYPE_WSTR)
(*pLengthFirst)-=sizeof(WCHAR);
*/
pFirst = pData+cbRowSize+(*ppDBBINDINFO)[iParam].obValue;
pSecond = pData+(*ppDBBINDINFO)[iParam].obValue;
cbFirst = *pLengthFirst;
cbSecond = *pLengthSecond;
ulStFirst = *(DBSTATUS *)(pData+cbRowSize+(*ppDBBINDINFO)[iParam].obStatus);
ulStSecond = *(DBSTATUS *)(pData+(*ppDBBINDINFO)[iParam].obStatus);
// Get the previous row info (only used for ulTableRow > 1)
pPrev = pData+2*cbRowSize+(*ppDBBINDINFO)[iParam].obValue;
ulStPrev = *(DBSTATUS *)(pData+2*cbRowSize+(*ppDBBINDINFO)[iParam].obStatus);
cbPrev = *(DBLENGTH *)(pData+2*cbRowSize+(*ppDBBINDINFO)[iParam].obLength);
// Either of the fields may be NULL.
if (ulStFirst == DBSTATUS_S_ISNULL ||
ulStSecond == DBSTATUS_S_ISNULL ||
RELCMP_NULL_VARIANT == (lCompResult = RelativeCompare(pFirst, pSecond, (*ppDBBINDINFO)[iParam].wType, (USHORT)cbSecond,
(*ppDBBINDINFO)[iParam].bPrecision, (*ppDBBINDINFO)[iParam].bScale, (ULONG)cbFirst)))
{
// If either is NULL we can't verify input/output param because we
// have to use the IS NULL construct instead of a parameter.
odtLog << L"Couldn't verify input/output for parameter: " <<
pParamAll[iParam].pwszParamName << L" because one entry was NULL.\n";
// If the second row is NULL and the first isn't we can use IS NOT NULL construct
// to retrieve first row.
if (ulStSecond == DBSTATUS_S_ISNULL)
pParamAll[iParam].eCompareOp = CP_ISNOTNULL;
// If the first row is actually NULL then we must use IS NULL to retrieve,
// nothing else is possible.
if (ulStFirst == DBSTATUS_S_ISNULL)
pParamAll[iParam].eCompareOp = CP_ISNULL;
(*ppDBBINDINFO)[iParam].eParamIO &= ~DBPARAMIO_INPUT;
pParamAll[iParam].eParamIO &= ~DBPARAMIO_INPUT;
(*ppDBPARAMBINDINFO)[iParam].dwFlags &= ~DBPARAMFLAGS_ISINPUT;
}
else
{
// Hack for Oracle DBMS. Oracle uses a binary comparison, not a linguistic comparison,
// so need to revert to wcscmp/strcmp for that case.
if (g_bOracle)
{
switch((*ppDBBINDINFO)[iParam].wType)
{
case DBTYPE_STR:
lCompResult = strcmp((LPSTR)pFirst, (LPSTR)pSecond);
break;
case DBTYPE_WSTR:
lCompResult = wcscmp((LPWSTR)pFirst, (LPWSTR)pSecond);
break;
}
}
// Neither field was NULL
switch(lCompResult)
{
case 1:
pParamAll[iParam].eCompareOp = CP_GT;
break;
case -1:
pParamAll[iParam].eCompareOp = CP_LT;
break;
default:
// '=' is the default, however, we didn't verify i/o param if =.
odtLog << L"Couldn't verify input/output for parameter: " <<
pParamAll[iParam].pwszParamName << L" because the values were equal.\n";
}
}
// If requesting a row > 1 make sure we have a singleton select
if (!fSingletonSelect)
{
if (ulStFirst != ulStPrev)
fSingletonSelect = TRUE;
else
{
if (ulStFirst == DBSTATUS_S_OK && lCompResult != RelativeCompare(pPrev, pSecond,
(*ppDBBINDINFO)[iParam].wType, (USHORT)cbPrev, (*ppDBBINDINFO)[iParam].bPrecision,
(*ppDBBINDINFO)[iParam].bScale, (ULONG)cbFirst))
fSingletonSelect = TRUE;
}
}
}
// We must have a singleton select for this case. If we're asking for the first
// row then this is guaranteed due to the "select distinct"
TEST_COMPARE(fSingletonSelect, TRUE);
}
// Fill the parameter information for each row if requested.
// Note it's very important to fill the bindings before the syntax is created,
// since for NULL values the syntax is different (IS NULL must be used).
else if (ppData)
{
TEST_ALLOC(BYTE, *ppData, 0, (size_t)(cbRowSize*cParamSets));
for (iParamRow=0; iParamRow < cParamSets; iParamRow++)
{
BYTE * pData=(*ppData+cbRowSize*iParamRow);
TEST_CHECK (FillInputBindings(m_pTable, DBACCESSOR_PARAMETERDATA, cParams,
*ppDBBINDINFO, &pData, ulTableRow+iParamRow, cParams, pulColOrds, PRIMARY), S_OK);
// For output only bindings we want to zero out the buffer so we verify if it's been used
for (iParam=0; iParam < cParams; iParam++)
{
DBSTATUS * pParamSt = (DBSTATUS *)(pData+(*ppDBBINDINFO)[iParam].obStatus);
// If the data for the parameter will be NULL then in order to select it
// we need to use IS NULL in the where clause
if ((*ppDBBINDINFO)[iParam].eParamIO == DBPARAMIO_INPUT &&
*pParamSt == DBSTATUS_S_ISNULL &&
eProcType != T_EXEC_PROC_INSERT_INPUT) // For insert/updates statements we need to leave as-is
{
pParamAll[iParam].eCompareOp = CP_ISNULL;
(*ppDBBINDINFO)[iParam].eParamIO &= ~DBPARAMIO_INPUT;
pParamAll[iParam].eParamIO &= ~DBPARAMIO_INPUT;
(*ppDBPARAMBINDINFO)[iParam].dwFlags &= ~DBPARAMFLAGS_ISINPUT;
}
// For output params, set initial values to bogus.
if ((*ppDBBINDINFO)[iParam].eParamIO == DBPARAMIO_OUTPUT)
{
BYTE * pParamBuf = pData+(*ppDBBINDINFO)[iParam].obValue;
DBLENGTH ulParamLen = (*ppDBBINDINFO)[iParam].cbMaxLen;
memset(pParamBuf, 0xCA, (size_t)ulParamLen);
STATUS_BINDING((*ppDBBINDINFO)[iParam], pData) = OUT_PARAM_STATUS_INVALID;
LENGTH_BINDING((*ppDBBINDINFO)[iParam], pData) = OUT_PARAM_LENGTH_INVALID;
}
}
}
}
// Give the parameter struct info to the syntax lookups
m_Syntax.SetColMap(cParams, pParamAll);
m_Syntax.SetCurrentRow(ulTableRow);
m_Syntax.SetProcType(eProcType);
switch (eProcType)
{
case T_EXEC_PROC_SELECT_OUT:
eCreateProc = T_CREATE_PROC_SELECT_OUT;
eExecStmt = T_SELECT_OUT_MARKER;
break;
case T_EXEC_PROC_SELECT_OUT_DFLT:
eCreateProc = T_CREATE_PROC_SELECT_OUT_DFLT;
eExecStmt = T_NONE;
break;
case T_EXEC_PROC_SELECT_OUT_RET:
eCreateProc = T_CREATE_PROC_SELECT_OUT_RET;
eExecStmt = T_NONE;
break;
case T_EXEC_PROC_SELECT_IN:
eCreateProc = T_CREATE_PROC_SELECT_IN;
eExecStmt = T_SELECT_IN_MARKER;
break;
case T_EXEC_PROC_SELECT_NO_PARM:
eCreateProc = T_CREATE_PROC_SELECT_NO_PARM;
eExecStmt = T_SELECT_NO_PARM;
break;
case T_EXEC_PROC_SELECT_IN_RET:
eCreateProc = T_CREATE_PROC_SELECT_IN_RET;
eExecStmt = T_NONE;
break;
case T_EXEC_PROC_SELECT_INOUT:
eCreateProc = T_CREATE_PROC_SELECT_INOUT;
eExecStmt = T_NONE;
break;
case T_EXEC_PROC_SELECT_OUT_NULL:
eCreateProc = T_CREATE_PROC_SELECT_OUT_NULL;
eExecStmt = T_SELECT_OUT_NULL_MARKER;
break;
case T_EXEC_PROC_INSERT_INPUT:
eCreateProc = T_CREATE_PROC_INSERT_IN;
eExecStmt = T_INSERT_IN_MARKER;
break;
}
// Get the statement required to create the proc
if (ppwszCreateStmt && !(*ppwszCreateStmt = m_Syntax.GetSyntax(eCreateProc, pcColumns, prgColumnsOrd)))
goto CLEANUP;
// Get the statement required to execute the proc
if (ppwszExecProcStmt && !(*ppwszExecProcStmt = m_Syntax.GetSyntax(eProcType)))
goto CLEANUP;
// Get the statement required to execute the statement with parameter markers
if (ppwszExecStmt && eExecStmt != T_NONE && !(*ppwszExecStmt = m_Syntax.GetSyntax(eExecStmt)))
goto CLEANUP;
// Get the proc name
if (!*ppwszProcName && !(*ppwszProcName = m_Syntax.GetSyntax(T_PROC_NAME)))
goto CLEANUP;
// Can't strip out any ISNULL or ISNOTNULL params for an insert sproc
if (eProcType != T_EXEC_PROC_INSERT_INPUT) // For insert/updates statements we need to leave as-is
{
// Go back through the binding information and remove any that are using IS NULL or IS NOT NULL
// syntax for input params. They're not really input parameters. Also, when using a default
// param we remove the first input parameter from the final bindings.
cFinalParams = cParams;
iDestParam = 0;
iDefaultParam = ULONG_MAX;
for(iParam=0; iParam < cParams; iParam++)
{
if ((pParamAll[iParam].eCompareOp == CP_ISNULL ||
pParamAll[iParam].eCompareOp == CP_ISNOTNULL) ||
(eProcType == T_EXEC_PROC_SELECT_OUT_DFLT &&
iDefaultParam == ULONG_MAX))
{
if (!(pParamAll[iParam].eParamIO & DBPARAMIO_OUTPUT))
{
// It's not an output param, so we can remove it entirely
cFinalParams--;
// Can't adjust cbRowSize because we filled with all params and offsets.
// cbRowSize-=(*ppDBBINDINFO)[iParam].cbMaxLen;
PROVIDER_FREE(pParamAll[iParam].pwszParamName);
if (pParamAll[iParam].eCompareOp != CP_ISNULL &&
pParamAll[iParam].eCompareOp != CP_ISNOTNULL)
iDefaultParam = iParam;
}
else
iDestParam++;
}
else
{
if (iParam > iDestParam)
{
memcpy(&pParamAll[iDestParam], &pParamAll[iParam], sizeof(ParamStruct));
memcpy(&(*ppDBBINDINFO)[iDestParam], &(*ppDBBINDINFO)[iParam], sizeof(DBBINDING));
memcpy(&(*ppParamOrdinals)[iDestParam], &(*ppParamOrdinals)[iParam], sizeof(DB_UPARAMS));
memcpy(&pulColOrds[iDestParam], &pulColOrds[iParam], sizeof(DB_LORDINAL));
memcpy(&(*ppDBPARAMBINDINFO)[iDestParam], &(*ppDBPARAMBINDINFO)[iParam], sizeof(DBPARAMBINDINFO));
(*ppDBPARAMBINDINFO)[iDestParam].pwszDataSourceType = pParamAll[iDestParam].wszDataSourceType;
(*ppParamOrdinals)[iDestParam] = iDestParam+1;
(*ppDBBINDINFO)[iDestParam].iOrdinal = iDestParam+1;
}
iDestParam++;
}
}
}
// Set the output parameters if not set yet
pwszParamMarker = m_Syntax.GetSyntax(T_PARM_MARKER);
*pcParams=wcschcount(*ppwszExecProcStmt, *pwszParamMarker); // cFinalParams;
*pcbRowSize=cbRowSize;
*ppParamAll=pParamAll;
if (prgParamColOrd)
*prgParamColOrd = pulColOrds;
fReturn = TRUE;
CLEANUP:
if (pIRowset)
pIRowset->ReleaseRows(2, phRows, NULL, NULL, NULL);
SAFE_RELEASE(pIRowset);
PROVIDER_FREE(phRows);
PROVIDER_FREE(pColList);
PROVIDER_FREE(pParamList);
PROVIDER_FREE(pLimit);
if (!prgParamColOrd)
PROVIDER_FREE(pulColOrds);
PROVIDER_FREE(pwszSelect);
PROVIDER_FREE(pwszParamMarker);
return fReturn;
}
//--------------------------------------------------------------------------
// @mfunc Drop stored procedure
// @desc Drop the stored procedure created so that a new one can be added.
//
//--------------------------------------------------------------------------
BOOL
CICmdWParams::DropStoredProcedure(ICommandText *pICommandText, WCHAR * pwszProcedureName, BOOL fIsFunction)
{
WCHAR * pwszDropProc = NULL;
WCHAR * pwszDropString=NULL;
BOOL fSuccess = FALSE;
ULONG i=0;
enum TOKEN_ENUM eDropType = T_DROP;
// First free the rowset if exists.
if (m_pIStoredProcRowset)
{
m_pIStoredProcRowset->Release();
m_pIStoredProcRowset = NULL;
}
// Retrieve syntax for dropping stored proc
if (fIsFunction)
eDropType = T_DROP_FUN;
if (!(pwszDropProc = m_Syntax.GetSyntax(eDropType)))
goto CLEANUP;
// If we are passed a stored procedure name use it.
if (pwszProcedureName)
FormatString(&pwszDropString, pwszDropProc, 1, pwszProcedureName);
else
FormatString(&pwszDropString, pwszDropProc, 1, g_pwszProcedureName2);
pICommandText->SetCommandText(DBGUID_DBSQL , pwszDropString);
// If it exists drop it. No need to check return code.
pICommandText->Execute(NULL, IID_NULL, NULL, NULL, NULL);
if (m_hStoredProcAccessor != DB_NULL_HACCESSOR)
{
m_pCmdIAccessor->ReleaseAccessor (m_hStoredProcAccessor, NULL);
m_hStoredProcAccessor = DB_NULL_HACCESSOR;
}
for (i =0; i < m_cStoredProcParamColMap;i++)
{
// This was allocated by wcsDuplicate in privlib.
if (m_rgwszStoredProcDataSourceTypes[i])
PROVIDER_FREE(m_rgwszStoredProcDataSourceTypes[i]);
}
fSuccess = TRUE;
CLEANUP:
FREE_DATA (m_rgStoredProcColInfo);
FREE_DATA(m_rgStoredProcBindings);
FREE_DATA (m_rgStoredProcParamColMap);
FREE_DATA(m_rgStoredProcParamOrdinals);
FREE_DATA(m_rgStoredProcParamBindInfo);
FREE_DATA(m_rgwszStoredProcDataSourceTypes);
PROVIDER_FREE(pwszDropProc);
PROVIDER_FREE(pwszDropString);
// Reset the counts.
m_cStoredProcParamColMap = 0;
m_cStoredProcBindings = 0;
return fSuccess;
}
//--------------------------------------------------------------------------
// @mfunc ExecuteStoredProcedure
// @desc Execute the stored procedure for which text has already been set.
// generate the required data and execute the stored procedure.
//
//--------------------------------------------------------------------------
HRESULT
CICmdWParams::ExecuteStoredProcedure(ICommandText *pICommandText, DBPARAMS *pDbParams, DBCOUNTITEM ulRowNum, ULONG DataValues, ULONG BindingType)
{
BYTE *pData = NULL;
HRESULT hr = S_OK;
ULONG i = 0;
if (m_pIStoredProcRowset) ReleaseRowsetPtr(&m_pIStoredProcRowset);
m_cStoredProcRowsAffected = 0;
// Allocate pData.
pData = (BYTE *)m_pIMalloc->Alloc(m_cbStoredProcRowSize);
if (!pData)
{
return E_OUTOFMEMORY;
}
pDbParams->pData = pData;
// Fill input bindings.
if (!CHECK((hr = FillInputBindings(m_pTable, DBACCESSOR_PARAMETERDATA, m_cStoredProcBindings, m_rgStoredProcBindings,
(BYTE **)&pData, (ulRowNum-1), m_cStoredProcParamColMap, m_rgStoredProcParamColMap)), S_OK))
{
return hr;
}
if (DataValues == NULL_IN_VALID_OUT_DATA || BindingType == NULL_STATUS_ONLY)
{
// Setup the status bit as required.
for (i = 0; i < m_cStoredProcBindings; i++)
{
// Check to see if status was bound.
if (m_rgStoredProcBindings[i].dwPart & DBPART_STATUS)
{
*((DBSTATUS *)((BYTE *)pData + m_rgStoredProcBindings[i].obStatus)) = DBSTATUS_S_ISNULL;
}
else
{
// since we are expecting status to be bound return E_FAIL.
return E_FAIL;
}
}
}
if (BindingType == PASS_BYREF)
{
COMPARE (StorePassByRefPointers(pDbParams->cParamSets, pData), TRUE);
}
return pICommandText->Execute(NULL, IID_IRowset, pDbParams, &m_cStoredProcRowsAffected,
(IUnknown **)&m_pIStoredProcRowset);
}
BOOL
CICmdWParams::StorePassByRefPointers(DB_UPARAMS cRows, BYTE * pData)
{
ULONG i=0;
if (cRows > 1)
{
odtLog << L"Function to be updated for multiple parameter sets\n";
COMPARE(0, 1);
return FALSE;
}
// Allocate storage for atleast m_cStoredProcBindings
m_rgvpByRefPointers = (void **)m_pIMalloc->Alloc(m_cStoredProcBindings * sizeof (void *));
if (!m_rgvpByRefPointers)
{
odtLog << wszMemoryAllocationError;
return FALSE;
}
// This function
for (i = 0; i < m_cStoredProcBindings; i++)
{
// No need to worry about fixed length columns.
// Null out the pointer so that we know.
if (IsFixedLength(m_rgStoredProcBindings[i].wType))
{
// Now get the pointer and store it.
m_rgvpByRefPointers[i] = (void *)NULL;
}
else
{
m_rgvpByRefPointers[i] = (*(void **)(pData + m_rgStoredProcBindings[i].obValue));
}
}
return TRUE;
}
void
CICmdWParams::ReleaseInputBindingsMemoryByRef(ULONG cRows, BYTE * pData, BOOL fFreeProviderMemory)
{
ULONG i = 0;
void *ptr = NULL;
if (cRows > 1)
{
odtLog << L"Function to be updated for multiple parameter sets\n";
COMPARE(0, 1);
return;
}
if (!pData)
{
COMPARE(0, 1);
odtLog << L"Null pData to free\n";
}
for (i = 0; i < m_cStoredProcBindings; i++)
{
if (IsFixedLength(m_rgStoredProcBindings[i].wType))
continue;
//Rest we have to free.
if (m_rgvpByRefPointers[i])
m_pIMalloc->Free (m_rgvpByRefPointers[i]);
// If the data is valid, then we need to free it
if (*(DBSTATUS *)(pData + m_rgStoredProcBindings[i].obStatus) == DBSTATUS_S_OK)
{
ptr = (void *)(*(void **)(pData + m_rgStoredProcBindings[i].obValue));
// Now free the provider's memory.
if (fFreeProviderMemory)
m_pIMalloc->Free(ptr);
}
}
// Now we have to free pData.
FREE_DATA (pData);
FREE_DATA (m_rgvpByRefPointers);
}
//--------------------------------------------------------------------------
// @mfunc SwapOrdinal
// @desc Utility function to swap ordinal information for parameters
//--------------------------------------------------------------------------
void CICmdWParams::SwapOrdinal(ULONG ulBindIndex)
{
ULONG ulSwapIndex=m_cStoredProcBindings-ulBindIndex-1;
ASSERT(m_cStoredProcBindings > ulBindIndex);
if(ulBindIndex < ulSwapIndex)
{
// Swap the binding values for this and corresponding opposite parameter
DBORDINAL ulOrdinal;
ulOrdinal=m_rgStoredProcBindings[ulBindIndex].iOrdinal;
m_rgStoredProcBindings[ulBindIndex].iOrdinal=m_rgStoredProcBindings[ulSwapIndex].iOrdinal;
m_rgStoredProcBindings[ulSwapIndex].iOrdinal=ulOrdinal;
}
}
//--------------------------------------------------------------------------
// @mfunc ReverseArray
// @desc Utility function to reverse the contents of an array
//--------------------------------------------------------------------------
BOOL CICmdWParams::ReverseArray(void * rgArray, DBCOUNTITEM cElements, ULONG ulElementSize)
{
BYTE * pTemp=new BYTE[ulElementSize];
BYTE * prgStart=(BYTE *)rgArray;
BYTE * prgEnd=prgStart+(cElements-1)*ulElementSize;
if (cElements < 2)
return TRUE;
if (!pTemp)
return FALSE;
while (prgStart < prgEnd)
{
memcpy(pTemp, prgStart, ulElementSize);
memcpy(prgStart, prgEnd, ulElementSize);
memcpy(prgEnd, pTemp, ulElementSize);
prgStart+=ulElementSize;
prgEnd-=ulElementSize;
}
if (pTemp)
delete[] pTemp;
return TRUE;
}
//--------------------------------------------------------------------------
// @mfunc ScrambleArray
// @desc Utility function to scramble the contents of an array
//--------------------------------------------------------------------------
BOOL CICmdWParams::ScrambleArray(void * rgArray, DB_UPARAMS cElements, ULONG ulElementSize)
{
BYTE * pTemp=new BYTE[ulElementSize];
BYTE * prgStart=(BYTE *)rgArray;
BYTE * prgNext=prgStart+ulElementSize;
if (cElements < 2)
return TRUE;
if (!pTemp)
return FALSE;
for (ULONG idx=0; idx < cElements-1; idx+=2)
{
memcpy(pTemp, prgStart, ulElementSize);
memcpy(prgStart, prgNext, ulElementSize);
memcpy(prgNext, pTemp, ulElementSize);
prgStart+=2*ulElementSize;
prgNext+=2*ulElementSize;
}
if (pTemp)
delete[] pTemp;
return TRUE;
}
//--------------------------------------------------------------------------
// @mfunc CreateParameterNames
// @desc Utility function to populate parameter names in DBPARAMBINDINFO array
// ALL_VALID_NAMES,
// ALL_INVALID_NAMES, // Control character
// SOME_INVALID_NAMES, // Odd names are invalid
// ALL_NULL_NAMES,
// ALL_EMPTY_STRING_NAMES
//--------------------------------------------------------------------------
WCHAR ** CICmdWParams::CreateParameterNames(WCHAR ** ppwszParameterNames, enum NAME_ENUM eNameType,
DB_UPARAMS * pcParamNames, ULONG fColTypes)
{
CCol TempCol;
ULONG i=0;
ULONG iParamName=0;
if (!(ppwszParameterNames))
{
// Allocate one more than required and set to zero to simplify freeing.
ppwszParameterNames = (LPOLESTR *)m_pIMalloc->Alloc ((m_pTable->CountColumnsOnTable()+1) * sizeof (LPOLESTR));
if (!(ppwszParameterNames))
goto CLEANUP;
memset(ppwszParameterNames, 0, (size_t)(m_pTable->CountColumnsOnTable()+1) * sizeof (LPOLESTR));
}
for (i = 0; i < m_pTable->CountColumnsOnTable(); i++)
{
CHECK(m_pTable->GetColInfo(i+1, TempCol), S_OK);
// We want only the updatable non-long columns
if (TempCol.GetUpdateable())
{
if (!TempCol.GetIsLong() || fColTypes == INCLUDE_LONG_COLS)
{
if (!ppwszParameterNames[iParamName])
{
ppwszParameterNames[iParamName] = (LPOLESTR)m_pIMalloc->Alloc( sizeof (WCHAR) * SP_MAX_PARAMNAME_LENGTH + 1);
if (!ppwszParameterNames[iParamName])
goto CLEANUP;
}
// Create a parameter name from the column name appropriate for the
// provider/dbms combo.
switch (eNameType)
{
case ALL_VALID_NAMES:
// Use the provider required format if known
if (!(ppwszParameterNames[iParamName] = m_Syntax.MakeParamName(TempCol, 'P')))
goto CLEANUP;
break;
case SOME_INVALID_NAMES:
case ALL_INVALID_NAMES:
// Use the provider required format if known
if (!(ppwszParameterNames[iParamName] = m_Syntax.MakeParamName(TempCol, 'P')))
goto CLEANUP;
if (eNameType == ALL_INVALID_NAMES || iParamName % 2)
{
// Set first character of name to an invalid value
memset(ppwszParameterNames[iParamName], 1, 1);
}
break;
case ALL_NULL_NAMES:
if (ppwszParameterNames[iParamName])
FREE_DATA(ppwszParameterNames[iParamName]);
ppwszParameterNames[iParamName]=NULL;
break;
case ALL_EMPTY_STRING_NAMES:
wcscpy(ppwszParameterNames[iParamName], L"");
break;
}
iParamName++;
}
}
}
// Fill in the count of parameters if requested
if (pcParamNames)
*pcParamNames=iParamName;
return ppwszParameterNames;
CLEANUP:
FreeParameterNames(ppwszParameterNames);
return NULL;
}
//--------------------------------------------------------------------------
// @mfunc FreeParameterNames
// @desc Utility function to free all parameter names
//--------------------------------------------------------------------------
void CICmdWParams::FreeParameterNames(WCHAR ** ppwszParameterNames)
{
if (ppwszParameterNames)
{
for (ULONG idx=0; ppwszParameterNames[idx]; idx++)
FREE_DATA(ppwszParameterNames[idx]);
FREE_DATA(ppwszParameterNames);
}
}
void CICmdWParams::SetParameterNames(WCHAR ** pwszParameterNames)
{
WCHAR * pwszParameterName=NULL;
for (ULONG idx=0; idx < m_cDbParamBindInfo; idx++)
{
if (pwszParameterNames)
pwszParameterName=pwszParameterNames[idx];
m_rgDbParamBindInfo[idx].pwszName=pwszParameterName;
}
}
HRESULT CICmdWParams::VerifyParamInfo(DBCOUNTITEM cParamsExp, DB_UPARAMS * rgParamOrdinals, DBPARAMBINDINFO * rgDbParamBindInfo,
DB_UPARAMS cParams, DBPARAMINFO * rgParamInfo, LPOLESTR pNamesBuffer, ULONG idxStart, BOOL fDerived)
{
CCol TempCol;
DBORDINAL iOrdinal;
ULONG idxMatch;
BOOL bMatch=FALSE, bVerify=TRUE, bType=FALSE;
DBTYPE wType;
ULONG cFailures=0, cWarnings=0;
ULONG iFlag, ulAllFlags=0;
WCHAR wszMsg[MAX_MSG_LEN];
WCHAR * pwszParamName;
DBPARAMFLAGS dwFlags;
LPWSTR pwszRetName = m_Syntax.GetSyntax(T_RET_NAME);
// First make sure the count of params returned is accurate
if (!MYCOMPARE(cParamsExp, cParams, L"Count of parameters did not match.\n"))
return E_FAIL;
if (cParamsExp == 0)
{
if (!COMPARE(rgParamInfo, NULL))
return E_FAIL;
if (!COMPARE(pNamesBuffer, NULL))
return E_FAIL;
}
// Compare all values. Note that OLE DB doesn't guarantee you get back the values you set.
// When the driver can derive type information, the GetParameterInfo will return the
// "correct" values, not what was set. Even if the provider can't derive parameter information
// then the value may reflect the "best fit" parameter type for the DBTYPE specified in
// SetParameterInfo so the values may not match for DBTYPE, Precision, Scale, etc.
for (ULONG idx=0; idx < cParams; idx++)
{
iOrdinal = rgParamInfo[idx].iOrdinal;
// Ordinal values start at 1 and should be sequential
if (!MYCOMPARE(iOrdinal,idx+idxStart+1,L"rgParamInfo.iOrdinal values are not sequential.\n"))
cFailures++;
// Locate the entry in rgParamOrdinals, since the order doesn't necessarily match
bMatch=FALSE;
for (idxMatch=0; idxMatch < cParams; idxMatch++)
{
if (rgParamOrdinals[idxMatch] == iOrdinal)
{
bMatch=TRUE;
break;
}
}
// If no matching value found print a failure
if (!MYCOMPARE(bMatch, TRUE, L"Matching value not found in rgParamOrdinals.\n"))
{
cFailures++;
continue;
}
// Compare the other entries
// dwFlags
// Sql Server always sets the ISINPUT flag even if not requested
// when deriving parameter info except for return parameter.
dwFlags = rgDbParamBindInfo[idxMatch].dwFlags;
if (g_bSqlServer && fDerived &&
wcscmp(rgDbParamBindInfo[idxMatch].pwszName, pwszRetName) &&
dwFlags & DBPARAMFLAGS_ISOUTPUT &&
!(dwFlags & DBPARAMFLAGS_ISINPUT))
dwFlags |= DBPARAMFLAGS_ISINPUT;
// Sql Server always set the NULLABLE flag for sysname data type for parameters even though
// a sysname column is not nullable. The parameter is nullable.
if (g_bSqlServer && fDerived &&
!wcscmp(rgDbParamBindInfo[idxMatch].pwszDataSourceType, L"sysname") &&
!(dwFlags & DBPARAMFLAGS_ISNULLABLE))
dwFlags |= DBPARAMFLAGS_ISNULLABLE;
// Set up param name to be used if mismatch
if (rgDbParamBindInfo[idxMatch].pwszName)
pwszParamName = rgDbParamBindInfo[idxMatch].pwszName;
else
pwszParamName = L"<null>";
// Make sure at least one of DBPARAMFLAGS_ISINPUT or DBPARAMFLAGS_ISOUTPUT are set
if (!(rgParamInfo[idx].dwFlags & DBPARAMFLAGS_ISINPUT) &&
!(rgParamInfo[idx].dwFlags & DBPARAMFLAGS_ISOUTPUT))
swprintf(wszMsg, L"Neither DBPARAMFLAGS_ISINPUT nor DBPARAMFLAGS_ISOUTPUT were set for parameter %d type %s.\n",
idx+1, rgDbParamBindInfo[idxMatch].pwszDataSourceType);
CCOMPARE(m_EC, ((rgParamInfo[idx].dwFlags & (DBPARAMFLAGS_ISINPUT | DBPARAMFLAGS_ISOUTPUT)) > 0) == TRUE,
EC_MISSING_ISINPUT_AND_ISOUTPUT_FLAGS,
wszMsg,
FALSE);
// Check the setting of all known flags
for (iFlag = 0; iFlag < NUMELEM(g_rgParamFlags); iFlag++)
{
ulAllFlags |= g_rgParamFlags[iFlag].dwFlag;
// Set up message in case flag value did not match expected
if (rgParamInfo[idx].dwFlags & g_rgParamFlags[iFlag].dwFlag)
swprintf(wszMsg, L"%s was set and should have been clear for parameter %d type %s.\n",
g_rgParamFlags[iFlag].pwszFlagName, idx+1, rgDbParamBindInfo[idxMatch].pwszDataSourceType);
else
swprintf(wszMsg, L"%s was clear and should have been set for parameter %d type %s.\n",
g_rgParamFlags[iFlag].pwszFlagName, idx+1, rgDbParamBindInfo[idxMatch].pwszDataSourceType);
switch(g_rgParamFlags[iFlag].dwFlag)
{
case DBPARAMFLAGS_ISSIGNED:
CCOMPARE(m_EC, (rgParamInfo[idx].dwFlags & g_rgParamFlags[iFlag].dwFlag)==(dwFlags & g_rgParamFlags[iFlag].dwFlag),
EC_INVALID_ISSIGNED,
wszMsg,
FALSE);
break;
case DBPARAMFLAGS_ISNULLABLE:
CCOMPARE(m_EC, (rgParamInfo[idx].dwFlags & g_rgParamFlags[iFlag].dwFlag)==(dwFlags & g_rgParamFlags[iFlag].dwFlag),
EC_INVALID_ISNULLABLE,
wszMsg,
FALSE);
break;
case DBPARAMFLAGS_ISINPUT:
CCOMPARE(m_EC, (rgParamInfo[idx].dwFlags & g_rgParamFlags[iFlag].dwFlag)==(dwFlags & g_rgParamFlags[iFlag].dwFlag),
EC_INVALID_ISINPUT,
wszMsg,
FALSE);
break;
default:
if (!MYCOMPARE(rgParamInfo[idx].dwFlags & g_rgParamFlags[iFlag].dwFlag,
dwFlags & g_rgParamFlags[iFlag].dwFlag, wszMsg))
cFailures++;
}
}
// Make sure no unknown flags were set
swprintf(wszMsg, L"An unknown flag was set for parameter %d type %s.\n",
idx+1, rgDbParamBindInfo[idxMatch].pwszDataSourceType);
if (!MYCOMPARE(rgParamInfo[idx].dwFlags & ~ulAllFlags, 0, wszMsg))
cFailures++;
// pwszName
//
// Note that we assume names are supported if pNamesBuffer is not NULL or the pwszName is not NULL
//
// TODO: Check that pwszName in rgParamInfo points into pNamesNamesBuffer.
if (pNamesBuffer || rgParamInfo[idx].pwszName)
{
if (rgDbParamBindInfo[idxMatch].pwszName && rgParamInfo[idx].pwszName)
{
// We put in a name and got one back, make sure they're the same
// Need to convert our internal names to upper or lower case before compare if provider does so
if (m_ulIdentifierCase == DBPROPVAL_IC_UPPER)
_wcsupr(rgDbParamBindInfo[idxMatch].pwszName);
if (m_ulIdentifierCase == DBPROPVAL_IC_LOWER)
_wcslwr(rgDbParamBindInfo[idxMatch].pwszName);
CCOMPARE(m_EC, wcscmp(rgParamInfo[idx].pwszName, rgDbParamBindInfo[idxMatch].pwszName) == 0,
EC_INVALID_PARAM_NAME,
L"Parameter names did not match.\n",
FALSE);
}
else
{
// We either didn't put in a name (NULL), or put one in and didn't get one back
// In either case it's an error if they don't match
if (!MYCOMPARE(rgParamInfo[idx].pwszName, rgDbParamBindInfo[idxMatch].pwszName, L"Parameter names did not match.\n"))
cFailures++;
}
}
// pTypeInfo
if (!MYCOMPARE(rgParamInfo[idx].pTypeInfo, NULL, L"pTypeInfo mismatch.\n"))
cFailures++;
// wType, we need to find the corresponding provider type name so we can get the DBTYPE
CCol TempCol;
bMatch=FALSE;
for (ULONG iCol=1; iCol<=m_pTable->CountColumnsOnTable(); iCol++)
{
CHECK(m_pTable->GetColInfo(iCol, TempCol), S_OK);
if (!wcscmp(TempCol.GetProviderTypeName(), rgDbParamBindInfo[idxMatch].pwszDataSourceType))
{
bMatch=TRUE;
wType=TempCol.GetProviderType();
break;
}
}
if (!MYCOMPARE(bMatch, TRUE, L"Provider type name not found.\n"))
cFailures++;
else if (!(bType = (wType == rgParamInfo[idx].wType)))
{
/*
odtLog << L"Warning: Type identifier didn't match for parameter " << idx+1 << L"\n";
odtLog << L"Expected: " << wType << L" Received: " << rgParamInfo[idx].wType << L"\n";
odtLog << L"Per spec this isn't a failure since providers are allowed to return the 'best fit' type.\n";
odtLog << L"Skipping comparison of ulParamSize, bPrecision, and bScale.\n\n";
*/
cWarnings++;
}
if (bType && !g_bOracle)
{
// ulParamSize - if the types didn't compare then the size likely won't anyway.
if (!MYCOMPARE(rgParamInfo[idx].ulParamSize, rgDbParamBindInfo[idxMatch].ulParamSize, L"Parameter size did not match.\n"))
{
cFailures++;
odtLog << L"Parameter " << rgParamInfo[idx].iOrdinal << L" type " << rgDbParamBindInfo[idxMatch].pwszDataSourceType
<< L" returned length " << rgParamInfo[idx].ulParamSize << L", expected " << (rgDbParamBindInfo[idxMatch]).ulParamSize
<< L"\n";
}
// bPrecision
CCOMPARE(m_EC, rgParamInfo[idx].bPrecision == rgDbParamBindInfo[idxMatch].bPrecision,
EC_INVALID_PRECISION,
L"Precision did not match expected value",
FALSE);
// bScale
CCOMPARE(m_EC, rgParamInfo[idx].bScale==rgDbParamBindInfo[idxMatch].bScale,
EC_INVALID_SCALE,
L"Scale did not match expected value",
FALSE);
}
}
SAFE_FREE(pwszRetName);
return cFailures ? E_FAIL : S_OK;
}
// CICmdWParams::SetRowsetPropertyDefault ------------------------------------
//
// Sets the given rowset property using ICommandProperties to the default value
//
//
HRESULT CICmdWParams::SetRowsetPropertyDefault(DBPROPID DBPropID, ICommand * pICommand)
{
ICommandProperties * pICmdProps = NULL;
DBPROPSET DBPropSet;
DBPROP DBProp;
HRESULT hr = E_FAIL;
//Set up the rowset property structure to use the ID passed in
DBPropSet.rgProperties = &DBProp;
DBPropSet.cProperties = 1;
DBPropSet.guidPropertySet = DBPROPSET_ROWSET;
DBProp.dwPropertyID = DBPropID;
DBProp.dwOptions = DBPROPOPTIONS_OPTIONAL;
DBProp.colid = DB_NULLID;
DBProp.vValue.vt = VT_EMPTY; //Causes default to be set
if (!pICommand)
pICommand = m_pICommand;
ASSERT(pICommand);
TESTC_(pICommand->QueryInterface(IID_ICommandProperties,
(void **)&pICmdProps), S_OK);
TESTC_(pICmdProps->SetProperties(1, &DBPropSet), S_OK);
hr = S_OK;
CLEANUP:
SAFE_RELEASE(pICmdProps);
return hr;
}
//-----------------------------------------
//@mfunc Terminate function for base class;
//-----------------------------------------
BOOL
CICmdWParams::Terminate()
{
// Free allocated memory.
ReleaseDataForCommand();
FREE_DATA(m_rgParamColMap);
FREE_DATA(m_pwszSqlInsertAllWithParams);
FREE_DATA(m_rgBindings);
FREE_DATA(m_rgColInfo);
FREE_DATA(m_rgParamOrdinals);
if (m_pExtraTable)
{
((CTable *)m_pExtraTable)->DropTable();
delete (CTable *)m_pExtraTable;
m_pExtraTable = NULL;
}
PROVIDER_FREE(m_pParamStruct);
FREE_DATA(m_rgDbParamBindInfo);
// Release interface pointers.
RELEASE (m_pICmdWParams);
RELEASE (m_pGeneralCmdIAccessor);
RELEASE (m_pICommandPrepare);
RELEASE (m_pICommandText);
// Accessor being released.
if (m_pCmdIAccessor)
m_pCmdIAccessor->ReleaseAccessor(m_hAccessor, NULL);
RELEASE (m_pCmdIAccessor);
RELEASE (m_pICommand);
RELEASE (m_pIEmptyCommand);
cRow.ReleaseDBSession();
cRow.ReleaseDataSourceObject();
ReleaseDBSession();
ReleaseDataSourceObject();
return (COLEDB::Terminate());
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
// Test Case Section
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
// {{ TCW_TEST_CASE_MAP(TCGetParameterInfo_Rowset)
//--------------------------------------------------------------------
// @class Test case for GetParameterInfo.
//
class TCGetParameterInfo_Rowset : public CICmdWParams {
protected:
// @cmember Static array of variations
DECLARE_TEST_CASE_DATA();
public:
// {{ TCW_DECLARE_FUNCS
// @cmember Execution Routine
DECLARE_TEST_CASE_FUNCS(TCGetParameterInfo_Rowset,CICmdWParams);
// }} TCW_DECLARE_FUNCS_END
// @cmember Initialization Routine
virtual BOOL Init();
// @cmember Termination Routine
virtual BOOL Terminate();
// {{ TCW_TESTVARS()
// @cmember Call GetParameterInfo after SetCommandText. Delete text and call GetParameterInfo again.
int Variation_1();
// @cmember Call GetParameterInfo after ICommandPrepare.
int Variation_2();
// @cmember Call GetParameterInfo after execute and release
int Variation_3();
// @cmember Valid SQL with no parameters.
int Variation_4();
// @cmember No command object.
int Variation_5();
// @cmember Call GetParameterInfo twice.
int Variation_6();
// @cmember Parameters (NULL, NULL, Valid
int Variation_7();
// @cmember Parameters(NULL, Valid, Valid
int Variation_8();
// @cmember Parameters (Valid, NULL, Valid
int Variation_9();
// @cmember Parameters (NULL, NULL, NULL
int Variation_10();
// @cmember Parameters (Valid, Valid, NULL
int Variation_11();
// @cmember Call GetParameterInfo after SetCommandText for stored proc with INPUT params
int Variation_12();
// @cmember Call GetParameterInfo after calling SetCommandText with a stored procedure with OUTPUT params
int Variation_13();
// @cmember Call GetParamInfo after calling SetCommandText with a stored procedure with IN/OUT params.
int Variation_14();
// @cmember Call GetParameterInfo after SetCommandText with StoredProc with VeryLongParamNames
int Variation_15();
// @cmember Call GetParamInfo with a stored procedure which is returning a value.
int Variation_16();
// @cmember GetParameterInfo with text set to different types of SQL statments with funny characters to test parsing code of Dev.
int Variation_17();
// @cmember Stored procedure execution on two different command objects.
int Variation_18();
// @cmember Maximum length parameter names
int Variation_19();
// }} TCW_TESTVARS_END
};
// {{ TCW_TESTCASE(TCGetParameterInfo_Rowset)
#define THE_CLASS TCGetParameterInfo_Rowset
BEG_TEST_CASE(TCGetParameterInfo_Rowset, CICmdWParams, L"Test case for GetParameterInfo.")
TEST_VARIATION(1, L"Call GetParameterInfo after SetCommandText. Delete text and call GetParameterInfo again.")
TEST_VARIATION(2, L"Call GetParameterInfo after ICommandPrepare.")
TEST_VARIATION(3, L"Call GetParameterInfo after execute and release")
TEST_VARIATION(4, L"Valid SQL with no parameters.")
TEST_VARIATION(5, L"No command object.")
TEST_VARIATION(6, L"Call GetParameterInfo twice.")
TEST_VARIATION(7, L"Parameters (NULL, NULL, Valid")
TEST_VARIATION(8, L"Parameters(NULL, Valid, Valid")
TEST_VARIATION(9, L"Parameters (Valid, NULL, Valid")
TEST_VARIATION(10, L"Parameters (NULL, NULL, NULL")
TEST_VARIATION(11, L"Parameters (Valid, Valid, NULL")
TEST_VARIATION(12, L"Call GetParameterInfo after SetCommandText for stored proc with INPUT params")
TEST_VARIATION(13, L"Call GetParameterInfo after calling SetCommandText with a stored procedure with OUTPUT params")
TEST_VARIATION(14, L"Call GetParamInfo after calling SetCommandText with a stored procedure with IN/OUT params.")
TEST_VARIATION(15, L"Call GetParameterInfo after SetCommandText with StoredProc with VeryLongParamNames")
TEST_VARIATION(16, L"Call GetParamInfo with a stored procedure which is returning a value.")
TEST_VARIATION(17, L"GetParameterInfo with text set to different types of SQL statments with funny characters to test parsing code of Dev.")
TEST_VARIATION(18, L"Stored procedure execution on two different command objects.")
TEST_VARIATION(19, L"Maximum length parameter names")
END_TEST_CASE()
#undef THE_CLASS
// }} TCW_TESTCASE_END
// }} TCW_TEST_CASE_MAP_END
// {{ TCW_TEST_CASE_MAP(TCMapParameterNames)
//--------------------------------------------------------------------
// @class Test case for MapParameterNames.
//
class TCMapParameterNames : public CICmdWParams {
private:
// @cmember Static array of variations
DECLARE_TEST_CASE_DATA();
protected:
DBPARAMBINDINFO * m_pParamBindInfo;
DB_UPARAMS m_iNotTouched;
HRESULT m_hrExpect;
DB_UPARAMS m_cParamNames;
ULONG m_fColTypes;
BOOL m_fUseProc;
public:
// {{ TCW_DECLARE_FUNCS
// @cmember Execution Routine
DECLARE_TEST_CASE_FUNCS(TCMapParameterNames,CICmdWParams);
// }} TCW_DECLARE_FUNCS_END
// @cmember Constructor
TCMapParameterNames(void);
// @cmember Initialization Routine
virtual BOOL Init();
// @cmember Termination Routine
virtual BOOL Terminate();
// {{ TCW_TESTVARS()
// @cmember cParamNames = 0 : returns S_OK
int Variation_1();
// @cmember cParamNames > 0 and NULL pointer for rgParamNames: returns E_INVALIDARG
int Variation_2();
// @cmember rgParamOrdinals with NULL : returns E_INVALIDARG
int Variation_3();
// @cmember (0, NULL, NULL
int Variation_4();
// @cmember Map Parameter Names without prepared command: DB_E_NOTPREPARED
int Variation_5();
// @cmember MapParameterNames with all invalid parameter names: DB_E_ERRORSOCCURRED
int Variation_6();
// @cmember MapParameterNames with some invalid parameter names: DB_S_ERRORSOCCURRED
int Variation_7();
// @cmember MapParameterNames with all valid names: S_OK
int Variation_8();
// @cmember MapParameterNames with fewer names than set
int Variation_9();
// @cmember Random name order: S_OK
int Variation_10();
// @cmember Overridden with SetParameterInfo: S_OK or DB_E_ERRORSOCCURRED
int Variation_11();
// @cmember No command text set: DB_E_NOCOMMAND
int Variation_12();
// }} TCW_TESTVARS_END
};
// {{ TCW_TESTCASE(TCMapParameterNames)
#define THE_CLASS TCMapParameterNames
BEG_TEST_CASE(TCMapParameterNames, CICmdWParams, L"Test case for MapParameterNames.")
TEST_VARIATION(1, L"cParamNames = 0 : returns S_OK")
TEST_VARIATION(2, L"cParamNames > 0 and NULL pointer for rgParamNames: returns E_INVALIDARG")
TEST_VARIATION(3, L"rgParamOrdinals with NULL : returns E_INVALIDARG")
TEST_VARIATION(4, L"(0, NULL, NULL")
TEST_VARIATION(5, L"Map Parameter Names without prepared command: DB_E_NOTPREPARED")
TEST_VARIATION(6, L"MapParameterNames with all invalid parameter names: DB_E_ERRORSOCCURRED")
TEST_VARIATION(7, L"MapParameterNames with some invalid parameter names: DB_S_ERRORSOCCURRED")
TEST_VARIATION(8, L"MapParameterNames with all valid names: S_OK")
TEST_VARIATION(9, L"MapParameterNames with fewer names than set")
TEST_VARIATION(10, L"Random name order: S_OK")
TEST_VARIATION(11, L"Overridden with SetParameterInfo: S_OK or DB_E_ERRORSOCCURRED")
TEST_VARIATION(12, L"No command text set: DB_E_NOCOMMAND")
END_TEST_CASE()
#undef THE_CLASS
// }} TCW_TESTCASE_END
// }} TCW_TEST_CASE_MAP_END
// {{ TCW_TEST_CASE_MAP(TCCommandExecute_Rowset)
//--------------------------------------------------------------------
// @class Test case for ICommand::Execute (For Parameter related test cases
//
class TCCommandExecute_Rowset : public CICmdWParams {
protected:
// @cmember Static array of variations
DECLARE_TEST_CASE_DATA();
private:
// @cmember Stored procedure command text.
ICommandText *m_pSPICommandText;
// @cmember Stored procedure command prepare.
ICommandPrepare *m_pSPICommandPrepare;
// @cmember Pointer to parameter struct of mapping info
ParamStruct * m_pSPParamAll;
// @cmember number of Bindings for SP.
ULONG m_cSPBindings;
// @cmember range of Bindings for SP
DBBINDING *m_rgSPBindings;
DBPARAMBINDINFO * m_rgSPParamBind;
// @cmember Stored procedure ordinals and Colmap array.
ULONG m_cSPParamColMap;
DB_LORDINAL *m_rgSPParamColMap;
DB_UPARAMS *m_rgSPParamColOrdinals;
// @cmember Accessor interface for Stored procedure command
ULONG m_cbReadRowSize;
DBLENGTH m_cbSPRowSize;
IAccessor *m_pSPIAccessor;
HACCESSOR m_hReadAccessor;
ULONG m_cReadBindings;
DBBINDING * m_rgReadBindings;
// @cmember DBPARAMS data for execute statement
DBPARAMS m_SPDBParams;
// @cmember Rownumber used to create the stored procedure. (For Comapare data)
DBCOUNTITEM m_ulLocalSPRowNum;
// @cmember Pointer to the rowset generated by the stored procedure.
IRowset *m_pSPRowset;
// @cmember Number of rows affected by the stored procedure.
DBROWCOUNT m_cSPRowsAffected;
// @cmember CreateSPText with out parameters.
BOOL CreateSPText();
// @cmember CreateSP with out parameters.
BOOL CreateSP();
// @cmember Drop the stored procedure.
BOOL DropSP();
// @cmember Execute the stored procedure.
HRESULT ExecuteSP(DBPARAMS *pDbParams, DBCOUNTITEM ulRowNum);
public:
// {{ TCW_DECLARE_FUNCS
// @cmember Execution Routine
DECLARE_TEST_CASE_FUNCS(TCCommandExecute_Rowset,CICmdWParams);
// }} TCW_DECLARE_FUNCS_END
// @cmember Initialization Routine
virtual BOOL Init();
// @cmember Termination Routine
virtual BOOL Terminate();
// {{ TCW_TESTVARS()
// @cmember An invalid parameter in pParams, returns DB_E_INVALIDARG
int Variation_1();
// @cmember Supply too many parameter info than required. Should return S_OK.
int Variation_2();
// @cmember Supply too few parameters than required, returns DB_E_PARAMNOTOPTIONAL
int Variation_3();
// @cmember Supply more than one value for the same parameter. Returns DB_E_DUPLICATEPARAM
int Variation_4();
// @cmember Supply a parameter value outside the domain for that parameter, returns DB_E_OVERFLOW
int Variation_5();
// @cmember Accessor with PARAMETER_DATA | ROWDATA | READWRITE
int Variation_6();
// @cmember Accessor with ROWDATA with [in] and [in/out] parameters
int Variation_7();
// @cmember Accessor with only status binding.
int Variation_8();
// @cmember Accessor with only (value and length
int Variation_9();
// @cmember Verify Stored procedure (in/out
int Variation_10();
// @cmember DBPARAM invalid conditions.
int Variation_11();
// @cmember Supply a value which could not be co-erced, returns DB_E_CANTCONVERTVALUE.
int Variation_12();
// @cmember hAccessor in the DBPARAMS structure was invalid (DB_E_BADACCESSORHANDLE
int Variation_13();
// @cmember TEST Stuff
int Variation_14();
// @cmember Stored procedure with Valid Input/Output data.
int Variation_15();
// @cmember Stored Procedure with Valid OUTPUT only parameters.
int Variation_16();
// @cmember Stored procedure with NULL [out] and Valid [input] parameters.
int Variation_17();
// @cmember Stored procedure with NULL Output only parameters.
int Variation_18();
// @cmember Stored procedure with NULL input and Valid Output parameters.
int Variation_19();
// @cmember Input/Output parameters with only Status bound to NULL
int Variation_20();
// @cmember Output only parameters with Status only binding to NULL
int Variation_21();
// @cmember Input/output with status only binding to S_OK
int Variation_22();
// @cmember Output only with status only binding to S_OK
int Variation_23();
// @cmember Input/Output with Length and Value only bound.
int Variation_24();
// @cmember Output only with Length and Value bound
int Variation_25();
// @cmember Input/Output with Value and Status bound
int Variation_26();
// @cmember output only with Value and Status bound
int Variation_27();
// @cmember Input/output with only Value bound
int Variation_28();
// @cmember output only with Value only bound.
int Variation_29();
// @cmember INPUT/OUTPUT with BY_REF bindings
int Variation_30();
// @cmember OUTPUT_ONLY with BY_REF bindings.
int Variation_31();
// @cmember Return value for a stored procedure.
int Variation_32();
// @cmember Bind same output parameters multiple number of times.
int Variation_33();
// @cmember 1 parameter Set.
int Variation_34();
// @cmember 5 parameter sets
int Variation_35();
// @cmember 105 parameter sets.
int Variation_36();
// @cmember Different types of Good and Bad parameter Sets.
int Variation_37();
// @cmember Having a table such that Variable length columns make up first and last columns
int Variation_38();
// @cmember Having BLOB as first and last columns
int Variation_39();
// @cmember 5 parameter sets with different size of Variable length parameters.
int Variation_40();
// @cmember 5 parameters sets followed by 1 parameter set.
int Variation_41();
// @cmember BYREF accessor with PROVIDER_OWNED memory should fail.
int Variation_42();
// @cmember RestartPosition should either return S_OK or DB_E_CANNOTRESTART
int Variation_43();
// @cmember Named Parameters: Sproc parm count != bound parm count
int Variation_44();
// @cmember Named Parameters: Sproc parm order != binding order
int Variation_45();
// @cmember Named Parameters: Use all parameter names
int Variation_46();
// @cmember Named Parameters: Retrieve names and use without setting
int Variation_47();
// @cmember Named Parameters: Named return parameter from sproc
int Variation_48();
// @cmember Inline binding with more than 64K and embedded null term
int Variation_49();
// @cmember Insert with BSTR bindings to strings
int Variation_50();
// @cmember OpenRowset on a stored proc with params
int Variation_51();
// @cmember DBSTATUS_S_DEFAULT - Default values for param
int Variation_52();
// @cmember Multiple procs - execute two procs without SetParamInfo or Prepare
int Variation_53();
// @cmember Multiple procs - execute two procs with partial SetParamInfo and Prepare
int Variation_54();
// @cmember DBSTATUS_S_IGNORE - Not legal for params (DBSTATUS_E_BADSTATUS)
int Variation_55();
// @cmember OpenRowset on stored proc without params
int Variation_56();
// @cmember Select with DBSTATUS_S_ISNULL input params
int Variation_57();
// @cmember S_OK - Send less data than max for parameter
int Variation_58();
// @cmember S_OK - Insert proc with CANHOLDROWS OPTIONAL
int Variation_59();
// @cmember Insert with BSTR bindings to strings to stored proc
int Variation_60();
// @cmember S_OK: Input and output params with cParamSets > 1
int Variation_61();
// }} TCW_TESTVARS_END
};
// {{ TCW_TESTCASE(TCCommandExecute_Rowset)
#define THE_CLASS TCCommandExecute_Rowset
BEG_TEST_CASE(TCCommandExecute_Rowset, CICmdWParams, L"Test case for ICommand::Execute (For Parameter related test cases")
TEST_VARIATION(1, L"An invalid parameter in pParams, returns DB_E_INVALIDARG")
TEST_VARIATION(2, L"Supply too many parameter info than required. Should return S_OK.")
TEST_VARIATION(3, L"Supply too few parameters than required, returns DB_E_PARAMNOTOPTIONAL")
TEST_VARIATION(4, L"Supply more than one value for the same parameter. Returns DB_E_DUPLICATEPARAM")
TEST_VARIATION(5, L"Supply a parameter value outside the domain for that parameter, returns DB_E_OVERFLOW")
TEST_VARIATION(6, L"Accessor with PARAMETER_DATA | ROWDATA | READWRITE")
TEST_VARIATION(7, L"Accessor with ROWDATA with [in] and [in/out] parameters")
TEST_VARIATION(8, L"Accessor with only status binding.")
TEST_VARIATION(9, L"Accessor with only (value and length")
TEST_VARIATION(10, L"Verify Stored procedure (in/out")
TEST_VARIATION(11, L"DBPARAM invalid conditions.")
TEST_VARIATION(12, L"Supply a value which could not be co-erced, returns DB_E_CANTCONVERTVALUE.")
TEST_VARIATION(13, L"hAccessor in the DBPARAMS structure was invalid (DB_E_BADACCESSORHANDLE")
TEST_VARIATION(14, L"TEST Stuff")
TEST_VARIATION(15, L"Stored procedure with Valid Input/Output data.")
TEST_VARIATION(16, L"Stored Procedure with Valid OUTPUT only parameters.")
TEST_VARIATION(17, L"Stored procedure with NULL [out] and Valid [input] parameters.")
TEST_VARIATION(18, L"Stored procedure with NULL Output only parameters.")
TEST_VARIATION(19, L"Stored procedure with NULL input and Valid Output parameters.")
TEST_VARIATION(20, L"Input/Output parameters with only Status bound to NULL")
TEST_VARIATION(21, L"Output only parameters with Status only binding to NULL")
TEST_VARIATION(22, L"Input/output with status only binding to S_OK")
TEST_VARIATION(23, L"Output only with status only binding to S_OK")
TEST_VARIATION(24, L"Input/Output with Length and Value only bound.")
TEST_VARIATION(25, L"Output only with Length and Value bound")
TEST_VARIATION(26, L"Input/Output with Value and Status bound")
TEST_VARIATION(27, L"output only with Value and Status bound")
TEST_VARIATION(28, L"Input/output with only Value bound")
TEST_VARIATION(29, L"output only with Value only bound.")
TEST_VARIATION(30, L"INPUT/OUTPUT with BY_REF bindings")
TEST_VARIATION(31, L"OUTPUT_ONLY with BY_REF bindings.")
TEST_VARIATION(32, L"Return value for a stored procedure.")
TEST_VARIATION(33, L"Bind same output parameters multiple number of times.")
TEST_VARIATION(34, L"1 parameter Set.")
TEST_VARIATION(35, L"5 parameter sets")
TEST_VARIATION(36, L"105 parameter sets.")
TEST_VARIATION(37, L"Different types of Good and Bad parameter Sets.")
TEST_VARIATION(38, L"Having a table such that Variable length columns make up first and last columns")
TEST_VARIATION(39, L"Having BLOB as first and last columns")
TEST_VARIATION(40, L"5 parameter sets with different size of Variable length parameters.")
TEST_VARIATION(41, L"5 parameters sets followed by 1 parameter set.")
TEST_VARIATION(42, L"BYREF accessor with PROVIDER_OWNED memory should fail.")
TEST_VARIATION(43, L"RestartPosition should either return S_OK or DB_E_CANNOTRESTART")
TEST_VARIATION(44, L"Named Parameters: Sproc parm count != bound parm count")
TEST_VARIATION(45, L"Named Parameters: Sproc parm order != binding order")
TEST_VARIATION(46, L"Named Parameters: Use all parameter names")
TEST_VARIATION(47, L"Named Parameters: Retrieve names and use without setting")
TEST_VARIATION(48, L"Named Parameters: Named return parameter from sproc")
TEST_VARIATION(49, L"Inline binding with more than 64K and embedded null term")
TEST_VARIATION(50, L"Insert with BSTR bindings to strings")
TEST_VARIATION(51, L"OpenRowset on a stored proc with params")
TEST_VARIATION(52, L"DBSTATUS_S_DEFAULT - Default values for param")
TEST_VARIATION(53, L"Multiple procs - execute two procs without SetParamInfo or Prepare")
TEST_VARIATION(54, L"Multiple procs - execute two procs with partial SetParamInfo and Prepare")
TEST_VARIATION(55, L"DBSTATUS_S_IGNORE - Not legal for params (DBSTATUS_E_BADSTATUS)")
TEST_VARIATION(56, L"OpenRowset on stored proc without params")
TEST_VARIATION(57, L"Select with DBSTATUS_S_ISNULL input params")
TEST_VARIATION(58, L"S_OK - Send less data than max for parameter")
TEST_VARIATION(59, L"S_OK - Insert proc with CANHOLDROWS OPTIONAL")
TEST_VARIATION(60, L"Insert with BSTR bindings to strings to stored proc")
TEST_VARIATION(61, L"S_OK: Input and output params with cParamSets > 1")
END_TEST_CASE()
#undef THE_CLASS
// }} TCW_TESTCASE_END
// }} TCW_TEST_CASE_MAP_END
// {{ TCW_TEST_CASE_MAP(TCSetParameterInfo_Rowset)
//--------------------------------------------------------------------
// @class Test case for SetParameterInfo
//
class TCSetParameterInfo_Rowset : public CICmdWParams {
protected:
// @cmember Static array of variations
DECLARE_TEST_CASE_DATA();
public:
// {{ TCW_DECLARE_FUNCS
// @cmember Execution Routine
DECLARE_TEST_CASE_FUNCS(TCSetParameterInfo_Rowset,CICmdWParams);
// }} TCW_DECLARE_FUNCS_END
// @cmember Initialization Routine
virtual BOOL Init();
// @cmember Termination Routine
virtual BOOL Terminate();
// {{ TCW_TESTVARS()
// @cmember Call SetParameterInfo. GetParameterInfo. Should match.
int Variation_1();
// @cmember SetParameterInfo. Execute. S_OK
int Variation_2();
// @cmember SetParameterInfo, Execute. Use Rowset. DescribeParams.
int Variation_3();
// @cmember SetParameterInfo. Remove SetParameterInfo. GetParameterInfo. return original or fail
int Variation_4();
// @cmember SetParameterInfo for all. Delete a few. Override a few. Call GetParameterInfo and verify.
int Variation_5();
// @cmember (valid, NULL, NULL
int Variation_6();
// @cmember Bstr = NULL. What happens?
int Variation_7();
// @cmember SetParameterInfo with Illegal Coersions. Execute should fail.
int Variation_8();
// @cmember rgParamOrdinals[] = ULONG_MAX. what happens.
int Variation_9();
// @cmember Call execute with params before SetParameterInfo
int Variation_10();
// @cmember Wrong SetParameterInfo. Verify DP returns the same.
int Variation_11();
// @cmember Delete first parameter, last parameter. Verify.
int Variation_12();
// @cmember Call SetParameterInfo for Output Parameters.
int Variation_13();
// @cmember Call SetParameterInfo for Input/Output parameters.
int Variation_14();
// @cmember Call SetParameterInfo for All DBTYPES in the SPEC and Verify.
int Variation_15();
// @cmember Call SetParameterInfo for Native types and Verify.
int Variation_16();
// @cmember Open Rowset , and then call SetParameterInfo returns DB_E_OBJECTOPEN.
int Variation_17();
// @cmember Set parameter for variable length char fields and execute
int Variation_18();
// @cmember SetParameterInfo with reverse rgParamOrdinals
int Variation_19();
// @cmember SetParameterInfo with random rgParamOrdinals
int Variation_20();
// @cmember SetParameterInfo with duplicate parameter name
int Variation_21();
// @cmember SetParameterInfo with invalid parameter name
int Variation_22();
// @cmember Default Conversion: pwszDataSourceType == NULL
int Variation_23();
// @cmember Verify error on DBTYPE_NUMERIC with no precision
int Variation_24();
// }} TCW_TESTVARS_END
};
// {{ TCW_TESTCASE(TCSetParameterInfo_Rowset)
#define THE_CLASS TCSetParameterInfo_Rowset
BEG_TEST_CASE(TCSetParameterInfo_Rowset, CICmdWParams, L"Test case for SetParameterInfo")
TEST_VARIATION(1, L"Call SetParameterInfo. GetParameterInfo. Should match.")
TEST_VARIATION(2, L"SetParameterInfo. Execute. S_OK")
TEST_VARIATION(3, L"SetParameterInfo, Execute. Use Rowset. DescribeParams.")
TEST_VARIATION(4, L"SetParameterInfo. Remove SetParameterInfo. GetParameterInfo. return original or fail")
TEST_VARIATION(5, L"SetParameterInfo for all. Delete a few. Override a few. Call GetParameterInfo and verify.")
TEST_VARIATION(6, L"(valid, NULL, NULL")
TEST_VARIATION(7, L"Bstr = NULL. What happens?")
TEST_VARIATION(8, L"SetParameterInfo with Illegal Coersions. Execute should fail.")
TEST_VARIATION(9, L"rgParamOrdinals[] = ULONG_MAX. what happens.")
TEST_VARIATION(10, L"Call execute with params before SetParameterInfo")
TEST_VARIATION(11, L"Wrong SetParameterInfo. Verify DP returns the same.")
TEST_VARIATION(12, L"Delete first parameter, last parameter. Verify.")
TEST_VARIATION(13, L"Call SetParameterInfo for Output Parameters.")
TEST_VARIATION(14, L"Call SetParameterInfo for Input/Output parameters.")
TEST_VARIATION(15, L"Call SetParameterInfo for All DBTYPES in the SPEC and Verify.")
TEST_VARIATION(16, L"Call SetParameterInfo for Native types and Verify.")
TEST_VARIATION(17, L"Open Rowset , and then call SetParameterInfo returns DB_E_OBJECTOPEN.")
TEST_VARIATION(18, L"Set parameter for variable length char fields and execute")
TEST_VARIATION(19, L"SetParameterInfo with reverse rgParamOrdinals")
TEST_VARIATION(20, L"SetParameterInfo with random rgParamOrdinals")
TEST_VARIATION(21, L"SetParameterInfo with duplicate parameter name")
TEST_VARIATION(22, L"SetParameterInfo with invalid parameter name")
TEST_VARIATION(23, L"Default Conversion: pwszDataSourceType == NULL")
TEST_VARIATION(24, L"Verify error on DBTYPE_NUMERIC with no precision")
END_TEST_CASE()
#undef THE_CLASS
// }} TCW_TESTCASE_END
// }} TCW_TEST_CASE_MAP_END
// {{ TCW_TEST_CASE_MAP(TCExtendedErrors)
//--------------------------------------------------------------------
// @class Extended Errors
//
class TCExtendedErrors : public CICmdWParams {
private:
// @cmember Static array of variations
DECLARE_TEST_CASE_DATA();
public:
// {{ TCW_DECLARE_FUNCS
// @cmember Execution Routine
DECLARE_TEST_CASE_FUNCS(TCExtendedErrors,CICmdWParams);
// }} TCW_DECLARE_FUNCS_END
// @cmember Initialization Routine
virtual BOOL Init();
// @cmember Termination Routine
virtual BOOL Terminate();
// {{ TCW_TESTVARS()
// @cmember Valid GetParameterInfo calls with previous error object existing.
int Variation_1();
// @cmember Invalid GetParameterInfo calls with previous error object existing
int Variation_2();
// @cmember Invalid GetParameterInfo calls with no previous error object existing
int Variation_3();
// @cmember Valid SetParameterInfo calls with previous error object existing.
int Variation_4();
// @cmember Invalid SetParameterInfo calls with previous error object existing
int Variation_5();
// @cmember Invalid SetParameterInfo calls with no previous error object existing
int Variation_6();
// @cmember Valid MapParameterNames calls with previous error object existing.
int Variation_7();
// @cmember Invalid MapParameterNames calls with previous error object existing
int Variation_8();
// @cmember Invalid MapParameterNames calls with no previous error object existing
int Variation_9();
// }} TCW_TESTVARS_END
};
// {{ TCW_TESTCASE(TCExtendedErrors)
#define THE_CLASS TCExtendedErrors
BEG_TEST_CASE(TCExtendedErrors, CICmdWParams, L"Extended Errors")
TEST_VARIATION(1, L"Valid GetParameterInfo calls with previous error object existing.")
TEST_VARIATION(2, L"Invalid GetParameterInfo calls with previous error object existing")
TEST_VARIATION(3, L"Invalid GetParameterInfo calls with no previous error object existing")
TEST_VARIATION(4, L"Valid SetParameterInfo calls with previous error object existing.")
TEST_VARIATION(5, L"Invalid SetParameterInfo calls with previous error object existing")
TEST_VARIATION(6, L"Invalid SetParameterInfo calls with no previous error object existing")
TEST_VARIATION(7, L"Valid MapParameterNames calls with previous error object existing.")
TEST_VARIATION(8, L"Invalid MapParameterNames calls with previous error object existing")
TEST_VARIATION(9, L"Invalid MapParameterNames calls with no previous error object existing")
END_TEST_CASE()
#undef THE_CLASS
// }} TCW_TESTCASE_END
// }} TCW_TEST_CASE_MAP_END
// {{ TCW_TEST_CASE_MAP(TCBugRegressions)
//--------------------------------------------------------------------
// @class Test case to hold the regressions for bugs reported by other groups.
//
class TCBugRegressions : public CICmdWParams {
private:
// @cmember Static array of variations
DECLARE_TEST_CASE_DATA();
public:
// {{ TCW_DECLARE_FUNCS
// @cmember Execution Routine
DECLARE_TEST_CASE_FUNCS(TCBugRegressions,CICmdWParams);
// }} TCW_DECLARE_FUNCS_END
// @cmember Initialization Routine
virtual BOOL Init();
// @cmember Termination Routine
virtual BOOL Terminate();
// {{ TCW_TESTVARS()
// @cmember Cmd that executes SP with param type adText returns truncation error if string > 255 chars
int Variation_1();
// @cmember Prepare, Execute, Prepare, Execute w/o rebinding
int Variation_2();
// @cmember Retrieve a row after failing to retrieve with DBSTATUS_S_ISNULL
int Variation_3();
// @cmember Use int64 as input and output param binding in stored proc
int Variation_4();
// }} TCW_TESTVARS_END
};
// {{ TCW_TESTCASE(TCBugRegressions)
#define THE_CLASS TCBugRegressions
BEG_TEST_CASE(TCBugRegressions, CICmdWParams, L"Test case to hold the regressions for bugs reported by other groups.")
TEST_VARIATION(1, L"Cmd that executes SP with param type adText returns truncation error if string > 255 chars")
TEST_VARIATION(2, L"Prepare, Execute, Prepare, Execute w/o rebinding")
TEST_VARIATION(3, L"Retrieve a row after failing to retrieve with DBSTATUS_S_ISNULL")
TEST_VARIATION(4, L"Use int64 as input and output param binding in stored proc")
END_TEST_CASE()
#undef THE_CLASS
// }} TCW_TESTCASE_END
// }} TCW_TEST_CASE_MAP_END
// {{ TCW_TEST_CASE_MAP(TCIUnknown)
//--------------------------------------------------------------------
// @class Test parameter bindings to IUnknown
//
class TCIUnknown : public COLEDB {
private:
// @cmember Static array of variations
DECLARE_TEST_CASE_DATA();
// @cmember Default table object
CTable * m_pTable;
// @cmember Supported structured storage mask
ULONG_PTR m_ulStorageSupport;
// @cmember Defined storage flags
DWORD m_lStorageFlags;
// @cmember Whether provider is Kagera or not
BOOL m_fKagera;
// @cmember Result of test
TESTRESULT m_TestResult;
// @cmember HRESULT used for intermediate results
HRESULT m_hr;
// @cmember Expected HRESULT
HRESULT m_hrExpect;
// @cmember Flag for interface support
BOOL m_fSupportInterface;
// @cmember Bind status array
DBBINDSTATUS m_rgStatus[1];
// @cmember Command object pointer
ICommand * m_pICommand;
// @cmember Command text object pointer
ICommandText * m_pICommandText;
// @cmember Command parameter object pointer
ICommandWithParameters * m_pICommandWithParameters;
// @cmember Properties interface pointer
IDBProperties * m_pIDBProperties;
// @cmember IAccessor object pointer
IAccessor * m_pIAccessor;
// @cmember Local implementation of ISequentialStream
IUnknown * m_pIUnkVerifyObj;
public:
// {{ TCW_DECLARE_FUNCS
// @cmember Execution Routine
DECLARE_TEST_CASE_FUNCS(TCIUnknown,COLEDB);
// }} TCW_DECLARE_FUNCS_END
// @cmember Initialization Routine
virtual BOOL Init();
// @cmember Termination Routine
virtual BOOL Terminate();
// @cmember Returns a pointer to a data item for a column
LPVOID MakeData(CCol ColInfo, ULONG * pcbDataSize, ULONG ulSeed, enum EVALUE eValue = SECONDARY);
// @cmember IUnknown conversion check
HRESULT CanConvertIUnknown(IUnknown * pIUnknown, DBTYPE wType, DBCONVERTFLAGS dwConvertFlags);
// @cmember Returns TRUE if a type is a BLOB type
BOOL IsBLOB(DBTYPE wType);
// @cmember Creates the desired storage object for test or verification
HRESULT CreateStorageObject(REFIID iidObject, IUnknown ** ppIObject);
// @cmember Tests whether last byte to be sent is a lead byte.
BOOL IsLastByteLeadByte(LPVOID pvSrcData, ULONG cbSrcData, ULONG cbReadSize);
// @cmember Worker function to encapsulate common active error test code
TESTRESULT TestActiveError(enum ACTIVE_ERROR eTest, REFIID iidParamObject);
// @cmember Worker function to encapsulate common storage object test code
TESTRESULT TestStorageObject(enum PARAM_OBJECT_TEST eTest, REFIID iidParamObject,
REFIID iidVerifyObject, DB_UPARAMS cParamSets = 1);
BOOL MakeUnknownBinding(
DBCOUNTITEM iBind,
DBBINDING * pBind,
DB_UPARAMS * pParamOrdinals,
DBPARAMBINDINFO * pParamBind,
LPBYTE pData,
DBOBJECT * pParamStorageObjects,
REFIID iidParamObject,
DWORD dwFlags,
DBLENGTH cbRowSize,
DB_UPARAMS cParamSets,
ULONG ulUpdateRow,
CCol ColInfo,
enum PARAM_OBJECT_TEST eTest
);
// {{ TCW_TESTVARS()
// @cmember ISequentialStream
int Variation_1();
// @cmember ILockBytes
int Variation_2();
// @cmember IStorage
int Variation_3();
// @cmember IStream
int Variation_4();
// @cmember DBPROP_MULTIPLESTORAGEOBJECTS
int Variation_5();
// @cmember DBPROP_BLOCKINGSTORAGEOBJECTS
int Variation_6();
// @cmember NULL Storage Object - S_OK
int Variation_7();
// @cmember Empty storage object - S_OK
int Variation_8();
// @cmember DBSTATUS_S_ISNULL
int Variation_9();
// @cmember Verify streams active after error
int Variation_10();
// @cmember Verify streams released after error
int Variation_11();
// @cmember Multiple Paramsets
int Variation_12();
// @cmember Multiple Storage Objects
int Variation_13();
// }} TCW_TESTVARS_END
};
// {{ TCW_TESTCASE(TCIUnknown)
#define THE_CLASS TCIUnknown
BEG_TEST_CASE(TCIUnknown, COLEDB, L"Test parameter bindings to IUnknown")
TEST_VARIATION(1, L"ISequentialStream")
TEST_VARIATION(2, L"ILockBytes")
TEST_VARIATION(3, L"IStorage")
TEST_VARIATION(4, L"IStream")
TEST_VARIATION(5, L"DBPROP_MULTIPLESTORAGEOBJECTS")
TEST_VARIATION(6, L"DBPROP_BLOCKINGSTORAGEOBJECTS")
TEST_VARIATION(7, L"NULL Storage Object - S_OK")
TEST_VARIATION(8, L"Empty storage object - S_OK")
TEST_VARIATION(9, L"DBSTATUS_S_ISNULL")
TEST_VARIATION(10, L"Verify streams active after error")
TEST_VARIATION(11, L"Verify streams released after error")
TEST_VARIATION(12, L"Multiple Paramsets")
TEST_VARIATION(13, L"Multiple Storage Objects")
END_TEST_CASE()
#undef THE_CLASS
// }} TCW_TESTCASE_END
// }} TCW_TEST_CASE_MAP_END
// }} END_DECLARE_TEST_CASES()
////////////////////////////////////////////////////////////////////////
// Copying Test Cases to make duplicate ones.
//
////////////////////////////////////////////////////////////////////////
#define COPY_TEST_CASE(theClass, baseClass) \
class theClass : public baseClass \
{ \
public: \
static const WCHAR m_wszTestCaseName[]; \
DECLARE_TEST_CASE_FUNCS(theClass, baseClass); \
}; \
const WCHAR theClass::m_wszTestCaseName[] = { L#theClass }; \
#define TEST_CASE_WITH_PARAM(iCase, theClass, param) \
case iCase: \
pCTestCase = new theClass(NULL); \
((theClass*)pCTestCase)->SetTestCaseParam(param); \
pCTestCase->SetOwningMod(iCase-1, pCThisTestModule); \
return pCTestCase;
COPY_TEST_CASE(TCGetParameterInfo_Row, TCGetParameterInfo_Rowset)
COPY_TEST_CASE(TCCommandExecute_Row, TCCommandExecute_Rowset)
COPY_TEST_CASE(TCSetParameterInfo_Row, TCSetParameterInfo_Rowset)
#if 0
// {{ TCW_TESTMODULE(ThisModule)
TEST_MODULE(7, ThisModule, gwszModuleDescrip)
TEST_CASE(1, TCGetParameterInfo_Rowset)
TEST_CASE(2, TCMapParameterNames)
TEST_CASE(3, TCCommandExecute_Rowset)
TEST_CASE(4, TCSetParameterInfo_Rowset)
TEST_CASE(5, TCExtendedErrors)
TEST_CASE(6, TCBugRegressions)
TEST_CASE(7, TCIUnknown)
END_TEST_MODULE()
// }} TCW_TESTMODULE_END
#else
TEST_MODULE(10, ThisModule, gwszModuleDescrip)
TEST_CASE_WITH_PARAM(1, TCGetParameterInfo_Rowset, TC_Rowset)
TEST_CASE_WITH_PARAM(2, TCGetParameterInfo_Row, TC_Row)
TEST_CASE(3, TCMapParameterNames)
TEST_CASE_WITH_PARAM(4, TCCommandExecute_Rowset, TC_Rowset)
TEST_CASE_WITH_PARAM(5, TCCommandExecute_Row, TC_Row)
TEST_CASE_WITH_PARAM(6, TCSetParameterInfo_Rowset, TC_Rowset)
TEST_CASE_WITH_PARAM(7, TCSetParameterInfo_Row, TC_Row)
TEST_CASE(8, TCExtendedErrors)
TEST_CASE(9, TCBugRegressions)
TEST_CASE(10, TCIUnknown)
END_TEST_MODULE()
#endif
// {{ TCW_TC_PROTOTYPE(TCGetParameterInfo_Rowset)
//*-----------------------------------------------------------------------
//| Test Case: TCGetParameterInfo_Rowset - Test case for GetParameterInfo.
//| Created: 03/30/96
//*-----------------------------------------------------------------------
//--------------------------------------------------------------------
// @mfunc TestCase Initialization Routine
//
// @rdesc TRUE or FALSE
//
BOOL TCGetParameterInfo_Rowset::Init()
{
if (m_eTestCase == TC_Row && !g_fRowObj)
{
odtLog << L"Row objects not supported.\n";
return TEST_SKIPPED;
}
// {{ TCW_INIT_BASECLASS_CHECK
if(CICmdWParams::Init())
// }}
{
// Now proceed.
return TRUE;
}
return FALSE;
}
// {{ TCW_VAR_PROTOTYPE(1)
//*-----------------------------------------------------------------------
// @mfunc Call GetParameterInfo after SetCommandText. Delete text and call GetParameterInfo again.
//
// @rdesc TEST_PASS or TEST_FAIL
//
int TCGetParameterInfo_Rowset::Variation_1()
{
BOOL fResult = TRUE;
// Call ICommandPrepare.
ABORT_CHECK(m_pICommandPrepare->Prepare(1), S_OK);
ABORT_CHECK (m_pICmdWParams->GetParameterInfo(&m_cParams, &m_rgParamInfo, &m_pNamesBuffer), S_OK);
// Compare parameter information.
FAIL_VAR(CompareDBParamInfo(m_cBindings, m_rgParamColMap, m_cParams, m_rgParamInfo, m_pNamesBuffer), S_OK);
// Now set command text to empty.
ABORT_CHECK(m_pICommandText->SetCommandText(DBGUID_DBSQL , L""), S_OK);
// Free stuff for second call.
FreeDescParams();
if (m_bSetParameterInfo)
{
// Since parameter information is already set GetParameterInfo returns S_OK.
// Since return values have already been verified. No need to Check now.
ABORT_CHECK(m_pICmdWParams->GetParameterInfo(
&m_cParams, &m_rgParamInfo, &m_pNamesBuffer), S_OK);
FAIL_VAR(CompareDBParamInfo(m_cBindings, m_rgParamColMap, m_cParams, m_rgParamInfo, m_pNamesBuffer), S_OK);
}
else
{
HRESULT hrGet = DB_E_NOCOMMAND;
HRESULT hr = E_FAIL;
if (m_pICommandPrepare)
hrGet = DB_E_NOTPREPARED;
hr = m_pICmdWParams->GetParameterInfo(
&m_cParams, &m_rgParamInfo, &m_pNamesBuffer);
ABORT_CHECK(hr, hrGet);
FAIL_VAR(CompareDBParamInfo(0, NULL, NULL, m_rgParamInfo, m_pNamesBuffer), S_OK);
}
CLEANUP:
// Free stuff.
FreeDescParams();
return fResult ? TEST_PASS : TEST_FAIL;
}
// }}
// {{ TCW_VAR_PROTOTYPE(2)
//*-----------------------------------------------------------------------
// @mfunc Call GetParameterInfo after ICommandPrepare.
//
// @rdesc TEST_PASS or TEST_FAIL
//
int TCGetParameterInfo_Rowset::Variation_2()
{
BOOL fResult = FALSE;
// Set command text
TEST_CHECK(m_pICommandText->SetCommandText(DBGUID_DBSQL , m_pwszSqlInsertAllWithParams), S_OK);
// Call ICommandPrepare.
TEST_CHECK(m_pICommandPrepare->Prepare(1), S_OK);
TEST_CHECK(m_pICmdWParams->GetParameterInfo(&m_cParams, &m_rgParamInfo, &m_pNamesBuffer), S_OK);
CompareDBParamInfo(m_cBindings, m_rgParamColMap, m_cParams, m_rgParamInfo, m_pNamesBuffer);
// Call ICommandPrepare.
TEST_CHECK(m_pICommandPrepare->Unprepare(), S_OK);
// Now call Describe Parameters.
// Free stuff for second call.
FreeDescParams();
if (m_bSetParameterInfo)
{
// Since parameter information is already set GetParameterInfo returns S_OK.
// Since return values have already been verified. No need to Check now.
TEST_CHECK(m_pICmdWParams->GetParameterInfo(
&m_cParams, &m_rgParamInfo, &m_pNamesBuffer), S_OK);
}
else
{
TEST_CHECK(m_pICmdWParams->GetParameterInfo(
&m_cParams, &m_rgParamInfo, &m_pNamesBuffer), DB_E_NOTPREPARED);
}
fResult = TRUE;
CLEANUP:
FreeDescParams();
return (fResult) ? TEST_PASS : TEST_FAIL;
}
// }}
// {{ TCW_VAR_PROTOTYPE(3)
//*-----------------------------------------------------------------------
// @mfunc Call GetParameterInfo after execute and release
//
// @rdesc TEST_PASS or TEST_FAIL
//
int TCGetParameterInfo_Rowset::Variation_3()
{
BOOL fResult = FALSE;
DBROWCOUNT cRowsAffected = 0;
IRowset * pRowset = NULL;
IRowset * pIRowset = NULL;
DBCOUNTITEM ulRowNum;
ULONG i;
// To cover some possible values lets do execute in a loop.
// Set command text
TEST_CHECK (m_pICommandText->SetCommandText(DBGUID_DBSQL , m_pwszSqlInsertAllWithParams), S_OK);
// Call ICommandPrepare.
TEST_CHECK (m_pICommandPrepare->Prepare(1), S_OK);
for (i = 0; i < 10 ; i++ )
{
DBCOUNTITEM cRowsExpected = 1;
MakeDataForCommand((ulRowNum = NextInsertRowNum()));
// Now execute ICommand.
TEST_CHECK(m_pICommand->Execute(NULL, IID_IUnknown,
&m_DbParamsAll, &cRowsAffected, (IUnknown **)&pRowset), S_OK);
// Verify If we have inserted the row properly.
TEST_COMPARE(cRow.FindRow(ulRowNum, m_pTable, NULL, &pIRowset, NULL,
0, NULL, FALSE), TRUE);
// FindRow merely finds the appropriate row. We need to validate the inserted data for the default bindings.
TEST_COMPARE(VerifyObj(m_iidExec, pIRowset, ulRowNum, 0, NULL,
FALSE, FALSE, m_pTable, &cRowsExpected), S_OK);
SAFE_RELEASE(pIRowset);
SAFE_RELEASE(pRowset);
}
TEST_CHECK(m_pICmdWParams->GetParameterInfo(&m_cParams, &m_rgParamInfo, &m_pNamesBuffer), S_OK);
CompareDBParamInfo(m_cBindings, m_rgParamColMap, m_cParams, m_rgParamInfo, m_pNamesBuffer);
if (pRowset) ReleaseRowsetPtr (&pRowset);
FreeDescParams();
TEST_CHECK (m_pICmdWParams->GetParameterInfo(&m_cParams, &m_rgParamInfo, &m_pNamesBuffer), S_OK);
fResult = TRUE;
CLEANUP:
SAFE_RELEASE(pIRowset);
SAFE_RELEASE(pRowset);
FreeDescParams();
ReleaseDataForCommand();
if (pRowset)
ReleaseRowsetPtr(&pRowset);
return (fResult) ? TEST_PASS : TEST_FAIL;
}
// }}
// {{ TCW_VAR_PROTOTYPE(4)
//*-----------------------------------------------------------------------
// @mfunc Valid SQL with no parameters.
//
// @rdesc TEST_PASS or TEST_FAIL
//
int TCGetParameterInfo_Rowset::Variation_4()
{
WCHAR *pwszSqlSelectAllFromTbl = NULL;
BOOL fResult = FALSE;
// Set command text
TEST_CHECK (m_pICommandText->SetCommandText(DBGUID_DBSQL , m_pwszSqlInsertAllWithParams), S_OK);
// Call ICommandPrepare.
TEST_CHECK(m_pICommandPrepare->Prepare(1), S_OK);
FreeDescParams();
TEST_CHECK(m_pICmdWParams->GetParameterInfo(&m_cParams, &m_rgParamInfo, &m_pNamesBuffer), S_OK);
CompareDBParamInfo(m_cBindings, m_rgParamColMap, m_cParams, m_rgParamInfo, m_pNamesBuffer);
//Replace text in CommandObject With select statment with no parameters.
TEST_CHECK(m_hr = m_pTable->CreateSQLStmt(SELECT_COLLISTFROMTBL, NULL, &pwszSqlSelectAllFromTbl, NULL, NULL ), S_OK);
// Set command text
TEST_CHECK(m_pICommandText->SetCommandText(DBGUID_DBSQL , pwszSqlSelectAllFromTbl), S_OK);
// Call ICommandPrepare.
TEST_CHECK(m_pICommandPrepare->Prepare(1), S_OK);
FreeDescParams();
TEST_CHECK(m_pICmdWParams->GetParameterInfo(&m_cParams, &m_rgParamInfo, &m_pNamesBuffer), S_OK);
if (m_bSetParameterInfo)
{
// In case we did SetParameterInfo, expect those parameters.
TEST_COMPARE (m_cParams, m_cDbParamBindInfo );
}
else
{
// We have not called SetParameterInfo.
// Text is prepared with no parameters expect 0.
TEST_COMPARE (m_cParams, 0);
}
fResult = TRUE;
CLEANUP:
// Restore original text.
if (!CHECK (m_pICommandText->SetCommandText(DBGUID_DBSQL , m_pwszSqlInsertAllWithParams), S_OK))
fResult = FALSE;
FREE_DATA (pwszSqlSelectAllFromTbl);
FreeDescParams();
return (fResult) ? TEST_PASS : TEST_FAIL;
}
// }}
// {{ TCW_VAR_PROTOTYPE(5)
//*-----------------------------------------------------------------------
// @mfunc No command object.
//
// @rdesc TEST_PASS or TEST_FAIL
//
int TCGetParameterInfo_Rowset::Variation_5()
{
BOOL fResult = FALSE;
ICommandWithParameters *pICommandWithParameters = NULL;
ICommandText *pLocalICommandText = NULL;
HRESULT hr = E_FAIL;
if (!VerifyInterface(m_pIEmptyCommand, IID_ICommandWithParameters,
COMMAND_INTERFACE, (IUnknown **)&pICommandWithParameters))
{
goto CLEANUP;
}
// For m_pIEmptyCommand No command text interface is present.
// Describe parameters should return DB_E_NOTPREPARED.
hr = pICommandWithParameters->GetParameterInfo(&m_cParams, &m_rgParamInfo, &m_pNamesBuffer);
if (m_bSetParameterInfo)
{
// Provider doesn't derive and requires setting parameter information.
TEST_CHECK(hr, DB_E_PARAMUNAVAILABLE);
}
else
{
// Provider can derive
// Some providers may (legally) return DB_E_NOTPREPARED since this was in the spec for this
// condition under GetParameterInfo. But the front matter command state table shows
// DB_E_NOCOMMAND. So we will warn for DB_E_NOTPREPARED.
if (hr == DB_E_NOTPREPARED)
TESTW_(hr, DB_E_NOCOMMAND);
else
{
TEST_CHECK(hr, DB_E_NOCOMMAND);
}
}
// Now get text interface but do not set text.
// DB_E_NOTPREPARED should be returned.
if (!VerifyInterface(m_pIEmptyCommand, IID_ICommandText,
COMMAND_INTERFACE, (IUnknown **)&pLocalICommandText))
{
goto CLEANUP;
}
hr = pICommandWithParameters->GetParameterInfo(&m_cParams, &m_rgParamInfo, &m_pNamesBuffer);
if (m_bSetParameterInfo)
{
// Provider doesn't derive and requires setting parameter information.
TEST_CHECK(hr, DB_E_PARAMUNAVAILABLE);
}
else
{
// Provider can derive
// Some providers may (legally) return DB_E_NOTPREPARED since this was in the spec for this
// condition under GetParameterInfo. But the front matter command state table shows
// DB_E_NOCOMMAND. So we will warn for DB_E_NOTPREPARED.
if (hr == DB_E_NOTPREPARED)
TESTW_(hr, DB_E_NOCOMMAND);
else
{
TEST_CHECK(hr, DB_E_NOCOMMAND);
}
}
fResult = TRUE;
CLEANUP:
if (pICommandWithParameters)
pICommandWithParameters->Release();
if (pLocalICommandText)
pLocalICommandText->Release();
return (fResult) ? TEST_PASS : TEST_FAIL;
}
// }}
// {{ TCW_VAR_PROTOTYPE(6)
//*-----------------------------------------------------------------------
// @mfunc Call GetParameterInfo twice.
//
// @rdesc TEST_PASS or TEST_FAIL
//
int TCGetParameterInfo_Rowset::Variation_6()
{
BOOL fResult = FALSE;
// Just to be sure set the appropriate text. Restore original text.
TEST_CHECK (m_pICommandText->SetCommandText(DBGUID_DBSQL , m_pwszSqlInsertAllWithParams), S_OK);
// Call ICommandPrepare.
TEST_CHECK(m_pICommandPrepare->Prepare(1), S_OK);
TEST_CHECK(m_pICmdWParams->GetParameterInfo(&m_cParams, &m_rgParamInfo, &m_pNamesBuffer), S_OK);
CompareDBParamInfo(m_cBindings, m_rgParamColMap, m_cParams, m_rgParamInfo, m_pNamesBuffer);
FreeDescParams();
TEST_CHECK(m_pICmdWParams->GetParameterInfo(&m_cParams, &m_rgParamInfo, &m_pNamesBuffer), S_OK);
// Second call should yield the same results.
CompareDBParamInfo(m_cBindings, m_rgParamColMap, m_cParams, m_rgParamInfo, m_pNamesBuffer);
fResult = TRUE;
CLEANUP:
FreeDescParams();
return (fResult) ? TEST_PASS : TEST_FAIL;
}
// }}
// {{ TCW_VAR_PROTOTYPE(7)
//*-----------------------------------------------------------------------
// @mfunc Parameters (NULL, NULL, Valid
//
// @rdesc TEST_PASS or TEST_FAIL
//
int TCGetParameterInfo_Rowset::Variation_7()
{
//
FreeDescParams();
if (!CHECK(m_pICmdWParams->GetParameterInfo(NULL, NULL, &m_pNamesBuffer),
E_INVALIDARG))
{
FreeDescParams();
return TEST_FAIL;
}
return TEST_PASS;
}
// }}
// {{ TCW_VAR_PROTOTYPE(8)
//*-----------------------------------------------------------------------
// @mfunc Parameters(NULL, Valid, Valid
//
// @rdesc TEST_PASS or TEST_FAIL
//
int TCGetParameterInfo_Rowset::Variation_8()
{
FreeDescParams();
if (!CHECK(m_pICmdWParams->GetParameterInfo(NULL, &m_rgParamInfo, &m_pNamesBuffer),
E_INVALIDARG))
{
FreeDescParams();
return TEST_FAIL;
}
return TEST_PASS;
}
// }}
// {{ TCW_VAR_PROTOTYPE(9)
//*-----------------------------------------------------------------------
// @mfunc Parameters (Valid, NULL, Valid
//
// @rdesc TEST_PASS or TEST_FAIL
//
int TCGetParameterInfo_Rowset::Variation_9()
{
FreeDescParams();
if (!CHECK(m_pICmdWParams->GetParameterInfo(&m_cParams, NULL, &m_pNamesBuffer),
E_INVALIDARG))
{
FreeDescParams();
return TEST_FAIL;
}
return TEST_PASS;
}
// }}
// {{ TCW_VAR_PROTOTYPE(10)
//*-----------------------------------------------------------------------
// @mfunc Parameters (NULL, NULL, NULL
//
// @rdesc TEST_PASS or TEST_FAIL
//
int TCGetParameterInfo_Rowset::Variation_10()
{
FreeDescParams();
if (!CHECK(m_pICmdWParams->GetParameterInfo(NULL, NULL, NULL),
E_INVALIDARG))
{
FreeDescParams();
return TEST_FAIL;
}
return TEST_PASS;
}
// }}
// {{ TCW_VAR_PROTOTYPE(11)
//*-----------------------------------------------------------------------
// @mfunc Parameters (Valid, Valid, NULL
//
// @rdesc TEST_PASS or TEST_FAIL
//
int TCGetParameterInfo_Rowset::Variation_11()
{
FreeDescParams();
if (!CHECK(m_pICmdWParams->GetParameterInfo(&m_cParams, &m_rgParamInfo, NULL),
S_OK))
{
FreeDescParams();
return TEST_FAIL;
}
return TEST_PASS;
}
// }}
// {{ TCW_VAR_PROTOTYPE(12)
//--------------------------------------------------------------------
// @mfunc Call GetParameterInfo after SetCommandText for stored proc with INPUT params
//
// @rdesc TEST_PASS or TEST_FAIL
//
int TCGetParameterInfo_Rowset::Variation_12()
{
BOOL fResult = TRUE;
ULONG cParams, cParamSets = 1;
DBLENGTH cbRowSize;
DBCOUNTITEM ulRowNum = 1;
DBBINDING * pBINDING=NULL;
DB_UPARAMS * pParamOrdinals=NULL;
DBPARAMBINDINFO * pPARAMBIND=NULL;
WCHAR * pwszCreateProcStmt=NULL;
WCHAR * pwszExecProcStmt=NULL;
WCHAR * pwszExecStmt=NULL;
WCHAR * pwszProcName=NULL;
BYTE * pData=NULL;
ParamStruct * pParamAll=NULL;
DBORDINAL cColumns = 0;
DB_LORDINAL * prgColumnsOrd = NULL;
BOOL fCanDerive = FALSE;
HRESULT hrSetProp = E_FAIL;
if (!m_fProcedureSupport)
{
odtLog << "Procedures not supported.\n";
return TEST_SKIPPED;
}
// Oracle cannot return a rowset from within a stored proc at this time.
if (g_bOracle)
{
odtLog << L"Oracle cannot return a rowset from a stored proc.\n";
return TEST_SKIPPED;
}
// Create the syntax and binding for a stored proc with input parameters
ABORT_COMPARE(CreateProcBindings(
T_EXEC_PROC_SELECT_IN, // [IN] Proc type, regular proc or function (has return value)
TRUE, // [IN] If TRUE then we add parameter names to the rgParamBindInfo
cParamSets, // [IN] Number of sets of parameters to be created
DBTYPE_I2, // [IN] Return parameter type
ulRowNum, // [IN] Row number in table to select, insert, or update
&cParams, // [OUT] Count of params created
&cbRowSize, // [OUT] Count of bytes for a single row of parameters
&pBINDING, // [OUT] Binding array for CreateAccessor
&pParamOrdinals,
&pPARAMBIND, // [OUT] rgParamBindInfo for SetParameterInfo
&pwszCreateProcStmt, // [OUT] SQL stmt to create the stored proc
&pwszExecProcStmt, // [OUT] SQL stmt to execute the stored proc
&pwszExecStmt, // [OUT] SQL stmt to execute without stored proc
&pwszProcName, // [OUT] Name of procedure
&pData, // [OUT] Pointer to data for the parameters
&pParamAll,
&cColumns,
&prgColumnsOrd
), TRUE);
// Some providers can't retrieve BLOB data without this property or IRowsetLocate on
if (SupportedProperty(DBPROP_ACCESSORDER, DBPROPSET_ROWSET, m_pThisTestModule->m_pIUnknown,SESSION_INTERFACE))
hrSetProp = SetRowsetProperty(m_pICommand, DBPROPSET_ROWSET, DBPROP_ACCESSORDER, (LONG_PTR)DBPROPVAL_AO_RANDOM);
// Set up to execute the stored proc
ABORT_CHECK(PrepareForExecute(pwszExecProcStmt, cParams, pParamOrdinals, pPARAMBIND,
&fCanDerive, pwszProcName, pwszCreateProcStmt), S_OK);
FAIL_VAR(ValidateGetParameterInfo(cParams, cParamSets, ulRowNum, pParamOrdinals, pPARAMBIND,
pBINDING, cbRowSize, pParamAll, pData, ROWSET_MAYBE, cColumns, prgColumnsOrd, fCanDerive), S_OK);
CLEANUP:
// If we set RANDOM REQUIRED above we need to set back to default
if (hrSetProp == S_OK)
CHECK(SetRowsetPropertyDefault(DBPROP_ACCESSORDER), S_OK);
DropStoredProcedure(m_pICommandText, pwszProcName);
// Free the buffers we got from GetParameterInfo
PROVIDER_FREE(pBINDING);
PROVIDER_FREE(pParamOrdinals);
PROVIDER_FREE(pPARAMBIND);
PROVIDER_FREE(pwszCreateProcStmt);
PROVIDER_FREE(pwszExecProcStmt);
PROVIDER_FREE(pwszExecStmt);
PROVIDER_FREE(pwszProcName);
PROVIDER_FREE(pData);
::FreeParameterNames(cParams, pParamAll);
PROVIDER_FREE(pParamAll);
return fResult ? TEST_PASS : TEST_FAIL;
}
// }}
// {{ TCW_VAR_PROTOTYPE(13)
//*-----------------------------------------------------------------------
// @mfunc Call GetParameterInfo after calling SetCommandText with a stored procedure with OUTPUT params
//
// @rdesc TEST_PASS or TEST_FAIL
//
int TCGetParameterInfo_Rowset::Variation_13()
{
BOOL fResult = TRUE;
ULONG cParams, cParamSets = 1;
DBLENGTH cbRowSize;
DBCOUNTITEM ulRowNum = 2;
DBBINDING * pBINDING=NULL;
DB_UPARAMS * pParamOrdinals=NULL;
DBPARAMBINDINFO * pPARAMBIND=NULL;
WCHAR * pwszCreateProcStmt=NULL;
WCHAR * pwszExecProcStmt=NULL;
WCHAR * pwszExecStmt=NULL;
WCHAR * pwszProcName=NULL;
BYTE * pData=NULL;
ParamStruct * pParamAll=NULL;
ULONG cColumns = 0;
DB_LORDINAL * prgColumnsOrd = NULL;
BOOL fCanDerive = FALSE;
if (!m_fProcedureSupport)
{
odtLog << "Procedures not supported.\n";
return TEST_SKIPPED;
}
if (g_ulOutParamsSupported == DBPROPVAL_OA_NOTSUPPORTED)
{
odtLog << "Output parameters not supported.\n";
return TEST_SKIPPED;
}
if (g_bOracle)
{
odtLog << "Skipping comparison of ulParamSize, bPrecision, and bScale for Oracle DBMS.\n";
}
// Create the syntax and binding for a stored proc with output parameters
ABORT_COMPARE(CreateProcBindings(
T_EXEC_PROC_SELECT_OUT, // [IN] Proc type, regular proc or function (has return value)
TRUE, // [IN] If TRUE then we add parameter names to the rgParamBindInfo
1, // [IN] Number of sets of parameters to be created
DBTYPE_I2, // [IN] Return parameter type
ulRowNum, // [IN] Row number in table to select, insert, or update
&cParams, // [OUT] Count of params created
&cbRowSize, // [OUT] Count of bytes for a single row of parameters
&pBINDING, // [OUT] Binding array for CreateAccessor
&pParamOrdinals,
&pPARAMBIND, // [OUT] rgParamBindInfo for SetParameterInfo
&pwszCreateProcStmt, // [OUT] SQL stmt to create the stored proc
&pwszExecProcStmt, // [OUT] SQL stmt to execute the stored proc
&pwszExecStmt, // [OUT] SQL stmt to execute without stored proc
&pwszProcName, // [OUT] Name of procedure
&pData, // [OUT] Pointer to data for the parameters
&pParamAll
), TRUE);
// Set up to execute the stored proc
ABORT_CHECK(PrepareForExecute(pwszExecProcStmt, cParams, pParamOrdinals, pPARAMBIND,
&fCanDerive, pwszProcName, pwszCreateProcStmt), S_OK);
FAIL_VAR(ValidateGetParameterInfo(cParams, cParamSets, ulRowNum, pParamOrdinals, pPARAMBIND,
pBINDING, cbRowSize, pParamAll, pData, ROWSET_NONE, cColumns, prgColumnsOrd, fCanDerive), S_OK);
CLEANUP:
DropStoredProcedure(m_pICommandText, pwszProcName);
// Free the buffers we got from GetParameterInfo
PROVIDER_FREE(pBINDING);
PROVIDER_FREE(pParamOrdinals);
PROVIDER_FREE(pPARAMBIND);
PROVIDER_FREE(pwszCreateProcStmt);
PROVIDER_FREE(pwszExecProcStmt);
PROVIDER_FREE(pwszExecStmt);
PROVIDER_FREE(pwszProcName);
PROVIDER_FREE(pData);
::FreeParameterNames(cParams, pParamAll);
PROVIDER_FREE(pParamAll);
return fResult ? TEST_PASS : TEST_FAIL;
}
// }}
// {{ TCW_VAR_PROTOTYPE(14)
//*-----------------------------------------------------------------------
// @mfunc Call GetParamInfo after calling SetCommandText with a stored procedure with IN/OUT params.
//
// @rdesc TEST_PASS or TEST_FAIL
//
int TCGetParameterInfo_Rowset::Variation_14()
{
BOOL fResult = TRUE;
ULONG cParams, cParamSets = 1;
DBLENGTH cbRowSize;
DBCOUNTITEM ulRowNum = 1;
DBBINDING * pBINDING=NULL;
DB_UPARAMS * pParamOrdinals=NULL;
DBPARAMBINDINFO * pPARAMBIND=NULL;
WCHAR * pwszCreateProcStmt=NULL;
WCHAR * pwszExecProcStmt=NULL;
WCHAR * pwszExecStmt=NULL;
WCHAR * pwszProcName=NULL;
BYTE * pData=NULL;
ParamStruct * pParamAll=NULL;
ULONG cColumns = 0;
DB_LORDINAL * prgColumnsOrd = NULL;
BOOL fCanDerive = FALSE;
if (!m_fProcedureSupport)
{
odtLog << "Procedures not supported.\n";
return TEST_SKIPPED;
}
if (g_ulOutParamsSupported == DBPROPVAL_OA_NOTSUPPORTED)
{
odtLog << "Output parameters not supported.\n";
return TEST_SKIPPED;
}
if (g_bOracle)
{
odtLog << "Skipping comparison of ulParamSize, bPrecision, and bScale for Oracle DBMS.\n";
}
// Create the syntax and binding for a stored proc with output parameters
TEST_COMPARE(CreateProcBindings(
T_EXEC_PROC_SELECT_INOUT, // [IN] Proc type, regular proc or function (has return value)
TRUE, // [IN] If TRUE then we add parameter names to the rgParamBindInfo
1, // [IN] Number of sets of parameters to be created
DBTYPE_I2, // [IN] Return parameter type
1, // [IN] Row number in table to select, insert, or update
&cParams, // [OUT] Count of params created
&cbRowSize, // [OUT] Count of bytes for a single row of parameters
&pBINDING, // [OUT] Binding array for CreateAccessor
&pParamOrdinals,
&pPARAMBIND, // [OUT] rgParamBindInfo for SetParameterInfo
&pwszCreateProcStmt, // [OUT] SQL stmt to create the stored proc
&pwszExecProcStmt, // [OUT] SQL stmt to execute the stored proc
&pwszExecStmt, // [OUT] SQL stmt to execute without stored proc
&pwszProcName, // [OUT] Name of procedure
&pData, // [OUT] Pointer to data for the parameters
&pParamAll
), TRUE);
// Set up to execute the stored proc
ABORT_CHECK(PrepareForExecute(pwszExecProcStmt, cParams, pParamOrdinals, pPARAMBIND,
&fCanDerive, pwszProcName, pwszCreateProcStmt), S_OK);
FAIL_VAR(ValidateGetParameterInfo(cParams, cParamSets, ulRowNum, pParamOrdinals, pPARAMBIND,
pBINDING, cbRowSize, pParamAll, pData, ROWSET_NONE, cColumns, prgColumnsOrd, fCanDerive,
VERIFY_USE_PDATA), S_OK);
CLEANUP:
DropStoredProcedure(m_pICommandText, pwszProcName);
// Free the buffers we got from GetParameterInfo
PROVIDER_FREE(pBINDING);
PROVIDER_FREE(pParamOrdinals);
PROVIDER_FREE(pPARAMBIND);
PROVIDER_FREE(pwszCreateProcStmt);
PROVIDER_FREE(pwszExecProcStmt);
PROVIDER_FREE(pwszExecStmt);
PROVIDER_FREE(pwszProcName);
PROVIDER_FREE(pData);
::FreeParameterNames(cParams, pParamAll);
PROVIDER_FREE(pParamAll);
return fResult ? TEST_PASS : TEST_FAIL;
}
// }}
// {{ TCW_VAR_PROTOTYPE(15)
//*-----------------------------------------------------------------------
// @mfunc Call GetParameterInfo after SetCommandText with StoredProc with VeryLongParamNames
//
// @rdesc TEST_PASS or TEST_FAIL
//
int TCGetParameterInfo_Rowset::Variation_15()
{
BOOL fResult = TRUE;
ULONG cParams, cParamSets = 1;
DBLENGTH cbRowSize;
DBCOUNTITEM ulRowNum = 1;
DBBINDING * pBINDING=NULL;
DB_UPARAMS * pParamOrdinals=NULL;
DBPARAMBINDINFO * pPARAMBIND=NULL;
WCHAR * pwszCreateProcStmt=NULL;
WCHAR * pwszExecProcStmt=NULL;
WCHAR * pwszExecStmt=NULL;
WCHAR * pwszProcName=NULL;
BYTE * pData=NULL;
ParamStruct * pParamAll=NULL;
ULONG cColumns = 0;
DB_LORDINAL * prgColumnsOrd = NULL;
BOOL fCanDerive = FALSE;
HRESULT hr = E_FAIL;
if (!m_fProcedureSupport)
{
odtLog << "Procedures not supported.\n";
return TEST_SKIPPED;
}
if (g_ulOutParamsSupported == DBPROPVAL_OA_NOTSUPPORTED)
{
odtLog << "Output parameters not supported.\n";
return TEST_SKIPPED;
}
if (g_bOracle)
{
odtLog << "Skipping comparison of ulParamSize, bPrecision, and bScale for Oracle DBMS.\n";
}
// Create the syntax and binding for a stored proc with output parameters
TEST_COMPARE(CreateProcBindings(
T_EXEC_PROC_SELECT_INOUT, // [IN] Proc type, regular proc or function (has return value)
TRUE, // [IN] If TRUE then we add parameter names to the rgParamBindInfo
1, // [IN] Number of sets of parameters to be created
DBTYPE_I2, // [IN] Return parameter type
1, // [IN] Row number in table to select, insert, or update
&cParams, // [OUT] Count of params created
&cbRowSize, // [OUT] Count of bytes for a single row of parameters
&pBINDING, // [OUT] Binding array for CreateAccessor
&pParamOrdinals,
&pPARAMBIND, // [OUT] rgParamBindInfo for SetParameterInfo
&pwszCreateProcStmt, // [OUT] SQL stmt to create the stored proc
&pwszExecProcStmt, // [OUT] SQL stmt to execute the stored proc
&pwszExecStmt, // [OUT] SQL stmt to execute without stored proc
&pwszProcName, // [OUT] Name of procedure
&pData, // [OUT] Pointer to data for the parameters
&pParamAll,
NULL,
NULL,
NULL,
CREATE_LONG_NAMES
), TRUE);
// Set up to execute the stored proc
hr = CreateStoredProc(m_pICommandText, pwszProcName, pwszCreateProcStmt, FALSE);
if (FAILED(hr))
{
fResult = CHECK(hr, DB_E_ERRORSINCOMMAND);
goto CLEANUP;
}
ABORT_CHECK(PrepareForExecute(pwszExecProcStmt, cParams, pParamOrdinals, pPARAMBIND,
&fCanDerive, pwszProcName, pwszCreateProcStmt), S_OK);
FAIL_VAR(ValidateGetParameterInfo(cParams, cParamSets, ulRowNum, pParamOrdinals, pPARAMBIND,
pBINDING, cbRowSize, pParamAll, pData, ROWSET_NONE, cColumns, prgColumnsOrd, fCanDerive,
VERIFY_USE_PDATA), S_OK);
CLEANUP:
DropStoredProcedure(m_pICommandText, pwszProcName);
// Free the buffers we got from GetParameterInfo
PROVIDER_FREE(pBINDING);
PROVIDER_FREE(pParamOrdinals);
PROVIDER_FREE(pPARAMBIND);
PROVIDER_FREE(pwszCreateProcStmt);
PROVIDER_FREE(pwszExecProcStmt);
PROVIDER_FREE(pwszExecStmt);
PROVIDER_FREE(pwszProcName);
PROVIDER_FREE(pData);
::FreeParameterNames(cParams, pParamAll);
PROVIDER_FREE(pParamAll);
return fResult ? TEST_PASS : TEST_FAIL;
}
// }}
// {{ TCW_VAR_PROTOTYPE(16)
//*-----------------------------------------------------------------------
// @mfunc Call GetParamInfo with a stored procedure which is returning a value.
//
// @rdesc TEST_PASS or TEST_FAIL
//
int TCGetParameterInfo_Rowset::Variation_16()
{
BOOL fResult = TRUE;
ULONG cParams, cParamSets = 1;
DBLENGTH cbRowSize;
DBCOUNTITEM ulRowNum = 1;
DBBINDING * pBINDING=NULL;
DB_UPARAMS * pParamOrdinals=NULL;
DBPARAMBINDINFO * pPARAMBIND=NULL;
WCHAR * pwszCreateProcStmt=NULL;
WCHAR * pwszExecProcStmt=NULL;
WCHAR * pwszExecStmt=NULL;
WCHAR * pwszProcName=NULL;
BYTE * pData=NULL;
ParamStruct * pParamAll=NULL;
ULONG cColumns = 0;
DB_LORDINAL * prgColumnsOrd = NULL;
BOOL fCanDerive = FALSE;
WCHAR * pwszRetTypeName = NULL;
DBTYPE wRetType = DBTYPE_EMPTY;
ULONG iType;
if (!m_fProcedureSupport)
{
odtLog << "Procedures not supported.\n";
return TEST_SKIPPED;
}
if (g_ulOutParamsSupported == DBPROPVAL_OA_NOTSUPPORTED)
{
odtLog << "Output parameters not supported.\n";
return TEST_SKIPPED;
}
if (g_bOracle)
{
odtLog << "Skipping comparison of ulParamSize, bPrecision, and bScale for Oracle DBMS.\n";
}
// It is provider specific what types can be returned from a sproc. Retrieve a valid
// type name for this provider.
if (!(pwszRetTypeName = m_Syntax.GetSyntax(T_RET_TYPE_NAME)))
goto CLEANUP;
// Find the actual type in the standard type name list
for (iType = 0; iType < NUMELEM(g_rgStdParamBindInfo); iType++)
{
if (!wcscmp(pwszRetTypeName, g_rgStdParamBindInfo[iType].wszStdTypeName))
{
wRetType = g_rgStdParamBindInfo[iType].wType;
break;
}
}
ABORT_COMPARE(wRetType != DBTYPE_EMPTY, TRUE);
// Create the syntax and binding for a stored proc with output parameters
ABORT_COMPARE(CreateProcBindings(
T_EXEC_PROC_SELECT_OUT_RET, // [IN] Proc type, regular proc or function (has return value)
TRUE, // [IN] If TRUE then we add parameter names to the rgParamBindInfo
1, // [IN] Number of sets of parameters to be created
wRetType, // [IN] Return parameter type
1, // [IN] Row number in table to select, insert, or update
&cParams, // [OUT] Count of params created
&cbRowSize, // [OUT] Count of bytes for a single row of parameters
&pBINDING, // [OUT] Binding array for CreateAccessor
&pParamOrdinals,
&pPARAMBIND, // [OUT] rgParamBindInfo for SetParameterInfo
&pwszCreateProcStmt, // [OUT] SQL stmt to create the stored proc
&pwszExecProcStmt, // [OUT] SQL stmt to execute the stored proc
&pwszExecStmt, // [OUT] SQL stmt to execute without stored proc
&pwszProcName, // [OUT] Name of procedure
&pData, // [OUT] Pointer to data for the parameters
&pParamAll
), TRUE);
// Set up to execute the stored proc
ABORT_CHECK(PrepareForExecute(pwszExecProcStmt, cParams, pParamOrdinals, pPARAMBIND,
&fCanDerive, pwszProcName, pwszCreateProcStmt, TRUE), S_OK);
FAIL_VAR(ValidateGetParameterInfo(cParams, cParamSets, ulRowNum, pParamOrdinals, pPARAMBIND,
pBINDING, cbRowSize, pParamAll, pData, ROWSET_NONE, cColumns, prgColumnsOrd, fCanDerive), S_OK);
CLEANUP:
DropStoredProcedure(m_pICommandText, pwszProcName, TRUE);
// Free the buffers we got from GetParameterInfo
PROVIDER_FREE(pBINDING);
PROVIDER_FREE(pParamOrdinals);
PROVIDER_FREE(pPARAMBIND);
PROVIDER_FREE(pwszCreateProcStmt);
PROVIDER_FREE(pwszExecProcStmt);
PROVIDER_FREE(pwszExecStmt);
PROVIDER_FREE(pwszProcName);
PROVIDER_FREE(pData);
::FreeParameterNames(cParams, pParamAll);
PROVIDER_FREE(pParamAll);
PROVIDER_FREE(pwszRetTypeName);
return fResult ? TEST_PASS : TEST_FAIL;
}
// }}
// {{ TCW_VAR_PROTOTYPE(17)
//*-----------------------------------------------------------------------
// @mfunc GetParameterInfo with text set to different types of SQL statments with funny characters to test parsing code of Dev.
//
// @rdesc TEST_PASS or TEST_FAIL
//
int TCGetParameterInfo_Rowset::Variation_17()
{
//@todo More case of strange procedure names can be added if Execute(Params)chokes on this one.
BOOL fResult = TRUE;
ULONG cParams, cParamSets = 1;
DBLENGTH cbRowSize;
DBCOUNTITEM ulRowNum = 1;
DBBINDING * pBINDING=NULL;
DB_UPARAMS * pParamOrdinals=NULL;
DBPARAMBINDINFO * pPARAMBIND=NULL;
WCHAR * pwszCreateProcStmt=NULL;
WCHAR * pwszExecProcStmt=NULL;
WCHAR * pwszExecStmt=NULL;
WCHAR * pwszProcNameRoot=L"\"''Stra# _* & % n ";
WCHAR * pwszProcName = NULL;
WCHAR * pwszRootName = NULL;
BYTE * pData=NULL;
ParamStruct * pParamAll=NULL;
ULONG cColumns = 0;
DB_LORDINAL * prgColumnsOrd = NULL;
BOOL fCanDerive = FALSE;
DBLITERALINFO* pTableLiteral = m_pTable->GetLiteralInfo(DBLITERAL_TABLE_NAME);
if (!m_fProcedureSupport)
{
odtLog << "Procedures not supported.\n";
return TEST_SKIPPED;
}
if (g_ulOutParamsSupported == DBPROPVAL_OA_NOTSUPPORTED)
{
odtLog << "Output parameters not supported.\n";
return TEST_SKIPPED;
}
if (g_bOracle)
{
odtLog << "Skipping comparison of ulParamSize, bPrecision, and bScale for Oracle DBMS.\n";
pwszProcNameRoot = L"\"Stra# _* & % n ";
}
// Allocate a block for the final proc name
SAFE_ALLOC(pwszProcName, WCHAR, pTableLiteral->cchMaxLen);
// Create a name containing the funny characters, but also a unique portion to prevent
// duplicate names when two tests running at once
pwszRootName = MakeObjectName(pwszProcNameRoot, pTableLiteral->cchMaxLen-1); // -1 to allow for closing quote
TEST_PTR(pwszRootName);
// In Intl scenarios privlib will ignore the pwszProcNameRoot and instead create a name
// containing int'l chars. This is not quite what we want here
if ( GetModInfo()->GetLocaleInfo() && GetModInfo()->GetUseIntlIdentifier() )
{
wcsncpy(pwszProcName, pwszProcNameRoot, pTableLiteral->cchMaxLen);
wcsncpy(pwszProcName+wcslen(pwszProcNameRoot), pwszRootName, pTableLiteral->cchMaxLen - wcslen(pwszProcNameRoot));
}
else
{
// Copy the root name
wcscpy(pwszProcName, pwszRootName);
}
// Add trailing double quote
wcscat(pwszProcName,L"\"");
// Create the syntax and binding for a stored proc with output parameters
ABORT_COMPARE(CreateProcBindings(
T_EXEC_PROC_SELECT_OUT, // [IN] Proc type, regular proc or function (has return value)
TRUE, // [IN] If TRUE then we add parameter names to the rgParamBindInfo
1, // [IN] Number of sets of parameters to be created
DBTYPE_I4, // [IN] Return parameter type
1, // [IN] Row number in table to select, insert, or update
&cParams, // [OUT] Count of params created
&cbRowSize, // [OUT] Count of bytes for a single row of parameters
&pBINDING, // [OUT] Binding array for CreateAccessor
&pParamOrdinals,
&pPARAMBIND, // [OUT] rgParamBindInfo for SetParameterInfo
&pwszCreateProcStmt, // [OUT] SQL stmt to create the stored proc
&pwszExecProcStmt, // [OUT] SQL stmt to execute the stored proc
&pwszExecStmt, // [OUT] SQL stmt to execute without stored proc
&pwszProcName, // [OUT] Name of procedure
&pData, // [OUT] Pointer to data for the parameters
&pParamAll
), TRUE);
// Set up to execute the stored proc
ABORT_CHECK(PrepareForExecute(pwszExecProcStmt, cParams, pParamOrdinals, pPARAMBIND,
&fCanDerive, pwszProcName, pwszCreateProcStmt), S_OK);
FAIL_VAR(ValidateGetParameterInfo(cParams, cParamSets, ulRowNum, pParamOrdinals, pPARAMBIND,
pBINDING, cbRowSize, pParamAll, pData, ROWSET_NONE, cColumns, prgColumnsOrd, fCanDerive), S_OK);
CLEANUP:
DropStoredProcedure(m_pICommandText, pwszProcName);
// Free the buffers we got from GetParameterInfo
PROVIDER_FREE(pBINDING);
PROVIDER_FREE(pParamOrdinals);
PROVIDER_FREE(pPARAMBIND);
PROVIDER_FREE(pwszCreateProcStmt);
PROVIDER_FREE(pwszExecProcStmt);
PROVIDER_FREE(pwszExecStmt);
PROVIDER_FREE(pData);
::FreeParameterNames(cParams, pParamAll);
PROVIDER_FREE(pParamAll);
SAFE_FREE(pwszProcName);
SAFE_FREE(pwszRootName);
return fResult ? TEST_PASS : TEST_FAIL;
}
// }}
// {{ TCW_VAR_PROTOTYPE(18)
//*-----------------------------------------------------------------------
// @mfunc Stored procedure execution on two different command objects.
//
// @rdesc TEST_PASS or TEST_FAIL
//
int TCGetParameterInfo_Rowset::Variation_18()
{
BOOL fResult = TRUE;
ULONG cParams, cParamSets = 1;
DBLENGTH cbRowSize;
DBCOUNTITEM ulRowNum = 1;
DBBINDING * pBINDING=NULL;
DB_UPARAMS * pParamOrdinals=NULL;
DBPARAMBINDINFO * pPARAMBIND=NULL;
WCHAR * pwszCreateProcStmt=NULL;
WCHAR * pwszExecProcStmt=NULL;
WCHAR * pwszExecStmt=NULL;
WCHAR * pwszProcName=NULL;
BYTE * pData=NULL;
ParamStruct * pParamAll=NULL;
ULONG cColumns = 0;
DB_LORDINAL * prgColumnsOrd = NULL;
ICommand * pICommand2 = NULL;
ICommandText * pICommandText2 = NULL;
ICommandPrepare * pICommandPrepare2 = NULL;
BOOL fCanDerive = FALSE;
if (!m_fProcedureSupport)
{
odtLog << "Procedures not supported.\n";
return TEST_SKIPPED;
}
if (g_ulOutParamsSupported == DBPROPVAL_OA_NOTSUPPORTED)
{
odtLog << "Output parameters not supported.\n";
return TEST_SKIPPED;
}
if (g_bOracle)
{
odtLog << "Skipping comparison of ulParamSize, bPrecision, and bScale for Oracle DBMS.\n";
}
// Create the syntax and binding for a stored proc with output parameters
ABORT_COMPARE(CreateProcBindings(
T_EXEC_PROC_SELECT_OUT, // [IN] Proc type, regular proc or function (has return value)
TRUE, // [IN] If TRUE then we add parameter names to the rgParamBindInfo
1, // [IN] Number of sets of parameters to be created
DBTYPE_I4, // [IN] Return parameter type
1, // [IN] Row number in table to select, insert, or update
&cParams, // [OUT] Count of params created
&cbRowSize, // [OUT] Count of bytes for a single row of parameters
&pBINDING, // [OUT] Binding array for CreateAccessor
&pParamOrdinals,
&pPARAMBIND, // [OUT] rgParamBindInfo for SetParameterInfo
&pwszCreateProcStmt, // [OUT] SQL stmt to create the stored proc
&pwszExecProcStmt, // [OUT] SQL stmt to execute the stored proc
&pwszExecStmt, // [OUT] SQL stmt to execute without stored proc
&pwszProcName, // [OUT] Name of procedure
&pData, // [OUT] Pointer to data for the parameters
&pParamAll
), TRUE);
// Set up to execute the stored proc
ABORT_CHECK(PrepareForExecute(pwszExecProcStmt, cParams, pParamOrdinals, pPARAMBIND,
&fCanDerive, pwszProcName, pwszCreateProcStmt), S_OK);
FAIL_VAR(ValidateGetParameterInfo(cParams, cParamSets, ulRowNum, pParamOrdinals, pPARAMBIND,
pBINDING, cbRowSize, pParamAll, pData, ROWSET_NONE, cColumns, prgColumnsOrd, fCanDerive), S_OK);
// Create a second command object.
ABORT_CHECK(m_pIDBCreateCommand->CreateCommand(NULL, IID_ICommand, (IUnknown **)&pICommand2), S_OK);
// Get the Interface for a Command Text Object.
ABORT_COMPARE(VerifyInterface(pICommand2, IID_ICommandText,
COMMAND_INTERFACE,(IUnknown **)&pICommandText2), TRUE);
VerifyInterface(pICommand2, IID_ICommandPrepare,
COMMAND_INTERFACE,(IUnknown **)&pICommandPrepare2);
ABORT_CHECK(pICommandText2->SetCommandText(DBGUID_DBSQL, pwszExecProcStmt), S_OK);
// Prepare if supported
if (pICommandPrepare2)
ABORT_CHECK(pICommandPrepare2->Prepare(1), S_OK);
FAIL_VAR(ValidateGetParameterInfo(cParams, cParamSets, ulRowNum, pParamOrdinals, pPARAMBIND,
pBINDING, cbRowSize, pParamAll, pData, ROWSET_NONE, cColumns, prgColumnsOrd, fCanDerive,
VERIFY_USE_TABLE, pICommand2), S_OK);
CLEANUP:
DropStoredProcedure(m_pICommandText, pwszProcName);
// Free the buffers we got from GetParameterInfo
PROVIDER_FREE(pBINDING);
PROVIDER_FREE(pParamOrdinals);
PROVIDER_FREE(pPARAMBIND);
PROVIDER_FREE(pwszCreateProcStmt);
PROVIDER_FREE(pwszExecProcStmt);
PROVIDER_FREE(pwszExecStmt);
PROVIDER_FREE(pwszProcName);
PROVIDER_FREE(pData);
::FreeParameterNames(cParams, pParamAll);
PROVIDER_FREE(pParamAll);
SAFE_RELEASE(pICommandPrepare2);
SAFE_RELEASE(pICommandText2);
SAFE_RELEASE(pICommand2);
return (fResult) ? TEST_PASS : TEST_FAIL;
}
// }}
// {{ TCW_VAR_PROTOTYPE(19)
//*-----------------------------------------------------------------------
// @mfunc Maximum length parameter names
//
// @rdesc TEST_PASS or TEST_FAIL
//
int TCGetParameterInfo_Rowset::Variation_19()
{
BOOL fResult = TRUE;
ULONG cParams, cParamSets = 1;
DBLENGTH cbRowSize;
DBCOUNTITEM ulRowNum = 1;
DBBINDING * pBINDING=NULL;
DB_UPARAMS * pParamOrdinals=NULL;
DBPARAMBINDINFO * pPARAMBIND=NULL;
WCHAR * pwszCreateProcStmt=NULL;
WCHAR * pwszExecProcStmt=NULL;
WCHAR * pwszExecStmt=NULL;
WCHAR * pwszProcName=NULL;
BYTE * pData=NULL;
ParamStruct * pParamAll=NULL;
ULONG cColumns = 0;
DB_LORDINAL * prgColumnsOrd = NULL;
BOOL fCanDerive = FALSE;
if (!m_fProcedureSupport)
{
odtLog << "Procedures not supported.\n";
return TEST_SKIPPED;
}
if (g_ulOutParamsSupported == DBPROPVAL_OA_NOTSUPPORTED)
{
odtLog << "Output parameters not supported.\n";
return TEST_SKIPPED;
}
if (g_bOracle)
{
odtLog << "Skipping comparison of ulParamSize, bPrecision, and bScale for Oracle DBMS.\n";
}
// Create the syntax and binding for a stored proc with output parameters
TEST_COMPARE(CreateProcBindings(
T_EXEC_PROC_SELECT_INOUT, // [IN] Proc type, regular proc or function (has return value)
TRUE, // [IN] If TRUE then we add parameter names to the rgParamBindInfo
1, // [IN] Number of sets of parameters to be created
DBTYPE_I2, // [IN] Return parameter type
1, // [IN] Row number in table to select, insert, or update
&cParams, // [OUT] Count of params created
&cbRowSize, // [OUT] Count of bytes for a single row of parameters
&pBINDING, // [OUT] Binding array for CreateAccessor
&pParamOrdinals,
&pPARAMBIND, // [OUT] rgParamBindInfo for SetParameterInfo
&pwszCreateProcStmt, // [OUT] SQL stmt to create the stored proc
&pwszExecProcStmt, // [OUT] SQL stmt to execute the stored proc
&pwszExecStmt, // [OUT] SQL stmt to execute without stored proc
&pwszProcName, // [OUT] Name of procedure
&pData, // [OUT] Pointer to data for the parameters
&pParamAll,
NULL,
NULL,
NULL,
CREATE_MAX_NAMES
), TRUE);
ABORT_CHECK(PrepareForExecute(pwszExecProcStmt, cParams, pParamOrdinals, pPARAMBIND,
&fCanDerive, pwszProcName, pwszCreateProcStmt), S_OK);
FAIL_VAR(ValidateGetParameterInfo(cParams, cParamSets, ulRowNum, pParamOrdinals, pPARAMBIND,
pBINDING, cbRowSize, pParamAll, pData, ROWSET_NONE, cColumns, prgColumnsOrd, fCanDerive,
VERIFY_USE_PDATA), S_OK);
CLEANUP:
DropStoredProcedure(m_pICommandText, pwszProcName);
// Free the buffers we got from GetParameterInfo
PROVIDER_FREE(pBINDING);
PROVIDER_FREE(pParamOrdinals);
PROVIDER_FREE(pPARAMBIND);
PROVIDER_FREE(pwszCreateProcStmt);
PROVIDER_FREE(pwszExecProcStmt);
PROVIDER_FREE(pwszExecStmt);
PROVIDER_FREE(pwszProcName);
PROVIDER_FREE(pData);
::FreeParameterNames(cParams, pParamAll);
PROVIDER_FREE(pParamAll);
return fResult ? TEST_PASS : TEST_FAIL;}
// }} TCW_VAR_PROTOTYPE_END
// {{ TCW_TERMINATE_METHOD
//--------------------------------------------------------------------
// @mfunc TestCase Termination Routine
//
// @rdesc TRUE or FALSE
//
BOOL TCGetParameterInfo_Rowset::Terminate()
{
if (m_pICommandPrepare)
m_pICommandPrepare->Unprepare();
// {{ TCW_TERM_BASECLASS_CHECK2
return(CICmdWParams::Terminate());
} // }}
// }}
// }}
// {{ TCW_TC_PROTOTYPE(TCMapParameterNames)
//*-----------------------------------------------------------------------
//| Test Case: TCMapParameterNames - Test case for MapParameterNames.
//| Created: 03/30/96
//*-----------------------------------------------------------------------
//--------------------------------------------------------------------
// @mfunc TestCase Initialization Routine
//
// @rdesc TRUE or FALSE
//
BOOL TCMapParameterNames::Init()
{
BOOL fResult = TRUE;
ULONG cParams, cParamSets = 1 ;
DBLENGTH cbRowSize;
DBCOUNTITEM ulRowNum = 1;
DBBINDING * pBINDING=NULL;
DB_UPARAMS * pParamOrdinals=NULL;
DBPARAMBINDINFO * pPARAMBIND=NULL;
WCHAR * pwszCreateProcStmt=NULL;
WCHAR * pwszExecProcStmt=NULL;
WCHAR * pwszExecStmt=NULL;
WCHAR * pwszProcName=NULL;
BYTE * pData=NULL;
ParamStruct * pParamAll=NULL;
ULONG cColumns = 0;
DB_LORDINAL * prgColumnsOrd = NULL;
BOOL fCanDerive = FALSE;
DBPARAMBINDINFO * pParamBindInfo = NULL;
DBPARAMFLAGS *rgDwParamFlags = NULL;
HRESULT hr;
// Assume we'll include LONG columns in the parameters
m_fColTypes = INCLUDE_LONG_COLS;
// {{ TCW_INIT_BASECLASS_CHECK
if(CICmdWParams::Init())
// }}
{
m_hrExpect=S_OK;
// For Kagera and 2.x drivers expect not implemented.
if (m_dwDriverODBCVer > 0 && m_dwDriverODBCVer < 3)
m_hrExpect=E_NOTIMPL;
memset(&m_iNotTouched, NOT_TOUCHED_ORD, sizeof(DB_UPARAMS));
m_fUseProc = (m_fProcedureSupport && g_ulOutParamsSupported != DBPROPVAL_OA_NOTSUPPORTED && m_Syntax.IsKnown());
// Create a stored procedure and set the command text if we know the syntax for doing so. Otherwise
// we'll have to use the command text from the base class init (insert statement).
if (m_fUseProc)
{
// We're assuming parameter names aren't available at this time
ASSERT(!m_prgwszParameterNames);
// Create the syntax and binding for a stored proc with output parameters
ABORT_COMPARE(CreateProcBindings(
T_EXEC_PROC_SELECT_OUT, // [IN] Proc type, regular proc or function (has return value)
TRUE, // [IN] If TRUE then we add parameter names to the rgParamBindInfo
1, // [IN] Number of sets of parameters to be created
DBTYPE_I2, // [IN] Return parameter type
1, // [IN] Row number in table to select, insert, or update
&cParams, // [OUT] Count of params created
&cbRowSize, // [OUT] Count of bytes for a single row of parameters
&pBINDING, // [OUT] Binding array for CreateAccessor
&pParamOrdinals,
&pPARAMBIND, // [OUT] rgParamBindInfo for SetParameterInfo
&pwszCreateProcStmt, // [OUT] SQL stmt to create the stored proc
&pwszExecProcStmt, // [OUT] SQL stmt to execute the stored proc
&pwszExecStmt, // [OUT] SQL stmt to execute without stored proc
&pwszProcName, // [OUT] Name of procedure
&pData, // [OUT] Pointer to data for the parameters
&pParamAll
), TRUE);
// Set up to execute the stored proc
ABORT_CHECK(PrepareForExecute(pwszExecProcStmt, cParams, pParamOrdinals, pPARAMBIND,
&fCanDerive, pwszProcName, pwszCreateProcStmt), S_OK);
TEST_ALLOC(WCHAR *, m_prgwszParameterNames, 0, (cParams+1) * sizeof(WCHAR *));
for (ULONG iParam = 0; iParam < cParams; iParam++)
{
if (!(m_prgwszParameterNames[iParam] = wcsDuplicate(pPARAMBIND[iParam].pwszName)))
goto CLEANUP;
}
m_cParamNames = cParams;
m_prgParamOrdinals = pParamOrdinals;
m_pParamBindInfo = pPARAMBIND;
// If we can't derive we need to call SetParameterInfo
if (!fCanDerive)
{
if (!CHECK(hr = m_pICmdWParams->SetParameterInfo(cParams, pParamOrdinals, pPARAMBIND),
S_OK)) // We set reset parameter info in PrepareForExecute.
return FALSE;
}
return TRUE;
}
else
{
if (!m_bSetParameterInfo && m_pICommandPrepare)
TEST_CHECK(m_pICommandPrepare->Prepare(1), S_OK);
m_cParamNames = m_cDbParamBindInfo;
m_pParamBindInfo = m_rgDbParamBindInfo;
}
// If we didn't get parameter names from the CreateStoredProcedure we need to create them here
if (!m_prgwszParameterNames && !(m_prgwszParameterNames=CreateParameterNames(NULL, ALL_VALID_NAMES, &m_cParamNames, m_fColTypes)))
return FALSE;
if (!m_prgParamOrdinals && !(m_prgParamOrdinals=(DB_UPARAMS *)PROVIDER_ALLOC(m_cParamNames*sizeof(DB_UPARAMS))))
return FALSE;
// Copy the ordinals we had in base class init.
memcpy(m_prgParamOrdinals, m_rgParamOrdinals, (size_t)(m_cParamNames * sizeof(DB_UPARAMS)));
// For providers that support command preparation and can derive parameter information we need to prepare here
// in case they check for NOT_PREPARED before other error conditions in the following variations. For providers
// that can't derive parameter information we need to be prepared before we call SetParameterInfo below.
if (!m_bSetParameterInfo && m_pICommandPrepare && !CHECK(m_pICommandPrepare->Prepare(1), S_OK))
return FALSE;
// For providers that can't derive parameter information we need to set parameter names before MapParameterNames
// can work. Note we didn't set them in the base class init, we left them NULL in case the provider doesn't
// support actually setting the names.
// We also need to call SetParameterInfo if not using a stored proc so that parameter names will be set where
// they are not defined in the proc.
if (m_bSetParameterInfo || !m_fUseProc)
{
// We're assuming the count of the names we created above matches the parameter count in base class init
ASSERT(m_cDbParamBindInfo == m_cParamNames);
SetParameterNames(m_prgwszParameterNames);
CHECK(hr = m_pICmdWParams->SetParameterInfo(m_cParamNames, m_rgParamOrdinals, m_rgDbParamBindInfo),
DB_S_TYPEINFOOVERRIDDEN); // We set it with NULL names in base class init.
// Even if we got a warning we can proceed
if (!SUCCEEDED(hr))
return FALSE;
}
return TRUE;
}
CLEANUP:
return FALSE;
}
// {{ TCW_VAR_PROTOTYPE(1)
//*-----------------------------------------------------------------------
// @mfunc cParamNames = 0 : returns S_OK
//
// @rdesc TEST_PASS or TEST_FAIL
//
int TCMapParameterNames::Variation_1()
{
LONG cParamNames = 3;
WCHAR *rgParamNames[] = {L"ONE", L"TWO", L"THREE" };
DB_LPARAMS *rgParamOrdinals = NULL;
BOOL fSuccess = FALSE;
if ((rgParamOrdinals = (DB_LPARAMS *)m_pIMalloc->Alloc(cParamNames *sizeof (DB_LPARAMS))) == NULL)
{
odtLog << wszMemoryAllocationError;
return TEST_FAIL;
}
TEST_CHECK (m_pICmdWParams->MapParameterNames(0, (const WCHAR **)rgParamNames, rgParamOrdinals), S_OK);
fSuccess = TRUE;
CLEANUP:
if (rgParamOrdinals)
m_pIMalloc->Free(rgParamOrdinals);
if (fSuccess)
return TEST_PASS;
else
return TEST_FAIL;
}
// }}
// {{ TCW_VAR_PROTOTYPE(2)
//*-----------------------------------------------------------------------
// @mfunc cParamNames > 0 and NULL pointer for rgParamNames: returns E_INVALIDARG
//
// @rdesc TEST_PASS or TEST_FAIL
//
int TCMapParameterNames::Variation_2()
{
LONG cParamNames = 3;
DB_LPARAMS *rgParamOrdinals = NULL;
BOOL fSuccess = FALSE;
if ((rgParamOrdinals = (DB_LPARAMS *)m_pIMalloc->Alloc(cParamNames *sizeof (DB_LPARAMS))) == NULL)
{
odtLog << wszMemoryAllocationError;
return TEST_FAIL;
}
for (LONG i = 0; i < cParamNames; i++ )
rgParamOrdinals[i] = i+1;
TEST_CHECK (m_pICmdWParams->MapParameterNames(cParamNames, NULL, rgParamOrdinals), E_INVALIDARG);
fSuccess = TRUE;
CLEANUP:
if (rgParamOrdinals)
m_pIMalloc->Free(rgParamOrdinals);
if (fSuccess)
return TEST_PASS;
else
return TEST_FAIL;
}
// }}
// {{ TCW_VAR_PROTOTYPE(3)
//*-----------------------------------------------------------------------
// @mfunc rgParamOrdinals with NULL : returns E_INVALIDARG
//
// @rdesc TEST_PASS or TEST_FAIL
//
int TCMapParameterNames::Variation_3()
{
LONG cParamNames = 3;
WCHAR *rgParamNames[] = {L"ONE", L"TWO", L"THREE" };
if (CHECK (m_pICmdWParams->MapParameterNames(cParamNames, (const WCHAR **)rgParamNames, NULL), E_INVALIDARG))
return TEST_PASS;
else
return TEST_FAIL;
}
// }}
// {{ TCW_VAR_PROTOTYPE(4)
//*-----------------------------------------------------------------------
// @mfunc (0, NULL, NULL
//
// @rdesc TEST_PASS or TEST_FAIL
//
int TCMapParameterNames::Variation_4()
{
if (CHECK (m_pICmdWParams->MapParameterNames (0, NULL, NULL), S_OK))
return TEST_PASS;
else
return TEST_FAIL;
}
// }}
// {{ TCW_VAR_PROTOTYPE(5)
//*-----------------------------------------------------------------------
// @mfunc Map Parameter Names without prepared command: DB_E_NOTPREPARED
//
// @rdesc TEST_PASS or TEST_FAIL
//
int TCMapParameterNames::Variation_5()
{
BOOL fResult=TRUE;
LONG cParamNames = 3;
WCHAR *rgParamNames[] = {L"ONE", L"TWO", L"THREE" };
DB_LPARAMS rgParamOrdinals[] = { LONG_MAX, LONG_MAX, LONG_MAX };
HRESULT hr = E_FAIL;
if (!m_pICommandPrepare)
{
odtLog << L"Provider doesn't support ICommandPrepare.\n\n";
return TEST_SKIPPED;
}
// If we Prepared in the init, then we need to unprepare here
if (!CHECK(m_pICommandPrepare->Unprepare(), S_OK))
return TEST_FAIL;
// If we've set parameter info unset it here. We always set parameter info when not using a stored proc.
if (!m_fUseProc)
m_pICmdWParams->SetParameterInfo(0, NULL, NULL);
// If the provider is capable of deriving error information, then expect DB_E_NOTPREPARED
// here, else spec is unclear but since the interface is supported then all methods should be supported.
// Some providers return E_NOTIMPL, and a review of the ole docs show that an existing method is
// allowed to return E_NOTIMPL if it doesn't support all semantics of a method.
hr = m_pICmdWParams->MapParameterNames(cParamNames, (const WCHAR **)rgParamNames, rgParamOrdinals);
if (hr == E_NOTIMPL)
{
odtLog << L"MapParameterNames not implemented.\n";
return TEST_SKIPPED;
}
fResult = CHECK (hr, DB_E_NOTPREPARED);
// Need to reprepare for the rest of the variations.
if (!CHECK(m_pICommandPrepare->Prepare(1), S_OK))
fResult = FALSE;
return (fResult) ? TEST_PASS : TEST_FAIL;
}
// }}
// {{ TCW_VAR_PROTOTYPE(6)
//*-----------------------------------------------------------------------
// @mfunc MapParameterNames with all invalid parameter names: DB_E_ERRORSOCCURRED
//
// @rdesc TEST_PASS or TEST_FAIL
//
int TCMapParameterNames::Variation_6()
{
TESTRESULT testresult=TEST_FAIL;
LONG iOrdinal=0;
WCHAR ** prgwszParameterNames;
DB_LPARAMS * pParamOrdinals = NULL;
ULONG iParam;
HRESULT hr = E_FAIL;
TEST_ALLOC(DB_LPARAMS, pParamOrdinals, NOT_TOUCHED_ORD, (size_t)(m_cParamNames*sizeof(DB_LPARAMS)));
// Copy the name array
TEST_ALLOC(WCHAR *, prgwszParameterNames, 0, (size_t)(m_cParamNames*sizeof(WCHAR *)));
memcpy(prgwszParameterNames, m_prgwszParameterNames, (size_t)m_cParamNames*sizeof(WCHAR *));
// Set the names to NULL
for (iParam = 0; iParam < m_cParamNames; iParam++)
prgwszParameterNames[iParam] = NULL;
TEST_CHECK(m_pICommandPrepare->Prepare(1), S_OK);
hr = m_pICmdWParams->MapParameterNames(m_cParamNames, (const WCHAR **)prgwszParameterNames, (DB_LPARAMS *)pParamOrdinals);
if (hr == E_NOTIMPL)
{
odtLog << L"MapParameterNames not implemented.\n";
testresult = TEST_SKIPPED;
goto CLEANUP;
}
TEST_CHECK (hr, DB_E_ERRORSOCCURRED);
// Per spec on failure all ordinal values are set to 0
for (iParam = 0; iParam < m_cParamNames; iParam++)
TEST_COMPARE(pParamOrdinals[iParam], 0);
// If we made it to here everything succeeded
testresult=TEST_PASS;
CLEANUP:
PROVIDER_FREE(prgwszParameterNames);
PROVIDER_FREE(pParamOrdinals);
return testresult;
}
// }}
// {{ TCW_VAR_PROTOTYPE(7)
//*-----------------------------------------------------------------------
// @mfunc MapParameterNames with some invalid parameter names: DB_S_ERRORSOCCURRED
//
// @rdesc TEST_PASS or TEST_FAIL
//
int TCMapParameterNames::Variation_7()
{
TESTRESULT testresult=TEST_FAIL;
LONG iOrdinal=0;
WCHAR ** prgwszParameterNames;
DB_UPARAMS * pParamOrdinals = NULL;
ULONG iParam;
HRESULT hr = E_FAIL;
TEST_ALLOC(DB_UPARAMS, pParamOrdinals, NOT_TOUCHED_ORD, (size_t)m_cParamNames*sizeof(DB_UPARAMS));
// Copy the name array
TEST_ALLOC(WCHAR *, prgwszParameterNames, 0, (size_t)m_cParamNames*sizeof(WCHAR *));
memcpy(prgwszParameterNames, m_prgwszParameterNames, (size_t)m_cParamNames*sizeof(WCHAR *));
// If not using stored proc we must call SetParameterInfo to set the names even if a provider
// can derive.
if (!m_fUseProc)
{
hr = m_pICmdWParams->SetParameterInfo(m_cParamNames, m_rgParamOrdinals, m_rgDbParamBindInfo);
if (hr != DB_S_TYPEINFOOVERRIDDEN)
CHECK(hr, S_OK);
}
// Set some of the names to NULL
for (iParam = 0; iParam < m_cParamNames; iParam++)
{
if (iParam % 2)
prgwszParameterNames[iParam] = NULL;
}
TEST_CHECK(m_pICommandPrepare->Prepare(1), S_OK);
hr = m_pICmdWParams->MapParameterNames(m_cParamNames, (const WCHAR **)prgwszParameterNames, (DB_LPARAMS *)pParamOrdinals);
if (hr == E_NOTIMPL)
{
odtLog << L"MapParameterNames not implemented.\n";
testresult = TEST_SKIPPED;
goto CLEANUP;
}
TEST_CHECK (hr, DB_S_ERRORSOCCURRED);
// Per spec on failure all ordinal values are set to 0
for (iParam = 0; iParam < m_cParamNames; iParam++)
{
if (iParam % 2)
{
TEST_COMPARE(pParamOrdinals[iParam], 0);
}
else
{
TEST_COMPARE(pParamOrdinals[iParam], m_prgParamOrdinals[iParam]);
}
}
// If we made it to here everything succeeded
testresult=TEST_PASS;
CLEANUP:
PROVIDER_FREE(prgwszParameterNames);
PROVIDER_FREE(pParamOrdinals);
return testresult;
}
// }}
// {{ TCW_VAR_PROTOTYPE(8)
//*-----------------------------------------------------------------------
// @mfunc MapParameterNames with all valid names: S_OK
//
// @rdesc TEST_PASS or TEST_FAIL
//
int TCMapParameterNames::Variation_8()
{
TESTRESULT testresult=TEST_FAIL;
LONG iOrdinal=0;
DB_UPARAMS * pParamOrdinals = NULL;
ULONG iParam;
HRESULT hr = E_FAIL;
TEST_ALLOC(DB_UPARAMS, pParamOrdinals, NOT_TOUCHED_ORD, (size_t)m_cParamNames*sizeof(DB_UPARAMS));
TEST_CHECK(m_pICommandPrepare->Prepare(1), S_OK);
hr = m_pICmdWParams->MapParameterNames(m_cParamNames, (const WCHAR **)m_prgwszParameterNames, (DB_LPARAMS *)pParamOrdinals);
if (hr == E_NOTIMPL)
{
odtLog << L"MapParameterNames not implemented.\n";
testresult = TEST_SKIPPED;
goto CLEANUP;
}
TEST_CHECK (hr, S_OK);
// Per spec on failure all ordinal values are set to 0
for (iParam = 0; iParam < m_cParamNames; iParam++)
TEST_COMPARE(pParamOrdinals[iParam], m_prgParamOrdinals[iParam]);
// If we made it to here everything succeeded
testresult=TEST_PASS;
CLEANUP:
PROVIDER_FREE(pParamOrdinals);
return testresult;
}
// }}
// {{ TCW_VAR_PROTOTYPE(9)
//*-----------------------------------------------------------------------
// @mfunc MapParameterNames with fewer names than set
//
// @rdesc TEST_PASS or TEST_FAIL
//
int TCMapParameterNames::Variation_9()
{
TESTRESULT testresult=TEST_FAIL;
ULONG iOrdinal=0;
DB_UPARAMS * pParamOrdinals = NULL;
ULONG ulMaxNames=1;
HRESULT hr;
// Set all ordinal values to a known value
TEST_ALLOC(DB_UPARAMS, pParamOrdinals, NOT_TOUCHED_ORD, (size_t)m_cParamNames*sizeof(DB_UPARAMS));
if (!CHECK (m_pICommandPrepare->Prepare(1), S_OK))
goto CLEANUP;
// Ask for the last parameters to be mapped only, rest should be 0
hr=m_pICmdWParams->MapParameterNames(ulMaxNames, (const WCHAR **)(&m_prgwszParameterNames[m_cParamNames-ulMaxNames]),
(DB_LPARAMS *)pParamOrdinals);
if (hr == E_NOTIMPL)
{
odtLog << L"MapParameterNames not implemented.\n";
testresult = TEST_SKIPPED;
goto CLEANUP;
}
TEST_CHECK(hr, S_OK);
// Now check that the ordinal values are correct
for (iOrdinal=0; iOrdinal < m_cParamNames; iOrdinal++)
{
// For the requested parameters the correct ordinal should be returned
if (iOrdinal < ulMaxNames)
{
TEST_COMPARE(pParamOrdinals[iOrdinal], m_prgParamOrdinals[iOrdinal+m_cParamNames-ulMaxNames]);
}
else
{
// Otherwise the "not touched" value
TEST_COMPARE(pParamOrdinals[iOrdinal], m_iNotTouched);
}
}
// If we made it to here everything succeeded
testresult=TEST_PASS;
CLEANUP:
PROVIDER_FREE(pParamOrdinals);
return testresult;
}
// }}
// {{ TCW_VAR_PROTOTYPE(10)
//*-----------------------------------------------------------------------
// @mfunc Random name order: S_OK
//
// @rdesc TEST_PASS or TEST_FAIL
//
int TCMapParameterNames::Variation_10()
{
TESTRESULT testresult=TEST_FAIL;
LONG iOrdinal=0, iOrd2;
HRESULT hr;
DB_LPARAMS * pParamOrdinals = NULL;
WCHAR ** prgwszParameterNames = NULL;
TEST_ALLOC(DB_LPARAMS, pParamOrdinals, NOT_TOUCHED_ORD, (size_t)m_cParamNames*sizeof(DB_LPARAMS));
// Allocate space for duplicate of param names array
prgwszParameterNames = new WCHAR *[(ULONG)m_cParamNames];
if (!prgwszParameterNames)
return TEST_FAIL;
// Copy the names from the original array
memcpy(prgwszParameterNames, m_prgwszParameterNames, (size_t)m_cParamNames * sizeof(WCHAR *));
ScrambleArray(prgwszParameterNames, m_cParamNames, sizeof(WCHAR *));
if (!CHECK (m_pICommandPrepare->Prepare(1), S_OK))
goto CLEANUP;
hr=m_pICmdWParams->MapParameterNames(m_cParamNames, (const WCHAR **)prgwszParameterNames, pParamOrdinals);
if (hr == E_NOTIMPL)
{
odtLog << L"MapParameterNames not implemented.\n";
testresult = TEST_SKIPPED;
goto CLEANUP;
}
TEST_CHECK(hr, S_OK);
// Now check that the ordinal values are correct
for (iOrdinal=0; iOrdinal < (LONG)m_cParamNames; iOrdinal++)
{
// Locate the name in the properly ordered list
for (iOrd2=0; iOrd2 < (LONG)m_cParamNames; iOrd2++)
{
if (!wcscmp(prgwszParameterNames[iOrdinal], m_prgwszParameterNames[iOrd2]))
break;
}
if (!MYCOMPARE(pParamOrdinals[iOrdinal], iOrd2+1,L"Incorrect ordinal value returned."))
goto CLEANUP;
}
// If we made it to here everything succeeded
testresult=TEST_PASS;
CLEANUP:
delete[] prgwszParameterNames;
PROVIDER_FREE(pParamOrdinals);
return testresult;
}
// }}
// {{ TCW_VAR_PROTOTYPE(11)
//*-----------------------------------------------------------------------
// @mfunc Overridden with SetParameterInfo: S_OK or DB_E_ERRORSOCCURRED
//
// @rdesc TEST_PASS or TEST_FAIL
//
int TCMapParameterNames::Variation_11()
{
TESTRESULT testresult=TEST_FAIL;
DB_UPARAMS iOrdinal=0;
HRESULT hr;
DB_LPARAMS * pParamOrdinals = NULL;
WCHAR ** rgwszParamNames=NULL;
DBPARAMBINDINFO * pParamBindInfo = NULL;
WCHAR * pwszParamNameFmt = NULL;
WCHAR * pwszName1 = NULL;
WCHAR * pwszName2 = NULL;
WCHAR NameOverRide[][SP_MAX_PARAMNAME_LENGTH] = {
L"ABCD",
L"xyz"
};
// Providers have different syntax for parameter names. We need to match with the
// overridden names. Retrieve syntax.
if (!(pwszParamNameFmt = m_Syntax.GetSyntax(T_PARM_NAME_FMT)))
goto CLEANUP;
if (!FormatString(&pwszName1, pwszParamNameFmt, 1, NameOverRide[0]))
goto CLEANUP;
if (!FormatString(&pwszName2, pwszParamNameFmt, 1, NameOverRide[1]))
goto CLEANUP;
TEST_ALLOC(DB_LPARAMS, pParamOrdinals, NOT_TOUCHED_ORD, (size_t)m_cParamNames*sizeof(DB_LPARAMS));
TEST_ALLOC(DBPARAMBINDINFO, pParamBindInfo, NOT_TOUCHED_ORD, (size_t)m_cParamNames*sizeof(DBPARAMBINDINFO));
memcpy(pParamBindInfo, m_pParamBindInfo, (size_t)m_cParamNames*sizeof(DBPARAMBINDINFO));
// Initialize GetParameterInfo buffers to NULL
m_pNamesBuffer=NULL;
m_rgParamInfo=NULL;
// Must be prepared
TEST_CHECK(m_pICommandPrepare->Prepare(1), S_OK);
// Make an array to store the parameter names
TEST_ALLOC(WCHAR *, rgwszParamNames, 0, (size_t)m_cParamNames*sizeof(WCHAR *));
memcpy(rgwszParamNames, m_prgwszParameterNames, (size_t)m_cParamNames * sizeof(WCHAR *));
// Set the parameter names, over-riding first and last name
pParamBindInfo[0].pwszName=pwszName1;
rgwszParamNames[0]=pwszName1;
pParamBindInfo[m_cParamNames-1].pwszName=pwszName2;
rgwszParamNames[m_cParamNames-1]=pwszName2;
// Expect DB_S_TYPEINFOOVERRIDDEN here because we either set it in the init or the provider
// can derive.
TEST_CHECK(hr = m_pICmdWParams->SetParameterInfo(m_cParamNames, m_prgParamOrdinals, pParamBindInfo),
DB_S_TYPEINFOOVERRIDDEN);
hr=m_pICmdWParams->MapParameterNames(m_cParamNames, (const OLECHAR **)rgwszParamNames, pParamOrdinals);
if (hr == E_NOTIMPL)
{
odtLog << L"MapParameterNames not implemented.\n";
testresult = TEST_SKIPPED;
goto CLEANUP;
}
TEST_CHECK(hr, S_OK);
// Now check that the ordinal values are correct
for (iOrdinal=0; iOrdinal < m_cParamNames; iOrdinal++)
{
if (!MYCOMPARE(m_prgParamOrdinals[iOrdinal], iOrdinal+1,L"Incorrect ordinal value returned."))
goto CLEANUP;
}
// If we made it to here everything succeeded
testresult=TEST_PASS;
CLEANUP:
// Set the parameter information back for further variations
if (!CHECK(hr = m_pICmdWParams->SetParameterInfo(m_cParamNames, m_prgParamOrdinals, m_pParamBindInfo),
DB_S_TYPEINFOOVERRIDDEN))
testresult = TEST_FAIL;
PROVIDER_FREE(rgwszParamNames);
PROVIDER_FREE(pParamOrdinals);
PROVIDER_FREE(pParamBindInfo);
PROVIDER_FREE(pwszName1);
PROVIDER_FREE(pwszName2);
PROVIDER_FREE(pwszParamNameFmt);
return testresult;
}
// }}
// {{ TCW_VAR_PROTOTYPE(12)
//*-----------------------------------------------------------------------
// @mfunc No command text set: DB_E_NOCOMMAND
//
// @rdesc TEST_PASS or TEST_FAIL
//
int TCMapParameterNames::Variation_12()
{
WCHAR * wszExecuteString;
TESTRESULT testresult=TEST_FAIL;
GUID guidSQL=DBGUID_DBSQL;
DB_LPARAMS * pParamOrdinals = NULL;
HRESULT hr = E_FAIL;
// We need to remove any parameter info set previously, since the provider is allowed
// to use any set info for MapParameterNames
TEST_CHECK(m_pICmdWParams->SetParameterInfo(0, NULL, NULL), S_OK);
TEST_ALLOC(DB_LPARAMS, pParamOrdinals, NOT_TOUCHED_ORD, (size_t)m_cParamNames * sizeof(DB_LPARAMS));
// Save the command text so we can put it back
TEST_CHECK (m_pICommandText->GetCommandText(&guidSQL, &wszExecuteString), S_OK);
// Make sure we have no command text set
TEST_CHECK (m_pICommandText->SetCommandText(guidSQL, NULL), S_OK);
hr = m_pICmdWParams->MapParameterNames(m_cParamNames, (const WCHAR **)m_prgwszParameterNames, pParamOrdinals);
if (hr == E_NOTIMPL)
{
odtLog << L"MapParameterNames not implemented.\n";
testresult = TEST_SKIPPED;
goto CLEANUP;
}
TEST_CHECK(hr, DB_E_NOCOMMAND);
testresult = TEST_PASS;
CLEANUP:
// Reset param info
if (!COMPARE(SUCCEEDED(m_pICmdWParams->SetParameterInfo(m_cParamNames, m_prgParamOrdinals, m_pParamBindInfo)),
TRUE))
testresult = TEST_FAIL;
// Reset the command text for other variations
if (!CHECK (m_pICommandText->SetCommandText(guidSQL, wszExecuteString), S_OK))
testresult = TEST_FAIL;
PROVIDER_FREE(pParamOrdinals);
return testresult;
}
// }}
// {{ TCW_TERMINATE_METHOD
//--------------------------------------------------------------------
// @mfunc TestCase Termination Routine
//
// @rdesc TRUE or FALSE
//
BOOL TCMapParameterNames::Terminate()
{
PROVIDER_FREE(m_prgParamOrdinals);
FreeParameterNames(m_prgwszParameterNames);
DropStoredProcedure(m_pICommandText);
// {{ TCW_TERM_BASECLASS_CHECK2
return(CICmdWParams::Terminate());
} // }}
// }}
// }}
// {{ TCW_TC_PROTOTYPE(TCCommandExecute_Rowset)
//*-----------------------------------------------------------------------
//| Test Case: TCCommandExecute_Rowset - Test case for ICommand::Execute (For Parameter related test cases
//| Created: 04/10/96
//*-----------------------------------------------------------------------
//--------------------------------------------------------------------
// @mfunc TestCase Initialization Routine
//
// @rdesc TRUE or FALSE
//--------------------------------------------------------------------
BOOL TCCommandExecute_Rowset::Init()
{
WCHAR *pwszSelectAllFromTbl = NULL;
// Initialize values.
m_cSPParamColMap = NULL;
m_rgSPParamColOrdinals = NULL;
m_rgSPBindings = NULL;
m_rgSPParamBind = NULL;
m_rgSPParamColMap = NULL;
m_cSPBindings = 0;
m_pSPParamAll = NULL;
// USe the Empty command it is already created anyway.
m_pSPICommandText = NULL;
m_pSPICommandPrepare = NULL;
m_pSPIAccessor = NULL;
m_pSPRowset = NULL;
m_hReadAccessor = DB_NULL_HACCESSOR;
m_cReadBindings = 0;
m_cbReadRowSize = 0;
m_cbSPRowSize = 0;
m_rgReadBindings = NULL;
// If we're using an ini file then skip the test case. Privlib crashes
// due to the unavailability of the underlying provider type name information
// needed for CreateColDef when using an ini file.
// This restriction can be removed when TableDump can provide the provider type
// name and create params.
// TODO: Move the stored proc test variations to their own test case so all other
// variations can still be run.
if (GetModInfo()->GetFileName())
{
odtLog << L"Currently can't run this test case using an ini file.\n";
return TEST_SKIPPED;
}
if (m_eTestCase == TC_Row && !g_fRowObj)
{
odtLog << L"Row objects not supported.\n";
return TEST_SKIPPED;
}
// {{ TCW_INIT_BASECLASS_CHECK
if(CICmdWParams::Init())
// }}
{
// If we know the SQL syntax for the provider and output parameters are supported then
// create a stored procedure and accessor.
if (g_ulOutParamsSupported != DBPROPVAL_OA_NOTSUPPORTED)
{
// Get Command text interface.
if (!VerifyInterface(m_pIEmptyCommand,
IID_ICommandText, COMMAND_INTERFACE, (IUnknown **)&m_pSPICommandText))
{
return FALSE;
}
// Get accessor interface.
if (!VerifyInterface(m_pIEmptyCommand,
IID_IAccessor, COMMAND_INTERFACE, (IUnknown **)&m_pSPIAccessor))
{
return FALSE;
}
if (!VerifyInterface(m_pIEmptyCommand,
IID_ICommandPrepare, COMMAND_INTERFACE, (IUnknown **)&m_pSPICommandPrepare))
{
return FALSE;
}
if (!COMPARE (CreateSP(), TRUE))
return FALSE;
}
return TRUE;
}
return TEST_SKIPPED;
}
//--------------------------------------------------------------------
// @mfunc Drop temporary stored procedure.
//
// @rdesc TRUE always.
//--------------------------------------------------------------------
BOOL
TCCommandExecute_Rowset::DropSP()
{
WCHAR * pwszDropString = NULL;
WCHAR * pwszDropProc = NULL;
BOOL fSuccess = FALSE;
if (m_pwszCreateProcedureString && !(pwszDropProc = m_Syntax.GetSyntax(T_DROP)))
goto CLEANUP;
FormatString(&pwszDropString, pwszDropProc, 1, &g_pwszProcedureName[0]);
if (m_pSPICommandText)
m_pSPICommandText->SetCommandText(DBGUID_DBSQL , pwszDropString);
// If it exists drop it. No need to check return code.
if (m_pIEmptyCommand)
m_pIEmptyCommand->Execute(NULL, IID_NULL, NULL, NULL, NULL);
// Free the strings pointed to be CreateProcedure and execute procedure.
FREE_DATA (m_pwszCreateProcedureString);
FREE_DATA (m_pwszExecuteProcedureString);
fSuccess = TRUE;
CLEANUP:
PROVIDER_FREE(pwszDropString);
PROVIDER_FREE(pwszDropProc);
return fSuccess;
}
//--------------------------------------------------------------------
// @mfunc Create text for temporary stored procedure.
//
// @rdesc TRUE or FALSE
//--------------------------------------------------------------------
BOOL TCCommandExecute_Rowset::CreateSPText()
{
// Creates both the stored procedure text and the execute procedure text and bindings
BOOL fSuccess = FALSE;
ULONG cParams, cParamSets = 1;
DBLENGTH cbRowSize;
DBCOUNTITEM ulRowNum = 1;
DBBINDING * pBINDING=NULL;
DB_UPARAMS * pParamOrdinals=NULL;
DBPARAMBINDINFO * pPARAMBIND=NULL;
WCHAR * pwszCreateProcStmt=NULL;
WCHAR * pwszExecProcStmt=NULL;
WCHAR * pwszExecStmt=NULL;
WCHAR * pwszProcName=wcsDuplicate((WCHAR *)g_pwszProcedureName);
BYTE * pData=NULL;
ParamStruct * pParamAll=NULL;
ULONG cColumns = 0;
DB_LORDINAL * prgColumnsOrd = NULL;
ULONG iParam;
// Initialize Local data.
m_ulLocalSPRowNum = m_pTable->GetRowsOnCTable();
m_rgSPParamColMap = NULL;
if (!m_fProcedureSupport)
{
odtLog << "Procedures not supported.\n";
return FALSE;
}
if (g_ulOutParamsSupported == DBPROPVAL_OA_NOTSUPPORTED)
{
odtLog << "Output parameters not supported.\n";
return FALSE;
}
// Create the syntax and binding for a stored proc with output parameters
TEST_COMPARE(CreateProcBindings(
T_EXEC_PROC_SELECT_OUT, // [IN] Proc type, regular proc or function (has return value)
TRUE, // [IN] If TRUE then we add parameter names to the rgParamBindInfo
1, // [IN] Number of sets of parameters to be created
DBTYPE_I2, // [IN] Return parameter type
1, // [IN] Row number in table to select, insert, or update
&cParams, // [OUT] Count of params created
&cbRowSize, // [OUT] Count of bytes for a single row of parameters
&pBINDING, // [OUT] Binding array for CreateAccessor
&pParamOrdinals,
&pPARAMBIND, // [OUT] rgParamBindInfo for SetParameterInfo
&pwszCreateProcStmt, // [OUT] SQL stmt to create the stored proc
&pwszExecProcStmt, // [OUT] SQL stmt to execute the stored proc
&pwszExecStmt, // [OUT] SQL stmt to execute without stored proc
&pwszProcName, // [OUT] Name of procedure
NULL, // [OUT] Pointer to data for the parameters
&pParamAll
), TRUE);
// Allocate space for param to column map
TEST_ALLOC(DB_LORDINAL, m_rgSPParamColMap, 0, cParams * sizeof(DB_LORDINAL));
// Fill it
for (iParam = 0; iParam < cParams; iParam++)
m_rgSPParamColMap[iParam] = pParamAll[iParam].ulColIndex;
fSuccess = TRUE;
CLEANUP:
m_cSPBindings = cParams;
m_cbSPRowSize = cbRowSize;
m_cSPParamColMap = cParams;
m_rgSPParamColOrdinals = pParamOrdinals;
m_rgSPBindings = pBINDING;
m_rgSPParamBind = pPARAMBIND;
m_pwszCreateProcedureString = pwszCreateProcStmt;
m_pwszExecuteProcedureString = pwszExecProcStmt;
m_pSPParamAll = pParamAll;
SAFE_FREE(pwszProcName);
return fSuccess;
}
//--------------------------------------------------------------------
// @mfunc Create the stored procedure in the backend
//
// @rdesc TRUE or FALSE.
//--------------------------------------------------------------------
BOOL
TCCommandExecute_Rowset::CreateSP()
{
// In case it exists we should try to drop it first
DropSP();
CreateSPText();
TEST_CHECK(m_pSPICommandText->SetCommandText(DBGUID_DBSQL , m_pwszCreateProcedureString), S_OK);
TEST_CHECK(m_pIEmptyCommand->Execute(NULL, IID_NULL, NULL, NULL, NULL), S_OK);
return TRUE;
CLEANUP:
return FALSE;
}
//--------------------------------------------------------------------
// @mfunc Execute temporary stored procedure.
//
// @rdesc HRESULT
//--------------------------------------------------------------------
HRESULT
TCCommandExecute_Rowset::ExecuteSP(DBPARAMS *pDbParams, DBCOUNTITEM ulRowNum)
{
BYTE *pData = NULL;
HRESULT hr = S_OK;
hr = m_pSPICommandText->SetCommandText(DBGUID_DBSQL , m_pwszExecuteProcedureString);
if (! CHECK (hr, S_OK))
return hr;
if (!CHECK ((hr = m_pSPICommandPrepare->Prepare(1)), S_OK))
{
return hr;
}
if (m_pSPRowset) ReleaseRowsetPtr(&m_pSPRowset);
m_cSPRowsAffected = 0;
// Allocate pData.
pData = (BYTE *)m_pIMalloc->Alloc(m_cbSPRowSize);
if (!pData)
{
return E_OUTOFMEMORY;
}
pDbParams->pData = pData;
// Fill input bindings.
if (!CHECK((hr = FillInputBindings(m_pTable,
DBACCESSOR_PARAMETERDATA, m_cSPBindings, m_rgSPBindings,
(BYTE **)&pData, (ulRowNum-1), m_cSPParamColMap, m_rgSPParamColMap)), S_OK))
{
return hr;
}
return m_pIEmptyCommand->Execute(NULL, IID_IRowset,
pDbParams, &m_cSPRowsAffected, (IUnknown **)&m_pSPRowset);
}
// {{ TCW_VAR_PROTOTYPE(1)
//*-----------------------------------------------------------------------
// @mfunc An invalid parameter in pParams, returns DB_E_INVALIDARG
//
// @rdesc TEST_PASS or TEST_FAIL
//
int TCCommandExecute_Rowset::Variation_1()
{
DBPARAMS sInvalidDbParams;
DBROWCOUNT cRowsAffected;
IRowset * pRowset = NULL;
sInvalidDbParams.cParamSets = 1;
sInvalidDbParams.hAccessor = m_hAccessor;
sInvalidDbParams.pData = NULL;
if (!CHECK(m_pICommand->Execute(NULL, IID_NULL, &sInvalidDbParams, &cRowsAffected,
(IUnknown **)&pRowset), E_INVALIDARG))
{
return TEST_FAIL;
}
return TEST_PASS;
}
// }}
// {{ TCW_VAR_PROTOTYPE(2)
//*-----------------------------------------------------------------------
// @mfunc Supply too many parameter info than required. Should return S_OK.
//
// @rdesc TEST_PASS or TEST_FAIL
//
int TCCommandExecute_Rowset::Variation_2()
{
DBCOUNTITEM cBindings;
DBBINDING *rgBindings = NULL;
DB_LORDINAL *rgParamColMap = NULL;
BOOL fResult = FALSE;
void *pData = NULL;
HACCESSOR hAccessor = DB_NULL_HACCESSOR;
DBPARAMS ExecDbParams;
DBBYTEOFFSET ulOffset;
DBROWCOUNT cRowsAffected = 0;
IRowset * pRowset = NULL;
HRESULT hr = E_FAIL;
UINT i = 0;
DBCOUNTITEM ulRowNum;
// One more than number of bindings we need.
cBindings = m_cBindings + 1;
// Allocate memory for the array.
rgBindings = (DBBINDING *)m_pIMalloc->Alloc(cBindings * sizeof(DBBINDING));
if (rgBindings == NULL)
{
odtLog << wszMemoryAllocationError;
return TEST_FAIL;
}
rgParamColMap = (DB_LORDINAL *)m_pIMalloc->Alloc(cBindings * sizeof(DB_LORDINAL));
if (rgParamColMap == NULL)
{
odtLog << wszMemoryAllocationError;
goto CLEANUP;
}
// Copy all the bindings.
memcpy(rgBindings, m_rgBindings, (size_t)m_cBindings*sizeof(DBBINDING));
memcpy(rgParamColMap, m_rgParamColMap, (size_t)m_cBindings*sizeof (DB_LORDINAL));
// Now initialize the last binding.
rgBindings[cBindings -1] = rgBindings[cBindings -2];
// Also initialize the column array. (We have sufficient space in the column array)
rgParamColMap[cBindings - 1] = rgParamColMap[cBindings - 2];
// Get to the end.
ulOffset = rgBindings[cBindings -1].obValue + sizeof(DBLENGTH);
//If we bound the value, we need to compensate for any extra room
//the value may have taken over the bValue size allocated in the struct
if (rgBindings[cBindings -1].dwPart & DBPART_VALUE)
if (rgBindings[cBindings - 1].cbMaxLen > sizeof(DBLENGTH))
ulOffset += rgBindings[cBindings - 1].cbMaxLen - sizeof(DBLENGTH);
//Make sure our structure begins on a correct byte alignment
ulOffset = ROUND_UP(ulOffset,ROUND_UP_AMOUNT);
if (rgBindings[cBindings - 1].dwPart & DBPART_VALUE)
{
rgBindings[cBindings - 1].obValue = ulOffset + offsetof(DATA,bValue);
}
rgBindings[cBindings - 1].pObject = NULL;
//Only set length offset if we're binding length
if (rgBindings[cBindings - 1].dwPart & DBPART_LENGTH)
{
rgBindings[cBindings - 1].obLength = ulOffset + offsetof(DATA,ulLength);
}
//Only set status offset if we're binding status
if (rgBindings[cBindings - 1].dwPart & DBPART_STATUS)
{
rgBindings[cBindings - 1].obStatus = ulOffset + offsetof(DATA,sStatus);
}
// Now create data for these accessors.
ulRowNum = NextInsertRowNum();
if (!CHECK(FillInputBindings((CTable *)m_pThisTestModule->m_pVoid, DBACCESSOR_PARAMETERDATA,
cBindings, rgBindings, (BYTE **)&pData, ulRowNum, cBindings, rgParamColMap), S_OK))
{
goto CLEANUP;
}
// change parameter ordinal.
rgBindings[cBindings -1].iOrdinal++;
for (i = 0; i < cBindings; i++)
{
rgBindings[i].dwMemOwner = DBMEMOWNER_CLIENTOWNED;
rgBindings[i].eParamIO = DBPARAMIO_INPUT;
rgBindings[i].dwPart = DBPART_VALUE | DBPART_LENGTH | DBPART_STATUS;
rgBindings[i].dwFlags = 0;
}
// Now create the parameter accessor with the extra ordinal.
if (!CHECK (m_pCmdIAccessor->CreateAccessor( DBACCESSOR_PARAMETERDATA,
cBindings, rgBindings, m_cbRowSize, &hAccessor, NULL), S_OK))
{
goto CLEANUP;
}
ExecDbParams.cParamSets = 1;
ExecDbParams.hAccessor = hAccessor;
ExecDbParams.pData = pData;
// Too many parameters supplied.
// Should return DB_E_ERRORSOCCURRED or DB_E_BADORDINAL
hr = m_pICommand->Execute(NULL, IID_IUnknown, &ExecDbParams, &cRowsAffected, (IUnknown **)&pRowset);
if (FAILED(hr))
// Since the row wasn't inserted, decrement next insert row number incremented above.
m_cInsertRowNum--;
if (hr == DB_E_ERRORSOCCURRED)
{
// Check to see if status is set for the Last arguement. if not test fail.
TEST_COMPARE ((*(DBSTATUS *)((BYTE *)pData + rgBindings[cBindings - 1].obStatus)), DBSTATUS_E_BADACCESSOR);
}
else if (hr != DB_E_BADORDINAL)
{
CHECK(hr, DB_E_ERRORSOCCURRED); // To force an error in the log
goto CLEANUP;
}
fResult = TRUE;
CLEANUP:
ReleaseDataForCommand();
if (pData)
{
ReleaseInputBindingsMemory(cBindings, rgBindings, (BYTE *)pData, TRUE);
pData = NULL;
}
FREE_DATA (rgBindings);
FREE_DATA (rgParamColMap);
if (hAccessor)
CHECK(m_pCmdIAccessor->ReleaseAccessor (hAccessor, NULL), S_OK);
return (fResult) ? TEST_PASS : TEST_FAIL;
}
// }}
// {{ TCW_VAR_PROTOTYPE(3)
//*-----------------------------------------------------------------------
// @mfunc Supply too few parameters than required, returns DB_E_PARAMNOTOPTIONAL
//
// @rdesc TEST_PASS or TEST_FAIL
//
int TCCommandExecute_Rowset::Variation_3()
{
HACCESSOR hAccessor = DB_NULL_HACCESSOR;
DBCOUNTITEM cBindings = m_cBindings - m_cBindings/2;
DBPARAMS ExecDbParams;
void *pData = NULL;
DBROWCOUNT cRowsAffected;
IRowset * pRowset = NULL;
BOOL fResult = FALSE;
DBCOUNTITEM ulRowNum = 0;
HRESULT hr = E_FAIL;
// Now create the parameter accessor with less bindings.
if (!CHECK (m_pCmdIAccessor->CreateAccessor(
DBACCESSOR_PARAMETERDATA,
cBindings, m_rgBindings, m_cbRowSize,
&hAccessor, NULL), S_OK))
{
goto CLEANUP;
}
ulRowNum = NextInsertRowNum();
if (!CHECK(FillInputBindings((CTable *)m_pThisTestModule->m_pVoid,
DBACCESSOR_PARAMETERDATA,
cBindings, m_rgBindings,
(BYTE **)&pData, ulRowNum, m_cParamColMap,(DB_LORDINAL *)m_rgParamColMap), S_OK))
{
goto CLEANUP;
}
ExecDbParams.cParamSets = 1;
ExecDbParams.hAccessor = hAccessor;
ExecDbParams.pData = pData;
// Now call Execute. It should fail with DB_E_PARAMNOTOPTIONAL;
//
hr = m_pICommand->Execute(NULL, IID_IUnknown, &ExecDbParams, &cRowsAffected,
(IUnknown **)&pRowset);
if (FAILED(hr))
// Since the row wasn't inserted, decrement next insert row number incremented above.
m_cInsertRowNum--;
if (!CHECK(hr, DB_E_PARAMNOTOPTIONAL))
{
goto CLEANUP;
}
fResult = TRUE;
CLEANUP:
if (pData)
{
ReleaseInputBindingsMemory(cBindings, m_rgBindings, (BYTE *)pData, TRUE);
pData = NULL;
}
if (hAccessor)
CHECK(m_pCmdIAccessor->ReleaseAccessor (hAccessor, NULL), S_OK);
return (fResult) ? TEST_PASS : TEST_FAIL;
}
// }}
// {{ TCW_VAR_PROTOTYPE(4)
//*-----------------------------------------------------------------------
// @mfunc Supply more than one value for the same parameter. Returns DB_E_DUPLICATEPARAM
//
// @rdesc TEST_PASS or TEST_FAIL
//
int TCCommandExecute_Rowset::Variation_4()
{
HACCESSOR hAccessor = DB_NULL_HACCESSOR;
DBCOUNTITEM cBindings = m_cBindings - m_cBindings/2;
IRowset * pRowset = NULL;
BOOL fResult = FALSE;
DBBINDSTATUS *rgStatus = NULL;
DBORDINAL cSaveCol;
if (m_cBindings < 3)
{
odtLog << L"Too few columns in this table for this variation\n";
return TEST_SKIPPED;
}
// Reassigning the column number to create a duplicate parameter.
cSaveCol = m_rgBindings[1].iOrdinal;
m_rgBindings[1].iOrdinal = m_rgBindings[2].iOrdinal;
rgStatus = (DBBINDSTATUS *)m_pIMalloc->Alloc(m_cBindings*sizeof(DBBINDSTATUS));
if (!rgStatus)
{
odtLog << wszMemoryAllocationError;
goto CLEANUP;
}
// Now create the parameter accessor with the extra ordinal.
// Create accessor will fail with the duplicate ordinal.
// This test gets completed here. (Changes in M08-M09)
if (!CHECK (m_pCmdIAccessor->CreateAccessor( DBACCESSOR_PARAMETERDATA, m_cBindings,
m_rgBindings, m_cbRowSize, &hAccessor, rgStatus), DB_E_ERRORSOCCURRED))
{
goto CLEANUP;
}
if (!COMPARE (rgStatus[2], DBBINDSTATUS_BADBINDINFO))
goto CLEANUP;
fResult = TRUE;
CLEANUP:
m_rgBindings[1].iOrdinal = cSaveCol;
FREE_DATA (rgStatus);
if (hAccessor)
CHECK(m_pCmdIAccessor->ReleaseAccessor (hAccessor, NULL), S_OK);
return (fResult) ? TEST_PASS : TEST_FAIL;
}
// }}
// {{ TCW_VAR_PROTOTYPE(5)
//*-----------------------------------------------------------------------
// @mfunc Supply a parameter value outside the domain for that parameter, returns DB_E_OVERFLOW
//
// @rdesc TEST_PASS or TEST_FAIL
//
int TCCommandExecute_Rowset::Variation_5()
{
BOOL fSuccess = FALSE; // Variation passed or failed
HRESULT hr = E_FAIL; // HRESULT
ICommand* pICommand = NULL; // ICommandPrepare Object
ICommandPrepare* pICommandPrep = NULL; // ICommandPrepare Object
ICommandWithParameters* pICmdWPar = NULL; // ICommandWithParameters.
ICommandText * pICommandText = NULL;
WCHAR * pwszSQLStmt = NULL; // SQL Statement
WCHAR * pTableName = NULL; // Name of the table
DBORDINAL pcColumns = 0; // Count of columns
ULONG count = 0; // Loop counter
ULONG ColPrec = 0; // Numerical column precision.
CList <DBTYPE, DBTYPE> DBTypeList;
CCol NewCol(m_pIMalloc); // Class CCol
IAccessor * pLocalAccessor = NULL;
HACCESSOR hAccessor = DB_NULL_HACCESSOR;
DBPARAMS Params;
void * pData = NULL; // pdata for parameters.
LONGLONG cllMax = _I64_MAX; // Max longlong value.
ULONG cParams = 1;
DB_UPARAMS rgParamOrdinals[1] = { 1 };
WCHAR wszDataSourceType[15] = L"DBTYPE_I4";
DBPARAMBINDINFO rgParamBindInfo[1] = { &wszDataSourceType[0], NULL, 2, DBPARAMFLAGS_ISINPUT|DBPARAMFLAGS_ISSIGNED, (BYTE)~255, (BYTE)~255};
// Creates a column list from the Ctable
pcColumns = m_pTable->CountColumnsOnTable();
// Loop thru column types
for( count=1; count <= pcColumns; count++)
{
m_pTable->GetColInfo(count, NewCol);
// When we find an updatable I4, I2 or I1 column we're done
if ((NewCol.GetProviderType() == DBTYPE_I4 ||
NewCol.GetProviderType() == DBTYPE_I2 ||
NewCol.GetProviderType() == DBTYPE_I1)
&& NewCol.GetUpdateable())
break;
}
// Make sure we found the type we need
if (count >= pcColumns)
{
odtLog << L"Couldn't find a DBTYPE_I1, DBTYPE_I2, or I4 column needed for this test variation.\n";
return TEST_SKIPPED;
}
// If we found an I1 column we have to fix up rgparamBindInfo
if (NewCol.GetProviderType() == DBTYPE_I2)
wcscpy(rgParamBindInfo[0].pwszDataSourceType, L"DBTYPE_I2");
if (NewCol.GetProviderType() == DBTYPE_I1)
wcscpy(rgParamBindInfo[0].pwszDataSourceType, L"DBTYPE_I1");
DBTypeList.AddHead(NewCol.GetProviderType());
// Create a table
if(!CHECK(m_pExtraTable->CreateTable(DBTypeList,
1, // Number of rows to insert
0, // Column to put index on
NULL, // Table name
PRIMARY), // Primary or secondary values
S_OK))
{
// Free memory in the list
DBTypeList.RemoveAll();
return TEST_FAIL;
}
// Get the name of the table just created
pTableName = m_pExtraTable->GetTableName();
// Alloc Memory
pwszSQLStmt = (WCHAR *) m_pIMalloc->Alloc( SP_TEXT_BLOCK_SIZE);
// Format SQL Statement
swprintf(pwszSQLStmt, g_wszInsertInvalidValue, pTableName);
// Command to return a ICommand with Text Set
if( !CHECK(m_pExtraTable->BuildCommand(pwszSQLStmt, // SQL STMT
IID_IRowset, // IID
EXECUTE_NEVER, // EXECUTE
NULL, // # Prop's
NULL, // Prop's
NULL, // Params
NULL, // # Rowset
NULL, // Rowsets
&pICommand), // ICommand
S_OK) )
goto CLEANUP;
// QI for ICommandPrepare
if( !CHECK(pICommand->QueryInterface(IID_ICommandPrepare, (void **)&pICommandPrep), S_OK) )
goto CLEANUP;
// Setup the parameter sturcture.
// 1. Create the accessor.
// Verify The Interface for a Command Accessor Object.
if (!VerifyInterface(pICommand, IID_IAccessor, COMMAND_INTERFACE, (IUnknown **)&pLocalAccessor))
{
//ICommandAccessor is not supported
goto CLEANUP;
}
// Set the text first.
if (!VerifyInterface(pICommand, IID_ICommandText, COMMAND_INTERFACE,(IUnknown **)&pICommandText))
{
//ICommandText is not supported.
goto CLEANUP;
}
// set text.
TEST_CHECK (pICommandText->SetCommandText (DBGUID_DBSQL, pwszSQLStmt), S_OK );
// Create the bindings and the create handle to accessor.
DBBINDING DbBinding;
// Set entire dbbinding structure to 0 to prevent interface remoting problems.
memset(&DbBinding, 0, sizeof(DBBINDING));
DbBinding.dwPart = DBPART_VALUE | DBPART_LENGTH | DBPART_STATUS;
DbBinding.eParamIO = DBPARAMIO_INPUT;
DbBinding.iOrdinal = 1;
DbBinding.pTypeInfo = NULL;
DbBinding.obValue = offsetof(DATA, bValue);
DbBinding.cbMaxLen = sizeof (LONGLONG);
DbBinding.obLength = offsetof(DATA, ulLength);
DbBinding.obStatus = offsetof (DATA, sStatus);
DbBinding.wType = DBTYPE_I8; // Column is I4, I2, or I1. I8 will overflow.
DbBinding.dwMemOwner = DBMEMOWNER_CLIENTOWNED;
DbBinding.pBindExt = NULL;
DbBinding.bPrecision = 0;
DbBinding.bScale = 0;
// Call create accessor.
TEST_CHECK (pLocalAccessor->CreateAccessor(
DBACCESSOR_PARAMETERDATA, 1, &DbBinding, m_cbRowSize,
&hAccessor, NULL), S_OK);
// Allocate memory
pData = (void *)m_pIMalloc->Alloc( ( 2 * sizeof (LONGLONG) + DbBinding.cbMaxLen ) );
if (pData == NULL)
{
goto CLEANUP;
}
// 2. Create the data.
memcpy((void *)((BYTE *)pData + DbBinding.obValue), &cllMax, sizeof(LONGLONG));
*((DBLENGTH *)((BYTE *)pData + DbBinding.obLength)) = sizeof (LONGLONG);
*((DBSTATUS *)((BYTE *)pData + DbBinding.obStatus)) = DBSTATUS_S_OK;
// 3. Set the ParamData.
Params.cParamSets = 1;
Params.hAccessor = hAccessor;
Params.pData = pData;
if (!VerifyInterface(pICommand, IID_ICommandWithParameters,
COMMAND_INTERFACE,(IUnknown **)&pICmdWPar))
{
//ICommandWithParameters is not supported
goto CLEANUP;
}
// Lets set the parameter info.
hr = pICmdWPar->SetParameterInfo (cParams, rgParamOrdinals, rgParamBindInfo);
if (!(hr == S_OK || hr == DB_S_TYPEINFOOVERRIDDEN ))
goto CLEANUP;
// Execute here, don't Prepare since Prepare doesn't set status if it fails.
TEST_CHECK(pICommand->Execute(NULL, IID_NULL,
&Params, NULL, NULL), DB_E_ERRORSOCCURRED);
// Check for Overflow status
TEST_COMPARE (*((DBSTATUS *)((BYTE *)pData + DbBinding.obStatus)), DBSTATUS_E_DATAOVERFLOW);
fSuccess = TRUE;
CLEANUP:
// Drop the table
m_pExtraTable->DropTable();
// Free memory in the list
DBTypeList.RemoveAll();
// Free Memory
FREE_DATA ( pwszSQLStmt );
FREE_DATA ( pData );
// Release Objects
if (pLocalAccessor) pLocalAccessor->ReleaseAccessor (hAccessor, NULL);
RELEASE (pLocalAccessor);
RELEASE (pICommandText);
RELEASE (pICommandPrep);
RELEASE (pICmdWPar);
RELEASE (pICommand);
if (fSuccess)
return TEST_PASS;
else
return TEST_FAIL;
}
// }}
// {{ TCW_VAR_PROTOTYPE(6)
//*-----------------------------------------------------------------------
// @mfunc Accessor with PARAMETER_DATA | ROWDATA | READWRITE
//
// @rdesc TEST_PASS or TEST_FAIL
//
// Verify the same accessor can be used as both a parameter and rowdata
// accessor.
int TCCommandExecute_Rowset::Variation_6()
{
HACCESSOR hAccessor = DB_NULL_HACCESSOR;
DBPARAMS ExecDbParams;
DBROWCOUNT cRowsAffected;
DBCOUNTITEM cRowsObtained = 0;
IRowset *pRowset = NULL;
IRowset *pIRowset = NULL;
BOOL fResult = FALSE;
DBCOUNTITEM ulRowNum = 0;
HRESULT hr= S_OK;
HROW * prghRows = NULL;
HRESULT hrSetProp = E_FAIL;
DBCOUNTITEM cRowsExpected = 1;
if (m_eTestCase == TC_Row)
{
odtLog << L"Row objects can't share an accessor with a command object.\n";
return TEST_SKIPPED;
}
// Some providers can't retrieve BLOB data without this property or IRowsetLocate on
if (SupportedProperty(DBPROP_ACCESSORDER, DBPROPSET_ROWSET, m_pThisTestModule->m_pIUnknown,SESSION_INTERFACE))
hrSetProp = SetRowsetProperty(m_pICommand, DBPROPSET_ROWSET, DBPROP_ACCESSORDER, (LONG_PTR)DBPROPVAL_AO_RANDOM);
// Now create the parameter accessor
// Change the bindings. to input/output bindings.
ExecDbParams.pData = NULL;
// Create a shared parameter and row accessor. Note eParamIO ignored for row accessors
// so we can leave it as DBPARAMIO_INPUT.
TEST_CHECK (m_pCmdIAccessor->CreateAccessor( DBACCESSOR_ROWDATA | DBACCESSOR_PARAMETERDATA,
m_cBindings, m_rgBindings, m_cbRowSize, &hAccessor, NULL), S_OK);
// Create data.
TEST_CHECK(FillInputBindings((CTable *)m_pThisTestModule->m_pVoid, DBACCESSOR_PARAMETERDATA,
m_cBindings, m_rgBindings, (BYTE **)&ExecDbParams.pData, (ulRowNum = NextInsertRowNum()),
m_cParamColMap,(DB_LORDINAL *)m_rgParamColMap), S_OK);
ExecDbParams.cParamSets = 1;
ExecDbParams.hAccessor = hAccessor;
// Now call Execute and expect success as this is an insert statement with params
// This is a fatal error if it doesn't succeed since we can't verify shared accessors work
TEST_CHECK(m_pICommand->Execute(NULL, IID_IUnknown, &ExecDbParams, &cRowsAffected, (IUnknown **)&pRowset), S_OK);
// Make sure the row really was inserted
TEST_COMPARE(cRow.FindRow(ulRowNum, m_pTable, NULL, &pIRowset, NULL,
0, NULL, FALSE), TRUE);
// FindRow merely finds the appropriate row. We need to validate the inserted data for the default bindings.
TEST_COMPARE(VerifyObj(m_iidExec, pIRowset, ulRowNum, 0, NULL,
FALSE, FALSE, m_pTable, &cRowsExpected), S_OK);
// Successful insert or failure shouldn't create a rowset
if (!COMPARE(pRowset, NULL))
SAFE_RELEASE(pRowset);
// Now try to use the same accessor to retrieve data
//Create a select for only the updatable columns
TEST_CHECK (m_pTable->ExecuteCommand(SELECT_UPDATEABLE, IID_IUnknown, NULL, NULL,
NULL, NULL, EXECUTE_NEVER, 0, NULL, NULL, NULL, &m_pICommand), S_OK);
// Since we've already got the accessor and data buffers created just try to execute with the new command text
TEST_CHECK(m_pICommand->Execute(NULL, IID_IRowset, &ExecDbParams, &cRowsAffected, (IUnknown **)&pRowset), S_OK);
// Make sure a rowset was opened this time
TEST_COMPARE(pRowset != NULL, TRUE);
// Rowsets don't return cRowsAffected, that is it's undefined, so we can't verify it
// Note we don't use VerifyRowset because we want to make sure we use the same accessor
// and the same bindings, whereas VerifyRowset creates the accessor and bindings for you
// Now fetch a row and compare data using the same accessor
TEST_CHECK(pRowset->GetNextRows(NULL, 0, 1, &cRowsObtained, &prghRows), S_OK);
// We asked for only one row, we'd better get it
TEST_COMPARE(cRowsObtained, 1);
// Now actually retrieve the data using the same accessor
CHECK(hr = pRowset->GetData(*prghRows, hAccessor, ExecDbParams.pData), S_OK);
if (SUCCEEDED(hr))
{
// Make sure the data was retrieved properly
TEST_COMPARE(CompareData(
m_cBindings,
(DB_LORDINAL *)m_rgParamColMap,
1,
ExecDbParams.pData,
m_cBindings,
m_rgBindings,
m_pTable,
m_pIMalloc,
PRIMARY,
COMPARE_ONLY,
COMPARE_ALL,
TRUE
), TRUE);
}
fResult = TRUE;
CLEANUP:
if (prghRows)
{
CHECK(pRowset->ReleaseRows(1, prghRows, NULL, NULL, NULL), S_OK);
PROVIDER_FREE(prghRows);
}
SAFE_RELEASE(pRowset);
SAFE_RELEASE(pIRowset);
if (ExecDbParams.pData)
{
ReleaseInputBindingsMemory(m_cBindings, m_rgBindings, (BYTE *) ExecDbParams.pData, TRUE);
ExecDbParams.pData = NULL;
}
if (hAccessor != DB_NULL_HACCESSOR)
CHECK(m_pCmdIAccessor->ReleaseAccessor (hAccessor, NULL), S_OK);
// If we set RANDOM REQUIRED above we need to set back OPTIONAL
if (hrSetProp == S_OK)
CHECK(SetRowsetProperty(m_pICommand, DBPROPSET_ROWSET, DBPROP_ACCESSORDER, (LONG_PTR)DBPROPVAL_AO_RANDOM, DBPROPOPTIONS_OPTIONAL), S_OK);
return (fResult) ? TEST_PASS : TEST_FAIL;
}
// }}
// {{ TCW_VAR_PROTOTYPE(7)
//*-----------------------------------------------------------------------
// @mfunc Accessor with ROWDATA with [in] and [in/out] parameters
//
// @rdesc TEST_PASS or TEST_FAIL
//
int TCCommandExecute_Rowset::Variation_7()
{
HACCESSOR hAccessor = DB_NULL_HACCESSOR;
HACCESSOR hSPAccessor = DB_NULL_HACCESSOR;
DBPARAMS ExecDbParams;
DBPARAMS ExecSPParams;
void *pData = NULL;
DBROWCOUNT cRowsAffected;
IRowset *pRowset = NULL;
BOOL fResult = FALSE;
DBCOUNTITEM ulRowNum = 0;
HRESULT hr = E_FAIL;
ExecDbParams.pData = NULL;
ExecSPParams.pData = NULL;
// Reset command text since another variation may have altered it.
TEST_CHECK(m_pTable->ExecuteCommand(INSERT_ALLWITHPARAMS, IID_IUnknown, NULL,
NULL, NULL, NULL, EXECUTE_NEVER, 0, NULL, NULL, NULL, &m_pICommand), S_OK);
// Now create a row accessor
TEST_CHECK (m_pCmdIAccessor->CreateAccessor( DBACCESSOR_ROWDATA,
m_cBindings, m_rgBindings, m_cbRowSize, &hAccessor, NULL), S_OK);
// Create data for the bindings
TEST_CHECK(FillInputBindings((CTable *)m_pThisTestModule->m_pVoid,
DBACCESSOR_PARAMETERDATA, m_cBindings, m_rgBindings,
(BYTE **)&pData,(ulRowNum = NextInsertRowNum()), m_cParamColMap,(DB_LORDINAL *)m_rgParamColMap), S_OK);
ExecDbParams.cParamSets = 1;
ExecDbParams.hAccessor = hAccessor;
ExecDbParams.pData = pData;
// Now call Execute and expect failure since we used a rowdata accessor for parameters.
hr = m_pICommand->Execute(NULL, IID_IUnknown, &ExecDbParams,
&cRowsAffected, (IUnknown **)&pRowset);
if (FAILED(hr))
// Since the row wasn't inserted, decrement next insert row number incremented above.
m_cInsertRowNum--;
TEST_CHECK(hr, DB_E_BADACCESSORTYPE);
// Initialize it to Null first.
if (g_ulOutParamsSupported != DBPROPVAL_OA_NOTSUPPORTED)
{
// Now create the accessor for in out parameters.
TEST_CHECK (m_pSPIAccessor->CreateAccessor( DBACCESSOR_ROWDATA ,
m_cSPBindings, m_rgSPBindings, m_cbSPRowSize, &hSPAccessor, NULL), S_OK);
ExecSPParams.cParamSets = 1;
ExecSPParams.hAccessor = hSPAccessor;
ExecSPParams.pData = NULL;
// Now call Execute. It should Fail. Since there are in/out and out parameters..
if (!CHECK (ExecuteSP(&ExecSPParams, m_ulLocalSPRowNum), DB_E_BADACCESSORTYPE))
{
odtLog << L"Should have failed with rowdata accessor.(Stored procedure with input and out parameters)\n";
goto CLEANUP;
}
}
fResult = TRUE;
CLEANUP:
if (ExecDbParams.pData)
ReleaseInputBindingsMemory(m_cBindings, m_rgBindings, (BYTE *) ExecDbParams.pData, TRUE);
if (ExecSPParams.pData)
ReleaseInputBindingsMemory(m_cSPBindings, m_rgSPBindings, (BYTE *) ExecSPParams.pData, TRUE);
ReleaseRowsetPtr(&pRowset);
if (hAccessor)
CHECK(m_pCmdIAccessor->ReleaseAccessor (hAccessor, NULL), S_OK);
return (fResult) ? TEST_PASS : TEST_FAIL;
}
// }}
// {{ TCW_VAR_PROTOTYPE(8)
//*-----------------------------------------------------------------------
// @mfunc Accessor with only status binding.
//
// @rdesc TEST_PASS or TEST_FAIL
//
int TCCommandExecute_Rowset::Variation_8()
{
HACCESSOR hAccessor = DB_NULL_HACCESSOR;
DBPARAMS ExecDbParams;
void *pData = NULL;
DBROWCOUNT cRowsAffected;
IRowset *pRowset=NULL;
BOOL fResult = FALSE;
UINT iIndex = 0, i = 0;
CCol TempCol;
DBCOUNTITEM ulRowNum = 0;
if( (m_pExtraTable->CreateTable(TOTAL_NUMBER_OF_ROWS)) != S_OK)
return FALSE;
// Lets restore the text object to insert command.
TEST_CHECK(m_pExtraTable->ExecuteCommand(INSERT_ALLWITHPARAMS, IID_NULL, NULL,
NULL, NULL, NULL, EXECUTE_NEVER, 0, NULL, NULL, NULL, &m_pICommand), S_OK);
// Fill in regular values.
TEST_CHECK(FillInputBindings(m_pExtraTable, DBACCESSOR_PARAMETERDATA, m_cBindings,
m_rgBindings, (BYTE **)&pData, ulRowNum, m_cParamColMap, (DB_LORDINAL *)m_rgParamColMap), S_OK);
// Now set the bindings of nullable columns to DBPART_STATUS and set the status bit appropriately for them.
iIndex = 0;
for (i = 1; i <= m_pExtraTable->CountColumnsOnTable(); i++)
{
CHECK(m_pExtraTable->GetColInfo(i, TempCol), S_OK);
//Record the column number in the array
//if it is
if (TempCol.GetUpdateable() )
{
ASSERT(iIndex < m_cBindings);
if (TempCol.GetNullable() == TRUE )
{
m_rgBindings[iIndex].dwPart = 0| DBPART_STATUS;
// Set pData to DBSTATUS_ISNULL;
*((DBSTATUS *)((BYTE *)pData + m_rgBindings[iIndex].obStatus)) = DBSTATUS_S_ISNULL;
}
iIndex++; // For updateable Column.
}
}
// Now create the parameter accessor
if (!CHECK (m_pCmdIAccessor->CreateAccessor( DBACCESSOR_ROWDATA | DBACCESSOR_PARAMETERDATA,
m_cBindings, m_rgBindings, m_cbRowSize, &hAccessor, NULL), S_OK))
{
goto CLEANUP;
}
ExecDbParams.cParamSets = 1;
ExecDbParams.hAccessor = hAccessor;
ExecDbParams.pData = pData;
// Now call Execute.
TEST_CHECK (m_pICommand->Execute(NULL, IID_NULL, &ExecDbParams, &cRowsAffected,
(IUnknown **)&pRowset), S_OK);
fResult = TRUE;
CLEANUP:
// Drop the table
m_pExtraTable->DropTable();
if (ExecDbParams.pData)
ReleaseInputBindingsMemory (m_cBindings, m_rgBindings, (BYTE *)pData, TRUE);
ReleaseRowsetPtr(&pRowset);
// Restoring the columnpart bindings.
for (iIndex = 0; iIndex < m_cBindings; iIndex++)
{
m_rgBindings[iIndex].dwPart = DBPART_LENGTH | DBPART_VALUE | DBPART_STATUS;
}
if (hAccessor)
CHECK(m_pCmdIAccessor->ReleaseAccessor (hAccessor, NULL), S_OK);
return (fResult) ? TEST_PASS : TEST_FAIL;
}
// }}
// {{ TCW_VAR_PROTOTYPE(9)
//*-----------------------------------------------------------------------
// @mfunc Accessor with only (value and length
//
// @rdesc TEST_PASS or TEST_FAIL
//
int TCCommandExecute_Rowset::Variation_9()
{
HACCESSOR hAccessor = DB_NULL_HACCESSOR;
DBPARAMS ExecDbParams;
void *pData = NULL;
DBROWCOUNT cRowsAffected = 0;
IRowset *pRowset = NULL;
BOOL fResult = FALSE;
UINT iIndex;
DBCOUNTITEM ulRowNum = 0;
// Reset command text since another variation may have altered it.
TEST_CHECK(m_pTable->ExecuteCommand(INSERT_ALLWITHPARAMS, IID_IUnknown, NULL,
NULL, NULL, NULL, EXECUTE_NEVER, 0, NULL, NULL, NULL, &m_pICommand), S_OK);
// changing all bindings to VALUE and length only bindings.
for (iIndex = 0; iIndex < m_cBindings; iIndex++)
{
m_rgBindings[iIndex].dwPart = 0| DBPART_VALUE | DBPART_LENGTH;
}
// Now create the parameter accessor with Value only bindings.
TEST_CHECK (m_pCmdIAccessor->CreateAccessor( DBACCESSOR_PARAMETERDATA,
m_cBindings, m_rgBindings, m_cbRowSize, &hAccessor, NULL), S_OK);
TEST_CHECK(FillInputBindings((CTable *)m_pThisTestModule->m_pVoid, DBACCESSOR_PARAMETERDATA,
m_cBindings, m_rgBindings, (BYTE **)&pData, (ulRowNum = NextInsertRowNum()), m_cParamColMap,
(DB_LORDINAL *)m_rgParamColMap), S_OK);
ExecDbParams.cParamSets = 1;
ExecDbParams.hAccessor = hAccessor;
ExecDbParams.pData = pData;
// Now call Execute.
TEST_CHECK (m_pICommand->Execute(NULL, IID_NULL, &ExecDbParams, &cRowsAffected, (IUnknown **)&pRowset), S_OK);
fResult = TRUE;
CLEANUP:
ReleaseInputBindingsMemory (m_cBindings, m_rgBindings, (BYTE *)pData, TRUE);
ReleaseRowsetPtr(&pRowset);
// Restoring the columnpart bindings.
for (iIndex = 0; iIndex < m_cBindings; iIndex++)
{
m_rgBindings[iIndex].dwPart = DBPART_LENGTH | DBPART_VALUE | DBPART_STATUS;
}
if (hAccessor)
CHECK(m_pCmdIAccessor->ReleaseAccessor (hAccessor, NULL), S_OK);
return (fResult) ? TEST_PASS : TEST_FAIL;
}
// }}
// {{ TCW_VAR_PROTOTYPE(10)
//*-----------------------------------------------------------------------
// @mfunc Verify Stored procedure (in/out
//
// @rdesc TEST_PASS or TEST_FAIL
//
int TCCommandExecute_Rowset::Variation_10()
{
BOOL fResult = FALSE;
BYTE * pData = NULL;
// Note, the row number here MUST match the row number used when creating the
// stored proc due to different SQL created for different rows if any of the
// columns are NULL.
DBCOUNTITEM ulRowNum = 1;
if (g_ulOutParamsSupported == DBPROPVAL_OA_NOTSUPPORTED)
{
odtLog << "Stored procedure syntax unknown or output Parameters are not supported \n";
return TEST_SKIPPED;
}
// The proc was created in the init, prepare to execute it
TEST_CHECK(PrepareForExecute(m_pwszExecuteProcedureString, m_cSPBindings, m_rgSPParamColOrdinals,
m_rgSPParamBind, NULL, (WCHAR *)g_pwszProcedureName, NULL), S_OK);
// Fill input bindings.
TEST_CHECK(FillInputBindings(m_pTable,
DBACCESSOR_PARAMETERDATA, m_cSPBindings, m_rgSPBindings,
(BYTE **)&pData, ulRowNum, m_cSPParamColMap,m_rgSPParamColMap), S_OK);
TEST_CHECK(ExecuteAndVerify(m_cSPBindings, 1, m_pSPParamAll, ulRowNum, m_rgSPBindings, m_cbSPRowSize,
pData, ROWSET_NONE, 0, NULL, VERIFY_USE_TABLE, TRUE), S_OK);
fResult = TRUE;
CLEANUP:
if (pData)
{
ReleaseInputBindingsMemory(m_cSPBindings, m_rgSPBindings, pData, TRUE);
pData = NULL;
}
return (fResult) ? TEST_PASS : TEST_FAIL;
}
// }}
// {{ TCW_VAR_PROTOTYPE(11)
//*-----------------------------------------------------------------------
// @mfunc DBPARAM invalid conditions.
//
// @rdesc TEST_PASS or TEST_FAIL
//
int TCCommandExecute_Rowset::Variation_11()
{
HACCESSOR hAccessor = DB_NULL_HACCESSOR;
DBPARAMS ExecDbParams;
DBROWCOUNT cRowsAffected;
IRowset * pRowset = NULL;
BOOL fResult = FALSE;
WCHAR *pwszSqlSelectAllFromTbl = NULL;
DBCOUNTITEM ulRowNum=0;
TEST_CHECK(m_pICommandText->SetCommandText(DBGUID_DBSQL , m_pwszSqlInsertAllWithParams), S_OK);
//Replace text in CommandObject With select statment with no parameters.
TEST_CHECK(m_hr = m_pTable->CreateSQLStmt(SELECT_COLLISTFROMTBL, NULL, &pwszSqlSelectAllFromTbl, NULL, NULL ), S_OK);
// Now create the parameter accessor with less bindings.
if (!CHECK (m_pCmdIAccessor->CreateAccessor( DBACCESSOR_PARAMETERDATA, m_cBindings,
m_rgBindings, m_cbRowSize, &hAccessor, NULL), S_OK))
{
goto CLEANUP;
}
ExecDbParams.cParamSets = 1;
ExecDbParams.hAccessor = hAccessor;
ExecDbParams.pData = NULL;
// Should return E_INVALIDARG
if (!CHECK(m_pICommand->Execute(NULL, IID_IUnknown, &ExecDbParams,
&cRowsAffected, (IUnknown **)&pRowset), E_INVALIDARG))
{
goto CLEANUP;
}
MakeDataForCommand(1);
// For a command executing with parameters it should return either E_INVALIDARG
ExecDbParams.cParamSets = 0;
// Should return E_INVALIDARG
if (!CHECK(m_pICommand->Execute(NULL, IID_IUnknown, &ExecDbParams, &cRowsAffected,
(IUnknown **)&pRowset), E_INVALIDARG))
{
goto CLEANUP;
}
if (!CHECK (m_pICommand->Execute (NULL, IID_IUnknown, NULL, &cRowsAffected, (IUnknown **)&pRowset),
DB_E_PARAMNOTOPTIONAL))
{
goto CLEANUP;
}
//Set Command text with no parameters.
TEST_CHECK(m_pICommandText->SetCommandText(DBGUID_DBSQL , pwszSqlSelectAllFromTbl), S_OK);
// It should execute fine. (with ExecDbParams.cParamSets = 0 and pData = NULL )
// For a command without parameter it should execute fine.
if (!CHECK(m_pICommand->Execute(NULL, IID_IUnknown, &ExecDbParams, &cRowsAffected,
(IUnknown **)&pRowset), S_OK))
{
goto CLEANUP;
}
fResult = TRUE;
CLEANUP:
ReleaseRowsetPtr(&pRowset);
ReleaseDataForCommand();
if (hAccessor)
CHECK(m_pCmdIAccessor->ReleaseAccessor (hAccessor, NULL), S_OK);
// Restore original text.
CHECK (m_pICommandText->SetCommandText(DBGUID_DBSQL , m_pwszSqlInsertAllWithParams), S_OK);
FREE_DATA (pwszSqlSelectAllFromTbl);
return (fResult) ? TEST_PASS : TEST_FAIL;
}
// {{ TCW_VAR_PROTOTYPE(12)
//*-----------------------------------------------------------------------
// @mfunc Supply a value which could not be co-erced, returns DB_E_CANTCONVERTVALUE.
//
// @rdesc TEST_PASS or TEST_FAIL
//
int TCCommandExecute_Rowset::Variation_12()
{
BOOL fSuccess = FALSE; // Variation passed or failed
HRESULT hr = E_FAIL; // HRESULT
ICommand* pICommand = NULL; // ICommandPrepare Object
WCHAR * pwszSQLStmt = NULL; // SQL Statement
WCHAR * pTableName = NULL; // Name of the table
WCHAR * pwszNumeric = NULL; // Numeric value
DBORDINAL pcColumns = 0; // Count of columns
ULONG count = 0; // Loop counter
CList <DBTYPE, DBTYPE> DBTypeList;
CCol NewCol(m_pIMalloc); // Class CCol
IAccessor * pLocalAccessor = NULL;
HACCESSOR hAccessor = DB_NULL_HACCESSOR;
DBPARAMS Params;
WCHAR wcTmpVal = 'C'; // Some temporary value.
void * pData = NULL;
// Creates a column list from the Ctable
pcColumns = m_pTable->CountColumnsOnTable();
// Loop thru column types
for( count=1; count <= pcColumns; count++)
{
m_pTable->GetColInfo(count, NewCol);
// If first column is already numeric then were done
if( IsColDateTime(NewCol.GetProviderType()) )
break;
}
if (count > pcColumns)
{
odtLog << L"Couldn't find a datetime column for this variation.\n";
return TEST_SKIPPED;
}
DBTypeList.AddHead(NewCol.GetProviderType());
// Create a table
if(!CHECK(m_pExtraTable->CreateTable(DBTypeList,
1, // Number of rows to insert
0, // Column to put index on
NULL, // Table name
PRIMARY), // Primary or secondary values
S_OK))
{
// Free memory in the list
DBTypeList.RemoveAll();
return TEST_FAIL;
}
// Get the name of the table just created
pTableName = m_pExtraTable->GetTableName();
// Alloc Memory
pwszSQLStmt = (WCHAR *) m_pIMalloc->Alloc( sizeof(WCHAR) + (sizeof(WCHAR) *
(wcslen(g_wszInsertInvalidChar) + wcslen(pTableName))) );
// Format SQL Statement
swprintf(pwszSQLStmt, g_wszInsertInvalidChar, pTableName);
// Command to return a ICommand with Text Set
if( !CHECK(m_pExtraTable->BuildCommand(pwszSQLStmt, // SQL STMT
m_iidExec, // IID
EXECUTE_NEVER, // EXECUTE
NULL, // # Prop's
NULL, // Prop's
&Params, // Params
NULL, // # Rowset
NULL, // Rowsets
&pICommand), // ICommand
S_OK) )
goto CLEANUP;
// Setup the parameter sturcture.
// 1. Create the accessor.
// Verify The Interface for a Command Accessor Object.
if (!VerifyInterface(pICommand, IID_IAccessor,
COMMAND_INTERFACE,(IUnknown **)&pLocalAccessor))
{
//ICommandAccessor is not supported
goto CLEANUP;
}
// Create the bindings and the create handle to accessor.
DBBINDING DbBinding;
memset(&DbBinding, 0, sizeof(DBBINDING));
DbBinding.dwPart = DBPART_VALUE | DBPART_LENGTH | DBPART_STATUS;
DbBinding.eParamIO = DBPARAMIO_INPUT;
DbBinding.iOrdinal = 1;
DbBinding.pTypeInfo = NULL;
DbBinding.obValue = offsetof(DATA, bValue);
DbBinding.cbMaxLen = sizeof (WCHAR);
DbBinding.obLength = offsetof(DATA, ulLength);
DbBinding.obStatus = offsetof (DATA, sStatus);
DbBinding.wType = DBTYPE_WSTR;
DbBinding.dwMemOwner = DBMEMOWNER_CLIENTOWNED;
DbBinding.pBindExt = NULL;
DbBinding.bPrecision = 0;
DbBinding.bScale = 0;
// Call create accessor.
TEST_CHECK (pLocalAccessor->CreateAccessor(
DBACCESSOR_PARAMETERDATA, 1, &DbBinding, m_cbRowSize,
&hAccessor, NULL), S_OK);
// Allocate memory
pData = (void *)m_pIMalloc->Alloc(sizeof(DATA) + DbBinding.cbMaxLen + sizeof(WCHAR));
if (pData == NULL)
{
goto CLEANUP;
}
// 2. Create the data.
memcpy((WCHAR *)((BYTE *)pData + DbBinding.obValue), &wcTmpVal, sizeof (WCHAR));
LENGTH_BINDING(DbBinding, pData) = sizeof (WCHAR);
STATUS_BINDING(DbBinding, pData) = DBSTATUS_S_OK ;
// 3. Set the ParamData.
Params.cParamSets = 1;
Params.hAccessor = hAccessor;
Params.pData = pData;
// Need to call SetParameterInfo here else Execute result is undefined for providers that can't derive
hr = pICommand->Execute(NULL, IID_NULL, &Params, NULL, NULL);
if (hr != DB_E_CANTCONVERTVALUE)
{
TEST_CHECK (hr, DB_E_ERRORSOCCURRED);
TEST_COMPARE (STATUS_BINDING(DbBinding, pData), DBSTATUS_E_CANTCONVERTVALUE);
}
fSuccess = TRUE;
CLEANUP:
// Drop the table
m_pExtraTable->DropTable();
// Free memory in the list
DBTypeList.RemoveAll();
// Free Memory
FREE_DATA ( pwszSQLStmt );
FREE_DATA ( pData);
if (pLocalAccessor) pLocalAccessor->ReleaseAccessor (hAccessor, NULL);
RELEASE (pLocalAccessor);
RELEASE ( pICommand );
if (fSuccess)
return TEST_PASS;
else
return TEST_FAIL;
}
// }}
// {{ TCW_VAR_PROTOTYPE(13)
//*-----------------------------------------------------------------------
// @mfunc hAccessor in the DBPARAMS structure was invalid (DB_E_BADACCESSORHANDLE
//
// @rdesc TEST_PASS or TEST_FAIL
//
int TCCommandExecute_Rowset::Variation_13()
{
HACCESSOR hAccessor = DB_NULL_HACCESSOR;
DBPARAMS ExecDbParams;
void *pData = NULL;
DBROWCOUNT cRowsAffected;
IRowset *pRowset = NULL;
BOOL fResult = FALSE;
DBCOUNTITEM ulRowNum = 0;
HRESULT hr = E_FAIL;
// Create data.
TEST_CHECK(FillInputBindings((CTable *)m_pThisTestModule->m_pVoid,
DBACCESSOR_PARAMETERDATA, m_cBindings, m_rgBindings,
(BYTE **)&pData,(ulRowNum = NextInsertRowNum()), m_cParamColMap,
(DB_LORDINAL *)m_rgParamColMap), S_OK);
ExecDbParams.cParamSets = 1;
ExecDbParams.hAccessor = hAccessor;
ExecDbParams.pData = pData;
hr = m_pICommand->Execute(NULL, IID_IUnknown, &ExecDbParams,
&cRowsAffected, (IUnknown **)&pRowset);
if (FAILED(hr))
// Since the row wasn't inserted, decrement next insert row number incremented above.
m_cInsertRowNum--;
TEST_CHECK(hr, DB_E_BADACCESSORHANDLE);
fResult = TRUE;
CLEANUP:
if (ExecDbParams.pData)
ReleaseInputBindingsMemory(m_cBindings, m_rgBindings, (BYTE *) ExecDbParams.pData, TRUE);
return (fResult) ? TEST_PASS : TEST_FAIL;
}
// }}
// {{ TCW_VAR_PROTOTYPE(14)
//*-----------------------------------------------------------------------
// @mfunc TEST Stuff
//
// @rdesc TEST_PASS or TEST_FAIL
//
int TCCommandExecute_Rowset::Variation_14()
{
odtLog << L"This variation only duplicates more robust testing in other variations and was removed.\n";
return TEST_SKIPPED;
}
// }}
// {{ TCW_VAR_PROTOTYPE(15)
//*-----------------------------------------------------------------------
// @mfunc Stored procedure with Valid Input/Output data.
//
// @rdesc TEST_PASS or TEST_FAIL
//
int TCCommandExecute_Rowset::Variation_15()
{
// Need to test with all paramter names set to NULL so we can verify
// names aren't required.
BOOL fResult = TRUE;
ULONG cParams, cParamSets = 1;
DBLENGTH cbRowSize;
DBCOUNTITEM ulRowNum = 1;
DBBINDING * pBINDING=NULL;
DB_UPARAMS * pParamOrdinals=NULL;
DBPARAMBINDINFO * pPARAMBIND=NULL;
WCHAR * pwszCreateProcStmt=NULL;
WCHAR * pwszExecProcStmt=NULL;
WCHAR * pwszExecStmt=NULL;
WCHAR * pwszProcName=NULL;
BYTE * pData=NULL;
ParamStruct * pParamAll=NULL;
ULONG cColumns = 0;
DB_LORDINAL * prgColumnsOrd = NULL;
BOOL fCanDerive = FALSE;
ULONG iParam;
HRESULT hrSet = DB_S_TYPEINFOOVERRIDDEN;
DB_UPARAMS cActParams = 0;
WCHAR * pNamesBuffer = NULL;
DBPARAMINFO * pParamInfo = NULL;
if (!m_fProcedureSupport)
{
odtLog << "Procedures not supported.\n";
return TEST_SKIPPED;
}
if (g_ulOutParamsSupported == DBPROPVAL_OA_NOTSUPPORTED)
{
odtLog << "Output parameters not supported.\n";
return TEST_SKIPPED;
}
// Create the syntax and binding for a stored proc with output parameters
ABORT_COMPARE(CreateProcBindings(
T_EXEC_PROC_SELECT_INOUT,// [IN] Proc type, regular proc or function (has return value)
TRUE, // [IN] If TRUE then we add parameter names to the rgParamBindInfo
1, // [IN] Number of sets of parameters to be created
DBTYPE_I2, // [IN] Return parameter type
1, // [IN] Row number in table to select, insert, or update
&cParams, // [OUT] Count of params created
&cbRowSize, // [OUT] Count of bytes for a single row of parameters
&pBINDING, // [OUT] Binding array for CreateAccessor
&pParamOrdinals,
&pPARAMBIND, // [OUT] rgParamBindInfo for SetParameterInfo
&pwszCreateProcStmt, // [OUT] SQL stmt to create the stored proc
&pwszExecProcStmt, // [OUT] SQL stmt to execute the stored proc
&pwszExecStmt, // [OUT] SQL stmt to execute without stored proc
&pwszProcName, // [OUT] Name of procedure
&pData, // [OUT] Pointer to data for the parameters
&pParamAll
), TRUE);
// Set all the parameter names NULL
for (iParam=0; iParam < cParams; iParam++)
pPARAMBIND[iParam].pwszName = NULL;
// Set up to execute the stored proc
ABORT_CHECK(PrepareForExecute(pwszExecProcStmt, cParams, pParamOrdinals, pPARAMBIND,
&fCanDerive, pwszProcName, pwszCreateProcStmt), S_OK);
if (!fCanDerive)
hrSet = S_OK;
// Now set the parameter information correctly
ABORT_CHECK(m_pICmdWParams->SetParameterInfo(cParams, pParamOrdinals, pPARAMBIND), hrSet);
// Get it again so we can validate we get back what we set.
ABORT_CHECK(m_pICmdWParams->GetParameterInfo(&cActParams, &pParamInfo, &pNamesBuffer), S_OK);
// Verify results. If we didn't get back what was set it might not be a failure
FAIL_VAR(VerifyParamInfo(cParams, pParamOrdinals, pPARAMBIND,
cActParams, pParamInfo, pNamesBuffer), S_OK);
// The final proof is that we can execute with these values
FAIL_VAR(ExecuteAndVerify(cParams, cParamSets, pParamAll, ulRowNum, pBINDING, cbRowSize, pData, ROWSET_NONE,
cColumns, prgColumnsOrd, VERIFY_USE_PDATA, TRUE), S_OK);
CLEANUP:
DropStoredProcedure(m_pICommandText, pwszProcName);
// Free the buffers we got from GetParameterInfo
PROVIDER_FREE(pParamInfo);
PROVIDER_FREE(pNamesBuffer);
PROVIDER_FREE(pBINDING);
PROVIDER_FREE(pParamOrdinals);
PROVIDER_FREE(pPARAMBIND);
PROVIDER_FREE(pwszCreateProcStmt);
PROVIDER_FREE(pwszExecProcStmt);
PROVIDER_FREE(pwszExecStmt);
PROVIDER_FREE(pwszProcName);
PROVIDER_FREE(pData);
::FreeParameterNames(cParams, pParamAll);
PROVIDER_FREE(pParamAll);
return fResult ? TEST_PASS : TEST_FAIL;
}
// }}
// {{ TCW_VAR_PROTOTYPE(16)
//*-----------------------------------------------------------------------
// @mfunc Stored Procedure with Valid OUTPUT only parameters.
//
// @rdesc TEST_PASS or TEST_FAIL
//
int TCCommandExecute_Rowset::Variation_16()
{
// Need to test with all paramter names set to NULL so we can verify
// names aren't required.
BOOL fResult = TRUE;
ULONG cParams, cParamSets = 1;
DBLENGTH cbRowSize;
DBCOUNTITEM ulRowNum = 3;
DBBINDING * pBINDING=NULL;
DB_UPARAMS * pParamOrdinals=NULL;
DBPARAMBINDINFO * pPARAMBIND=NULL;
WCHAR * pwszCreateProcStmt=NULL;
WCHAR * pwszExecProcStmt=NULL;
WCHAR * pwszExecStmt=NULL;
WCHAR * pwszProcName=NULL;
BYTE * pData=NULL;
ParamStruct * pParamAll=NULL;
ULONG cColumns = 0;
DB_LORDINAL * prgColumnsOrd = NULL;
BOOL fCanDerive = FALSE;
ULONG iParam;
HRESULT hrSet = DB_S_TYPEINFOOVERRIDDEN;
DB_UPARAMS cActParams = 0;
WCHAR * pNamesBuffer = NULL;
DBPARAMINFO * pParamInfo = NULL;
if (!m_fProcedureSupport)
{
odtLog << "Procedures not supported.\n";
return TEST_SKIPPED;
}
if (g_ulOutParamsSupported == DBPROPVAL_OA_NOTSUPPORTED)
{
odtLog << "Output parameters not supported.\n";
return TEST_SKIPPED;
}
// Create the syntax and binding for a stored proc with output parameters
ABORT_COMPARE(CreateProcBindings(
T_EXEC_PROC_SELECT_OUT,// [IN] Proc type, regular proc or function (has return value)
TRUE, // [IN] If TRUE then we add parameter names to the rgParamBindInfo
1, // [IN] Number of sets of parameters to be created
DBTYPE_I2, // [IN] Return parameter type
ulRowNum, // [IN] Row number in table to select, insert, or update
&cParams, // [OUT] Count of params created
&cbRowSize, // [OUT] Count of bytes for a single row of parameters
&pBINDING, // [OUT] Binding array for CreateAccessor
&pParamOrdinals,
&pPARAMBIND, // [OUT] rgParamBindInfo for SetParameterInfo
&pwszCreateProcStmt, // [OUT] SQL stmt to create the stored proc
&pwszExecProcStmt, // [OUT] SQL stmt to execute the stored proc
&pwszExecStmt, // [OUT] SQL stmt to execute without stored proc
&pwszProcName, // [OUT] Name of procedure
&pData, // [OUT] Pointer to data for the parameters
&pParamAll
), TRUE);
// Set all the parameter names NULL
for (iParam=0; iParam < cParams; iParam++)
pPARAMBIND[iParam].pwszName = NULL;
// Set up to execute the stored proc
ABORT_CHECK(PrepareForExecute(pwszExecProcStmt, cParams, pParamOrdinals, pPARAMBIND,
&fCanDerive, pwszProcName, pwszCreateProcStmt), S_OK);
if (!fCanDerive)
hrSet = S_OK;
// Now set the parameter information correctly
ABORT_CHECK(m_pICmdWParams->SetParameterInfo(cParams, pParamOrdinals, pPARAMBIND), hrSet);
// Get it again so we can validate we get back what we set.
ABORT_CHECK(m_pICmdWParams->GetParameterInfo(&cActParams, &pParamInfo, &pNamesBuffer), S_OK);
// Verify results. If we didn't get back what was set it might not be a failure
FAIL_VAR(VerifyParamInfo(cParams, pParamOrdinals, pPARAMBIND,
cActParams, pParamInfo, pNamesBuffer), S_OK);
// The final proof is that we can execute with these values
FAIL_VAR(ExecuteAndVerify(cParams, cParamSets, pParamAll, ulRowNum, pBINDING, cbRowSize, pData, ROWSET_NONE,
cColumns, prgColumnsOrd, VERIFY_USE_TABLE, TRUE), S_OK);
CLEANUP:
DropStoredProcedure(m_pICommandText, pwszProcName);
// Free the buffers we got from GetParameterInfo
PROVIDER_FREE(pParamInfo);
PROVIDER_FREE(pNamesBuffer);
PROVIDER_FREE(pBINDING);
PROVIDER_FREE(pParamOrdinals);
PROVIDER_FREE(pPARAMBIND);
PROVIDER_FREE(pwszCreateProcStmt);
PROVIDER_FREE(pwszExecProcStmt);
PROVIDER_FREE(pwszExecStmt);
PROVIDER_FREE(pwszProcName);
PROVIDER_FREE(pData);
::FreeParameterNames(cParams, pParamAll);
PROVIDER_FREE(pParamAll);
return fResult ? TEST_PASS : TEST_FAIL;
}
// }}
// {{ TCW_VAR_PROTOTYPE(17)
//*-----------------------------------------------------------------------
// @mfunc Stored procedure with NULL [out] and Valid [input] parameters.
//
// @rdesc TEST_PASS or TEST_FAIL
//
int TCCommandExecute_Rowset::Variation_17()
{
BOOL fResult = TRUE;
HRESULT hr = E_FAIL;
ULONG iCol;
DBCOUNTITEM ulRowNum;
DBCOUNTITEM cBinding = 0;
DBLENGTH cbRowSize = 0;
CCol TempCol;
HACCESSOR hParamAccessor = DB_NULL_HACCESSOR;
DBBINDING * pBinding = NULL;
DBPARAMBINDINFO rgParamBindInfo[2];
DB_LPARAMS rgParamOrdinals[2] = {1, 2};
DBPARAMS Param;
ParamStruct rgParamAll[2];
IAccessor * pCmdIAccessor = NULL;
BYTE * pData = NULL;
WCHAR * pTableStringsBuffer = NULL;
WCHAR * pwszCreateProc = NULL;
WCHAR * pwszExecProc = NULL;
WCHAR * pwszDropProc = NULL;
WCHAR * pwszProcName = NULL;
CList <WCHAR *, WCHAR *> DBTypeList;
// New table, no NULLS.
CTable DupColTable((IUnknown *)m_pThisTestModule->m_pIUnknown2, (LPWSTR)gwszModuleName, NULLABLE);
// Due to the nature of this variation it needs to create a table with two columns
// of identical types, looping through all the data types. This will NOT work when
// using an ini file or against a read-only provider 'cause we can't create a new table.
// I/O params only supported with procs
if (!m_fProcedureSupport)
{
odtLog << "Procedures not supported.\n";
return TEST_SKIPPED;
}
// If no output params, then no I/O params.
if (g_ulOutParamsSupported == DBPROPVAL_OA_NOTSUPPORTED)
{
odtLog << "Output parameters not supported.\n";
return TEST_SKIPPED;
}
pwszProcName = MakeObjectName(L"ICMDWPAR", wcslen(m_pTable->GetTableName()));
TEST_PTR(pwszProcName);
// Tell the syntax builder to use a temporary proc name
m_Syntax.SetProcName(pwszProcName);
//Get accessor interface on the command object on which we will do the execute
ABORT_COMPARE(VerifyInterface(m_pICommand, IID_IAccessor, COMMAND_INTERFACE, (IUnknown **)&pCmdIAccessor), TRUE);
// For simplicity, use the default table as the source of the column info for the new table
for (iCol = 1; iCol <= m_pTable->CountColumnsOnTable(); iCol++)
{
// Get the column information
ABORT_CHECK(m_pTable->GetColInfo(iCol, TempCol), S_OK);
// If the column is not updatable or is not nullable or is LONG then it's useless to us
if (!TempCol.GetUpdateable() || !TempCol.GetNullable() || TempCol.GetIsLong())
continue;
// Now we create a table with two columns of this type
DBTypeList.AddTail(TempCol.GetProviderTypeName());
DBTypeList.AddTail(TempCol.GetProviderTypeName());
// If table creation succeeded we'd like to go ahead with the rest of the
// testing and at least drop the table
hr = DupColTable.CreateTable(DBTypeList,0,0);
IF_CHECK(hr, S_OK)
{
CCol DupCol;
// Insert 5 non-NULL rows
FAIL_CHECK(DupColTable.InsertWithParams(0, PRIMARY, TRUE, NULL, 5), S_OK);
// Get the column information
ABORT_CHECK(DupColTable.GetColInfo(1, DupCol), S_OK);
// Create a parameter name for this table
rgParamAll[0].pwszParamName = m_Syntax.MakeParamName(DupCol, 'P');
rgParamAll[1].pwszParamName = m_Syntax.MakeParamName(DupCol, 'P');
// Create a statement and corresponding accessor to insert another row
hr = DupColTable.ExecuteCommand(SELECT_UPDATEABLE, IID_IUnknown,
NULL, NULL, NULL, NULL,
EXECUTE_NEVER, 0, NULL, NULL, NULL, &m_pICommand);
IF_CHECK(hr, S_OK)
{
// If prepare is supported we must prepare before we can get
// the columns info
if (m_pICommandPrepare)
{
FAIL_CHECK(m_pICommandPrepare->Prepare(1), S_OK);
}
// Create param info for optional SetParameterInfo call
AddParam(0, 1, DBPARAMIO_INPUT, rgParamAll[0].pwszParamName, TRUE, NULL, NULL, rgParamBindInfo,
rgParamAll, &DupColTable);
AddParam(1, 2, DBPARAMIO_INPUT, rgParamAll[1].pwszParamName, TRUE, NULL, NULL, rgParamBindInfo,
rgParamAll, &DupColTable);
// Call SetParameterInfo if needed
// We have to call SetParameterInfo if the provider can't derive
// otherwise Execute behavior is "undefined" per spec.
SetParameterInfoIfNeeded(2, (DB_UPARAMS *)rgParamOrdinals, rgParamBindInfo, m_pICommand);
// Create a parameter accessor
hr = GetAccessorAndBindings(pCmdIAccessor,
DBACCESSOR_PARAMETERDATA,&hParamAccessor, &pBinding, &cBinding, &cbRowSize,
DBPART_VALUE | DBPART_STATUS | DBPART_LENGTH,
ALL_COLS_BOUND, FORWARD, NO_COLS_BY_REF, NULL,
NULL, NULL, DBTYPE_EMPTY, 0, NULL, NULL,
NO_COLS_OWNED_BY_PROV, DBPARAMIO_INPUT,
BLOB_LONG);
IF_CHECK(hr, S_OK)
{
ulRowNum = DupColTable.GetNextRowNumber();
// Fill the bindings with normal data for the next available row
hr = FillInputBindings(&DupColTable, DBACCESSOR_ROWDATA, cBinding,
pBinding, &pData, ulRowNum, 0, NULL, PRIMARY);
IF_CHECK(hr, S_OK)
{
// Set the second column to be NULL. We use the second column 'cause the
// first one might be a primary key that can't be NULL.
*(DBSTATUS *)(pData+pBinding[1].obStatus) = DBSTATUS_S_ISNULL;
// Set the command text to insert with params
hr = DupColTable.ExecuteCommand(INSERT_ALLWITHPARAMS, IID_IUnknown,
NULL, NULL, NULL, NULL,
EXECUTE_NEVER, 0, NULL, NULL, NULL, &m_pICommand);
IF_CHECK(hr, S_OK)
{
DBROWCOUNT cRowsAffected = 0;
// Set the Execute params.
Param.cParamSets = 1;
Param.hAccessor = hParamAccessor;
Param.pData = pData;
// Now insert the row with the NULL
hr = m_pICommand->Execute(NULL, IID_NULL, &Param,
&cRowsAffected, NULL);
// Release the accessor now that we're done with the row
SAFE_RELEASE_ACCESSOR(pCmdIAccessor, hParamAccessor);
IF_CHECK(hr,S_OK)
{
// Make sure we have one row assuming the provider knows
IF_COMPARE(cRowsAffected == 1 || cRowsAffected == DB_COUNTUNAVAILABLE, TRUE)
{
// Change the first parameter to Input/Output
pBinding[0].eParamIO = DBPARAMIO_INPUT | DBPARAMIO_OUTPUT;
rgParamBindInfo[0].dwFlags |= DBPARAMFLAGS_ISINPUT | DBPARAMFLAGS_ISOUTPUT;
rgParamAll[0].eParamIO = pBinding[0].eParamIO;
// Give required information to the syntax builder
m_Syntax.SetColMap(cBinding-1, rgParamAll);
m_Syntax.SetCurrentRow(ulRowNum);
m_Syntax.SetTable(&DupColTable);
// Now we can actually create the stored proc and execute it
// select c2 into p1 where c1 = p1
pwszCreateProc = m_Syntax.GetSyntax(T_CREATE_PROC_SELECT_LOOKUP);
pwszExecProc = m_Syntax.GetSyntax(T_EXEC_PROC_SELECT_LOOKUP);
pwszDropProc = m_Syntax.GetSyntax(T_DROP_PROC);
// Make sure we got the pointers back
ABORT_PTR(pwszCreateProc);
ABORT_PTR(pwszExecProc);
ABORT_PTR(pwszDropProc);
// Drop the stored proc in case it exists due to a crashed test
// Normally will not exist, so don't check return code.
DupColTable.BuildCommand(pwszDropProc, IID_NULL, EXECUTE_ALWAYS, 0, NULL,
NULL, NULL, NULL, &m_pICommand);
// Create the stored proc using the syntax required
hr = DupColTable.BuildCommand(pwszCreateProc, IID_NULL, EXECUTE_ALWAYS, 0, NULL,
NULL, NULL, NULL, &m_pICommand);
IF_CHECK(hr, S_OK)
{
// Set the parameter accessor and parameters
ABORT_CHECK(pCmdIAccessor->CreateAccessor(DBACCESSOR_PARAMETERDATA,
cBinding-1,
pBinding,
cbRowSize,
&hParamAccessor,
NULL), S_OK);
Param.cParamSets = 1;
Param.hAccessor = hParamAccessor;
Param.pData = pData;
// Set the command text only
ABORT_CHECK(DupColTable.BuildCommand(pwszExecProc, IID_NULL, EXECUTE_NEVER, 0, NULL,
NULL, NULL, NULL, &m_pICommand), S_OK);
// We need to prepare the command before we can desribe params, if we can at all
if (m_pICommandPrepare)
ABORT_CHECK(hr = m_pICommandPrepare->Prepare(1), S_OK);
ABORT_CHECK(SetParameterInfoIfNeeded(1, (DB_UPARAMS *)rgParamOrdinals, rgParamBindInfo, m_pICommand), S_OK);
// Execute the stored proc
hr = DupColTable.BuildCommand(pwszExecProc, IID_NULL, EXECUTE_ALWAYS, 0, NULL,
&Param, NULL, NULL, &m_pICommand);
IF_CHECK(hr, S_OK)
{
// Validate the results. The status must be NULL.
FAIL_COMPARE(*(DBSTATUS *)(pData+pBinding[0].obStatus), DBSTATUS_S_ISNULL);
}
SAFE_RELEASE_ACCESSOR(pCmdIAccessor, hParamAccessor);
// Drop the stored proc. Can't do anything about failure
ABORT_CHECK(DupColTable.BuildCommand(pwszDropProc, IID_NULL, EXECUTE_ALWAYS, 0, NULL,
NULL, NULL, NULL, &m_pICommand), S_OK);
}
SAFE_FREE(pwszCreateProc);
SAFE_FREE(pwszExecProc);
SAFE_FREE(pwszDropProc);
}
}
}
SAFE_FREE(pData);
}
}
// Release the accessor
SAFE_RELEASE_ACCESSOR(pCmdIAccessor, hParamAccessor);
}
// Drop the table
DupColTable.DropTable();
}
DBTypeList.RemoveAll();
SAFE_FREE(pBinding);
}
CLEANUP:
m_Syntax.SetTable(m_pTable);
SAFE_FREE(pwszProcName);
SAFE_FREE(pwszCreateProc);
SAFE_FREE(pwszExecProc);
SAFE_FREE(pwszDropProc);
SAFE_FREE(pData);
SAFE_FREE(pBinding);
SAFE_FREE(pTableStringsBuffer);
SAFE_RELEASE_ACCESSOR(pCmdIAccessor, hParamAccessor);
// Drop the table
DupColTable.DropTable();
SAFE_RELEASE(pCmdIAccessor);
return fResult;
}
// }}
// {{ TCW_VAR_PROTOTYPE(18)
//*-----------------------------------------------------------------------
// @mfunc Stored procedure with NULL Output only parameters.
//
// @rdesc TEST_PASS or TEST_FAIL
//
int TCCommandExecute_Rowset::Variation_18()
{
odtLog << L"Testing that NULL output params can be returned was done in Variation_17.\n";
return TEST_SKIPPED;
}
// }}
// {{ TCW_VAR_PROTOTYPE(19)
//*-----------------------------------------------------------------------
// @mfunc Stored procedure with NULL input and Valid Output parameters.
//
// @rdesc TEST_PASS or TEST_FAIL
//
int TCCommandExecute_Rowset::Variation_19()
{
// NULL input param is illegal in the sense it will never return any rows.
// This might be tested using provider-specific batch SQL:
// insert into table values @p1; select @p1=col2 where col1 IS NULL.
odtLog << L"This variation is not a valid test for SQL providers and is provider specific, therefore was removed.\n";
return TEST_SKIPPED;
}
// }}
// {{ TCW_VAR_PROTOTYPE(20)
//*-----------------------------------------------------------------------
// @mfunc Input/Output parameters with only Status bound to NULL
//
// @rdesc TEST_PASS or TEST_FAIL
//
int TCCommandExecute_Rowset::Variation_20()
{
/*
NULL input param is illegal in the sense it will never return any rows and therefore will not
populate the output param.
We should add a test case for populating output params when the search clause doesn't return any rows. In the
meantime this variation can be skipped as it doesn't provide any
valid testing, at least for SQL based providers. We could consider just skipping the variation if the provider
is SQL based but leaving it for non-SQL providers. However, it would be provider-specific whether they support
retrieving NULL data by binding to DBSTATUS_S_ISNULL.
*/
odtLog << L"This variation is not a valid test for SQL providers and is provider specific, therefore was removed.\n";
return TEST_SKIPPED;
}
// }}
// {{ TCW_VAR_PROTOTYPE(21)
//*-----------------------------------------------------------------------
// @mfunc Output only parameters with Status only binding to NULL
//
// @rdesc TEST_PASS or TEST_FAIL
//
int TCCommandExecute_Rowset::Variation_21()
{
// "binding to NULL" has no meaning for output params, however, a NULL can be
// returned to an output param with status-only binding.
// Use "select NULL, NULL, NULL, ... into p1, p2, p3, ... from table where
// col1 = l1 and col2 = l2 ...
BOOL fResult = TRUE;
ULONG cParams, cParamSets = 1, iParam;
DBLENGTH cbRowSize = 0;
DBCOUNTITEM ulRowNum = 2;
DBBINDING * pBINDING=NULL;
DB_UPARAMS * pParamOrdinals=NULL;
DBPARAMBINDINFO * pPARAMBIND=NULL;
WCHAR * pwszCreateProcStmt=NULL;
WCHAR * pwszExecProcStmt=NULL;
WCHAR * pwszExecStmt=NULL;
WCHAR * pwszProcName=NULL;
BYTE * pData=NULL;
ParamStruct * pParamAll=NULL;
ULONG cColumns = 0;
DB_LORDINAL * prgColumnsOrd = NULL;
BOOL fCanDerive = FALSE;
if (!m_fProcedureSupport)
{
odtLog << "Procedures not supported.\n";
return TEST_SKIPPED;
}
if (g_ulOutParamsSupported == DBPROPVAL_OA_NOTSUPPORTED)
{
odtLog << "Output parameters not supported.\n";
return TEST_SKIPPED;
}
// Create the syntax and binding for a stored proc with output parameters
ABORT_COMPARE(CreateProcBindings(
T_EXEC_PROC_SELECT_OUT_NULL,// [IN] Proc type, regular proc or function (has return value)
TRUE, // [IN] If TRUE then we add parameter names to the rgParamBindInfo
1, // [IN] Number of sets of parameters to be created
DBTYPE_I2, // [IN] Return parameter type
ulRowNum, // [IN] Row number in table to select, insert, or update
&cParams, // [OUT] Count of params created
&cbRowSize, // [OUT] Count of bytes for a single row of parameters
&pBINDING, // [OUT] Binding array for CreateAccessor
&pParamOrdinals,
&pPARAMBIND, // [OUT] rgParamBindInfo for SetParameterInfo
&pwszCreateProcStmt, // [OUT] SQL stmt to create the stored proc
&pwszExecProcStmt, // [OUT] SQL stmt to execute the stored proc
&pwszExecStmt, // [OUT] SQL stmt to execute without stored proc
&pwszProcName, // [OUT] Name of procedure
&pData, // [OUT] Pointer to data for the parameters
&pParamAll
), TRUE);
// Set up to execute the stored proc
ABORT_CHECK(PrepareForExecute(pwszExecProcStmt, cParams, pParamOrdinals, pPARAMBIND,
&fCanDerive, pwszProcName, pwszCreateProcStmt), S_OK);
// Reset the binding status to only DBPART_STATUS for the output params
for (iParam=0; iParam < cParams; iParam++)
if (pBINDING[iParam].eParamIO == DBPARAMIO_OUTPUT)
pBINDING[iParam].dwPart = DBPART_STATUS;
// Validate the parameters and execute results
FAIL_VAR(ValidateGetParameterInfo(cParams, cParamSets, ulRowNum, pParamOrdinals, pPARAMBIND,
pBINDING, cbRowSize, pParamAll, pData, ROWSET_NONE, cColumns, prgColumnsOrd, fCanDerive,
VERIFY_NULL), S_OK);
CLEANUP:
DropStoredProcedure(m_pICommandText, pwszProcName);
// Free the buffers we got from GetParameterInfo
PROVIDER_FREE(pBINDING);
PROVIDER_FREE(pParamOrdinals);
PROVIDER_FREE(pPARAMBIND);
PROVIDER_FREE(pwszCreateProcStmt);
PROVIDER_FREE(pwszExecProcStmt);
PROVIDER_FREE(pwszExecStmt);
PROVIDER_FREE(pwszProcName);
PROVIDER_FREE(pData);
::FreeParameterNames(cParams, pParamAll);
PROVIDER_FREE(pParamAll);
return fResult ? TEST_PASS : TEST_FAIL;
}
// }}
// {{ TCW_VAR_PROTOTYPE(22)
//*-----------------------------------------------------------------------
// @mfunc Input/output with status only binding to S_OK
//
// @rdesc TEST_PASS or TEST_FAIL
//
int TCCommandExecute_Rowset::Variation_22()
{
// Error case (DB_E_ERRORSOCCURRED) for status-only binding on input. No output
// params are populated, all status set to DBSTATUS_E_UNAVAILABLE.
BOOL fResult = TRUE;
ULONG cParams, cParamSets = 1, iParam;
DBLENGTH cbRowSize = 0;
DBCOUNTITEM ulRowNum = 1;
DBBINDING * pBINDING=NULL;
DB_UPARAMS * pParamOrdinals=NULL;
DBPARAMBINDINFO * pPARAMBIND=NULL;
WCHAR * pwszCreateProcStmt=NULL;
WCHAR * pwszExecProcStmt=NULL;
WCHAR * pwszExecStmt=NULL;
WCHAR * pwszProcName=NULL;
BYTE * pData=NULL;
ParamStruct * pParamAll=NULL;
ULONG cColumns = 0;
DB_LORDINAL * prgColumnsOrd = NULL;
BOOL fCanDerive = FALSE;
if (!m_fProcedureSupport)
{
odtLog << "Procedures not supported.\n";
return TEST_SKIPPED;
}
if (g_ulOutParamsSupported == DBPROPVAL_OA_NOTSUPPORTED)
{
odtLog << "Output parameters not supported.\n";
return TEST_SKIPPED;
}
// Create the syntax and binding for a stored proc with output parameters
TEST_COMPARE(CreateProcBindings(
T_EXEC_PROC_SELECT_INOUT,// [IN] Proc type, regular proc or function (has return value)
TRUE, // [IN] If TRUE then we add parameter names to the rgParamBindInfo
1, // [IN] Number of sets of parameters to be created
DBTYPE_I2, // [IN] Return parameter type
1, // [IN] Row number in table to select, insert, or update
&cParams, // [OUT] Count of params created
&cbRowSize, // [OUT] Count of bytes for a single row of parameters
&pBINDING, // [OUT] Binding array for CreateAccessor
&pParamOrdinals,
&pPARAMBIND, // [OUT] rgParamBindInfo for SetParameterInfo
&pwszCreateProcStmt, // [OUT] SQL stmt to create the stored proc
&pwszExecProcStmt, // [OUT] SQL stmt to execute the stored proc
&pwszExecStmt, // [OUT] SQL stmt to execute without stored proc
&pwszProcName, // [OUT] Name of procedure
&pData, // [OUT] Pointer to data for the parameters
&pParamAll
), TRUE);
// Set up to execute the stored proc
ABORT_CHECK(PrepareForExecute(pwszExecProcStmt, cParams, pParamOrdinals, pPARAMBIND,
&fCanDerive, pwszProcName, pwszCreateProcStmt), S_OK);
// Reset the binding status to only DBPART_STATUS and status to S_OK (as it might be DBSTATUS_S_ISNULL)
for (iParam=0; iParam < cParams; iParam++)
{
if (pBINDING[iParam].eParamIO == (DBPARAMIO_OUTPUT | DBPARAMIO_INPUT))
{
pBINDING[iParam].dwPart = DBPART_STATUS;
pBINDING[iParam].cbMaxLen = ULONG_MAX; // cbMaxLen should be ignored if no value
*(DBSTATUS *)(pData+pBINDING[iParam].obStatus) = DBSTATUS_S_OK;
*(DBSTATUS *)(pData+pBINDING[iParam].obValue) = 0;
*(DBSTATUS *)(pData+pBINDING[iParam].obLength) = 0;
}
}
// Set parameter info if provider can't derive
if (!fCanDerive)
ABORT_CHECK(m_pICmdWParams->SetParameterInfo(cParams, pParamOrdinals, pPARAMBIND), S_OK);
// Create param accessor and call Execute expecting DB_E_ERRORSOCCURRED
FAIL_VAR(ExecuteAndVerify(cParams, cParamSets, pParamAll,ulRowNum, pBINDING, cbRowSize, pData, ROWSET_NONE,
cColumns, prgColumnsOrd, VERIFY_USE_PDATA, TRUE, m_pICommand, DB_E_ERRORSOCCURRED), S_OK);
// Validate the status values returned.
for (iParam=0; iParam < cParams; iParam++)
{
if (pBINDING[iParam].eParamIO == (DBPARAMIO_OUTPUT | DBPARAMIO_INPUT))
{
FAIL_COMPARE(*(DBSTATUS *)(pData+pBINDING[iParam].obStatus), DBSTATUS_E_UNAVAILABLE);
}
}
CLEANUP:
DropStoredProcedure(m_pICommandText, pwszProcName);
// Free the buffers we got from GetParameterInfo
PROVIDER_FREE(pBINDING);
PROVIDER_FREE(pParamOrdinals);
PROVIDER_FREE(pPARAMBIND);
PROVIDER_FREE(pwszCreateProcStmt);
PROVIDER_FREE(pwszExecProcStmt);
PROVIDER_FREE(pwszExecStmt);
PROVIDER_FREE(pwszProcName);
PROVIDER_FREE(pData);
::FreeParameterNames(cParams, pParamAll);
PROVIDER_FREE(pParamAll);
return fResult ? TEST_PASS : TEST_FAIL;
}
// }}
// {{ TCW_VAR_PROTOTYPE(23)
//*-----------------------------------------------------------------------
// @mfunc Output only with status only binding to S_OK
//
// @rdesc TEST_PASS or TEST_FAIL
//
int TCCommandExecute_Rowset::Variation_23()
{
// This should be DBSTATUS_S_ISNULL for NULL columns or DBSTATUS_S_OK for non-NULL columns.
BOOL fResult = TRUE;
ULONG cParams, cParamSets = 1, iParam, cParamsChanged = 0;
DBLENGTH cbRowSize = 0;
DBCOUNTITEM ulRowNum = 2;
DBBINDING * pBINDING=NULL;
DB_UPARAMS * pParamOrdinals=NULL;
DBPARAMBINDINFO * pPARAMBIND=NULL;
WCHAR * pwszCreateProcStmt=NULL;
WCHAR * pwszExecProcStmt=NULL;
WCHAR * pwszExecStmt=NULL;
WCHAR * pwszProcName=NULL;
BYTE * pData=NULL;
ParamStruct * pParamAll=NULL;
ULONG cColumns = 0;
DB_LORDINAL * prgColumnsOrd = NULL;
BOOL fCanDerive = FALSE;
HRESULT hrExp = DB_S_ERRORSOCCURRED;
if (!m_fProcedureSupport)
{
odtLog << "Procedures not supported.\n";
return TEST_SKIPPED;
}
if (g_ulOutParamsSupported == DBPROPVAL_OA_NOTSUPPORTED)
{
odtLog << "Output parameters not supported.\n";
return TEST_SKIPPED;
}
// Create the syntax and binding for a stored proc with output parameters
ABORT_COMPARE(CreateProcBindings(
T_EXEC_PROC_SELECT_OUT, // [IN] Proc type, regular proc or function (has return value)
TRUE, // [IN] If TRUE then we add parameter names to the rgParamBindInfo
1, // [IN] Number of sets of parameters to be created
DBTYPE_I2, // [IN] Return parameter type
ulRowNum, // [IN] Row number in table to select, insert, or update
&cParams, // [OUT] Count of params created
&cbRowSize, // [OUT] Count of bytes for a single row of parameters
&pBINDING, // [OUT] Binding array for CreateAccessor
&pParamOrdinals,
&pPARAMBIND, // [OUT] rgParamBindInfo for SetParameterInfo
&pwszCreateProcStmt, // [OUT] SQL stmt to create the stored proc
&pwszExecProcStmt, // [OUT] SQL stmt to execute the stored proc
&pwszExecStmt, // [OUT] SQL stmt to execute without stored proc
&pwszProcName, // [OUT] Name of procedure
&pData, // [OUT] Pointer to data for the parameters
&pParamAll
), TRUE);
// Set up to execute the stored proc
ABORT_CHECK(PrepareForExecute(pwszExecProcStmt, cParams, pParamOrdinals, pPARAMBIND,
&fCanDerive, pwszProcName, pwszCreateProcStmt), S_OK);
// Reset the binding status to only DBPART_STATUS for the output params
for (iParam=0; iParam < cParams; iParam++)
{
if (pBINDING[iParam].eParamIO == DBPARAMIO_OUTPUT)
{
pBINDING[iParam].dwPart = DBPART_STATUS;
*(DBSTATUS *)(pData+pBINDING[iParam].obStatus) = ULONG_MAX;
cParamsChanged++;
}
}
if (cParamsChanged == cParams)
hrExp = DB_E_ERRORSOCCURRED;
// Set parameter info if provider can't derive
if (!fCanDerive)
ABORT_CHECK(m_pICmdWParams->SetParameterInfo(cParams, pParamOrdinals, pPARAMBIND), S_OK);
// Create param accessor and call Execute expecting DB_S_ERRORSOCCURRED
FAIL_VAR(ExecuteAndVerify(cParams, cParamSets, pParamAll,ulRowNum, pBINDING, cbRowSize, pData, ROWSET_NONE,
cColumns, prgColumnsOrd, VERIFY_USE_TABLE, TRUE, m_pICommand, hrExp), S_OK);
// Validate the status values returned.
for (iParam=0; iParam < cParams; iParam++)
{
if (pBINDING[iParam].eParamIO == DBPARAMIO_OUTPUT)
{
WCHAR wszData[MAXDATALEN] = L"";
HRESULT hrMakeData = m_pTable->MakeData(wszData, ulRowNum, pParamAll[iParam].ulColIndex, PRIMARY, TRUE);
switch(hrMakeData)
{
case S_OK:
FAIL_COMPARE(*(DBSTATUS *)(pData+pBINDING[iParam].obStatus), DBSTATUS_S_OK);
break;
case S_FALSE:
FAIL_COMPARE(*(DBSTATUS *)(pData+pBINDING[iParam].obStatus), DBSTATUS_S_ISNULL);
break;
default:
ASSERT(!L"MakeData failure!!");
}
}
}
CLEANUP:
DropStoredProcedure(m_pICommandText, pwszProcName);
// Free the buffers we got from GetParameterInfo
PROVIDER_FREE(pBINDING);
PROVIDER_FREE(pParamOrdinals);
PROVIDER_FREE(pPARAMBIND);
PROVIDER_FREE(pwszCreateProcStmt);
PROVIDER_FREE(pwszExecProcStmt);
PROVIDER_FREE(pwszExecStmt);
PROVIDER_FREE(pwszProcName);
PROVIDER_FREE(pData);
::FreeParameterNames(cParams, pParamAll);
PROVIDER_FREE(pParamAll);
return fResult ? TEST_PASS : TEST_FAIL;
}
// }}
// {{ TCW_VAR_PROTOTYPE(24)
//*-----------------------------------------------------------------------
// @mfunc Input/Output with Length and Value only bound.
//
// @rdesc TEST_PASS or TEST_FAIL
//
int TCCommandExecute_Rowset::Variation_24()
{
// On input, provider assumes DBSTATUS_S_OK, so for a valid success case we must
// have non-NULL data for input params.
// S_OK for non-NULL ouput data, DB_S_ERRORSOCCURRED for some NULL output data.
BOOL fResult = TRUE;
ULONG cParams, cParamSets = 1, iParam;
DBLENGTH cbRowSize = 0;
DBCOUNTITEM ulRowNum = 1;
DBBINDING * pBINDING=NULL;
DB_UPARAMS * pParamOrdinals=NULL;
DBPARAMBINDINFO * pPARAMBIND=NULL;
WCHAR * pwszCreateProcStmt=NULL;
WCHAR * pwszExecProcStmt=NULL;
WCHAR * pwszExecStmt=NULL;
WCHAR * pwszProcName=NULL;
BYTE * pData=NULL;
ParamStruct * pParamAll=NULL;
ULONG cColumns = 0;
DB_LORDINAL * prgColumnsOrd = NULL;
BOOL fCanDerive = FALSE;
if (!m_fProcedureSupport)
{
odtLog << "Procedures not supported.\n";
return TEST_SKIPPED;
}
if (g_ulOutParamsSupported == DBPROPVAL_OA_NOTSUPPORTED)
{
odtLog << "Output parameters not supported.\n";
return TEST_SKIPPED;
}
// Create the syntax and binding for a stored proc with output parameters
TEST_COMPARE(CreateProcBindings(
T_EXEC_PROC_SELECT_INOUT, // [IN] Proc type, regular proc or function (has return value)
TRUE, // [IN] If TRUE then we add parameter names to the rgParamBindInfo
1, // [IN] Number of sets of parameters to be created
DBTYPE_I2, // [IN] Return parameter type
ulRowNum, // [IN] Row number in table to select, insert, or update
&cParams, // [OUT] Count of params created
&cbRowSize, // [OUT] Count of bytes for a single row of parameters
&pBINDING, // [OUT] Binding array for CreateAccessor
&pParamOrdinals,
&pPARAMBIND, // [OUT] rgParamBindInfo for SetParameterInfo
&pwszCreateProcStmt, // [OUT] SQL stmt to create the stored proc
&pwszExecProcStmt, // [OUT] SQL stmt to execute the stored proc
&pwszExecStmt, // [OUT] SQL stmt to execute without stored proc
&pwszProcName, // [OUT] Name of procedure
&pData, // [OUT] Pointer to data for the parameters
&pParamAll
), TRUE);
// Set up to execute the stored proc
ABORT_CHECK(PrepareForExecute(pwszExecProcStmt, cParams, pParamOrdinals, pPARAMBIND,
&fCanDerive, pwszProcName, pwszCreateProcStmt), S_OK);
// Reset the binding status to only DBPART_STATUS and status to S_OK (as it might be DBSTATUS_S_ISNULL)
for (iParam=0; iParam < cParams; iParam++)
{
if (pBINDING[iParam].eParamIO == (DBPARAMIO_OUTPUT | DBPARAMIO_INPUT))
{
pBINDING[iParam].dwPart = DBPART_LENGTH | DBPART_VALUE;
*(DBSTATUS *)(pData+pBINDING[iParam].obStatus) = DBSTATUS_S_OK;
}
}
// Set parameter info if provider can't derive
if (!fCanDerive)
ABORT_CHECK(m_pICmdWParams->SetParameterInfo(cParams, pParamOrdinals, pPARAMBIND), S_OK);
// Create param accessor and call Execute expecting S_OK
FAIL_VAR(ExecuteAndVerify(cParams, cParamSets, pParamAll,ulRowNum, pBINDING, cbRowSize, pData, ROWSET_NONE,
cColumns, prgColumnsOrd, VERIFY_USE_PDATA, TRUE, m_pICommand, S_OK), S_OK);
CLEANUP:
DropStoredProcedure(m_pICommandText, pwszProcName);
// Free the buffers we got from GetParameterInfo
PROVIDER_FREE(pBINDING);
PROVIDER_FREE(pParamOrdinals);
PROVIDER_FREE(pPARAMBIND);
PROVIDER_FREE(pwszCreateProcStmt);
PROVIDER_FREE(pwszExecProcStmt);
PROVIDER_FREE(pwszExecStmt);
PROVIDER_FREE(pwszProcName);
PROVIDER_FREE(pData);
::FreeParameterNames(cParams, pParamAll);
PROVIDER_FREE(pParamAll);
return fResult ? TEST_PASS : TEST_FAIL;
}
// }}
// {{ TCW_VAR_PROTOTYPE(25)
//*-----------------------------------------------------------------------
// @mfunc Output only with Length and Value bound
//
// @rdesc TEST_PASS or TEST_FAIL
//
int TCCommandExecute_Rowset::Variation_25()
{
// S_OK for non-NULL ouput data, DB_S_ERRORSOCCURRED for some NULL output data.
// On input, provider assumes DBSTATUS_S_OK.
BOOL fResult = TRUE;
ULONG cParams, cParamSets = 1, iParam;
DBLENGTH cbRowSize = 0;
DBCOUNTITEM ulRowNum = 3;
DBCOUNTITEM cRowsObtained=0;
HROW * prghRow = NULL;
DBBINDING * pBINDING=NULL;
DB_UPARAMS * pParamOrdinals=NULL;
DBPARAMBINDINFO * pPARAMBIND=NULL;
WCHAR * pwszCreateProcStmt=NULL;
WCHAR * pwszExecProcStmt=NULL;
WCHAR * pwszExecStmt=NULL;
WCHAR * pwszProcName=NULL;
BYTE * pData=NULL;
BYTE * pDataSt = NULL;
ParamStruct * pParamAll=NULL;
ULONG cColumns = 0;
DB_LORDINAL * prgColumnsOrd = NULL;
BOOL fCanDerive = FALSE;
if (!m_fProcedureSupport)
{
odtLog << "Procedures not supported.\n";
return TEST_SKIPPED;
}
if (g_ulOutParamsSupported == DBPROPVAL_OA_NOTSUPPORTED)
{
odtLog << "Output parameters not supported.\n";
return TEST_SKIPPED;
}
// Create the syntax and binding for a stored proc with output parameters
TEST_COMPARE(CreateProcBindings(
T_EXEC_PROC_SELECT_OUT,// [IN] Proc type, regular proc or function (has return value)
TRUE, // [IN] If TRUE then we add parameter names to the rgParamBindInfo
1, // [IN] Number of sets of parameters to be created
DBTYPE_I2, // [IN] Return parameter type
ulRowNum, // [IN] Row number in table to select, insert, or update
&cParams, // [OUT] Count of params created
&cbRowSize, // [OUT] Count of bytes for a single row of parameters
&pBINDING, // [OUT] Binding array for CreateAccessor
&pParamOrdinals,
&pPARAMBIND, // [OUT] rgParamBindInfo for SetParameterInfo
&pwszCreateProcStmt, // [OUT] SQL stmt to create the stored proc
&pwszExecProcStmt, // [OUT] SQL stmt to execute the stored proc
&pwszExecStmt, // [OUT] SQL stmt to execute without stored proc
&pwszProcName, // [OUT] Name of procedure
&pData, // [OUT] Pointer to data for the parameters
&pParamAll
), TRUE);
// Set up to execute the stored proc
ABORT_CHECK(PrepareForExecute(pwszExecProcStmt, cParams, pParamOrdinals, pPARAMBIND,
&fCanDerive, pwszProcName, pwszCreateProcStmt), S_OK);
// Reset the binding status to only DBPART_STATUS and status to a bad value
for (iParam=0; iParam < cParams; iParam++)
{
if (pBINDING[iParam].eParamIO == DBPARAMIO_OUTPUT)
{
pBINDING[iParam].dwPart = DBPART_LENGTH | DBPART_VALUE;
*(DBSTATUS *)(pData+pBINDING[iParam].obStatus) = DBSTATUS_E_BADSTATUS;
}
}
// Set parameter info if provider can't derive
if (!fCanDerive)
ABORT_CHECK(m_pICmdWParams->SetParameterInfo(cParams, pParamOrdinals, pPARAMBIND), S_OK);
// Create param accessor and call Execute expecting S_OK
FAIL_VAR(ExecuteAndVerify(cParams, cParamSets, pParamAll,ulRowNum, pBINDING, cbRowSize, pData, ROWSET_NONE,
cColumns, prgColumnsOrd, VERIFY_USE_TABLE, TRUE, m_pICommand, S_OK), S_OK);
CLEANUP:
DropStoredProcedure(m_pICommandText, pwszProcName);
// Free the buffers we got from GetParameterInfo
PROVIDER_FREE(pBINDING);
PROVIDER_FREE(pParamOrdinals);
PROVIDER_FREE(pPARAMBIND);
PROVIDER_FREE(pwszCreateProcStmt);
PROVIDER_FREE(pwszExecProcStmt);
PROVIDER_FREE(pwszExecStmt);
PROVIDER_FREE(pwszProcName);
PROVIDER_FREE(pData);
::FreeParameterNames(cParams, pParamAll);
PROVIDER_FREE(pParamAll);
return fResult ? TEST_PASS : TEST_FAIL;
}
// }}
// {{ TCW_VAR_PROTOTYPE(26)
//*-----------------------------------------------------------------------
// @mfunc Input/Output with Value and Status bound
//
// @rdesc TEST_PASS or TEST_FAIL
//
int TCCommandExecute_Rowset::Variation_26()
{
// S_OK for fixed-length input data, output data. For DBTYPE_BYTES data and DBSTATUS_S_OK
// the provider assumes the length value is cbMaxLen from the binding. Error for populating
// output params. For strings we can assume NULL terminated if the length isn't bound.
BOOL fResult = TRUE;
ULONG cParams, cParamSets = 1, iParam;
DBLENGTH cbRowSize = 0;
DBCOUNTITEM ulRowNum = 2;
DBCOUNTITEM cRowsObtained=0;
HROW * prghRow = NULL;
DBBINDING * pBINDING=NULL;
DB_UPARAMS * pParamOrdinals=NULL;
DBPARAMBINDINFO * pPARAMBIND=NULL;
WCHAR * pwszCreateProcStmt=NULL;
WCHAR * pwszExecProcStmt=NULL;
WCHAR * pwszExecStmt=NULL;
WCHAR * pwszProcName=NULL;
BYTE * pData=NULL;
BYTE * pDataSt = NULL;
ParamStruct * pParamAll=NULL;
ULONG cColumns = 0;
DB_LORDINAL * prgColumnsOrd = NULL;
BOOL fCanDerive = FALSE;
if (!m_fProcedureSupport)
{
odtLog << "Procedures not supported.\n";
return TEST_SKIPPED;
}
if (g_ulOutParamsSupported == DBPROPVAL_OA_NOTSUPPORTED)
{
odtLog << "Output parameters not supported.\n";
return TEST_SKIPPED;
}
// Create the syntax and binding for a stored proc with output parameters
TEST_COMPARE(CreateProcBindings(
T_EXEC_PROC_SELECT_INOUT,// [IN] Proc type, regular proc or function (has return value)
TRUE, // [IN] If TRUE then we add parameter names to the rgParamBindInfo
1, // [IN] Number of sets of parameters to be created
DBTYPE_I2, // [IN] Return parameter type
ulRowNum, // [IN] Row number in table to select, insert, or update
&cParams, // [OUT] Count of params created
&cbRowSize, // [OUT] Count of bytes for a single row of parameters
&pBINDING, // [OUT] Binding array for CreateAccessor
&pParamOrdinals,
&pPARAMBIND, // [OUT] rgParamBindInfo for SetParameterInfo
&pwszCreateProcStmt, // [OUT] SQL stmt to create the stored proc
&pwszExecProcStmt, // [OUT] SQL stmt to execute the stored proc
&pwszExecStmt, // [OUT] SQL stmt to execute without stored proc
&pwszProcName, // [OUT] Name of procedure
&pData, // [OUT] Pointer to data for the parameters
&pParamAll
), TRUE);
// Set up to execute the stored proc
ABORT_CHECK(PrepareForExecute(pwszExecProcStmt, cParams, pParamOrdinals, pPARAMBIND,
&fCanDerive, pwszProcName, pwszCreateProcStmt), S_OK);
// Reset the binding status to only DBPART_STATUS and DBPART_VALUE
for (iParam=0; iParam < cParams; iParam++)
{
if (pBINDING[iParam].eParamIO == (DBPARAMIO_OUTPUT | DBPARAMIO_INPUT) &&
(IsFixedLength(pBINDING[iParam].wType) ||
pBINDING[iParam].wType == DBTYPE_STR ||
pBINDING[iParam].wType == DBTYPE_WSTR))
{
pBINDING[iParam].dwPart = DBPART_VALUE | DBPART_STATUS;
*(DBSTATUS *)(pData+pBINDING[iParam].obStatus) = DBSTATUS_S_OK;
}
}
// Set parameter info if provider can't derive
if (!fCanDerive)
ABORT_CHECK(m_pICmdWParams->SetParameterInfo(cParams, pParamOrdinals, pPARAMBIND), S_OK);
// Create param accessor and call Execute expecting S_OK
FAIL_VAR(ExecuteAndVerify(cParams, cParamSets, pParamAll,ulRowNum, pBINDING, cbRowSize, pData, ROWSET_NONE,
cColumns, prgColumnsOrd, VERIFY_USE_PDATA, TRUE, m_pICommand, S_OK), S_OK);
CLEANUP:
DropStoredProcedure(m_pICommandText, pwszProcName);
// Free the buffers we got from GetParameterInfo
PROVIDER_FREE(pBINDING);
PROVIDER_FREE(pParamOrdinals);
PROVIDER_FREE(pPARAMBIND);
PROVIDER_FREE(pwszCreateProcStmt);
PROVIDER_FREE(pwszExecProcStmt);
PROVIDER_FREE(pwszExecStmt);
PROVIDER_FREE(pwszProcName);
PROVIDER_FREE(pData);
::FreeParameterNames(cParams, pParamAll);
PROVIDER_FREE(pParamAll);
return fResult ? TEST_PASS : TEST_FAIL;
}
// }}
// {{ TCW_VAR_PROTOTYPE(27)
//*-----------------------------------------------------------------------
// @mfunc output only with Value and Status bound
//
// @rdesc TEST_PASS or TEST_FAIL
//
int TCCommandExecute_Rowset::Variation_27()
{
// S_OK for fixed-length input data, output data. For DBTYPE_BYTES data and DBSTATUS_S_OK
// the provider assumes the length value is cbMaxLen from the binding. Error for populating
// output params. For strings we can assume NULL terminated if the length isn't bound.
BOOL fResult = TRUE;
ULONG cParams, cParamSets = 1, iParam;
DBLENGTH cbRowSize = 0;
DBCOUNTITEM ulRowNum = 3;
DBCOUNTITEM cRowsObtained=0;
HROW * prghRow = NULL;
DBBINDING * pBINDING=NULL;
DB_UPARAMS * pParamOrdinals=NULL;
DBPARAMBINDINFO * pPARAMBIND=NULL;
WCHAR * pwszCreateProcStmt=NULL;
WCHAR * pwszExecProcStmt=NULL;
WCHAR * pwszExecStmt=NULL;
WCHAR * pwszProcName=NULL;
BYTE * pData=NULL;
BYTE * pDataSt = NULL;
ParamStruct * pParamAll=NULL;
ULONG cColumns = 0;
DB_LORDINAL * prgColumnsOrd = NULL;
BOOL fCanDerive = FALSE;
if (!m_fProcedureSupport)
{
odtLog << "Procedures not supported.\n";
return TEST_SKIPPED;
}
if (g_ulOutParamsSupported == DBPROPVAL_OA_NOTSUPPORTED)
{
odtLog << "Output parameters not supported.\n";
return TEST_SKIPPED;
}
// Create the syntax and binding for a stored proc with output parameters
TEST_COMPARE(CreateProcBindings(
T_EXEC_PROC_SELECT_OUT,// [IN] Proc type, regular proc or function (has return value)
TRUE, // [IN] If TRUE then we add parameter names to the rgParamBindInfo
1, // [IN] Number of sets of parameters to be created
DBTYPE_I2, // [IN] Return parameter type
ulRowNum, // [IN] Row number in table to select, insert, or update
&cParams, // [OUT] Count of params created
&cbRowSize, // [OUT] Count of bytes for a single row of parameters
&pBINDING, // [OUT] Binding array for CreateAccessor
&pParamOrdinals,
&pPARAMBIND, // [OUT] rgParamBindInfo for SetParameterInfo
&pwszCreateProcStmt, // [OUT] SQL stmt to create the stored proc
&pwszExecProcStmt, // [OUT] SQL stmt to execute the stored proc
&pwszExecStmt, // [OUT] SQL stmt to execute without stored proc
&pwszProcName, // [OUT] Name of procedure
&pData, // [OUT] Pointer to data for the parameters
&pParamAll
), TRUE);
// Set up to execute the stored proc
ABORT_CHECK(PrepareForExecute(pwszExecProcStmt, cParams, pParamOrdinals, pPARAMBIND,
&fCanDerive, pwszProcName, pwszCreateProcStmt), S_OK);
// Reset the binding status to only DBPART_STATUS and DBPART_VALUE
for (iParam=0; iParam < cParams; iParam++)
{
if (pBINDING[iParam].eParamIO == DBPARAMIO_INPUT)
{
pBINDING[iParam].dwPart = DBPART_VALUE | DBPART_STATUS;
*(DBSTATUS *)(pData+pBINDING[iParam].obStatus) = DBSTATUS_S_OK;
*(DBSTATUS *)(pData+pBINDING[iParam].obLength) = 0;
}
if (pBINDING[iParam].eParamIO == DBPARAMIO_OUTPUT &&
(IsFixedLength(pBINDING[iParam].wType) ||
pBINDING[iParam].wType == DBTYPE_STR ||
pBINDING[iParam].wType == DBTYPE_WSTR))
{
pBINDING[iParam].dwPart = DBPART_VALUE | DBPART_STATUS;
*(DBSTATUS *)(pData+pBINDING[iParam].obStatus) = DBSTATUS_S_OK;
*(DBSTATUS *)(pData+pBINDING[iParam].obLength) = 0;
}
}
// Set parameter info if provider can't derive
if (!fCanDerive)
ABORT_CHECK(m_pICmdWParams->SetParameterInfo(cParams, pParamOrdinals, pPARAMBIND), S_OK);
// Create param accessor and call Execute expecting S_OK
FAIL_VAR(ExecuteAndVerify(cParams, cParamSets, pParamAll,ulRowNum, pBINDING, cbRowSize, pData, ROWSET_NONE,
cColumns, prgColumnsOrd, VERIFY_USE_TABLE, TRUE, m_pICommand, S_OK), S_OK);
CLEANUP:
DropStoredProcedure(m_pICommandText, pwszProcName);
// Free the buffers we got from GetParameterInfo
PROVIDER_FREE(pBINDING);
PROVIDER_FREE(pParamOrdinals);
PROVIDER_FREE(pPARAMBIND);
PROVIDER_FREE(pwszCreateProcStmt);
PROVIDER_FREE(pwszExecProcStmt);
PROVIDER_FREE(pwszExecStmt);
PROVIDER_FREE(pwszProcName);
PROVIDER_FREE(pData);
::FreeParameterNames(cParams, pParamAll);
PROVIDER_FREE(pParamAll);
return fResult ? TEST_PASS : TEST_FAIL;
}
// }}
// {{ TCW_VAR_PROTOTYPE(28)
//*-----------------------------------------------------------------------
// @mfunc Input/output with only Value bound
//
// @rdesc TEST_PASS or TEST_FAIL
//
int TCCommandExecute_Rowset::Variation_28()
{
// Error for NULL data, S_OK for input param, error for variable length binary output data
BOOL fResult = TRUE;
ULONG cParams, cParamSets = 1, iParam;
DBLENGTH cbRowSize = 0;
DBCOUNTITEM ulRowNum = 3;
DBCOUNTITEM cRowsObtained=0;
HROW * prghRow = NULL;
DBBINDING * pBINDING=NULL;
DB_UPARAMS * pParamOrdinals=NULL;
DBPARAMBINDINFO * pPARAMBIND=NULL;
WCHAR * pwszCreateProcStmt=NULL;
WCHAR * pwszExecProcStmt=NULL;
WCHAR * pwszExecStmt=NULL;
WCHAR * pwszProcName=NULL;
BYTE * pData=NULL;
BYTE * pDataSt = NULL;
ParamStruct * pParamAll=NULL;
ULONG cColumns = 0;
DB_LORDINAL * prgColumnsOrd = NULL;
BOOL fCanDerive = FALSE;
if (!m_fProcedureSupport)
{
odtLog << "Procedures not supported.\n";
return TEST_SKIPPED;
}
if (g_ulOutParamsSupported == DBPROPVAL_OA_NOTSUPPORTED)
{
odtLog << "Output parameters not supported.\n";
return TEST_SKIPPED;
}
// Create the syntax and binding for a stored proc with output parameters
TEST_COMPARE(CreateProcBindings(
T_EXEC_PROC_SELECT_INOUT,// [IN] Proc type, regular proc or function (has return value)
TRUE, // [IN] If TRUE then we add parameter names to the rgParamBindInfo
1, // [IN] Number of sets of parameters to be created
DBTYPE_I2, // [IN] Return parameter type
ulRowNum, // [IN] Row number in table to select, insert, or update
&cParams, // [OUT] Count of params created
&cbRowSize, // [OUT] Count of bytes for a single row of parameters
&pBINDING, // [OUT] Binding array for CreateAccessor
&pParamOrdinals,
&pPARAMBIND, // [OUT] rgParamBindInfo for SetParameterInfo
&pwszCreateProcStmt, // [OUT] SQL stmt to create the stored proc
&pwszExecProcStmt, // [OUT] SQL stmt to execute the stored proc
&pwszExecStmt, // [OUT] SQL stmt to execute without stored proc
&pwszProcName, // [OUT] Name of procedure
&pData, // [OUT] Pointer to data for the parameters
&pParamAll
), TRUE);
// Set up to execute the stored proc
ABORT_CHECK(PrepareForExecute(pwszExecProcStmt, cParams, pParamOrdinals, pPARAMBIND,
&fCanDerive, pwszProcName, pwszCreateProcStmt), S_OK);
// Reset the binding status to only DBPART_VALUE. We don't reset for NULL values,
// since that's an error case and we want a success case. Variable length non null
// terminated data is also an error case since we don't know the length.
for (iParam=0; iParam < cParams; iParam++)
{
if (pBINDING[iParam].eParamIO == (DBPARAMIO_OUTPUT | DBPARAMIO_INPUT) &&
(IsFixedLength(pBINDING[iParam].wType) ||
pBINDING[iParam].wType == DBTYPE_STR ||
pBINDING[iParam].wType == DBTYPE_WSTR) &&
STATUS_BINDING(pBINDING[iParam], pData) != DBSTATUS_S_ISNULL)
{
pBINDING[iParam].dwPart = DBPART_VALUE;
pBINDING[iParam].obLength = 0;
pBINDING[iParam].obStatus = 0;
}
}
// Set parameter info if provider can't derive
if (!fCanDerive)
ABORT_CHECK(m_pICmdWParams->SetParameterInfo(cParams, pParamOrdinals, pPARAMBIND), S_OK);
// Create param accessor and call Execute expecting S_OK
FAIL_VAR(ExecuteAndVerify(cParams, cParamSets, pParamAll,ulRowNum, pBINDING, cbRowSize, pData, ROWSET_NONE,
cColumns, prgColumnsOrd, VERIFY_USE_PDATA, TRUE, m_pICommand, S_OK), S_OK);
CLEANUP:
DropStoredProcedure(m_pICommandText, pwszProcName);
// Free the buffers we got from GetParameterInfo
PROVIDER_FREE(pBINDING);
PROVIDER_FREE(pParamOrdinals);
PROVIDER_FREE(pPARAMBIND);
PROVIDER_FREE(pwszCreateProcStmt);
PROVIDER_FREE(pwszExecProcStmt);
PROVIDER_FREE(pwszExecStmt);
PROVIDER_FREE(pwszProcName);
PROVIDER_FREE(pData);
::FreeParameterNames(cParams, pParamAll);
PROVIDER_FREE(pParamAll);
return fResult ? TEST_PASS : TEST_FAIL;
}
// }}
// {{ TCW_VAR_PROTOTYPE(29)
//*-----------------------------------------------------------------------
// @mfunc output only with Value only bound.
//
// @rdesc TEST_PASS or TEST_FAIL
//
int TCCommandExecute_Rowset::Variation_29()
{
// Error for NULL data, S_OK for input param, error for variable length binary output data
BOOL fResult = TRUE;
ULONG cParams, cParamSets = 1, iParam;
DBLENGTH cbRowSize = 0;
DBCOUNTITEM ulRowNum = 3;
DBCOUNTITEM cRowsObtained=0;
HROW * prghRow = NULL;
DBBINDING * pBINDING=NULL;
DB_UPARAMS * pParamOrdinals=NULL;
DBPARAMBINDINFO * pPARAMBIND=NULL;
WCHAR * pwszCreateProcStmt=NULL;
WCHAR * pwszExecProcStmt=NULL;
WCHAR * pwszExecStmt=NULL;
WCHAR * pwszProcName=NULL;
BYTE * pData=NULL;
BYTE * pDataSt = NULL;
ParamStruct * pParamAll=NULL;
ULONG cColumns = 0;
DB_LORDINAL * prgColumnsOrd = NULL;
BOOL fCanDerive = FALSE;
if (!m_fProcedureSupport)
{
odtLog << "Procedures not supported.\n";
return TEST_SKIPPED;
}
if (g_ulOutParamsSupported == DBPROPVAL_OA_NOTSUPPORTED)
{
odtLog << "Output parameters not supported.\n";
return TEST_SKIPPED;
}
// Create the syntax and binding for a stored proc with output parameters
TEST_COMPARE(CreateProcBindings(
T_EXEC_PROC_SELECT_OUT, // [IN] Proc type, regular proc or function (has return value)
TRUE, // [IN] If TRUE then we add parameter names to the rgParamBindInfo
1, // [IN] Number of sets of parameters to be created
DBTYPE_I2, // [IN] Return parameter type
ulRowNum, // [IN] Row number in table to select, insert, or update
&cParams, // [OUT] Count of params created
&cbRowSize, // [OUT] Count of bytes for a single row of parameters
&pBINDING, // [OUT] Binding array for CreateAccessor
&pParamOrdinals,
&pPARAMBIND, // [OUT] rgParamBindInfo for SetParameterInfo
&pwszCreateProcStmt, // [OUT] SQL stmt to create the stored proc
&pwszExecProcStmt, // [OUT] SQL stmt to execute the stored proc
&pwszExecStmt, // [OUT] SQL stmt to execute without stored proc
&pwszProcName, // [OUT] Name of procedure
&pData, // [OUT] Pointer to data for the parameters
&pParamAll
), TRUE);
// Set up to execute the stored proc
ABORT_CHECK(PrepareForExecute(pwszExecProcStmt, cParams, pParamOrdinals, pPARAMBIND,
&fCanDerive, pwszProcName, pwszCreateProcStmt), S_OK);
// Reset the binding status to only DBPART_VALUE. We don't reset for NULL values,
// since that's an error case and we want a success case. Variable length non null
// terminated data is also an error case since we don't know the length.
for (iParam=0; iParam < cParams; iParam++)
{
if ((IsFixedLength(pBINDING[iParam].wType) ||
pBINDING[iParam].wType == DBTYPE_STR ||
pBINDING[iParam].wType == DBTYPE_WSTR) &&
STATUS_BINDING(pBINDING[iParam], pData) != DBSTATUS_S_ISNULL)
{
pBINDING[iParam].dwPart = DBPART_VALUE;
pBINDING[iParam].obLength = 0;
pBINDING[iParam].obStatus = 0;
}
}
// Set parameter info if provider can't derive
if (!fCanDerive)
ABORT_CHECK(m_pICmdWParams->SetParameterInfo(cParams, pParamOrdinals, pPARAMBIND), S_OK);
// Create param accessor and call Execute expecting S_OK
FAIL_VAR(ExecuteAndVerify(cParams, cParamSets, pParamAll,ulRowNum, pBINDING, cbRowSize, pData, ROWSET_NONE,
cColumns, prgColumnsOrd, VERIFY_USE_TABLE, TRUE, m_pICommand, S_OK), S_OK);
CLEANUP:
DropStoredProcedure(m_pICommandText, pwszProcName);
// Free the buffers we got from GetParameterInfo
PROVIDER_FREE(pBINDING);
PROVIDER_FREE(pParamOrdinals);
PROVIDER_FREE(pPARAMBIND);
PROVIDER_FREE(pwszCreateProcStmt);
PROVIDER_FREE(pwszExecProcStmt);
PROVIDER_FREE(pwszExecStmt);
PROVIDER_FREE(pwszProcName);
PROVIDER_FREE(pData);
::FreeParameterNames(cParams, pParamAll);
PROVIDER_FREE(pParamAll);
return fResult ? TEST_PASS : TEST_FAIL;
}
// }}
// {{ TCW_VAR_PROTOTYPE(30)
//*-----------------------------------------------------------------------
// @mfunc INPUT/OUTPUT with BY_REF bindings
//
// @rdesc TEST_PASS or TEST_FAIL
//
int TCCommandExecute_Rowset::Variation_30()
{
BOOL fResult = TRUE;
ULONG cParams, cParamSets = 1, iParam;
DBLENGTH cbRowSize = 0;
DBCOUNTITEM ulRowNum = 3, cbByRef = 0, ulOffSet=0;
DBCOUNTITEM cRowsObtained=0;
HROW * prghRow = NULL;
DBBINDING * pBINDING=NULL;
DB_UPARAMS * pParamOrdinals=NULL;
DBPARAMBINDINFO * pPARAMBIND=NULL;
WCHAR * pwszCreateProcStmt=NULL;
WCHAR * pwszExecProcStmt=NULL;
WCHAR * pwszExecStmt=NULL;
WCHAR * pwszProcName=NULL;
BYTE * pData=NULL;
BYTE * pDataBYREF=NULL;
DBBINDING * pBINDINGBYREF=NULL;
ParamStruct * pParamAll=NULL;
ULONG cColumns = 0;
DB_LORDINAL * prgColumnsOrd = NULL;
BOOL fCanDerive = FALSE;
IConvertType * pIConvertType = NULL;
DBTYPE wType;
if (!m_fProcedureSupport)
{
odtLog << "Procedures not supported.\n";
return TEST_SKIPPED;
}
if (g_ulOutParamsSupported == DBPROPVAL_OA_NOTSUPPORTED)
{
odtLog << "Output parameters not supported.\n";
return TEST_SKIPPED;
}
// Create the syntax and binding for a stored proc with output parameters
TEST_COMPARE(CreateProcBindings(
T_EXEC_PROC_SELECT_INOUT,// [IN] Proc type, regular proc or function (has return value)
TRUE, // [IN] If TRUE then we add parameter names to the rgParamBindInfo
1, // [IN] Number of sets of parameters to be created
DBTYPE_I2, // [IN] Return parameter type
ulRowNum, // [IN] Row number in table to select, insert, or update
&cParams, // [OUT] Count of params created
&cbRowSize, // [OUT] Count of bytes for a single row of parameters
&pBINDING, // [OUT] Binding array for CreateAccessor
&pParamOrdinals,
&pPARAMBIND, // [OUT] rgParamBindInfo for SetParameterInfo
&pwszCreateProcStmt, // [OUT] SQL stmt to create the stored proc
&pwszExecProcStmt, // [OUT] SQL stmt to execute the stored proc
&pwszExecStmt, // [OUT] SQL stmt to execute without stored proc
&pwszProcName, // [OUT] Name of procedure
&pData, // [OUT] Pointer to data for the parameters
&pParamAll
), TRUE);
// Get an IConverType interface from the command
VerifyInterface (m_pICommand, IID_IConvertType, COMMAND_INTERFACE, (IUnknown **)&pIConvertType);
// By default CreateProcBindings creates in-line bindings, not BYREF. We need to
// go through all the bindings and set them to BYREF. Can't use FillInputBindings
// because for input/output params we don't know the underlying row number.
// Find out how big our data buffer will be when set for BYREF.
// It is provider-specific whether fixed-len types can be bound BYREF, so
// we can't include those.
for(iParam=0; iParam < cParams; iParam++)
{
wType = pBINDING[iParam].wType;
cbByRef+=sizeof(DBSTATUS)+sizeof(DBLENGTH);
if(pIConvertType && (S_OK == pIConvertType->CanConvert(wType, wType|DBTYPE_BYREF, DBCONVERTFLAGS_PARAMETER)) ||
!IsFixedLength(wType))
cbByRef+=sizeof(LPVOID);
else
cbByRef+=GetDBTypeSize(wType);
}
// Copy the DBBINDING array since we need it to use in the comparison
TEST_ALLOC(DBBINDING, pBINDINGBYREF, 0, cParams * sizeof(DBBINDING));
memcpy(pBINDINGBYREF, pBINDING, cParams * sizeof(DBBINDING));
// Go through the old binding structure and convert to BYREF. Note we
// can't copy the old pData and convert in place because some data types
// may be smaller than sizeof(LPVOID).
for(iParam=0; iParam < cParams; iParam++)
{
wType = pBINDING[iParam].wType;
// Reset obValue, obLength, obStatus
pBINDINGBYREF[iParam].obStatus = ulOffSet;
ulOffSet+=sizeof(DBSTATUS);
ulOffSet = ROUND_UP(ulOffSet, ROUND_UP_AMOUNT);
pBINDINGBYREF[iParam].obLength = ulOffSet;
ulOffSet+=sizeof(DBLENGTH);
pBINDINGBYREF[iParam].obValue = ulOffSet;
if(pIConvertType && (S_OK == pIConvertType->CanConvert(wType, wType|DBTYPE_BYREF, DBCONVERTFLAGS_PARAMETER)) ||
!IsFixedLength(wType))
{
// Change the binding to BYREF.
pBINDINGBYREF[iParam].wType |= DBTYPE_BYREF;
pBINDINGBYREF[iParam].cbMaxLen = sizeof(LPVOID);
}
ulOffSet+=pBINDINGBYREF[iParam].cbMaxLen;
ulOffSet = ROUND_UP(ulOffSet, ROUND_UP_AMOUNT);
}
// Create a new pDataBYREF buffer to use for the BYREF bindings. We'll just
// point our new params to the old pData since it already has all the right
// values.
TEST_ALLOC(BYTE, pDataBYREF, 0, (size_t)ulOffSet);
for(iParam=0; iParam < cParams; iParam++)
{
wType = pBINDING[iParam].wType;
// Copy the status
STATUS_BINDING(pBINDINGBYREF[iParam], pDataBYREF) = STATUS_BINDING(pBINDING[iParam], pData);
// Copy the length
LENGTH_BINDING(pBINDINGBYREF[iParam], pDataBYREF) = LENGTH_BINDING(pBINDING[iParam], pData);
if(pIConvertType && (S_OK == pIConvertType->CanConvert(wType, wType|DBTYPE_BYREF, DBCONVERTFLAGS_PARAMETER)) ||
!IsFixedLength(wType))
{
// Set the value to point to the old pData's value
(LPVOID)VALUE_BINDING(pBINDINGBYREF[iParam], pDataBYREF) = &VALUE_BINDING(pBINDING[iParam], pData);
// If the data for the parameter is NULL, make the pointer NULL.
if (STATUS_BINDING(pBINDINGBYREF[iParam], pData) == DBSTATUS_S_ISNULL)
VALUE_BINDING(pBINDINGBYREF[iParam], pDataBYREF) = NULL;
}
else
{
pBINDINGBYREF[iParam].cbMaxLen = pBINDING[iParam].cbMaxLen;
// Copy the data
memcpy(&VALUE_BINDING(pBINDINGBYREF[iParam], pDataBYREF),
&VALUE_BINDING(pBINDING[iParam], pData), (size_t)pBINDINGBYREF[iParam].cbMaxLen);
}
}
// Set up to execute the stored proc
ABORT_CHECK(PrepareForExecute(pwszExecProcStmt, cParams, pParamOrdinals, pPARAMBIND,
&fCanDerive, pwszProcName, pwszCreateProcStmt), S_OK);
// Set parameter info if provider can't derive
if (!fCanDerive)
ABORT_CHECK(m_pICmdWParams->SetParameterInfo(cParams, pParamOrdinals, pPARAMBIND), S_OK);
// Create param accessor and call Execute expecting S_OK
FAIL_VAR(ExecuteAndVerify(cParams, cParamSets, pParamAll,ulRowNum, pBINDINGBYREF, cbByRef, pDataBYREF, ROWSET_NONE,
cColumns, prgColumnsOrd, VERIFY_USE_PDATA, TRUE, m_pICommand, S_OK, pBINDING, pData+cbRowSize), S_OK);
CLEANUP:
DropStoredProcedure(m_pICommandText, pwszProcName);
SAFE_RELEASE(pIConvertType);
// Free the buffers we got from GetParameterInfo
PROVIDER_FREE(pBINDING);
PROVIDER_FREE(pBINDINGBYREF);
PROVIDER_FREE(pParamOrdinals);
PROVIDER_FREE(pPARAMBIND);
PROVIDER_FREE(pwszCreateProcStmt);
PROVIDER_FREE(pwszExecProcStmt);
PROVIDER_FREE(pwszExecStmt);
PROVIDER_FREE(pwszProcName);
PROVIDER_FREE(pDataBYREF);
PROVIDER_FREE(pData);
::FreeParameterNames(cParams, pParamAll);
PROVIDER_FREE(pParamAll);
return fResult ? TEST_PASS : TEST_FAIL;
}
// }}
// {{ TCW_VAR_PROTOTYPE(31)
//*-----------------------------------------------------------------------
// @mfunc OUTPUT_ONLY with BY_REF bindings.
//
// @rdesc TEST_PASS or TEST_FAIL
//
int TCCommandExecute_Rowset::Variation_31()
{
BOOL fResult = TRUE;
ULONG cParams, cParamSets = 1, iParam;
DBLENGTH cbRowSize = 0;
DBCOUNTITEM ulRowNum = 3, cbByRef = 0, ulOffSet=0;
DBCOUNTITEM cRowsObtained=0;
HROW * prghRow = NULL;
DBBINDING * pBINDING=NULL;
DB_UPARAMS * pParamOrdinals=NULL;
DBPARAMBINDINFO * pPARAMBIND=NULL;
WCHAR * pwszCreateProcStmt=NULL;
WCHAR * pwszExecProcStmt=NULL;
WCHAR * pwszExecStmt=NULL;
WCHAR * pwszProcName=NULL;
BYTE * pData=NULL;
BYTE * pDataBYREF=NULL;
DBBINDING * pBINDINGBYREF=NULL;
ParamStruct * pParamAll=NULL;
ULONG cColumns = 0;
DB_LORDINAL * prgColumnsOrd = NULL;
BOOL fCanDerive = FALSE;
IConvertType * pIConvertType = NULL;
DBTYPE wType;
if (!m_fProcedureSupport)
{
odtLog << "Procedures not supported.\n";
return TEST_SKIPPED;
}
if (g_ulOutParamsSupported == DBPROPVAL_OA_NOTSUPPORTED)
{
odtLog << "Output parameters not supported.\n";
return TEST_SKIPPED;
}
// Create the syntax and binding for a stored proc with output parameters
TEST_COMPARE(CreateProcBindings(
T_EXEC_PROC_SELECT_OUT,// [IN] Proc type, regular proc or function (has return value)
TRUE, // [IN] If TRUE then we add parameter names to the rgParamBindInfo
1, // [IN] Number of sets of parameters to be created
DBTYPE_I2, // [IN] Return parameter type
ulRowNum, // [IN] Row number in table to select, insert, or update
&cParams, // [OUT] Count of params created
&cbRowSize, // [OUT] Count of bytes for a single row of parameters
&pBINDING, // [OUT] Binding array for CreateAccessor
&pParamOrdinals,
&pPARAMBIND, // [OUT] rgParamBindInfo for SetParameterInfo
&pwszCreateProcStmt, // [OUT] SQL stmt to create the stored proc
&pwszExecProcStmt, // [OUT] SQL stmt to execute the stored proc
&pwszExecStmt, // [OUT] SQL stmt to execute without stored proc
&pwszProcName, // [OUT] Name of procedure
&pData, // [OUT] Pointer to data for the parameters
&pParamAll
), TRUE);
// Get an IConverType interface from the command
VerifyInterface (m_pICommand, IID_IConvertType, COMMAND_INTERFACE, (IUnknown **)&pIConvertType);
// By default CreateProcBindings creates in-line bindings, not BYREF. We need to
// go through all the bindings and set them to BYREF. Can't use FillInputBindings
// because for input/output params we don't know the underlying row number.
// Copy the DBBINDING array since we need it to use in the comparison
TEST_ALLOC(DBBINDING, pBINDINGBYREF, 0, cParams * sizeof(DBBINDING));
memcpy(pBINDINGBYREF, pBINDING, cParams * sizeof(DBBINDING));
// Go through the old binding structure and convert to BYREF. Note we
// can't copy the old pData and convert in place because some data types
// may be smaller than sizeof(LPVOID).
for(iParam=0; iParam < cParams; iParam++)
{
wType = pBINDING[iParam].wType;
// Reset obValue, obLength, obStatus
pBINDINGBYREF[iParam].obStatus = ulOffSet;
ulOffSet+=sizeof(DBSTATUS);
ulOffSet = ROUND_UP(ulOffSet, ROUND_UP_AMOUNT);
pBINDINGBYREF[iParam].obLength = ulOffSet;
ulOffSet+=sizeof(DBLENGTH);
pBINDINGBYREF[iParam].obValue = ulOffSet;
if(pIConvertType && (S_OK == pIConvertType->CanConvert(wType, wType|DBTYPE_BYREF, DBCONVERTFLAGS_PARAMETER)) ||
!IsFixedLength(wType))
{
// Change the binding to BYREF.
pBINDINGBYREF[iParam].wType |= DBTYPE_BYREF;
pBINDINGBYREF[iParam].cbMaxLen = sizeof(LPVOID);
}
ulOffSet+=pBINDINGBYREF[iParam].cbMaxLen;
ulOffSet = ROUND_UP(ulOffSet, ROUND_UP_AMOUNT);
}
// Create a new pDataBYREF buffer to use for the BYREF bindings. We'll just
// point our new params to the old pData since it already has all the right
// values.
TEST_ALLOC(BYTE, pDataBYREF, 0, (size_t)ulOffSet);
for(iParam=0; iParam < cParams; iParam++)
{
wType = pBINDING[iParam].wType;
// Copy the status
STATUS_BINDING(pBINDINGBYREF[iParam], pDataBYREF) = STATUS_BINDING(pBINDING[iParam], pData);
// Copy the length
LENGTH_BINDING(pBINDINGBYREF[iParam], pDataBYREF) = LENGTH_BINDING(pBINDING[iParam], pData);
if(pIConvertType && (S_OK == pIConvertType->CanConvert(wType, wType|DBTYPE_BYREF, DBCONVERTFLAGS_PARAMETER)) ||
!IsFixedLength(wType))
{
// Set the value to point to the old pData's value
(LPVOID)VALUE_BINDING(pBINDINGBYREF[iParam], pDataBYREF) = &VALUE_BINDING(pBINDING[iParam], pData);
// If the data for the parameter is NULL, make the pointer NULL.
if (STATUS_BINDING(pBINDINGBYREF[iParam], pData) == DBSTATUS_S_ISNULL)
VALUE_BINDING(pBINDINGBYREF[iParam], pDataBYREF) = NULL;
}
else
{
pBINDINGBYREF[iParam].cbMaxLen = pBINDING[iParam].cbMaxLen;
// Copy the data
memcpy(&VALUE_BINDING(pBINDINGBYREF[iParam], pDataBYREF),
&VALUE_BINDING(pBINDING[iParam], pData), (size_t)pBINDINGBYREF[iParam].cbMaxLen);
}
}
// Set up to execute the stored proc
ABORT_CHECK(PrepareForExecute(pwszExecProcStmt, cParams, pParamOrdinals, pPARAMBIND,
&fCanDerive, pwszProcName, pwszCreateProcStmt), S_OK);
// Set parameter info if provider can't derive
if (!fCanDerive)
ABORT_CHECK(m_pICmdWParams->SetParameterInfo(cParams, pParamOrdinals, pPARAMBIND), S_OK);
// Create param accessor and call Execute expecting S_OK
FAIL_VAR(ExecuteAndVerify(cParams, cParamSets, pParamAll,ulRowNum, pBINDINGBYREF, ulOffSet, pDataBYREF, ROWSET_NONE,
cColumns, prgColumnsOrd, VERIFY_USE_TABLE, TRUE, m_pICommand, S_OK), S_OK);
CLEANUP:
DropStoredProcedure(m_pICommandText, pwszProcName);
SAFE_RELEASE(pIConvertType);
// Free the buffers we got from GetParameterInfo
PROVIDER_FREE(pBINDING);
PROVIDER_FREE(pBINDINGBYREF);
PROVIDER_FREE(pParamOrdinals);
PROVIDER_FREE(pPARAMBIND);
PROVIDER_FREE(pwszCreateProcStmt);
PROVIDER_FREE(pwszExecProcStmt);
PROVIDER_FREE(pwszExecStmt);
PROVIDER_FREE(pwszProcName);
PROVIDER_FREE(pDataBYREF);
PROVIDER_FREE(pData);
::FreeParameterNames(cParams, pParamAll);
PROVIDER_FREE(pParamAll);
return fResult ? TEST_PASS : TEST_FAIL;
}
// }}
// {{ TCW_VAR_PROTOTYPE(32)
//*-----------------------------------------------------------------------
// @mfunc Return value for a stored procedure.
//
// @rdesc TEST_PASS or TEST_FAIL
//
int TCCommandExecute_Rowset::Variation_32()
{
// return TestStoredProcBindings (RETURN_VALUE_TYPE, VALID_IN_OUT_DATA, LENGTH_VALUE_STATUS, REGULAR_PARAMNAMES);
// TODO: Since TCGetParameterInfo_Rowset::Variation_16 only tests one return type, use this variation
// to test other return data types if supported by the provider (Oracle does).
odtLog << L"This is already tested in TCGetParameterInfo_Rowset::Variation_16.\n";
return TEST_SKIPPED;
}
// }}
// {{ TCW_VAR_PROTOTYPE(33)
//*-----------------------------------------------------------------------
// @mfunc Bind same output parameters multiple number of times.
//
// @rdesc TEST_PASS or TEST_FAIL
//
int TCCommandExecute_Rowset::Variation_33()
{
BOOL fResult = TRUE;
ULONG cParams, cParamSets = 1;
DBLENGTH cbRowSize = 0;
DBCOUNTITEM ulRowNum = 1;
DBBINDING * pBINDING=NULL;
DB_UPARAMS * pParamOrdinals=NULL;
DBPARAMBINDINFO * pPARAMBIND=NULL;
WCHAR * pwszCreateProcStmt=NULL;
WCHAR * pwszExecProcStmt=NULL;
WCHAR * pwszExecStmt=NULL;
WCHAR * pwszProcName=NULL;
BYTE * pData=NULL;
ParamStruct * pParamAll=NULL;
ULONG cColumns = 0;
DB_LORDINAL * prgColumnsOrd = NULL;
BOOL fCanDerive = FALSE;
DBBINDING * pMultiBinding=NULL;
DBPARAMBINDINFO * pMultiParamBindInfo = NULL;
ParamStruct * pMultiParamAll = NULL;
DB_UPARAMS * pMultiParamOrdinals=NULL;
DB_LORDINAL * pMultiColMap = NULL;
ULONG cMultiParams = 0;
DBLENGTH cbMultiRowSize = 0;
ULONG iParam;
HRESULT hrSet = S_OK;
if (!m_fProcedureSupport)
{
odtLog << "Procedures not supported.\n";
return TEST_SKIPPED;
}
if (g_ulOutParamsSupported == DBPROPVAL_OA_NOTSUPPORTED)
{
odtLog << "Output parameters not supported.\n";
return TEST_SKIPPED;
}
// Create the syntax and binding for a stored proc with output parameters
ABORT_COMPARE(CreateProcBindings(
T_EXEC_PROC_SELECT_OUT, // [IN] Proc type, regular proc or function (has return value)
TRUE, // [IN] If TRUE then we add parameter names to the rgParamBindInfo
1, // [IN] Number of sets of parameters to be created
DBTYPE_I2, // [IN] Return parameter type
1, // [IN] Row number in table to select, insert, or update
&cParams, // [OUT] Count of params created
&cbRowSize, // [OUT] Count of bytes for a single row of parameters
&pBINDING, // [OUT] Binding array for CreateAccessor
&pParamOrdinals,
&pPARAMBIND, // [OUT] rgParamBindInfo for SetParameterInfo
&pwszCreateProcStmt, // [OUT] SQL stmt to create the stored proc
&pwszExecProcStmt, // [OUT] SQL stmt to execute the stored proc
&pwszExecStmt, // [OUT] SQL stmt to execute without stored proc
&pwszProcName, // [OUT] Name of procedure
NULL, // [OUT] Pointer to data for the parameters
&pParamAll
), TRUE);
// The bindings for the output parameters above are native bindings by default.
// Create a binding to WCHAR for each output parameter since that's the only
// supported/required conversion for every data type.
cMultiParams = cParams * 2;
TEST_ALLOC(DBBINDING, pMultiBinding, 0, cMultiParams*sizeof(DBBINDING));
TEST_ALLOC(DBPARAMBINDINFO, pMultiParamBindInfo, 0, cMultiParams*sizeof(DBPARAMBINDINFO));
TEST_ALLOC(DB_UPARAMS, pMultiParamOrdinals, 0, cMultiParams*sizeof(DB_UPARAMS));
TEST_ALLOC(ParamStruct, pMultiParamAll, 0, cMultiParams*sizeof(ParamStruct));
TEST_ALLOC(DB_LORDINAL, pMultiColMap, 0, cMultiParams*sizeof(DB_LORDINAL));
// Copy the binding information one-by-one, but for each output param create an additional binding
// to WCHAR.
cMultiParams = 0;
for (iParam = 0; iParam < cParams; iParam++)
{
// Copy the original binding info
memcpy(&pMultiBinding[cMultiParams], &pBINDING[iParam], sizeof(DBBINDING));
// Adjust offsets
pMultiBinding[cMultiParams].obStatus = cbMultiRowSize;
cbMultiRowSize += sizeof(DBSTATUS);
cbMultiRowSize = ROUND_UP(cbMultiRowSize, ROUND_UP_AMOUNT);
pMultiBinding[cMultiParams].obLength = cbMultiRowSize;
cbMultiRowSize += sizeof(DBLENGTH);
pMultiBinding[cMultiParams].obValue = cbMultiRowSize;
cbMultiRowSize += pMultiBinding[cMultiParams].cbMaxLen;
cbMultiRowSize = ROUND_UP(cbMultiRowSize, ROUND_UP_AMOUNT);
memcpy(&pMultiParamBindInfo[cMultiParams], &pPARAMBIND[iParam], sizeof(DBPARAMBINDINFO));
pMultiParamOrdinals[cMultiParams] = pParamOrdinals[iParam];
memcpy(&pMultiParamAll[cMultiParams], &pParamAll[iParam], sizeof(ParamStruct));
pMultiColMap[cMultiParams] = pParamAll[iParam].ulColIndex;
cMultiParams++;
// Create the WCHAR binding also if it's an output only parameter.
if (pBINDING[iParam].eParamIO == DBPARAMIO_OUTPUT)
{
CCol TempCol;
TEST_CHECK(m_pTable->GetColInfo(pParamAll[iParam].ulColIndex, TempCol), S_OK);
// Copy the original binding info
memcpy(&pMultiBinding[cMultiParams], &pBINDING[iParam], sizeof(DBBINDING));
// Adjust data type and cbMaxLen for WCHAR value
pMultiBinding[cMultiParams].wType = DBTYPE_WSTR;
pMultiBinding[cMultiParams].cbMaxLen = DisplaySize(TempCol)+sizeof(WCHAR);
// Adjust offsets
pMultiBinding[cMultiParams].obStatus = cbMultiRowSize;
cbMultiRowSize += sizeof(DBSTATUS);
cbMultiRowSize = ROUND_UP(cbMultiRowSize, ROUND_UP_AMOUNT);
pMultiBinding[cMultiParams].obLength = cbMultiRowSize;
cbMultiRowSize += sizeof(DBLENGTH);
pMultiBinding[cMultiParams].obValue = cbMultiRowSize;
cbMultiRowSize += pMultiBinding[cMultiParams].cbMaxLen;
cbMultiRowSize = ROUND_UP(cbMultiRowSize, ROUND_UP_AMOUNT);
// Copy the other associated info
memcpy(&pMultiParamBindInfo[cMultiParams], &pPARAMBIND[iParam], sizeof(DBPARAMBINDINFO));
pMultiParamOrdinals[cMultiParams] = pParamOrdinals[iParam];
memcpy(&pMultiParamAll[cMultiParams], &pParamAll[iParam], sizeof(ParamStruct));
pMultiColMap[cMultiParams] = pParamAll[iParam].ulColIndex;
cMultiParams++;
}
}
// Fill the parameters
ABORT_CHECK (FillInputBindings(m_pTable, DBACCESSOR_PARAMETERDATA, cMultiParams,
pMultiBinding, &pData, ulRowNum, cMultiParams, pMultiColMap, PRIMARY), S_OK);
// Set up to execute the stored proc
ABORT_CHECK(PrepareForExecute(pwszExecProcStmt, cMultiParams, pMultiParamOrdinals, pMultiParamBindInfo,
&fCanDerive, pwszProcName, pwszCreateProcStmt), S_OK);
// Since the count of parametr bindings doesn't match the count of parameter markers in the statement
// we ALWAYS have to call SetParameterInfo
if (fCanDerive)
hrSet = DB_S_TYPEINFOOVERRIDDEN;
ABORT_CHECK(m_pICmdWParams->SetParameterInfo(cMultiParams, pMultiParamOrdinals, pMultiParamBindInfo),
hrSet);
FAIL_VAR(ExecuteAndVerify(cMultiParams, cParamSets, pMultiParamAll, ulRowNum, pMultiBinding, cbMultiRowSize,
pData, ROWSET_NONE, cMultiParams, pMultiColMap, VERIFY_USE_TABLE, TRUE), S_OK);
CLEANUP:
DropStoredProcedure(m_pICommandText, pwszProcName);
// Free the buffers we got from GetParameterInfo
PROVIDER_FREE(pBINDING);
PROVIDER_FREE(pParamOrdinals);
PROVIDER_FREE(pPARAMBIND);
PROVIDER_FREE(pwszCreateProcStmt);
PROVIDER_FREE(pwszExecProcStmt);
PROVIDER_FREE(pwszExecStmt);
PROVIDER_FREE(pwszProcName);
PROVIDER_FREE(pData);
::FreeParameterNames(cParams, pParamAll);
PROVIDER_FREE(pParamAll);
PROVIDER_FREE(pMultiBinding);
PROVIDER_FREE(pMultiParamBindInfo);
PROVIDER_FREE(pMultiParamOrdinals);
PROVIDER_FREE(pMultiParamAll);
PROVIDER_FREE(pMultiColMap);
return fResult ? TEST_PASS : TEST_FAIL;
}
// }}
// {{ TCW_VAR_PROTOTYPE(34)
//*-----------------------------------------------------------------------
// @mfunc 1 parameter Set.
//
// @rdesc TEST_PASS or TEST_FAIL
//
int TCCommandExecute_Rowset::Variation_34()
{
BOOL fResult = FALSE;
DBROWCOUNT cRowsAffected = 0;
DBCOUNTITEM ulRowNum;
DBCOUNTITEM cRowsExpected = 1;
IRowset * pIRowset = NULL;
// Remove any old parameter info
TEST_CHECK(m_pICmdWParams->SetParameterInfo(0, NULL, NULL), S_OK);
// Set command text
TEST_CHECK (m_pICommandText->SetCommandText(DBGUID_DBSQL , m_pwszSqlInsertAllWithParams), S_OK);
// Call ICommandPrepare.
TEST_CHECK (m_pICommandPrepare->Prepare(1), S_OK);
//This sets it to one row of data.
MakeDataForCommand((ulRowNum = NextInsertRowNum()));
// Now execute ICommand.
TEST_CHECK(m_pICommand->Execute(NULL, IID_NULL,
&m_DbParamsAll, &cRowsAffected, NULL), S_OK);
// Make sure the row really was inserted
TEST_COMPARE(cRow.FindRow(ulRowNum, m_pTable, NULL, &pIRowset, NULL,
0, NULL, FALSE), TRUE);
// FindRow merely finds the appropriate row. We need to validate the inserted data for the default bindings.
TEST_COMPARE(VerifyObj(m_iidExec, pIRowset, ulRowNum, 0, NULL,
FALSE, FALSE, m_pTable, &cRowsExpected), S_OK);
fResult = TRUE;
CLEANUP:
SAFE_RELEASE(pIRowset);
ReleaseDataForCommand();
return (fResult) ? TEST_PASS : TEST_FAIL;
}
// }}
// {{ TCW_VAR_PROTOTYPE(35)
//*-----------------------------------------------------------------------
// @mfunc 5 parameter sets
//
// @rdesc TEST_PASS or TEST_FAIL
//
int TCCommandExecute_Rowset::Variation_35()
{
BOOL fResult = FALSE;
DBROWCOUNT cRowsAffected = 0;
ULONG cNumRows, i;
DBCOUNTITEM *rgRowNums=NULL;
BYTE *pData = NULL;
IRowset * pIRowset = NULL;
DBCOUNTITEM cRowsExpected = 1;
if (!g_bMultipleParamSets)
{
odtLog << "Multiple parameter sets are not supported \n";
return TEST_SKIPPED;
}
cNumRows = 5; // For now
TEST_ALLOC(DBCOUNTITEM, rgRowNums, 0, cNumRows * sizeof (DBCOUNTITEM));
for (i = 0; i < cNumRows ; i++)
rgRowNums[i] = NextInsertRowNum();
// Remove any old parameter info
TEST_CHECK(m_pICmdWParams->SetParameterInfo(0, NULL, NULL), S_OK);
// Set command text
TEST_CHECK (m_pICommandText->SetCommandText(DBGUID_DBSQL , m_pwszSqlInsertAllWithParams), S_OK);
// Call ICommandPrepare.
TEST_CHECK (m_pICommandPrepare->Prepare(1), S_OK);
TEST_ALLOC(BYTE, pData, 0, (size_t)(cNumRows * m_cbRowSize * sizeof (BYTE)));
// Create the Parameter array.
TEST_CHECK (FillInputBindingsForArray (m_pTable, DBACCESSOR_PARAMETERDATA, m_cbRowSize, m_cBindings, m_rgBindings,
(BYTE **)&pData, cNumRows, rgRowNums, m_cParamColMap, (DB_LORDINAL *)m_rgParamColMap), S_OK);
m_DbParamsAll.cParamSets = cNumRows;
m_DbParamsAll.hAccessor = m_hAccessor;
m_DbParamsAll.pData = pData;
TEST_CHECK(SetParameterInfoIfNeeded(m_cDbParamBindInfo, m_rgParamOrdinals, m_rgDbParamBindInfo), S_OK);
// Now execute ICommand.
TEST_CHECK(m_pICommand->Execute(NULL, IID_NULL,
&m_DbParamsAll, &cRowsAffected, NULL), S_OK);
// Verify If we have inserted the row properly.
for (i=0; i < cNumRows; i++)
{
// Verify If we have inserted the row properly.
TEST_COMPARE(cRow.FindRow(rgRowNums[i], m_pTable, NULL, &pIRowset, NULL,
0, NULL, FALSE), TRUE);
// FindRow merely finds the appropriate row. We need to validate the inserted data for the default bindings.
TEST_COMPARE(VerifyObj(m_iidExec, pIRowset, rgRowNums[i], 0, NULL,
FALSE, FALSE, m_pTable, &cRowsExpected), S_OK);
SAFE_RELEASE(pIRowset);
}
fResult = TRUE;
CLEANUP:
SAFE_RELEASE(pIRowset);
SAFE_RELEASE(pIRowset);
PROVIDER_FREE(rgRowNums);
PROVIDER_FREE(pData);
return (fResult) ? TEST_PASS : TEST_FAIL;
}
// }}
// {{ TCW_VAR_PROTOTYPE(36)
//*-----------------------------------------------------------------------
// @mfunc 105 parameter sets.
//
// @rdesc TEST_PASS or TEST_FAIL
//
int TCCommandExecute_Rowset::Variation_36()
{
BOOL fResult = FALSE;
DBROWCOUNT cRowsAffected = 0;
ULONG cNumRows, i;
DBCOUNTITEM *rgRowNums=NULL;
BYTE *pData = NULL;
IRowset * pIRowset = NULL;
DBCOUNTITEM cRowsExpected = 1;
if (!g_bMultipleParamSets)
{
odtLog << "Multiple parameter sets are not supported \n";
return TEST_SKIPPED;
}
cNumRows = 105;
TEST_ALLOC(DBCOUNTITEM, rgRowNums, 0, cNumRows * sizeof (DBCOUNTITEM));
for (i = 0; i < cNumRows ; i++)
rgRowNums[i] = NextInsertRowNum();
// Remove any old parameter info
TEST_CHECK(m_pICmdWParams->SetParameterInfo(0, NULL, NULL), S_OK);
// Set command text
TEST_CHECK (m_pICommandText->SetCommandText(DBGUID_DBSQL , m_pwszSqlInsertAllWithParams), S_OK);
// Call ICommandPrepare.
TEST_CHECK (m_pICommandPrepare->Prepare(1), S_OK);
TEST_ALLOC(BYTE, pData, 0, (size_t)(cNumRows * m_cbRowSize * sizeof (BYTE)));
// Create the Parameter array.
TEST_CHECK (FillInputBindingsForArray (m_pTable, DBACCESSOR_PARAMETERDATA, m_cbRowSize, m_cBindings, m_rgBindings,
(BYTE **)&pData, cNumRows, rgRowNums, m_cParamColMap, (DB_LORDINAL *)m_rgParamColMap), S_OK);
m_DbParamsAll.cParamSets = cNumRows;
m_DbParamsAll.hAccessor = m_hAccessor;
m_DbParamsAll.pData = pData;
TEST_CHECK(SetParameterInfoIfNeeded(m_cDbParamBindInfo, m_rgParamOrdinals, m_rgDbParamBindInfo), S_OK);
// Now execute ICommand.
TEST_CHECK(m_pICommand->Execute(NULL, IID_NULL,
&m_DbParamsAll, &cRowsAffected, NULL), S_OK);
// Verify If we have inserted the row properly.
for (i=0; i < cNumRows; i++)
{
// Verify If we have inserted the row properly.
TEST_COMPARE(cRow.FindRow(rgRowNums[i], m_pTable, NULL, &pIRowset, NULL,
0, NULL, FALSE), TRUE);
// FindRow merely finds the appropriate row. We need to validate the inserted data for the default bindings.
TEST_COMPARE(VerifyObj(m_iidExec, pIRowset, rgRowNums[i], 0, NULL,
FALSE, FALSE, m_pTable, &cRowsExpected), S_OK);
SAFE_RELEASE(pIRowset);
}
fResult = TRUE;
CLEANUP:
SAFE_RELEASE(pIRowset);
PROVIDER_FREE(rgRowNums);
PROVIDER_FREE(pData);
return (fResult) ? TEST_PASS : TEST_FAIL;
}
// }}
// {{ TCW_VAR_PROTOTYPE(37)
//*-----------------------------------------------------------------------
// @mfunc Different types of Good and Bad parameter Sets.
//
// @rdesc TEST_PASS or TEST_FAIL
//
int TCCommandExecute_Rowset::Variation_37()
{
BOOL fResult = FALSE;
DBROWCOUNT cRowsAffected = 0;
ULONG cNumRows, i;
DBCOUNTITEM *rgRowNums=NULL;
void *pData = NULL;
void * pInt = NULL;
BYTE *pRowData = NULL;
const BYTE cTinyInt25 = 25;
const SHORT cShortMax = SHRT_MAX;
const LONG cLongMax = LONG_MAX;
const LONGLONG cInt64Max = _I64_MAX;
DBLENGTH cbMaxLen = 0;
DBLENGTH cbRowSize=m_cbRowSize;
ULONG j = 0;
LONG lSaveIndex = -1;
DBTYPE dbSaveType;
HACCESSOR hLocalAccessor = DB_NULL_HACCESSOR;
ULONG iBadParam = 0;
HRESULT hr = E_FAIL;
DBBINDING * pBind = NULL;
if (!g_bMultipleParamSets)
{
odtLog << "Multiple parameter sets are not supported \n";
return TEST_SKIPPED;
}
// Make a copy of our bindings that we can change
TEST_ALLOC(DBBINDING, pBind, 0, m_cBindings * sizeof(DBBINDING));
memcpy(pBind, m_rgBindings, m_cBindings * sizeof(DBBINDING));
// Find a column to insert data into
for (i=0; i < m_cBindings; i++)
{
// We assume most providers will support one of these integer types
if (lSaveIndex == -1 &&
(pBind[i].wType == DBTYPE_I1 ||
pBind[i].wType == DBTYPE_UI1 ||
pBind[i].wType == DBTYPE_I2 ||
pBind[i].wType == DBTYPE_UI2 ||
pBind[i].wType == DBTYPE_I4 ||
pBind[i].wType == DBTYPE_UI4))
{
// Change the binding to the next larger integer type
lSaveIndex = i;
dbSaveType = pBind[i].wType;
cbMaxLen = pBind[i].cbMaxLen;
// The next type is always twice as large
pBind[i].cbMaxLen *= 2;
// Adjust the row size accordingly
cbRowSize-=cbMaxLen;
cbRowSize+=pBind[i].cbMaxLen;
switch (pBind[i].wType)
{
case DBTYPE_I1:
case DBTYPE_UI1:
pBind[i].wType = DBTYPE_I2;
pInt = (void *)&cShortMax;
break;
case DBTYPE_I2:
case DBTYPE_UI2:
pBind[i].wType = DBTYPE_I4;
pInt = (void *)&cLongMax;
break;
case DBTYPE_I4:
case DBTYPE_UI4:
pBind[i].wType = DBTYPE_I8;
pInt = (void *)&cInt64Max;
break;
}
}
}
// Make sure we actually found one of the integer types we want
if (lSaveIndex == -1)
{
odtLog << L"Couldn't find an integer type needed for this variation.\n";
return TEST_SKIPPED;
}
// Since we changed the type and size for one of the bindings we need to reset the offsets
Repack(m_cBindings, pBind, &cbRowSize);
cNumRows = 5; // For now
TEST_ALLOC(DBCOUNTITEM, rgRowNums, 0, cNumRows * sizeof (DBCOUNTITEM));
for (i = 0; i < cNumRows ; i++)
{
rgRowNums[i] = NextInsertRowNum();
}
// Set command text
TEST_CHECK (m_pICommandText->SetCommandText(DBGUID_DBSQL , m_pwszSqlInsertAllWithParams), S_OK);
// Call ICommandPrepare.
TEST_CHECK (m_pICommandPrepare->Prepare(1), S_OK);
TEST_ALLOC(BYTE, pData, 0, (size_t)(cNumRows * cbRowSize * sizeof (BYTE)));
// Create the Parameter array.
TEST_CHECK (FillInputBindingsForArray (m_pTable, DBACCESSOR_PARAMETERDATA, cbRowSize, m_cBindings, pBind,
(BYTE **)&pData, cNumRows, rgRowNums, m_cParamColMap, (DB_LORDINAL *)m_rgParamColMap), S_OK);
// Modify pData to Throw an error on the odd rows.
for (j = 1; j < cNumRows; j+=2)
{
// Create an overflow scenario.
pRowData = (BYTE *)pData + j*cbRowSize;
*((DBLENGTH *)(((BYTE *)pRowData) + pBind[lSaveIndex].obLength)) = pBind[lSaveIndex].cbMaxLen;
memcpy((void *)(((BYTE *)pRowData) + pBind[lSaveIndex].obValue), pInt, (size_t)pBind[lSaveIndex].cbMaxLen);
}
// Make sure the first row and the correct rows have small enough number not to
// create overflows.
for (j = 0; j < cNumRows; j+=2)
{
pRowData = (BYTE *)pData + j*cbRowSize;
*((DBLENGTH *)(((BYTE *)pRowData) + pBind[lSaveIndex].obLength)) = sizeof (BYTE);
memcpy((void *)(((BYTE *)pRowData) + pBind[lSaveIndex].obValue), &cTinyInt25, sizeof(BYTE));
}
// We Will have to create a new accessor.
TEST_CHECK (m_pCmdIAccessor->CreateAccessor ( DBACCESSOR_PARAMETERDATA, m_cBindings, pBind,
cbRowSize, &hLocalAccessor, NULL), S_OK);
m_DbParamsAll.cParamSets = cNumRows;
m_DbParamsAll.hAccessor = hLocalAccessor;
m_DbParamsAll.pData = pData;
// Now execute ICommand.
hr = m_pICommand->Execute(NULL, IID_NULL,
&m_DbParamsAll, &cRowsAffected, NULL);
if (FAILED(hr))
// Since the rows weren't inserted, decrement next insert row number incremented above.
m_cInsertRowNum -= cNumRows;
TEST_CHECK(hr, DB_E_ERRORSOCCURRED);
// Verify If we have inserted the row properly.
fResult = TRUE;
for (j=0; j < cNumRows; j++)
{
for (i=0; i < m_cBindings; i++)
{
// The status will be DBSTATUS_E_DATAOVERFLOW for the rows & cols we set above, except providers
// are allowed to set DBSTATUS_E_UNAVAILABLE for all except the one that failed.
DBSTATUS ulExpectedStatus;
DBSTATUS ulStatus = *((DBSTATUS *)(((BYTE *)pData + j*cbRowSize) + pBind[i].obStatus));
BOOL fComp = TRUE;
if ((LONG)i == lSaveIndex && (j % 2))
{
// This is one of the parameters we set to overflow
ulExpectedStatus = DBSTATUS_E_DATAOVERFLOW;
// The first bad parameter should always have the correct status but the others
// might be DBSTATUS_E_UNAVAILABLE
if (!iBadParam || (ulStatus != ulExpectedStatus && ulStatus != DBSTATUS_E_UNAVAILABLE))
fComp = COMPARE(ulStatus,ulExpectedStatus);
iBadParam++;
}
else
{
// This parameter had correct data and should not overflow, but also might be
// UNAVAILABLE
ulExpectedStatus = DBSTATUS_S_OK;
if (ulStatus != ulExpectedStatus && ulStatus != DBSTATUS_E_UNAVAILABLE)
fComp = COMPARE(ulStatus,ulExpectedStatus);
}
if (!fComp)
{
odtLog << "( " << i+1 << " )th Parameter in Row ( " << j+1 << " ) has bad status ( " <<
ulStatus << " )\n";
fResult=FALSE;
}
}
}
CLEANUP:
if(hLocalAccessor != DB_NULL_HACCESSOR)
m_pCmdIAccessor->ReleaseAccessor (hLocalAccessor, NULL);
PROVIDER_FREE(pBind);
PROVIDER_FREE(rgRowNums);
PROVIDER_FREE(pData);
return (fResult) ? TEST_PASS : TEST_FAIL;
}
// }}
// {{ TCW_VAR_PROTOTYPE(38)
//*-----------------------------------------------------------------------
// @mfunc Having a table such that Variable length columns make up first and last columns
//
// @rdesc TEST_PASS or TEST_FAIL
//
int TCCommandExecute_Rowset::Variation_38()
{
CList <WCHAR *, WCHAR *> DBTypeList;
CList <WCHAR *, WCHAR *> DBVarLengthList;
POSITION ListPosition, HeadPosition;
DBORDINAL cSaveTableCols = 0;
DBORDINAL cColumns = 0;
ULONG count;
CTable * pSaveTable= NULL;
BOOL fResult = FALSE;
LONG i;
WCHAR *pwszSqlStatement = NULL;
ULONG cDbParamBindInfo=0;
DB_UPARAMS *rgParamOrdinals = NULL;
DB_LORDINAL *rgUpdateableCols = NULL;
ULONG cUpdateableCols = 0;
CCol TempCol;
void *pData = NULL;
DBROWCOUNT cRowsAffected = 0;
DBPARAMS DbParamsAll;
HACCESSOR hLocalAccessor = DB_NULL_HACCESSOR;
DBBINDING *rgLocalBindings = NULL;
DBCOUNTITEM cBindings = 0;
DBLENGTH cbRowSize = 0;
ULONG cColsOut = 0;
ULONG cRowsOnTable = 1;
WCHAR *pwszSqlSelectAllFromTbl = NULL;
WCHAR *pwszInsertAllWithParams = NULL;
ULONG ulRowNum = 0;
LONG cNumRows = 5;
DBCOUNTITEM *rgRowNums=NULL;
CCol NewCol;
WCHAR ** ppwszProviderTypeNames=NULL;
DBPARAMBINDINFO * pParamBindInfo = NULL;
ParamStruct * pParamStruct = NULL;
WCHAR * pStringsBuffer = NULL;
if (!g_bMultipleParamSets)
{
cNumRows = 1;
odtLog << L"Multiple parameter sets are not supported; using 1 parameter set \n";
}
else
odtLog << L"Using " << cNumRows << L" parameter sets.\n";
TEST_ALLOC(DBCOUNTITEM, rgRowNums, 0, cNumRows * sizeof (DBCOUNTITEM));
for (i = 0; i < cNumRows ; i++)
{
rgRowNums[i] = cRowsOnTable+i+1;
}
// Creates a column list from the Ctable
cColumns = m_pTable->CountColumnsOnTable();
// Allocate space for array of provider type names and set them to NULL
TEST_ALLOC(WCHAR *, ppwszProviderTypeNames, 0, (size_t)cColumns * sizeof(WCHAR *));
// Loop thru column types
for( count=1; count <= cColumns; count++)
{
m_pTable->GetColInfo(count, NewCol);
// Allocate a buffer and copy the type name string
if (!(ppwszProviderTypeNames[count-1]=wcsDuplicate(NewCol.GetProviderTypeName())))
goto CLEANUP;
// We want variable length non-BLOB columns. Next variation uses BLOB cols.
if (!IsFixedLength(NewCol.GetProviderType()) && !NewCol.GetIsLong())
DBVarLengthList.AddTail(ppwszProviderTypeNames[count-1]);
else
DBTypeList.AddTail(ppwszProviderTypeNames[count-1]);
}
// Now from the VarList append some in beginint and Add some at the end.
ListPosition = DBVarLengthList.GetHeadPosition();
// Add this element to the head of types list.
if (ListPosition)
{
HeadPosition = DBTypeList.GetHeadPosition();
DBTypeList.InsertBefore(HeadPosition, DBVarLengthList.GetNext(ListPosition));
// Rest add at the tail.
while (ListPosition != NULL)
{
DBTypeList.AddTail(DBVarLengthList.GetNext(ListPosition));
}
}
// We have constructed the list. Now create the table.
pSaveTable = new CTable((IUnknown *)m_pThisTestModule->m_pIUnknown2,
(LPWSTR)gwszModuleName, NONULLS);
if (!pSaveTable)
goto CLEANUP;
// Now Create the table. Some providers (Kagera) require an index and
// can't create an index on a
if(!CHECK(pSaveTable->CreateTable(DBTypeList, cRowsOnTable), S_OK))
goto CLEANUP;
//Replace text in CommandObject With select statment with no parameters.
TEST_CHECK(m_hr = pSaveTable->CreateSQLStmt(SELECT_UPDATEABLE, NULL, &pwszSqlSelectAllFromTbl, NULL, NULL ), S_OK);
//Create a Update command with parameters
// Set command text
TEST_CHECK(m_pICommandText->SetCommandText(DBGUID_DBSQL , pwszSqlSelectAllFromTbl), S_OK);
// Call ICommandPrepare.
TEST_CHECK (m_pICommandPrepare->Prepare(1), S_OK);
// Create the accessor and get the binding information.
TEST_CHECK(GetAccessorAndBindings(m_pCmdIAccessor, DBACCESSOR_PARAMETERDATA,
&hLocalAccessor, &rgLocalBindings, &cBindings, &cbRowSize,
DBPART_VALUE | DBPART_STATUS | DBPART_LENGTH,
UPDATEABLE_COLS_BOUND, FORWARD, NO_COLS_BY_REF,
NULL, NULL,
&pStringsBuffer, DBTYPE_EMPTY, cUpdateableCols, rgUpdateableCols ,
(DBORDINAL *)rgParamOrdinals ,NO_COLS_OWNED_BY_PROV, DBPARAMIO_INPUT, BLOB_LONG ), S_OK);
cSaveTableCols = pSaveTable->CountColumnsOnTable();
TEST_ALLOC(DB_UPARAMS, rgParamOrdinals, 0, (size_t)(cSaveTableCols * sizeof(DB_UPARAMS)));
TEST_ALLOC(DB_LORDINAL, rgUpdateableCols, 0, (size_t)(cSaveTableCols * sizeof(DB_LORDINAL)));
TEST_ALLOC(DBPARAMBINDINFO, pParamBindInfo, 0, (size_t)(cSaveTableCols * sizeof(DBPARAMBINDINFO)));
TEST_ALLOC(ParamStruct, pParamStruct, 0, (size_t)(cSaveTableCols * sizeof(ParamStruct)));
//We'll use this count as the index to the array as we build it
cDbParamBindInfo = 0;
for (i = 1; i <= (LONG)cSaveTableCols; i++)
{
TEST_CHECK(pSaveTable->GetColInfo(i, TempCol), S_OK);
//Record the column number in the array
//if it is
if (TempCol.GetUpdateable() )
{
// Build Information for SetParameterInfo.
AddParam(cDbParamBindInfo, i, DBPARAMIO_INPUT, NULL, FALSE, NULL, NULL, pParamBindInfo,
pParamStruct, pSaveTable);
rgUpdateableCols[cUpdateableCols++] = TempCol.GetColNum(); // Column number which is the next Updateable col.
rgParamOrdinals[cDbParamBindInfo] = cDbParamBindInfo + 1; // Parameter ordinal number.
cDbParamBindInfo++;
}
}
// Lets restore the text object to insert command.
TEST_CHECK(pSaveTable->ExecuteCommand(INSERT_ALLWITHPARAMS, IID_IUnknown,
NULL, &pwszInsertAllWithParams, NULL, NULL,
EXECUTE_NEVER, 0, NULL, NULL, NULL, &m_pICommand), S_OK);
// Set command text
TEST_CHECK (m_pICommandText->SetCommandText(DBGUID_DBSQL , pwszInsertAllWithParams), S_OK);
// Call ICommandPrepare.
TEST_CHECK (m_pICommandPrepare->Prepare(1), S_OK);
TEST_CHECK(SetParameterInfoIfNeeded(cDbParamBindInfo, rgParamOrdinals, pParamBindInfo), S_OK);
TEST_ALLOC(BYTE, pData, 0, (size_t)(cNumRows * cbRowSize * sizeof (BYTE)));
// Now call execute.
// Create the Parameter array.
TEST_CHECK (FillInputBindingsForArray (pSaveTable, DBACCESSOR_PARAMETERDATA, cbRowSize, cBindings, rgLocalBindings,
(BYTE **)&pData, cNumRows, rgRowNums, cUpdateableCols, rgUpdateableCols), S_OK);
DbParamsAll.cParamSets = cNumRows;
DbParamsAll.hAccessor = hLocalAccessor;
DbParamsAll.pData = pData;
// Now execute ICommand.
TEST_CHECK(m_pICommandText->Execute(NULL, IID_NULL,
&DbParamsAll, &cRowsAffected, NULL), S_OK);
TEST_COMPARE (cRowsAffected, cNumRows);
fResult = TRUE;
CLEANUP:
// Free the provider type name array
for (count=0; count < cColumns; count++)
PROVIDER_FREE(ppwszProviderTypeNames[count]);
PROVIDER_FREE(ppwszProviderTypeNames);
PROVIDER_FREE(rgRowNums);
PROVIDER_FREE(pData);
PROVIDER_FREE(rgParamOrdinals );
PROVIDER_FREE(pParamStruct );
PROVIDER_FREE(pwszSqlSelectAllFromTbl) ;
PROVIDER_FREE(pwszInsertAllWithParams) ;
PROVIDER_FREE(rgUpdateableCols) ;
PROVIDER_FREE(rgLocalBindings) ;
PROVIDER_FREE(pStringsBuffer);
DBTypeList.RemoveAll();
DBVarLengthList.RemoveAll();
if (pSaveTable)
{
pSaveTable->DropTable();
delete pSaveTable;
}
return (fResult) ? TEST_PASS : TEST_FAIL;
}
// }}
// {{ TCW_VAR_PROTOTYPE(39)
//*-----------------------------------------------------------------------
// @mfunc Having BLOB as first and last columns
//
// @rdesc TEST_PASS or TEST_FAIL
//
int TCCommandExecute_Rowset::Variation_39()
{
CList <WCHAR *, WCHAR *> DBTypeList;
CList <WCHAR *, WCHAR *> DBBlobList;
POSITION ListPosition, HeadPosition;
DBORDINAL cColumns = 0;
ULONG count;
CTable * pSaveTable= NULL;
BOOL fResult = FALSE;
LONG i;
WCHAR *pwszSqlStatement = NULL;
ULONG cDbParamBindInfo=0;
DB_UPARAMS *rgParamOrdinals = NULL;
DB_LORDINAL *rgUpdateableCols = NULL;
ULONG cUpdateableCols = 0;
CCol TempCol;
void *pData = NULL;
DBROWCOUNT cRowsAffected = 0;
IRowset *pRowset = NULL;
DBPARAMS DbParamsAll;
HACCESSOR hLocalAccessor = DB_NULL_HACCESSOR;
DBBINDING *rgLocalBindings = NULL;
DBCOUNTITEM cBindings = 0;
DBLENGTH cbRowSize = 0;
ULONG cColsOut = 0;
ULONG cRowsOnTable=1;
WCHAR *pwszSqlSelectAllFromTbl = NULL;
WCHAR *pwszInsertAllWithParams = NULL;
ULONG ulRowNum = 0;
LONG cNumRows = 5;
DBCOUNTITEM *rgRowNums=NULL;
CCol NewCol;
WCHAR ** ppwszProviderTypeNames=NULL;
DBPARAMBINDINFO * pParamBindInfo = NULL;
ParamStruct * pParamStruct = NULL;
DBORDINAL cSaveTableCols;
IRowset * pIRowset = NULL;
DBCOUNTITEM cRowsExpected = 1;
if (!g_bMultipleParamSets)
{
cNumRows = 1;
odtLog << L"Multiple parameter sets are not supported; using 1 parameter set \n";
}
else
odtLog << L"Using " << cNumRows << L" parameter sets.\n";
TEST_ALLOC(DBCOUNTITEM, rgRowNums, 0, cNumRows * sizeof (DBCOUNTITEM));
for (i = 0; i < cNumRows ; i++)
rgRowNums[i] = cRowsOnTable+i+1;
// Creates a column list from the Ctable
cColumns = m_pTable->CountColumnsOnTable();
// Allocate space for array of provider type names
if (!(ppwszProviderTypeNames = (WCHAR **)PROVIDER_ALLOC(cColumns * sizeof(WCHAR *))))
goto CLEANUP;
// Initialize all the pointers to NULL
memset(ppwszProviderTypeNames, 0, (size_t)(cColumns * sizeof(WCHAR *)));
// Loop thru column types
for( count=1; count <= cColumns; count++)
{
m_pTable->GetColInfo(count, NewCol);
// Allocate a buffer and copy the type name string
if (!(ppwszProviderTypeNames[count-1]=wcsDuplicate(NewCol.GetProviderTypeName())))
goto CLEANUP;
if (NewCol.GetIsLong())
DBBlobList.AddTail(ppwszProviderTypeNames[count-1]);
else
DBTypeList.AddTail(ppwszProviderTypeNames[count-1]);
}
// Now from the VarList append some in begining and Add some at the end.
ListPosition = DBBlobList.GetHeadPosition();
if (ListPosition)
{
HeadPosition = DBTypeList.GetHeadPosition();
DBTypeList.InsertBefore(HeadPosition, DBBlobList.GetNext(ListPosition));
// Rest add at the tail.
while (ListPosition != NULL)
{
DBTypeList.AddTail(DBBlobList.GetNext(ListPosition));
}
}
// We have constructed the list. Now create the table.
pSaveTable = new CTable((IUnknown *)m_pThisTestModule->m_pIUnknown2,
(LPWSTR)gwszModuleName, NONULLS);
if (!pSaveTable)
goto CLEANUP;
// Create a table with one row. Since some providers require indexes
// and can't have an index on a BLOB col we make the index col 2.
if(!CHECK(pSaveTable->CreateTable(DBTypeList, cRowsOnTable, 2), S_OK))
{
// Free memory in the list
DBTypeList.RemoveAll();
goto CLEANUP;
}
cSaveTableCols = pSaveTable->CountColumnsOnTable();
TEST_ALLOC(DB_UPARAMS, rgParamOrdinals, 0, (size_t)(cSaveTableCols * sizeof(DB_UPARAMS)));
TEST_ALLOC(DB_LORDINAL, rgUpdateableCols, 0, (size_t)(cSaveTableCols * sizeof(DB_LORDINAL)));
TEST_ALLOC(DBPARAMBINDINFO, pParamBindInfo, 0, (size_t)(cSaveTableCols * sizeof(DBPARAMBINDINFO)));
TEST_ALLOC(ParamStruct, pParamStruct, 0, (size_t)(cSaveTableCols * sizeof(ParamStruct)));
//We'll use this count as the index to the array as we build it
cDbParamBindInfo = 0;
for (i = 1; i <= (LONG)pSaveTable->CountColumnsOnTable(); i++)
{
TEST_CHECK(pSaveTable->GetColInfo(i, TempCol), S_OK);
//Record the column number in the array if it is
if (TempCol.GetUpdateable() )
{
// Build Information for SetParameterInfo.
AddParam(cDbParamBindInfo, i, DBPARAMIO_INPUT, NULL, FALSE, NULL, NULL, pParamBindInfo, pParamStruct, pSaveTable);
rgUpdateableCols[cUpdateableCols++] = TempCol.GetColNum(); // Column number which is the next Updateable col.
rgParamOrdinals[cDbParamBindInfo] = cDbParamBindInfo + 1; // Parameter ordinal number.
cDbParamBindInfo++;
}
}
//Replace text in CommandObject With select statment with no parameters.
TEST_CHECK(m_hr = pSaveTable->CreateSQLStmt(SELECT_UPDATEABLE, NULL, &pwszSqlSelectAllFromTbl, NULL, NULL ), S_OK);
//Create a Update command with parameters
// Set command text
TEST_CHECK(m_pICommandText->SetCommandText(DBGUID_DBSQL , pwszSqlSelectAllFromTbl), S_OK);
// Call ICommandPrepare.
TEST_CHECK (m_pICommandPrepare->Prepare(1), S_OK);
TEST_CHECK(SetParameterInfoIfNeeded(cDbParamBindInfo, rgParamOrdinals, pParamBindInfo), S_OK);
// Create the accessor and get the binding information.
TEST_CHECK(GetAccessorAndBindings(m_pCmdIAccessor, DBACCESSOR_PARAMETERDATA,
&hLocalAccessor, &rgLocalBindings, &cBindings, &cbRowSize,
DBPART_VALUE | DBPART_STATUS | DBPART_LENGTH,
UPDATEABLE_COLS_BOUND, FORWARD, NO_COLS_BY_REF,
NULL, NULL,
NULL, DBTYPE_EMPTY, cUpdateableCols, rgUpdateableCols ,
(DBORDINAL *)rgParamOrdinals ,NO_COLS_OWNED_BY_PROV, DBPARAMIO_INPUT, BLOB_LONG ), S_OK);
// Lets restore the text object to insert command.
TEST_CHECK(pSaveTable->ExecuteCommand(INSERT_ALLWITHPARAMS, IID_IUnknown,
NULL, &pwszInsertAllWithParams, NULL, NULL,
EXECUTE_NEVER, 0, NULL, NULL, NULL, &m_pICommand), S_OK);
// Set command text
TEST_CHECK (m_pICommandText->SetCommandText(DBGUID_DBSQL , pwszInsertAllWithParams), S_OK);
// Call ICommandPrepare.
TEST_CHECK (m_pICommandPrepare->Prepare(1), S_OK);
pData = (BYTE *)m_pIMalloc->Alloc (cNumRows * cbRowSize * sizeof (BYTE));
if (!pData) goto CLEANUP;
// Now call execute.
// Create the Parameter array.
TEST_CHECK (FillInputBindingsForArray (pSaveTable, DBACCESSOR_PARAMETERDATA, cbRowSize, cBindings, rgLocalBindings,
(BYTE **)&pData, cNumRows, rgRowNums, cUpdateableCols, rgUpdateableCols), S_OK);
DbParamsAll.cParamSets = cNumRows;
DbParamsAll.hAccessor = hLocalAccessor;
DbParamsAll.pData = pData;
// Now execute ICommand.
TEST_CHECK(m_pICommandText->Execute(NULL, IID_NULL,
&DbParamsAll, &cRowsAffected, NULL), S_OK);
TEST_COMPARE (cRowsAffected, cNumRows);
// Verify If we have inserted the row properly.
for (i=0; i < cNumRows; i++)
{
// Verify If we have inserted the row properly.
TEST_COMPARE(cRow.FindRow(rgRowNums[i], pSaveTable, NULL, &pIRowset, NULL,
0, NULL, FALSE), TRUE);
// FindRow merely finds the appropriate row. We need to validate the inserted data for the default bindings.
TEST_COMPARE(VerifyObj(m_iidExec, pIRowset, rgRowNums[i], 0, NULL,
FALSE, FALSE, pSaveTable, &cRowsExpected), S_OK);
SAFE_RELEASE(pIRowset);
}
fResult = TRUE;
CLEANUP:
SAFE_RELEASE(pIRowset);
// Free the provider type name array
for (count=0; count < cColumns; count++)
PROVIDER_FREE(ppwszProviderTypeNames[count]);
PROVIDER_FREE(ppwszProviderTypeNames);
ReleaseInputBindingsForArray(cNumRows, cbRowSize, cBindings, rgLocalBindings, (BYTE *)pData);
if (pRowset) ReleaseRowsetPtr(&pRowset);
FREE_DATA (rgRowNums);
FREE_DATA (rgParamOrdinals );
FREE_DATA (pwszSqlSelectAllFromTbl);
FREE_DATA (pwszInsertAllWithParams);
FREE_DATA (rgUpdateableCols);
FREE_DATA (rgLocalBindings);
DBTypeList.RemoveAll();
DBBlobList.RemoveAll();
if (pSaveTable)
{
pSaveTable->DropTable();
delete pSaveTable;
}
return (fResult) ? TEST_PASS : TEST_FAIL;
}
// }}
// {{ TCW_VAR_PROTOTYPE(40)
//*-----------------------------------------------------------------------
// @mfunc 5 parameter sets with different size of Variable length parameters.
//
// @rdesc TEST_PASS or TEST_FAIL
//
int TCCommandExecute_Rowset::Variation_40()
{
BOOL fResult = FALSE;
DBROWCOUNT cRowsAffected = 0;
IRowset * pRowset = NULL;
TESTRESULT testresult = TEST_FAIL;
LONG cNumRows = 5;
LONG i;
DBCOUNTITEM *rgRowNums=NULL;
void *pData = NULL;
LONG VarColumn=0;
DBLENGTH * pLength = NULL;
ULONG ulNum[5] = {1, 7, 4, 8, 3};
if (!g_bMultipleParamSets)
{
cNumRows = 1;
odtLog << L"Multiple parameter sets are not supported; using 1 parameter set \n";
}
else
odtLog << L"Using " << cNumRows << L" parameter sets.\n";
TEST_ALLOC(DBCOUNTITEM, rgRowNums, 0, cNumRows * sizeof (DBCOUNTITEM));
for (i = 0; i < cNumRows ; i++)
rgRowNums[i] = NextInsertRowNum();
// Set command text
TEST_CHECK (m_pICommandText->SetCommandText(DBGUID_DBSQL , m_pwszSqlInsertAllWithParams), S_OK);
// Call ICommandPrepare.
TEST_CHECK (m_pICommandPrepare->Prepare(1), S_OK);
TEST_CHECK(SetParameterInfoIfNeeded(m_cDbParamBindInfo, m_rgParamOrdinals, m_rgDbParamBindInfo), S_OK);
TEST_ALLOC(BYTE, pData, 0, (size_t)(cNumRows * m_cbRowSize * sizeof (BYTE)));
// Create the Parameter array.
TEST_CHECK (FillInputBindingsForArray (m_pTable, DBACCESSOR_PARAMETERDATA, m_cbRowSize, m_cBindings, m_rgBindings,
(BYTE **)&pData, cNumRows, rgRowNums, m_cParamColMap, (DB_LORDINAL *)m_rgParamColMap), S_OK);
m_DbParamsAll.cParamSets = cNumRows;
m_DbParamsAll.hAccessor = m_hAccessor;
m_DbParamsAll.pData = pData;
// Now Lets Modify length and Value buffers of pData.
// For Testing purpose. 1st Buffer smallest(1/8th of Orig). 2nd Buffer biggest(7/8th).
// 3rd buffer(4/8), 4th buffer ( 8/8) 5th buffer (3/8)
// First get the index of a Variable length column.
VarColumn = -1;
for (i=0; i < (LONG)m_cBindings && VarColumn == -1; i++)
{
if (!IsFixedLength(m_rgBindings[i].wType))
VarColumn = i; // Just need the index.
}
if (VarColumn == -1)
{
// Couldn't get a variable column.
odtLog << "No variable size column in the table\n";
testresult = TEST_SKIPPED;
goto CLEANUP;
}
// Now lets change pData.
// First Array.
// Change How much of Data you are putting in.
for (i=0; i < cNumRows; i++)
{
pLength = ((DBLENGTH *)((BYTE *)pData + (i*m_cbRowSize) + m_rgBindings[VarColumn].obLength));
*pLength = *pLength*ulNum[i]/8;
// If it's a unicode string we must put in an even number.
if (m_rgBindings[VarColumn].wType == DBTYPE_WSTR && *pLength % 2)
(*pLength)++;
}
// Now execute ICommand.
TEST_CHECK(m_pICommand->Execute(NULL, IID_NULL,
&m_DbParamsAll, &cRowsAffected, NULL), S_OK);
TEST_COMPARE (cRowsAffected, cNumRows);
// Some how verify How much data actually got put in.
// @todo.
// At this point our table contains perverted data. We must either delete these
// extra rows or fix them up to match expected pattern, or later verifications will
// fail.
// Due to the difficulty in deleting only the rows inserted above we will delete all
// rows and reinsert
m_pTable->Delete();
m_pTable->Insert(1, PRIMARY, TRUE, NULL, FALSE, TOTAL_NUMBER_OF_ROWS); // 0 means next row
m_cInsertRowNum = TOTAL_NUMBER_OF_ROWS+1;
testresult = TEST_PASS;
CLEANUP:
PROVIDER_FREE(pData);
PROVIDER_FREE(rgRowNums);
return testresult;
}
// }}
// {{ TCW_VAR_PROTOTYPE(41)
//*-----------------------------------------------------------------------
// @mfunc 5 parameters sets followed by 1 parameter set.
//
// @rdesc TEST_PASS or TEST_FAIL
//
int TCCommandExecute_Rowset::Variation_41()
{
BOOL fResult = FALSE;
DBROWCOUNT cRowsAffected = 0;
ULONG cNumRows, i;
DBCOUNTITEM *rgRowNums=NULL;
BYTE *pData = NULL;
if (!g_bMultipleParamSets)
{
odtLog << "Multiple parameter sets are not supported \n";
return TEST_SKIPPED;
}
cNumRows = 5; // For now
TEST_ALLOC(DBCOUNTITEM, rgRowNums, 0, cNumRows * sizeof (DBCOUNTITEM));
for (i = 0; i < cNumRows ; i++)
rgRowNums[i] = NextInsertRowNum();
// Set command text
TEST_CHECK (m_pICommandText->SetCommandText(DBGUID_DBSQL , m_pwszSqlInsertAllWithParams), S_OK);
// Call ICommandPrepare.
TEST_CHECK (m_pICommandPrepare->Prepare(1), S_OK);
TEST_CHECK(SetParameterInfoIfNeeded(m_cDbParamBindInfo, m_rgParamOrdinals, m_rgDbParamBindInfo), S_OK);
TEST_ALLOC(BYTE, pData, 0, (size_t)(cNumRows * m_cbRowSize * sizeof (BYTE)));
// Create the Parameter array.
TEST_CHECK (FillInputBindingsForArray (m_pTable, DBACCESSOR_PARAMETERDATA, m_cbRowSize, m_cBindings, m_rgBindings,
(BYTE **)&pData, cNumRows, rgRowNums, m_cParamColMap, (DB_LORDINAL *)m_rgParamColMap), S_OK);
m_DbParamsAll.cParamSets = cNumRows;
m_DbParamsAll.hAccessor = m_hAccessor;
m_DbParamsAll.pData = pData;
// Now execute ICommand.
TEST_CHECK(m_pICommand->Execute(NULL, IID_NULL,
&m_DbParamsAll, &cRowsAffected, NULL), S_OK);
// Verify If we have inserted the row properly.
for (i=0; i < cNumRows; i++)
{
TEST_COMPARE(cRow.FindRow(rgRowNums[i], m_pTable), TRUE);
// Tell the CTable object we have another row
m_pTable->AddRow();
}
cNumRows = 1; // For now
for (i = 0; i < cNumRows ; i++)
rgRowNums[i] = NextInsertRowNum();
// Create the Parameter array.
TEST_CHECK (FillInputBindingsForArray (m_pTable, DBACCESSOR_PARAMETERDATA, m_cbRowSize, m_cBindings, m_rgBindings,
(BYTE **)&pData, cNumRows, rgRowNums, m_cParamColMap, (DB_LORDINAL *)m_rgParamColMap), S_OK);
m_DbParamsAll.cParamSets = cNumRows;
m_DbParamsAll.hAccessor = m_hAccessor;
m_DbParamsAll.pData = pData;
// Now execute ICommand.
TEST_CHECK(m_pICommand->Execute(NULL, IID_NULL,
&m_DbParamsAll, &cRowsAffected, NULL), S_OK);
// Verify If we have inserted the row properly.
for (i=0; i < cNumRows; i++)
{
TEST_COMPARE(cRow.FindRow(rgRowNums[i], m_pTable), TRUE);
// Tell the CTable object we have another row
m_pTable->AddRow();
}
fResult = TRUE;
CLEANUP:
PROVIDER_FREE(rgRowNums);
PROVIDER_FREE(pData);
return (fResult) ? TEST_PASS : TEST_FAIL;
}
// }}
// {{ TCW_VAR_PROTOTYPE(42)
//*-----------------------------------------------------------------------
// @mfunc BYREF accessor with PROVIDER_OWNED memory should fail.
//
// @rdesc TEST_PASS or TEST_FAIL
//
int TCCommandExecute_Rowset::Variation_42()
{
DBCOUNTITEM cBindings=1;
DBBINDING DbBindings;
HACCESSOR hLocalAccessor = DB_NULL_HACCESSOR;
DbBindings.iOrdinal = 1;
DbBindings.obValue = offsetof(DATA, bValue) + sizeof (DATA);
DbBindings.obLength = offsetof(DATA, ulLength) + sizeof (DATA);
DbBindings.obStatus = offsetof (DATA, sStatus) + sizeof (DATA);
DbBindings.pTypeInfo = NULL;
DbBindings.pObject = NULL;
DbBindings.pBindExt = NULL;
DbBindings.dwPart = DBPART_VALUE | DBPART_LENGTH | DBPART_STATUS;
DbBindings.dwMemOwner = DBMEMOWNER_PROVIDEROWNED;
DbBindings.eParamIO = DBPARAMIO_OUTPUT;
DbBindings.cbMaxLen = sizeof (LONG);
DbBindings.dwFlags = 0;
DbBindings.wType = DBTYPE_STR | DBTYPE_BYREF;
DbBindings.bPrecision = 0;
DbBindings.bScale = 0;
// We Will have to create a new accessor.
if (!CHECK (m_pCmdIAccessor->CreateAccessor ( DBACCESSOR_PARAMETERDATA, cBindings, &DbBindings,
m_cbRowSize, &hLocalAccessor, NULL), DB_E_ERRORSOCCURRED))
{
return TEST_FAIL;
}
return TEST_PASS;
}
// }}
// {{ TCW_VAR_PROTOTYPE(43)
//*-----------------------------------------------------------------------
// @mfunc RestartPosition should either return S_OK or DB_E_CANNOTRESTART
//
// @rdesc TEST_PASS or TEST_FAIL
//
int TCCommandExecute_Rowset::Variation_43()
{
BOOL fResult = TRUE;
IRowset * pIRowset = NULL;
BYTE * pData = NULL;
DBBINDING * rgBindings = NULL;
HACCESSOR hExecAccessor=DB_NULL_HACCESSOR;
ULONG cSearchableCols = 0;
ULONG i = 0;
DBLENGTH cbRowSize = 0;
DBCOUNTITEM cBindings = 0;
DB_LORDINAL * rgSearchableCols = NULL;
DBORDINAL rgParamOrds[] = {1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25};
DBROWCOUNT cRowsAffected = 0;
DBPARAMS Param;
CCol TempCol;
if (m_eTestCase == TC_Row)
{
odtLog << L"RestartPosition not supported for row objects.\n";
return TEST_SKIPPED;
}
// SetParameter Info information should be deleted.
TEST_CHECK (m_pICmdWParams->SetParameterInfo(0, NULL, NULL), S_OK);
//Get memory to hold array of all col numbers. NOTE: This
//is the max possible, we won't necessarily use them all.
rgSearchableCols = (DB_LORDINAL *)m_pIMalloc->Alloc(m_pTable->CountColumnsOnTable() * sizeof(DB_LORDINAL));
if (!rgSearchableCols)
return FALSE;
//We'll use this count as the index to the array as we build it
cSearchableCols = 0;
for (i=1; i<=m_pTable->CountColumnsOnTable(); i++)
{
CHECK(m_pTable->GetColInfo(i, TempCol), S_OK);
//Record the column number in the array, if it is searchable
if (TempCol.GetIsLong())
continue;
if ( (TempCol.GetSearchable() != DB_UNSEARCHABLE) && TempCol.GetUpdateable() )
{
rgSearchableCols[cSearchableCols] = TempCol.GetColNum();
cSearchableCols++;
}
}
//Just set the command so we can do IColumnsInfo to generate accessor
if (!CHECK(m_pTable->ExecuteCommand(SELECT_ALLFROMTBL, IID_IRowset, NULL, NULL, NULL, NULL,
EXECUTE_NEVER, 0, NULL, NULL, NULL, &m_pICommand), S_OK))
goto CLEANUP;
// Create the accessor.
if (!CHECK(GetAccessorAndBindings(m_pCmdIAccessor, DBACCESSOR_PARAMETERDATA,
&hExecAccessor, &rgBindings, &cBindings, &cbRowSize,
DBPART_VALUE | DBPART_STATUS | DBPART_LENGTH, USE_COLS_TO_BIND_ARRAY,
FORWARD, NO_COLS_BY_REF, NULL, NULL, NULL, DBTYPE_EMPTY,
cSearchableCols, rgSearchableCols, rgParamOrds, NO_COLS_OWNED_BY_PROV,
DBPARAMIO_INPUT, BLOB_LONG), S_OK))
goto CLEANUP;
// Set command text for a select query using prameters.
if (FAILED(m_hr = m_pTable->ExecuteCommand(SELECT_ALL_WITH_SEARCHABLE_AND_UPDATEABLE, IID_IUnknown,
NULL,NULL,NULL, NULL, EXECUTE_NEVER, 0, NULL, NULL, NULL, &m_pICommand)))
goto CLEANUP;
//Alloc enough memory to hold a row of parameter data
pData = (BYTE *)m_pIMalloc->Alloc(cbRowSize);
if (!pData)
{
PRVTRACE (wszMemoryAllocationError);
goto CLEANUP;
}
memset(pData, 0, (size_t)cbRowSize);
//Set up parameter input values for selecting row 1
// m_ulFixedRowNum contains the row number guarenteed to be in the table.
TEST_CHECK(FillInputBindings(m_pTable, DBACCESSOR_PARAMETERDATA, cBindings,
rgBindings, &pData, m_ulFixedRowNum, cSearchableCols, rgSearchableCols, PRIMARY), S_OK);
Param.cParamSets = 1;
Param.hAccessor = hExecAccessor;
Param.pData = pData;
// Call SetParameterInfo if the provider can't derive.
// Need to use the proper paraminfo here.
// TEST_CHECK(SetParameterInfoIfNeeded(m_cDbParamBindInfo, m_rgParamOrdinals, m_rgDbParamBindInfo), S_OK);
// Execute Command statement.
if (!CHECK(m_hr = m_pICommand->Execute(NULL, IID_IRowset, &Param,
&cRowsAffected, (IUnknown **)&pIRowset),S_OK))
goto CLEANUP;
// Expecting a rowset (with atleast one row)
if (!pIRowset)
goto CLEANUP;
m_hr = pIRowset->RestartPosition(NULL);
// Should return S_OK or DB_E_CANNOTRESTART
if (m_hr != S_OK)
{
// If we did not succeed in doing restart position it should give this error.
TEST_CHECK (m_hr, DB_E_CANNOTRESTART);
}
fResult = TRUE;
CLEANUP:
// Release pData before we set it again.
ReleaseInputBindingsMemory(cBindings, rgBindings, pData);
FREE_DATA (pData);
if ((hExecAccessor != DB_NULL_HACCESSOR ) && m_pCmdIAccessor)
{
CHECK(m_pCmdIAccessor->ReleaseAccessor(hExecAccessor, NULL), S_OK);
hExecAccessor = DB_NULL_HACCESSOR;
}
// Release Bindings.
FREE_DATA (rgBindings);
FREE_DATA (rgSearchableCols);
RELEASE (pIRowset);
return (fResult) ? TEST_PASS : TEST_FAIL;
}
// }}
// {{ TCW_VAR_PROTOTYPE(44)
//*-----------------------------------------------------------------------
// @mfunc Named Parameters: Sproc parm count != bound parm count
//
// @rdesc TEST_PASS or TEST_FAIL
//
int TCCommandExecute_Rowset::Variation_44()
{
// The stored proc contains one parameter with a default value, so the count of parameters
// in the proc don't match those bound. We bind to a parameter by name after the first one such that
// if the name isn't used to resolve the parameter, then a conversion error will result. If no
// bindings can be made that would result in a conversion error, then the test is inconclusive unless
// we validate the rowset returned (that the parm values were resolved correctly). This might happen
// if the provider only supports char type, for instance.
//
BOOL fResult = TRUE;
ULONG cParams, cParamSets = 1;
DBLENGTH cbRowSize = 0;
DBCOUNTITEM ulRowNum = 2;
DBBINDING * pBINDING=NULL;
DB_UPARAMS * pParamOrdinals=NULL;
DBPARAMBINDINFO * pPARAMBIND=NULL;
DBPARAMINFO * pParamInfo = NULL;
WCHAR * pwszCreateProcStmt=NULL;
WCHAR * pwszExecProcStmt=NULL;
WCHAR * pwszExecStmt=NULL;
WCHAR * pwszProcName=NULL;
WCHAR * pNamesBuffer = NULL;
BYTE * pData=NULL;
ParamStruct * pParamAll=NULL;
ULONG cColumns = 0;
DB_LORDINAL * prgColumnsOrd = NULL;
BOOL fCanDerive = FALSE;
HRESULT hrSet = DB_S_TYPEINFOOVERRIDDEN;
enum TOKEN_ENUM eProcType = T_EXEC_PROC_SELECT_OUT_DFLT;
CHAR * pszStmt = NULL;
if (!m_fProcedureSupport)
{
odtLog << "Procedures not supported.\n";
return TEST_SKIPPED;
}
if (g_ulOutParamsSupported == DBPROPVAL_OA_NOTSUPPORTED)
{
odtLog << "Output params not supported.\n";
return TEST_SKIPPED;
}
// Create the syntax and binding for a stored proc with output parameters
ABORT_COMPARE(CreateProcBindings(
eProcType, // [IN] Proc type, regular proc or function (has return value)
TRUE, // [IN] If TRUE then we add parameter names to the rgParamBindInfo
1, // [IN] Number of sets of parameters to be created
DBTYPE_I2, // [IN] Return parameter type
ulRowNum, // [IN] Row number in table to select, insert, or update
&cParams, // [OUT] Count of params created
&cbRowSize, // [OUT] Count of bytes for a single row of parameters
&pBINDING, // [OUT] Binding array for CreateAccessor
&pParamOrdinals,
&pPARAMBIND, // [OUT] rgParamBindInfo for SetParameterInfo
&pwszCreateProcStmt, // [OUT] SQL stmt to create the stored proc
&pwszExecProcStmt, // [OUT] SQL stmt to execute the stored proc
&pwszExecStmt, // [OUT] SQL stmt to execute without stored proc
&pwszProcName, // [OUT] Name of procedure
&pData, // [OUT] Pointer to data for the parameters
&pParamAll
), TRUE);
// Set up to execute the stored proc
ABORT_CHECK(PrepareForExecute(pwszExecProcStmt, cParams, pParamOrdinals, pPARAMBIND,
&fCanDerive, pwszProcName, pwszCreateProcStmt), S_OK);
if (!fCanDerive)
hrSet = S_OK;
// Set parameter info
ABORT_CHECK(m_pICmdWParams->SetParameterInfo(cParams, pParamOrdinals, pPARAMBIND), hrSet);
// Create param accessor and call Execute expecting S_OK
FAIL_VAR(ExecuteAndVerify(cParams, cParamSets, pParamAll,ulRowNum, pBINDING, cbRowSize, pData, ROWSET_NONE,
cColumns, prgColumnsOrd, VERIFY_USE_TABLE, TRUE, m_pICommand, S_OK), S_OK);
CLEANUP:
DropStoredProcedure(m_pICommandText, pwszProcName);
// Free the buffers we got from GetParameterInfo
PROVIDER_FREE(pParamInfo);
PROVIDER_FREE(pNamesBuffer);
PROVIDER_FREE(pBINDING);
PROVIDER_FREE(pParamOrdinals);
PROVIDER_FREE(pPARAMBIND);
PROVIDER_FREE(pwszCreateProcStmt);
PROVIDER_FREE(pwszExecProcStmt);
PROVIDER_FREE(pwszExecStmt);
PROVIDER_FREE(pwszProcName);
PROVIDER_FREE(pData);
::FreeParameterNames(cParams, pParamAll);
PROVIDER_FREE(pParamAll);
return fResult ? TEST_PASS : TEST_FAIL;
}
// }}
// {{ TCW_VAR_PROTOTYPE(45)
//*-----------------------------------------------------------------------
// @mfunc Named Parameters: Sproc parm order != binding order
//
// @rdesc TEST_PASS or TEST_FAIL
//
int TCCommandExecute_Rowset::Variation_45()
{
// The parameters are bound in the reverse order of the parameters found in the
// sql stmt, and are resolved via parameter name only. We need to validate the rowset
// returned in case a provider only suports char type and all parameter types match the
// bindings even when reversed.
BOOL fResult = TRUE;
ULONG cParams, cParamSets = 1;
DBLENGTH cbRowSize = 0;
DBCOUNTITEM ulRowNum = 2;
DBBINDING * pBINDING=NULL;
DB_UPARAMS * pParamOrdinals=NULL;
DBPARAMBINDINFO * pPARAMBIND=NULL;
DBPARAMINFO * pParamInfo = NULL;
WCHAR * pwszCreateProcStmt=NULL;
WCHAR * pwszExecProcStmt=NULL;
WCHAR * pwszExecStmt=NULL;
WCHAR * pwszProcName=NULL;
WCHAR * pNamesBuffer = NULL;
BYTE * pData=NULL;
ParamStruct * pParamAll=NULL;
DBORDINAL cColumns = 0;
DB_LORDINAL * prgColumnsOrd = NULL;
BOOL fCanDerive = FALSE;
HRESULT hrSet = DB_S_TYPEINFOOVERRIDDEN;
enum TOKEN_ENUM eProcType = T_EXEC_PROC_SELECT_OUT;
enum ROWSET_ENUM eRowset = ROWSET_NONE;
if (!m_fProcedureSupport)
{
odtLog << "Procedures not supported.\n";
return TEST_SKIPPED;
}
if (g_ulOutParamsSupported == DBPROPVAL_OA_NOTSUPPORTED)
{
eProcType = T_EXEC_PROC_SELECT_IN;
eRowset = ROWSET_ALWAYS;
}
// Create the syntax and binding for a stored proc with output parameters
ABORT_COMPARE(CreateProcBindings(
eProcType, // [IN] Proc type, regular proc or function (has return value)
TRUE, // [IN] If TRUE then we add parameter names to the rgParamBindInfo
1, // [IN] Number of sets of parameters to be created
DBTYPE_I2, // [IN] Return parameter type
ulRowNum, // [IN] Row number in table to select, insert, or update
&cParams, // [OUT] Count of params created
&cbRowSize, // [OUT] Count of bytes for a single row of parameters
&pBINDING, // [OUT] Binding array for CreateAccessor
&pParamOrdinals,
&pPARAMBIND, // [OUT] rgParamBindInfo for SetParameterInfo
&pwszCreateProcStmt, // [OUT] SQL stmt to create the stored proc
&pwszExecProcStmt, // [OUT] SQL stmt to execute the stored proc
&pwszExecStmt, // [OUT] SQL stmt to execute without stored proc
&pwszProcName, // [OUT] Name of procedure
&pData, // [OUT] Pointer to data for the parameters
&pParamAll,
&cColumns,
&prgColumnsOrd
), TRUE);
// Set up to execute the stored proc
ABORT_CHECK(PrepareForExecute(pwszExecProcStmt, cParams, pParamOrdinals, pPARAMBIND,
&fCanDerive, pwszProcName, pwszCreateProcStmt), S_OK);
if (!fCanDerive)
hrSet = S_OK;
// Set parameter info
ABORT_CHECK(m_pICmdWParams->SetParameterInfo(cParams, pParamOrdinals, pPARAMBIND), hrSet);
ReverseArray(pBINDING, cParams, sizeof(DBBINDING));
ReverseArray(pParamAll, cParams, sizeof(ParamStruct));
// Create param accessor and call Execute expecting S_OK
FAIL_VAR(ExecuteAndVerify(cParams, cParamSets, pParamAll,ulRowNum, pBINDING, cbRowSize, pData, eRowset,
cColumns, prgColumnsOrd, VERIFY_USE_TABLE, TRUE, m_pICommand, S_OK), S_OK);
CLEANUP:
DropStoredProcedure(m_pICommandText, pwszProcName);
// Free the buffers we got from GetParameterInfo
PROVIDER_FREE(pParamInfo);
PROVIDER_FREE(pNamesBuffer);
PROVIDER_FREE(pBINDING);
PROVIDER_FREE(pParamOrdinals);
PROVIDER_FREE(pPARAMBIND);
PROVIDER_FREE(pwszCreateProcStmt);
PROVIDER_FREE(pwszExecProcStmt);
PROVIDER_FREE(pwszExecStmt);
PROVIDER_FREE(pwszProcName);
PROVIDER_FREE(pData);
::FreeParameterNames(cParams, pParamAll);
PROVIDER_FREE(pParamAll);
return fResult ? TEST_PASS : TEST_FAIL;
}
// }}
// {{ TCW_VAR_PROTOTYPE(46)
//*-----------------------------------------------------------------------
// @mfunc Named Parameters: Use all parameter names
//
// @rdesc TEST_PASS or TEST_FAIL
//
int TCCommandExecute_Rowset::Variation_46()
{
// Tests with all named parameters. Since this uses the column name as the parameter name, when the
// column name contains international characters the parameter will also, but must be run on an
// international OS (code page). Parameter bindings match parameter markers in Sql stmt.
// return TestStoredProcBindings (INPUT_OUTPUT, VALID_IN_OUT_DATA, LENGTH_VALUE_STATUS, REGULAR_PARAMNAMES, TRUE);
odtLog << L"This is already tested in other variations now, for example, TCGetParameterInfo_Rowset::Variation_13 and 14.\n";
return TEST_SKIPPED;
}
// }}
// {{ TCW_VAR_PROTOTYPE(47)
//*-----------------------------------------------------------------------
// @mfunc Named Parameters: Retrieve names and use without setting
//
// @rdesc TEST_PASS or TEST_FAIL
//
int TCCommandExecute_Rowset::Variation_47()
{
odtLog << L"Not yet implemented.\n";
return TEST_PASS;
}
// }}
// {{ TCW_VAR_PROTOTYPE(48)
//*-----------------------------------------------------------------------
// @mfunc Named Parameters: Named return parameter from sproc
//
// @rdesc TEST_PASS or TEST_FAIL
//
int TCCommandExecute_Rowset::Variation_48()
{
odtLog << L"Not yet implemented.\n";
return TEST_PASS;
}
// }}
// {{ TCW_VAR_PROTOTYPE(49)
//*-----------------------------------------------------------------------
// @mfunc Inline binding with more than 64K and embedded null term
//
// @rdesc TEST_PASS or TEST_FAIL
//
int TCCommandExecute_Rowset::Variation_49()
{
BOOL fResult = FALSE;
DBROWCOUNT cRowsAffected = 0;
DBCOUNTITEM ulRowNum;
CCol ColInfo;
ULONG iBind=0;
ULONG iCol=0;
DBBINDING * pSaveBinding = NULL;
ULONG * pulBigBind = NULL;
ULONG cBigBind = 0;
DBLENGTH ulColumnSize = 0;
DBLENGTH cbRowSize = 0;
BYTE * pData = NULL;
HRESULT hrSetProp = E_FAIL;
HRESULT hrIns = E_FAIL;
// Some providers can't retrieve BLOB data without this property or IRowsetLocate on.
if (SupportedProperty(DBPROP_ACCESSORDER, DBPROPSET_ROWSET, m_pThisTestModule->m_pIUnknown,SESSION_INTERFACE))
hrSetProp = SetRowsetProperty(m_pICommand, DBPROPSET_ROWSET, DBPROP_ACCESSORDER, (LONG_PTR)DBPROPVAL_AO_RANDOM);
// Make a copy of the bindings so we can increase the size appropriately
SAFE_ALLOC(pSaveBinding, DBBINDING, m_cBindings * sizeof(DBBINDING));
memcpy(pSaveBinding, m_rgBindings, (size_t)(m_cBindings * sizeof(DBBINDING)));
SAFE_ALLOC(pulBigBind, ULONG, m_cBindings * sizeof(ULONG));
// Make sure we have a column that allows more than 64K data
for (iCol = 1; iCol <= m_pTable->CountColumnsOnTable(); iCol++)
{
// Look up information for the column
ABORT_CHECK(m_pTable->GetColInfo(iCol, ColInfo), S_OK);
// We only want the uptable columns
if (!ColInfo.GetUpdateable())
continue;
ulColumnSize = ColInfo.GetColumnSize();
// If the size is more than our boundary value we're happy
if (ulColumnSize > BOUNDARY_VALUE)
{
// Note the column may not set DBCOLUMNFLAGS_ISLONG
m_rgBindings[iBind].cbMaxLen = min(MAX_LONG_VALUE, ulColumnSize);
// WCHAR columns must be even
if ((m_rgBindings[iBind].wType == DBTYPE_WSTR) && (m_rgBindings[iBind].cbMaxLen %2))
m_rgBindings[iBind].cbMaxLen--;
pulBigBind[cBigBind++]=iBind;
}
// Reset the obValue, obLength, obStatus
if (iBind)
{
m_rgBindings[iBind].obValue = m_rgBindings[iBind-1].obValue+m_rgBindings[iBind-1].cbMaxLen
+sizeof(DBSTATUS)+sizeof(DBLENGTH);
m_rgBindings[iBind].obLength = m_rgBindings[iBind-1].obLength+m_rgBindings[iBind-1].cbMaxLen
+sizeof(DBSTATUS)+sizeof(DBLENGTH);
m_rgBindings[iBind].obStatus = m_rgBindings[iBind-1].obStatus+m_rgBindings[iBind-1].cbMaxLen
+sizeof(DBSTATUS)+sizeof(DBLENGTH);
}
cbRowSize+=sizeof(DBLENGTH)+sizeof(DBSTATUS)+m_rgBindings[iBind].cbMaxLen;
iBind++;
}
if (!cBigBind)
{
odtLog << L"No columns with size > " << BOUNDARY_VALUE << " to test with.\n";
fResult = TEST_SKIPPED;
goto CLEANUP;
}
Repack(m_cBindings, m_rgBindings, &cbRowSize);
ReleaseDataForCommand();
SAFE_ALLOC(m_pData, BYTE, cbRowSize);
// Set memory to a known value
memset(m_pData, 'S', (size_t)cbRowSize);
// Remove any old parameter info
TESTC_(m_pICmdWParams->SetParameterInfo(0, NULL, NULL), S_OK);
// Set command text
TESTC_(m_pICommandText->SetCommandText(DBGUID_DBSQL , m_pwszSqlInsertAllWithParams), S_OK);
// Call ICommandPrepare.
TESTC_(m_pICommandPrepare->Prepare(1), S_OK);
//This sets it to one row of data.
if (!CHECK(FillInputBindings(m_pTable, DBACCESSOR_PARAMETERDATA,
m_cBindings, m_rgBindings, &m_pData, ulRowNum = m_pTable->GetNextRowNumber(), m_cParamColMap,
(DB_LORDINAL *)m_rgParamColMap), S_OK))
{
return FALSE;
}
m_DbParamsAll.cParamSets = 1;
m_DbParamsAll.pData = m_pData;
// Reset the accessor to reflect the new bindings
TESTC_(m_pCmdIAccessor->CreateAccessor(DBACCESSOR_PARAMETERDATA | DBACCESSOR_ROWDATA,
m_cBindings,
m_rgBindings,
cbRowSize,
&m_DbParamsAll.hAccessor,
NULL), S_OK);
// Now reset the length binding for the big columns
// Note the data value filled for the big columns does not fill the space available, but
// there is some random data there anyway. We'll use that for validation. String data will
// have an embedded null terminator, which is legal.
for (iBind = 0; iBind < cBigBind; iBind++)
{
LENGTH_BINDING(m_rgBindings[pulBigBind[iBind]], m_pData)=m_rgBindings[pulBigBind[iBind]].cbMaxLen;
// For CHAR and WCHAR we must allow space for NULL terminator. This is not required for
// sending data but for retrieving, otherwise we'll truncate
if (m_rgBindings[pulBigBind[iBind]].wType == DBTYPE_WSTR)
LENGTH_BINDING(m_rgBindings[pulBigBind[iBind]], m_pData) -= sizeof(WCHAR);
if (m_rgBindings[pulBigBind[iBind]].wType == DBTYPE_STR)
LENGTH_BINDING(m_rgBindings[pulBigBind[iBind]], m_pData) -= sizeof(CHAR);
}
// Now execute ICommand.
TESTC_(hrIns = m_pICommand->Execute(NULL, IID_NULL,
&m_DbParamsAll, &cRowsAffected, NULL), S_OK);
// Tell the CTable object we have another row
m_pTable->AddRow();
// Verify If we have inserted the row properly.
TESTC((pData = cRow.GetUpdatableCols(ulRowNum, m_cBindings, m_rgBindings,
cbRowSize, m_pICommand)) != NULL);
// Validate information retrieved
TESTC(CompareBuffer(pData, m_pData, m_cBindings, m_rgBindings, NULL, TRUE, FALSE, COMPARE_ONLY));
fResult = TRUE;
CLEANUP:
// Delete the row we added above because it does not match the expected values and may cause
// subsequent data validation to fail.
// if (SUCCEEDED(hrIns))
// m_pTable->DeleteRows(ulRowNum);
ReleaseDataForCommand();
SAFE_RELEASE_ACCESSOR(m_pCmdIAccessor, m_DbParamsAll.hAccessor);
// Put the bindings back
if (pSaveBinding)
memcpy(m_rgBindings, pSaveBinding, (size_t)(m_cBindings * sizeof(DBBINDING)));
SAFE_FREE(pSaveBinding);
SAFE_FREE(pulBigBind);
SAFE_FREE(pData);
// If we set RANDOM REQUIRED above we need to set back OPTIONAL
if (hrSetProp == S_OK)
CHECK(SetRowsetPropertyDefault(DBPROP_ACCESSORDER), S_OK);
return fResult;
}
// }} TCW_VAR_PROTOTYPE_END
// {{ TCW_VAR_PROTOTYPE(50)
//*-----------------------------------------------------------------------
// @mfunc Insert with BSTR bindings to strings
//
// @rdesc TEST_PASS or TEST_FAIL
//
int TCCommandExecute_Rowset::Variation_50()
{
BOOL fResult = TEST_FAIL;
DBROWCOUNT cRowsAffected = 0;
IConvertType * pIConvertType = NULL;
DBBINDING * pBindings = NULL;
ULONG iBind;
HACCESSOR hParamAccessor = m_hAccessor;
BOOL fConvertSTR = FALSE;
BOOL fConvertWSTR = FALSE;
HRESULT hrConv = E_FAIL;
HRESULT hrSet = DB_S_TYPEINFOOVERRIDDEN;
// Make sure the provider claims it can support conversion from BSTR -> STR
// or BSTR -> WSTR.
TESTC(VerifyInterface(m_pICommand, IID_IConvertType,
COMMAND_INTERFACE,(IUnknown **)&pIConvertType));
hrConv = pIConvertType->CanConvert(DBTYPE_BSTR, DBTYPE_STR, DBCONVERTFLAGS_PARAMETER);
if (S_OK == hrConv)
fConvertSTR = TRUE;
else
CHECK(hrConv, S_FALSE);
hrConv = pIConvertType->CanConvert(DBTYPE_BSTR, DBTYPE_WSTR, DBCONVERTFLAGS_PARAMETER);
if (S_OK == hrConv)
fConvertWSTR = TRUE;
else
CHECK(hrConv, S_FALSE);
if (!fConvertSTR && !fConvertWSTR)
{
odtLog << L"Provider doesn't support conversions from BSTR to string types.\n";
fResult = TEST_SKIPPED;
goto CLEANUP;
}
// Remove any old parameter info
TEST_CHECK(m_pICmdWParams->SetParameterInfo(0, NULL, NULL), S_OK);
// Now set back to "default" values for the insert statement we got in the base class init
if (m_bSetParameterInfo)
hrSet = S_OK;
FAIL_CHECK(m_pICmdWParams->SetParameterInfo(m_cDbParamBindInfo, m_rgParamOrdinals, m_rgDbParamBindInfo), hrSet);
// Reset str and wstr bindings to BSTR.
SAFE_ALLOC(pBindings, DBBINDING, m_cBindings);
memcpy(pBindings, m_rgBindings, (size_t)(m_cBindings*sizeof(DBBINDING)));
// Release any previous binding memory
ReleaseDataForCommand();
// Allocate a block of memory to hold the bindings
// Note we allocate up front instead of letting FillInputBindings do it. This is
// because FillInputBindings will allocate a minimum sized block of mem, while
// we have extra space in the binding array for the str types.
SAFE_ALLOC(m_pData, BYTE, m_cbRowSize);
for (iBind=0; iBind < m_cBindings; iBind++)
{
if (m_rgBindings[iBind].wType == DBTYPE_STR && fConvertSTR)
m_rgBindings[iBind].wType = DBTYPE_BSTR;
if (m_rgBindings[iBind].wType == DBTYPE_WSTR && fConvertWSTR)
m_rgBindings[iBind].wType = DBTYPE_BSTR;
}
TEST_CHECK(m_pCmdIAccessor->CreateAccessor(DBACCESSOR_PARAMETERDATA, m_cBindings, m_rgBindings, m_cbRowSize,
&m_hAccessor, NULL), S_OK);
// Set command text
TEST_CHECK (m_pICommandText->SetCommandText(DBGUID_DBSQL , m_pwszSqlInsertAllWithParams), S_OK);
// Call ICommandPrepare.
TEST_CHECK (m_pICommandPrepare->Prepare(1), S_OK);
TESTC_(FillInputBindings(m_pTable, DBACCESSOR_PARAMETERDATA,
m_cBindings, m_rgBindings, &m_pData, m_pTable->GetNextRowNumber(), m_cParamColMap, (DB_LORDINAL *)m_rgParamColMap), S_OK);
m_DbParamsAll.cParamSets = 1;
m_DbParamsAll.hAccessor = m_hAccessor;
m_DbParamsAll.pData = m_pData;
// Now execute ICommand.
TEST_CHECK(m_pICommand->Execute(NULL, IID_NULL,
&m_DbParamsAll, &cRowsAffected, NULL), S_OK);
// Verify If we have inserted the row properly.
TEST_COMPARE(cRow.FindRow(m_pTable->GetNextRowNumber(), m_pTable), TRUE);
// Tell the CTable object we have another row
m_pTable->AddRow();
fResult = TEST_PASS;
CLEANUP:
ReleaseDataForCommand();
memcpy(m_rgBindings, pBindings, (size_t)(m_cBindings * sizeof(DBBINDING)));
SAFE_FREE(pBindings);
SAFE_RELEASE_ACCESSOR(m_pCmdIAccessor, m_hAccessor);
SAFE_RELEASE(pIConvertType);
m_hAccessor = hParamAccessor;
return fResult;
}
// }} TCW_VAR_PROTOTYPE_END
// {{ TCW_VAR_PROTOTYPE(51)
//*-----------------------------------------------------------------------
// @mfunc OpenRowset on a stored proc with params
//
// @rdesc TEST_PASS or TEST_FAIL
//
int TCCommandExecute_Rowset::Variation_51()
{
BOOL fResult = TRUE;
ULONG cParams, cParamSets = 1;
DBLENGTH cbRowSize = 0;
DBCOUNTITEM ulRowNum = 1;
DBBINDING * pBINDING=NULL;
DB_UPARAMS * pParamOrdinals=NULL;
DBPARAMBINDINFO * pPARAMBIND=NULL;
WCHAR * pwszCreateProcStmt=NULL;
WCHAR * pwszExecProcStmt=NULL;
WCHAR * pwszExecStmt=NULL;
WCHAR * pwszProcName=NULL;
BYTE * pData=NULL;
ParamStruct * pParamAll=NULL;
IOpenRowset * pIOpenRowset = NULL;
IRowset * pIRowset = NULL;
DBID dbidProc = DB_NULLID;
HRESULT hr = E_FAIL;
if (!m_fProcedureSupport)
{
odtLog << "Procedures not supported.\n";
return TEST_SKIPPED;
}
if (g_ulOutParamsSupported == DBPROPVAL_OA_NOTSUPPORTED)
{
odtLog << "Output parameters not supported.\n";
return TEST_SKIPPED;
}
// Create the syntax and binding for a stored proc with output parameters
ABORT_COMPARE(CreateProcBindings(
T_EXEC_PROC_SELECT_INOUT,// [IN] Proc type, regular proc or function (has return value)
TRUE, // [IN] If TRUE then we add parameter names to the rgParamBindInfo
1, // [IN] Number of sets of parameters to be created
DBTYPE_I2, // [IN] Return parameter type
1, // [IN] Row number in table to select, insert, or update
&cParams, // [OUT] Count of params created
&cbRowSize, // [OUT] Count of bytes for a single row of parameters
&pBINDING, // [OUT] Binding array for CreateAccessor
&pParamOrdinals,
&pPARAMBIND, // [OUT] rgParamBindInfo for SetParameterInfo
&pwszCreateProcStmt, // [OUT] SQL stmt to create the stored proc
&pwszExecProcStmt, // [OUT] SQL stmt to execute the stored proc
&pwszExecStmt, // [OUT] SQL stmt to execute without stored proc
&pwszProcName, // [OUT] Name of procedure
&pData, // [OUT] Pointer to data for the parameters
&pParamAll
), TRUE);
// Get an OpenRowset interface
TEST_COMPARE(VerifyInterface(m_pThisTestModule->m_pIUnknown2,
IID_IOpenRowset, SESSION_INTERFACE, (IUnknown**)&pIOpenRowset),TRUE);
// Create a DBID from the proc name
dbidProc.eKind = DBKIND_NAME;
dbidProc.uName.pwszName = pwszProcName;
// Call OpenRowset on the stored proc
hr = pIOpenRowset->OpenRowset(NULL, &dbidProc, NULL, m_iidExec,
0, NULL, (IUnknown **)&pIRowset);
// Allow DB_E_PARAMNOTOPTIONAL or DB_E_NOTABLE (proc isn't a table).
TEST2C_(hr, DB_E_NOTABLE, DB_E_PARAMNOTOPTIONAL);
fResult = TEST_PASS;
CLEANUP:
DropStoredProcedure(m_pICommandText, pwszProcName);
// Free the buffers we got from GetParameterInfo
PROVIDER_FREE(pBINDING);
PROVIDER_FREE(pParamOrdinals);
PROVIDER_FREE(pPARAMBIND);
PROVIDER_FREE(pwszCreateProcStmt);
PROVIDER_FREE(pwszExecProcStmt);
PROVIDER_FREE(pwszExecStmt);
PROVIDER_FREE(pwszProcName);
PROVIDER_FREE(pData);
::FreeParameterNames(cParams, pParamAll);
PROVIDER_FREE(pParamAll);
SAFE_RELEASE(pIOpenRowset);
return fResult ? TEST_PASS : TEST_FAIL;
}
// }} TCW_VAR_PROTOTYPE_END
// {{ TCW_VAR_PROTOTYPE(52)
//*-----------------------------------------------------------------------
// @mfunc DBSTATUS_S_DEFAULT - Default values for param
//
// @rdesc TEST_PASS or TEST_FAIL
//
int TCCommandExecute_Rowset::Variation_52()
{
/*
We have to create a new table here because we can't create a default value for a non-updatable column, and
the only way we know if a column is updatable is to have the table already created. Therefore we use the
original table to tell us the updatability. Notice we create the new table with the same column ordering.
*/
ULONG cDefaultCols =0;
BOOL fResult = TEST_FAIL;
CTable * pDefaultTable = NULL;
ULONG iCol;
DBORDINAL cCols;
DBCOLUMNDESC * prgColumnDesc = NULL;
DBROWCOUNT cRowsAffected = 0;
ULONG ulRowNum = TOTAL_NUMBER_OF_ROWS+1;
ULONG iBind = 0;
IRowset * pIRowset = NULL;
WCHAR * pwszSqlInsertAllWithParams = NULL;
HRESULT hrSetProp = E_FAIL;
DB_LORDINAL * pCols = NULL;
DBCOUNTITEM cRowsExpected = 1;
// Make sure the provider supports default params in statements
// Oracle doesn't support default params in statements, only PL/SQL.
// In ODBC, SQL_DEFAULT_PARAM is not supported for statements, per ODBC spec.
if (!m_fDefaultVal)
{
odtLog << L"Provider doesn't support default values for columns.\n";
return TEST_SKIPPED;
}
// Initialize our accessor
m_DbParamsAll.hAccessor = DB_NULL_HACCESSOR;
// We have to jump through some hoops to get default values for each column
// These are necessary until CreateColInfo takes pointers
CList <WCHAR * ,WCHAR *> ListNativeTemp;
CList <DBTYPE,DBTYPE> ListDataTypes;
if (!(pDefaultTable = new CTable((IUnknown *)m_pThisTestModule->m_pIUnknown2,
(LPWSTR)gwszModuleName, NONULLS)))
{
PRVTRACE (wszMemoryAllocationError);
goto CLEANUP;
}
// Determine the SQL Support
pDefaultTable->ProviderSQLSupport();
// Create the colinfo for the table
TESTC_(pDefaultTable->CreateColInfo(ListNativeTemp,ListDataTypes,ALLTYPES,FALSE), S_OK);
cCols = pDefaultTable->CountColumnsOnTable();
// Make sure the count of columns is the same
TESTC(cCols == m_pTable->CountColumnsOnTable());
// Set the unique index flag to match the original table
pDefaultTable->SetIndexColumn(m_pTable->GetIndexColumn());
// Now go through the colinfo and update to force a default value for each column
for (iCol = 1; iCol <= cCols; iCol++)
{
// Get colinfo for column in default table for update
CCol& DefaultCol=pDefaultTable->GetColInfoForUpdate(iCol);
CCol OrigCol;
TESTC_(m_pTable->GetColInfo(iCol, OrigCol), S_OK);
// Make sure the columns appear to match
// TESTC(!_wcsicmp(DefaultCol.GetColName(), OrigCol.GetColName()));
// Set the updatability to match the original table
DefaultCol.SetUpdateable(OrigCol.GetUpdateable());
// We can only have defaults for updatable cols
if (!DefaultCol.GetUpdateable())
continue;
// Now create the default value and set flag indicating there's a default
if (pDefaultTable->SetDefaultValue(DefaultCol, ulRowNum))
cDefaultCols++;
}
// Make sure we actually have some default cols
if (!cDefaultCols)
{
odtLog << L"No default columns available.\n";
fResult = TEST_SKIPPED;
goto CLEANUP;
}
TESTC_(pDefaultTable->BuildColumnDescs(&prgColumnDesc), S_OK);
pDefaultTable->SetColumnDesc(prgColumnDesc, pDefaultTable->CountColumnsOnTable());
pDefaultTable->SetBuildColumnDesc(FALSE);
// Now actually create the table containing default values
TESTC_(pDefaultTable->CreateTable(ulRowNum-1), S_OK);
// Remove any old parameter info
TEST_CHECK(m_pICmdWParams->SetParameterInfo(0, NULL, NULL), S_OK);
TESTC_(pDefaultTable->ExecuteCommand(INSERT_ALLWITHPARAMS, IID_IUnknown, NULL,
&pwszSqlInsertAllWithParams, NULL, NULL, EXECUTE_NEVER, 0, NULL, NULL, NULL, &m_pICommand), S_OK);
// Set command text
TEST_CHECK (m_pICommandText->SetCommandText(DBGUID_DBSQL , pwszSqlInsertAllWithParams), S_OK);
// Just to be different don't call Prepare
// This sets it to one row of data. Note it actually uses the original table
// to get the data values, but since they match it's OK.
TESTC_(FillInputBindings(pDefaultTable, DBACCESSOR_PARAMETERDATA,
m_cBindings, m_rgBindings, &m_pData, ulRowNum, m_cParamColMap, (DB_LORDINAL *)m_rgParamColMap), S_OK);
// For each column with a default value, set obValue and obLength to bogus values
// as provider must ignore them.
for (iCol = 1; iCol <= cCols; iCol++)
{
CCol DefaultCol;
TESTC_(pDefaultTable->GetColInfo(iCol, DefaultCol), S_OK);
// We only want the uptable columns
if (!DefaultCol.GetUpdateable())
continue;
if (DefaultCol.GetHasDefault())
{
// The column has a default value, set the value and length to bogus values.
// Also set status to DBSTATUS_S_DEFAULT.
STATUS_BINDING(m_rgBindings[iBind], m_pData) = DBSTATUS_S_DEFAULT;
VALUE_BINDING(m_rgBindings[iBind], m_pData) = 0;
LENGTH_BINDING(m_rgBindings[iBind], m_pData) = ULONG_MAX;
}
iBind++;
}
m_DbParamsAll.cParamSets = 1;
m_DbParamsAll.pData = m_pData;
// Reset the accessor to reflect the new bindings
TEST_CHECK(m_pCmdIAccessor->CreateAccessor(DBACCESSOR_PARAMETERDATA | DBACCESSOR_ROWDATA,
m_cBindings,
m_rgBindings,
m_cbRowSize,
&m_DbParamsAll.hAccessor,
NULL), S_OK);
// Perform insert. We use a row seed larger than number of rows table was created
// with to avoid duplicate rows.
TEST_CHECK(m_pICommand->Execute(NULL, IID_NULL,
&m_DbParamsAll, &cRowsAffected, NULL), S_OK);
// Some providers can't retrieve BLOB data without this property or IRowsetLocate on.
if (SupportedProperty(DBPROP_ACCESSORDER, DBPROPSET_ROWSET, m_pThisTestModule->m_pIUnknown,SESSION_INTERFACE))
hrSetProp = SetRowsetProperty(m_pICommand, DBPROPSET_ROWSET, DBPROP_ACCESSORDER, (LONG_PTR)DBPROPVAL_AO_RANDOM);
// Verify If we have inserted the row properly.
TEST_COMPARE(cRow.FindRow(ulRowNum, pDefaultTable, m_pICommand, &pIRowset, NULL,
&cCols, &pCols, FALSE), TRUE);
// FindRow merely finds the appropriate row. We need to validate the inserted data for the default bindings.
TEST_COMPARE(VerifyObj(m_iidExec, pIRowset, ulRowNum, cCols, pCols,
FALSE, FALSE, pDefaultTable, &cRowsExpected), S_OK);
fResult = TEST_PASS;
CLEANUP:
// Release the rowset
SAFE_RELEASE(pIRowset);
// If we set RANDOM REQUIRED above we need to set back to default
if (hrSetProp == S_OK)
CHECK(SetRowsetPropertyDefault(DBPROP_ACCESSORDER), S_OK);
ReleaseDataForCommand();
SAFE_RELEASE_ACCESSOR(m_pCmdIAccessor, m_DbParamsAll.hAccessor);
SAFE_FREE(pwszSqlInsertAllWithParams);
SAFE_FREE(pCols);
ReleaseInputBindingsMemory(m_cBindings, m_rgBindings, m_pData, TRUE);
if (pDefaultTable)
{
pDefaultTable->DropTable();
delete pDefaultTable;
}
return fResult;
}
// }} TCW_VAR_PROTOTYPE_END
// {{ TCW_VAR_PROTOTYPE(53)
//*-----------------------------------------------------------------------
// @mfunc Multiple procs - execute two procs without SetParamInfo or Prepare
//
// @rdesc TEST_PASS or TEST_FAIL
//
int TCCommandExecute_Rowset::Variation_53()
{
BOOL fResult = TRUE;
ULONG cParams, cParamSets = 1;
DBLENGTH cbRowSize = 0;
DBCOUNTITEM ulRowNum = 1;
DBBINDING * pBINDING=NULL;
DB_UPARAMS * pParamOrdinals=NULL;
DBPARAMBINDINFO * pPARAMBIND=NULL;
WCHAR * pwszCreateProcStmt=NULL;
WCHAR * pwszExecProcStmt=NULL;
WCHAR * pwszExecStmt=NULL;
WCHAR * pwszProcName=NULL;
BYTE * pData=NULL;
ParamStruct * pParamAll=NULL;
ULONG cColumns = 0;
DB_LORDINAL * prgColumnsOrd = NULL;
BOOL fCanDerive = FALSE;
HRESULT hrSet = DB_S_TYPEINFOOVERRIDDEN;
DB_UPARAMS cActParams = 0;
WCHAR * pNamesBuffer = NULL;
DBPARAMINFO * pParamInfo = NULL;
// Variables needed for second proc
ULONG cParams2, cParamSets2 = 1;
DBLENGTH cbRowSize2;
DBBINDING * pBINDING2 = NULL;
DB_UPARAMS * pParamOrdinals2 = NULL;
DBPARAMBINDINFO * pPARAMBIND2=NULL;
WCHAR * pwszCreateProcStmt2=NULL;
WCHAR * pwszExecProcStmt2=NULL;
WCHAR * pwszExecStmt2=NULL;
WCHAR * pwszProcName2=NULL;
BYTE * pData2=NULL;
ParamStruct * pParamAll2=NULL;
HRESULT hr = E_FAIL;
DB_UPARAMS cParamsOut=0;
DBPARAMINFO * pParamInfoOut = NULL;
WCHAR * pNamesBufferOut = NULL;
if (!m_fProcedureSupport)
{
odtLog << "Procedures not supported.\n";
return TEST_SKIPPED;
}
if (g_ulOutParamsSupported == DBPROPVAL_OA_NOTSUPPORTED)
{
odtLog << "Output parameters not supported.\n";
return TEST_SKIPPED;
}
// Create the syntax and binding for a stored proc with input and output parameters
ABORT_COMPARE(CreateProcBindings(
T_EXEC_PROC_SELECT_INOUT,// [IN] Proc type, regular proc or function (has return value)
TRUE, // [IN] If TRUE then we add parameter names to the rgParamBindInfo
1, // [IN] Number of sets of parameters to be created
DBTYPE_I2, // [IN] Return parameter type
ulRowNum, // [IN] Row number in table to select, insert, or update
&cParams, // [OUT] Count of params created
&cbRowSize, // [OUT] Count of bytes for a single row of parameters
&pBINDING, // [OUT] Binding array for CreateAccessor
&pParamOrdinals,
&pPARAMBIND, // [OUT] rgParamBindInfo for SetParameterInfo
&pwszCreateProcStmt, // [OUT] SQL stmt to create the stored proc
&pwszExecProcStmt, // [OUT] SQL stmt to execute the stored proc
&pwszExecStmt, // [OUT] SQL stmt to execute without stored proc
&pwszProcName, // [OUT] Name of procedure
&pData, // [OUT] Pointer to data for the parameters
&pParamAll
), TRUE);
// Create the syntax and binding for a second stored proc with output parameters
ABORT_COMPARE(CreateProcBindings(
T_EXEC_PROC_SELECT_OUT,// [IN] Proc type, regular proc or function (has return value)
TRUE, // [IN] If TRUE then we add parameter names to the rgParamBindInfo
1, // [IN] Number of sets of parameters to be created
DBTYPE_I2, // [IN] Return parameter type
2, // [IN] Row number in table to select, insert, or update
&cParams2, // [OUT] Count of params created
&cbRowSize2, // [OUT] Count of bytes for a single row of parameters
&pBINDING2, // [OUT] Binding array for CreateAccessor
&pParamOrdinals2,
&pPARAMBIND2, // [OUT] rgParamBindInfo for SetParameterInfo
&pwszCreateProcStmt2, // [OUT] SQL stmt to create the stored proc
&pwszExecProcStmt2, // [OUT] SQL stmt to execute the stored proc
&pwszExecStmt2, // [OUT] SQL stmt to execute without stored proc
&pwszProcName2, // [OUT] Name of procedure
&pData2, // [OUT] Pointer to data for the parameters
&pParamAll2
), TRUE);
// Remove any old parameter info
FAIL_CHECK(m_pICmdWParams->SetParameterInfo(0, NULL, NULL), S_OK);
ABORT_CHECK(hr = CreateStoredProc(m_pICommandText, pwszProcName, pwszCreateProcStmt, FALSE), S_OK);
ABORT_CHECK(hr = CreateStoredProc(m_pICommandText, pwszProcName2, pwszCreateProcStmt2, FALSE), S_OK);
// Set the command text to execute the stored proc
ABORT_CHECK(hr = m_pICommandText->SetCommandText(DBGUID_DBSQL, pwszExecProcStmt), S_OK);
if (m_pICommandPrepare)
ABORT_CHECK(hr = m_pICommandPrepare->Prepare(1), S_OK);
ABORT_CHECK(m_pICmdWParams->GetParameterInfo(&cParamsOut, &pParamInfoOut, &pNamesBufferOut), S_OK);
// The final proof is that we can execute with these values
FAIL_VAR(ExecuteAndVerify(cParams, cParamSets, pParamAll, ulRowNum, pBINDING, cbRowSize, pData, ROWSET_NONE,
cColumns, prgColumnsOrd, VERIFY_USE_PDATA, TRUE), S_OK);
// Now set up to execute the second stored proc
// Set the command text to execute the stored proc
ABORT_CHECK(hr = m_pICommandText->SetCommandText(DBGUID_DBSQL, pwszExecProcStmt2), S_OK);
// The final proof is that we can execute with these values
FAIL_VAR(ExecuteAndVerify(cParams2, cParamSets2, pParamAll2, 2, pBINDING2, cbRowSize2, pData2, ROWSET_NONE,
cColumns, prgColumnsOrd, VERIFY_USE_TABLE, TRUE), S_OK);
CLEANUP:
DropStoredProcedure(m_pICommandText, pwszProcName);
DropStoredProcedure(m_pICommandText, pwszProcName2);
// Free the buffers we got from GetParameterInfo
PROVIDER_FREE(pBINDING);
PROVIDER_FREE(pParamOrdinals);
PROVIDER_FREE(pPARAMBIND);
PROVIDER_FREE(pwszCreateProcStmt);
PROVIDER_FREE(pwszExecProcStmt);
PROVIDER_FREE(pwszExecStmt);
PROVIDER_FREE(pwszProcName);
PROVIDER_FREE(pData);
::FreeParameterNames(cParams, pParamAll);
PROVIDER_FREE(pParamAll);
PROVIDER_FREE(pBINDING2);
PROVIDER_FREE(pParamOrdinals2);
PROVIDER_FREE(pPARAMBIND2);
PROVIDER_FREE(pwszCreateProcStmt2);
PROVIDER_FREE(pwszExecProcStmt2);
PROVIDER_FREE(pwszExecStmt2);
PROVIDER_FREE(pwszProcName2);
PROVIDER_FREE(pData2);
PROVIDER_FREE(pParamAll2);
return fResult ? TEST_PASS : TEST_FAIL;
}
// }} TCW_VAR_PROTOTYPE_END
// {{ TCW_VAR_PROTOTYPE(54)
//*-----------------------------------------------------------------------
// @mfunc Multiple procs - execute two procs with partial SetParamInfo and Prepare
//
// @rdesc TEST_PASS or TEST_FAIL
//
int TCCommandExecute_Rowset::Variation_54()
{
BOOL fResult = TRUE;
ULONG cParams, cParamSets = 1;
DBLENGTH cbRowSize = 0;
DBCOUNTITEM ulRowNum = 1;
DBBINDING * pBINDING=NULL;
DB_UPARAMS * pParamOrdinals=NULL;
DBPARAMBINDINFO * pPARAMBIND=NULL;
WCHAR * pwszCreateProcStmt=NULL;
WCHAR * pwszExecProcStmt=NULL;
WCHAR * pwszExecStmt=NULL;
WCHAR * pwszProcName=NULL;
BYTE * pData=NULL;
ParamStruct * pParamAll=NULL;
ULONG cColumns = 0;
DB_LORDINAL * prgColumnsOrd = NULL;
BOOL fCanDerive = FALSE;
HRESULT hrSet = DB_S_TYPEINFOOVERRIDDEN;
DB_UPARAMS cActParams = 0;
WCHAR * pNamesBuffer = NULL;
DBPARAMINFO * pParamInfo = NULL;
// Variables needed for second proc
ULONG cParams2, cParamSets2 = 1;
DBLENGTH cbRowSize2 = 0;
DBBINDING * pBINDING2 = NULL;
DB_UPARAMS * pParamOrdinals2 = NULL;
DBPARAMBINDINFO * pPARAMBIND2=NULL;
WCHAR * pwszCreateProcStmt2=NULL;
WCHAR * pwszExecProcStmt2=NULL;
WCHAR * pwszExecStmt2=NULL;
WCHAR * pwszProcName2=NULL;
BYTE * pData2=NULL;
ParamStruct * pParamAll2=NULL;
HRESULT hr = E_FAIL;
// For extra parameter validation after setting first param
DB_UPARAMS cParamsOut=0;
DBPARAMINFO * pParamInfoOut = NULL;
WCHAR * pNamesBufferOut = NULL;
if (!m_fProcedureSupport)
{
odtLog << "Procedures not supported.\n";
return TEST_SKIPPED;
}
if (g_ulOutParamsSupported == DBPROPVAL_OA_NOTSUPPORTED)
{
odtLog << "Output parameters not supported.\n";
return TEST_SKIPPED;
}
// Create the syntax and binding for a stored proc with input and output parameters
ABORT_COMPARE(CreateProcBindings(
T_EXEC_PROC_SELECT_OUT,// [IN] Proc type, regular proc or function (has return value)
TRUE, // [IN] If TRUE then we add parameter names to the rgParamBindInfo
1, // [IN] Number of sets of parameters to be created
DBTYPE_I2, // [IN] Return parameter type
ulRowNum, // [IN] Row number in table to select, insert, or update
&cParams, // [OUT] Count of params created
&cbRowSize, // [OUT] Count of bytes for a single row of parameters
&pBINDING, // [OUT] Binding array for CreateAccessor
&pParamOrdinals,
&pPARAMBIND, // [OUT] rgParamBindInfo for SetParameterInfo
&pwszCreateProcStmt, // [OUT] SQL stmt to create the stored proc
&pwszExecProcStmt, // [OUT] SQL stmt to execute the stored proc
&pwszExecStmt, // [OUT] SQL stmt to execute without stored proc
&pwszProcName, // [OUT] Name of procedure
&pData, // [OUT] Pointer to data for the parameters
&pParamAll
), TRUE);
// Create the syntax and binding for a second stored proc with output parameters
ABORT_COMPARE(CreateProcBindings(
T_EXEC_PROC_SELECT_INOUT,// [IN] Proc type, regular proc or function (has return value)
TRUE, // [IN] If TRUE then we add parameter names to the rgParamBindInfo
1, // [IN] Number of sets of parameters to be created
DBTYPE_I2, // [IN] Return parameter type
2, // [IN] Row number in table to select, insert, or update
&cParams2, // [OUT] Count of params created
&cbRowSize2, // [OUT] Count of bytes for a single row of parameters
&pBINDING2, // [OUT] Binding array for CreateAccessor
&pParamOrdinals2,
&pPARAMBIND2, // [OUT] rgParamBindInfo for SetParameterInfo
&pwszCreateProcStmt2, // [OUT] SQL stmt to create the stored proc
&pwszExecProcStmt2, // [OUT] SQL stmt to execute the stored proc
&pwszExecStmt2, // [OUT] SQL stmt to execute without stored proc
&pwszProcName2, // [OUT] Name of procedure
&pData2, // [OUT] Pointer to data for the parameters
&pParamAll2
), TRUE);
// Create the second stored proc first
ABORT_CHECK(hr = CreateStoredProc(m_pICommandText, pwszProcName2, pwszCreateProcStmt2, FALSE), S_OK);
// Remove any old parameter info
FAIL_CHECK(m_pICmdWParams->SetParameterInfo(0, NULL, NULL), S_OK);
// Set up to execute the first stored proc
ABORT_CHECK(PrepareForExecute(pwszExecProcStmt, cParams, pParamOrdinals, pPARAMBIND,
&fCanDerive, pwszProcName, pwszCreateProcStmt), S_OK);
// Call SetParameterInfo for the first param
hr = m_pICmdWParams->SetParameterInfo(1, pParamOrdinals, pPARAMBIND);
ABORT_CHECK(m_pICmdWParams->GetParameterInfo(&cParamsOut, &pParamInfoOut, &pNamesBufferOut), S_OK);
// Per spec it is provider-specific whether a provider returns information for all parameters or only
// for the one I set above. So if 1 is not returned then cParams should be
if (cParamsOut != 1)
FAIL_COMPARE(cParamsOut, cParams);
// Check the parameter information returned for the proper number of params
FAIL_VAR(VerifyParamInfo(cParamsOut, pParamOrdinals, pPARAMBIND,
cParamsOut, pParamInfoOut, pNamesBufferOut), S_OK);
SAFE_FREE(pParamInfoOut);
SAFE_FREE(pNamesBufferOut);
// If the provider returns information about all the params then we should be able to Execute.
// Otherwise we don't have information about all params, and result of Execute is undefined.
if (cParamsOut == cParams)
{
FAIL_VAR(ExecuteAndVerify(cParams, cParamSets, pParamAll, ulRowNum, pBINDING, cbRowSize, pData, ROWSET_NONE,
cColumns, prgColumnsOrd, VERIFY_USE_TABLE, TRUE), S_OK);
}
// Now set up to execute the second stored proc
ABORT_CHECK(hr = m_pICommandText->SetCommandText(DBGUID_DBSQL, pwszExecProcStmt2), S_OK);
if (m_pICommandPrepare)
ABORT_CHECK(hr = m_pICommandPrepare->Prepare(1), S_OK);
// Now get parameter info again
ABORT_CHECK(m_pICmdWParams->GetParameterInfo(&cParamsOut, &pParamInfoOut, &pNamesBufferOut), S_OK);
// Per spec it is provider-specific whether a provider returns information for all parameters or only
// for the one I set above. So if 1 is not returned then cParams should be
if (cParamsOut != 1)
FAIL_COMPARE(cParamsOut, cParams2);
// Since we called SetParameterInfo for only one param it should match what we set
FAIL_VAR(VerifyParamInfo(1, pParamOrdinals, pPARAMBIND,
cParamsOut, pParamInfoOut, pNamesBufferOut), S_OK);
// If any other were returned they should match the second stored proc
if (cParamsOut > 1)
{
FAIL_VAR(VerifyParamInfo(cParamsOut-1, &pParamOrdinals2[1], &pPARAMBIND2[1],
cParamsOut-1, &pParamInfoOut[1], pNamesBufferOut, 1), S_OK);
}
// We don't expect execution to succeed now that our parameter information is incorrect.
// Since the parameter is really I/O but we set it Output only in SetParameterInfo then
// there's no input parameter. Expected return code may be DB_E_ERRORSOCCURRED or DB_E_PARAMNOTOPTIONAL.
// However, since we claim in the spec "Result of executing with wrong parameter information is undefined"
// then we could even succeed and return bogus data, or could crash. So don't bother to test it.
// FAIL_VAR(ExecuteAndVerify(cParams2, cParamSets2, pParamAll2, ulRowNum, pBINDING2, cbRowSize2, pData2, ROWSET_NONE,
// cColumns, prgColumnsOrd, VERIFY_USE_PDATA, TRUE, NULL, DB_E_ERRORSOCCURRED), S_OK);
CLEANUP:
DropStoredProcedure(m_pICommandText, pwszProcName);
DropStoredProcedure(m_pICommandText, pwszProcName2);
// Free the buffers we got from GetParameterInfo
PROVIDER_FREE(pNamesBufferOut);
PROVIDER_FREE(pParamInfoOut);
// Free other buffers
PROVIDER_FREE(pBINDING);
PROVIDER_FREE(pParamOrdinals);
PROVIDER_FREE(pPARAMBIND);
PROVIDER_FREE(pwszCreateProcStmt);
PROVIDER_FREE(pwszExecProcStmt);
PROVIDER_FREE(pwszExecStmt);
PROVIDER_FREE(pwszProcName);
PROVIDER_FREE(pData);
::FreeParameterNames(cParams, pParamAll);
PROVIDER_FREE(pParamAll);
PROVIDER_FREE(pBINDING2);
PROVIDER_FREE(pParamOrdinals2);
PROVIDER_FREE(pPARAMBIND2);
PROVIDER_FREE(pwszCreateProcStmt2);
PROVIDER_FREE(pwszExecProcStmt2);
PROVIDER_FREE(pwszExecStmt2);
PROVIDER_FREE(pwszProcName2);
PROVIDER_FREE(pData2);
PROVIDER_FREE(pParamAll2);
return fResult ? TEST_PASS : TEST_FAIL;
}
// }} TCW_VAR_PROTOTYPE_END
// {{ TCW_VAR_PROTOTYPE(55)
//*-----------------------------------------------------------------------
// @mfunc DBSTATUS_S_IGNORE - Not legal for params (DBSTATUS_E_BADSTATUS)
//
// @rdesc TEST_PASS or TEST_FAIL
//
int TCCommandExecute_Rowset::Variation_55()
{
BOOL fResult = FALSE;
DBROWCOUNT cRowsAffected = 0;
ULONG iParam;
DBCOUNTITEM ulRowNum;
HRESULT hr = E_FAIL;
// Set command text
TEST_CHECK (m_pICommandText->SetCommandText(DBGUID_DBSQL , m_pwszSqlInsertAllWithParams), S_OK);
// Call ICommandPrepare.
TEST_CHECK (m_pICommandPrepare->Prepare(1), S_OK);
// No need to check return code, checked inside function
if (FAILED(SetParameterInfoIfNeeded(m_cDbParamBindInfo, m_rgParamOrdinals, m_rgDbParamBindInfo)))
goto CLEANUP;
//This sets it to one row of data.
MakeDataForCommand((ulRowNum = NextInsertRowNum()));
// Set each param status to DBSTATUS_S_IGNORE in turn, Execute should fail.
// We have to set them individually because the provider might fail to catch
// for some data types.
for (iParam = 0; iParam < m_cBindings; iParam++)
{
// We assume the initial status was DBSTATUS_S_OK
if (iParam && STATUS_BINDING(m_rgBindings[iParam-1], m_pData) == DBSTATUS_S_IGNORE)
STATUS_BINDING(m_rgBindings[iParam-1], m_pData) = DBSTATUS_S_OK;
if (STATUS_BINDING(m_rgBindings[iParam], m_pData) == DBSTATUS_S_OK)
{
STATUS_BINDING(m_rgBindings[iParam], m_pData) = DBSTATUS_S_IGNORE;
// Now execute ICommand.
hr = m_pICommand->Execute(NULL, IID_NULL, &m_DbParamsAll, &cRowsAffected, NULL);
if (SUCCEEDED(hr))
m_pTable->AddRow();
TESTC_(hr, DB_E_ERRORSOCCURRED);
// Validate the status value is DBSTATUS_E_BADSTATUS
TESTC(STATUS_BINDING(m_rgBindings[iParam], m_pData) == DBSTATUS_E_BADSTATUS);
}
}
fResult = TRUE;
CLEANUP:
ReleaseDataForCommand();
return (fResult) ? TEST_PASS : TEST_FAIL;
}
// }} TCW_VAR_PROTOTYPE_END
// {{ TCW_VAR_PROTOTYPE(56)
//*-----------------------------------------------------------------------
// @mfunc OpenRowset on stored proc without params
//
// @rdesc TEST_PASS or TEST_FAIL
//
int TCCommandExecute_Rowset::Variation_56()
{
// Check for support. If provider supports DBPROP_OPENROWSETSUPPORT and
// DBPROPVAL_ORS_STOREDPROC, then OpenRowset will open a stored proc that
// has zero params.
ULONG_PTR ulOpenRowsetSupport = 0;
BOOL fResult = TRUE;
ULONG cParams, cParamSets = 1;
DBLENGTH cbRowSize = 0;
DBCOUNTITEM ulRowNum = 1;
DBBINDING * pBINDING=NULL;
DB_UPARAMS * pParamOrdinals=NULL;
DBPARAMBINDINFO * pPARAMBIND=NULL;
WCHAR * pwszCreateProcStmt=NULL;
WCHAR * pwszExecProcStmt=NULL;
WCHAR * pwszExecStmt=NULL;
WCHAR * pwszProcName=NULL;
BYTE * pData=NULL;
ParamStruct * pParamAll=NULL;
DBORDINAL cColumns = 0;
DB_LORDINAL * prgColumnsOrd = NULL;
BOOL fCanDerive = FALSE;
HRESULT hrSetProp = E_FAIL;
DBID dbidProc = DB_NULLID;
IOpenRowset * pIOpenRowset = NULL;
IRowset * pIRowset = NULL;
HRESULT hrOpen = E_FAIL;
HRESULT hrExpected = S_OK;
if (!(GetProperty(DBPROP_OPENROWSETSUPPORT,
DBPROPSET_DATASOURCEINFO,m_pIDBInitialize, &ulOpenRowsetSupport) &&
ulOpenRowsetSupport & DBPROPVAL_ORS_STOREDPROC))
{
odtLog << L"Provider doesn't support OpenRowset on a stored proc.\n";
hrExpected = DB_E_NOTABLE;
}
if (!m_fProcedureSupport)
{
odtLog << "Procedures not supported.\n";
return TEST_SKIPPED;
}
// Oracle cannot return a rowset from within a stored proc at this time.
if (g_bOracle)
{
odtLog << L"Oracle cannot return a rowset from a stored proc.\n";
return TEST_SKIPPED;
}
// Create the syntax and binding for a stored proc with input parameters
ABORT_COMPARE(CreateProcBindings(
T_EXEC_PROC_SELECT_NO_PARM, // [IN] Proc type, regular proc or function (has return value)
TRUE, // [IN] If TRUE then we add parameter names to the rgParamBindInfo
cParamSets, // [IN] Number of sets of parameters to be created
DBTYPE_I2, // [IN] Return parameter type
ulRowNum, // [IN] Row number in table to select, insert, or update
&cParams, // [OUT] Count of params created
&cbRowSize, // [OUT] Count of bytes for a single row of parameters
&pBINDING, // [OUT] Binding array for CreateAccessor
&pParamOrdinals,
&pPARAMBIND, // [OUT] rgParamBindInfo for SetParameterInfo
&pwszCreateProcStmt, // [OUT] SQL stmt to create the stored proc
&pwszExecProcStmt, // [OUT] SQL stmt to execute the stored proc
&pwszExecStmt, // [OUT] SQL stmt to execute without stored proc
&pwszProcName, // [OUT] Name of procedure
&pData, // [OUT] Pointer to data for the parameters
&pParamAll,
&cColumns,
&prgColumnsOrd
), TRUE);
// Some providers can't retrieve BLOB data without this property or IRowsetLocate on. However, other providers
// can't retrieve a row object with ACCESSORDER RANDOM. So we won't set it when retrieving row object.
if (m_iidExec == IID_IRowset && SupportedProperty(DBPROP_ACCESSORDER, DBPROPSET_ROWSET, m_pThisTestModule->m_pIUnknown,SESSION_INTERFACE))
hrSetProp = SetRowsetProperty(m_pICommand, DBPROPSET_ROWSET, DBPROP_ACCESSORDER, (LONG_PTR)DBPROPVAL_AO_RANDOM);
// Set up to execute the stored proc
ABORT_CHECK(PrepareForExecute(pwszExecProcStmt, cParams, pParamOrdinals, pPARAMBIND,
&fCanDerive, pwszProcName, pwszCreateProcStmt), S_OK);
// Can't validate data at this time for row objects w/o ini file (if any extra row object columns)
if (m_iidExec == IID_IRow && g_bConfProv && !GetModInfo()->GetFileName())
odtLog << L"Can't verify row object data w/o ini file at this time. Data compasison skipped. \n";
else
{
FAIL_VAR(ValidateGetParameterInfo(cParams, cParamSets, ulRowNum, pParamOrdinals, pPARAMBIND,
pBINDING, cbRowSize, pParamAll, pData, ROWSET_MAYBE, cColumns, prgColumnsOrd, fCanDerive), S_OK);
}
// Now try to "open" the stored proc via OpenRowset
// Get an OpenRowset interface
TEST_COMPARE(VerifyInterface(m_pThisTestModule->m_pIUnknown2,
IID_IOpenRowset, SESSION_INTERFACE, (IUnknown**)&pIOpenRowset),TRUE);
// Create a DBID from the proc name
dbidProc.eKind = DBKIND_NAME;
dbidProc.uName.pwszName = pwszProcName;
// Call OpenRowset on the stored proc
hrOpen = pIOpenRowset->OpenRowset(NULL, &dbidProc, NULL, m_iidExec,
0, NULL, (IUnknown **)&pIRowset);
TEST_CHECK(hrOpen, hrExpected);
// Validate the rowset returned
if (hrOpen == S_OK)
FAIL_VAR(VerifyObj(m_iidExec, pIRowset, ulRowNum, cColumns, prgColumnsOrd, TRUE), S_OK);
CLEANUP:
// If we set RANDOM REQUIRED above we need to set back OPTIONAL
if (hrSetProp == S_OK)
CHECK(SetRowsetProperty(m_pICommand, DBPROPSET_ROWSET, DBPROP_ACCESSORDER, (LONG_PTR)DBPROPVAL_AO_RANDOM, DBPROPOPTIONS_OPTIONAL), S_OK);
DropStoredProcedure(m_pICommandText, pwszProcName);
// Free the buffers we got from GetParameterInfo
PROVIDER_FREE(pBINDING);
PROVIDER_FREE(pParamOrdinals);
PROVIDER_FREE(pPARAMBIND);
PROVIDER_FREE(pwszCreateProcStmt);
PROVIDER_FREE(pwszExecProcStmt);
PROVIDER_FREE(pwszExecStmt);
PROVIDER_FREE(pwszProcName);
PROVIDER_FREE(pData);
::FreeParameterNames(cParams, pParamAll);
PROVIDER_FREE(pParamAll);
SAFE_RELEASE(pIOpenRowset);
return fResult ? TEST_PASS : TEST_FAIL;
}
// }} TCW_VAR_PROTOTYPE_END
// {{ TCW_VAR_PROTOTYPE(57)
//*-----------------------------------------------------------------------
// @mfunc Select with DBSTATUS_S_ISNULL input params
//
// @rdesc TEST_PASS or TEST_FAIL
//
int TCCommandExecute_Rowset::Variation_57()
{
BOOL fResult = TEST_PASS;
ULONG cParams, cParamSets = 1;
DBLENGTH cbRowSize = 0;
DBCOUNTITEM ulRowNum = 1;
DBBINDING * pBINDING=NULL;
DB_UPARAMS * pParamOrdinals=NULL;
DBPARAMBINDINFO * pPARAMBIND=NULL;
IRowset * pIRowset = NULL;
WCHAR * pwszCreateProcStmt=NULL;
WCHAR * pwszExecProcStmt=NULL;
WCHAR * pwszExecStmt=NULL;
WCHAR * pwszProcName=NULL;
BYTE * pData=NULL;
ParamStruct * pParamAll=NULL;
DBORDINAL cColumns = 0;
DB_LORDINAL * prgColumnsOrd = NULL;
DB_LORDINAL * prgParamColOrd = NULL;
BOOL fCanDerive = FALSE;
HRESULT hrSetProp = E_FAIL;
HRESULT hrGetNextRows = E_FAIL;
DBROWCOUNT cRowsAffected = 0;
DBCOUNTITEM cRowsObtained = 0;
DBCOUNTITEM cRowsInRowset = 0;
HROW * prghRows = NULL;
ULONG iParam, iRow;
ULONG ulNullRow = 0;
ULONG ulNullParam = 0;
ULONG ulNonNullRow = 0;
HRESULT hrExpect = S_OK;
// If we're asking for a row object then a select with NULL params will likely return DB_E_NOTFOUND
if (m_iidExec == IID_IRow)
hrExpect = DB_E_NOTFOUND;
// Temporarily set the CTable object not to create NULL data, otherwise columns containing
// NULL will not have a parameter.
m_pTable->SetNull(NONULLS);
// Create the syntax and binding for a stored proc and statement using input parameters
ABORT_COMPARE(CreateProcBindings(
T_EXEC_PROC_SELECT_IN, // [IN] Proc type, regular proc or function (has return value)
TRUE, // [IN] If TRUE then we add parameter names to the rgParamBindInfo
cParamSets, // [IN] Number of sets of parameters to be created
DBTYPE_I2, // [IN] Return parameter type
ulRowNum, // [IN] Row number in table to select, insert, or update
&cParams, // [OUT] Count of params created
&cbRowSize, // [OUT] Count of bytes for a single row of parameters
&pBINDING, // [OUT] Binding array for CreateAccessor
&pParamOrdinals,
&pPARAMBIND, // [OUT] rgParamBindInfo for SetParameterInfo
&pwszCreateProcStmt, // [OUT] SQL stmt to create the stored proc
&pwszExecProcStmt, // [OUT] SQL stmt to execute the stored proc
&pwszExecStmt, // [OUT] SQL stmt to execute without stored proc
&pwszProcName, // [OUT] Name of procedure
&pData, // [OUT] Pointer to data for the parameters
&pParamAll,
&cColumns,
&prgColumnsOrd,
&prgParamColOrd
), TRUE);
// Some providers can't retrieve BLOB data without this property or IRowsetLocate on
if (SupportedProperty(DBPROP_ACCESSORDER, DBPROPSET_ROWSET, m_pThisTestModule->m_pIUnknown,SESSION_INTERFACE))
hrSetProp = SetRowsetProperty(m_pICommand, DBPROPSET_ROWSET, DBPROP_ACCESSORDER, (LONG_PTR)DBPROPVAL_AO_RANDOM);
// Set up to execute the statement
ABORT_CHECK(PrepareForExecute(pwszExecStmt, cParams, pParamOrdinals, pPARAMBIND,
&fCanDerive, pwszProcName, NULL), S_OK);
// Set the parameter info if we need to
ABORT_CHECK(SetParameterInfoIfNeeded(cParams, pParamOrdinals, pPARAMBIND), S_OK);
// Find a row with a NULL in one of the parameter columns
m_pTable->SetNull(USENULLS);
for (iParam = 0; iParam < cParams && !ulNullRow; iParam++)
{
HRESULT hrNULL = E_FAIL;
ulNullRow = 0;
for (iRow = 1; iRow <= m_pTable->GetRowsOnCTable() && !ulNullRow; iRow++)
{
WCHAR wszData[MAXDATALEN] = L"";
// If this row/column data is NULL
hrNULL = m_pTable->MakeData(wszData, iRow, pParamAll[iParam].ulColIndex, PRIMARY, TRUE);
if (hrNULL == S_FALSE)
{
if (!ulNullRow)
{
ulNullRow = iRow;
ulNullParam = iParam;
}
}
}
}
// Find a row without a NULL in any of the parameter columns
for (iRow = 1; iRow <= m_pTable->GetRowsOnCTable() && !ulNonNullRow; iRow++)
{
HRESULT hrNULL = E_FAIL;
ulNonNullRow = iRow;
for (iParam = 0; iParam < cParams; iParam++)
{
WCHAR wszData[MAXDATALEN] = L"";
// If this row/column data is NULL
hrNULL = m_pTable->MakeData(wszData, iRow, pParamAll[iParam].ulColIndex, PRIMARY, TRUE);
if (hrNULL == S_FALSE)
{
ulNonNullRow = 0;
break;
}
}
}
if (!ulNullRow)
{
odtLog << L"No row containing a NULL we can use as a param.\n";
fResult = TEST_SKIPPED;
goto CLEANUP;
}
if (!ulNonNullRow)
{
odtLog << L"No row with all non-NULL data.\n";
fResult = TEST_SKIPPED;
goto CLEANUP;
}
// Now refill the parameter data buffer because we filled it with non-null data
ABORT_CHECK (FillInputBindings(m_pTable, DBACCESSOR_PARAMETERDATA, cParams,
pBINDING, &pData, ulNullRow, cParams, prgParamColOrd, PRIMARY), S_OK);
// Execute and make sure results are either 0 rows or valid row.
ABORT_CHECK(ExecuteAndVerify(cParams, cParamSets, pParamAll,ulNullRow, pBINDING,
cbRowSize, pData, ROWSET_ALWAYS, cColumns, prgColumnsOrd, VERIFY_USE_TABLE,
TRUE, NULL, hrExpect, NULL, NULL, &cRowsInRowset), S_OK);
// Typically we will get 0 rows in the rowset, but don't fail a provider if it returned
// one row with valid data.
if (cRowsInRowset)
FAIL_COMPARE(cRowsInRowset, 1);
// Now we have to validate we can get a rowset back properly when NOT asking for
// DBSTATUS_S_ISNULL.
// If set the row number a non-NULL row for this param
// Now refill the parameter data buffer because we filled it with non-null data
ABORT_CHECK (FillInputBindings(m_pTable, DBACCESSOR_PARAMETERDATA, cParams,
pBINDING, &pData, ulNonNullRow, cParams, prgParamColOrd, PRIMARY), S_OK);
// Check GetParameterInfo result and Execute result for this statement
cRowsInRowset = 1; // We expect only one row back, 0 or >1 is an error.
ABORT_CHECK(ExecuteAndVerify(cParams, cParamSets, pParamAll,ulNonNullRow, pBINDING,
cbRowSize, pData, ROWSET_ALWAYS, cColumns, prgColumnsOrd, VERIFY_USE_TABLE,
TRUE, NULL, S_OK, NULL, NULL, &cRowsInRowset), S_OK);
CLEANUP:
// Remove parameter info
CHECK(m_pICmdWParams->SetParameterInfo(0, NULL, NULL), S_OK);
// Make sure we set back to USENULLS in case of abort.
m_pTable->SetNull(USENULLS);
// If we set RANDOM REQUIRED above we need to set back OPTIONAL
if (hrSetProp == S_OK)
CHECK(SetRowsetProperty(m_pICommand, DBPROPSET_ROWSET, DBPROP_ACCESSORDER, (LONG_PTR)DBPROPVAL_AO_RANDOM, DBPROPOPTIONS_OPTIONAL), S_OK);
SAFE_RELEASE(pIRowset);
// Free the buffers we got from GetParameterInfo
PROVIDER_FREE(pBINDING);
PROVIDER_FREE(pParamOrdinals);
PROVIDER_FREE(pPARAMBIND);
PROVIDER_FREE(pwszCreateProcStmt);
PROVIDER_FREE(pwszExecProcStmt);
PROVIDER_FREE(pwszExecStmt);
PROVIDER_FREE(pwszProcName);
PROVIDER_FREE(pData);
::FreeParameterNames(cParams, pParamAll);
PROVIDER_FREE(pParamAll);
PROVIDER_FREE(prgColumnsOrd);
return fResult;
}
// }} TCW_VAR_PROTOTYPE_END
// {{ TCW_VAR_PROTOTYPE(58)
//*-----------------------------------------------------------------------
// @mfunc S_OK - Send less data than max for parameter
//
// @rdesc TEST_PASS or TEST_FAIL
//
int TCCommandExecute_Rowset::Variation_58()
{
BOOL fResult = FALSE;
DBROWCOUNT cRowsAffected = 0;
DBCOUNTITEM ulRowNum;
ULONG iBind;
ULONG cParamSets = 1;
ULONG cParams;
DBCOUNTITEM cRowsObtained = 100;
DBLENGTH cbRowSize = 0;
HROW * prghRows = NULL;
DBBINDING * pBINDING=NULL;
DB_UPARAMS * pParamOrdinals=NULL;
DBPARAMBINDINFO * pPARAMBIND=NULL;
WCHAR * pwszCreateProcStmt=NULL;
WCHAR * pwszExecProcStmt=NULL;
WCHAR * pwszExecStmt=NULL;
WCHAR * pwszProcName=NULL;
BYTE * pData=NULL;
ParamStruct * pParamAll=NULL;
DBORDINAL cColumns = 0;
DB_LORDINAL * prgColumnsOrd = NULL;
BOOL fCanDerive = FALSE;
HRESULT hrSetProp = E_FAIL;
HACCESSOR hParamAccessor = DB_INVALID_HACCESSOR;
DBPARAMS ExecDbParams;
IRowset * pIRowset = NULL;
IAccessor * pIAccessor = NULL;
HACCESSOR hAccessor = DB_NULL_HACCESSOR;
DBBINDING * pBindings = NULL;
DBCOUNTITEM cBindings = 0;
DBLENGTH cbRowSizeRowset = 0;
BYTE * pDataRowset = NULL;
DBCOLUMNINFO * pColInfo = NULL;
LPWSTR pStringsBuffer = NULL;
if (!m_fProcedureSupport)
{
odtLog << "Procedures not supported.\n";
return TEST_SKIPPED;
}
// Oracle cannot return a rowset from within a stored proc at this time.
if (g_bOracle)
{
odtLog << L"Oracle cannot return a rowset from a stored proc.\n";
return TEST_SKIPPED;
}
if (m_iidExec == IID_IRow)
{
odtLog << L"Can't run this var for row objects at this time because accessors are not supported. \n";
return TEST_SKIPPED;
}
// Remove any old parameter info
TEST_CHECK(m_pICmdWParams->SetParameterInfo(0, NULL, NULL), S_OK);
// Set command text
TEST_CHECK (m_pICommandText->SetCommandText(DBGUID_DBSQL , m_pwszSqlInsertAllWithParams), S_OK);
// Call ICommandPrepare.
TEST_CHECK (m_pICommandPrepare->Prepare(1), S_OK);
//This sets it to one row of data.
MakeDataForCommand(ulRowNum = m_pTable->GetNextRowNumber());
// Use local names
pData = m_pData;
pBINDING = &m_rgBindings[0];
cParams = (ULONG)m_cBindings;
// We created normal data above, now we have to change it
for (iBind = 0; iBind < cParams; iBind++)
{
DBBINDING * pBind = &pBINDING[iBind];
LPBYTE pCol = (LPBYTE)&VALUE_BINDING(*pBind, pData);
DBTYPE wType = pBind->wType;
DBLENGTH cbMaxLen = pBind->cbMaxLen;
DBLENGTH ulDataSize = LENGTH_BINDING(*pBind, pData);
DBSTATUS sStatus = STATUS_BINDING(*pBind, pData);
// Don't try to fix up NULL status values
if (sStatus != DBSTATUS_S_OK)
continue;
// Since FillInputBindings uses bogus length values for fixed length
// types we won't be able to compare the lengths unless we fix them up.
if (IsFixedLength(wType))
ulDataSize = GetDBTypeSize(wType);
if (DBTYPE_WSTR == wType)
ulDataSize = sizeof(WCHAR);
else if (wType == DBTYPE_STR ||
wType == DBTYPE_BYTES)
{
// Assume 1 byte
ulDataSize = 1;
// Can't split a lead byte
if (DBTYPE_STR == wType && IsDBCSLeadByte(*pCol))
ulDataSize++;
}
// Now adjust the data
LENGTH_BINDING(*pBind, pData) = ulDataSize;
}
// Reset vars
pData = NULL;
pBINDING = NULL;
cParams = 0;
cbRowSize = 0;
// Now execute ICommand.
TEST_CHECK(m_pICommand->Execute(NULL, IID_NULL,
&m_DbParamsAll, &cRowsAffected, NULL), S_OK);
// Now we have a row of one char/byte values, let's see if we can retrieve it
// using a stored proc
// Create the syntax and binding for a stored proc with input parameters
ABORT_COMPARE(CreateProcBindings(
T_EXEC_PROC_SELECT_IN, // [IN] Proc type, regular proc or function (has return value)
TRUE, // [IN] If TRUE then we add parameter names to the rgParamBindInfo
cParamSets, // [IN] Number of sets of parameters to be created
DBTYPE_I2, // [IN] Return parameter type
ulRowNum, // [IN] Row number in table to select, insert, or update
&cParams, // [OUT] Count of params created
&cbRowSize, // [OUT] Count of bytes for a single row of parameters
&pBINDING, // [OUT] Binding array for CreateAccessor
&pParamOrdinals,
&pPARAMBIND, // [OUT] rgParamBindInfo for SetParameterInfo
&pwszCreateProcStmt, // [OUT] SQL stmt to create the stored proc
&pwszExecProcStmt, // [OUT] SQL stmt to execute the stored proc
&pwszExecStmt, // [OUT] SQL stmt to execute without stored proc
&pwszProcName, // [OUT] Name of procedure
&pData, // [OUT] Pointer to data for the parameters
&pParamAll,
&cColumns,
&prgColumnsOrd
), TRUE);
// Some providers can't retrieve BLOB data without this property or IRowsetLocate on
if (SupportedProperty(DBPROP_ACCESSORDER, DBPROPSET_ROWSET, m_pThisTestModule->m_pIUnknown,SESSION_INTERFACE))
hrSetProp = SetRowsetProperty(m_pICommand, DBPROPSET_ROWSET, DBPROP_ACCESSORDER, (LONG_PTR)DBPROPVAL_AO_RANDOM);
// Some providers can't retrieve BLOB data without this property or IRowsetLocate on.
if (SupportedProperty(DBPROP_IRowsetChange, DBPROPSET_ROWSET, m_pThisTestModule->m_pIUnknown,SESSION_INTERFACE))
CHECK(SetRowsetProperty(m_pICommand, DBPROPSET_ROWSET, DBPROP_IRowsetChange, TRUE), S_OK);
if (SupportedProperty(DBPROP_UPDATABILITY, DBPROPSET_ROWSET, m_pThisTestModule->m_pIUnknown,SESSION_INTERFACE))
CHECK(SetRowsetProperty(m_pICommand, DBPROPSET_ROWSET, DBPROP_UPDATABILITY, (LONG_PTR)DBPROPVAL_UP_DELETE), S_OK);
// Set up to execute the stored proc
ABORT_CHECK(PrepareForExecute(pwszExecProcStmt, cParams, pParamOrdinals, pPARAMBIND,
&fCanDerive, pwszProcName, pwszCreateProcStmt), S_OK);
// Go through the params and set to the same size we did previously
for (iBind = 0; iBind < cParams; iBind++)
{
DBBINDING * pBind = &pBINDING[iBind];
LPBYTE pCol = (LPBYTE)&VALUE_BINDING(*pBind, pData);
DBTYPE wType = pBind->wType;
DBLENGTH ulDataSize = LENGTH_BINDING(*pBind, pData);
DBSTATUS sStatus = STATUS_BINDING(*pBind, pData);
// Don't try to fix up NULL status values
if (sStatus != DBSTATUS_S_OK)
continue;
if (DBTYPE_WSTR == wType)
ulDataSize = sizeof(WCHAR);
else if (wType == DBTYPE_STR ||
wType == DBTYPE_BYTES)
{
// Assume 1 byte
ulDataSize = 1;
// Can't split a lead byte
if (DBTYPE_STR == wType && IsDBCSLeadByte(*pCol))
ulDataSize++;
}
// Now adjust the data
LENGTH_BINDING(*pBind, pData) = ulDataSize;
}
// Set up call to execute stored proc
// Null out DBPARAMS
memset(&ExecDbParams, 0, sizeof(DBPARAMS));
ABORT_CHECK (m_pCmdIAccessor->CreateAccessor( DBACCESSOR_PARAMETERDATA,
cParams, pBINDING, cbRowSize, &hParamAccessor, NULL), S_OK);
// Set up our param accessor
ExecDbParams.hAccessor = hParamAccessor;
ExecDbParams.cParamSets = cParamSets;
ExecDbParams.pData = pData;
// Execute the stored proc, should get one row back
ABORT_CHECK(m_pICommand->Execute(NULL, m_iidExec, &ExecDbParams,
&cRowsAffected, (IUnknown **)&pIRowset), S_OK);
// Make sure we can get an accessor interface
// TESTBUG: Row object does not support IAccessor interface. Needs work.
TESTC(VerifyInterface(pIRowset, IID_IAccessor,
ROWSET_INTERFACE, (IUnknown **)&pIAccessor));
TESTC_(GetAccessorAndBindings(pIAccessor, DBACCESSOR_ROWDATA,
&hAccessor, &pBindings, &cBindings, &cbRowSizeRowset,
DBPART_LENGTH | DBPART_STATUS | DBPART_VALUE,
UPDATEABLE_COLS_BOUND, FORWARD, NO_COLS_BY_REF,
&pColInfo, NULL, &pStringsBuffer, DBTYPE_EMPTY, 0, NULL, NULL,
NO_COLS_OWNED_BY_PROV, DBPARAMIO_NOTPARAM, BLOB_LONG), S_OK);
SAFE_ALLOC(pDataRowset, BYTE, cbRowSizeRowset);
// Now validate the data
TESTC_(pIRowset->GetNextRows(NULL, 0, 100, &cRowsObtained, &prghRows), DB_S_ENDOFROWSET);
TESTC(cRowsObtained == 1);
TESTC(prghRows != NULL);
TESTC_(pIRowset->GetData(*prghRows, hAccessor, pDataRowset), S_OK);
TESTC(cBindings == m_cBindings);
// STR, WSTR, and BTYPES columns that return DBCOLUMNFLAGS_ISFIXEDLENGTH will be
// padded with spaces or NULLs out to the size of the column. This will cause
// comparebuffer to fail unless we fix this up also.
for (iBind = 0; iBind < cBindings; iBind++)
{
DBTYPE wType = m_rgBindings[iBind].wType;
DBSTATUS sStatus = STATUS_BINDING(m_rgBindings[iBind], m_pData);
// We have to get the ordinal from pBindings because that matches the rowset
// returned and therefore the colinfo.
DBCOLUMNFLAGS dwFlags = pColInfo[pBindings[iBind].iOrdinal - !!pColInfo[0].iOrdinal].dwFlags;
ASSERT(pBindings[iBind].iOrdinal == pColInfo[pBindings[iBind].iOrdinal - !!pColInfo[0].iOrdinal].iOrdinal);
if ((IS_BASE_TYPE(wType, DBTYPE_STR) ||
IS_BASE_TYPE(wType, DBTYPE_WSTR) ||
IS_BASE_TYPE(wType, DBTYPE_BYTES)) &&
dwFlags & DBCOLUMNFLAGS_ISFIXEDLENGTH &&
sStatus == DBSTATUS_S_OK)
{
// We need to fix up the size of our inserted data buffer
DBLENGTH ulLength = LENGTH_BINDING(m_rgBindings[iBind], m_pData);
DBLENGTH ulNewLength;
BYTE * pFill = (BYTE *)&VALUE_BINDING(m_rgBindings[iBind], m_pData)+ulLength;
// Fill with spaces, wide spaces, or 0's
if (IS_BASE_TYPE(wType, DBTYPE_STR))
{
ulNewLength = m_rgBindings[iBind].cbMaxLen-sizeof(CHAR);
_strnset((LPSTR)pFill, ' ', (size_t)(ulNewLength-ulLength));
}
else if (IS_BASE_TYPE(wType, DBTYPE_WSTR))
{
ulNewLength = m_rgBindings[iBind].cbMaxLen-sizeof(WCHAR);
_wcsnset((LPWSTR)pFill, L' ', (size_t)((ulNewLength-ulLength)/sizeof(WCHAR)));
}
else if (IS_BASE_TYPE(wType, DBTYPE_BYTES))
{
ulNewLength = m_rgBindings[iBind].cbMaxLen;
memset(pFill, 0, (size_t)(ulNewLength-ulLength));
}
LENGTH_BINDING(m_rgBindings[iBind], m_pData) = ulNewLength;
}
}
TESTC(CompareBuffer(pDataRowset, m_pData, cBindings, pBindings, NULL, TRUE, FALSE, COMPARE_ONLY,
FALSE, m_cBindings, m_rgBindings));
fResult = TRUE;
CLEANUP:
ReleaseDataForCommand();
// We have a goofy row in the table which we should remove. Use the row handle. Note if
// the provider has a bug where we don't actually get the row handle then we may fail to
// remove the row.
if (prghRows)
{
IRowsetChange * pIRowsetChange = NULL;
COMPARE(VerifyInterface (pIRowset, IID_IRowsetChange, ROWSET_INTERFACE,
(IUnknown **)&pIRowsetChange), TRUE);
if (pIRowsetChange)
CHECK(pIRowsetChange->DeleteRows(NULL, 1, prghRows, NULL), S_OK);
SAFE_RELEASE(pIRowsetChange);
}
SAFE_RELEASE_ACCESSOR(pIAccessor, hAccessor);
SAFE_RELEASE(pIAccessor);
SAFE_FREE(pBindings);
SAFE_FREE(pColInfo);
SAFE_FREE(pStringsBuffer);
SAFE_FREE(pDataRowset);
if (pIRowset && prghRows)
CHECK(pIRowset->ReleaseRows(1, prghRows, NULL, NULL, NULL), S_OK);
SAFE_FREE(prghRows);
SAFE_RELEASE(pIRowset);
DropStoredProcedure(m_pICommandText, pwszProcName);
// Set props back to default
CHECK(SetRowsetPropertyDefault(DBPROP_UPDATABILITY), S_OK);
CHECK(SetRowsetPropertyDefault(DBPROP_IRowsetChange), S_OK);
CHECK(SetRowsetPropertyDefault(DBPROP_ACCESSORDER), S_OK);
// Free the buffers we got from GetParameterInfo
PROVIDER_FREE(pBINDING);
PROVIDER_FREE(pParamOrdinals);
PROVIDER_FREE(pPARAMBIND);
PROVIDER_FREE(pwszCreateProcStmt);
PROVIDER_FREE(pwszExecProcStmt);
PROVIDER_FREE(pwszExecStmt);
PROVIDER_FREE(pwszProcName);
PROVIDER_FREE(pData);
::FreeParameterNames(cParams, pParamAll);
PROVIDER_FREE(pParamAll);
return fResult ? TEST_PASS : TEST_FAIL;
}
// }} TCW_VAR_PROTOTYPE_END
// {{ TCW_VAR_PROTOTYPE(59)
//*-----------------------------------------------------------------------
// @mfunc S_OK - Insert proc with CANHOLDROWS OPTIONAL
//
// @rdesc TEST_PASS or TEST_FAIL
//
int TCCommandExecute_Rowset::Variation_59()
{
BOOL fResult = TEST_FAIL;
ULONG cParams, cParamSets = 1;
DBLENGTH cbRowSize = 0;
DBCOUNTITEM ulRowNum = 1;
DBBINDING * pBINDING=NULL;
DB_UPARAMS * pParamOrdinals=NULL;
DBPARAMBINDINFO * pPARAMBIND=NULL;
WCHAR * pwszCreateProcStmt=NULL;
WCHAR * pwszExecProcStmt=NULL;
WCHAR * pwszExecStmt=NULL;
WCHAR * pwszProcName=NULL;
BYTE * pData=NULL;
ParamStruct * pParamAll=NULL;
DBORDINAL cColumns = 0;
DB_LORDINAL * prgColumnsOrd = NULL;
BOOL fCanDerive = FALSE;
HRESULT hrSetProp = E_FAIL;
IRowset * pIRowset = NULL;
DBCOUNTITEM cRows = 1; // We expect one row to be retrieved, the one we insert.
DBORDINAL cCols = 0;
DB_LORDINAL * pCols = NULL;
if (!m_fProcedureSupport)
{
odtLog << "Procedures not supported.\n";
return TEST_SKIPPED;
}
if (m_iidExec == IID_IRow && g_bConfProv && !GetModInfo()->GetFileName())
{
odtLog << L"Can't verify row object data w/o ini file at this time. \n";
return TEST_SKIPPED;
}
// Get a valid row we can insert
ulRowNum = m_pTable->GetNextRowNumber();
// Create the syntax and binding for a stored proc with input parameters
ABORT_COMPARE(CreateProcBindings(
T_EXEC_PROC_INSERT_INPUT,// [IN] Proc type, regular proc or function (has return value)
TRUE, // [IN] If TRUE then we add parameter names to the rgParamBindInfo
cParamSets, // [IN] Number of sets of parameters to be created
DBTYPE_I2, // [IN] Return parameter type
ulRowNum, // [IN] Row number in table to select, insert, or update
&cParams, // [OUT] Count of params created
&cbRowSize, // [OUT] Count of bytes for a single row of parameters
&pBINDING, // [OUT] Binding array for CreateAccessor
&pParamOrdinals,
&pPARAMBIND, // [OUT] rgParamBindInfo for SetParameterInfo
&pwszCreateProcStmt, // [OUT] SQL stmt to create the stored proc
&pwszExecProcStmt, // [OUT] SQL stmt to execute the stored proc
&pwszExecStmt, // [OUT] SQL stmt to execute without stored proc
&pwszProcName, // [OUT] Name of procedure
&pData, // [OUT] Pointer to data for the parameters
&pParamAll,
&cColumns,
&prgColumnsOrd
), TRUE);
// Some providers can't retrieve BLOB data without this property or IRowsetLocate on
if (m_iidExec != IID_IRow || !g_bLuxor &&
SupportedProperty(DBPROP_ACCESSORDER, DBPROPSET_ROWSET, m_pThisTestModule->m_pIUnknown,SESSION_INTERFACE))
hrSetProp = SetRowsetProperty(m_pICommand, DBPROPSET_ROWSET, DBPROP_ACCESSORDER, (LONG_PTR)DBPROPVAL_AO_RANDOM);
// Set CANHOLDROWS
TESTC_(SetRowsetProperty(m_pICommand, DBPROPSET_ROWSET, DBPROP_CANHOLDROWS, TRUE, DBPROPOPTIONS_OPTIONAL), S_OK);
// Set up to execute the stored proc
ABORT_CHECK(PrepareForExecute(pwszExecProcStmt, cParams, pParamOrdinals, pPARAMBIND,
&fCanDerive, pwszProcName, pwszCreateProcStmt), S_OK);
// Check GetParameterInfo and execute
TESTC_(ExecuteAndVerify(cParams, cParamSets, pParamAll, ulRowNum, pBINDING, cbRowSize,
pData, ROWSET_NONE, cColumns, prgColumnsOrd, VERIFY_USE_TABLE, TRUE, NULL, S_OK), S_OK);
// Tell the table object there's one more row. Note we really need to
// split up the ExecuteAndVerify above so we know Execute itself succeeded.
m_pTable->AddRow();
// Set CANHOLDROWS to default for verification in case it can't be supported.
CHECK(SetRowsetPropertyDefault(DBPROP_CANHOLDROWS), S_OK);
// See if the row was properly inserted
// Verify If we have inserted the row properly.
TESTC(cRow.FindRow(ulRowNum, m_pTable, m_pICommand, &pIRowset, NULL,
&cCols, &pCols, FALSE));
// FindRow merely finds the appropriate row. We need to validate the inserted data for the default bindings.
TESTC_(VerifyObj(m_iidExec, pIRowset, ulRowNum, cCols, pCols, FALSE, FALSE, m_pTable, &cRows), S_OK);
fResult = TEST_PASS;
CLEANUP:
// Free the rowset
SAFE_RELEASE(pIRowset);
// Set CANHOLDROWS to default
CHECK(SetRowsetPropertyDefault(DBPROP_CANHOLDROWS), S_OK);
// Set RANDOM back to default
CHECK(SetRowsetPropertyDefault(DBPROP_ACCESSORDER), S_OK);
DropStoredProcedure(m_pICommandText, pwszProcName);
SAFE_FREE(pCols);
// Free the buffers we got from GetParameterInfo
PROVIDER_FREE(pBINDING);
PROVIDER_FREE(pParamOrdinals);
PROVIDER_FREE(pPARAMBIND);
PROVIDER_FREE(pwszCreateProcStmt);
PROVIDER_FREE(pwszExecProcStmt);
PROVIDER_FREE(pwszExecStmt);
PROVIDER_FREE(pwszProcName);
PROVIDER_FREE(pData);
::FreeParameterNames(cParams, pParamAll);
PROVIDER_FREE(pParamAll);
return fResult;
}
// }} TCW_VAR_PROTOTYPE_END
// {{ TCW_VAR_PROTOTYPE(60)
//*-----------------------------------------------------------------------
// @mfunc Insert with BSTR bindings to strings to stored proc
//
// @rdesc TEST_PASS or TEST_FAIL
//
int TCCommandExecute_Rowset::Variation_60()
{
BOOL fResult = TEST_FAIL;
ULONG cParams, cParamSets = 1;
DBLENGTH cbRowSize = 0;
DBCOUNTITEM ulRowNum = 1;
DBBINDING * pBINDING=NULL;
DB_UPARAMS * pParamOrdinals=NULL;
DBPARAMBINDINFO * pPARAMBIND=NULL;
WCHAR * pwszCreateProcStmt=NULL;
WCHAR * pwszExecProcStmt=NULL;
WCHAR * pwszExecStmt=NULL;
WCHAR * pwszProcName=NULL;
BYTE * pData=NULL;
ParamStruct * pParamAll=NULL;
DBORDINAL cColumns = 0;
DB_LORDINAL * prgColumnsOrd = NULL;
BOOL fCanDerive = FALSE;
HRESULT hrSetProp = E_FAIL;
IRowset * pIRowset = NULL;
DBCOUNTITEM cRows = 1; // We expect one row to be retrieved, the one we insert.
BOOL fConvertSTR = FALSE;
BOOL fConvertWSTR = FALSE;
HRESULT hrConv = E_FAIL;
ULONG iBind;
IConvertType * pIConvertType = NULL;
DB_LORDINAL * prgParamColOrd = NULL;
DBORDINAL cCols = 0;
DB_LORDINAL * pCols = NULL;
if (!m_fProcedureSupport)
{
odtLog << "Procedures not supported.\n";
return TEST_SKIPPED;
}
// Get a valid row we can insert
ulRowNum = m_pTable->GetNextRowNumber();
// Create the syntax and binding for a stored proc with input parameters
ABORT_COMPARE(CreateProcBindings(
T_EXEC_PROC_INSERT_INPUT,// [IN] Proc type, regular proc or function (has return value)
TRUE, // [IN] If TRUE then we add parameter names to the rgParamBindInfo
cParamSets, // [IN] Number of sets of parameters to be created
DBTYPE_I2, // [IN] Return parameter type
ulRowNum, // [IN] Row number in table to select, insert, or update
&cParams, // [OUT] Count of params created
&cbRowSize, // [OUT] Count of bytes for a single row of parameters
&pBINDING, // [OUT] Binding array for CreateAccessor
&pParamOrdinals,
&pPARAMBIND, // [OUT] rgParamBindInfo for SetParameterInfo
&pwszCreateProcStmt, // [OUT] SQL stmt to create the stored proc
&pwszExecProcStmt, // [OUT] SQL stmt to execute the stored proc
&pwszExecStmt, // [OUT] SQL stmt to execute without stored proc
&pwszProcName, // [OUT] Name of procedure
&pData, // [OUT] Pointer to data for the parameters
&pParamAll,
&cColumns,
&prgColumnsOrd,
&prgParamColOrd
), TRUE);
// Some providers can't retrieve BLOB data without this property or IRowsetLocate on
if (SupportedProperty(DBPROP_ACCESSORDER, DBPROPSET_ROWSET, m_pThisTestModule->m_pIUnknown,SESSION_INTERFACE))
hrSetProp = SetRowsetProperty(m_pICommand, DBPROPSET_ROWSET, DBPROP_ACCESSORDER, (LONG_PTR)DBPROPVAL_AO_RANDOM);
TESTC_(PrepareForExecute(pwszExecProcStmt, cParams, pParamOrdinals, pPARAMBIND,
&fCanDerive, pwszProcName, pwszCreateProcStmt), S_OK);
if (!fCanDerive)
TESTC_(m_pICmdWParams->SetParameterInfo(cParams, pParamOrdinals, pPARAMBIND), S_OK);
// Make sure the provider claims it can support conversion from BSTR -> STR
// or BSTR -> WSTR.
TESTC(VerifyInterface(m_pICommand, IID_IConvertType,
COMMAND_INTERFACE,(IUnknown **)&pIConvertType));
hrConv = pIConvertType->CanConvert(DBTYPE_BSTR, DBTYPE_STR, DBCONVERTFLAGS_PARAMETER);
if (S_OK == hrConv)
fConvertSTR = TRUE;
else
CHECK(hrConv, S_FALSE);
hrConv = pIConvertType->CanConvert(DBTYPE_BSTR, DBTYPE_WSTR, DBCONVERTFLAGS_PARAMETER);
if (S_OK == hrConv)
fConvertWSTR = TRUE;
else
CHECK(hrConv, S_FALSE);
if (!fConvertSTR && !fConvertWSTR)
{
odtLog << L"Provider doesn't support conversions from BSTR to string types.\n";
fResult = TEST_SKIPPED;
goto CLEANUP;
}
// Release the previous parameter set
ReleaseInputBindingsMemory(cParams, pBINDING, pData, TRUE);
pData = NULL; // Freed above.
// Change str and wstr types to bstr
for (iBind=0; iBind < cParams; iBind++)
{
if (pBINDING[iBind].wType == DBTYPE_STR && fConvertSTR)
pBINDING[iBind].wType = DBTYPE_BSTR;
if (pBINDING[iBind].wType == DBTYPE_WSTR && fConvertWSTR)
pBINDING[iBind].wType = DBTYPE_BSTR;
}
// Repack the obValue, obLength, and obStatus based on the new bindings
Repack(cParams, pBINDING, &cbRowSize);
// Create a new pData based on the new bindings
TESTC_(FillInputBindings(m_pTable, DBACCESSOR_PARAMETERDATA, cParams,
pBINDING, &pData, ulRowNum, cParams, prgParamColOrd, PRIMARY), S_OK);
// Execute
TESTC_(ExecuteAndVerify(cParams, cParamSets, pParamAll, ulRowNum, pBINDING, cbRowSize,
pData, ROWSET_NONE, cColumns, prgColumnsOrd, VERIFY_USE_TABLE, TRUE, NULL, S_OK), S_OK);
// Tell the table object there's one more row. Note we really need to
// split up the ExecuteAndVerify above so we know Execute itself succeeded.
m_pTable->AddRow();
// See if the row was properly inserted
// Verify If we have inserted the row properly.
TESTC(cRow.FindRow(ulRowNum, m_pTable, m_pICommand, &pIRowset, NULL,
&cCols, &pCols, FALSE));
// FindRow merely finds the appropriate row. We need to validate the inserted data for the default bindings.
TESTC_(VerifyObj(m_iidExec, pIRowset, ulRowNum, cCols, pCols, FALSE, FALSE, m_pTable, &cRows), S_OK);
fResult = TEST_PASS;
CLEANUP:
// Free the rowset
SAFE_RELEASE(pIRowset);
SAFE_RELEASE(pIConvertType);
// Set RANDOM back to default
CHECK(SetRowsetPropertyDefault(DBPROP_ACCESSORDER), S_OK);
DropStoredProcedure(m_pICommandText, pwszProcName);
SAFE_FREE(pCols);
// Free the buffers we got from GetParameterInfo
PROVIDER_FREE(pBINDING);
PROVIDER_FREE(pParamOrdinals);
PROVIDER_FREE(pPARAMBIND);
PROVIDER_FREE(pwszCreateProcStmt);
PROVIDER_FREE(pwszExecProcStmt);
PROVIDER_FREE(pwszExecStmt);
PROVIDER_FREE(pwszProcName);
PROVIDER_FREE(pData);
::FreeParameterNames(cParams, pParamAll);
PROVIDER_FREE(pParamAll);
return fResult;
}
// }} TCW_VAR_PROTOTYPE_END
// {{ TCW_VARIATION_DELETED *************** DELETED *************** DELETED *************** DELETED
//*-----------------------------------------------------------------------
// @mfunc Test variation - remove
//
// @rdesc TEST_PASS or TEST_FAIL
//
/*
{
BOOL fResult = TEST_FAIL;
ULONG cParams, cParamSets = 1, cbRowSize;
DBCOUNTITEM ulRowNum = 1;
DBBINDING * pBINDING=NULL;
DB_UPARAMS * pParamOrdinals=NULL;
DBPARAMBINDINFO * pPARAMBIND=NULL;
WCHAR * pwszCreateProcStmt=NULL;
WCHAR * pwszExecProcStmt=NULL;
WCHAR * pwszExecStmt=NULL;
WCHAR * pwszProcName=NULL;
BYTE * pData=NULL;
ParamStruct * pParamAll=NULL;
ULONG cColumns = 0;
DB_LORDINAL * prgColumnsOrd = NULL;
BOOL fCanDerive = FALSE;
HRESULT hrSetProp = E_FAIL;
IRowset * pIRowset = NULL;
ULONG cRows = 1; // We expect one row to be retrieved, the one we insert.
BOOL fConvertSTR = FALSE;
BOOL fConvertWSTR = FALSE;
HRESULT hrConv = E_FAIL;
ULONG iBind;
IConvertType * pIConvertType = NULL;
DB_LORDINAL * prgParamColOrd = NULL;
WCHAR wszProcCall[] = L"{call sp_tables(?)}";
if (!m_fProcedureSupport)
{
odtLog << "Procedures not supported.\n";
return TEST_SKIPPED;
}
// Get a valid row we can insert
ulRowNum = m_pTable->GetNextRowNumber();
// Create the syntax and binding for a stored proc with input parameters
ABORT_COMPARE(CreateProcBindings(
T_EXEC_PROC_INSERT_INPUT,// [IN] Proc type, regular proc or function (has return value)
TRUE, // [IN] If TRUE then we add parameter names to the rgParamBindInfo
cParamSets, // [IN] Number of sets of parameters to be created
DBTYPE_I2, // [IN] Return parameter type
ulRowNum, // [IN] Row number in table to select, insert, or update
&cParams, // [OUT] Count of params created
&cbRowSize, // [OUT] Count of bytes for a single row of parameters
&pBINDING, // [OUT] Binding array for CreateAccessor
&pParamOrdinals,
&pPARAMBIND, // [OUT] rgParamBindInfo for SetParameterInfo
&pwszCreateProcStmt, // [OUT] SQL stmt to create the stored proc
&pwszExecProcStmt, // [OUT] SQL stmt to execute the stored proc
&pwszExecStmt, // [OUT] SQL stmt to execute without stored proc
&pwszProcName, // [OUT] Name of procedure
&pData, // [OUT] Pointer to data for the parameters
&pParamAll,
&cColumns,
&prgColumnsOrd,
&prgParamColOrd
), TRUE);
// Some providers can't retrieve BLOB data without this property or IRowsetLocate on
if (SupportedProperty(DBPROP_ACCESSORDER, DBPROPSET_ROWSET, m_pThisTestModule->m_pIUnknown,SESSION_INTERFACE))
hrSetProp = SetRowsetProperty(m_pICommand, DBPROPSET_ROWSET, DBPROP_ACCESSORDER, (LONG_PTR)DBPROPVAL_AO_RANDOM);
// Set up to execute the stored proc
if (pwszCreateProcStmt)
// Create the stored procedure
TESTC_(CreateStoredProc(m_pICommandText, pwszProcName, pwszCreateProcStmt, FALSE), S_OK);
pwszExecProcStmt = (LPWSTR)wszProcCall;
// Set the command text to execute the stored proc
TESTC_(m_pICommandText->SetCommandText(DBGUID_DBSQL, pwszExecProcStmt), S_OK);
// Make sure the provider claims it can support conversion from BSTR -> STR
// or BSTR -> WSTR.
TESTC(VerifyInterface(m_pICommand, IID_IConvertType,
COMMAND_INTERFACE,(IUnknown **)&pIConvertType));
hrConv = pIConvertType->CanConvert(DBTYPE_BSTR, DBTYPE_STR, DBCONVERTFLAGS_PARAMETER);
if (S_OK == hrConv)
fConvertSTR = TRUE;
else
CHECK(hrConv, S_FALSE);
hrConv = pIConvertType->CanConvert(DBTYPE_BSTR, DBTYPE_WSTR, DBCONVERTFLAGS_PARAMETER);
if (S_OK == hrConv)
fConvertWSTR = TRUE;
else
CHECK(hrConv, S_FALSE);
if (!fConvertSTR && !fConvertWSTR)
{
odtLog << L"Provider doesn't support conversions from BSTR to string types.\n";
fResult = TEST_SKIPPED;
goto CLEANUP;
}
// Release the previous parameter set
ReleaseInputBindingsMemory(cParams, pBINDING, pData, TRUE);
pData = NULL; // Freed above.
// Change str and wstr types to bstr
for (iBind=0; iBind < cParams; iBind++)
{
if (pBINDING[iBind].wType == DBTYPE_WSTR && fConvertWSTR)
pBINDING[iBind].wType = DBTYPE_BSTR;
break;
}
cParams = 1;
// Repack the obValue, obLength, and obStatus based on the new bindings
Repack(cParams, &pBINDING[iBind], &cbRowSize);
// Create a new pData based on the new bindings
TESTC_(FillInputBindings(m_pTable, DBACCESSOR_PARAMETERDATA, cParams,
&pBINDING[iBind], &pData, ulRowNum, cParams, (LONG *)&prgParamColOrd[iBind], PRIMARY), S_OK);
// Now copy in a valid table name
wcscpy((LPWSTR)&VALUE_BINDING(pBINDING[iBind], pData), L"sysusers");
// Execute
TESTC_(ExecuteAndVerify(cParams, cParamSets, &pParamAll[iBind], ulRowNum, &pBINDING[iBind], cbRowSize,
pData, ROWSET_NONE, 1, &prgColumnsOrd[iBind], VERIFY_USE_TABLE, TRUE, NULL, S_OK), S_OK);
// See if the row was properly inserted
// Verify If we have inserted the row properly.
TESTC(cRow.FindRow(ulRowNum, m_pTable, m_pICommand, &pIRowset));
// FindRow merely finds the appropriate row. We need to validate the inserted data for the default bindings.
TESTC_(VerifyObj(m_iidExec, pIRowset, ulRowNum, cColumns, prgColumnsOrd, FALSE, TRUE, m_pTable, &cRows), S_OK);
fResult = TEST_PASS;
CLEANUP:
// Free the rowset
SAFE_RELEASE(pIRowset);
SAFE_RELEASE(pIConvertType);
// Set CANHOLDROWS to default
TESTC_(SetRowsetProperty(m_pICommand, DBPROPSET_ROWSET, DBPROP_CANHOLDROWS, VT_EMPTY), S_OK);
// Set RANDOM back to default
CHECK(SetRowsetPropertyDefault(DBPROP_ACCESSORDER), S_OK);
DropStoredProcedure(m_pICommandText, pwszProcName);
// Free the buffers we got from GetParameterInfo
PROVIDER_FREE(pBINDING);
PROVIDER_FREE(pParamOrdinals);
PROVIDER_FREE(pPARAMBIND);
PROVIDER_FREE(pwszCreateProcStmt);
PROVIDER_FREE(pwszExecProcStmt);
PROVIDER_FREE(pwszExecStmt);
PROVIDER_FREE(pwszProcName);
PROVIDER_FREE(pData);
::FreeParameterNames(cParams, pParamAll);
PROVIDER_FREE(pParamAll);
return fResult;
}
*/
// }} TCW_VAR_PROTOTYPE_END
// {{ TCW_VAR_PROTOTYPE(61)
//*-----------------------------------------------------------------------
// @mfunc S_OK: Input and output params with cParamSets > 1
//
// @rdesc TEST_PASS or TEST_FAIL
//
int TCCommandExecute_Rowset::Variation_61()
{
// Need to test with all paramter names set to NULL so we can verify
// names aren't required.
BOOL fResult = TRUE;
ULONG cParams = 0, cParamSets = 5;
DBLENGTH cbRowSize;
DBCOUNTITEM iRow, ulRowNum = 3;
DBBINDING * pBINDING=NULL;
DB_UPARAMS * pParamOrdinals=NULL;
DBPARAMBINDINFO * pPARAMBIND=NULL;
WCHAR * pwszCreateProcStmt=NULL;
WCHAR * pwszExecProcStmt=NULL;
WCHAR * pwszExecStmt=NULL;
WCHAR * pwszProcName=NULL;
BYTE * pData=NULL;
ParamStruct * pParamAll=NULL;
ULONG cColumns = 0;
DB_LORDINAL * prgColumnsOrd = NULL;
BOOL fCanDerive = FALSE;
ULONG iParam;
HRESULT hrSet = DB_S_TYPEINFOOVERRIDDEN;
DB_UPARAMS cActParams = 0;
WCHAR * pNamesBuffer = NULL;
DBPARAMINFO * pParamInfo = NULL;
BOOL fTableHadNulls = FALSE;
if (!m_fProcedureSupport)
{
odtLog << "Procedures not supported.\n";
return TEST_SKIPPED;
}
if (g_ulOutParamsSupported == DBPROPVAL_OA_NOTSUPPORTED)
{
odtLog << "Output parameters not supported.\n";
return TEST_SKIPPED;
}
if (!g_bMultipleParamSets)
{
odtLog << "Multiple parameter sets are not supported \n";
return TEST_SKIPPED;
}
// This variation cannot work if one of the values in one of the table columns is NULL
// unless the whole column is NULL, because the syntax used in the 'where' clause
// has to use the IS NULL construct rather than using a parameter. This would require
// different syntax for each param set, meaning a different statement for each param
// set. Since this isn't possible we will have to insert cParamSets rows that do not
// contain any NULLs. Since these violate the underlying table's information we will
// have to remove them afterwards.
if (m_pTable->GetNull() == USENULLS)
{
// Record that table did have nulls
fTableHadNulls = TRUE;
// Make the table think it doesn't have NULLS
m_pTable->SetNull(NONULLS);
// Record the starting number of the new rows
ulRowNum = m_pTable->GetNextRowNumber();
// Insert cParamSets new rows
TESTC_(m_pTable->Insert(ulRowNum, PRIMARY, TRUE, NULL, FALSE, cParamSets), S_OK);
}
// Create the syntax and binding for a stored proc with output parameters
ABORT_COMPARE(CreateProcBindings(
T_EXEC_PROC_SELECT_OUT,// [IN] Proc type, regular proc or function (has return value)
TRUE, // [IN] If TRUE then we add parameter names to the rgParamBindInfo
cParamSets, // [IN] Number of sets of parameters to be created
DBTYPE_I2, // [IN] Return parameter type
ulRowNum, // [IN] Row number in table to select, insert, or update
&cParams, // [OUT] Count of params created
&cbRowSize, // [OUT] Count of bytes for a single row of parameters
&pBINDING, // [OUT] Binding array for CreateAccessor
&pParamOrdinals,
&pPARAMBIND, // [OUT] rgParamBindInfo for SetParameterInfo
&pwszCreateProcStmt, // [OUT] SQL stmt to create the stored proc
&pwszExecProcStmt, // [OUT] SQL stmt to execute the stored proc
&pwszExecStmt, // [OUT] SQL stmt to execute without stored proc
&pwszProcName, // [OUT] Name of procedure
&pData, // [OUT] Pointer to data for the parameters
&pParamAll
), TRUE);
// Set all the parameter names NULL
for (iParam=0; iParam < cParams; iParam++)
pPARAMBIND[iParam].pwszName = NULL;
// Set up to execute the stored proc
ABORT_CHECK(PrepareForExecute(pwszExecProcStmt, cParams, pParamOrdinals, pPARAMBIND,
&fCanDerive, pwszProcName, pwszCreateProcStmt), S_OK);
if (!fCanDerive)
hrSet = S_OK;
// Now set the parameter information correctly
ABORT_CHECK(m_pICmdWParams->SetParameterInfo(cParams, pParamOrdinals, pPARAMBIND), hrSet);
// Get it again so we can validate we get back what we set.
ABORT_CHECK(m_pICmdWParams->GetParameterInfo(&cActParams, &pParamInfo, &pNamesBuffer), S_OK);
// Verify results. If we didn't get back what was set it might not be a failure
FAIL_VAR(VerifyParamInfo(cParams, pParamOrdinals, pPARAMBIND,
cActParams, pParamInfo, pNamesBuffer), S_OK);
// The final proof is that we can execute with these values
FAIL_VAR(ExecuteAndVerify(cParams, cParamSets, pParamAll, ulRowNum, pBINDING, cbRowSize, pData, ROWSET_NONE,
cColumns, prgColumnsOrd, VERIFY_USE_TABLE, TRUE), S_OK);
CLEANUP:
DropStoredProcedure(m_pICommandText, pwszProcName);
// Free the buffers we got from GetParameterInfo
PROVIDER_FREE(pParamInfo);
PROVIDER_FREE(pNamesBuffer);
PROVIDER_FREE(pBINDING);
PROVIDER_FREE(pParamOrdinals);
PROVIDER_FREE(pPARAMBIND);
PROVIDER_FREE(pwszCreateProcStmt);
PROVIDER_FREE(pwszExecProcStmt);
PROVIDER_FREE(pwszExecStmt);
PROVIDER_FREE(pwszProcName);
PROVIDER_FREE(pData);
::FreeParameterNames(cParams, pParamAll);
PROVIDER_FREE(pParamAll);
// Reset table parameters and delete any added rows
if (fTableHadNulls)
{
// Delete the extra non-null rows we added
for (iRow = ulRowNum; iRow < ulRowNum + cParamSets; iRow++)
CHECK(m_pTable->Delete(iRow), S_OK);
m_pTable->SetNull(USENULLS);
}
return fResult ? TEST_PASS : TEST_FAIL;
}
// }} TCW_VAR_PROTOTYPE_END
// {{ TCW_TERMINATE_METHOD
//--------------------------------------------------------------------
// @mfunc TestCase Termination Routine
//
// @rdesc TRUE or FALSE
//
BOOL TCCommandExecute_Rowset::Terminate()
{
if (g_ulOutParamsSupported != DBPROPVAL_OA_NOTSUPPORTED)
{
if (m_pSPIAccessor)
m_pSPIAccessor->ReleaseAccessor(m_hReadAccessor, NULL);
DropSP();
RELEASE (m_pSPICommandText);
RELEASE (m_pSPICommandPrepare);
SAFE_RELEASE(m_pSPIAccessor);
PROVIDER_FREE(m_rgSPBindings);
PROVIDER_FREE(m_rgSPParamBind);
PROVIDER_FREE(m_rgSPParamColOrdinals);
PROVIDER_FREE(m_rgSPParamColMap);
PROVIDER_FREE(m_rgReadBindings);
::FreeParameterNames(m_cSPBindings, m_pSPParamAll);
PROVIDER_FREE(m_pSPParamAll);
}
// {{ TCW_TERM_BASECLASS_CHECK2
return(CICmdWParams::Terminate());
} // }}
// }}
// }}
// {{ TCW_TC_PROTOTYPE(TCSetParameterInfo_Rowset)
//*-----------------------------------------------------------------------
//| Test Case: TCSetParameterInfo_Rowset - Test case for SetParameterInfo
//| Created: 05/03/96
//*-----------------------------------------------------------------------
//--------------------------------------------------------------------
// @mfunc TestCase Initialization Routine
//
// @rdesc TRUE or FALSE
//
BOOL TCSetParameterInfo_Rowset::Init()
{
HRESULT hr;
BOOL fResult = FALSE;
if (m_eTestCase == TC_Row && !g_fRowObj)
{
odtLog << L"Row objects not supported.\n";
return TEST_SKIPPED;
}
// {{ TCW_INIT_BASECLASS_CHECK
if(CICmdWParams::Init())
// }}
{
if (!m_bSetParameterInfo)
{
// IF SetParameterInfo is not set. set it anyway. for testing SetParameterInfo.
if (! CHECK (m_pICommandPrepare->Prepare(1), S_OK))
goto CLEANUP;
hr = m_pICmdWParams->SetParameterInfo(m_cDbParamBindInfo, m_rgParamOrdinals, m_rgDbParamBindInfo);
if (hr != DB_S_TYPEINFOOVERRIDDEN)
{
if (!CHECK(hr, S_OK))
goto CLEANUP;
}
}
// Create an array of parameter names
m_pwszParameterNames=CreateParameterNames(NULL, ALL_VALID_NAMES, NULL, INCLUDE_LONG_COLS);
if (!m_pwszParameterNames)
goto CLEANUP;
fResult = TRUE;
}
CLEANUP:
return fResult;
}
// {{ TCW_VAR_PROTOTYPE(1)
//*-----------------------------------------------------------------------
// @mfunc Call SetParameterInfo. GetParameterInfo. Should match.
//
// @rdesc TEST_PASS or TEST_FAIL
//
int TCSetParameterInfo_Rowset::Variation_1()
{
BOOL fResult = TRUE;
// Note: SetParameterInfo called in init for insert statement with params
FreeDescParams();
ABORT_CHECK (m_pICmdWParams->GetParameterInfo(&m_cParams, &m_rgParamInfo, &m_pNamesBuffer), S_OK);
FAIL_VAR(CompareDBParamInfo(m_cDbParamBindInfo, m_rgParamColMap, m_cParams, m_rgParamInfo, m_pNamesBuffer), S_OK);
CLEANUP:
FreeDescParams();
return (fResult) ? TEST_PASS : TEST_FAIL;
}
// }}
// {{ TCW_VAR_PROTOTYPE(2)
//*-----------------------------------------------------------------------
// @mfunc SetParameterInfo. Execute. S_OK
//
// @rdesc TEST_PASS or TEST_FAIL
//
int TCSetParameterInfo_Rowset::Variation_2()
{
BOOL fResult = FALSE;
DBROWCOUNT cRowsAffected = 0;
DBCOUNTITEM ulRowNum = 0;
HRESULT hr = S_OK;
// Note: SetParameterInfo called in init for insert statement with params
// Set command text
TEST_CHECK (m_pICommandText->SetCommandText(DBGUID_DBSQL , m_pwszSqlInsertAllWithParams), S_OK);
// Call ICommandPrepare.
TEST_CHECK (m_pICommandPrepare->Prepare(1), S_OK);
MakeDataForCommand((ulRowNum = NextInsertRowNum()));
// Now execute ICommand.
TEST_CHECK(m_pICommand->Execute(NULL, IID_NULL,
&m_DbParamsAll, &cRowsAffected, NULL), S_OK);
TEST_COMPARE(cRow.FindRow(ulRowNum, m_pTable), TRUE);
MakeDataForCommand((ulRowNum = NextInsertRowNum()));
// Now execute ICommand.
TEST_CHECK(m_pICommand->Execute(NULL, IID_NULL,
&m_DbParamsAll, &cRowsAffected, NULL), S_OK);
TEST_COMPARE(cRow.FindRow(ulRowNum, m_pTable), TRUE);
fResult = TRUE;
CLEANUP:
// RESET THE TYPEINFO correctly.
hr = m_pICmdWParams->SetParameterInfo(m_cDbParamBindInfo, m_rgParamOrdinals, m_rgDbParamBindInfo);
if (! (hr == S_OK || hr == DB_S_TYPEINFOOVERRIDDEN))
fResult = FALSE;
ReleaseDataForCommand();
return (fResult) ? TEST_PASS : TEST_FAIL;
}
// }}
// {{ TCW_VAR_PROTOTYPE(3)
//*-----------------------------------------------------------------------
// @mfunc SetParameterInfo, Execute. Use Rowset. DescribeParams.
// Over-rides parameter information from non-parameterized query
//
// @rdesc TEST_PASS or TEST_FAIL
//
int TCSetParameterInfo_Rowset::Variation_3()
{
UINT i;
WCHAR *pwszSqlStatement = NULL;
ULONG cDbParamBindInfo=0;
DBPARAMBINDINFO *rgDbParamBindInfo = NULL;
DB_UPARAMS *rgParamOrdinals = NULL;
DBORDINAL *rgUpdateableCols = NULL;
ULONG cUpdateableCols = 0;
CCol TempCol;
void *pData = NULL;
DBROWCOUNT cRowsAffected = 0;
DBPARAMS DbParamsAll;
HACCESSOR hLocalAccessor = DB_NULL_HACCESSOR;
DBBINDING *rgLocalBindings = NULL;
DBCOLUMNINFO *rgColInfo = NULL;
DBCOUNTITEM cBindings = 0;
DBLENGTH cbRowSize = 0;
DBORDINAL cColsOut = 0;
HRESULT hr;
BOOL fResult = TRUE;
WCHAR *pwszSqlSelectAllFromTbl = NULL;
DBCOUNTITEM ulRowNum = 0;
DBORDINAL cTableCols = m_pTable->CountColumnsOnTable();
ParamStruct * pParamStruct = NULL;
IRowset * pRowset = NULL;
// Remove any old parameter info
ABORT_CHECK(m_pICmdWParams->SetParameterInfo(0, NULL, NULL), S_OK);
// Set command text
ABORT_CHECK (m_pICommandText->SetCommandText(DBGUID_DBSQL , m_pwszSqlInsertAllWithParams), S_OK);
// Call ICommandPrepare.
if (m_pICommandPrepare)
ABORT_CHECK(m_pICommandPrepare->Prepare(1), S_OK);
// Set the parameter info if the provider requires it
ABORT_CHECK(SetParameterInfoIfNeeded(m_cDbParamBindInfo, m_rgParamOrdinals, m_rgDbParamBindInfo), S_OK);
MakeDataForCommand((ulRowNum = NextInsertRowNum()));
// Now execute ICommand.
ABORT_CHECK(m_pICommand->Execute(NULL, IID_NULL,
&m_DbParamsAll, &cRowsAffected, NULL), S_OK);
// No rowset to use. IRowset with parameters not supported currently.
ABORT_ALLOC(DB_UPARAMS, rgParamOrdinals, 0, (size_t)(cTableCols * sizeof(DB_UPARAMS)));
ABORT_ALLOC(DBORDINAL, rgUpdateableCols, 0, (size_t)(cTableCols * sizeof(DBORDINAL)));
ABORT_ALLOC(DBPARAMBINDINFO, rgDbParamBindInfo, 0, (size_t)(cTableCols * sizeof(DBPARAMBINDINFO)));
ABORT_ALLOC(DBBINDING, rgLocalBindings, 0, (size_t)(cTableCols * sizeof(DBBINDING)));
ABORT_ALLOC(ParamStruct, pParamStruct, 0, (size_t)(cTableCols * sizeof(ParamStruct)));
//We'll use this count as the index to the array as we build it
for (i = 1; i <= m_pTable->CountColumnsOnTable(); i++)
{
ABORT_CHECK(m_pTable->GetColInfo(i, TempCol), S_OK);
//Record the column number in the array
if (TempCol.GetUpdateable())
{
// Build Information for SetParameterInfo.
rgUpdateableCols[cUpdateableCols++] = TempCol.GetColNum(); // Column number which is the next Updateable col.
rgParamOrdinals[cDbParamBindInfo] = cDbParamBindInfo + 1; // Parameter ordinal number.
AddParam(cDbParamBindInfo++, i, DBPARAMIO_INPUT, NULL, FALSE, &cbRowSize, rgLocalBindings, rgDbParamBindInfo, pParamStruct, m_pTable);
}
}
// Create the accessor
ABORT_CHECK(m_pCmdIAccessor->CreateAccessor(DBACCESSOR_PARAMETERDATA, cDbParamBindInfo, rgLocalBindings,
cbRowSize, &hLocalAccessor, NULL), S_OK);
//Replace text in CommandObject With select statment with no parameters.
ABORT_CHECK(m_hr = m_pTable->CreateSQLStmt(SELECT_UPDATEABLE, NULL, &pwszSqlSelectAllFromTbl, NULL, NULL ), S_OK);
ABORT_CHECK(m_pICommandText->SetCommandText(DBGUID_DBSQL , pwszSqlSelectAllFromTbl), S_OK);
// Call ICommandPrepare.
if (m_pICommandPrepare)
ABORT_CHECK (m_pICommandPrepare->Prepare(1), S_OK);
// Now override the previous settings and call SetParameterInfo.
FAIL_CHECK(m_pICmdWParams->SetParameterInfo(cDbParamBindInfo, rgParamOrdinals, rgDbParamBindInfo), DB_S_TYPEINFOOVERRIDDEN);
// Set command text for parameters.
ABORT_CHECK(m_pICommandText->SetCommandText(DBGUID_DBSQL , m_pwszSqlInsertAllWithParams), S_OK);
// Call ICommandPrepare.
if (m_pICommandPrepare)
ABORT_CHECK (m_pICommandPrepare->Prepare(1), S_OK);
// Now generate data for execute
ABORT_CHECK(FillInputBindings((CTable *)m_pTable,
DBACCESSOR_PARAMETERDATA, cDbParamBindInfo, rgLocalBindings,
(BYTE **)&pData, (ulRowNum = NextInsertRowNum()) , cUpdateableCols, (DB_LORDINAL *)rgUpdateableCols), S_OK);
DbParamsAll.cParamSets = 1;
DbParamsAll.hAccessor = hLocalAccessor;
DbParamsAll.pData = pData;
ABORT_CHECK(m_pICommand->Execute(NULL, m_iidExec,
&DbParamsAll, &cRowsAffected, (IUnknown **)&pRowset), S_OK);
FAIL_COMPARE(cRowsAffected, 1);
FAIL_COMPARE(pRowset, NULL);
CLEANUP:
// RESET THE TYPEINFO correctly.
hr = m_pICmdWParams->SetParameterInfo(m_cDbParamBindInfo, m_rgParamOrdinals, m_rgDbParamBindInfo);
// We might have set typeinfo above or not depending upon where we jump from.
if (! (hr == S_OK || hr == DB_S_TYPEINFOOVERRIDDEN))
fResult = FALSE;
if (hLocalAccessor != DB_NULL_HACCESSOR)
m_pCmdIAccessor->ReleaseAccessor(hLocalAccessor, NULL);
PROVIDER_FREE(pData);
PROVIDER_FREE(rgParamOrdinals );
PROVIDER_FREE(rgDbParamBindInfo) ;
PROVIDER_FREE(pwszSqlSelectAllFromTbl);
PROVIDER_FREE(rgUpdateableCols);
PROVIDER_FREE(rgLocalBindings);
PROVIDER_FREE(rgColInfo );
PROVIDER_FREE(pParamStruct );
return (fResult) ? TEST_PASS : TEST_FAIL;
}
// }}
// {{ TCW_VAR_PROTOTYPE(4)
//*-----------------------------------------------------------------------
// @mfunc SetParameterInfo. Remove SetParameterInfo. GetParameterInfo. return original or fail
//
// @rdesc TEST_PASS or TEST_FAIL
//
int TCSetParameterInfo_Rowset::Variation_4()
{
BOOL fResult = FALSE;
HRESULT hr;
LPWSTR wszTypeInfo[] = { L"DBTYPE_I1", L"DBTYPE_I2" };
DBPARAMBINDINFO rgParamBindInfo[] = { { NULL, NULL, 0, 0, 0, 0 }, { NULL, NULL, 0, 0, 0, 0 } };
DB_UPARAMS rgParamOrdinals[2] = { 1, 2 };
rgParamBindInfo[0].pwszDataSourceType = wszTypeInfo[0];
rgParamBindInfo[1].pwszDataSourceType = wszTypeInfo[1];
// Set command text
TEST_CHECK (m_pICommandText->SetCommandText(DBGUID_DBSQL , m_pwszSqlInsertAllWithParams), S_OK);
// Now override the previous settings and call SetParameterInfo.
// Should return DB_S_TYPEINFOOVERRIDDEN because we called it once in Init.
TEST_CHECK (m_pICmdWParams->SetParameterInfo(m_cDbParamBindInfo, m_rgParamOrdinals, m_rgDbParamBindInfo),DB_S_TYPEINFOOVERRIDDEN);
FreeDescParams();
TEST_CHECK (m_pICmdWParams->GetParameterInfo(&m_cParams, &m_rgParamInfo, &m_pNamesBuffer), S_OK);
CompareDBParamInfo(m_cDbParamBindInfo, m_rgParamColMap, m_cParams, m_rgParamInfo, m_pNamesBuffer);
// Now remove SetParameterInfo.
TEST_CHECK (m_pICmdWParams->SetParameterInfo (0, NULL, NULL), S_OK);
TEST_CHECK (m_pICommandPrepare->Prepare(1), S_OK);
FreeDescParams();
hr = m_pICmdWParams->GetParameterInfo(&m_cParams, &m_rgParamInfo, &m_pNamesBuffer);
if (m_bSetParameterInfo)
{
TEST_COMPARE (m_cParams, 0);
TEST_CHECK (hr, DB_E_PARAMUNAVAILABLE);
}
else
{
TEST_COMPARE (m_cParams, m_cDbParamBindInfo);
TEST_CHECK (hr, S_OK);
}
hr = m_pICmdWParams->SetParameterInfo(m_cDbParamBindInfo, m_rgParamOrdinals, m_rgDbParamBindInfo);
if (m_bSetParameterInfo)
{
// Provider couldn't describe.
// Since TypeInfo was removed we should be able to set.
TEST_CHECK (hr, S_OK);
}
else
{
// Provider could describe but SetParameterInfo overrode.
TEST_CHECK (hr, DB_S_TYPEINFOOVERRIDDEN);
}
// Try other combinations for removal.(0, Valid, NULL)
// Now remove SetParameterInfo.
TEST_CHECK (m_pICmdWParams->SetParameterInfo (0, rgParamOrdinals, NULL), S_OK);
FreeDescParams();
hr = m_pICmdWParams->GetParameterInfo(&m_cParams, &m_rgParamInfo, &m_pNamesBuffer);
if (m_bSetParameterInfo)
{
TEST_CHECK (hr, DB_E_PARAMUNAVAILABLE);
}
else
{
TEST_CHECK (hr, S_OK);
}
// TEST_COMPARE (m_cParams, 0);
hr = m_pICmdWParams->SetParameterInfo(m_cDbParamBindInfo, m_rgParamOrdinals, m_rgDbParamBindInfo);
if (m_bSetParameterInfo)
{
// Since TypeInfo was removed we should be able to set.
TEST_CHECK (hr, S_OK);
}
else
{
// Provider could describe but SetParameterInfo overrode.
TEST_CHECK (hr, DB_S_TYPEINFOOVERRIDDEN);
}
// Try other combinations for removal.(0, NULL, Valid)
// Now remove SetParameterInfo.
TEST_CHECK (m_pICmdWParams->SetParameterInfo (0, NULL, rgParamBindInfo), S_OK);
FreeDescParams();
hr = m_pICmdWParams->GetParameterInfo(&m_cParams, &m_rgParamInfo, &m_pNamesBuffer);
if (m_bSetParameterInfo)
{
TEST_COMPARE (m_cParams, 0);
TEST_CHECK (hr, DB_E_PARAMUNAVAILABLE);
}
else
{
TEST_CHECK (hr, S_OK);
}
// IN M08, we will have to prepare the statement here.
hr = m_pICmdWParams->SetParameterInfo(m_cDbParamBindInfo, m_rgParamOrdinals, m_rgDbParamBindInfo);
if (m_bSetParameterInfo)
{
// Since TypeInfo was removed we should be able to set.
TEST_CHECK (hr, S_OK);
}
else
{
// Provider could describe but SetParameterInfo overrode.
TEST_CHECK (hr, DB_S_TYPEINFOOVERRIDDEN);
}
// Try other combinations for removal.(0, Valid, Valid)
// Now remove SetParameterInfo.
TEST_CHECK (m_pICmdWParams->SetParameterInfo (0, rgParamOrdinals, rgParamBindInfo), S_OK);
FreeDescParams();
hr = m_pICmdWParams->GetParameterInfo(&m_cParams, &m_rgParamInfo, &m_pNamesBuffer);
if (m_bSetParameterInfo)
{
TEST_COMPARE (m_cParams, 0);
TEST_CHECK (hr, DB_E_PARAMUNAVAILABLE);
}
else
{
TEST_COMPARE (m_cParams, m_cDbParamBindInfo);
TEST_CHECK (hr, S_OK);
}
// IN M08, we will have to prepare the statement here.
hr = m_pICmdWParams->SetParameterInfo(m_cDbParamBindInfo, m_rgParamOrdinals, m_rgDbParamBindInfo);
if (m_bSetParameterInfo)
{
// Since TypeInfo was removed we should be able to set.
TEST_CHECK (hr, S_OK);
}
else
{
// Provider could describe but SetParameterInfo overrode.
TEST_CHECK (hr, DB_S_TYPEINFOOVERRIDDEN);
}
fResult = TRUE;
CLEANUP:
FreeDescParams();
// RESET THE TYPEINFO correctly.
hr = m_pICmdWParams->SetParameterInfo(m_cDbParamBindInfo, m_rgParamOrdinals, m_rgDbParamBindInfo);
// We might have set typeinfo above or not depending upon where we jump from.
if (! (hr == S_OK || hr == DB_S_TYPEINFOOVERRIDDEN))
fResult = FALSE;
return (fResult) ? TEST_PASS : TEST_FAIL;
}
// }}
// {{ TCW_VAR_PROTOTYPE(5)
//*-----------------------------------------------------------------------
// @mfunc SetParameterInfo for all. Delete a few. Override a few. Call GetParameterInfo and verify.
//
// @rdesc TEST_PASS or TEST_FAIL
//
int TCSetParameterInfo_Rowset::Variation_5()
{
BOOL fResult = FALSE;
DB_UPARAMS cParams = 0;
DB_UPARAMS *rgParamOrdinals = NULL;
HRESULT hr;
DB_UPARAMS i;
// Now override the previous settings and call SetParameterInfo.
// Should return DB_S_TYPEINFOOVERRIDDEN because we called it once in Init.
TEST_CHECK (m_pICmdWParams->SetParameterInfo(m_cDbParamBindInfo, m_rgParamOrdinals, m_rgDbParamBindInfo),DB_S_TYPEINFOOVERRIDDEN);
FreeDescParams();
TEST_CHECK (m_pICmdWParams->GetParameterInfo(&m_cParams, &m_rgParamInfo, &m_pNamesBuffer), S_OK);
CompareDBParamInfo(m_cDbParamBindInfo, m_rgParamColMap, m_cParams, m_rgParamInfo, m_pNamesBuffer);
// Now remove SetParameterInfo.
TEST_CHECK (m_pICmdWParams->SetParameterInfo (0, NULL, NULL), S_OK);
TEST_CHECK (m_pICmdWParams->SetParameterInfo (0, NULL, NULL), S_OK);
TEST_CHECK (m_pICommandPrepare->Prepare(1), S_OK);
FreeDescParams();
hr = m_pICmdWParams->GetParameterInfo(&m_cParams, &m_rgParamInfo, &m_pNamesBuffer);
if (m_bSetParameterInfo)
{
TEST_COMPARE (m_cParams, 0);
TEST_CHECK (hr, DB_E_PARAMUNAVAILABLE);
}
else
{
// Only derived information is available.
TEST_COMPARE (m_cParams, m_cDbParamBindInfo);
TEST_CHECK (hr, S_OK);
CompareDBParamInfo(m_cDbParamBindInfo, m_rgParamColMap, m_cParams, m_rgParamInfo, m_pNamesBuffer);
}
FreeDescParams();
// RESET THE TYPEINFO correctly.
hr = m_pICmdWParams->SetParameterInfo(m_cDbParamBindInfo, m_rgParamOrdinals, m_rgDbParamBindInfo);
if (! (hr == S_OK || hr == DB_S_TYPEINFOOVERRIDDEN))
{
fResult = FALSE;
goto CLEANUP;
}
// Now verify.
hr = m_pICmdWParams->GetParameterInfo(&m_cParams, &m_rgParamInfo, &m_pNamesBuffer);
TEST_CHECK (hr, S_OK);
CompareDBParamInfo(m_cDbParamBindInfo, m_rgParamColMap, m_cParams, m_rgParamInfo, m_pNamesBuffer);
// Now lets prepare to remove the last half and verify.
cParams = m_cDbParamBindInfo/2;
rgParamOrdinals = (DB_UPARAMS *)m_pIMalloc->Alloc((m_cDbParamBindInfo - cParams)* sizeof(DB_UPARAMS));
if (rgParamOrdinals == NULL)
{
odtLog << wszMemoryAllocationError;
goto CLEANUP;
}
// Initialize.the ordinal array.
for (i =cParams; i < m_cDbParamBindInfo; i++)
rgParamOrdinals[i-cParams] = m_rgParamOrdinals[i];
// Now remove the upper half of the params, leaving cParams left.
hr = m_pICmdWParams->SetParameterInfo((m_cDbParamBindInfo - cParams), rgParamOrdinals, NULL);
if (hr != DB_S_TYPEINFOOVERRIDDEN)
{
if (hr != S_OK)
goto CLEANUP;
}
FreeDescParams();
hr = m_pICmdWParams->GetParameterInfo(&m_cParams, &m_rgParamInfo, &m_pNamesBuffer);
TEST_CHECK (hr, S_OK);
// Spec change: Providers that can derive parameter info are allowed to return information for all params.
if (m_bSetParameterInfo)
// Provider couldn't derive, so we expect some params to be removed
CompareDBParamInfo(cParams, m_rgParamColMap, m_cParams, m_rgParamInfo, m_pNamesBuffer);
else
{
// Provider can derive, params may be removed or may not be removed. Derived information should be
// available for params that were removed above if they are returned anyway.
COMPARE(m_cParams == m_cDbParamBindInfo || m_cParams == cParams, TRUE);
CompareDBParamInfo(m_cParams, m_rgParamColMap, m_cParams, m_rgParamInfo, m_pNamesBuffer);
}
fResult = TRUE;
CLEANUP:
FreeDescParams();
// RESET THE TYPEINFO correctly.
hr = m_pICmdWParams->SetParameterInfo(m_cDbParamBindInfo, m_rgParamOrdinals, m_rgDbParamBindInfo);
// We might have set typeinfo above or not depending upon where we jump from.
if (! (hr == S_OK || hr == DB_S_TYPEINFOOVERRIDDEN))
fResult = FALSE;
if (rgParamOrdinals)
FREE_DATA(rgParamOrdinals);
return (fResult) ? TEST_PASS : TEST_FAIL;
}
// }}
// {{ TCW_VAR_PROTOTYPE(6)
//*-----------------------------------------------------------------------
// @mfunc (valid, NULL, NULL
//
// @rdesc TEST_PASS or TEST_FAIL
//
int TCSetParameterInfo_Rowset::Variation_6()
{
ULONG cParams = 2;
LPWSTR wszTypeInfo[] = { L"DBTYPE_I1", L"DBTYPE_I2" };
HRESULT hr = S_OK;
DBPARAMBINDINFO rgParamBindInfo[] = { { NULL, NULL, 0, 0, 0, 0 }, { NULL, NULL, 0, 0, 0, 0 } };
BOOL fResult = FALSE;
rgParamBindInfo[0].pwszDataSourceType = wszTypeInfo[0];
rgParamBindInfo[1].pwszDataSourceType = wszTypeInfo[1];
TEST_CHECK (m_pICmdWParams->SetParameterInfo (cParams, NULL, rgParamBindInfo), E_INVALIDARG);
TEST_CHECK (m_pICmdWParams->SetParameterInfo (cParams, NULL, NULL), E_INVALIDARG);
fResult = TRUE;
CLEANUP:
return (fResult) ? TEST_PASS : TEST_FAIL;
}
// }}
// {{ TCW_VAR_PROTOTYPE(7)
//*-----------------------------------------------------------------------
// @mfunc Bstr = NULL. What happens?
//
// @rdesc TEST_PASS or TEST_FAIL
//
int TCSetParameterInfo_Rowset::Variation_7()
{
ULONG cParams = 2;
DB_UPARAMS rgParamOrdinals[2] = { 1, 2 };
LPWSTR wszTypeInfo[] = { L"DBTYPE_I1", L"DBTYPE_I2" };
DBPARAMBINDINFO rgParamBindInfo[] = { { NULL, NULL, 0, 0, 0, 0 }, { NULL, NULL, 0, 0, 0, 0 } };
HRESULT hr;
BOOL fResult = FALSE;
// rgParamBindInfo[0].pwszDataSourceType is already initialized to NULL.
rgParamBindInfo[1].pwszDataSourceType = wszTypeInfo[1];
TEST_CHECK (m_pICmdWParams->SetParameterInfo (cParams, rgParamOrdinals, rgParamBindInfo), E_INVALIDARG);
fResult = TRUE;
CLEANUP:
// RESET THE TYPEINFO correctly.
hr = m_pICmdWParams->SetParameterInfo(m_cDbParamBindInfo, m_rgParamOrdinals, m_rgDbParamBindInfo);
// We might have set typeinfo above or not depending upon where we jump from.
if (! (hr == S_OK || hr == DB_S_TYPEINFOOVERRIDDEN))
fResult = FALSE;
return (fResult) ? TEST_PASS : TEST_FAIL;
}
// }}
// {{ TCW_VAR_PROTOTYPE(8)
//*-----------------------------------------------------------------------
// @mfunc SetParameterInfo with Illegal Coersions. Execute should fail.
//
// @rdesc TEST_PASS or TEST_FAIL
//
int TCSetParameterInfo_Rowset::Variation_8()
{
HRESULT hr;
BOOL fResult = FALSE;
DBROWCOUNT cRowsAffected = 0;
IRowset *pRowset = NULL;
DB_UPARAMS *rgParamOrdinals = NULL;
UINT i;
BOOL fValidStatus = FALSE;
ULONG iParam;
// First Set type info incorrectly.
// reverse the ordinal array.
rgParamOrdinals = (DB_UPARAMS *)m_pIMalloc->Alloc (m_cDbParamBindInfo*sizeof (DB_UPARAMS));
if (m_cDbParamBindInfo > 1)
{
//swap;
for (i = 0; i < m_cDbParamBindInfo; i++ )
rgParamOrdinals[i] = m_rgParamOrdinals[m_cDbParamBindInfo - i -1];
}
else
{
// Only One column. return TRUE
return TEST_PASS;
}
// Set command text
TEST_CHECK (m_pICommandText->SetCommandText(DBGUID_DBSQL , m_pwszSqlInsertAllWithParams), S_OK);
// Call ICommandPrepare.
TEST_CHECK (m_pICommandPrepare->Prepare(1), S_OK);
MakeDataForCommand(NextInsertRowNum());
// Now override the previous settings and call SetParameterInfo.
// Should return DB_S_TYPEINFOOVERRIDDEN because we called it once in Init.
TEST_CHECK (m_pICmdWParams->SetParameterInfo(m_cDbParamBindInfo, rgParamOrdinals, m_rgDbParamBindInfo),DB_S_TYPEINFOOVERRIDDEN);
// Now execute ICommand.
hr = m_pICommand->Execute(NULL, IID_IUnknown, &m_DbParamsAll, &cRowsAffected, (IUnknown **)&pRowset);
if (FAILED(hr))
// Since the row wasn't inserted, decrement next insert row number incremented above.
m_cInsertRowNum--;
switch (hr)
{
case DB_E_ERRORSOCCURRED:
// Status value should be set to something informative, NOT DBSTATUS_E_BADACCESSOR
// for at least one of the parameters.
for (iParam=0; iParam < m_cBindings; iParam++)
if (IsErrorStatus(STATUS_BINDING(m_rgBindings[iParam], m_pData)) &&
STATUS_BINDING(m_rgBindings[iParam], m_pData) != DBSTATUS_E_BADACCESSOR)
fValidStatus = TRUE;
break;
case DB_E_UNSUPPORTEDCONVERSION:
// Status value should be set to DBSTATUS_E_BADACCESSOR for at least one of the parameters
for (iParam=0; iParam < m_cBindings; iParam++)
if (STATUS_BINDING(m_rgBindings[iParam], m_pData) == DBSTATUS_E_BADACCESSOR)
fValidStatus = TRUE;
break;
default:
TEST_CHECK(hr, DB_E_ERRORSOCCURRED);
}
TEST_COMPARE(fValidStatus, TRUE);
fResult = TRUE;
CLEANUP:
FreeDescParams();
ReleaseDataForCommand();
ReleaseRowsetPtr(&pRowset);
FREE_DATA (rgParamOrdinals);
// RESET THE TYPEINFO correctly.
hr = m_pICmdWParams->SetParameterInfo(m_cDbParamBindInfo, m_rgParamOrdinals, m_rgDbParamBindInfo);
// We might have set typeinfo above or not depending upon where we jump from.
if (! (hr == S_OK || hr == DB_S_TYPEINFOOVERRIDDEN))
fResult = FALSE;
return (fResult) ? TEST_PASS : TEST_FAIL;
}
// }}
// {{ TCW_VAR_PROTOTYPE(9)
//*-----------------------------------------------------------------------
// @mfunc rgParamOrdinals[] = ULONG_MAX. what happens.
//
// @rdesc TEST_PASS or TEST_FAIL
//
int TCSetParameterInfo_Rowset::Variation_9()
{
ULONG cParams = 2;
DB_UPARAMS ulNumParams=0;
HRESULT hr = E_FAIL, hrSet = E_FAIL;
DB_UPARAMS rgParamOrdinals[2] = { 1, ULONG_MAX };
LPWSTR wszTypeInfo[] = { L"DBTYPE_I1", L"DBTYPE_I2" };
WCHAR *pwszSqlSelectAllFromTbl=NULL;
DBPARAMBINDINFO rgParamBindInfo[] = { { NULL, NULL, 0, 0, 0, 0 }, { NULL, NULL, 0, 0, 0, 0 } };
BOOL fResult = FALSE;
rgParamBindInfo[0].pwszDataSourceType = wszTypeInfo[0];
rgParamBindInfo[1].pwszDataSourceType = wszTypeInfo[1];
// Set command text
FAIL_CHECK(m_pICommandText->SetCommandText(DBGUID_DBSQL , m_pwszSqlInsertAllWithParams), S_OK);
// Make sure we're prepared in case a previous variation failed the prepare
TEST_CHECK (m_pICommandPrepare->Prepare(1), S_OK);
// First remove all.
hr = m_pICmdWParams->SetParameterInfo(0, NULL, NULL);
// Verify.
FreeDescParams();
hr = m_pICmdWParams->GetParameterInfo(&m_cParams, &m_rgParamInfo, &m_pNamesBuffer);
if (m_bSetParameterInfo)
{
// Since we delete all we should get Unavailable;
TEST_CHECK (hr, DB_E_PARAMUNAVAILABLE);
TEST_COMPARE(m_cParams, 0);
TEST_COMPARE(m_rgParamInfo, NULL);
TEST_COMPARE(m_pNamesBuffer, NULL);
}
else
{
TEST_CHECK (hr, S_OK);
ulNumParams = m_cParams;
}
// Now unprepare and delete command text to keep the provider from knowing ULONG_MAX is not a
// valid ordinal if they can derive
if (! CHECK (m_pICommandPrepare->Unprepare(), S_OK))
goto CLEANUP;
// Set command text to none
TEST_CHECK (m_pICommandText->SetCommandText(DBGUID_DBSQL , NULL), S_OK);
// Now set the ULONG_MAX ordinal. We're mainly concerned the provider has some
// valid behavior for this case, doesn't crash or return an unexpected error.
// Note 1: Some providers allocate memory for each parameter up to the max (ULONG_MAX)
// and will run out of memory, which is not really a failure, so pass that behavior
// Note 2: Some providers don't allow ULONG_MAX iOrdinals and have an internal limit. In
// that case they will return an error. Again, this is valid behavior.
hrSet = m_pICmdWParams->SetParameterInfo (cParams, rgParamOrdinals, rgParamBindInfo);
switch(hrSet)
{
case DB_S_TYPEINFOOVERRIDDEN:
case E_OUTOFMEMORY:
case DB_E_ERRORSOCCURRED:
break;
default:
TEST_CHECK(hrSet, S_OK);
}
// Set command text
TEST_CHECK (m_pICommandText->SetCommandText(DBGUID_DBSQL , m_pwszSqlInsertAllWithParams), S_OK);
// Prepare again
TEST_CHECK (m_pICommandPrepare->Prepare(1), S_OK);
// Verify with get parameter info.
FreeDescParams();
hr = m_pICmdWParams->GetParameterInfo(&m_cParams, &m_rgParamInfo, &m_pNamesBuffer);
// If the set was successful
if (SUCCEEDED(hrSet))
{
TEST_CHECK(hr, S_OK);
TEST_COMPARE (m_cParams, cParams);
// Verify 2nd ordinal is ULongMax.
TEST_COMPARE (m_rgParamInfo[cParams-1].iOrdinal, ULONG_MAX);
}
// If set failed but the provider can derive ULONG_MAX was not added
else if (!m_bSetParameterInfo)
{
TEST_CHECK(hr, S_OK);
TEST_COMPARE (m_cParams, ulNumParams);
TEST_COMPARE (m_rgParamInfo[ulNumParams-1].iOrdinal != ULONG_MAX, TRUE);
}
// Set failed and provider can't derive
else
{
TEST_CHECK (hr, DB_E_PARAMUNAVAILABLE);
TEST_COMPARE(m_cParams, 0);
TEST_COMPARE(m_rgParamInfo, NULL);
TEST_COMPARE(m_pNamesBuffer, NULL);
}
fResult = TRUE;
CLEANUP:
FreeDescParams();
// Set command text
FAIL_CHECK(m_pICommandText->SetCommandText(DBGUID_DBSQL , m_pwszSqlInsertAllWithParams), S_OK);
// RESET THE TYPEINFO correctly.
FAIL_CHECK(m_pICmdWParams->SetParameterInfo(0, NULL, NULL), S_OK);
hr = m_pICmdWParams->SetParameterInfo(m_cDbParamBindInfo, m_rgParamOrdinals, m_rgDbParamBindInfo);
// We might have set typeinfo above or not depending upon where we jump from.
if (! (hr == S_OK || hr == DB_S_TYPEINFOOVERRIDDEN))
fResult = FALSE;
return (fResult) ? TEST_PASS : TEST_FAIL;
}
// }}
// {{ TCW_VAR_PROTOTYPE(10)
//*-----------------------------------------------------------------------
// @mfunc Call execute with params before SetParameterInfo
//
// @rdesc TEST_PASS or TEST_FAIL
//
int TCSetParameterInfo_Rowset::Variation_10()
{
ICommand *pICommand = NULL;
ICommandPrepare *pICommandPrep = NULL;
IAccessor *pLocalIAccessor = NULL;
ICommandWithParameters *pICmdWPar = NULL;
BOOL fResult = FALSE;
HACCESSOR hLocalAccessor = DB_NULL_HACCESSOR;
void *pData = NULL;
DBPARAMS ExecDbParams;
DBROWCOUNT cRowsAffected;
IRowset * pRowset = NULL;
DBCOUNTITEM ulRowNum = 0;
HRESULT hr = S_OK;
// Command to return a ICommand with Text Set
if( !CHECK(m_pTable->BuildCommand(m_pwszSqlInsertAllWithParams, // SQL STMT
IID_ICommand, // IID no more that IID_ICommand is needed.
EXECUTE_NEVER, // EXECUTE
NULL, // # Prop's
NULL, // Prop's
NULL, // Params
NULL, // # Rowset
NULL, // Rowsets
&pICommand), // ICommand
S_OK) )
goto CLEANUP;
// QI for ICommandPrepare
if (!VerifyInterface (pICommand, IID_ICommandPrepare, COMMAND_INTERFACE, (IUnknown **)&pICommandPrep))
goto CLEANUP;
// Verify The Interface for a Command Accessor Object.
if (!VerifyInterface(pICommand, IID_IAccessor, COMMAND_INTERFACE,(IUnknown **)&pLocalIAccessor))
{
//ICommandAccessor is not supported
goto CLEANUP;
}
// We might need to set the property for this or we need not. REVISIT and CHECK.
if (!VerifyInterface(pICommand, IID_ICommandWithParameters, COMMAND_INTERFACE, (IUnknown **)&pICmdWPar))
goto CLEANUP;
// 1. Create accessor.
// Get accessor.
TEST_CHECK (pLocalIAccessor->CreateAccessor ( DBACCESSOR_PARAMETERDATA, m_cBindings, m_rgBindings,
m_cbRowSize, &hLocalAccessor, NULL), S_OK);
TEST_CHECK (FillInputBindings(m_pTable, DBACCESSOR_PARAMETERDATA, m_cBindings, m_rgBindings,
(BYTE **)&pData, (ulRowNum = NextInsertRowNum()), m_cParamColMap, (DB_LORDINAL *)m_rgParamColMap), S_OK);
ExecDbParams.cParamSets = 1;
ExecDbParams.hAccessor = hLocalAccessor;
ExecDbParams.pData = pData;
//ExecDbParams.cbParamSetSize = 0;
// Now call Execute.
// Since SetParameterInfo is not called, execute should try to coerce the paraemters based
// on accessor type and succeed. Should return S_OK.
TEST_CHECK(pICommand->Execute(NULL, IID_IUnknown, &ExecDbParams,
&cRowsAffected, (IUnknown **)&pRowset), S_OK);
fResult = TRUE;
CLEANUP:
// RESET THE TYPEINFO correctly.
hr = m_pICmdWParams->SetParameterInfo(m_cDbParamBindInfo, m_rgParamOrdinals, m_rgDbParamBindInfo);
// We might have set typeinfo above or not depending upon where we jump from.
if (! (hr == S_OK || hr == DB_S_TYPEINFOOVERRIDDEN))
fResult = FALSE;
ReleaseInputBindingsMemory(m_cBindings, m_rgBindings, (BYTE *)ExecDbParams.pData, TRUE);
// Release Objects
pLocalIAccessor->ReleaseAccessor(hLocalAccessor, NULL);
RELEASE (pLocalIAccessor);
RELEASE( pICommandPrep );
RELEASE( pICmdWPar);
RELEASE( pICommand );
return (fResult) ? TEST_PASS : TEST_FAIL;
}
// }}
// {{ TCW_VAR_PROTOTYPE(11)
//*-----------------------------------------------------------------------
// @mfunc Wrong SetParameterInfo. Verify DP returns the same.
//
// @rdesc TEST_PASS or TEST_FAIL
//
int TCSetParameterInfo_Rowset::Variation_11()
{
BOOL fResult = FALSE;
HRESULT hr;
UINT i =0;
DBPARAMBINDINFO * pParamBindInfo = NULL;
// Delete all the information.
TEST_CHECK(m_pICmdWParams->SetParameterInfo (0, NULL, NULL), S_OK);
// Make a copy of the "correct" info
TEST_ALLOC(DBPARAMBINDINFO, pParamBindInfo, 0, (size_t)(m_cBindings * sizeof(DBPARAMBINDINFO)));
memcpy(pParamBindInfo, m_rgDbParamBindInfo, (size_t)(m_cBindings * sizeof(DBPARAMBINDINFO)));
// Make it incorrect
ReverseArray(pParamBindInfo, m_cBindings, sizeof(DBPARAMBINDINFO));
TEST_CHECK (m_pICommandText->SetCommandText(DBGUID_DBSQL , m_pwszSqlInsertAllWithParams), S_OK);
if (m_pICommandPrepare)
TEST_CHECK(m_pICommandPrepare->Prepare(1), S_OK);
// Set it with some type so that we can verify back again.
hr = m_pICmdWParams->SetParameterInfo (m_cBindings, m_rgParamOrdinals, pParamBindInfo);
if (! (hr == S_OK || hr == DB_S_TYPEINFOOVERRIDDEN))
goto CLEANUP;
// Compare contents. with set values.
// First call GetParameterInfo.
FreeDescParams();
TEST_CHECK (m_pICmdWParams->GetParameterInfo(&m_cParams, &m_rgParamInfo, &m_pNamesBuffer), S_OK);
// Compare GetParameterInfo.
TEST_CHECK(VerifyParamInfo(m_cBindings, m_rgParamOrdinals, pParamBindInfo, m_cParams,
m_rgParamInfo, m_pNamesBuffer), S_OK);
fResult = TRUE;
CLEANUP:
// Set command text
FreeDescParams();
// RESET THE TYPEINFO correctly.
hr = m_pICmdWParams->SetParameterInfo(m_cDbParamBindInfo, m_rgParamOrdinals, m_rgDbParamBindInfo);
// We might have set typeinfo above or not depending upon where we jump from.
if (! (hr == S_OK || hr == DB_S_TYPEINFOOVERRIDDEN))
fResult = FALSE;
return (fResult) ? TEST_PASS : TEST_FAIL;
}
// }}
// {{ TCW_VAR_PROTOTYPE(12)
//*-----------------------------------------------------------------------
// @mfunc Delete first parameter, last parameter. Verify.
//
// @rdesc TEST_PASS or TEST_FAIL
//
int TCSetParameterInfo_Rowset::Variation_12()
{
ULONG cDeleteParams = 2;
DB_UPARAMS rgDeleteOrdinals[2];
BOOL fResult = FALSE;
HRESULT hr;
// Delete all the information.
TEST_CHECK(m_pICmdWParams->SetParameterInfo (0, NULL, NULL), S_OK);
// Prepare if needed
if (m_pICommandPrepare)
TEST_CHECK(m_pICommandPrepare->Prepare(1), S_OK);
// Set it with some type so that we can verify back again.
hr = m_pICmdWParams->SetParameterInfo (m_cBindings, m_rgParamOrdinals, m_rgDbParamBindInfo);
if (! (hr == S_OK || hr == DB_S_TYPEINFOOVERRIDDEN))
goto CLEANUP;
// Set the delete ordinals
rgDeleteOrdinals[0] = m_rgParamOrdinals[0];
rgDeleteOrdinals[1] = m_rgParamOrdinals[m_cBindings-1];
// Delete the First and last. Only the 2nd should remain.
hr = m_pICmdWParams->SetParameterInfo(cDeleteParams, rgDeleteOrdinals, NULL);
if (! (hr == S_OK || hr == DB_S_TYPEINFOOVERRIDDEN))
goto CLEANUP;
// Compare contents. with set values.
// First call GetParameterInfo.
FreeDescParams();
TEST_CHECK (m_pICmdWParams->GetParameterInfo(&m_cParams, &m_rgParamInfo, &m_pNamesBuffer), S_OK);
TEST_CHECK(VerifyParamInfo(m_cBindings-cDeleteParams, &m_rgParamOrdinals[1], &m_rgDbParamBindInfo[1], m_cParams,
m_rgParamInfo, m_pNamesBuffer, 1), S_OK);
fResult = TRUE;
CLEANUP:
FreeDescParams();
// Set command text
if (!CHECK (m_pICommandText->SetCommandText(DBGUID_DBSQL , m_pwszSqlInsertAllWithParams), S_OK))
fResult = FALSE;
hr = m_pICmdWParams->SetParameterInfo(m_cDbParamBindInfo, m_rgParamOrdinals, m_rgDbParamBindInfo);
// We might have set typeinfo above or not depending upon where we jump from.
if (! (hr == S_OK || hr == DB_S_TYPEINFOOVERRIDDEN))
fResult = FALSE;
return (fResult) ? TEST_PASS : TEST_FAIL;
}
// }}
// {{ TCW_VAR_PROTOTYPE(13)
//*-----------------------------------------------------------------------
// @mfunc Call SetParameterInfo for Output Parameters.
//
// @rdesc TEST_PASS or TEST_FAIL
//
int TCSetParameterInfo_Rowset::Variation_13()
{
BOOL fResult = TRUE;
ULONG cParams, cParamSets = 1;
DBLENGTH cbRowSize = 0;
DBCOUNTITEM ulRowNum = 1;
DBBINDING * pBINDING=NULL;
DB_UPARAMS * pParamOrdinals=NULL;
DBPARAMBINDINFO * pPARAMBIND=NULL;
WCHAR * pwszCreateProcStmt=NULL;
WCHAR * pwszExecProcStmt=NULL;
WCHAR * pwszExecStmt=NULL;
WCHAR * pwszProcName=NULL;
BYTE * pData=NULL;
ParamStruct * pParamAll=NULL;
DB_LORDINAL * prgColumnsOrd = NULL;
BOOL fCanDerive = FALSE;
HRESULT hrSet = S_OK;
if (!m_fProcedureSupport)
{
odtLog << "Procedures not supported.\n";
return TEST_SKIPPED;
}
if (g_ulOutParamsSupported == DBPROPVAL_OA_NOTSUPPORTED)
{
odtLog << "Output parameters not supported.\n";
return TEST_SKIPPED;
}
// Create the syntax and binding for a stored proc with output parameters
ABORT_COMPARE(CreateProcBindings(
T_EXEC_PROC_SELECT_OUT, // [IN] Proc type, regular proc or function (has return value)
TRUE, // [IN] If TRUE then we add parameter names to the rgParamBindInfo
1, // [IN] Number of sets of parameters to be created
DBTYPE_I2, // [IN] Return parameter type
1, // [IN] Row number in table to select, insert, or update
&cParams, // [OUT] Count of params created
&cbRowSize, // [OUT] Count of bytes for a single row of parameters
&pBINDING, // [OUT] Binding array for CreateAccessor
&pParamOrdinals,
&pPARAMBIND, // [OUT] rgParamBindInfo for SetParameterInfo
&pwszCreateProcStmt, // [OUT] SQL stmt to create the stored proc
&pwszExecProcStmt, // [OUT] SQL stmt to execute the stored proc
&pwszExecStmt, // [OUT] SQL stmt to execute without stored proc
&pwszProcName, // [OUT] Name of procedure
&pData, // [OUT] Pointer to data for the parameters
&pParamAll
), TRUE);
// Set up to execute the stored proc
ABORT_CHECK(PrepareForExecute(pwszExecProcStmt, cParams, pParamOrdinals, pPARAMBIND,
&fCanDerive, pwszProcName, pwszCreateProcStmt), S_OK);
if (fCanDerive)
hrSet = DB_S_TYPEINFOOVERRIDDEN;
// Call SetParameterInfo without having called GetParameterInfo first
ABORT_CHECK(m_pICmdWParams->SetParameterInfo(cParams, pParamOrdinals, pPARAMBIND), hrSet);
FAIL_VAR(ExecuteAndVerify(cParams, cParamSets, pParamAll,ulRowNum, pBINDING, cbRowSize, pData, ROWSET_NONE,
0, NULL, VERIFY_USE_TABLE, TRUE), S_OK);
CLEANUP:
DropStoredProcedure(m_pICommandText, pwszProcName);
// Free the buffers we got from GetParameterInfo
PROVIDER_FREE(pBINDING);
PROVIDER_FREE(pParamOrdinals);
PROVIDER_FREE(pPARAMBIND);
PROVIDER_FREE(pwszCreateProcStmt);
PROVIDER_FREE(pwszExecProcStmt);
PROVIDER_FREE(pwszExecStmt);
PROVIDER_FREE(pwszProcName);
PROVIDER_FREE(pData);
::FreeParameterNames(cParams, pParamAll);
PROVIDER_FREE(pParamAll);
return fResult ? TEST_PASS : TEST_FAIL;
}
// }}
// {{ TCW_VAR_PROTOTYPE(14)
//*-----------------------------------------------------------------------
// @mfunc Call SetParameterInfo for Input/Output parameters.
//
// @rdesc TEST_PASS or TEST_FAIL
//
int TCSetParameterInfo_Rowset::Variation_14()
{
BOOL fResult = TRUE;
ULONG cParams, cParamSets = 1;
DBLENGTH cbRowSize = 0;
DBCOUNTITEM ulRowNum = 1;
DBBINDING * pBINDING=NULL;
DB_UPARAMS * pParamOrdinals=NULL;
DBPARAMBINDINFO * pPARAMBIND=NULL;
WCHAR * pwszCreateProcStmt=NULL;
WCHAR * pwszExecProcStmt=NULL;
WCHAR * pwszExecStmt=NULL;
WCHAR * pwszProcName=NULL;
BYTE * pData=NULL;
ParamStruct * pParamAll=NULL;
ULONG cColumns = 0;
DB_LORDINAL * prgColumnsOrd = NULL;
BOOL fCanDerive = FALSE;
HRESULT hrSet = S_OK;
if (!m_fProcedureSupport)
{
odtLog << "Procedures not supported.\n";
return TEST_SKIPPED;
}
if (g_ulOutParamsSupported == DBPROPVAL_OA_NOTSUPPORTED)
{
odtLog << "Output parameters not supported.\n";
return TEST_SKIPPED;
}
// Create the syntax and binding for a stored proc with output parameters
TEST_COMPARE(CreateProcBindings(
T_EXEC_PROC_SELECT_INOUT, // [IN] Proc type, regular proc or function (has return value)
TRUE, // [IN] If TRUE then we add parameter names to the rgParamBindInfo
1, // [IN] Number of sets of parameters to be created
DBTYPE_I2, // [IN] Return parameter type
1, // [IN] Row number in table to select, insert, or update
&cParams, // [OUT] Count of params created
&cbRowSize, // [OUT] Count of bytes for a single row of parameters
&pBINDING, // [OUT] Binding array for CreateAccessor
&pParamOrdinals,
&pPARAMBIND, // [OUT] rgParamBindInfo for SetParameterInfo
&pwszCreateProcStmt, // [OUT] SQL stmt to create the stored proc
&pwszExecProcStmt, // [OUT] SQL stmt to execute the stored proc
&pwszExecStmt, // [OUT] SQL stmt to execute without stored proc
&pwszProcName, // [OUT] Name of procedure
&pData, // [OUT] Pointer to data for the parameters
&pParamAll
), TRUE);
// Set up to execute the stored proc
ABORT_CHECK(PrepareForExecute(pwszExecProcStmt, cParams, pParamOrdinals, pPARAMBIND,
&fCanDerive, pwszProcName, pwszCreateProcStmt), S_OK);
if (fCanDerive)
hrSet = DB_S_TYPEINFOOVERRIDDEN;
// Call SetParameterInfo without having called GetParameterInfo first
ABORT_CHECK(m_pICmdWParams->SetParameterInfo(cParams, pParamOrdinals, pPARAMBIND), hrSet);
FAIL_VAR(ExecuteAndVerify(cParams, cParamSets, pParamAll,ulRowNum, pBINDING, cbRowSize, pData, ROWSET_NONE,
0, NULL, VERIFY_USE_PDATA, TRUE), S_OK);
CLEANUP:
DropStoredProcedure(m_pICommandText, pwszProcName);
// Free the buffers we got from GetParameterInfo
PROVIDER_FREE(pBINDING);
PROVIDER_FREE(pParamOrdinals);
PROVIDER_FREE(pPARAMBIND);
PROVIDER_FREE(pwszCreateProcStmt);
PROVIDER_FREE(pwszExecProcStmt);
PROVIDER_FREE(pwszExecStmt);
PROVIDER_FREE(pwszProcName);
PROVIDER_FREE(pData);
::FreeParameterNames(cParams, pParamAll);
PROVIDER_FREE(pParamAll);
return fResult ? TEST_PASS : TEST_FAIL;
}
// }}
// {{ TCW_VAR_PROTOTYPE(15)
//*-----------------------------------------------------------------------
// @mfunc Call SetParameterInfo for All DBTYPES in the SPEC and Verify.
//
// @rdesc TEST_PASS or TEST_FAIL
//
int TCSetParameterInfo_Rowset::Variation_15()
{
BOOL fResult = TRUE;
HRESULT hr;
ULONG iStdType;
DB_UPARAMS rgParamOrdinals[1] = {1};
// Delete all the information. DELETING Should not return DB_S_TYPEINFOOVERRIDDEN
TEST_CHECK(m_pICmdWParams->SetParameterInfo (0, NULL, NULL), S_OK);
TEST_CHECK(m_pICommandText->SetCommandText(DBGUID_DBSQL , m_pwszSqlInsertAllWithParams), S_OK);
TEST_CHECK(m_pICommandPrepare->Prepare(1), S_OK);
// For each standard type in the list attempt to set parameter information
for (iStdType =0; iStdType < g_cStdParams; iStdType++)
{
// Set the DBPARAMBINDINFO pointer to the proper type name
g_rgStdParamBindInfo[iStdType].rgParamBindInfo[0].pwszDataSourceType = g_rgStdParamBindInfo[iStdType].wszStdTypeName;
// Delete all the information. DELETING Should not return DB_S_TYPEINFOOVERRIDDEN
TEST_CHECK(m_pICmdWParams->SetParameterInfo (0, NULL, NULL), S_OK);
// Set it with the type so that we can verify back again.
hr = m_pICmdWParams->SetParameterInfo (1, rgParamOrdinals, g_rgStdParamBindInfo[iStdType].rgParamBindInfo);
// If we didn't succeed setting the name it should be because the type name wasn't supported
if (!SUCCEEDED(hr))
{
fResult &= CHECK(hr, DB_E_BADTYPENAME);
odtLog << L"Couldn't set standard type name for type: " << g_rgStdParamBindInfo[iStdType].wszStdTypeName << "\n\n";
// TODO: If the name wasn't set verify it's not a type in the provider types rowset
}
else
{
// Providers that can derive type information may return DB_S_TYPEINFOOVERRIDDEN here
if (!m_bSetParameterInfo)
CHECK(hr, DB_S_TYPEINFOOVERRIDDEN);
else
// Since we deleted parameter information above this should never be DB_S_TYPEINFOOVERRIDDEN
fResult &= CHECK(hr, S_OK);
// Compare contents. with set values.
// First call GetParameterInfo.
FreeDescParams();
TEST_CHECK (m_pICmdWParams->GetParameterInfo(&m_cParams, &m_rgParamInfo, &m_pNamesBuffer), S_OK);
// Per spec it's valid for providers to return the "best fit" type in GetParameterInfo. So if
// the types don't match it's not a failure, and we can't verify precision, scale, etc.
if (m_rgParamInfo [0].wType == g_rgStdParamBindInfo[iStdType].wType)
{
fResult &= COMPARE (m_rgParamInfo [0].bPrecision, g_rgStdParamBindInfo[iStdType].rgParamBindInfo[0].bPrecision);
fResult &= COMPARE (m_rgParamInfo [0].ulParamSize, g_rgStdParamBindInfo[iStdType].rgParamBindInfo[0].ulParamSize);
fResult &= COMPARE (m_rgParamInfo [0].bScale, g_rgStdParamBindInfo[iStdType].rgParamBindInfo[0].bScale);
}
else
{
odtLog << L"Warning: Type identifier didn't match for type " << g_rgStdParamBindInfo[iStdType].wszStdTypeName
<< L"\n";
odtLog << L"Skipping comparison of ulParamSize, bPrecision, and bScale.\n";
odtLog << L"Per spec this is not a failure because providers are allowed to return the 'best fit' type.\n\n";
}
// But we expect the flags to match regardless
fResult &= COMPARE (m_rgParamInfo [0].dwFlags, g_rgStdParamBindInfo[iStdType].rgParamBindInfo[0].dwFlags);
// TODO: Actually execute with standard type names set and verify data is retrieved properly.
}
}
CLEANUP:
// Set command text again
if (!CHECK (m_pICommandText->SetCommandText(DBGUID_DBSQL , m_pwszSqlInsertAllWithParams), S_OK))
fResult = FALSE;
FreeDescParams();
// RESET THE TYPEINFO correctly.
CHECK(m_pICmdWParams->SetParameterInfo(0, NULL, NULL), S_OK);
hr = m_pICmdWParams->SetParameterInfo(m_cDbParamBindInfo, m_rgParamOrdinals, m_rgDbParamBindInfo);
// We might have set typeinfo above or not depending upon where we jump from.
if (! (hr == S_OK || hr == DB_S_TYPEINFOOVERRIDDEN))
fResult = FALSE;
return (fResult) ? TEST_PASS : TEST_FAIL;
}
// }}
// {{ TCW_VAR_PROTOTYPE(16)
//*-----------------------------------------------------------------------
// @mfunc Call SetParameterInfo for Native types and Verify.
//
// @rdesc TEST_PASS or TEST_FAIL
//
int TCSetParameterInfo_Rowset::Variation_16()
{
ULONG cParams = 1;
HRESULT hr;
DB_UPARAMS rgParamOrdinals[1] = { 1 };
LPWSTR wszTypeInfo[] = { L"bit"};
DBTYPE dbTypeValues[] = { DBTYPE_BOOL };
WCHAR *pwszSqlSelectAllFromTbl=NULL;
odtLog << L"This variation only duplicates more complete testing done in many other variations.\n";
return TEST_SKIPPED;
DBPARAMBINDINFO rgParamBindInfo[] = { { NULL, NULL, 0, 0, 0, 0 } };
BOOL fResult = FALSE;
rgParamBindInfo[0].pwszDataSourceType = wszTypeInfo[0];
// Set command text
TEST_CHECK (m_pICommandText->SetCommandText(DBGUID_DBSQL , m_pwszSqlInsertAllWithParams), S_OK);
// RESET THE TYPEINFO correctly.
hr = m_pICmdWParams->SetParameterInfo(m_cDbParamBindInfo, m_rgParamOrdinals, m_rgDbParamBindInfo);
// Prepare
TEST_CHECK (m_pICommandPrepare->Prepare(1), S_OK);
// First remove all.
TEST_CHECK (m_pICmdWParams->SetParameterInfo(0, NULL, NULL), S_OK);
// Now set the Native types.
hr = m_pICmdWParams->SetParameterInfo (cParams, rgParamOrdinals, rgParamBindInfo);
if (hr != DB_S_TYPEINFOOVERRIDDEN)
{
if (hr != S_OK)
goto CLEANUP;
}
// Verify with get parameter info.
FreeDescParams();
hr = m_pICmdWParams->GetParameterInfo(&m_cParams, &m_rgParamInfo, &m_pNamesBuffer);
TEST_CHECK (hr, S_OK);
TEST_COMPARE (m_cParams, 1);
fResult = TRUE;
CLEANUP:
FreeDescParams();
// Set command text
if (!CHECK (m_pICommandText->SetCommandText(DBGUID_DBSQL , m_pwszSqlInsertAllWithParams), S_OK))
fResult = FALSE;
// RESET THE TYPEINFO correctly.
hr = m_pICmdWParams->SetParameterInfo(m_cDbParamBindInfo, m_rgParamOrdinals, m_rgDbParamBindInfo);
// We might have set typeinfo above or not depending upon where we jump from.
if (! (hr == S_OK || hr == DB_S_TYPEINFOOVERRIDDEN))
fResult = FALSE;
return (fResult) ? TEST_PASS : TEST_FAIL;
}
// }}
// {{ TCW_VAR_PROTOTYPE(17)
//*-----------------------------------------------------------------------
// @mfunc Open Rowset , and then call SetParameterInfo returns DB_E_OBJECTOPEN.
//
// @rdesc TEST_PASS or TEST_FAIL
//
int TCSetParameterInfo_Rowset::Variation_17()
{
BOOL fResult = FALSE;
ULONG cParams, cParamSets = 1;
DBLENGTH cbRowSize = 0;
DBCOUNTITEM ulRowNum = 1;
DBBINDING * pBINDING=NULL;
DB_UPARAMS * pParamOrdinals=NULL;
DBPARAMBINDINFO * pPARAMBIND=NULL;
WCHAR * pwszCreateProcStmt=NULL;
WCHAR * pwszExecProcStmt=NULL;
WCHAR * pwszExecStmt=NULL;
WCHAR * pwszProcName=NULL;
BYTE * pData=NULL;
ParamStruct * pParamAll=NULL;
DBORDINAL cColumns = 0;
DB_LORDINAL * prgColumnsOrd = NULL;
BOOL fCanDerive = FALSE;
HACCESSOR hParamAccessor = DB_NULL_HACCESSOR;
DBPARAMS ExecParams;
IRowset * pIRowset = NULL;
WCHAR * pwszStmt = NULL;
DBROWCOUNT cRowsAffected=0;
// Note: This variation previously used a stored proc, so it didn't work on providers that didn't
// support stored procs. Rewritten to use statement.
ABORT_COMPARE(CreateProcBindings(
T_EXEC_PROC_SELECT_IN,// [IN] Proc type, regular proc or function (has return value)
TRUE, // [IN] If TRUE then we add parameter names to the rgParamBindInfo
cParamSets, // [IN] Number of sets of parameters to be created
DBTYPE_I2, // [IN] Return parameter type
ulRowNum, // [IN] Row number in table to select, insert, or update
&cParams, // [OUT] Count of params created
&cbRowSize, // [OUT] Count of bytes for a single row of parameters
&pBINDING, // [OUT] Binding array for CreateAccessor
&pParamOrdinals,
&pPARAMBIND, // [OUT] rgParamBindInfo for SetParameterInfo
&pwszCreateProcStmt, // [OUT] SQL stmt to create the stored proc
&pwszExecProcStmt, // [OUT] SQL stmt to execute the stored proc
&pwszExecStmt, // [OUT] SQL stmt to execute without stored proc
&pwszProcName, // [OUT] Name of procedure
&pData, // [OUT] Pointer to data for the parameters
&pParamAll,
&cColumns,
&prgColumnsOrd
), TRUE);
// Set up to execute the statement
ABORT_CHECK(PrepareForExecute(pwszExecStmt, cParams, pParamOrdinals, pPARAMBIND,
&fCanDerive, NULL, NULL), S_OK);
// Create the parameter accessor
ABORT_CHECK (m_pCmdIAccessor->CreateAccessor(DBACCESSOR_PARAMETERDATA, cParams,
pBINDING, cbRowSize, &hParamAccessor, NULL), S_OK);
// Set the parameter info properly. This might return DB_S_TYPEINFOOVERRIDDEN or S_OK.
if (!fCanDerive)
ABORT_COMPARE(FAILED(m_pICmdWParams->SetParameterInfo(cParams, pParamOrdinals, pPARAMBIND)), FALSE);
ExecParams.hAccessor = hParamAccessor;
ExecParams.cParamSets = 1;
ExecParams.pData = pData;
// Execute the command, opening a rowset
ABORT_CHECK(m_pICommand->Execute(NULL, m_iidExec, &ExecParams, &cRowsAffected, (IUnknown **)&pIRowset), S_OK);
ABORT_CHECK (m_pICmdWParams->SetParameterInfo(cParams, pParamOrdinals,
pPARAMBIND), DB_E_OBJECTOPEN);
// If we got here then success
fResult = TRUE;
CLEANUP:
if (hParamAccessor != DB_NULL_HACCESSOR)
m_pCmdIAccessor->ReleaseAccessor(hParamAccessor, NULL);
SAFE_RELEASE(pIRowset);
PROVIDER_FREE(pBINDING);
PROVIDER_FREE(pParamOrdinals);
PROVIDER_FREE(pPARAMBIND);
PROVIDER_FREE(pwszCreateProcStmt);
PROVIDER_FREE(pwszExecProcStmt);
PROVIDER_FREE(pwszExecStmt);
PROVIDER_FREE(pwszProcName);
PROVIDER_FREE(pData);
::FreeParameterNames(cParams, pParamAll);
PROVIDER_FREE(pParamAll);
return (fResult) ? TEST_PASS : TEST_FAIL;
}
// }}
// {{ TCW_VAR_PROTOTYPE(18)
//*-----------------------------------------------------------------------
// @mfunc Set parameter for variable length char fields and execute
//
// @rdesc TEST_PASS or TEST_FAIL
//
int TCSetParameterInfo_Rowset::Variation_18()
{
BOOL fResult = FALSE;
// Regression case for 3146 (oledb).
fResult = ExecuteWithSetParamInfo(L"DBTYPE_LONGVARCHAR", DBTYPE_STR);
fResult = ExecuteWithSetParamInfo(L"DBTYPE_VARCHAR", DBTYPE_STR);
fResult = ExecuteWithSetParamInfo(L"DBTYPE_CHAR", DBTYPE_STR);
fResult = ExecuteWithSetParamInfo(L"DBTYPE_LONGVARBINARY", DBTYPE_BYTES);
fResult = ExecuteWithSetParamInfo(L"DBTYPE_VARBINARY", DBTYPE_BYTES);
fResult = ExecuteWithSetParamInfo(L"DBTYPE_BINARY", DBTYPE_BYTES);
return (fResult) ? TEST_PASS : TEST_FAIL;
}
// }}
// {{ TCW_VAR_PROTOTYPE(19)
//*-----------------------------------------------------------------------
// @mfunc SetParameterInfo with reverse rgParamOrdinals
//
// @rdesc TEST_PASS or TEST_FAIL
//
int TCSetParameterInfo_Rowset::Variation_19()
{
BOOL fResult = FALSE;
HRESULT hr = E_FAIL;
TEST_CHECK (m_pICmdWParams->SetParameterInfo(0, NULL, NULL), S_OK);
// Reset command text since another variation may have altered it.
TEST_CHECK(m_pTable->ExecuteCommand(INSERT_ALLWITHPARAMS, IID_IUnknown, NULL,
NULL, NULL, NULL, EXECUTE_NEVER, 0, NULL, NULL, NULL, &m_pICommand), S_OK);
// Prepare if supported
if (m_pICommandPrepare)
TEST_CHECK(m_pICommandPrepare->Prepare(1), S_OK);
// Populate the name value in m_rgDbParamBindInfo
SetParameterNames(m_pwszParameterNames);
ReverseArray(m_rgParamOrdinals, m_cDbParamBindInfo, sizeof(DB_UPARAMS));
ReverseArray(m_rgDbParamBindInfo, m_cDbParamBindInfo, sizeof(DBPARAMBINDINFO));
// Now set the reversed parameter info
hr = m_pICmdWParams->SetParameterInfo(m_cDbParamBindInfo, m_rgParamOrdinals, m_rgDbParamBindInfo);
if (hr != S_OK && hr != DB_S_TYPEINFOOVERRIDDEN)
TEST_CHECK(hr, S_OK);
TEST_CHECK (m_pICmdWParams->GetParameterInfo(&m_cParams, &m_rgParamInfo, &m_pNamesBuffer), S_OK);
TEST_CHECK(VerifyParamInfo(m_cDbParamBindInfo, m_rgParamOrdinals, m_rgDbParamBindInfo, m_cParams,
m_rgParamInfo, m_pNamesBuffer), S_OK);
fResult = TRUE;
CLEANUP:
FreeDescParams();
// Reset everything
ReverseArray(m_rgParamOrdinals, m_cDbParamBindInfo, sizeof(DB_UPARAMS));
ReverseArray(m_rgDbParamBindInfo, m_cDbParamBindInfo, sizeof(DBPARAMBINDINFO));
SetParameterNames(NULL);
CHECK(m_pICmdWParams->SetParameterInfo(m_cDbParamBindInfo, m_rgParamOrdinals, m_rgDbParamBindInfo), DB_S_TYPEINFOOVERRIDDEN);
return (fResult) ? TEST_PASS : TEST_FAIL;
}
// }}
// {{ TCW_VAR_PROTOTYPE(20)
//*-----------------------------------------------------------------------
// @mfunc SetParameterInfo with random rgParamOrdinals
//
// @rdesc TEST_PASS or TEST_FAIL
//
int TCSetParameterInfo_Rowset::Variation_20()
{
BOOL fResult = FALSE;
// Populate the name value in m_rgDbParamBindInfo
SetParameterNames(m_pwszParameterNames);
ScrambleArray(m_rgParamOrdinals, m_cDbParamBindInfo, sizeof(DB_UPARAMS));
ScrambleArray(m_rgDbParamBindInfo, m_cDbParamBindInfo, sizeof(DBPARAMBINDINFO));
// Now override the previous settings and call SetParameterInfo.
// Will return DB_S_TYPEINFOOVERRIDDEN because we called it once in Init.
TEST_CHECK (m_pICmdWParams->SetParameterInfo(m_cDbParamBindInfo, m_rgParamOrdinals, m_rgDbParamBindInfo),DB_S_TYPEINFOOVERRIDDEN);
TEST_CHECK (m_pICmdWParams->GetParameterInfo(&m_cParams, &m_rgParamInfo, &m_pNamesBuffer), S_OK);
TEST_CHECK(VerifyParamInfo(m_cDbParamBindInfo, m_rgParamOrdinals, m_rgDbParamBindInfo, m_cParams,
m_rgParamInfo, m_pNamesBuffer), S_OK);
fResult = TRUE;
CLEANUP:
FreeDescParams();
// Reset everything
ScrambleArray(m_rgParamOrdinals, m_cDbParamBindInfo, sizeof(DB_UPARAMS));
ScrambleArray(m_rgDbParamBindInfo, m_cDbParamBindInfo, sizeof(DBPARAMBINDINFO));
// Populate the name value in m_rgDbParamBindInfo
SetParameterNames(NULL);
CHECK(m_pICmdWParams->SetParameterInfo(m_cDbParamBindInfo, m_rgParamOrdinals, m_rgDbParamBindInfo), DB_S_TYPEINFOOVERRIDDEN);
return (fResult) ? TEST_PASS : TEST_FAIL;
}
// }}
// {{ TCW_VAR_PROTOTYPE(21)
//*-----------------------------------------------------------------------
// @mfunc SetParameterInfo with duplicate parameter name
//
// @rdesc TEST_PASS or TEST_FAIL
//
int TCSetParameterInfo_Rowset::Variation_21()
{
BOOL fResult = TRUE;
ULONG idxDupSource=0; // First parameter is the source of the duplicate name
DB_UPARAMS idxDupTarget=m_cDbParamBindInfo-1; // Last parameter is the duplicate
LPOLESTR pwszSavedName;
OLECHAR wszDupName[SP_MAX_PARAMNAME_LENGTH + 1];
HRESULT hr;
// Populate the name value in m_rgDbParamBindInfo
SetParameterNames(m_pwszParameterNames);
pwszSavedName=m_rgDbParamBindInfo[idxDupTarget].pwszName;
// There are two ways to duplicate a parameter name: 1) Pointer is to same name; 2) Pointer is different, but buffer
// contains the same name. Both are errors.
// Duplicate pointer
m_rgDbParamBindInfo[idxDupTarget].pwszName=m_rgDbParamBindInfo[idxDupSource].pwszName;
hr=m_pICmdWParams->SetParameterInfo(m_cDbParamBindInfo, m_rgParamOrdinals, m_rgDbParamBindInfo);
if (DB_E_BADPARAMETERNAME != hr)
{
if (!CHECK (hr,DB_S_TYPEINFOOVERRIDDEN))
fResult=FALSE;
}
// Duplicate buffer
wcscpy(wszDupName, m_rgDbParamBindInfo[idxDupSource].pwszName);
m_rgDbParamBindInfo[idxDupTarget].pwszName=(LPOLESTR)&wszDupName;
hr=m_pICmdWParams->SetParameterInfo(m_cDbParamBindInfo, m_rgParamOrdinals, m_rgDbParamBindInfo);
if (DB_E_BADPARAMETERNAME != hr)
{
if (!CHECK (hr,DB_S_TYPEINFOOVERRIDDEN))
fResult=FALSE;
}
// Reset everything
SetParameterNames(NULL);
CHECK(m_pICmdWParams->SetParameterInfo(m_cDbParamBindInfo, m_rgParamOrdinals, m_rgDbParamBindInfo), DB_S_TYPEINFOOVERRIDDEN);
return (fResult) ? TEST_PASS : TEST_FAIL;
}
// }}
// {{ TCW_VAR_PROTOTYPE(22)
//*-----------------------------------------------------------------------
// @mfunc SetParameterInfo with invalid parameter name
//
// @rdesc TEST_PASS or TEST_FAIL
//
int TCSetParameterInfo_Rowset::Variation_22()
{
BOOL fResult = TRUE;
DB_UPARAMS idxBadParam=m_cDbParamBindInfo-1; // We'll use the last parameter
LPOLESTR pwszSavedName;
OLECHAR wszBadName[SP_MAX_PARAMNAME_LENGTH + 1];
HRESULT hr;
// Some providers might not be able to validate parameter names, so they could return S_OK instead of DB_E_BADPARAMETERNAME.
SetParameterNames(m_pwszParameterNames);
pwszSavedName=m_rgDbParamBindInfo[idxBadParam].pwszName;
// Set one of the parameters to NULL. It's an error to have one unnamed parameter.
m_rgDbParamBindInfo[idxBadParam].pwszName=NULL;
hr=m_pICmdWParams->SetParameterInfo(m_cDbParamBindInfo, m_rgParamOrdinals, m_rgDbParamBindInfo);
if (DB_E_BADPARAMETERNAME != hr)
{
if (!CHECK (hr,DB_S_TYPEINFOOVERRIDDEN))
fResult=FALSE;
}
// Set one of the parameters to an empty string. I'm assuming this isn't a valid name.
wcscpy(wszBadName, L"");
m_rgDbParamBindInfo[idxBadParam].pwszName=(LPOLESTR)&wszBadName;
hr=m_pICmdWParams->SetParameterInfo(m_cDbParamBindInfo, m_rgParamOrdinals, m_rgDbParamBindInfo);
if (DB_E_BADPARAMETERNAME != hr)
{
if (!CHECK (hr,DB_S_TYPEINFOOVERRIDDEN))
fResult=FALSE;
}
// Set one of the parameters to a value containing a control character. I'm assuming this isn't a valid name.
wcscpy(wszBadName, pwszSavedName);
memset(wszBadName, 1, 1); // Set first character to Control-A
hr=m_pICmdWParams->SetParameterInfo(m_cDbParamBindInfo, m_rgParamOrdinals, m_rgDbParamBindInfo);
if (DB_E_BADPARAMETERNAME != hr)
{
if (!CHECK (hr,DB_S_TYPEINFOOVERRIDDEN))
fResult=FALSE;
}
// Reset everything
SetParameterNames(NULL);
CHECK(m_pICmdWParams->SetParameterInfo(m_cDbParamBindInfo, m_rgParamOrdinals, m_rgDbParamBindInfo), DB_S_TYPEINFOOVERRIDDEN);
return (fResult) ? TEST_PASS : TEST_FAIL;
}
// }}
// {{ TCW_VAR_PROTOTYPE(23)
//*-----------------------------------------------------------------------
// @mfunc Default Conversion: pwszDataSourceType == NULL
//
// @rdesc TEST_PASS or TEST_FAIL
//
int TCSetParameterInfo_Rowset::Variation_23()
{
UINT i;
DBCOUNTITEM ulRowNum;
DBPARAMBINDINFO *rgDbParamBindInfo = NULL;
DBROWCOUNT cRowsAffected = 0;
HRESULT hr;
BOOL fResult = TEST_FAIL;
IRowset * pRowset = NULL;
// Remove any old parameter info
ABORT_CHECK(m_pICmdWParams->SetParameterInfo(0, NULL, NULL), S_OK);
// Set command text
ABORT_CHECK (m_pICommandText->SetCommandText(DBGUID_DBSQL , m_pwszSqlInsertAllWithParams), S_OK);
// Make a copy of the parameter info
SAFE_ALLOC(rgDbParamBindInfo, DBPARAMBINDINFO, m_cDbParamBindInfo * sizeof(DBPARAMBINDINFO));
memcpy(rgDbParamBindInfo, m_rgDbParamBindInfo, (size_t)(m_cDbParamBindInfo * sizeof(DBPARAMBINDINFO)));
// Set all pwszDataSourceType's to NULL. This will return E_INVALIDARG if provider doesn't support
// default conversions.
for (i=0; i < m_cDbParamBindInfo; i++)
rgDbParamBindInfo[i].pwszDataSourceType = NULL;
// Set the parameter info
hr = m_pICmdWParams->SetParameterInfo(m_cDbParamBindInfo, m_rgParamOrdinals, rgDbParamBindInfo);
if (E_INVALIDARG == hr)
{
odtLog << L"This provider doesn't support default parameter conversions.\n";
fResult = TEST_SKIPPED;
goto CLEANUP;
}
// If the provider can derive we'll get DB_S_TYPEINFOOVERRIDDEN
if (DB_S_TYPEINFOOVERRIDDEN != hr)
ABORT_CHECK(hr, S_OK);
// Call ICommandPrepare.
if (m_pICommandPrepare)
ABORT_CHECK(m_pICommandPrepare->Prepare(1), S_OK);
MakeDataForCommand((ulRowNum = NextInsertRowNum()));
// Now execute ICommand.
ABORT_CHECK(m_pICommand->Execute(NULL, IID_NULL,
&m_DbParamsAll, &cRowsAffected, NULL), S_OK);
FAIL_COMPARE(cRowsAffected, 1);
FAIL_COMPARE(pRowset, NULL);
// Make sure the row was inserted. This provides data validation for updatable non-LONG columns
TEST_COMPARE(cRow.FindRow(ulRowNum, m_pTable), TRUE);
// Currently no providers actually support this feature, all return E_INVALIDARG above.
// TODO: Validate data for row so LONG columns are verified.
// TODO: Call GetParameterInfo and verify valid type names are returned.
fResult = TEST_PASS;
CLEANUP:
// RESET THE TYPEINFO correctly.
hr = m_pICmdWParams->SetParameterInfo(m_cDbParamBindInfo, m_rgParamOrdinals, m_rgDbParamBindInfo);
// We might have set typeinfo above or not depending upon where we jump from.
if (! (hr == S_OK || hr == DB_S_TYPEINFOOVERRIDDEN))
fResult = FALSE;
ReleaseDataForCommand();
PROVIDER_FREE(rgDbParamBindInfo) ;
return fResult;
}
// }} TCW_VAR_PROTOTYPE_END
// {{ TCW_VAR_PROTOTYPE(24)
//*-----------------------------------------------------------------------
// @mfunc Verify error on DBTYPE_NUMERIC with no precision
//
// @rdesc TEST_PASS or TEST_FAIL
//
int TCSetParameterInfo_Rowset::Variation_24()
{
// TO DO: Add your own code here
return TRUE;
}
// }} TCW_VAR_PROTOTYPE_END
// {{ TCW_TERMINATE_METHOD
//--------------------------------------------------------------------
// @mfunc TestCase Termination Routine
//
// @rdesc TRUE or FALSE
//
BOOL TCSetParameterInfo_Rowset::Terminate()
{
HRESULT hr;
// RESET THE TYPEINFO correctly.
if (m_pICmdWParams)
{
// We might have set typeinfo above or not depending upon where we jump from.
hr=m_pICmdWParams->SetParameterInfo(m_cDbParamBindInfo, m_rgParamOrdinals, m_rgDbParamBindInfo);
if (! (hr == S_OK || hr == DB_S_TYPEINFOOVERRIDDEN))
{
// Free parameter names array
FreeParameterNames(m_pwszParameterNames);
m_pwszParameterNames=NULL;
CICmdWParams::Terminate();
return FALSE;
}
}
// Free parameter names array
FreeParameterNames(m_pwszParameterNames);
m_pwszParameterNames=NULL;
// {{ TCW_TERM_BASECLASS_CHECK2
return(CICmdWParams::Terminate());
} // }}
// }}
// {{ TCW_TC_PROTOTYPE(TCExtendedErrors)
//*-----------------------------------------------------------------------
//| Test Case: TCExtendedErrors - Extended Errors
//| Created: 07/29/96
//*-----------------------------------------------------------------------
//--------------------------------------------------------------------
// @mfunc TestCase Initialization Routine
//
// @rdesc TRUE or FALSE
//
BOOL TCExtendedErrors::Init()
{
// {{ TCW_INIT_BASECLASS_CHECK
if(CICmdWParams::Init())
// }}
{
return TRUE;
}
return FALSE;
}
// {{ TCW_VAR_PROTOTYPE(1)
//*-----------------------------------------------------------------------
// @mfunc Valid GetParameterInfo calls with previous error object existing.
//
// @rdesc TEST_PASS or TEST_FAIL
//
int TCExtendedErrors::Variation_1()
{
BOOL fResult = FALSE;
HRESULT hr;
//For each method of the interface, first create an error object on
//the current thread, then try get S_OK from the ICommandWithParameters method.
//We then check extended errors to verify nothing is set since an
//error object shouldn't exist following a successful call.
// Set command text
TEST_CHECK(m_pICommandText->SetCommandText(DBGUID_DBSQL , m_pwszSqlInsertAllWithParams), S_OK);
// Call ICommandPrepare.
TEST_CHECK(m_pICommandPrepare->Prepare(1), S_OK);
//create an error object
m_pExtError->CauseError();
TEST_CHECK(hr=m_pICmdWParams->GetParameterInfo(&m_cParams, &m_rgParamInfo, &m_pNamesBuffer), S_OK);
//do extended error check following the GetParameterInfo
fResult = XCHECK(m_pICmdWParams, IID_ICommandWithParameters, hr);
CLEANUP:
FreeDescParams();
m_pICommandPrepare->Unprepare();
return (fResult) ? TEST_PASS : TEST_FAIL;
}
// }}
// {{ TCW_VAR_PROTOTYPE(2)
//*-----------------------------------------------------------------------
// @mfunc Invalid GetParameterInfo calls with previous error object existing
//
// @rdesc TEST_PASS or TEST_FAIL
//
int TCExtendedErrors::Variation_2()
{
HRESULT hr;
BOOL fResult = FALSE;
//For each method of the interface, first create an error object on
//the current thread, then try get a failure from the ICommandWithParameters method.
//We then check extended errors to verify the right extended error behavior.
FreeDescParams();
//create an error object
m_pExtError->CauseError();
if (CHECK(hr=m_pICmdWParams->GetParameterInfo(NULL, NULL, &m_pNamesBuffer),
E_INVALIDARG))
//Do extended check following GetParameterInfo
fResult = XCHECK(m_pICmdWParams, IID_ICommandWithParameters, hr);
FreeDescParams();
m_pICommandPrepare->Unprepare();
return (fResult) ? TEST_PASS : TEST_FAIL;
}
// }}
// {{ TCW_VAR_PROTOTYPE(3)
//*-----------------------------------------------------------------------
// @mfunc Invalid GetParameterInfo calls with no previous error object existing
//
// @rdesc TEST_PASS or TEST_FAIL
//
int TCExtendedErrors::Variation_3()
{
HRESULT hr;
BOOL fResult = FALSE;
ICommandWithParameters *pICommandWithParameters = NULL;
//For each method of the interface, with no error object on
//the current thread, then try get a failure from the ICommandWithParameters method.
//We then check extended errors to verify the right extended error behavior.
if (!VerifyInterface(m_pIEmptyCommand, IID_ICommandWithParameters, COMMAND_INTERFACE, (IUnknown **)&pICommandWithParameters))
{
goto CLEANUP;
}
if (m_bSetParameterInfo)
{
TEST_CHECK(hr=pICommandWithParameters->GetParameterInfo(&m_cParams, &m_rgParamInfo, &m_pNamesBuffer),
DB_E_PARAMUNAVAILABLE);
}
else
{
TEST_CHECK(hr=pICommandWithParameters->GetParameterInfo(&m_cParams, &m_rgParamInfo, &m_pNamesBuffer),
DB_E_NOTPREPARED);
}
//Do extended check following GetParameterInfo
fResult = XCHECK(m_pICmdWParams, IID_ICommandWithParameters, hr);
CLEANUP:
if (pICommandWithParameters)
pICommandWithParameters->Release();
m_pICommandPrepare->Unprepare();
return (fResult) ? TEST_PASS : TEST_FAIL;
}
// }}
// {{ TCW_VAR_PROTOTYPE(4)
//*-----------------------------------------------------------------------
// @mfunc Valid SetParameterInfo calls with previous error object existing.
//
// @rdesc TEST_PASS or TEST_FAIL
//
int TCExtendedErrors::Variation_4()
{
BOOL fResult = FALSE;
HRESULT hr;
//For each method of the interface, first create an error object on
//the current thread, then try get S_OK from the ICommandWithParameters method.
//We then check extended errors to verify nothing is set since an
//error object shouldn't exist following a successful call.
if (!m_bSetParameterInfo)
{
// IF SetParameterInfo is not set. set it anyway. for testing SetParameterInfo.
if (! CHECK (m_pICommandPrepare->Prepare(1), S_OK))
goto CLEANUP;
hr = m_pICmdWParams->SetParameterInfo(m_cDbParamBindInfo, m_rgParamOrdinals, m_rgDbParamBindInfo);
if (hr != DB_S_TYPEINFOOVERRIDDEN)
TEST_CHECK(hr, S_OK);
}
FreeDescParams();
//create an error object
m_pExtError->CauseError();
// Now remove SetParameterInfo.
TEST_CHECK (hr=m_pICmdWParams->SetParameterInfo (0, NULL, NULL), S_OK);
//Do extended check following SetParameterInfo
fResult = XCHECK(m_pICmdWParams, IID_ICommandWithParameters, hr);
fResult = TRUE;
CLEANUP:
FreeDescParams();
// RESET THE TYPEINFO correctly.
hr = m_pICmdWParams->SetParameterInfo(m_cDbParamBindInfo, m_rgParamOrdinals, m_rgDbParamBindInfo);
// We might have set typeinfo above or not depending upon where we jump from.
if (! (hr == S_OK || hr == DB_S_TYPEINFOOVERRIDDEN))
fResult &= FALSE;
return (fResult) ? TEST_PASS : TEST_FAIL;
}
//}}
// {{ TCW_VAR_PROTOTYPE(5)
//*-----------------------------------------------------------------------
// @mfunc Invalid SetParameterInfo calls with previous error object existing
//
// @rdesc TEST_PASS or TEST_FAIL
//
int TCExtendedErrors::Variation_5()
{
DBPARAMBINDINFO rgParamBindInfo[] = { { NULL, NULL, 0, 0, 0, 0 }, { NULL, NULL, 0, 0, 0, 0 } };
BOOL fResult = FALSE;
HRESULT hr;
LPWSTR wszTypeInfo[] = { L"DBTYPE_I1", L"DBTYPE_I2" };
ULONG cParams = 2;
//For each method of the interface, first create an error object on
//the current thread, then try get a failure from the ICommandWithParameters method.
//We then check extended errors to verify the right extended error behavior.
rgParamBindInfo[0].pwszDataSourceType = wszTypeInfo[0];
rgParamBindInfo[1].pwszDataSourceType = wszTypeInfo[1];
//create an error object
m_pExtError->CauseError();
TEST_CHECK (hr=m_pICmdWParams->SetParameterInfo (cParams, NULL, rgParamBindInfo), E_INVALIDARG);
//Do extended check following SetParameterInfo
fResult = XCHECK(m_pICmdWParams, IID_ICommandWithParameters, hr);
// RESET THE TYPEINFO correctly.
hr = m_pICmdWParams->SetParameterInfo(m_cDbParamBindInfo, m_rgParamOrdinals, m_rgDbParamBindInfo);
// We might have set typeinfo above or not depending upon where we jump from.
if (hr != DB_S_TYPEINFOOVERRIDDEN)
{
if (!CHECK(hr, S_OK))
fResult &= FALSE;
}
CLEANUP:
FreeDescParams();
return (fResult) ? TEST_PASS : TEST_FAIL;
}
// }}
// {{ TCW_VAR_PROTOTYPE(6)
//*-----------------------------------------------------------------------
// @mfunc Invalid SetParameterInfo calls with no previous error object existing
//
// @rdesc TEST_PASS or TEST_FAIL
//
int TCExtendedErrors::Variation_6()
{
DBPARAMBINDINFO rgParamBindInfo[] = { { NULL, NULL, 0, 0, 0, 0 }, { NULL, NULL, 0, 0, 0, 0 } };
BOOL fResult = FALSE;
HRESULT hr;
LPWSTR wszTypeInfo[] = { L"DBTYPE_I1", L"DBTYPE_I2" };
ULONG cParams = 2;
//For each method of the interface, with no error object on
//the current thread, then try get a failure from the ICommandWithParameters method.
//We then check extended errors to verify the right extended error behavior.
rgParamBindInfo[0].pwszDataSourceType = wszTypeInfo[0];
rgParamBindInfo[1].pwszDataSourceType = wszTypeInfo[1];
TEST_CHECK (hr=m_pICmdWParams->SetParameterInfo (cParams, NULL, rgParamBindInfo), E_INVALIDARG);
//Do extended check following SetParameterInfo
fResult = XCHECK(m_pICmdWParams, IID_ICommandWithParameters, hr);
// RESET THE TYPEINFO correctly.
hr = m_pICmdWParams->SetParameterInfo(m_cDbParamBindInfo, m_rgParamOrdinals, m_rgDbParamBindInfo);
// We might have set typeinfo above or not depending upon where we jump from.
if (hr != DB_S_TYPEINFOOVERRIDDEN)
{
if (!CHECK(hr, S_OK))
fResult &= FALSE;
}
CLEANUP:
FreeDescParams();
return (fResult) ? TEST_PASS : TEST_FAIL;
}
// }}
// {{ TCW_VAR_PROTOTYPE(7)
//*-----------------------------------------------------------------------
// @mfunc Valid MapParameterNames calls with previous error object existing.
//
// @rdesc TEST_PASS or TEST_FAIL
//
int TCExtendedErrors::Variation_7()
{
LONG cParamNames = 3;
WCHAR *rgParamNames[] = {L"ONE", L"TWO", L"THREE" };
DB_LPARAMS *rgParamOrdinals = NULL;
BOOL fResult = FALSE;
HRESULT hr;
//For each method of the interface, first create an error object on
//the current thread, then try get S_OK from the ICommandWithParameters method.
//We then check extended errors to verify nothing is set since an
//error object shouldn't exist following a successful call.
if ((rgParamOrdinals = (DB_LPARAMS *)m_pIMalloc->Alloc(cParamNames *sizeof (DB_LPARAMS))) == NULL)
{
odtLog << wszMemoryAllocationError;
return TEST_FAIL;
}
// May need to be prepared here
if (m_pICommandPrepare)
m_pICommandPrepare->Prepare(1);
//create an error object
m_pExtError->CauseError();
TEST_CHECK (hr=m_pICmdWParams->MapParameterNames(0, (const WCHAR **)rgParamNames, rgParamOrdinals), S_OK);
//do extended error check following the MapParameterNames
fResult = XCHECK(m_pICmdWParams, IID_ICommandWithParameters, hr);
CLEANUP:
if (rgParamOrdinals)
m_pIMalloc->Free(rgParamOrdinals);
return (fResult) ? TEST_PASS : TEST_FAIL;
}
// }}
// {{ TCW_VAR_PROTOTYPE(8)
//*-----------------------------------------------------------------------
// @mfunc Invalid MapParameterNames calls with previous error object existing
//
// @rdesc TEST_PASS or TEST_FAIL
//
int TCExtendedErrors::Variation_8()
{
LONG cParamNames = 3;
DB_LPARAMS *rgParamOrdinals = NULL;
BOOL fResult = FALSE;
HRESULT hr;
//For each method of the interface, first create an error object on
//the current thread, then try get a failure from the ICommandWithParameters method.
//We then check extended errors to verify the right extended error behavior.
if ((rgParamOrdinals = (DB_LPARAMS *)m_pIMalloc->Alloc(cParamNames *sizeof (DB_LPARAMS))) == NULL)
{
odtLog << wszMemoryAllocationError;
return TEST_FAIL;
}
for (LONG i = 0; i < cParamNames; i++ )
rgParamOrdinals[i] = i+1;
// May need to be prepared here
if (m_pICommandPrepare)
m_pICommandPrepare->Prepare(1);
//create an error object
m_pExtError->CauseError();
TEST_CHECK (hr=m_pICmdWParams->MapParameterNames(cParamNames, NULL, rgParamOrdinals), E_INVALIDARG);
//Do extended check following MapParameterNames
fResult = XCHECK(m_pICmdWParams, IID_ICommandWithParameters, hr);
CLEANUP:
if (rgParamOrdinals)
m_pIMalloc->Free(rgParamOrdinals);
return (fResult) ? TEST_PASS : TEST_FAIL;
}
// }}
// {{ TCW_VAR_PROTOTYPE(9)
//*-----------------------------------------------------------------------
// @mfunc Invalid MapParameterNames calls with no previous error object existing
//
// @rdesc TEST_PASS or TEST_FAIL
//
int TCExtendedErrors::Variation_9()
{
BOOL fResult = FALSE;
HRESULT hr;
LONG cParamNames = 3;
WCHAR *rgParamNames[] = {L"ONE", L"TWO", L"THREE" };
//For each method of the interface, with no error object on
//the current thread, then try get a failure from the ICommandWithParameters method.
//We then check extended errors to verify the right extended error behavior.
// May need to be prepared here
if (m_pICommandPrepare)
m_pICommandPrepare->Prepare(1);
if (CHECK (hr=m_pICmdWParams->MapParameterNames(cParamNames, (const WCHAR **)rgParamNames, NULL), E_INVALIDARG))
//Do extended check following MapParameterNames
fResult = XCHECK(m_pICmdWParams, IID_ICommandWithParameters, hr);
return (fResult) ? TEST_PASS : TEST_FAIL;
}
// }}
// {{ TCW_TERMINATE_METHOD
//--------------------------------------------------------------------
// @mfunc TestCase Termination Routine
//
// @rdesc TRUE or FALSE
//
BOOL TCExtendedErrors::Terminate()
{
// {{ TCW_TERM_BASECLASS_CHECK2
return(CICmdWParams::Terminate());
// }}
}
// }}
// }}
// {{ TCW_TC_PROTOTYPE(TCBugRegressions)
//*-----------------------------------------------------------------------
//| Test Case: TCBugRegressions - Test case to hold the regressions for bugs reported by other groups.
//| Created: 11/05/96
//*-----------------------------------------------------------------------
//--------------------------------------------------------------------
// @mfunc TestCase Initialization Routine
//
// @rdesc TRUE or FALSE
//
BOOL TCBugRegressions::Init()
{
// {{ TCW_INIT_BASECLASS_CHECK
if(CICmdWParams::Init())
// }}
{
return TRUE;
}
return FALSE;
}
// {{ TCW_VAR_PROTOTYPE(1)
//*-----------------------------------------------------------------------
// @mfunc Cmd that executes SP with param type adText returns truncation error if string > 255 chars
//
// @rdesc TEST_PASS or TEST_FAIL
//
int TCBugRegressions::Variation_1()
{
ULONG cParams = 1;
DB_UPARAMS rgParamOrdinals[] = { 1 };
LPWSTR wszTypeInfo[] = { L"DBTYPE_LONGVARCHAR" };
ULONG cTypes=2;
DBPARAMBINDINFO rgParamBindInfo[] = {
{ NULL, NULL, LONG_MAX, DBPARAMFLAGS_ISNULLABLE | DBPARAMFLAGS_ISINPUT, (BYTE)~0, (BYTE)~0 } };
BOOL fResult = FALSE;
ULONG i =0;
DBBINDING DbBindings[1];
void *pData = NULL;
DBPARAMS Params;
HRESULT hr;
VARIANT vBstrValue;
BSTR wszInputStr = L"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA";
HACCESSOR hAccessor = DB_NULL_HACCESSOR;
rgParamBindInfo[0].pwszDataSourceType = wszTypeInfo[0];
if (! (g_bSqlServer && ( g_ulOutParamsSupported != DBPROPVAL_OA_NOTSUPPORTED) ))
{
odtLog << g_wszNotSqlServerOrNoOutParams;
return TRUE;
}
// Init array to 0
memset(DbBindings, 0, sizeof(DBBINDING));
// First Drop the sp if exists.
TEST_CHECK (m_pICommandText->SetCommandText(DBGUID_DBSQL, L"Drop Procedure CmdWParamBug26SP;"), S_OK);
m_pICommandText->Execute(NULL, IID_NULL, NULL, NULL, NULL);
// Now create the Stored procedure.
TEST_CHECK (m_pICommandText->SetCommandText(DBGUID_DBSQL,
L"Create Procedure CmdWParamBug26SP (@ADOInParam text) AS RETURN(1)"), S_OK);
TEST_CHECK (m_pICommandText->Execute(NULL, IID_NULL, NULL, NULL, NULL), S_OK);
// Now set the execute SP statement.
TEST_CHECK (m_pICommandText->SetCommandText(DBGUID_DBSQL, L"{ call CmdWParamBug26SP (? ) }"), S_OK);
if (!SUCCEEDED(m_pICmdWParams->SetParameterInfo(0, NULL, NULL)))
goto CLEANUP;
hr = m_pICmdWParams->SetParameterInfo (cParams, rgParamOrdinals, rgParamBindInfo);
if (! (hr == S_OK || hr == DB_S_TYPEINFOOVERRIDDEN))
goto CLEANUP;
for (i = 0; i < cParams; i++)
{
DbBindings[i].dwPart = DBPART_VALUE | DBPART_LENGTH | DBPART_STATUS;
DbBindings[i].eParamIO = DBPARAMIO_INPUT;
DbBindings[i].iOrdinal = i+1; // Parameter Ordinal.
DbBindings[i].pTypeInfo = NULL;
DbBindings[i].cbMaxLen = LONG_MAX;
DbBindings[i].wType = DBTYPE_VARIANT;
DbBindings[i].dwMemOwner = DBMEMOWNER_CLIENTOWNED;
DbBindings[i].pBindExt = NULL;
DbBindings[i].bPrecision = 0;
DbBindings[i].bScale = 0;
}
VariantClear(&vBstrValue);
vBstrValue.vt = VT_BSTR;
V_BSTR(&vBstrValue) = wszInputStr;
// Change first parameter to input only.
DbBindings[0].eParamIO |= DBPARAMIO_INPUT;
DbBindings[0].obValue = offsetof(DATA, bValue) ;
DbBindings[0].obLength = offsetof(DATA, ulLength);
DbBindings[0].obStatus = offsetof (DATA, sStatus);
// Call create accessor.
TEST_CHECK (m_pCmdIAccessor->CreateAccessor(
DBACCESSOR_PARAMETERDATA, cParams, DbBindings, m_cbRowSize,
&hAccessor, NULL), S_OK);
// Allocate memory
pData = (void *)m_pIMalloc->Alloc(m_cbRowSize);
if (pData == NULL)
{
goto CLEANUP;
}
// 2. Create the data.
for (i=0; i < cParams; i++ )
{
memcpy((void *)((BYTE *)pData + DbBindings[i].obValue), (void *)&vBstrValue, sizeof (VARIANT));
// wcscpy (((WCHAR *)((BYTE *)pData + DbBindings[i].obValue)), wszInputStr);
*((DBLENGTH *)((BYTE *)pData + DbBindings[i].obLength)) = sizeof (VARIANT);
*((DBSTATUS *)((BYTE *)pData + DbBindings[i].obStatus)) = DBSTATUS_S_OK ;
}
Params.cParamSets = 1;
Params.hAccessor = hAccessor;
Params.pData = pData;
TEST_CHECK(m_pICommandText->Execute(NULL, IID_NULL,
&Params, NULL, NULL), S_OK);
fResult = TRUE;
//TEST_CHECK ( GetAccessorAndBindingsFromTypes(cParams, rgParamBindInfo, &hLocalAccessor, &pData), S_OK);
CLEANUP:
// Drop the stored proc we created above
if (!CHECK (m_pICommandText->SetCommandText(DBGUID_DBSQL, L"Drop Procedure CmdWParamBug26SP;"), S_OK))
fResult = FALSE;
m_pICommandText->Execute(NULL, IID_NULL, NULL, NULL, NULL);
FREE_DATA (pData);
return (fResult) ? TEST_PASS : TEST_FAIL;
}
// }}
// {{ TCW_VAR_PROTOTYPE(2)
//*-----------------------------------------------------------------------
// @mfunc Prepare, Execute, Prepare, Execute w/o rebinding
//
// @rdesc TEST_PASS or TEST_FAIL
//
int TCBugRegressions::Variation_2()
{
BOOL fResult = FALSE;
DBROWCOUNT cRowsAffected = 0;
DBCOUNTITEM ulRowNum;
// Remove any old parameter info
TEST_CHECK(m_pICmdWParams->SetParameterInfo(0, NULL, NULL), S_OK);
// Set command text
TEST_CHECK (m_pICommandText->SetCommandText(DBGUID_DBSQL , m_pwszSqlInsertAllWithParams), S_OK);
// Call ICommandPrepare.
TEST_CHECK (m_pICommandPrepare->Prepare(1), S_OK);
//This sets it to one row of data.
MakeDataForCommand((ulRowNum = NextInsertRowNum()));
// Now execute ICommand.
TEST_CHECK(m_pICommand->Execute(NULL, IID_NULL,
&m_DbParamsAll, &cRowsAffected, NULL), S_OK);
// Verify If we have inserted the row properly.
TEST_COMPARE(cRow.FindRow(ulRowNum, m_pTable), TRUE);
// Prepare again
TEST_CHECK (m_pICommandPrepare->Prepare(1), S_OK);
// Make data for the next valid row
MakeDataForCommand((ulRowNum = NextInsertRowNum()));
// Now execute ICommand.
TEST_CHECK(m_pICommand->Execute(NULL, IID_NULL,
&m_DbParamsAll, &cRowsAffected, NULL), S_OK);
// Verify If we have inserted the row properly.
TEST_COMPARE(cRow.FindRow(ulRowNum, m_pTable), TRUE);
fResult = TRUE;
CLEANUP:
ReleaseDataForCommand();
return (fResult) ? TEST_PASS : TEST_FAIL;
}
// }} TCW_VAR_PROTOTYPE_END
// {{ TCW_VAR_PROTOTYPE(3)
//*-----------------------------------------------------------------------
// @mfunc Retrieve a row after failing to retrieve with DBSTATUS_S_ISNULL
//
// @rdesc TEST_PASS or TEST_FAIL
//
int TCBugRegressions::Variation_3()
{
BOOL fResult = FALSE;
ULONG cParams, cParamSets = 1;
DBLENGTH cbRowSize = 0;
DBCOUNTITEM ulRowNum = 2;
DBBINDING * pBINDING=NULL;
DB_UPARAMS * pParamOrdinals=NULL;
DBPARAMBINDINFO * pPARAMBIND=NULL;
WCHAR * pwszCreateProcStmt=NULL;
WCHAR * pwszExecProcStmt=NULL;
WCHAR * pwszExecStmt=NULL;
WCHAR * pwszProcName=NULL;
BYTE * pData=NULL;
ParamStruct * pParamAll=NULL;
DBORDINAL cColumns = 0;
DB_LORDINAL * prgColumnsOrd = NULL;
BOOL fCanDerive = FALSE;
ULONG_PTR ulSqlSupport = DBPROPVAL_SQL_NONE;
DBSTATUS dbStatus = DBSTATUS_E_BADSTATUS;
DBPARAMS Param;
HACCESSOR hExecAccessor;
DBROWCOUNT cRowsAffected = 0;
DBCOUNTITEM cRowsObtained = 0;
IRowset * pIRowset = NULL;
DBCOUNTITEM cRowsInRowset = 0;
HRESULT hrSetProp = E_FAIL;
// Create the syntax and binding for a stored proc with input parameters
TESTC(CreateProcBindings(
T_EXEC_PROC_SELECT_IN, // [IN] Proc type, regular proc or function (has return value)
TRUE, // [IN] If TRUE then we add parameter names to the rgParamBindInfo
cParamSets, // [IN] Number of sets of parameters to be created
DBTYPE_I2, // [IN] Return parameter type
ulRowNum, // [IN] Row number in table to select, insert, or update
&cParams, // [OUT] Count of params created
&cbRowSize, // [OUT] Count of bytes for a single row of parameters
&pBINDING, // [OUT] Binding array for CreateAccessor
&pParamOrdinals,
&pPARAMBIND, // [OUT] rgParamBindInfo for SetParameterInfo
&pwszCreateProcStmt, // [OUT] SQL stmt to create the stored proc
&pwszExecProcStmt, // [OUT] SQL stmt to execute the stored proc
&pwszExecStmt, // [OUT] SQL stmt to execute without stored proc
&pwszProcName, // [OUT] Name of procedure
&pData, // [OUT] Pointer to data for the parameters
&pParamAll,
&cColumns,
&prgColumnsOrd
));
// Find out if we're a SQL provider
TESTC(GetProperty(DBPROP_SQLSUPPORT,
DBPROPSET_DATASOURCEINFO,m_pIDBInitialize, &ulSqlSupport));
TESTC_(m_pICmdWParams->SetParameterInfo(0, NULL, NULL), S_OK);
TESTC_(m_pICommandText->SetCommandText(DBGUID_DEFAULT, pwszExecStmt), S_OK);
// Set the status value for the first binding to DBSTATUS_S_ISNULL so query shouldn't retrieve any rows
dbStatus = STATUS_BINDING(pBINDING[0], pData);
STATUS_BINDING(pBINDING[0], pData) = DBSTATUS_S_ISNULL;
// Create a parameter accessor
TESTC_(m_pCmdIAccessor->CreateAccessor(DBACCESSOR_PARAMETERDATA, cParams, pBINDING, cbRowSize,
&hExecAccessor, NULL), S_OK);
Param.cParamSets = 1;
Param.hAccessor = hExecAccessor;
Param.pData = pData;
// Some providers can't retrieve BLOB data without this property or IRowsetLocate on
if (SupportedProperty(DBPROP_ACCESSORDER, DBPROPSET_ROWSET, m_pThisTestModule->m_pIUnknown,SESSION_INTERFACE))
hrSetProp = SetRowsetProperty(m_pICommand, DBPROPSET_ROWSET, DBPROP_ACCESSORDER, (LONG_PTR)DBPROPVAL_AO_RANDOM);
// Execute Command statement.
TESTC_(m_pICommand->Execute(NULL, IID_IRowset, &Param,
&cRowsAffected, (IUnknown **)&pIRowset),S_OK);
// If we made it to here we have a (possibly empty) rowset
TESTC_(VerifyObj(m_iidExec, pIRowset, ulRowNum, cColumns, prgColumnsOrd,
FALSE, TRUE, NULL, &cRowsInRowset), S_OK);
// Release the rowset pointer
SAFE_RELEASE(pIRowset);
// For SQL providers we expect this to return 0 rows if there were NULLS in the bindings,
// but it's not a failure if they succeed.
if (ulSqlSupport != DBPROPVAL_SQL_NONE)
TESTW(cRowsInRowset == 0);
else
TESTC(cRowsInRowset > 0);
// Now reset the binding correctly to see if we get the desired row
STATUS_BINDING(pBINDING[0], pData) = dbStatus;
// Execute again
TESTC_(m_pICommand->Execute(NULL, IID_IRowset, &Param,
&cRowsAffected, (IUnknown **)&pIRowset),S_OK);
// We should have the row
TESTC_(VerifyObj(m_iidExec, pIRowset, ulRowNum, cColumns, prgColumnsOrd,
FALSE, TRUE), S_OK);
fResult = TEST_PASS;
CLEANUP:
SAFE_RELEASE_ACCESSOR(m_pCmdIAccessor, hExecAccessor);
CHECK(ReleaseInputBindingsMemory(cParams, pBINDING, pData, FALSE), S_OK);
SAFE_RELEASE(pIRowset);
// If we set RANDOM REQUIRED above we need to set back OPTIONAL
if (hrSetProp == S_OK)
CHECK(SetRowsetProperty(m_pICommand, DBPROPSET_ROWSET, DBPROP_ACCESSORDER, (LONG_PTR)DBPROPVAL_AO_RANDOM, DBPROPOPTIONS_OPTIONAL), S_OK);
PROVIDER_FREE(pBINDING);
PROVIDER_FREE(pParamOrdinals);
PROVIDER_FREE(pPARAMBIND);
PROVIDER_FREE(pwszCreateProcStmt);
PROVIDER_FREE(pwszExecProcStmt);
PROVIDER_FREE(pwszExecStmt);
PROVIDER_FREE(pwszProcName);
PROVIDER_FREE(pData);
::FreeParameterNames(cParams, pParamAll);
PROVIDER_FREE(pParamAll);
PROVIDER_FREE(prgColumnsOrd);
return fResult;
}
// }} TCW_VAR_PROTOTYPE_END
// {{ TCW_VAR_PROTOTYPE(4)
//*-----------------------------------------------------------------------
// @mfunc Use int64 as input and output param binding in stored proc
//
// @rdesc TEST_PASS or TEST_FAIL
//
int TCBugRegressions::Variation_4()
{
BOOL fResult = TEST_FAIL;
ULONG cParams, cParamSets = 1;
DBLENGTH cbRowSize = 0;
DBCOUNTITEM ulRowNum = 2;
DBBINDING * pBINDING=NULL;
DB_UPARAMS * pParamOrdinals=NULL;
DBPARAMBINDINFO * pPARAMBIND=NULL;
WCHAR * pwszCreateProcStmt=NULL;
WCHAR * pwszExecProcStmt=NULL;
WCHAR * pwszExecStmt=NULL;
WCHAR * pwszProcName=NULL;
BYTE * pData=NULL;
ParamStruct * pParamAll=NULL;
DBORDINAL cColumns = 0;
DB_LORDINAL * prgColumnsOrd = NULL;
BOOL fCanDerive = FALSE;
IConvertType * pIConvType = NULL;
ULONG iParam;
DB_LORDINAL * pParamColOrd = NULL;
BYTE * pData64 = NULL;
BYTE * pData64Match = NULL;
DBBINDING * pBINDING64 = NULL;
DBLENGTH cbRowSize64 = 0;
ULONG ci64 = 0;
USHORT uSrcData = 0;
WCHAR wszData[MAXDATALEN] = L"";
HRESULT hrMakeData = E_FAIL;
HRESULT hrSetParam = E_FAIL;
HRESULT hrExec = S_OK;
void * pvData = NULL;
DB_UPARAMS cDerivedParams = 0;
DBPARAMINFO * pParamInfo = NULL;
if (!m_fProcedureSupport)
{
odtLog << "Procedures not supported.\n";
return TEST_SKIPPED;
}
if (g_ulOutParamsSupported == DBPROPVAL_OA_NOTSUPPORTED)
{
odtLog << "Output parameters not supported.\n";
return TEST_SKIPPED;
}
// Create the syntax and binding for a stored proc with output parameters
TESTC(CreateProcBindings(
T_EXEC_PROC_SELECT_OUT, // [IN] Proc type, regular proc or function (has return value)
TRUE, // [IN] If TRUE then we add parameter names to the rgParamBindInfo
cParamSets, // [IN] Number of sets of parameters to be created
DBTYPE_I2, // [IN] Return parameter type
ulRowNum, // [IN] Row number in table to select, insert, or update
&cParams, // [OUT] Count of params created
&cbRowSize, // [OUT] Count of bytes for a single row of parameters
&pBINDING, // [OUT] Binding array for CreateAccessor
&pParamOrdinals,
&pPARAMBIND, // [OUT] rgParamBindInfo for SetParameterInfo
&pwszCreateProcStmt, // [OUT] SQL stmt to create the stored proc
&pwszExecProcStmt, // [OUT] SQL stmt to execute the stored proc
&pwszExecStmt, // [OUT] SQL stmt to execute without stored proc
&pwszProcName, // [OUT] Name of procedure
&pData, // [OUT] Pointer to data for the parameters
&pParamAll,
&cColumns,
&prgColumnsOrd,
&pParamColOrd
));
// Make a copy of the original DBBINDING structure
SAFE_ALLOC(pBINDING64, DBBINDING, cParams);
memcpy(pBINDING64, pBINDING, cParams*sizeof(DBBINDING));
// We need to convert all eligible consumer bindings to I8, so get an IConvertType pointer.
TESTC(VerifyInterface(m_pICommand, IID_IConvertType, COMMAND_INTERFACE, (IUnknown**)&pIConvType));
// Go through each of the parameters and see if it will convert from I8 to native type.
cbRowSize64 = cbRowSize;
for (iParam = 0; iParam < cParams; iParam++)
{
HRESULT hrConv = E_FAIL;
if (pBINDING[iParam].eParamIO == DBPARAMIO_INPUT)
hrConv = pIConvType->CanConvert(DBTYPE_I8, pBINDING[iParam].wType, DBCONVERTFLAGS_PARAMETER);
else
hrConv = pIConvType->CanConvert(pBINDING[iParam].wType, DBTYPE_I8, DBCONVERTFLAGS_PARAMETER);
TEST2C_(hrConv, S_OK, S_FALSE);
// If we can convert, then this is an eligible binding
if (S_OK == hrConv)
{
// The conversion is supported, but not necessarily for every value.
// For example 'H123' won't convert to I2, but '123' will, etc. So we need
// to ensure the data value in the table will convert to I8.
WCHAR wszData64[MAXDATALEN] = L"";
BOOL fConv = FALSE;
// Get a string copy of the data.
hrMakeData = m_pTable->MakeData(wszData, ulRowNum, pParamAll[iParam].ulColIndex, PRIMARY);
TEST2C_(hrMakeData, S_OK, S_FALSE);
if (S_OK == hrMakeData)
{
// See if it will convert to I8
pvData = WSTR2DBTYPE(wszData, DBTYPE_I8, &uSrcData);
if (pvData)
{
// It's also important that we didn't truncate fractional digits or otherwise change
// the data during the conversion. To see if this is so, convert the I8 to a wstr,
// it should match the original data string.
_i64tow(*(LONGLONG *)pvData, wszData64, 10);
if (!wcscmp(wszData, wszData64))
{
// The values matched, we didn't lop off decimals, etc.
fConv = TRUE;
SAFE_FREE(pvData);
}
}
}
else
// Must be NULL. We can always retrieve a NULL into an int64.
fConv = TRUE;
if (fConv)
{
// Change the data type in the binding
pBINDING64[iParam].wType = DBTYPE_I8;
ci64++;
}
}
}
// See if there's at least one eligible binding
if (!ci64)
{
odtLog << L"No data types available that contain i64 convertible data.\n";
fResult = TEST_SKIPPED;
goto CLEANUP;
}
else
odtLog << L"There are now " << ci64 << " int64 bindings.\n";
// Repack the obValue, obLength, and obStatus based on the new bindings
Repack(cParams, pBINDING64, &cbRowSize64);
// Create a new pData based on the new bindings
TESTC_(FillInputBindings(m_pTable, DBACCESSOR_PARAMETERDATA, cParams,
pBINDING64, &pData64, ulRowNum, cParams, pParamColOrd, PRIMARY), S_OK);
// Make a copy of the param data that will contain valid data for out params to compare
// with.
SAFE_ALLOC(pData64Match, BYTE, cbRowSize64);
memcpy(pData64Match, pData64, (size_t)cbRowSize64);
// Now replace with converted data
for (iParam = 0; iParam < cParams; iParam++)
{
// We have an I8 binding, convert the data
if (pBINDING64[iParam].wType == DBTYPE_I8)
{
// Just in case, free any other data in this binding
ReleaseInputBindingsMemory(1, &pBINDING64[iParam], pData64, FALSE);
// Get a string copy of the data.
hrMakeData = m_pTable->MakeData(wszData, ulRowNum, pParamAll[iParam].ulColIndex, PRIMARY);
if (S_OK == hrMakeData)
{
// Convert to I8
pvData = WSTR2DBTYPE(wszData, DBTYPE_I8, &uSrcData);
TESTC(pvData != NULL);
// Put valid data in both match and param buffers
*(LONGLONG *)&VALUE_BINDING(pBINDING64[iParam], pData64Match) = *(LONGLONG *)pvData;
STATUS_BINDING(pBINDING64[iParam], pData64Match) = DBSTATUS_S_OK;
LENGTH_BINDING(pBINDING64[iParam], pData64Match) = GetDataSize(pBINDING64[iParam].wType,
uSrcData);
*(LONGLONG *)&VALUE_BINDING(pBINDING64[iParam], pData64) = *(LONGLONG *)pvData;
SAFE_FREE(pvData);
}
}
else
// Set valid length for other fixed length types
LENGTH_BINDING(pBINDING64[iParam], pData64Match) = GetDataSize(pBINDING64[iParam].wType,
LENGTH_BINDING(pBINDING64[iParam], pData64Match));
// Set output only params to garbage
if (pBINDING64[iParam].eParamIO == DBPARAMIO_OUTPUT)
{
STATUS_BINDING(pBINDING64[iParam], pData64) = OUT_PARAM_STATUS_INVALID;
LENGTH_BINDING(pBINDING64[iParam], pData64) = OUT_PARAM_LENGTH_INVALID;
memset(&VALUE_BINDING(pBINDING64[iParam], pData64),
0xCA,
(size_t)GetDataSize(pBINDING64[iParam].wType, pBINDING64[iParam].cbMaxLen));
}
}
// In order to test this you must do a partial SetParameterInfo.
// If you set all or none the bug won't repro. Note if we do a partial
// SetParameterInfo we can't set a parameter name because then we'd end up
// with some that have names and some that don't - error condition.
TESTC_(hrSetParam = m_pICmdWParams->SetParameterInfo(0, NULL, NULL), S_OK);
pPARAMBIND[0].pwszName = NULL;
hrSetParam = m_pICmdWParams->SetParameterInfo(1, &pParamOrdinals[0], &pPARAMBIND[0]);
TEST2C_(hrSetParam, S_OK, DB_S_TYPEINFOOVERRIDDEN);
if (hrSetParam == S_OK)
fCanDerive = FALSE;
else
fCanDerive = TRUE;
// Set up to execute the stored proc
if (pwszCreateProcStmt)
// Create the stored procedure
TESTC_(CreateStoredProc(m_pICommandText, pwszProcName, pwszCreateProcStmt, FALSE), S_OK);
// Set the command text to execute the stored proc
TESTC_(m_pICommandText->SetCommandText(DBGUID_DBSQL, pwszExecProcStmt), S_OK);
// If the provider can derive, then it's permissible for them to derive the additional
// parameter information. However, if they don't then we'll fail executing the proc.
if (fCanDerive)
{
TESTC_(m_pICmdWParams->GetParameterInfo(&cDerivedParams, &pParamInfo, NULL), S_OK);
// The provider shouldn't give me more params than there are.
COMPARE(cDerivedParams <= cParams, TRUE);
// But if there are less, then there must be only 1.
if (cDerivedParams < cParams)
{
COMPARE(cDerivedParams, 1);
// And Execute will fail because we don't know all the params.
// While I would prefer DB_E_ERRORSOCCURRED because we have invalid input
// param value, the spec doesn't seem to require this.
hrExec = E_FAIL;
}
}
TESTC_(ExecuteAndVerify(cParams, cParamSets, pParamAll, ulRowNum, pBINDING64, cbRowSize64,
pData64, ROWSET_NONE, cColumns, prgColumnsOrd, VERIFY_USE_PDATA, TRUE, NULL, hrExec,
NULL, pData64Match), S_OK);
fResult = TEST_PASS;
CLEANUP:
DropStoredProcedure(m_pICommandText, pwszProcName);
SAFE_RELEASE(pIConvType);
// Free the buffers we got from GetParameterInfo
PROVIDER_FREE(pParamInfo);
PROVIDER_FREE(pvData);
PROVIDER_FREE(pBINDING64);
PROVIDER_FREE(pData64);
PROVIDER_FREE(pData64Match);
PROVIDER_FREE(pParamColOrd);
PROVIDER_FREE(pBINDING);
PROVIDER_FREE(pParamOrdinals);
PROVIDER_FREE(pPARAMBIND);
PROVIDER_FREE(pwszCreateProcStmt);
PROVIDER_FREE(pwszExecProcStmt);
PROVIDER_FREE(pwszExecStmt);
PROVIDER_FREE(pwszProcName);
PROVIDER_FREE(pData);
::FreeParameterNames(cParams, pParamAll);
PROVIDER_FREE(pParamAll);
return fResult;;
}
// }} TCW_VAR_PROTOTYPE_END
// {{ TCW_TERMINATE_METHOD
//--------------------------------------------------------------------
// @mfunc TestCase Termination Routine
//
// @rdesc TRUE or FALSE
//
BOOL TCBugRegressions::Terminate()
{
// {{ TCW_TERM_BASECLASS_CHECK2
return(CICmdWParams::Terminate());
} // }}
// }}
// }}
// {{ TCW_TC_PROTOTYPE(TCIUnknown)
//*-----------------------------------------------------------------------
//| Test Case: TCIUnknown - Test parameter bindings to IUnknown
//| Created: 06/23/97
//*-----------------------------------------------------------------------
// Determines whether desireed IUnknown conversion should be successful for the given column
HRESULT TCIUnknown::CanConvertIUnknown(IUnknown * pIUnknown, DBTYPE wType, DBCONVERTFLAGS dwConvertFlags)
{
HRESULT hr=S_FALSE;
ULONG cColumns=0;
IConvertType * pIConvertType=NULL;
IColumnsInfo * pIColumnsInfo=NULL;
DBCOLUMNINFO * prgInfo=NULL;
LPWSTR pStringsBuffer=NULL;
// Interface could be a command or rowset interface
pIUnknown->QueryInterface(IID_IConvertType, (void **)&pIConvertType);
if (!pIConvertType)
return E_NOINTERFACE;
pIUnknown->QueryInterface(IID_IColumnsInfo, (void **)&pIColumnsInfo);
if (!pIColumnsInfo)
return E_NOINTERFACE;
switch (dwConvertFlags)
{
case DBCONVERTFLAGS_PARAMETER:
hr = pIConvertType->CanConvert(DBTYPE_IUNKNOWN, wType, dwConvertFlags);
break;
case DBCONVERTFLAGS_COLUMN:
hr = pIConvertType->CanConvert(wType, DBTYPE_IUNKNOWN, dwConvertFlags);
break;
default:
hr = E_FAIL;
}
pIConvertType->Release();
pIColumnsInfo->Release();
FREE_DATA(prgInfo);
FREE_DATA(pStringsBuffer);
return hr;
}
BOOL TCIUnknown::IsBLOB(DBTYPE wType)
{
switch(wType)
{
case DBTYPE_STR:
case DBTYPE_WSTR:
case DBTYPE_BYTES:
return TRUE;
default:
return FALSE;
}
}
LPVOID TCIUnknown::MakeData(CCol ColInfo, ULONG * pcbDataSize, ULONG ulSeed, enum EVALUE eValue)
{
LPWSTR pwszDataItem=NULL;
LPVOID pvData=NULL;
DBTYPE wType = ColInfo.GetProviderType();
ULONG cbSrcData=0;
USHORT uSrcData=0;
// Allocate a WCHAR buffer large enough to hold a data item
TEST_ALLOC(WCHAR, pwszDataItem, 0, (size_t)(DisplaySize(ColInfo)+sizeof(WCHAR)));
// Make a data item for this column
TEST_CHECK(m_pTable->MakeData(pwszDataItem, ulSeed, ColInfo.GetColNum(), eValue, wType, TRUE), S_OK);
// Convert the char item to the appropriate type for this column.
// Note uSrcData only used for DBTYPE_BYTES
pvData = WSTR2DBTYPE(pwszDataItem, wType, &uSrcData);
switch(wType)
{
case DBTYPE_BYTES:
case DBTYPE_VARNUMERIC:
cbSrcData = uSrcData;
break;
case DBTYPE_STR:
cbSrcData = (ULONG)strlen((CHAR *)pvData);
break;
case DBTYPE_WSTR:
cbSrcData = (ULONG)wcslen(pwszDataItem)*sizeof(WCHAR);
break;
default:
cbSrcData = GetDBTypeSize(wType);
break;
}
CLEANUP:
FREE_DATA(pwszDataItem);
if (pcbDataSize)
*pcbDataSize = cbSrcData;
return pvData;
}
HRESULT TCIUnknown::CreateStorageObject(REFIID iidObject, IUnknown ** ppIObject)
{
if (!ppIObject)
goto CLEANUP;
if (IID_ISequentialStream == iidObject)
*ppIObject = new ICmdWParSequentialStream();
else
{
*ppIObject = NULL;
goto CLEANUP;
}
TEST_PTR(*ppIObject);
return S_OK;
CLEANUP:
return E_FAIL;
}
BOOL TCIUnknown::IsLastByteLeadByte(LPVOID pvSrcData, ULONG cbSrcData, ULONG cbReadSize)
{
BYTE * pbSrcData = (BYTE *)pvSrcData;
ULONG iLast, iByte = 0;
// Adjust for read size greater or less than source buffer.
if (cbReadSize > cbSrcData)
iLast = cbReadSize % cbSrcData - 1;
else
iLast = cbReadSize - 1;
// Wrap to source buffer size
if (!(iLast + 1))
iLast+=cbSrcData;
while (iByte <= iLast)
{
if (IsDBCSLeadByte(pbSrcData[iByte]))
{
if (iByte == iLast)
return TRUE;
// Skip the trail byte
iByte++;
}
iByte++;
}
return FALSE;
}
TESTRESULT TCIUnknown::TestActiveError(enum ACTIVE_ERROR eTest, REFIID iidParamObject)
{
BOOL fResult = TRUE;
CCol ColInfo;
const WCHAR wszUpdateFormat[] = L"update %s set %s = ?";
const WCHAR wszSelectFormat[] = L"select %s from %s";
WCHAR wszCommand[MAX_COMMAND_BUF] = L"";
ULONG cbSrcData;
LPVOID pvSrcData=NULL;
ULONG iCol, cRefCount, cParams = 1, iRow;
DBROWCOUNT cRowsAffected;
IRowset * pIRowset=NULL;
IRowset * pIRowsetOpenObject=NULL;
IUnknown * pIParamObject=NULL;
HACCESSOR hParamAccessor;
IAccessor * pIAccessor=NULL;
ULONG_PTR ulStorageInterface;
DBPROPID ColumnStorageProperty;
DBACCESSORFLAGS dwAccessorFlags = DBACCESSOR_PARAMETERDATA;
IUnknown * pUnkOuter = NULL;
HRESULT hrExp = S_OK;
DBSTATUS ulInputStatus;
ULONG cRef = 0;
BOOL fRelease = TRUE;
DBLENGTH ulParamOffset = 0;
// If the provider supports IMultipleResults then we can't test this one
if (eTest == INVALIDARG_MULTRES)
{
// Actually we don't support this one yet because the framework expects
// to use an update and we need a select for this test.
// Testing not implemented yet.
return TEST_PASS;
}
if (eTest == NOAGGREGATION_OLEDB1)
{
// If this isn't an OLEDB 1.0 or 1.1 provider can't test
// For now, just return passed because we don't test
// 1.0 or 1.1 providers
// Testing not implemented yet.
return TEST_PASS;
}
if (eTest == OBJECTOPEN)
{
// If this provider does not support DBPROP_MULTIPLECONNECTIONS,
// then it will not return DB_E_OBJECTOPEN on Execute. Need to
// check the property and expect Execute to succeed. Otherwise
// if the prop is supported then set off. Need to add code to
// do that.
// Testing not implemented yet.
return TEST_PASS;
}
// If the provider supports IMultipleResults then we can't test this one
if (eTest == BADBINDINFO)
{
// Testing not implemented yet.
return TEST_PASS;
}
// Structure for parameter and row buffers for IUnknown and in-line data
struct tagParameterData {
DBLENGTH ulDataSize;
DBSTATUS ulStatus;
union {
IUnknown * pIUnknown;
BYTE bData[MAX_DATA_BUF+1];
};
} *pParameterData;
// Storage object info
DBOBJECT ParamStorageObject;
// Common client parameter binding info
DBBINDING rgParamBind[1];
rgParamBind[0].iOrdinal =1;
if (eTest == BADORDINAL)
rgParamBind[0].iOrdinal =100;
rgParamBind[0].obLength =ulParamOffset;
ulParamOffset += sizeof(DBLENGTH);
rgParamBind[0].obStatus =ulParamOffset;
ulParamOffset += sizeof(DBSTATUS);
ulParamOffset = ROUND_UP(ulParamOffset, ROUND_UP_AMOUNT);
rgParamBind[0].obValue =ulParamOffset;
rgParamBind[0].pTypeInfo=NULL;
rgParamBind[0].pObject =&ParamStorageObject;
rgParamBind[0].pBindExt =NULL;
rgParamBind[0].dwPart =DBPART_VALUE | DBPART_LENGTH | DBPART_STATUS;
rgParamBind[0].dwMemOwner=DBMEMOWNER_CLIENTOWNED;
rgParamBind[0].eParamIO =DBPARAMIO_INPUT;
rgParamBind[0].cbMaxLen =0;
rgParamBind[0].dwFlags =0;
rgParamBind[0].wType =DBTYPE_IUNKNOWN;
rgParamBind[0].bPrecision=0;
rgParamBind[0].bScale =0;
// Backend parameter binding info
DB_UPARAMS rgParamOrdinals[1]={1};
DBPARAMBINDINFO rgParamBindInfo[1];
rgParamBindInfo[0].pwszDataSourceType = NULL;
rgParamBindInfo[0].pwszName = NULL;
rgParamBindInfo[0].ulParamSize = 0;
rgParamBindInfo[0].dwFlags = DBPARAMFLAGS_ISINPUT;
rgParamBindInfo[0].bPrecision = 0;
rgParamBindInfo[0].bScale = 0;
SAFE_ALLOC(pParameterData, struct tagParameterData, 1);
// Assume failure until proven otherwise
m_TestResult=TEST_PASS;
// Set the property values we need to use
if (iidParamObject==IID_ISequentialStream)
{
ulStorageInterface=DBPROPVAL_SS_ISEQUENTIALSTREAM;
ColumnStorageProperty = DBPROP_ISequentialStream;
}
else if (iidParamObject==IID_ILockBytes)
{
ulStorageInterface=DBPROPVAL_SS_ILOCKBYTES;
ColumnStorageProperty = DBPROP_ILockBytes;
}
else if (iidParamObject==IID_IStorage)
{
ulStorageInterface=DBPROPVAL_SS_ISTORAGE;
ColumnStorageProperty = DBPROP_IStorage;
}
else if (iidParamObject==IID_IStream)
{
ulStorageInterface=DBPROPVAL_SS_ISTREAM;
ColumnStorageProperty = DBPROP_IStream;
}
else
{
// Not an object we know how to test
return TEST_FAIL;
}
// Make sure this property (interface) is supported by the provider (data source)
if (!(m_fSupportInterface = (m_ulStorageSupport & ulStorageInterface) > 0))
odtLog << L"Storage object is not supported.\n";
// Variation specific information
ParamStorageObject.iid = iidParamObject;
// Set the iid to an invalid value
if (eTest == NOINTERFACE)
ParamStorageObject.iid = IID_IRowset;
ParamStorageObject.dwFlags = STGM_READ;
if (eTest == BADSTORAGEFLAGS_ERR)
ParamStorageObject.dwFlags = STGM_INVALID;
// Create a parameter accessor for this parameter
if (eTest == BADACCESSORTYPE)
dwAccessorFlags = DBACCESSOR_ROWDATA;
m_hr = m_pIAccessor->CreateAccessor(dwAccessorFlags, 1, rgParamBind, sizeof(struct tagParameterData),
&hParamAccessor, m_rgStatus);
// If the provider caught the error at CreateAccessor time we can't test Execute
if (FAILED(m_hr))
{
fResult = TEST_PASS;
goto CLEANUP;
}
// Create a DBPARAMS structure to pass to execute later
DBPARAMS Params;
Params.pData=pParameterData;
Params.cParamSets=1;
Params.hAccessor=hParamAccessor;
// Pick a column to be used for this test
// Note that a column or parameter need not be specified as DBCOLUMNFLAGS_ISLONG
// to allow the use of structured storage.
for (iCol=1; iCol <= m_pTable->CountColumnsOnTable(); iCol++)
{
ULONG cbReadSize;
// Free the data buffers for this column
FREE_DATA(pvSrcData);
// Retrieve the information about this column
ABORT_CHECK(m_pTable->GetColInfo(iCol, ColInfo), S_OK);
// Pick the appropriate column
if (!ColInfo.GetUpdateable())
continue;
// If this is a key column then we can't do an update of the whole column to the same value.
if (iCol == m_pTable->GetIndexColumn())
continue;
// For UNSUPPORTEDCONVERVERSION pick a column that won't convert from IUnknown
if (eTest == UNSUPPORTEDCONVERSION)
{
if (ColInfo.GetIsLong())
continue;
if (S_OK == CanConvertIUnknown(m_pICommand, ColInfo.GetProviderType(),
DBCONVERTFLAGS_PARAMETER))
continue;
}
else if (!ColInfo.GetIsLong())
continue;
// Make a data item appropriate for this column
pvSrcData = MakeData(ColInfo, &cbSrcData, 1);
TEST_PTR(pvSrcData);
// Set the read size
// Use the data size, except for long data use MAX_LONG_VALUE
// This way we can test sending large parameters in pieces via our ISequentialStream
// implementation.
cbReadSize = cbSrcData;
if (ColInfo.GetColumnSize() > MAX_LONG_VALUE)
{
cbReadSize = MAX_LONG_VALUE; // Limit to something practical.
// For WSTR columns we must have an even amount
if (ColInfo.GetProviderType() == DBTYPE_WSTR)
{
if (cbReadSize % 2)
cbReadSize--;
}
// For STR columns we cannot split a DBCS character
if (ColInfo.GetProviderType() == DBTYPE_STR)
{
// See if this size would split a DBCS character and adjust if required.
// We will split a DBCS character if the last byte to be sent is a lead byte.
if (IsLastByteLeadByte(pvSrcData, cbSrcData, cbReadSize))
cbReadSize--;
}
}
// Get the storage object pointer. This will be addrefed, released, and therefore
// deleted by the provider during Execute. After Execute the pointer is not valid.
// Reference count must be 2 or greater to keep the object around after Execute
// Create the storage object
if (!SUCCEEDED(CreateStorageObject(iidParamObject, (IUnknown **)&pIParamObject)))
{
odtLog << L"Unable to create parameter storage object.\n";
goto CLEANUP;
}
// Initialize the storage object
if (IID_ISequentialStream == iidParamObject)
((ICmdWParSequentialStream *)pIParamObject)->Init(pvSrcData, cbSrcData, cbReadSize);
else
// Not a parameter object we know how to initialize
goto CLEANUP;
// Set the storage object pointer appropriately
pParameterData->pIUnknown = (IUnknown *)pIParamObject;
// Set the length of the data
pParameterData->ulDataSize = cbReadSize;
// Set the status on input
pParameterData->ulStatus = DBSTATUS_S_OK;
// update %s set %s = ?
swprintf(wszCommand, wszUpdateFormat, m_pTable->GetTableName(), ColInfo.GetColName());
// Set command text
ABORT_CHECK(m_pICommandText->SetCommandText(DBGUID_DBSQL, wszCommand), S_OK);
// Set the parameter information for this parameter
rgParamBindInfo[0].pwszDataSourceType = ColInfo.GetProviderTypeName();
// ulParamSize is cch for character types.
if (ColInfo.GetProviderType() == DBTYPE_WSTR)
rgParamBindInfo[0].ulParamSize = cbReadSize/sizeof(WCHAR);
else
rgParamBindInfo[0].ulParamSize = cbReadSize;
rgParamBindInfo[0].bPrecision = (BYTE)ColInfo.GetPrecision();
rgParamBindInfo[0].bScale = (BYTE)ColInfo.GetScale();
if (!SUCCEEDED(m_pICommandWithParameters->SetParameterInfo(1, rgParamOrdinals, rgParamBindInfo)))
goto CLEANUP;
// Reference count must be 2 or greater to keep the object around after Execute
// for non-NULL data.
COMPARE(pIParamObject->AddRef(), 2);
// Set common error conditions where possible
switch (eTest)
{
case INVALIDARG_NULL_PDATA:
Params.pData=NULL;
hrExp = E_INVALIDARG;
break;
case INVALIDARG_CPARAMSETS_ZERO:
Params.cParamSets=0;
hrExp = E_INVALIDARG;
break;
case BADACCESSORHANDLE:
Params.hAccessor=DB_NULL_HACCESSOR;
hrExp = DB_E_BADACCESSORHANDLE;
break;
case BADACCESSORTYPE:
// Nothing to do, rowdata accessor created above
hrExp = DB_E_BADACCESSORTYPE;
break;
case ERRORSOCCURRED:
// Set the status to a bad value
// TODO: We need more than one stream parameter and verify
// released for DBSTATUS_S_OK and not released otherwise.
odtLog << L"DB_E_ERRORSOCCURRED case not yet fully tested.\n";
pParameterData->ulStatus = DBSTATUS_E_BADSTATUS;
hrExp = DB_E_ERRORSOCCURRED;
break;
case NOINTERFACE:
// Nothing to do, invalid iid set above
hrExp = E_NOINTERFACE;
break;
case BADORDINAL:
// Nothing to do, ordinal 100 set above
hrExp = DB_E_BADORDINAL;
// For at least one case we know we will cause a crash on dll shutdown
// using this scenario. The workaround is to not release the storage
// object for this case, but post a failure.
if (GetModInfo()->GetClassContext() == CLSCTX_LOCAL_SERVER)
fRelease = FALSE;
break;
case BADSTORAGEFLAGS_ERR:
// Nothing to do, STGM_INVALID set above
hrExp = DB_E_BADSTORAGEFLAGS;
break;
case UNSUPPORTEDCONVERSION:
// Nothing to do, unsupported column picked above
hrExp = DB_E_UNSUPPORTEDCONVERSION;
break;
case NOAGGREGATION_NOT_IUNKOWN:
pUnkOuter=pIParamObject;
hrExp = DB_E_NOAGGREGATION;
break;
case NOCOMMAND:
ABORT_CHECK(m_pICommandText->SetCommandText(DBGUID_DBSQL, NULL), S_OK);
hrExp = DB_E_NOCOMMAND;
break;
case OBJECTOPEN:
// Open a rowset on the command
ABORT_CHECK (m_pTable->ExecuteCommand(SELECT_ALLFROMTBL, IID_IRowset, NULL, NULL,
NULL, NULL, EXECUTE_ALWAYS, 0, NULL, NULL, (IUnknown **)&pIRowsetOpenObject, &m_pICommand), S_OK);
hrExp = DB_E_OBJECTOPEN;
break;
case BADBINDINFO:
hrExp = DB_E_BADBINDINFO;
default:
// We don't understand any other tests
fResult = FALSE;
ABORT_COMPARE(TRUE, FALSE);
};
// Save the param input status
ulInputStatus = pParameterData->ulStatus;
// Perform the update.
// At this time we don't have a provider supporting anything but ISequentialStream, so
// we won't write consumer implementations of anything else. If a provider DOES support
// another interface, and DOESN'T bother to QI before using the interface passed in, then
// we will crash here. Otherwise QI will fail, and Execute will fail.
m_hr = m_pICommand->Execute(pUnkOuter, IID_IRowset, &Params, &cRowsAffected, (IUnknown **)&pIRowset);
// This is an error test and therefore should not succeed.
ABORT_CHECK(m_hr, hrExp);
// These are fatal errors and we don't expect our stream object
// to have been read
if (FAILED(m_hr))
{
FAIL_COMPARE(((ICmdWParSequentialStream *)pIParamObject)->WasRead(), FALSE);
}
// If the provider released the storage object, then after we release cRef == 0
// If provider didn't release then cRef == 1 after we release here.
if (fRelease)
cRef = pIParamObject->Release();
else
cRef = 0;
// If the ref count is 0 then don't attempt to delete object below
if (cRef == 0)
pIParamObject = NULL;
// Make sure the ref count is what we expect. Should be 1.
// Provider should not release if
// m_hr != DB_E_ERRORSOCCURRED or
// m_hr == DB_E_ERRORSOCCURRED and input status != DBSTATUS_S_OK
if (FAILED(m_hr) && ((m_hr == DB_E_ERRORSOCCURRED && ulInputStatus != DBSTATUS_S_OK) || m_hr != DB_E_ERRORSOCCURRED)
&& !COMPARE(cRef, 1))
{
odtLog << L"Provider released storage object.\n";
pIParamObject = NULL; // So we don't release again below
}
else
// Delete the storage object
SAFE_DELETE(pIParamObject);
// An update shouldn't open a rowset
COMPARE(pIRowset, NULL);
SAFE_RELEASE(pIRowset);
// Validate the return code. In some cases providers may return DB_E_ERRROSOCCURRED
// rather than the more descriptive error we might expect, for example DB_E_BADORDINAL,
// so we have to allow both.
switch(hrExp)
{
case E_NOINTERFACE:
case DB_E_BADBINDINFO:
case DB_E_BADORDINAL:
case DB_E_BADSTORAGEFLAGS:
case DB_E_UNSUPPORTEDCONVERSION:
if (m_hr == DB_E_ERRORSOCCURRED)
break;
default:
ABORT_CHECK(m_hr, hrExp);
}
// At this point we've done the test, break out of the column search loop
break;
} // Next iCol
CLEANUP:
// Reset the table to contain standard data by deleting all rows and reinserting
if (CHECK(m_pTable->Delete(ALLROWS), S_OK))
{
for (iRow = 0; iRow < TOTAL_NUMBER_OF_ROWS; iRow++)
{
CHECK(m_pTable->Insert(iRow+1), S_OK);
}
}
FREE_DATA(pvSrcData);
SAFE_RELEASE(pIRowsetOpenObject);
SAFE_RELEASE(pIRowset);
SAFE_DELETE(pIParamObject);
SAFE_FREE(pParameterData);
// Release the accessors now
m_pIAccessor->ReleaseAccessor(hParamAccessor, &cRefCount);
FAIL_COMPARE(cRefCount, 0);
return fResult ? TEST_PASS : TEST_FAIL;
}
BOOL TCIUnknown::MakeUnknownBinding(
DBCOUNTITEM iBind,
DBBINDING * pBind,
DB_UPARAMS * pParamOrdinals,
DBPARAMBINDINFO * pParamBindInfo,
LPBYTE pData,
DBOBJECT * pParamStorageObjects,
REFIID iidParamObject,
DWORD dwFlags,
DBLENGTH cbRowSize,
DB_UPARAMS cParamSets,
ULONG ulUpdateRow,
CCol ColInfo,
enum PARAM_OBJECT_TEST eTest
)
{
BOOL fResult = FALSE;
DB_UPARAMS iParamSet;
DBTYPE wBaseType = ColInfo.GetProviderType();
ICmdWParSequentialStream * pIParamObject=NULL;
LPVOID pvSrcData;
ULONG cbSrcData, cbReadSize = 0;
DBSTATUS sStatus = DBSTATUS_S_OK;
// At this time we only know how to deal with ISequentialStream objects
TESTC(IID_ISequentialStream == iidParamObject);
// Set the pObject in the binding structure
pBind[iBind].pObject = &pParamStorageObjects[iBind];
// Adjust the cbMaxLen
pBind[iBind].cbMaxLen = sizeof(IUnknown *);
// Set as DBTYPE_IUnknown
pBind[iBind].wType = DBTYPE_IUNKNOWN;
// Fill in the pObject itself
pParamStorageObjects[iBind].dwFlags = dwFlags;
pParamStorageObjects[iBind].iid = iidParamObject;
// Set the storage object pointer in the data structure
for (iParamSet = 0; iParamSet < cParamSets; iParamSet++)
{
// Make a data item appropriate for this column and row
pvSrcData = MakeData(ColInfo, &cbSrcData, ulUpdateRow + (ULONG)iParamSet, SECONDARY);
TEST_PTR(pvSrcData);
// Compute the read size
if (eTest == EMPTY_STORAGE_OBJECT)
cbReadSize = 0;
else
{
// Use the data size, except for long data use MAX_LONG_VALUE
// This way we can test sending large parameters in pieces via our ISequentialStream
// implementation.
cbReadSize = cbSrcData;
if (ColInfo.GetColumnSize() > MAX_LONG_VALUE)
{
cbReadSize = MAX_LONG_VALUE; // Limit to something practical.
// For WSTR columns we must have an even amount
if (wBaseType == DBTYPE_WSTR)
{
if (cbReadSize % 2)
cbReadSize--;
}
// For STR columns we cannot split a DBCS character
if (wBaseType == DBTYPE_STR)
{
// See if this size would split a DBCS character and adjust if required.
// We will split a DBCS character if the last byte to be sent is a lead byte.
if (IsLastByteLeadByte(pvSrcData, cbSrcData, cbReadSize))
cbReadSize--;
}
odtLog << L" Insert size set to " << cbReadSize << L" bytes.\n";
}
}
// A NULL storage object is legal, so don't create one for that case
if (NULL_STORAGE_OBJECT != eTest)
{
// Create the storage object itself
TESTC_(CreateStorageObject(iidParamObject, (IUnknown **)&pIParamObject), S_OK);
// Addref the param object so it doesn't get deleted by Execute later.
// Reference count must be 2 or greater to keep the object around after Execute
// for non-NULL data. For NULL data, the provider isn't allowed release the
// storage object
if (NULL_DATA != eTest)
{
// AddRef once so the object won't get destructed when the provider releases it
pIParamObject->AddRef();
// AddRef again so when we call Release on the object after Execute it won't
// destruct itself
pIParamObject->AddRef();
}
// Initialize this stream object to use this particular source buffer and
// allow the stream object to free the source buffer.
TESTC_(pIParamObject->Init(pvSrcData, cbSrcData, cbReadSize, TRUE), S_OK);
}
else
// Need to free the source data buffer because we can't free later
SAFE_FREE(pvSrcData);
// Set the status on input
if (eTest == NULL_DATA)
sStatus = DBSTATUS_S_ISNULL;
else if (eTest == DEFAULT_DATA)
sStatus = DBSTATUS_S_DEFAULT;
STATUS_BINDING(pBind[iBind], pData+iParamSet*cbRowSize) = sStatus;
// Set the length to the read size
LENGTH_BINDING(pBind[iBind], pData+iParamSet*cbRowSize) = cbReadSize;
// Set the value to the storage object
*(IUnknown **)&VALUE_BINDING(pBind[iBind], pData+iParamSet*cbRowSize) = pIParamObject;
}
// Fill in the parameter ordinal
pParamOrdinals[iBind] = iBind+1;
// Fill in the DBPARAMBINDINFO information
// ulParamSize is cch for character types.
if (ColInfo.GetProviderType() == DBTYPE_WSTR)
pParamBindInfo[iBind].ulParamSize = cbReadSize/sizeof(WCHAR);
else
pParamBindInfo[iBind].ulParamSize = cbReadSize;
pParamBindInfo[iBind].bPrecision = (BYTE)ColInfo.GetPrecision();
pParamBindInfo[iBind].bScale = (BYTE)ColInfo.GetScale();
fResult = TRUE;
CLEANUP:
return fResult;
}
TESTRESULT TCIUnknown::TestStorageObject(enum PARAM_OBJECT_TEST eTest, REFIID iidParamObject,
REFIID iidVerifyObject, DB_UPARAMS cParamSets)
{
BOOL fResult = FALSE;
CCol ColInfo;
ULONG cbSrcData = 0;
LPVOID pvSrcData=NULL;
LPVOID pvPrimaryData = NULL;
LPVOID pvReadBuf=NULL;
LPVOID pvCmpBuf=NULL; // Because we want to compare long data in pieces
DBORDINAL iCol;
ULONG cRefCount, cbReadProv, cbReadSrc;
DBCOUNTITEM cRowsObtained;
DBROWCOUNT cRowsAffected;
IRowset * pIRowset=NULL;
ISequentialStream * pISeqStreamRow=NULL;
ICmdWParSequentialStream * pIParamObject=NULL;
ICmdWParSequentialStream * pIParamObjectCompare=NULL;
HACCESSOR hRowAccessor = DB_NULL_HACCESSOR,
hParamAccessor = DB_NULL_HACCESSOR,
hRowAccessorInLine = DB_NULL_HACCESSOR;
HROW rghRows[1];
HROW * prghRows=rghRows;
UWORD iFlag;
DBSTATUS ulExpStatus;
IAccessor * pIAccessor=NULL;
HRESULT hrObject, hrInLine;
ULONG ulStorageInterface;
DBPROPID ColumnStorageProperty;
LPWSTR pwszDataItem = NULL;
ULONG ulUpdateRow = 1;
CCol WhereColInfo;
ULONG cStorageObjects = 0;
DBORDINAL cParams = 0;
ULONG cUpdateParams = 0, iRow;
LPWSTR pwszUpdateStmt = NULL;
LPWSTR pwszSelectStmt = NULL;
DB_LORDINAL * pParamColMap = NULL;
DBBINDING * pParamBindBase = NULL;
DBBINDING * pParamBind = NULL;
DBCOUNTITEM iBind, cParamBind = 0;// cUpdatableCols, cSearchableCols;
DBLENGTH cbParamRowSize = 0;
DB_UPARAMS iSet, iParam;
DB_UPARAMS * pParamOrdinals = NULL;
DBPARAMBINDINFO * pParamBindInfo = NULL;
DBOBJECT * pParamStorageObjects = NULL;
LPBYTE pData = NULL;
ParamStruct * pParamAll = NULL;
DBOBJECT VerifyStorageObject;
BOOL fTableHadNulls = FALSE;
IRowset ** ppRowset = &pIRowset;
ULONG cStorage = 0;
DBLENGTH ulRowOffset = 0;
VerifyStorageObject.iid = iidVerifyObject;
VerifyStorageObject.dwFlags=STGM_READ;
// Row binding info
DBBINDING rgRowBind[1];
rgRowBind[0].iOrdinal =1;
rgRowBind[0].obLength =ulRowOffset;
ulRowOffset += sizeof(DBLENGTH);
rgRowBind[0].obStatus =ulRowOffset;
ulRowOffset += sizeof(DBSTATUS);
ulRowOffset = ROUND_UP(ulRowOffset, ROUND_UP_AMOUNT);
rgRowBind[0].obValue =ulRowOffset;
rgRowBind[0].pTypeInfo =NULL;
rgRowBind[0].pBindExt =NULL;
rgRowBind[0].dwPart =DBPART_VALUE | DBPART_LENGTH | DBPART_STATUS;
rgRowBind[0].dwMemOwner =DBMEMOWNER_CLIENTOWNED;
rgRowBind[0].dwFlags =0;
rgRowBind[0].wType =DBTYPE_IUNKNOWN;
rgRowBind[0].bPrecision =0;
rgRowBind[0].bScale =0;
rgRowBind[0].pObject =&VerifyStorageObject;
rgRowBind[0].eParamIO =DBPARAMIO_NOTPARAM;
rgRowBind[0].cbMaxLen =MAX_DATA_BUF+1;
struct tagParameterData {
DBLENGTH ulDataSize;
DBSTATUS ulStatus;
union {
IUnknown * pIUnknown;
BYTE bData[MAX_DATA_BUF+1];
};
} * pRowData;
SAFE_ALLOC(pRowData, struct tagParameterData, 1);
if (cParamSets > 1 && !g_bMultipleParamSets)
{
odtLog << "Multiple parameter sets are not supported \n";
return TEST_SKIPPED;
}
// For cParamSets > 1 the table cannot have NULLS
if (m_pTable->GetNull() == USENULLS && cParamSets > 1)
{
// Record that table did have nulls
fTableHadNulls = TRUE;
// Make the table think it doesn't have NULLS
m_pTable->SetNull(NONULLS);
TESTC_(m_pTable->Delete(ALLROWS), S_OK);
for (iRow = 0; iRow < TOTAL_NUMBER_OF_ROWS; iRow++)
{
TESTC_(m_pTable->Insert(iRow+1), S_OK);
}
}
// For Remoting and Kagera, must set DBPROP_ACCESSORDER if multiple BLOB columns or
// only the BLOB at the end will be read properly.
if (g_bKagera && GetModInfo()->GetClassContext() == CLSCTX_LOCAL_SERVER)
{
TESTC_(SetRowsetProperty(m_pICommand, DBPROPSET_ROWSET, DBPROP_ACCESSORDER, (LONG_PTR)DBPROPVAL_AO_RANDOM), S_OK);
}
// We will be inserting SECONDARY data into the updatable columns. This is fine for
// those parameters that are for the column list of updatable cols, but we can't use
// SECONDARY data in the 'where' clause. To avoid this we need to limit the params used
// to exclude those in the 'where' clause, which basically means we use the first N
// updatable columns. Compute count of updatable columns.
for (iCol = 1; iCol <= m_pTable->CountColumnsOnTable(); iCol++)
{
TESTC_(m_pTable->GetColInfo(iCol, ColInfo), S_OK);
if (ColInfo.GetUpdateable())
cUpdateParams++;
}
TESTC_(m_pTable->CreateSQLStmt(UPDATE_WITH_PARAMS_WHERE, NULL, &pwszUpdateStmt,&cParams, &pParamColMap, ulUpdateRow), S_OK);
TESTC_(m_pTable->CreateSQLStmt(SELECT_VALIDATIONORDER, NULL, &pwszSelectStmt,NULL, NULL), S_OK);
// Allocate a base binding array that does not use IUknown bindings
SAFE_ALLOC(pParamBindBase, DBBINDING, cParams);
// Allocate a duplicate binding array that we can modify
SAFE_ALLOC(pParamBind, DBBINDING, cParams);
// Allocate an array of ordinals
SAFE_ALLOC(pParamOrdinals, DB_UPARAMS, cParams);
// Allocate DBPARAMBINDINFO
SAFE_ALLOC(pParamBindInfo, DBPARAMBINDINFO, cParams);
// Allocate ParamStruct that holds provider type name and other info
SAFE_ALLOC(pParamAll, ParamStruct, cParams);
// Allocate a buffer of DBOBJECTS to use
SAFE_ALLOC(pParamStorageObjects, DBOBJECT, cParams);
for (iParam = 0; iParam < cParams; iParam++)
{
TESTC(AddParam(iParam, pParamColMap[iParam], DBPARAMIO_INPUT, NULL, FALSE,
&cbParamRowSize, pParamBindBase, pParamBindInfo, pParamAll, m_pTable));
// Since later we will attempt to bind this parameter as a storage object we have
// to ensure there is adequate space for the IUnknown pointer.
if (pParamBindBase[iParam].cbMaxLen < sizeof(IUnknown *))
{
// Need to adjust space
cbParamRowSize += sizeof(IUnknown *) - pParamBindBase[iParam].cbMaxLen;
pParamBindBase[iParam].cbMaxLen = sizeof(IUnknown *);
}
pParamOrdinals[iParam] = iParam+1;
}
// Adjust for alignment
// cbParamRowSize = ROUND_UP(cbParamRowSize, ROUND_UP_AMOUNT);
Repack(cParams, pParamBindBase, &cbParamRowSize);
// Allocate a pData array of the appropriate size
SAFE_ALLOC(pData, BYTE, cbParamRowSize * cParamSets);
// Set the property values we need to use
if (iidParamObject==IID_ISequentialStream)
{
ulStorageInterface=DBPROPVAL_SS_ISEQUENTIALSTREAM;
ColumnStorageProperty = DBPROP_ISequentialStream;
}
else if (iidParamObject==IID_ILockBytes)
{
ulStorageInterface=DBPROPVAL_SS_ILOCKBYTES;
ColumnStorageProperty = DBPROP_ILockBytes;
}
else if (iidParamObject==IID_IStorage)
{
ulStorageInterface=DBPROPVAL_SS_ISTORAGE;
ColumnStorageProperty = DBPROP_IStorage;
}
else if (iidParamObject==IID_IStream)
{
ulStorageInterface=DBPROPVAL_SS_ISTREAM;
ColumnStorageProperty = DBPROP_IStream;
}
else
{
// Not an object we know how to test
return TEST_FAIL;
}
// Make sure this property (interface) is supported by the provider (data source)
if (!(m_fSupportInterface = (m_ulStorageSupport & ulStorageInterface) > 0))
odtLog << L"Storage object is not supported.\n";
// Get a pointer to the verification object
if (!SUCCEEDED(CreateStorageObject(iidVerifyObject, (IUnknown **)&m_pIUnkVerifyObj)))
{
odtLog << L"Unable to create verify storage object.\n";
goto CLEANUP;
}
// Try all possible storage mode flags
for (iFlag=0; iFlag < sizeof(g_StorageFlags)/sizeof(g_StorageFlags[0]); iFlag++)
{
// ISequentialStream doesn't use the Storage Flags so we only need to test
// the data for STGM_READ if using ISequentialStream. There's no sense in doing
// the same thing numerous times.
if (iidParamObject == IID_ISequentialStream && g_StorageFlags[iFlag].dwMode != STGM_READ)
continue;
odtLog << g_StorageFlags[iFlag].wszMode << L"\n";
// Create a DBPARAMS structure to pass to execute later
DBPARAMS Params;
// Row binding, copy param and modify
if (eTest == MULTIPLE_OBJECTS)
{
// Make a copy of the base binding structure to modify
memcpy(pParamBind, pParamBindBase, (size_t)(sizeof(DBBINDING) * cParams));
// Fill the bindings with valid data based on normal bindings
for (iSet = 0; iSet < cParamSets; iSet++)
{
LPBYTE pRow = pData+iSet*cbParamRowSize;
TESTC_(FillInputBindings(m_pTable, DBACCESSOR_PARAMETERDATA, cParams,
pParamBindBase, (BYTE **)&pRow,
ulUpdateRow + iSet, cParams, pParamColMap), S_OK);
}
}
// We need to verify for all data types (columns) provider supports
// Note that a column or parameter need not be specified as DBCOLUMNFLAGS_ISLONG
// to allow the use of structured storage, but many providers will require that
// anyway.
for (iBind=0; iBind < cUpdateParams; iBind++)
{
iCol = pParamColMap[iBind];
// Retrieve the information about this column
ABORT_CHECK(m_pTable->GetColInfo(iCol, ColInfo), S_OK);
// For multiple object testing we want to only include columns that will result
// in a successful stream binding, meaning BLOB columns, which some providers
// use as a synonym for LONG columns.
if (eTest == MULTIPLE_OBJECTS)
{
// Bind all BLOBS as storage
if (iBind < cUpdateParams-1)
{
// Must have a BLOB column
if (!ColInfo.GetIsLong())
continue;
odtLog << L" " << L" - " << ColInfo.GetProviderTypeName() << L"\n";
TESTC(MakeUnknownBinding(
iBind,
pParamBind,
pParamOrdinals,
pParamBindInfo,
pData,
pParamStorageObjects,
iidParamObject,
g_StorageFlags[iFlag].dwMode,
cbParamRowSize,
cParamSets,
ulUpdateRow,
ColInfo,
eTest));
cStorage++;
continue;
}
}
else
{
// Make a copy of the base binding structure to modify
memcpy(pParamBind, pParamBindBase, (size_t)(sizeof(DBBINDING) * cParams));
// Fill the bindings with valid data based on normal bindings
for (iSet = 0; iSet < cParamSets; iSet++)
{
LPBYTE pRow = pData+iSet*cbParamRowSize;
TESTC_(FillInputBindings(m_pTable, DBACCESSOR_PARAMETERDATA, cParams,
pParamBindBase, (BYTE **)&pRow,
ulUpdateRow + iSet, cParams, pParamColMap), S_OK);
}
odtLog << L" " << L" - " << ColInfo.GetProviderTypeName() << L"\n";
// Change this binding to be a storage object
TESTC(MakeUnknownBinding(
iBind,
pParamBind,
pParamOrdinals,
pParamBindInfo,
pData,
pParamStorageObjects,
iidParamObject,
g_StorageFlags[iFlag].dwMode,
cbParamRowSize,
cParamSets,
ulUpdateRow,
ColInfo,
eTest));
}
if (eTest == MULTIPLE_OBJECTS && cStorage <= 1)
{
odtLog << L"Only one BLOB column available for storage object.\n";
fResult = TEST_SKIPPED;
goto CLEANUP;
}
m_hr = m_pIAccessor->CreateAccessor(DBACCESSOR_PARAMETERDATA, cParams, pParamBind, cbParamRowSize,
&hParamAccessor, NULL);
// If requested interface is not supported then accessor validation may fail.
// This may be delayed until the accessor is used.
switch(m_hr)
{
case E_NOINTERFACE:
COMPARE(m_fSupportInterface, FALSE);
break;
case DB_E_ERRORSOCCURRED:
if (COMPARE(m_rgStatus[iBind],DBBINDSTATUS_BADSTORAGEFLAGS))
odtLog << L" Storage flag " << g_StorageFlags[iFlag].wszMode << L" is not supported for parameter accessors.\n";
break;
default:
CHECK(m_hr, S_OK);
}
// Set command text to update statement
TESTC_(m_pICommandText->SetCommandText(DBGUID_DBSQL, pwszUpdateStmt), S_OK);
m_hr = m_pICommandWithParameters->SetParameterInfo(cParams, pParamOrdinals, pParamBindInfo);
if (DB_S_TYPEINFOOVERRIDDEN != m_hr)
TESTC_(m_hr, S_OK);
// Set the params structure
Params.pData=pData;
Params.cParamSets=cParamSets;
Params.hAccessor=hParamAccessor;
// Perform the update.
// At this time we don't have a provider supporting anything but ISequentialStream, so
// we won't write consumer implementations of anything else. If a provider DOES support
// another interface, and DOESN'T bother to QI before using the interface passed in, then
// we will crash here. Otherwise QI will fail, and Execute will fail.
m_hr = m_pICommand->Execute(NULL, IID_IRowset, &Params, &cRowsAffected, (IUnknown **)ppRowset);
// An update shouldn't open a rowset
COMPARE(pIRowset, NULL);
SAFE_RELEASE(pIRowset);
// Validate the return code
switch(m_hr)
{
case E_NOINTERFACE:
// Interface is not supported
FAIL_COMPARE(m_fSupportInterface, FALSE);
break;
case DB_E_UNSUPPORTEDCONVERSION:
// The conversion wasn't valid
FAIL_COMPARE(S_OK != CanConvertIUnknown(m_pICommand, ColInfo.GetProviderType(),
DBCONVERTFLAGS_PARAMETER), TRUE);
break;
case DB_E_BADSTORAGEFLAGS:
// The Storage Mode flag was not supported or is invalid
odtLog << L" Storage flag " << g_StorageFlags[iFlag].wszMode <<
L" is not supported for parameter accessors.\n";
break;
case DB_E_ERRORSOCCURRED:
if (eTest == NULL_DATA && !ColInfo.GetNullable())
{
// The column wasn't nullable, check status
FAIL_COMPARE(STATUS_BINDING(pParamBind[iBind], pData), DBSTATUS_E_INTEGRITYVIOLATION);
}
else if (!IsBLOB(ColInfo.GetProviderType()))
{
// HACK!!
// Luxor reports conversion is supported for this case because they have a hack
// to support 0-length conversions "BY DESIGN".
if (!g_bLuxor || !(ColInfo.GetProviderType() == DBTYPE_VARIANT))
{
// The conversion wasn't valid so expect CanConvert to reflect that
FAIL_COMPARE(S_OK != CanConvertIUnknown(m_pICommand, ColInfo.GetProviderType(),
DBCONVERTFLAGS_PARAMETER), TRUE);
// We will allow fixed length types to error here
FAIL_COMPARE(STATUS_BINDING(pParamBind[iBind], pData), DBSTATUS_E_CANTCONVERTVALUE);
}
}
else
{
FAIL_CHECK(m_hr, S_OK);
}
break;
case E_FAIL:
// If the provider doesn't support reading non-LONG data via stream objects then
// this is the likely return code. Don't fail provider for this case because it's
// provider-specific.
if (!ColInfo.GetIsLong())
break;
// Fall through
default:
FAIL_CHECK(m_hr, S_OK);
}
// Free storage objects
// Release the accessor
SAFE_RELEASE_ACCESSOR(m_pIAccessor, hParamAccessor);
if (SUCCEEDED(m_hr))
{
ULONG iBind;
CCol ColInfo;
// We should have affected cParamSets rows
COMPARE(cRowsAffected, (DBROWCOUNT)cParamSets);
// Go through the bindings looking for the IUnknown ones
for (iBind=0; iBind < cUpdateParams; iBind++)
{
ULONG cbReadSize = 0;
// We will only verify the IUknown columns we updated at this time
if (pParamBind[iBind].wType != DBTYPE_IUNKNOWN)
continue;
// Validate data for this binding
iCol = pParamColMap[iBind];
// Retrieve the information about this column
ABORT_CHECK(m_pTable->GetColInfo(iCol, ColInfo), S_OK);
// Get the parameter stream object for this column
if (pParamBind[iBind].wType == DBTYPE_IUNKNOWN &&
STATUS_BINDING(pParamBind[iBind], pData) == DBSTATUS_S_OK)
pIParamObject = *(ICmdWParSequentialStream **)&VALUE_BINDING(pParamBind[iBind], pData);
else
pIParamObject = NULL;
if (NULL_STORAGE_OBJECT == eTest)
{
// We don't have a stream to get the expected cbSrcData, etc. from, but since this
// is the NULL storage object test we can just create it here. This
// will be padded with 0's or blanks if needed below.
pvSrcData = MakeData(ColInfo, &cbSrcData, ulUpdateRow);
TEST_PTR(pvSrcData);
TESTC_(CreateStorageObject(iidParamObject, (IUnknown **)&pIParamObject), S_OK);
if (NULL_DATA != eTest)
{
// AddRef once so the object won't get destructed when we release it
pIParamObject->AddRef();
}
// If the type will be padded by the provider we have to
// expect to read the full size
if (ColInfo.GetIsFixedLength())
cbReadSize = cbSrcData;
// Initialize this stream object to use this particular source buffer and
// allow the stream object to free the source buffer.
TESTC_(pIParamObject->Init(pvSrcData, cbSrcData, cbReadSize, TRUE), S_OK);
}
if (pIParamObject)
{
// Make sure the ref count is what we expect.
if (!COMPARE(pIParamObject->Release(), 1))
{
odtLog << L"Provider did not release storage object ";
if (FAILED(m_hr))
odtLog << L"on error.\n";
else
odtLog << L"on success.\n";
}
cbSrcData = pIParamObject->GetSrcBufferSize();
cbReadSize = pIParamObject->GetReadSize();
pvSrcData = pIParamObject->GetSrcBuffer();
// Rewind our source ISequentialStream to point to beginning
pIParamObject->Rewind();
}
// Allocate buffers to do reads and comparisons
// Use one more byte than needed to make sure we don't write past required length
TEST_ALLOC(BYTE, pvReadBuf, FILL_VALUE, cbSrcData+1);
TEST_ALLOC(BYTE, pvCmpBuf, FILL_VALUE, cbSrcData+1);
// Set the proper ordinal for the updated column
rgRowBind[0].iOrdinal = iCol;
rgRowBind[0].wType=DBTYPE_IUNKNOWN;
rgRowBind[0].bPrecision=0;
rgRowBind[0].bScale=0;
// Release any previously existing row accessor
SAFE_RELEASE_ACCESSOR(m_pIAccessor, hRowAccessor);
// Create a row accessor for this row
m_hr = m_pIAccessor->CreateAccessor(DBACCESSOR_ROWDATA, 1, rgRowBind, 0,
&hRowAccessor, m_rgStatus);
// If requested interface is not supported then accessor validation may fail.
// This may be delayed until the accessor is used.
switch(m_hr)
{
case E_NOINTERFACE:
COMPARE(m_fSupportInterface, FALSE);
break;
case DB_E_ERRORSOCCURRED:
if (COMPARE(m_rgStatus[0],DBBINDSTATUS_BADSTORAGEFLAGS))
odtLog << L" Storage flag " << g_StorageFlags[iFlag].wszMode << L" is not supported for row accessors.\n";
break;
default:
CHECK(m_hr, S_OK);
}
// Verify the inserted data
ABORT_CHECK(m_pICommandText->SetCommandText(DBGUID_DBSQL, pwszSelectStmt), S_OK);
ABORT_CHECK(m_pICommand->Execute(NULL, IID_IRowset, NULL, &cRowsAffected, (IUnknown **)&pIRowset), S_OK);
ABORT_CHECK(pIRowset->GetNextRows(NULL, ulUpdateRow-1, 1, &cRowsObtained, &prghRows), S_OK);
// Set Inline result so we know whether to release inline objects
hrInLine=E_FAIL;
// Try to get the data using the IUnknown accessor
hrObject=pIRowset->GetData(*prghRows, hRowAccessor, (void *)pRowData);
// Check the return code from GetData
switch(hrObject)
{
case DB_E_BADACCESSORHANDLE:
// Create accessor failed for IUnknown row accessor
FAIL_COMPARE(hRowAccessor, DB_NULL_HACCESSOR);
break;
case E_NOINTERFACE:
// Interface is not supported
FAIL_COMPARE(m_fSupportInterface, FALSE);
break;
case DB_E_UNSUPPORTEDCONVERSION:
// The conversion wasn't valid
FAIL_COMPARE(S_OK != CanConvertIUnknown(pIRowset, ColInfo.GetProviderType(),
DBCONVERTFLAGS_COLUMN), TRUE);
break;
case DB_E_BADSTORAGEFLAGS:
// The Storage Mode flag was not supported or is invalid
odtLog << L" Storage flag " << g_StorageFlags[iFlag].wszMode <<
L" is not supported for row accessors.\n";
break;
default:
FAIL_CHECK(hrObject, S_OK);
}
if (!SUCCEEDED(hrObject))
{
// Reading the data as an IUnknown object failed, read in-line instead
pRowData->ulStatus = S_OK;
// Update the inline binding data type
rgRowBind[0].wType=ColInfo.GetProviderType();
rgRowBind[0].bPrecision=ColInfo.GetPrecision();
rgRowBind[0].bScale=ColInfo.GetScale();
// Create a row accessor for this row
// This must be on the rowset object, not the command object
pIRowset->QueryInterface(IID_IAccessor, (void **)&pIAccessor);
if (!pIAccessor)
goto CLEANUP;
ABORT_CHECK(pIAccessor->CreateAccessor(DBACCESSOR_ROWDATA, 1, rgRowBind, 0,
&hRowAccessorInLine, m_rgStatus), S_OK);
// We have to read the data as in-memory data all at once
hrInLine=pIRowset->GetData(*prghRows, hRowAccessorInLine, (void *)pRowData);
}
// If neither of the read methods succeeded then we can't verify the data
if (SUCCEEDED(hrObject) || SUCCEEDED(hrInLine))
{
// Check the status code
switch (eTest)
{
case NULL_DATA:
ulExpStatus = DBSTATUS_S_ISNULL;
break;
case NULL_STORAGE_OBJECT:
case EMPTY_STORAGE_OBJECT:
// We should only be able to read 0 bytes from the provider for
// variable length data types. Fixed length types are padded.
if (!ColInfo.GetIsFixedLength())
cbReadSize = 0;
else
{
// The backend data should be nulls or zeros, so set source buffer
// the same so we can compare
switch (ColInfo.GetProviderType())
{
case DBTYPE_BYTES:
// Padded with NULLs
memset(pvSrcData, 0x00, cbSrcData);
break;
case DBTYPE_STR:
// Padded with spaces
memset(pvSrcData, 0x20, cbSrcData);
break;
case DBTYPE_WSTR:
// Padded with wide spaces
for (ULONG iChar=0; iChar < cbSrcData/sizeof(WCHAR); iChar++)
*((WCHAR *)pvSrcData+iChar) = L' ';
break;
}
// Since fixed length types were padded we should have data to read
cbReadSize = cbSrcData;
}
// Fall through for expected status
default:
ulExpStatus = DBSTATUS_S_OK;
}
// If the status is what we exect we can verify the data
// Note that some providers will fail here for NULL/empty storage objects because
// they cannot create a 0 length column and instead either have one byte or show
// it as DBSTATUS_S_ISNULL.
if (COMPARE(pRowData->ulStatus, ulExpStatus))
{
switch(pRowData->ulStatus)
{
case DBSTATUS_S_ISNULL:
// Status value compared above, nothing to do
break;
case DBSTATUS_S_TRUNCATED:
// We can only compare the amount actually read
cbReadSize = (ULONG)pRowData->ulDataSize;
// Fall through
case DBSTATUS_S_OK:
if (hrObject == S_OK)
{
ULONG iRead = 0;
cbReadSrc = 0;
// Verify using storage object
// RowData buffer is a pointer to an ISequentialStream instantiated by the provider over the column
pISeqStreamRow = (ISequentialStream *)pRowData->pIUnknown;
// Use the read method to get the data into our buffer
do
{
HRESULT hrRead;
// Set both read and compare buff to fill value as debugging aid for
// partial reads
memset(pvReadBuf, FILL_VALUE, cbSrcData+1);
memset(pvCmpBuf, FILL_VALUE, cbSrcData+1);
iRead++;
// Read a buffer full of data from the provider
hrRead = pISeqStreamRow->Read(pvReadBuf, cbSrcData, &cbReadProv);
// Make sure the hresult is valid. We really expect S_OK even when
// no more data, but implementations are allowed to return S_FALSE
// for this condition.
if (!(hrRead == S_OK || (hrRead == S_FALSE && cbReadProv < cbSrcData)))
ABORT_CHECK(hrRead, S_OK);
// Read data from our local copy in the size chunk we expect
if (pIParamObject)
ABORT_CHECK(pIParamObject->Read(pvCmpBuf, cbSrcData, &cbReadSrc), S_OK);
// The read size should match
if (COMPARE(cbReadProv, cbReadSrc))
{
// Compare this buffer with what was written. Can't use CompareDBTypeData
// because strings aren't null terminated (theoretically).
FAIL_COMPARE(memcmp(pvCmpBuf, pvReadBuf, cbReadSrc), 0);
// Make sure the fill value was not overwritten
FAIL_COMPARE(*((BYTE *)pvReadBuf+cbReadSrc), FILL_VALUE);
}
else
m_TestResult=TEST_FAIL;
}
while (cbReadSrc > 0 && cbReadProv == cbReadSrc);
FAIL_COMPARE(pISeqStreamRow->Release(), 0);
pISeqStreamRow=NULL;
}
else
{
// Fixed length data types will be padded so won't have zero length
if (!ColInfo.GetIsFixedLength() &&
(eTest == NULL_STORAGE_OBJECT ||
eTest == EMPTY_STORAGE_OBJECT))
{
// We already compared status above, size must be zero. Note that
// CompareDBTypeData uses strcmp, which fails for 0 length data
FAIL_COMPARE(pRowData->ulDataSize, 0);
}
else
{
// Have to verify using in-line data.
// Note: SQL based providers will fail here because they will pad
// the data if fixed length char/binary. But non-SQL providers
// will most likely not follow this behavior.
if (COMPARE(pRowData->ulDataSize, cbReadSize))
{
BYTE * pvData = pRowData->bData;
// Rewind our source ISequentialStream to point to beginning
if (pIParamObject)
pIParamObject->Rewind();
do
{
// Read data from our local copy in the size chunk we expect
if (pIParamObject)
ABORT_CHECK(pIParamObject->Read(pvCmpBuf, cbSrcData, &cbReadSrc), S_OK);
FAIL_COMPARE(memcmp(pvCmpBuf, pvData, cbReadSrc), 0);
pvData+=cbReadSrc;
}
while (cbReadSrc > 0);
// String data must be null terminated
if (ColInfo.GetProviderType() == DBTYPE_WSTR)
{
FAIL_COMPARE(((WCHAR *)pRowData->bData)[cbReadSize/sizeof(WCHAR)], L'\0');
}
if (ColInfo.GetProviderType() == DBTYPE_STR)
{
FAIL_COMPARE(((CHAR *)pRowData->bData)[cbReadSize], '\0');
}
}
else
m_TestResult = TEST_FAIL;
}
}
break;
default:
ASSERT(!L"Need more case statements");
}
}
else
m_TestResult = TEST_FAIL;
// If we retrieved in-line data then release it
if (SUCCEEDED(hrInLine))
{
// We're done verifying, release the accessor now
pIAccessor->ReleaseAccessor(hRowAccessorInLine, &cRefCount);
FAIL_COMPARE(cRefCount, 0);
FAIL_COMPARE(pIAccessor->Release(), 1); // Rowset interface should be all that's left
pIAccessor = NULL;
}
}
else
{
// Neither of the read methods succeeded, unable to verify data
// Force an error to be posted
FAIL_COMPARE(hrInLine, S_OK);
}
ABORT_CHECK(pIRowset->ReleaseRows(1, rghRows, NULL, NULL, NULL), S_OK);
RELEASE(pIRowset);
// Release the parameter stream object
SAFE_RELEASE(pIParamObject);
} // Next binding
// Reset the table to contain standard data by deleting all rows and reinserting
TESTC_(m_pTable->Delete(ALLROWS), S_OK);
for (iRow = 0; iRow < TOTAL_NUMBER_OF_ROWS; iRow++)
{
TESTC_(m_pTable->Insert(iRow+1), S_OK);
}
}
// Free binding memory
TESTC_(ReleaseInputBindingsMemory(cParams, pParamBind, pData, FALSE), S_OK);
} // Next iCol
} // Next iFlag
fResult = TRUE;
CLEANUP:
// FREE_DATA(pvSrcData); // Stream's data buffer is released by destructor
SAFE_FREE(pRowData);
FREE_DATA(pvPrimaryData);
FREE_DATA(pvReadBuf);
FREE_DATA(pvCmpBuf);
RELEASE(pISeqStreamRow);
RELEASE(pIRowset);
RELEASE(m_pIUnkVerifyObj);
// Release the accessors now
SAFE_RELEASE_ACCESSOR(m_pIAccessor, hParamAccessor);
SAFE_RELEASE_ACCESSOR(m_pIAccessor, hRowAccessor);
// Reset table parameters and delete any added rows
if (fTableHadNulls)
{
m_pTable->SetNull(USENULLS);
// Reset the table to contain standard data by deleting all rows and reinserting
CHECK(m_pTable->Delete(ALLROWS), S_OK);
for (iRow = 0; iRow < TOTAL_NUMBER_OF_ROWS; iRow++)
{
CHECK(m_pTable->Insert(iRow+1), S_OK);
}
}
switch (fResult)
{
case TEST_SKIPPED:
return TEST_SKIPPED;
case TRUE:
return TEST_PASS;
default:
return TEST_FAIL;
}
}
//--------------------------------------------------------------------
// @mfunc TestCase Initialization Routine
//
// @rdesc TRUE or FALSE
//
BOOL TCIUnknown::Init()
{
BOOL InitSuccess = FALSE;
CCol ColInfo;
ULONG_PTR ulOLEMask = 0;
IDBCreateCommand * pIDBCreateCommand=NULL;
IUnknown * pDSO = m_pThisTestModule->m_pIUnknown;
LPWSTR wszProviderName=NULL;
m_pICommand=NULL;
m_pICommandText=NULL;
m_pICommandWithParameters=NULL;
m_pIDBProperties=NULL;
m_pIAccessor=NULL;
m_fKagera=FALSE;
m_pTable = NULL;
m_pIUnkVerifyObj = NULL;
// {{ TCW_INIT_BASECLASS_CHECK
if(COLEDB::Init())
// }}
{
TEST_COMPARE(VerifyInterface(pDSO, IID_IDBProperties, DATASOURCE_INTERFACE, (IUnknown**)&m_pIDBProperties),TRUE);
TEST_COMPARE(VerifyInterface(m_pThisTestModule->m_pIUnknown2, IID_IDBCreateCommand, SESSION_INTERFACE, (IUnknown**)&pIDBCreateCommand),TRUE);
// Get a command object
TEST_CHECK(pIDBCreateCommand->CreateCommand(NULL, IID_ICommand,
(IUnknown **)&m_pICommand), S_OK);
RELEASE(pIDBCreateCommand);
TEST_COMPARE(VerifyInterface(m_pICommand, IID_ICommandText, COMMAND_INTERFACE, (IUnknown**)&m_pICommandText),TRUE);
TEST_COMPARE(VerifyInterface(m_pICommand, IID_ICommandWithParameters, COMMAND_INTERFACE, (IUnknown**)&m_pICommandWithParameters),TRUE);
TEST_COMPARE(VerifyInterface(m_pICommand, IID_IAccessor, COMMAND_INTERFACE, (IUnknown**)&m_pIAccessor),TRUE);
TEST_COMPARE(GetProperty(DBPROP_STRUCTUREDSTORAGE, DBPROPSET_DATASOURCEINFO, m_pIDBProperties, &m_ulStorageSupport),TRUE);
GetProperty(DBPROP_PROVIDERNAME, DBPROPSET_DATASOURCEINFO, m_pIDBProperties, &wszProviderName);
if (!wcscmp((LPWSTR)wszProviderName, L"MSDASQL.DLL"))
m_fKagera=TRUE;
PROVIDER_FREE(wszProviderName);
if (GetProperty(DBPROP_OLEOBJECTS, DBPROPSET_DATASOURCEINFO, m_pIDBProperties, &ulOLEMask))
{
if (ulOLEMask & DBPROPVAL_OO_BLOB)
{
//Create a table just for this test case
m_pTable = new CTable((IUnknown *)m_pThisTestModule->m_pIUnknown2,
(LPWSTR)gwszModuleName);
TEST_PTR(m_pTable);
TEST_CHECK(m_pTable->CreateTable(TOTAL_NUMBER_OF_ROWS), S_OK)
InitSuccess = TRUE;
}
else
odtLog << L"Provider does not support structured storage objects, test case skipped.\n";
}
else
odtLog << L"Unable to retrieve value of DBPROP_OLEOBJECTS.\n";
}
CLEANUP:
return InitSuccess;
}
// {{ TCW_VAR_PROTOTYPE(1)
//*-----------------------------------------------------------------------
// @mfunc ISequentialStream
//
// @rdesc TEST_PASS or TEST_FAIL
//
int TCIUnknown::Variation_1()
{
return TestStorageObject(VALID_INPUT_DATA, IID_ISequentialStream, IID_ISequentialStream);
}
// }}
// {{ TCW_VAR_PROTOTYPE(2)
//*-----------------------------------------------------------------------
// @mfunc ILockBytes
//
// @rdesc TEST_PASS or TEST_FAIL
//
int TCIUnknown::Variation_2()
{
odtLog << L"Not yet implemented.\n";
return TEST_PASS;
}
// }}
// {{ TCW_VAR_PROTOTYPE(3)
//*-----------------------------------------------------------------------
// @mfunc IStorage
//
// @rdesc TEST_PASS or TEST_FAIL
//
int TCIUnknown::Variation_3()
{
odtLog << L"Not yet implemented.\n";
return TEST_PASS;
}
// }}
// {{ TCW_VAR_PROTOTYPE(4)
//*-----------------------------------------------------------------------
// @mfunc IStream
//
// @rdesc TEST_PASS or TEST_FAIL
//
int TCIUnknown::Variation_4()
{
odtLog << L"Not yet implemented.\n";
return TEST_PASS;
}
// }}
// {{ TCW_VAR_PROTOTYPE(5)
//*-----------------------------------------------------------------------
// @mfunc DBPROP_MULTIPLESTORAGEOBJECTS
//
// @rdesc TEST_PASS or TEST_FAIL
//
int TCIUnknown::Variation_5()
{
odtLog << L"Not yet implemented.\n";
return TEST_PASS;
}
// }}
// {{ TCW_VAR_PROTOTYPE(6)
//*-----------------------------------------------------------------------
// @mfunc DBPROP_BLOCKINGSTORAGEOBJECTS
//
// @rdesc TEST_PASS or TEST_FAIL
//
int TCIUnknown::Variation_6()
{
odtLog << L"Not yet implemented.\n";
return TEST_PASS;
}
// }}
// {{ TCW_VAR_PROTOTYPE(7)
//*-----------------------------------------------------------------------
// @mfunc NULL Storage Object - S_OK
//
// @rdesc TEST_PASS or TEST_FAIL
//
int TCIUnknown::Variation_7()
{
return TestStorageObject(NULL_STORAGE_OBJECT, IID_ISequentialStream, IID_ISequentialStream);
}
// }}
// {{ TCW_VAR_PROTOTYPE(8)
//*-----------------------------------------------------------------------
// @mfunc Empty storage object - S_OK
//
// @rdesc TEST_PASS or TEST_FAIL
//
int TCIUnknown::Variation_8()
{
return TestStorageObject(EMPTY_STORAGE_OBJECT, IID_ISequentialStream, IID_ISequentialStream);
}
// }}
// {{ TCW_VAR_PROTOTYPE(9)
//*-----------------------------------------------------------------------
// @mfunc DBSTATUS_S_ISNULL
//
// @rdesc TEST_PASS or TEST_FAIL
//
int TCIUnknown::Variation_9()
{
return TestStorageObject(NULL_DATA, IID_ISequentialStream, IID_ISequentialStream);
}
// }}
// {{ TCW_VAR_PROTOTYPE(10)
//*-----------------------------------------------------------------------
// @mfunc Verify streams active after error
//
// @rdesc TEST_PASS or TEST_FAIL
//
int TCIUnknown::Variation_10()
{
ULONG iActiveError;
BOOL fResult = TEST_PASS;
for (iActiveError = INVALIDARG_MULTRES;
iActiveError < LAST_ACTIVE_ERROR;
iActiveError++)
{
fResult &= TestActiveError((enum ACTIVE_ERROR)iActiveError, IID_ISequentialStream);
}
return fResult;
}
// }} TCW_VAR_PROTOTYPE_END
// {{ TCW_VAR_PROTOTYPE(11)
//*-----------------------------------------------------------------------
// @mfunc Verify streams released after error
//
// @rdesc TEST_PASS or TEST_FAIL
//
int TCIUnknown::Variation_11()
{
// TO DO: Add your own code here
return TRUE;
}
// }} TCW_VAR_PROTOTYPE_END
// {{ TCW_VAR_PROTOTYPE(12)
//*-----------------------------------------------------------------------
// @mfunc Multiple Paramsets
//
// @rdesc TEST_PASS or TEST_FAIL
//
int TCIUnknown::Variation_12()
{
return TestStorageObject(VALID_INPUT_DATA, IID_ISequentialStream, IID_ISequentialStream, 3);
}
// }} TCW_VAR_PROTOTYPE_END
// {{ TCW_VAR_PROTOTYPE(13)
//*-----------------------------------------------------------------------
// @mfunc Multiple Storage Objects
//
// @rdesc TEST_PASS or TEST_FAIL
//
int TCIUnknown::Variation_13()
{
return TestStorageObject(MULTIPLE_OBJECTS, IID_ISequentialStream, IID_ISequentialStream);
}
// }} TCW_VAR_PROTOTYPE_END
// {{ TCW_TERMINATE_METHOD
//--------------------------------------------------------------------
// @mfunc TestCase Termination Routine
//
// @rdesc TRUE or FALSE
//
BOOL TCIUnknown::Terminate()
{
RELEASE(m_pIDBProperties);
RELEASE(m_pICommand);
RELEASE(m_pICommandWithParameters);
RELEASE(m_pICommandText);
RELEASE(m_pIAccessor);
if (m_pTable)
{
m_pTable->DropTable();
delete m_pTable;
}
// {{ TCW_TERM_BASECLASS_CHECK2
return(COLEDB::Terminate());
} // }}
// }}
// }}