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

757 lines
20 KiB
C++

//--------------------------------------------------------------------
// Microsoft OLE DB Sample Provider
// (C) Copyright 1991 - 1999 Microsoft Corporation. All Rights Reserved.
//
// @doc
//
// @module COMMAND.CPP | CCommand object implementation
//
//
// Includes ------------------------------------------------------------------
#include "headers.h"
// Code ----------------------------------------------------------------------
// CCommand::CCommand --------------------------------------------------------
//
// @mfunc Constructor for this class
//
// @rdesc NONE
//
CCommand::CCommand
(
CDBSession* pCSession, //@parm IN | Parent Session Object
LPUNKNOWN pUnkOuter //@parm IN | Outer Unkown Pointer
) // invoke ctor for base class
: CBaseObj( BOT_COMMAND )
{
assert( pCSession );
// Initialize simple member vars
m_pUnkOuter = pUnkOuter ? pUnkOuter : this;
m_dwStatus = 0L;
m_cRowsetsOpen = 0;
m_cRef = 0L;
// Establish the parent object
m_pCSession = pCSession;
// Initially, NULL all contained interfaces
m_pIAccessor = NULL;
m_pICommandText = NULL;
m_pICommandProperties = NULL;
m_pIColumnsInfo = NULL;
m_pIConvertType = NULL;
m_pUtilProp = NULL;
m_guidCmdDialect = DBGUID_DBSQL;
m_guidImpersonate = GUID_NULL;
m_strCmdText = NULL;
// Increment global object count.
OBJECT_CONSTRUCTED();
return;
}
// CCommand::~CCommand -------------------------------------------------------
//
// @mfunc Destructor for this class
//
// @rdesc NONE
//
CCommand::~CCommand (void)
{
// Free contained interfaces
SAFE_DELETE( m_pIAccessor );
SAFE_DELETE( m_pICommandText );
SAFE_DELETE( m_pICommandProperties );
SAFE_DELETE( m_pIColumnsInfo );
SAFE_DELETE( m_pIConvertType );
SAFE_DELETE( m_pUtilProp );
// Since Command Object is going away, we can decrement
// our count on the session object.
// Note that this typically deletes our hdbc, DataSource, Session.
// (So do this last.)
if( m_pCSession )
m_pCSession->GetOuterUnknown()->Release();
// Decrement global object count.
OBJECT_DESTRUCTED();
}
// CCommand::FInit -----------------------------------------------------------
//
// @mfunc Initialize the command Object. This FInit routine should be used by
// CreateCommand and also be called as a secondary initialization routine for
// for the other FInit on the command object.
//
// @side If this initialization routine fails, it is the callers responsibility
// to delete this object. The destructor will then clean up any allocated
// resources
//
// @rdesc Did the Initialization Succeed
// @flag S_OK | Initialization succeeded
// @flag E_OUTOFMEMORY | Could not allocate enough memory
// @flag E_FAIL | Initializtion failed
//
HRESULT CCommand::FInit()
{
assert( m_pUnkOuter ); // Set in constructor
assert( m_pCSession ); // Set in constructor
LPUNKNOWN pIUnknown = (LPUNKNOWN) this;
if (m_pUnkOuter)
pIUnknown = m_pUnkOuter;
if (m_pCSession && m_pCSession->GetOuterUnknown())
m_pCSession->GetOuterUnknown()->AddRef();
// Allocate Property Utility object
m_pUtilProp = new CUtilProp();
if( !m_pUtilProp )
return ResultFromScode(E_OUTOFMEMORY);
// Allocate contained interface objects
m_pIAccessor = new CImpIAccessor( this, pIUnknown );
m_pICommandText = new CImpICommandText( this, pIUnknown );
m_pICommandProperties = new CImpICommandProperties( this, pIUnknown );
m_pIColumnsInfo = new CImpIColumnsInfo( this, pIUnknown );
m_pIConvertType = new CImpIConvertType( this, pIUnknown );
if ( m_pIAccessor )
{
if( FAILED(m_pIAccessor->FInit(FALSE)))
return ResultFromScode(E_OUTOFMEMORY);
}
if( m_pIAccessor &&
m_pICommandText &&
m_pICommandProperties &&
m_pIColumnsInfo &&
m_pIConvertType
)
return ResultFromScode(S_OK);
else
return ResultFromScode(E_FAIL);
}
// CCommand::QueryInterface --------------------------------------------------
//
// @mfunc Returns a pointer to a specified interface. Callers use
// QueryInterface to determine which interfaces the called object
// supports.
//
// @rdesc HResult indicating the status of the method
// @flag S_OK | Interface is supported and ppvObject is set.
// @flag E_NOINTERFACE | Interface is not supported by the object
// @flag E_INVALIDARG | One or more arguments are invalid.
//
STDMETHODIMP CCommand::QueryInterface
(
REFIID riid, //@parm IN | Interface ID of the interface being queried for.
LPVOID * ppv //@parm OUT | Pointer to interface that was instantiated
)
{
if( ppv == NULL )
return ResultFromScode(E_INVALIDARG);
// This is the non-delegating IUnknown implementation
if( riid == IID_IAccessor )
*ppv = (LPVOID)m_pIAccessor;
else if( riid == IID_ICommand )
*ppv = (LPVOID)m_pICommandText;
else if( riid == IID_ICommandText )
*ppv = (LPVOID)m_pICommandText;
else if( riid == IID_ICommandProperties )
*ppv = (LPVOID)m_pICommandProperties;
else if( riid == IID_IColumnsInfo )
*ppv = (LPVOID)m_pIColumnsInfo;
else if( riid == IID_IConvertType )
*ppv = (LPVOID)m_pIConvertType;
else if( riid == IID_IUnknown )
*ppv = (LPVOID)this;
else
*ppv = NULL;
// If we're going to return an interface, AddRef it first
if( *ppv )
{
((LPUNKNOWN)*ppv)->AddRef();
return NOERROR;
}
return ResultFromScode(E_NOINTERFACE);
}
// CCommand::AddRef ----------------------------------------------------------
//
// @mfunc Increments a persistence count for the object
//
// @rdesc Current reference count
//
STDMETHODIMP_(DBREFCOUNT) CCommand::AddRef (void)
{
return ++m_cRef;
}
// CCommand::Release -------------------------------------------------------------------
//
// @mfunc Decrements a persistence count for the object and if
// persistence count is 0, the object destroys itself.
//
// @rdesc Current reference count
//
STDMETHODIMP_(DBREFCOUNT) CCommand::Release (void)
{
if (!--m_cRef)
{
delete this;
return 0;
}
return m_cRef;
}
// UTILITY FUNCTIONS UTILITY FUNCTIONS
// ICommandText specific interfaces
//-----------------------------------------------------------------------------------
// CImpICommandText::GetCommandText
//
// @mfunc Echos the current command as text, including all post-processing
// operations added.
//
// @rdesc HResult
// @flag S_OK | Method Succeeded
// @flag DB_S_DIALECTIGNORED | Method succeeded, but dialect ignored
// @flag E_INVALIDARG | ppwszCommand was a null pointer
// @flag E_OUTOFMEMORY | Out of Memory
//
STDMETHODIMP CImpICommandText::GetCommandText
(
GUID *pguidDialect, //@parm INOUT | Guid denoting the dialect of sql
LPOLESTR *ppwszCommand //@parm OUT | Pointer for the command text
)
{
HRESULT hr;
// Check Function Arguments
if( ppwszCommand == NULL )
{
hr = ResultFromScode(E_INVALIDARG);
goto exit;
}
*ppwszCommand = NULL;
// If the command has not been set, make sure the buffer
// contains an empty stringt to return to the consumer
if( !m_pObj->IsCommandSet() )
{
hr = ResultFromScode(DB_E_NOCOMMAND);
goto exit;
}
assert( m_pObj->m_strCmdText );
hr = NOERROR;
if( pguidDialect != NULL &&
*pguidDialect != DBGUID_DEFAULT &&
*pguidDialect != DBGUID_SAMPLEDIALECT )
{
hr = DB_S_DIALECTIGNORED;
*pguidDialect = DBGUID_DEFAULT;
}
// Allocate memory for the string we're going to return to the caller
*ppwszCommand = (LPWSTR) PROVIDER_ALLOC(
(wcslen(m_pObj->m_strCmdText) + 1) * sizeof(WCHAR));
if( !(*ppwszCommand) )
{
hr = ResultFromScode(E_OUTOFMEMORY);
goto exit;
}
// Copy our saved text into the newly allocated string
StringCchCopyW(*ppwszCommand,wcslen(m_pObj->m_strCmdText) + 1,m_pObj->m_strCmdText);
exit:
if( FAILED(hr) && pguidDialect )
memset(pguidDialect, 0, sizeof(GUID));
return hr;
}
//-----------------------------------------------------------------------------------
// CImpICommandText::SetCommandText
//
// @mfunc Sets the current command text
//
// @rdesc HResult
// @flag S_OK | Method Succeeded
// @flag E_FAIL | A provider-specific error occurred
// @flag DB_E_DIALECTNOTSUPPORTED | The dialect given was not supported
// @flag DB_E_OBJECTOPEN | A rowset was already open on the command object
//
STDMETHODIMP CImpICommandText::SetCommandText
(
REFGUID rguidDialect, //@parm IN | Guid denoting the dialect of sql
LPCOLESTR pwszCommand //@parm IN | Command Text
)
{
HRESULT hr = S_OK;
// Don't allow text to be set if we've got a rowset open
if( m_pObj->IsRowsetOpen() )
return ResultFromScode(DB_E_OBJECTOPEN);
// Check Dialect
if( rguidDialect != DBGUID_SAMPLEDIALECT &&
rguidDialect != DBGUID_DEFAULT )
return ResultFromScode(DB_E_DIALECTNOTSUPPORTED);
if( !pwszCommand || *pwszCommand == L'\0' )
{
// Free the current string
SAFE_DELETE_ARRAY(m_pObj->m_strCmdText);
m_pObj->m_dwStatus &= ~CMD_TEXT_SET;
return NOERROR;
}
WCHAR * pwszCMD = new WCHAR[wcslen(pwszCommand)+1];
if ( !pwszCMD )
hr = ResultFromScode(E_OUTOFMEMORY);
// Free the old memory and set the text
SAFE_DELETE_ARRAY(m_pObj->m_strCmdText);
m_pObj->m_strCmdText = pwszCMD;
StringCchCopyW(m_pObj->m_strCmdText,wcslen(pwszCommand)+1, pwszCommand);
// Set status flag that we have set text
m_pObj->m_dwStatus |= CMD_TEXT_SET;
return NOERROR;
}
//---------------------------------------------------------------------------
// CImpICommandText::Cancel
//
// @mfunc The consumer can allocate a secondary thread in which to cancel
// the currently executing thread. This cancel will only succeed if
// the result set is still being generated. If the rowset object is being
// created, then it will be to late to cancel.
//
// @rdesc Status of Method
// @flag S_OK | Cancel Succeeded
// @flag DB_E_CANTCANCEL | Execution could not be canceled.
// @flag OTHER | other result codes returned by called functions.
//---------------------------------------------------------------------------
STDMETHODIMP CImpICommandText::Cancel
(
void
)
{
if( m_pObj->m_dwStatus & CMD_EXECUTING )
return DB_E_CANTCANCEL;
return S_OK;
}
//---------------------------------------------------------------------------
// CImpICommandText::Execute
//
// @mfunc Execute the command. But before execution, it sets the ODBC
// statement and connection options based on the current properties.
//
// @rdesc Status of Execution
// @flag S_OK | Execution succeeded
// @flag E_INVALIDARG | Invalid parameter passed in.
// @flag E_FAIL | Provider specific error
// @flag E_OUTOFMEMORY | Not enough resources
// @flag E_NOINTERFACE | RIID specified was not supported
// @flag DB_E_NOCOMMAND | No Text or Tree set
// @flag DB_E_INTERFACECONFLICT | Interface conflict with previous properties
// @flag OTHER | other result codes returned by called functions.
//
STDMETHODIMP CImpICommandText::Execute
(
IUnknown* pUnkOuter, //@parm IN | Outer Unknown
REFIID riid, //@parm IN | Interface ID of the interface being queried for.
DBPARAMS* pParams, //@parm INOUT | Parameter Array
DBROWCOUNT* pcRowsAffected, //@parm OUT | count of rows affected by command
IUnknown** ppRowset //@parm OUT | Pointer to interface that was instantiated
)
{
HRESULT hr, hrProp = S_OK;
UDWORD dwFlags = 0;
UDWORD dwStatus=0;
ULONG cPropSets = 0;
ULONG ul;
ULONG ul2;
DBPROPSET *rgPropSets = NULL;
const IID* pIID = &IID_ICommand;
CFileIO* pFileio = NULL;
CRowset* pRowset = NULL;
BOOL fCreateRowObject = FALSE;
DBCOUNTITEM cRowsObtained = 0;
HROW * phRow = NULL;
IRowset * pIRowset = NULL;
IGetRow * pIGetRow = NULL;
assert( m_pObj->m_pUtilProp );
// Initialize Return Buffers
if( ppRowset )
*ppRowset = NULL;
if( pcRowsAffected )
*pcRowsAffected = -1;
if( riid != IID_NULL && !ppRowset )
return ResultFromScode( E_INVALIDARG );
// Only 1 ParamSet if ppRowset is non-Null
if( pParams && (pParams->cParamSets > 1) && ppRowset )
return ResultFromScode( E_INVALIDARG );
// Check that a command has been set
if( !m_pObj->IsCommandSet() )
return DB_E_NOCOMMAND;
// We do not allow the riid to be anything other than IID_IUnknown for aggregation
if( pUnkOuter && riid != IID_IUnknown )
return ResultFromScode( DB_E_NOAGGREGATION );
// Set Status
m_pObj->m_dwStatus |= CMD_EXECUTING;
// Check to see if IID_IOpenRowset is calling Execute to
// open a rowset.
if( m_pObj->m_guidImpersonate != GUID_NULL )
{
assert(m_pObj->m_guidImpersonate == IID_IOpenRowset);
pIID = &m_pObj->m_guidImpersonate;
}
hr = m_pObj->m_pUtilProp->GetProperties(
PROPSET_ROWSET,
0,
NULL,
&cPropSets,
&rgPropSets);
if( hr != S_OK )
{
// The only valid reason to fail is out of memory case
assert( hr == E_OUTOFMEMORY );
goto exit;
}
// Traverse the properties to see if a ROW object is requested.
for(ul=0;ul<cPropSets; ul++)
{
for(ul2=0;ul2<rgPropSets[ul].cProperties; ul2++)
{
// Check if a row object was requested.
// DBPROP_IRow, DBPROP_IRowChange, and DBPROP_IRowSchemaChange indicate
// that a ROW object is desired.
// Sample Provider does not support IRowSchemaChange so no need to check for that
// property
if( (rgPropSets[ul].rgProperties[ul2].dwPropertyID == DBPROP_IRow ||
rgPropSets[ul].rgProperties[ul2].dwPropertyID == DBPROP_IRowChange) &&
rgPropSets[ul].rgProperties[ul2].dwStatus == DBPROPSTATUS_OK &&
V_BOOL(&rgPropSets[ul].rgProperties[ul2].vValue) == VARIANT_TRUE )
fCreateRowObject = TRUE;
}
}
//Try to open the file...
if(FAILED(hr = m_pObj->m_pCSession->m_pCDataSource->OpenFile(m_pObj->m_strCmdText, &pFileio)))
goto exit;
// open and initialize a rowset\cursor object
pRowset = new CRowset( fCreateRowObject ? NULL : pUnkOuter );
if (!pRowset)
{
SAFE_DELETE( pFileio );
hr = E_OUTOFMEMORY;
goto exit;
}
// Initialize the rowset\cursor.
// For now, since don't yet support "settable" properties, so no properties to pass.
// The rowset will always create all of its interfaces.
// This is all-or-nothing.
//Assign creator pointer. Used to keep track of open rowsets
pRowset->m_pCreator = m_pObj;
pRowset->m_pCreator->m_pUnkOuter->AddRef();
if( !pRowset->FInit(
pFileio,
(*pIID == IID_IOpenRowset) ? (CBaseObj *) m_pObj->m_pCSession : (CBaseObj *) m_pObj,
m_pObj->m_strCmdText,
m_pObj->m_pCSession->m_pCDataSource->GetFilePath()
) )
{
SAFE_DELETE( pRowset );
hr = DB_E_NOTABLE;
goto exit;
}
// set the properties
if ( cPropSets )
hr = pRowset->GetCUtilProp()->SetProperties(PROPSET_ROWSET, cPropSets, rgPropSets);
// If all the properties set were OPTIONAL then we return
// DB_S_ERRORSOCCURRED and continue.
for(ul=0;ul<cPropSets; ul++)
{
for(ul2=0;ul2<rgPropSets[ul].cProperties; ul2++)
{
// Check for a required property that failed, if found, we must return
// DB_E_ERRORSOCCURRED
if( (rgPropSets[ul].rgProperties[ul2].dwStatus != DBPROPSTATUS_OK) &&
(rgPropSets[ul].rgProperties[ul2].dwOptions != DBPROPOPTIONS_OPTIONAL) )
{
SAFE_DELETE( pRowset );
hr = DB_E_ERRORSOCCURRED;
goto exit;
}
}
}
// Optional property failure is still a success
if( hr == DB_E_ERRORSOCCURRED )
hrProp = DB_S_ERRORSOCCURRED;
// if properties failed or ppRowset NULL
if( (FAILED(hr) && (hrProp==S_OK)) ||
(!ppRowset) )
{
SAFE_DELETE( pRowset );
goto exit;
}
// get requested interface pointer on rowset\cursor
if( fCreateRowObject || riid == IID_IRow || riid == IID_IRowChange )
{
hr = E_FAIL;
if( FAILED(pRowset->QueryInterface(IID_IRowset, (LPVOID *)&pIRowset)) )
goto exit;
if( FAILED(pIRowset->GetNextRows(DB_NULL_HCHAPTER, 0, 1, &cRowsObtained, &phRow)) )
goto exit;
assert( cRowsObtained == 1);
if( FAILED(pRowset->QueryInterface(IID_IGetRow, (LPVOID *)&pIGetRow)) )
goto exit;
if( FAILED(hr = pIGetRow->GetRowFromHROW(pUnkOuter, phRow[0], riid, ppRowset)) )
goto exit;
// Sample Provider does not support selection using a critera.
// Hence, DB_S_NOTSINGLETON is reported simply if the target file
// contains more than one row.
if( pRowset->GetFileObj()->GetRowCnt() > 1 )
hr = DB_S_NOTSINGLETON;
else
hr = S_OK;
}
else
{
hr = pRowset->QueryInterface( riid, (void **) ppRowset );
if( FAILED(hr) )
{
// Special case IID_NULL
if( riid == IID_NULL )
hr = S_OK;
SAFE_DELETE( pRowset );
goto exit;
}
}
if (SUCCEEDED(hr))
m_pObj->IncrementOpenRowsets();
SAFE_RELEASE(pIRowset);
SAFE_RELEASE(pIGetRow);
exit:
if( FAILED(hr) )
SAFE_DELETE(pRowset);
// Free the RowHandle
SAFE_FREE(phRow);
// Exiting the execution state
m_pObj->m_dwStatus &= ~CMD_EXECUTING;
for (ul=0; ul < cPropSets; ul++)
SAFE_FREE(rgPropSets[ul].rgProperties);
SAFE_FREE(rgPropSets);
return hr;
}
//---------------------------------------------------------------------------
// CImpICommandText::GetDBSession
//
// @mfunc Return an interface from the session object that created
// this command object
//
// @rdesc Status of Method
// @flag S_OK | Interface pointer returned
// @flag E_INVALIDARG | ppSession was invalid
// @flag OTHER | other result codes returned by called functions.
//
STDMETHODIMP CImpICommandText::GetDBSession
(
REFIID riid, //@parm IN | IID of the interface
IUnknown** ppSession //@parm OUT | Interface pointer
)
{
HRESULT hr;
assert( m_pObj->m_pCSession );
// Check Arguments
if( ppSession == NULL )
return ResultFromScode(E_INVALIDARG);
// Query for the interface on the session object. If failure,
// return the error from QueryInterface.
hr = m_pObj->m_pCSession->GetOuterUnknown()->QueryInterface(riid, (VOID**)ppSession);
if( FAILED(hr) )
{
*ppSession = NULL;
return hr;
}
return ResultFromScode(S_OK);
}
// ICommandProperties specific interfaces
//---------------------------------------------------------------------------
// CImpICommandProperties::GetProperties
//
// @mfunc Retrieve the Properties
//
// @rdesc HRESULT indicating the status of the method
// @flag OTHER | other result codes returned by called functions.
//
STDMETHODIMP CImpICommandProperties::GetProperties
(
const ULONG cPropertySets, //@parm IN | Number of property sets
const DBPROPIDSET rgPropertySets[], //@parm IN | Property Sets
ULONG* pcProperties, //@parm OUT | Count of structs returned
DBPROPSET** prgProperties //@parm OUT | Array of Properties
)
{
HRESULT hr;
assert( m_pObj );
assert( m_pObj->m_pUtilProp );
//NOTE: Since we are non-chaptered, we just ignore the
// rowset name argument.
hr = m_pObj->m_pUtilProp->GetPropertiesArgChk(
PROPSET_ROWSET,
cPropertySets,
rgPropertySets,
pcProperties,
prgProperties);
if( SUCCEEDED(hr) )
{
hr = m_pObj->m_pUtilProp->GetProperties(
PROPSET_ROWSET,
cPropertySets,
rgPropertySets,
pcProperties,
prgProperties);
}
// Just return the HResult
return hr;
}
//---------------------------------------------------------------------------
// CImpICommandProperties::SetProperties
//
// @mfunc Set the Properties
//
// @rdesc HRESULT indicating the status of the method
// @flag DB_E_OPENOBJECT | Can't prepare w/ open rowset
// @flag OTHER | other result codes returned by called functions.
//
STDMETHODIMP CImpICommandProperties::SetProperties
(
ULONG cPropertySets, //@parm IN | Count of structs returned
DBPROPSET rgPropertySets[] //@parm IN | Array of Properties
)
{
HRESULT hr = E_FAIL;
//
// Asserts
//
assert( m_pObj );
assert( m_pObj->m_pUtilProp );
//
// Quick return if the Count of Properties is 0
//
if( cPropertySets == 0 )
return S_OK;
//
// Check in-params and NULL out-params in case of error
//
hr=m_pObj->m_pUtilProp->SetPropertiesArgChk(cPropertySets, rgPropertySets);
if( FAILED(hr) )
return hr;
//
// Don't allow properties to be set if we've got a rowset open
//
if( m_pObj->IsRowsetOpen() )
return DB_E_OBJECTOPEN;
return m_pObj->m_pUtilProp->SetProperties(PROPSET_ROWSET,
cPropertySets, rgPropertySets);
}