windows-nt/Source/XPSP1/NT/inetsrv/query/icommand/qryspec.cxx
2020-09-26 16:20:57 +08:00

2443 lines
74 KiB
C++

//+---------------------------------------------------------------------------
//
// Microsoft Windows
// Copyright (C) Microsoft Corporation, 1995 - 2000.
//
// File: qryspec.cxx
//
// Contents: ICommandTree implementation for OFS file stores
//
// Classes: CRootQuerySpec
//
// Functions: CheckForErrors
// CheckForPriorTree
//
// History: 30 Jun 1995 AlanW Created
// 10-31-97 danleg Added ICommandText & ICommandPrepare
//
//----------------------------------------------------------------------------
#include "pch.cxx"
#pragma hdrstop
#include <colinfo.hxx>
#include <parstree.hxx>
#include <hraccess.hxx>
#include <mparser.h>
#include <propglob.hxx>
#include <doquery.hxx>
#include "qryspec.hxx"
// Command object Interfaces that support Ole DB error objects
static const IID * apCommandErrorIFs[] =
{
&IID_IAccessor,
&IID_IColumnsInfo,
&IID_ICommand,
&IID_ICommandProperties,
&IID_ICommandText,
&IID_IConvertType,
//&IID_IColumnsRowset,
&IID_ICommandPrepare,
&IID_ICommandTree,
//&IID_ICommandWithParameters,
&IID_IQuery,
//&IID_ISupportErrorInfo,
&IID_IServiceProperties
};
static const ULONG cCommandErrorIFs = sizeof(apCommandErrorIFs)/sizeof(apCommandErrorIFs[0]);
// SQL defining global views. These views are defined at the datasrc level, if one is present,
// or at the command level otherwise.
extern const LPWSTR s_pwszPredefinedViews =
L"SET GLOBAL ON; "
L"CREATE VIEW FILEINFO "
L" AS SELECT PATH, FILENAME, SIZE, WRITE, ATTRIB FROM SCOPE(); "
L"CREATE VIEW FILEINFO_ABSTRACT "
L" AS SELECT PATH, FILENAME, SIZE, WRITE, ATTRIB, CHARACTERIZATION FROM SCOPE(); "
L"CREATE VIEW EXTENDED_FILEINFO "
L" AS SELECT PATH, FILENAME, SIZE, WRITE, ATTRIB, DOCTITLE, DOCAUTHOR, DOCSUBJECT, DOCKEYWORDS, CHARACTERIZATION FROM SCOPE(); "
L"CREATE VIEW WEBINFO "
L" AS SELECT VPATH, PATH, FILENAME, SIZE, WRITE, ATTRIB, CHARACTERIZATION, DOCTITLE FROM SCOPE(); "
L"CREATE VIEW EXTENDED_WEBINFO "
L" AS SELECT VPATH, PATH, FILENAME, SIZE, CHARACTERIZATION, WRITE, DOCAUTHOR, DOCSUBJECT, DOCKEYWORDS, DOCTITLE FROM SCOPE(); "
L"CREATE VIEW SSWebInfo "
L" AS SELECT URL, DOCTITLE, RANK, SIZE, WRITE FROM SCOPE(); "
L"CREATE VIEW SSExtended_WebInfo "
L" AS SELECT URL, DOCTITLE, RANK, HITCOUNT, DOCAUTHOR, CHARACTERIZATION, SIZE, WRITE FROM SCOPE()";
//+-------------------------------------------------------------------------
//
// Member: CRootQuerySpec::CRootQuerySpec, public
//
// Synopsis: Constructor of a CRootQuerySpec
//
// Arguments: [pOuterUnk] - Outer unknown
// [ppMyUnk] - OUT: filled in with pointer to non-delegated
// IUnknown on return
//
// History: 08-Feb-96 KyleP Added support for virtual paths
//
//--------------------------------------------------------------------------
CRootQuerySpec::CRootQuerySpec (IUnknown * pOuterUnk,
IUnknown ** ppMyUnk,
CDBSession * pSession)
: _dwDepth(QUERY_SHALLOW),
_pInternalQuery(0),
_pQueryTree(0),
_pColumnsInfo(0),
#pragma warning(disable : 4355) // 'this' in a constructor
_impIUnknown(this),
_aAccessors( (IUnknown *) (ICommand *)this ),
_DBErrorObj( * ((IUnknown *) (ICommand *) this), _mtxCmd ),
#pragma warning(default : 4355) // 'this' in a constructor
_dwStatus(0),
_guidCmdDialect(DBGUID_SQL),
_fGenByOpenRowset(FALSE),
_pwszSQLText(0),
_RowsetProps( pSession ?
pSession->GetDataSrcPtr()->GetDSPropsPtr()->
GetValLong( CMDSProps::eid_DBPROPSET_DBINIT,
CMDSProps::eid_DBPROPVAL_INIT_LCID ) :
0 ),
_PropInfo()
{
if (pOuterUnk)
_pControllingUnknown = pOuterUnk;
else
_pControllingUnknown = (IUnknown * )&_impIUnknown;
_DBErrorObj.SetInterfaceArray(cCommandErrorIFs, apCommandErrorIFs);
if ( pSession )
{
_xSession.Set( pSession );
_xSession->AddRef();
_xpIPSession.Set( pSession->GetParserSession() );
//
// The above Set() doesn't AddRef. This will balance the XInterface<>
// dtor Release
//
_xpIPSession->AddRef();
}
*ppMyUnk = ((IUnknown *)&_impIUnknown);
(*ppMyUnk)->AddRef();
}
//+---------------------------------------------------------------------------
//
// Method: CRootQuerySpec::CRootQuerySpec, public
//
// Synopsis: Copy constructor
//
// Arguments: [src] -- Source query spec
//
// History: 27 Jun 95 AlanW Created
// 10 Jan 98 danleg Replaced body with Assert
//
//----------------------------------------------------------------------------
CRootQuerySpec::CRootQuerySpec( CRootQuerySpec & src )
: _dwDepth( src._dwDepth ),
_pInternalQuery(0),
_pQueryTree(0),
_pColumnsInfo(0),
#pragma warning(disable : 4355) // 'this' in a constructor
_impIUnknown(this),
_aAccessors( (IUnknown *) (ICommand *)this ),
_DBErrorObj( * ((IUnknown *) (ICommand *) this), _mtxCmd ),
#pragma warning(default : 4355) // 'this' in a constructor
_dwStatus(src._dwStatus),
_guidCmdDialect(src._guidCmdDialect),
_fGenByOpenRowset(src._fGenByOpenRowset),
_pwszSQLText(0),
_RowsetProps( src._RowsetProps ),
_PropInfo()
{
Win4Assert( !"CRootQuerySpec copy constructor not implemented.");
}
//+-------------------------------------------------------------------------
//
// Member: CRootQuerySpec::~CRootQuerySpec, private
//
// Synopsis: Destructor of a CRootQuerySpec
//
//--------------------------------------------------------------------------
CRootQuerySpec::~CRootQuerySpec()
{
ReleaseInternalQuery();
delete _pColumnsInfo;
delete _pQueryTree;
delete [] _pwszSQLText;
}
//+-------------------------------------------------------------------------
//
// Member: CRootQuerySpec::RealQueryInterface, public
//
// Synopsis: Get a reference to another interface on the cursor. AddRef
// is done in CImpIUnknown::QueryInterface
//
// History: 10-31-97 danleg Added ICommandText& ICommandPrepare
//
//--------------------------------------------------------------------------
//
// Hack #214: IID_ICommandProperties is intercepted by service layers, which
// don't like us passing in the magic code to fetch hidden scope
// properties. But the controlling unknown doesn't recognize
// IID_IKyleProp and sends it right to us. Implementation is
// identical to ICommandProperties.
//
extern GUID IID_IKyleProp;
SCODE CRootQuerySpec::RealQueryInterface(
REFIID ifid,
void * *ppiuk )
{
SCODE sc = S_OK;
TRY
{
// validate the param before any addrefs
*ppiuk = 0;
// note -- IID_IUnknown covered in QueryInterface
if ( IID_ICommand == ifid )
{
*ppiuk = (void *) ((ICommand *) this);
}
else if (IID_ISupportErrorInfo == ifid)
{
*ppiuk = (void *) ((IUnknown *) (ISupportErrorInfo *) &_DBErrorObj);
}
else if ( IID_IAccessor == ifid )
{
*ppiuk = (void *) (IAccessor *) this;
}
else if ( IID_IColumnsInfo == ifid )
{
*ppiuk = (void *) (IColumnsInfo *) GetColumnsInfo();
}
// NTRAID#DB-NTBUG9-84306-2000/07/31-dlee OLE-DB spec variance in Indexing Service, some interfaces not implemented
#if 0
else if ( IID_IRowsetInfo == ifid )
{
*ppiuk = (void *) (IRowsetInfo *) this;
}
#endif // 0
else if ( IID_ICommandTree == ifid )
{
*ppiuk = (void *) (ICommandTree *) this;
}
// NTRAID#DB-NTBUG9-84306-2000/07/31-dlee OLE-DB spec variance in Indexing Service, some interfaces not implemented
#if 0
else if ( IID_ICommandValidate == ifid )
{
*ppiuk = (void *) (ICommandValidate *) this;
}
#endif // 0
else if ( IID_IQuery == ifid )
{
*ppiuk = (void *) (IQuery *) this;
}
else if ( IID_ICommandProperties == ifid || IID_IKyleProp == ifid )
{
*ppiuk = (void *) (ICommandProperties *) this;
}
else if ( IID_IServiceProperties == ifid )
{
*ppiuk = (void *) (IServiceProperties *) this;
}
else if ( IID_IConvertType == ifid )
{
*ppiuk = (void *) (IConvertType *) this;
}
else if ( IID_ICommandText == ifid )
{
// Create parser sesson object if deferred during construction
if ( _xpIPSession.IsNull() )
CreateParser();
*ppiuk = (void *) (ICommandText *) this;
}
else if ( IID_ICommandPrepare == ifid )
{
*ppiuk = (void *) (ICommandPrepare *) this;
}
else
{
*ppiuk = 0;
sc = E_NOINTERFACE;
}
}
CATCH( CException, e )
{
vqDebugOut(( DEB_ERROR, "Exception %08x while doing QueryInterface \n",
e.GetErrorCode() ));
sc = GetOleError(e);
}
END_CATCH
return sc;
}
//+---------------------------------------------------------------------------
//
// Method: CRootQuerySpec::FindErrorNodes, public
//
// Synopsis: Find error nodes in a command tree
//
// Arguments: [pRoot] -- DBCOMMANDTREE node at root of tree
// [pcErrorNodes] -- pointer where count of error nodes is ret'd
//
// History: 27 Jun 95 AlanW Created
//
//----------------------------------------------------------------------------
BOOL CheckForErrors( const CDbCmdTreeNode *pNode )
{
if (pNode->GetError() != S_OK)
return TRUE;
else
return FALSE;
}
SCODE CRootQuerySpec::FindErrorNodes(
const DBCOMMANDTREE * pRoot,
ULONG * pcErrorNodes,
DBCOMMANDTREE *** prgErrorNodes)
{
SCODE sc = S_OK;
ULONG cErrors = 0;
XArrayOLE<DBCOMMANDTREE *> pErrorNodes;
TRANSLATE_EXCEPTIONS;
TRY
{
_FindTreeNodes( CDbCmdTreeNode::CastFromStruct(pRoot),
cErrors,
pErrorNodes,
CheckForErrors );
*pcErrorNodes = cErrors;
*prgErrorNodes = pErrorNodes.GetPointer();
pErrorNodes.Acquire();
}
CATCH( CException, e )
{
sc = _DBErrorObj.PostHResult( e, IID_ICommandTree );
}
END_CATCH;
UNTRANSLATE_EXCEPTIONS;
return sc;
}
//+---------------------------------------------------------------------------
//
// Method: CRootQuerySpec::_FindTreeNodes, private
//
// Synopsis: Find nodes in a command tree meeting some condition
//
// Arguments: [pRoot] -- DBCOMMANDTREE node at root of tree
// [rcMatchingNodes] -- count of matching nodes returned
// [prgMatchingNodes] -- pointer to array of nodes returned
// [pfnCheckNode] -- function which returns true if a tree
// node matches desired condition.
// [iDepth] -- depth of tree; for detecting cycles
//
// Notes: In order to avoid looping endlessly over a tree with cycles,
// this routine will bail out if the tree depth is greater
// than 1000 or if the tree width is greater than 100,000.
//
// We don't expect this routine to be called in situations
// where it will return very large numbers of tree nodes,
// so we grow the returned array only one element at a time.
//
// History: 27 Jun 95 AlanW Created
//
//----------------------------------------------------------------------------
const unsigned MAX_TREE_DEPTH = 1000; // max tree depth
const unsigned MAX_TREE_WIDTH = 100000; // max tree width
void CRootQuerySpec::_FindTreeNodes(
const CDbCmdTreeNode * pRoot,
ULONG & rcMatchingNodes,
XArrayOLE<DBCOMMANDTREE *> & rpMatchingNodes,
PFNCHECKTREENODE pfnCheckNode,
unsigned iDepth)
{
if (iDepth > MAX_TREE_DEPTH)
THROW(CException(E_FAIL));
unsigned iWidth = 0;
while (pRoot)
{
if (pRoot->GetFirstChild())
_FindTreeNodes( pRoot->GetFirstChild(),
rcMatchingNodes,
rpMatchingNodes,
pfnCheckNode,
iDepth+1);
if (iWidth > MAX_TREE_WIDTH)
THROW(CException(E_FAIL));
if ((pfnCheckNode)(pRoot))
{
XArrayOLE<DBCOMMANDTREE *> pMatchTemp( rcMatchingNodes+1 );
if (0 == pMatchTemp.GetPointer())
THROW(CException(E_OUTOFMEMORY));
if (rcMatchingNodes > 0)
{
Win4Assert(rpMatchingNodes.GetPointer() != 0);
RtlCopyMemory(pMatchTemp.GetPointer(),
rpMatchingNodes.GetPointer(),
sizeof (DBCOMMANDTREE*) * rcMatchingNodes);
CoTaskMemFree(rpMatchingNodes.Acquire());
}
(pMatchTemp.GetPointer())[rcMatchingNodes] = pRoot->CastToStruct();
rcMatchingNodes++;
rpMatchingNodes.Set( rcMatchingNodes, pMatchTemp.Acquire() );
}
pRoot = pRoot->GetNextSibling();
iWidth++;
}
return;
}
//+---------------------------------------------------------------------------
//
// Method: CRootQuerySpec::FreeCommandTree, public
//
// Synopsis: Free a command tree
//
// Arguments: [ppRoot] -- DBCOMMANDTREE node at root of tree to be freed
//
// History: 27 Jun 95 AlanW Created
//
//----------------------------------------------------------------------------
SCODE CRootQuerySpec::FreeCommandTree(
DBCOMMANDTREE * * ppRoot)
{
SCODE sc = S_OK;
if ( 0 == ppRoot )
return E_INVALIDARG;
TRANSLATE_EXCEPTIONS;
TRY
{
CDbCmdTreeNode * pCmdTree = (CDbCmdTreeNode *)
CDbCmdTreeNode::CastFromStruct(*ppRoot);
//
// If the user tries to delete our query tree, zero our pointer to it.
//
// NOTE: Nothing prevents the user from freeing a subtree of
// our tree if they called SetCommandTree with fCopy FALSE.
//
// There is a proposed spec change on this. According to the current spec,
// if fCopy was FALSE we need to return DB_E_CANNOTFREE here. (MDAC BUGG# 6386)
if ( _dwStatus & CMD_OWNS_TREE )
{
THROW( CException(DB_E_CANNOTFREE) );
}
else
{
if ( pCmdTree == _pQueryTree )
_pQueryTree = 0;
delete pCmdTree;
*ppRoot = 0;
}
}
CATCH( CException, e )
{
sc = _DBErrorObj.PostHResult( e, IID_ICommandTree );
}
END_CATCH;
UNTRANSLATE_EXCEPTIONS;
return sc;
}
//+---------------------------------------------------------------------------
//
// Method: CRootQuerySpec::GetCommandTree, public
//
// Synopsis: Get a copy of the command tree.
//
// Arguments: [ppRoot] -- pointer to where DBCOMMANDTREE is returned
//
// History: 27 Jun 95 AlanW Created
//
//----------------------------------------------------------------------------
SCODE CRootQuerySpec::GetCommandTree(
DBCOMMANDTREE * * ppRoot)
{
if ( 0 == ppRoot )
return _DBErrorObj.PostHResult( E_INVALIDARG, IID_ICommandTree );
SCODE sc = S_OK;
TRANSLATE_EXCEPTIONS;
TRY
{
//
// Initialize return parameter
//
*ppRoot = 0;
if ( 0 == _pQueryTree )
{
//
// Build it if we have a command text
//
if ( IsCommandSet() )
{
sc = BuildTree( );
// SET PROPERTYNAME ... query
if ( DB_S_NORESULT == sc )
return S_OK;
_dwStatus |= CMD_TREE_BUILT;
}
//
// The command text didn't generate a tree (i.e. it was
// either a CREATE VIEW or SET PROPERTYNAME ) or a
// command text wasn't set
//
if ( 0 == _pQueryTree )
{
return S_OK;
}
}
XPtr<CDbCmdTreeNode> TreeCopy( _pQueryTree->Clone(TRUE) );
if (0 == TreeCopy.GetPointer())
THROW(CException(E_OUTOFMEMORY));
*ppRoot = TreeCopy.GetPointer()->CastToStruct();
TreeCopy.Acquire();
}
CATCH( CException, e )
{
sc = _DBErrorObj.PostHResult( e, IID_ICommandTree );
}
END_CATCH;
UNTRANSLATE_EXCEPTIONS;
return sc;
}
//+---------------------------------------------------------------------------
//
// Method: CRootQuerySpec::SetCommandTree, public
//
// Synopsis: Set the command tree.
//
// Arguments: [ppRoot] -- pointer to DBCOMMANDTREE to be set in command obj
// [dwCommandReuse] -- indicates whether state is retained.
// [fCopy] -- if TRUE, a copy of ppRoot is made. Otherwise,
// ownership of the command tree passes to the
// command object.
//
// History: 27 Jun 95 AlanW Created
//
//----------------------------------------------------------------------------
SCODE CRootQuerySpec::SetCommandTree(
DBCOMMANDTREE * * ppRoot,
DBCOMMANDREUSE dwCommandReuse,
BOOL fCopy)
{
if ( HaveQuery() && _pInternalQuery->IsQueryActive() )
return DB_E_OBJECTOPEN;
if ( 0 == ppRoot )
return E_INVALIDARG;
SCODE sc = S_OK;
TRANSLATE_EXCEPTIONS;
TRY
{
_CheckRootNode( *ppRoot );
if ( 0 != _pQueryTree )
{
delete _pQueryTree;
_pQueryTree = 0;
}
CDbCmdTreeNode const * pCmdTree = CDbCmdTreeNode::CastFromStruct(*ppRoot);
if ( FALSE == fCopy )
{
_dwStatus |= CMD_OWNS_TREE;
_pQueryTree = (CDbCmdTreeNode *)pCmdTree;
*ppRoot = 0;
}
else
{
_pQueryTree = pCmdTree->Clone();
//
// If Clone() fails it cleans up after itself and returns 0
//
if ( 0 == _pQueryTree )
THROW( CException( E_OUTOFMEMORY ) );
}
_dwStatus &= ~CMD_COLINFO_NOTPREPARED;
if ( _pColumnsInfo )
InitColumns();
//
// If this is not being called internally (from BuildTree) remove the
// the command text
//
if ( _dwStatus & CMD_TEXT_TOTREE )
{
_dwStatus &= ~CMD_TEXT_TOTREE;
}
else
{
delete [] _pwszSQLText;
_pwszSQLText = 0;
_guidCmdDialect = DBGUID_SQL;
_dwStatus &= ~CMD_TEXT_SET;
}
}
CATCH( CException, e )
{
sc = _DBErrorObj.PostHResult( e, IID_ICommandTree );
}
END_CATCH;
UNTRANSLATE_EXCEPTIONS;
return sc;
}
//+---------------------------------------------------------------------------
//
// Method: CRootQuerySpec::Execute, public
//
// Synopsis: Execute the command; create rowsets for the query resu.
//
// Arguments: [pOuterUnk] -- controlling IUnknown for the rowset
// [riid] -- interface IID requested for the rowset
// [pParams] -- parameters for the query
// [pcRowsAffected] -- returned count of affected rows
// [ppRowset] -- returned rowset
//
// History: 27 Jun 95 AlanW Created
// 11-20-97 danleg Added ICommandText & ICommandPrepare
//
//----------------------------------------------------------------------------
SCODE CRootQuerySpec::Execute(
IUnknown * pOuterUnk,
REFIID riid,
DBPARAMS * pParams,
DBROWCOUNT * pcRowsAffected,
IUnknown * * ppRowset)
{
_DBErrorObj.ClearErrorInfo();
//
// Called from OpenRowset?
//
GUID guidPost = (_fGenByOpenRowset) ? IID_IOpenRowset : IID_ICommand;
if (0 == ppRowset && IID_NULL != riid )
return _DBErrorObj.PostHResult( E_INVALIDARG, guidPost );
if (0 != pOuterUnk && riid != IID_IUnknown)
return _DBErrorObj.PostHResult( DB_E_NOAGGREGATION, guidPost );
CLock lck( _mtxCmd );
SCODE scResult = S_OK;
IRowset * pIRowset = 0;
_dwStatus &= ~(CMD_EXEC_RUNNING);
TRANSLATE_EXCEPTIONS;
TRY
{
if ( ppRowset )
*ppRowset = 0;
// Impersonate the session logon user
HANDLE hToken = INVALID_HANDLE_VALUE;
if ( !_xSession.IsNull() )
hToken = _xSession->GetLogonToken();
CImpersonateSessionUser imp( hToken );
// Either SetCommandTree or SetCommandText should have already been called
if ( 0 == _pQueryTree )
{
if ( IsCommandSet() )
{
//
// No query tree. Build one from the command text if one has been set.
//
scResult = BuildTree( );
if ( DB_S_NORESULT == scResult )
return scResult;
_dwStatus |= CMD_TREE_BUILT;
}
else
THROW( CException(DB_E_NOCOMMAND) );
}
_dwStatus |= CMD_EXEC_RUNNING;
//
// Convert the tree into restriction, etc.
// The pParams should probably be passed to the ctor of
// the parser (when we do parameterized queries).
//
CParseCommandTree Parse;
Parse.ParseTree( _pQueryTree );
XGrowable<const WCHAR *,SCOPE_COUNT_GROWSIZE> xaScopes( SCOPE_COUNT_GROWSIZE );
XGrowable<ULONG,SCOPE_COUNT_GROWSIZE> xaFlags( SCOPE_COUNT_GROWSIZE );
XGrowable<const WCHAR *,SCOPE_COUNT_GROWSIZE> xaCatalogs( SCOPE_COUNT_GROWSIZE );
XGrowable<const WCHAR *,SCOPE_COUNT_GROWSIZE> xaMachines( SCOPE_COUNT_GROWSIZE );
unsigned cScopes = 0;
Parse.GetScopes( cScopes, xaScopes, xaFlags, xaCatalogs, xaMachines );
// If the tree had a DBOP_tree node instead of a DBOP_content_table, we can't
// parse scope information from the tree. The client is responsible for
// setting scope properties. Currently, this means the Tripolish parser
if ( 0 < cScopes )
{
SetScopeProperties( this,
cScopes,
xaScopes.Get(),
xaFlags.Get(),
xaCatalogs.Get(),
xaMachines.Get() );
}
CRestriction * pCrst = Parse.GetRestriction();
CCategorizationSet & categ = Parse.GetCategorization();
unsigned cRowsets = 1;
if ( 0 != categ.Count() )
cRowsets += categ.Count();
if (Parse.GetOutputColumns().Count() == 0)
THROW( CException(DB_E_ERRORSINCOMMAND) );
XArray<IUnknown *> Unknowns( cRowsets );
//
// If it appears the server went down between the time we made
// the connection and did the execute, attempt once to
// re-establish the connection.
//
int cTries = 1;
XPtr<CMRowsetProps> xProps;
do
{
//
// Use a copy of the properties so the command object isn't
// affected by the implied properties. xProps may be acquired
// in a failed loop if the server disconnects just before the
// setbindings call.
//
if ( xProps.IsNull() )
{
xProps.Set( new CMRowsetProps( _RowsetProps ) );
xProps->SetImpliedProperties( riid, cRowsets );
//
// Check if there are any properties in error. If properties are found
// to be in error, indicate this on _RowsetProps.
//
scResult = xProps->ArePropsInError( _RowsetProps );
if ( S_OK != scResult )
return scResult;
if ( Parse.GetMaxResults() > 0 )
xProps->SetValLong( CMRowsetProps::eid_DBPROPSET_ROWSET,
CMRowsetProps::eid_PROPVAL_MAXROWS,
Parse.GetMaxResults() );
if ( Parse.GetFirstRows() > 0 )
xProps->SetFirstRows( Parse.GetFirstRows() );
}
if ( !HaveQuery() )
_pInternalQuery = QueryInternalQuery();
SCODE scEx = S_OK;
TRY
{
//
// Used for GetSpecification on the rowset
//
IUnknown * pCreatorUnk = 0;
if ( IsGenByOpenRowset() )
{
Win4Assert( !_xSession.IsNull() );
pCreatorUnk = _xSession->GetOuterUnk();
}
else
{
pCreatorUnk = (IUnknown *) _pControllingUnknown;
}
Win4Assert( 0 != xProps.GetPointer() );
_pInternalQuery->Execute( pOuterUnk,
pCrst ? pCrst->CastToStruct() : 0, // Restrictions
Parse.GetPidmap(),
Parse.GetOutputColumns(), // Output columns
Parse.GetSortColumns(), // Sort Order
xProps,
categ, // Categorization
cRowsets,
Unknowns.GetPointer(), // Return interfaces
_aAccessors,
pCreatorUnk);
}
CATCH( CException, e )
{
scEx = e.GetErrorCode();
if ( ( STATUS_CONNECTION_DISCONNECTED != scEx ) ||
( cTries > 1 ) )
RETHROW();
}
END_CATCH;
if ( STATUS_CONNECTION_DISCONNECTED == scEx )
{
Win4Assert( 1 == cTries );
cTries++;
ReleaseInternalQuery();
continue;
}
else
{
Win4Assert( S_OK == scEx );
break;
}
}
while ( TRUE );
// release these categorized rowsets -- they are addref'ed when the
// client does a getreferencedrowset
for ( unsigned x = 1; x < cRowsets; x++ )
Unknowns[ x ]->Release();
XInterface<IUnknown> xUnknown( Unknowns[0] );
if (IID_IUnknown == riid)
{
*ppRowset = xUnknown.GetPointer();
xUnknown.Acquire();
}
else
{
if (IID_NULL != riid)
scResult = xUnknown->QueryInterface( riid, (void **)ppRowset );
Win4Assert( S_OK == scResult ); // should have failed earlier
if (FAILED(scResult))
THROW( CException(scResult) );
}
_dwStatus &= ~(CMD_EXEC_RUNNING);
imp.Revert();
}
CATCH( CException, e )
{
//
// Can't use PostHResult( e...) here because the final SCODE may get translated
//
scResult = e.GetErrorCode();
vqDebugOut(( DEB_ERROR, "Exception %08x while creating query\n", scResult ));
if ( QUERY_E_INVALIDRESTRICTION == scResult )
scResult = DB_E_ERRORSINCOMMAND;
//
// In the case of OpenRowset, don't want to Post DB_E_ERRORSINCOMMAND
//
if ( _fGenByOpenRowset )
{
if( scResult == DB_E_ERRORSINCOMMAND )
scResult = DB_E_NOTABLE;
}
_DBErrorObj.PostHResult( scResult, guidPost );
}
END_CATCH;
UNTRANSLATE_EXCEPTIONS;
#if CIDBG == 1
if ( ( S_OK == scResult ) && ( IID_NULL != riid ) )
Win4Assert( 0 != *ppRowset );
#endif
return scResult;
}
//+---------------------------------------------------------------------------
//
// Method: CRootQuerySpec::Cancel, public
//
// Synopsis: 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.
//
// History: 11-20-97 danleg Created
//----------------------------------------------------------------------------
SCODE CRootQuerySpec::Cancel( void )
{
_DBErrorObj.ClearErrorInfo();
if( 0 == (_dwStatus & CMD_EXEC_RUNNING) )
return S_OK;
return _DBErrorObj.PostHResult(DB_E_CANTCANCEL, IID_ICommand);
}
#if 0 // ICommandValidate not yet implemented
//
// ICommandValidate methods
//
SCODE CRootQuerySpec::ValidateCompletely( void )
{
_DBErrorObj.ClearErrorInfo();
vqDebugOut(( DEB_WARN, "CRootQuerySpec::ValidateCompletely not implemented\n" ));
return PostHResult(E_NOTIMPL, IID_ICommandValidate);
}
SCODE CRootQuerySpec::ValidateSyntax( void )
{
_DBErrorObj.ClearErrorInfo();
vqDebugOut(( DEB_WARN, "CRootQuerySpec::ValidateSyntax not implemented\n" ));
return PostHResult(E_NOTIMPL, IID_ICommandValidate);
}
#endif // 0
//+---------------------------------------------------------------------------
//
// Method: CRootQuerySpec::GetDBSession, public
//
// Synopsis: Return the session object associated with this command
//
// Arguments: [riid] -- IID of the desired interface
// [ppSession] -- pointer to where to return interface pointer
//
// History: 11-20-97 danleg Created
//----------------------------------------------------------------------------
SCODE CRootQuerySpec::GetDBSession(
REFIID riid,
IUnknown ** ppSession )
{
_DBErrorObj.ClearErrorInfo();
if (0 == ppSession)
return _DBErrorObj.PostHResult(E_INVALIDARG, IID_ICommand);
SCODE sc = S_OK;
if ( !_xSession.IsNull() )
{
sc = (_xSession->GetOuterUnk())->QueryInterface( riid, (void **) ppSession );
}
else // there was no session object
{
*ppSession = 0;
sc = S_FALSE;
}
return sc;
}
//
// ICommandText methods
//
//+---------------------------------------------------------------------------
//
// Method: CRootQuerySpec::GetCommandText, public
//
// Synopsis: Echos the current command as text, including all
// post-processing operations added.
//
// Arguments: [pguidDialect] -- Guid denoting the dialect of SQL
// [ppwszCommand] -- Pointer to mem where to return command text
//
// History: 10-01-97 danleg Created from Monarch
//
//----------------------------------------------------------------------------
STDMETHODIMP CRootQuerySpec::GetCommandText
(
GUID * pguidDialect, //@parm INOUT | Guid denoting the dialect of sql
LPOLESTR * ppwszCommand //@parm OUT | Pointer for the command text
)
{
SCODE sc = S_OK;
BOOL fpguidNULL = FALSE;
GUID guidDialect;
_DBErrorObj.ClearErrorInfo();
CLock lck( _mtxCmd );
TRANSLATE_EXCEPTIONS;
TRY
{
if( 0 == ppwszCommand )
{
THROW( CException(E_INVALIDARG) );
}
else
{
*ppwszCommand = 0;
// Substitute a correct GUID for a NULL pguidDialect
if( !pguidDialect )
{
guidDialect = DBGUID_SQL;
pguidDialect = &guidDialect;
// Don't return DB_S_DIALECTIGNORED in this case...
fpguidNULL = TRUE;
}
// If the command has not been set, make sure the buffer
// contains an empty string to return to the consumer
if( !IsCommandSet() )
{
THROW( CException(DB_E_NOCOMMAND) );
}
else
{
// Allocate memory for the string we're going to return to the caller
XArrayOLE<WCHAR> xwszCommand( wcslen(_pwszSQLText) + 1 ) ;
// Copy our saved text into the newly allocated string
wcscpy(xwszCommand.GetPointer(), _pwszSQLText);
// If the text we're giving back is a different dialect than was
// requested, let the caller know what dialect the text is in
if( !fpguidNULL && _guidCmdDialect != *pguidDialect )
{
*pguidDialect = _guidCmdDialect;
sc = DB_S_DIALECTIGNORED;
}
*ppwszCommand = xwszCommand.Acquire();
}
}
}
CATCH( CException, e )
{
sc = _DBErrorObj.PostHResult( e, IID_ICommandText );
if( pguidDialect )
RtlZeroMemory( pguidDialect, sizeof(GUID) );
}
END_CATCH;
UNTRANSLATE_EXCEPTIONS;
return sc;
}
//+---------------------------------------------------------------------------
//
// Method: CRootQuerySpec::SetCommandText, public
//
// Synopsis: Sets the current command text..
//
// Arguments: [rguidDialect] -- Guid denoting the dialect of SQL
// [pwszCommand] -- Command Text
//
// History: 10-01-97 danleg Created from Monarch
//
//----------------------------------------------------------------------------
STDMETHODIMP CRootQuerySpec::SetCommandText
(
REFGUID rguidDialect,
LPCOLESTR pwszCommand
)
{
SCODE sc = S_OK;
// Clear previous Error Object for this thread
_DBErrorObj.ClearErrorInfo();
CLock lck( _mtxCmd );
TRANSLATE_EXCEPTIONS;
TRY
{
// Don't allow text to be set if we've got a rowset open
if( !IsRowsetOpen() )
{
// Check Dialect
if( rguidDialect == DBGUID_SQL || rguidDialect == DBGUID_DEFAULT )
{
//
// Delete existing SQL text
//
delete [] _pwszSQLText;
_pwszSQLText = 0;
//
// Delete Command Tree
//
delete _pQueryTree;
_pQueryTree = 0;
if( (0 == pwszCommand) || (L'\0' == *pwszCommand) )
{
_guidCmdDialect = DBGUID_SQL;
_dwStatus &= ~CMD_TEXT_SET;
_dwStatus &= ~CMD_COLINFO_NOTPREPARED;
}
else
{
//
// Save the text and dialect
//
XArray<WCHAR> xwszSQLText( wcslen(pwszCommand) + 1 );
wcscpy(xwszSQLText.GetPointer(), pwszCommand);
_pwszSQLText = xwszSQLText.Acquire();
_guidCmdDialect = rguidDialect;
// Set status flag that we have set text
_dwStatus |= CMD_TEXT_SET;
_dwStatus |= CMD_COLINFO_NOTPREPARED;
}
if ( _pColumnsInfo )
InitColumns( );
_dwStatus &= ~CMD_TEXT_PREPARED;
_dwStatus &= ~CMD_TREE_BUILT;
// Whenever new text is set on the Command Object,
// the value for QUERY_RESTRICTION should be set to
// an empty string
_RowsetProps.SetValString(
CMRowsetProps::eid_DBPROPSET_MSIDXS_ROWSET_EXT,
CMRowsetProps::eid_MSIDXSPROPVAL_QUERY_RESTRICTION,
L"");
}
else
{
THROW( CException(DB_E_DIALECTNOTSUPPORTED) );
}
}
else
{
THROW( CException(DB_E_OBJECTOPEN) );
}
}
CATCH( CException, e )
{
sc = _DBErrorObj.PostHResult( e, IID_ICommandText );
}
END_CATCH;
UNTRANSLATE_EXCEPTIONS;
return sc;
}
//
// ICommandPrepare methods
//
//+---------------------------------------------------------------------------
//
// Method: CRootQuerySpec::Prepare, public
//
// Synopsis: Given that a SQL text has been set, prepare the statement
//
// History: 10-31-97 danleg Created from Monarch
//
//----------------------------------------------------------------------------
STDMETHODIMP CRootQuerySpec::Prepare
(
ULONG cExpectedRuns
)
{
SCODE sc = S_OK;
// Clear previous Error Object for this thread
_DBErrorObj.ClearErrorInfo();
CLock lck( _mtxCmd );
TRANSLATE_EXCEPTIONS;
TRY
{
// Don't allow a new prepare with an open rowset
if( !IsRowsetOpen() )
{
if( IsCommandSet() )
{
//
// Don't build the tree again if it was built as a result of
// GetCommandTree or Execute, and we haven't done a SetCommandText
// since.
//
if ( !(_dwStatus & CMD_TREE_BUILT) )
{
// Impersonate the session logon user
HANDLE hToken = INVALID_HANDLE_VALUE;
if ( !_xSession.IsNull() )
hToken = _xSession->GetLogonToken();
CImpersonateSessionUser imp( hToken );
sc = BuildTree( );
if ( DB_S_NORESULT == sc )
return sc;
_dwStatus |= CMD_TEXT_PREPARED;
if ( _pColumnsInfo )
InitColumns();
imp.Revert();
}
}
else
sc = DB_E_NOCOMMAND;
}
else
{
sc = DB_E_OBJECTOPEN;
}
}
CATCH( CException, e )
{
sc = _DBErrorObj.PostHResult( e, IID_ICommandText );
}
END_CATCH;
UNTRANSLATE_EXCEPTIONS;
return sc;
}
//+---------------------------------------------------------------------------
//
// Method: CRootQuerySpec::Unprepare, public
//
// Synopsis: Unprepare the current prepared command plan, if there is one.
//
// History: 10-31-97 danleg Created from Monarch
//
//----------------------------------------------------------------------------
STDMETHODIMP CRootQuerySpec::Unprepare
(
)
{
SCODE sc = S_OK;
// Clear previous Error Object for this thread
_DBErrorObj.ClearErrorInfo();
CLock lck( _mtxCmd );
TRANSLATE_EXCEPTIONS;
TRY
{
if( !IsRowsetOpen() )
{
_dwStatus &= ~CMD_TEXT_PREPARED;
_dwStatus |= CMD_COLINFO_NOTPREPARED;
if ( _pColumnsInfo )
InitColumns( );
}
else
THROW( CException(DB_E_OBJECTOPEN) );
}
CATCH( CException, e )
{
sc = _DBErrorObj.PostHResult( e, IID_ICommandPrepare );
}
END_CATCH;
UNTRANSLATE_EXCEPTIONS;
return sc;
}
//
// IQuery methods
//
//+---------------------------------------------------------------------------
//
// Method: CRootQuerySpec::AddPostProcessing, public
//
// Synopsis: Add to the top of a command tree
//
// Arguments: [ppRoot] -- DBCOMMANDTREE node at root of tree
// [fCopy] - TRUE if command tree should be copied
//
// History: 29 Jun 95 AlanW Created
//
//----------------------------------------------------------------------------
BOOL CheckForPriorTree( const CDbCmdTreeNode *pNode )
{
return pNode->GetCommandType() == DBOP_prior_command_tree;
}
SCODE CRootQuerySpec::AddPostProcessing(
DBCOMMANDTREE * * ppRoot,
BOOL fCopy)
{
_DBErrorObj.ClearErrorInfo();
// OLEDB spec. bug #????; fCopy is non-sensical
if (0 == ppRoot || FALSE == fCopy)
return _DBErrorObj.PostHResult(E_INVALIDARG, IID_IQuery);
SCODE sc = S_OK;
TRANSLATE_EXCEPTIONS;
TRY
{
_CheckRootNode( *ppRoot );
XPtr<CDbCmdTreeNode> TreeCopy( CDbCmdTreeNode::CastFromStruct(*ppRoot)->Clone() );
if (0 == TreeCopy.GetPointer())
THROW(CException(E_OUTOFMEMORY));
ULONG cNodes = 0;
XArrayOLE<DBCOMMANDTREE *> pNodes;
_FindTreeNodes( TreeCopy.GetPointer(),
cNodes,
pNodes,
CheckForPriorTree );
if (cNodes != 1)
{
vqDebugOut((DEB_WARN, "CRootQuerySpec::AddPostProcessing - "
"%d references to prior tree found\n",
cNodes));
THROW(CException(E_INVALIDARG)); // DB_E_BADCOMMANDTREE???
}
//
// The command tree node with DBOP_prior_command_tree can have
// siblings, but it must not have any children.
// Likewise, the original command tree can have children, but it
// must not have any siblings.
// Splice the trees together by copying the root node of the
// original tree onto the DBOP_prior_command_tree node, then
// freeing the orginal root node.
//
if (pNodes[0]->pctFirstChild != 0 ||
pNodes[0]->wKind != DBVALUEKIND_EMPTY)
THROW(CException(E_INVALIDARG)); // DB_E_BADCOMMANDTREE???
// Perhaps we should just substitute a DBOP_table_identifier
// node with default table in this case.
if (0 == _pQueryTree)
THROW(CException(E_INVALIDARG)); // DB_E_NOCOMMANDTREE???
//
// Transfer the pointers and values from the root node
// to the prior_command_tree node.
//
_pQueryTree->TransferNode( CDbCmdTreeNode::CastFromStruct(pNodes[0]) );
Win4Assert(0 == _pQueryTree->GetFirstChild() &&
0 == _pQueryTree->GetNextSibling() &&
DBVALUEKIND_EMPTY == _pQueryTree->GetValueType());
delete _pQueryTree;
_pQueryTree = TreeCopy.Acquire();
}
CATCH( CException, e )
{
sc = _DBErrorObj.PostHResult( e, IID_IQuery );
}
END_CATCH;
UNTRANSLATE_EXCEPTIONS;
return sc;
}
//+---------------------------------------------------------------------------
//
// Method: CRootQuerySpec::GetCardinalityEstimate, public
//
// Synopsis: Get estimated cardinality of the query tree
//
// Arguments: [pulCardinality] -- Pointer to memory to hold cardinality
//
// History: 29 Jun 95 AlanW Created
// 2 May 97 KrishnaN Added this header block
//
//----------------------------------------------------------------------------
SCODE CRootQuerySpec::GetCardinalityEstimate(
DBORDINAL * pulCardinality)
{
_DBErrorObj.ClearErrorInfo();
vqDebugOut(( DEB_WARN, "CRootQuerySpec::GetCardinalityEstimate not implemented\n" ));
return _DBErrorObj.PostHResult(S_FALSE, IID_IQuery);
}
//
// ICommandProperties methods
//
//+---------------------------------------------------------------------------
//
// Method: DetermineScodeIndex
//
// Synopsis: Returns an index into a static array of SCODEs
//
// NOTE: This function will go away once CRowsetProperties and
// CMRowsetProps are merged completely.
//
// Arguments: [sc] - SCODE for which an index is returned
//
// History: 01-05-98 danleg Created
//
//----------------------------------------------------------------------------
inline ULONG DetermineScodeIndex
(
SCODE sc
)
{
switch( sc )
{
case S_OK: return 0;
case DB_S_ERRORSOCCURRED: return 1;
case E_FAIL:
default: return 2;
case E_INVALIDARG: return 3;
case E_OUTOFMEMORY: return 4;
case DB_E_ERRORSOCCURRED: return 5;
}
}
//+---------------------------------------------------------------------------
//
// Method: DetermineNewSCODE
//
// Synopsis: Given two SCODEs returned by the two property handling
// mechanisms used, returned a resultant SCODE to return from
// Get/SetProperties.
//
// NOTE: This function will go away once CRowsetProperties and
// CMRowsetProps are merged completely.
//
// Arguments:
//
// History: 01-05-98 danleg Created
//
//----------------------------------------------------------------------------
SCODE DetermineNewSCODE
(
SCODE sc1,
SCODE sc2
)
{
ULONG isc1 = DetermineScodeIndex(sc1),
isc2 = DetermineScodeIndex(sc2);
static const SCODE s_rgPropHresultMap[6][6] =
{
{S_OK, DB_S_ERRORSOCCURRED, E_FAIL, E_INVALIDARG, E_OUTOFMEMORY, DB_S_ERRORSOCCURRED},
{DB_S_ERRORSOCCURRED, DB_S_ERRORSOCCURRED, E_FAIL, E_INVALIDARG, E_OUTOFMEMORY, DB_S_ERRORSOCCURRED},
{E_FAIL, E_FAIL, E_FAIL, E_FAIL, E_FAIL, E_FAIL},
{E_INVALIDARG, E_INVALIDARG, E_FAIL, E_INVALIDARG, E_INVALIDARG, E_INVALIDARG},
{E_OUTOFMEMORY, E_OUTOFMEMORY, E_FAIL, E_OUTOFMEMORY, E_OUTOFMEMORY, E_OUTOFMEMORY},
{DB_S_ERRORSOCCURRED, DB_S_ERRORSOCCURRED, E_FAIL, E_INVALIDARG, E_OUTOFMEMORY, DB_E_ERRORSOCCURRED},
};
return s_rgPropHresultMap[isc1][isc2];
}
//+---------------------------------------------------------------------------
//
// Method: CRootQuerySpec::GetProperties, public
//
// Synopsis: Get rowset properties
//
// Arguments: [cPropertySetIDs] - number of desired properties or 0
// [rgPropertySetIDs] - array of desired properties or NULL
// [pcPropertySets] - number of property sets returned
// [prgPropertySets] - array of returned property sets
//
// History: 16 Nov 95 AlanW Created
// 02-22-98 danleg Changed to use CMRowsetProps
//
//----------------------------------------------------------------------------
SCODE CRootQuerySpec::GetProperties(
const ULONG cPropertySetIDs,
const DBPROPIDSET rgPropertySetIDs[],
ULONG * pcPropertySets,
DBPROPSET ** prgPropertySets)
{
_DBErrorObj.ClearErrorInfo();
SCODE sc = S_OK;
TRANSLATE_EXCEPTIONS;
TRY
{
_RowsetProps.GetPropertiesArgChk( cPropertySetIDs,
rgPropertySetIDs,
pcPropertySets,
prgPropertySets );
sc = _RowsetProps.GetProperties( cPropertySetIDs,
rgPropertySetIDs,
pcPropertySets,
prgPropertySets );
}
CATCH( CException, e )
{
//
// Don't PostHResult here. Let the caller do the posting.
//
sc = _DBErrorObj.PostHResult( e, IID_ICommandProperties );
}
END_CATCH;
UNTRANSLATE_EXCEPTIONS;
return sc;
} //GetProperties
//+---------------------------------------------------------------------------
//
// Method: CRootQuerySpec::SetProperties, public
//
// Synopsis: Set rowset properties
//
// Arguments: [cPropertySets] - number of property sets
// [rgProperties] - array of property sets to be set
//
// History: 16 Nov 95 AlanW Created
//
//----------------------------------------------------------------------------
SCODE CRootQuerySpec::SetProperties(
ULONG cPropertySets,
DBPROPSET rgPropertySets[])
{
_DBErrorObj.ClearErrorInfo();
if ( HaveQuery() && _pInternalQuery->IsQueryActive() )
return _DBErrorObj.PostHResult(DB_E_OBJECTOPEN, IID_ICommandProperties);
//
// Quick return
//
if( cPropertySets == 0 )
return S_OK;
SCODE sc = S_OK;
TRANSLATE_EXCEPTIONS;
TRY
{
CUtlProps::SetPropertiesArgChk( cPropertySets, rgPropertySets );
if ( IsRowsetOpen() )
THROW( CException(DB_E_OBJECTOPEN) );
DWORD dwPropFlags = _RowsetProps.GetPropertyFlags();
sc = _RowsetProps.SetProperties( cPropertySets, rgPropertySets );
if ( SUCCEEDED( sc ) )
{
if ( _pColumnsInfo &&
_RowsetProps.GetPropertyFlags() != dwPropFlags )
InitColumns();
}
}
CATCH( CException, e )
{
//
// Don't PostHResult here. Let the caller do the posting.
//
sc = e.GetErrorCode();
}
END_CATCH;
UNTRANSLATE_EXCEPTIONS;
return sc;
}
//
// IServiceProperties methods
//
//+---------------------------------------------------------------------------
//
// Method: CRootQuerySpec::GetPropertyInfo, public
//
// Synopsis: Get rowset properties
//
// Arguments: [cPropertySetIDs] - number of desired properties or 0
// [rgPropertySetIDs] - array of desired properties or NULL
// [pcPropertySets] - number of property sets returned
// [prgPropertySets] - array of returned property sets
// [ppwszDesc] - if non-zero, property descriptions are
// returneed
//
// History: 16 Nov 95 AlanW Created
//
//----------------------------------------------------------------------------
SCODE CRootQuerySpec::GetPropertyInfo(
const ULONG cPropertySetIDs,
const DBPROPIDSET rgPropertySetIDs[],
ULONG * pcPropertySets,
DBPROPINFOSET ** prgPropertySets,
WCHAR ** ppwszDesc)
{
if ( (0 != cPropertySetIDs && 0 == rgPropertySetIDs) ||
0 == pcPropertySets ||
0 == prgPropertySets )
{
if (pcPropertySets)
*pcPropertySets = 0;
if (prgPropertySets)
*prgPropertySets = 0;
return _DBErrorObj.PostHResult(E_INVALIDARG, IID_IServiceProperties);
}
SCODE sc = S_OK;
*pcPropertySets = 0;
*prgPropertySets = 0;
if (ppwszDesc)
*ppwszDesc = 0;
TRANSLATE_EXCEPTIONS;
TRY
{
sc = _PropInfo.GetPropertyInfo( cPropertySetIDs,
rgPropertySetIDs,
pcPropertySets,
prgPropertySets,
ppwszDesc );
// Don't PostHResult here -- it's a good chance it's a scope
// property that we're expecting to fail. Spare the expense.
// The child object will post the error for us.
}
CATCH( CException, e )
{
_DBErrorObj.PostHResult(e.GetErrorCode(), IID_IServiceProperties);
sc = GetOleError(e);
}
END_CATCH;
UNTRANSLATE_EXCEPTIONS;
return sc;
}
//+---------------------------------------------------------------------------
//
// Method: CRootQuerySpec::SetRequestedProperties, public
//
// Synopsis: Set rowset properties
//
// Arguments: [cPropertySets] - number of property sets
// [rgProperties] - array of property sets to be set
//
// History: 16 Nov 95 AlanW Created
//
//----------------------------------------------------------------------------
SCODE CRootQuerySpec::SetRequestedProperties(
ULONG cPropertySets,
DBPROPSET rgPropertySets[])
{
if ( HaveQuery() && _pInternalQuery->IsQueryActive() )
return _DBErrorObj.PostHResult(DB_E_OBJECTOPEN, IID_IServiceProperties);
if ( 0 != cPropertySets && 0 == rgPropertySets)
return _DBErrorObj.PostHResult(E_INVALIDARG, IID_IServiceProperties);
SCODE sc = S_OK;
TRANSLATE_EXCEPTIONS;
TRY
{
sc = _RowsetProps.SetProperties( cPropertySets,
rgPropertySets );
// Don't PostHResult here -- it's a good chance it's a scope
// property that we're expecting to fail. Spare the expense.
// The child object will post the error for us.
}
CATCH( CException, e )
{
_DBErrorObj.PostHResult(e.GetErrorCode(), IID_IServiceProperties);
sc = GetOleError(e);
}
END_CATCH;
UNTRANSLATE_EXCEPTIONS;
return sc;
}
//+---------------------------------------------------------------------------
//
// Method: CRootQuerySpec::SetSuppliedProperties, public
//
// Synopsis: Set rowset properties
//
// Arguments: [cPropertySets] - number of property sets
// [rgProperties] - array of property sets to be set
//
// History: 16 Nov 95 AlanW Created
//
//----------------------------------------------------------------------------
SCODE CRootQuerySpec::SetSuppliedProperties(
ULONG cPropertySets,
DBPROPSET rgPropertySets[])
{
if ( HaveQuery() && _pInternalQuery->IsQueryActive() )
return _DBErrorObj.PostHResult(DB_E_OBJECTOPEN, IID_IServiceProperties);
if ( 0 != cPropertySets && 0 == rgPropertySets)
return _DBErrorObj.PostHResult(E_INVALIDARG, IID_IServiceProperties);
SCODE sc = S_OK;
TRANSLATE_EXCEPTIONS;
TRY
{
sc = _RowsetProps.SetProperties( cPropertySets,
rgPropertySets );
// Don't PostHResult here -- it's a good chance it's a scope
// property that we're expecting to fail. Spare the expense.
// The child object will post the error for us.
}
CATCH( CException, e )
{
_DBErrorObj.PostHResult(e.GetErrorCode(), IID_IServiceProperties);
sc = GetOleError(e);
}
END_CATCH;
UNTRANSLATE_EXCEPTIONS;
return sc;
}
//+---------------------------------------------------------------------------
//
// Method: CRootQuerySpec::_CheckRootNode, private
//
// Synopsis: Check a client's root node for validity
//
// Arguments: [pRoot] -- DBCOMMANDTREE node at root of tree
//
// Notes: A command tree root node may have children, but it
// may not have siblings.
//
// History: 29 Jun 95 AlanW Created
//
//----------------------------------------------------------------------------
void CRootQuerySpec::_CheckRootNode(
const DBCOMMANDTREE * pRoot)
{
if (pRoot->pctNextSibling)
THROW(CException(E_INVALIDARG));
}
//+---------------------------------------------------------------------------
//
// Method: CRootQuerySpec::GetColumnsInfo, private
//
// Synopsis: Create an IColumnsInfo* for the columns in the query tree
//
// Arguments: -none-
//
// Returns: CColumnsInfo* - a pointer to a CColumnsInfo that implements
// IColumnsInfo.
//
//----------------------------------------------------------------------------
static GUID guidBmk = DBBMKGUID;
CColumnsInfo * CRootQuerySpec::GetColumnsInfo()
{
if ( 0 == _pColumnsInfo )
{
_pColumnsInfo = new CColumnsInfo( *((IUnknown *) (ICommand *) this),
_DBErrorObj, FALSE );
InitColumns( );
}
return _pColumnsInfo;
}
//+---------------------------------------------------------------------------
//
// Method: CRootQuerySpec::InitColumns, private
//
// Synopsis: Reinitialize the columns associated with the CColumnsInfo
//
// Arguments: -none-
//
//----------------------------------------------------------------------------
void CRootQuerySpec::InitColumns( )
{
if ( _pQueryTree && !(_dwStatus & CMD_COLINFO_NOTPREPARED) )
{
BOOL fSequential = TRUE; // need to know for CColumnsInfo ctor
//
// Fault-in columnsinfo.
//
CParseCommandTree Parse;
Parse.ParseTree( _pQueryTree );
CCategorizationSet & rCateg = Parse.GetCategorization();
CPidMapperWithNames & pidmap = Parse.GetPidmap();
CColumnSet const * pColSet = &Parse.GetOutputColumns();
if ( 0 != rCateg.Count() )
{
//
// Ole-db spec says that for categorization, we must use the
// top-level columns only.
//
pColSet = &(rCateg.Get(0)->GetColumnSet());
}
for ( unsigned i = 0; i < pColSet->Count(); i++ )
{
CFullPropSpec const * pPropSpec = pidmap.PidToName( pColSet->Get(i));
if (pPropSpec->IsPropertyPropid() &&
pPropSpec->GetPropertyPropid() == PROPID_DBBMK_BOOKMARK &&
pPropSpec->GetPropSet() == guidBmk)
fSequential = FALSE;
}
if ( _RowsetProps.GetPropertyFlags() &
( eLocatable | eScrollable ) )
fSequential = FALSE;
_pColumnsInfo->InitColumns( *pColSet,
pidmap,
fSequential );
}
else
{
_pColumnsInfo->InitColumns( (_dwStatus & CMD_COLINFO_NOTPREPARED) );
}
}
//+---------------------------------------------------------------------------
//
// Member: CRootQuerySpec::CreateAccessor
//
// Synopsis: Makes an accessor that a client can use to get data.
//
// Arguments: [dwAccessorFlags] -- read/write access requested
// [cBindings] -- # of bindings in rgBindings
// [rgBindings] -- array of bindings for the accessor to support
// [cbRowSize] -- ignored for IRowset
// [phAccessor] -- returns created accessor if all is ok
// [rgBindStatus] -- array of binding statuses
//
// Returns: SCODE error code
//
// History: 11-07-95 srikants Created
//
// Notes:
//
//----------------------------------------------------------------------------
STDMETHODIMP CRootQuerySpec::CreateAccessor(
DBACCESSORFLAGS dwAccessorFlags,
DBCOUNTITEM cBindings,
const DBBINDING rgBindings[],
DBLENGTH cbRowSize,
HACCESSOR * phAccessor,
DBBINDSTATUS rgBindStatus[])
{
_DBErrorObj.ClearErrorInfo();
SCODE sc = S_OK;
if (0 == phAccessor || (0 != cBindings && 0 == rgBindings))
return _DBErrorObj.PostHResult(E_INVALIDARG, IID_IAccessor);
// Make sure pointer is good while zeroing in case of a later error
*phAccessor = 0;
TRANSLATE_EXCEPTIONS;
TRY
{
CColumnsInfo * pColumnsInfo = 0;
XPtr<CRowDataAccessor> Accessor(new CRowDataAccessor(dwAccessorFlags,
cBindings,
rgBindings,
rgBindStatus,
(_RowsetProps.GetPropertyFlags() & eExtendedTypes) != 0,
(IUnknown *) (ICommand *) this,
pColumnsInfo
));
CLock lck( _mtxCmd );
_aAccessors.Add( Accessor.GetPointer() );
*phAccessor = (Accessor.Acquire())->Cast();
}
CATCH(CException, e)
{
sc = _DBErrorObj.PostHResult( e, IID_IAccessor );
_DBErrorObj.PostHResult( sc, IID_IAccessor );
}
END_CATCH;
UNTRANSLATE_EXCEPTIONS;
return sc;
}
//+---------------------------------------------------------------------------
//
// Member: CRootQuerySpec::GetBindings, private
//
// Synopsis: Returns an accessor's bindings
//
// Arguments: [hAccessor] -- accessor being queried
// [dwAccessorFlags] -- returns read/write access of accessor
// [pcBindings] -- returns # of bindings in rgBindings
// [prgBindings] -- returns array of bindings
//
// Returns: SCODE error code
//
// History: 14 Dec 94 dlee Created
//
//----------------------------------------------------------------------------
STDMETHODIMP CRootQuerySpec::GetBindings(
HACCESSOR hAccessor,
DBACCESSORFLAGS * pdwAccessorFlags,
DBCOUNTITEM * pcBindings,
DBBINDING * * prgBindings) /*const*/
{
_DBErrorObj.ClearErrorInfo();
SCODE sc = S_OK;
if (0 == pdwAccessorFlags ||
0 == pcBindings ||
0 == prgBindings)
{
// fill in error values where possible
if (pdwAccessorFlags)
*pdwAccessorFlags = DBACCESSOR_INVALID;
if (pcBindings)
*pcBindings = 0;
if (prgBindings)
*prgBindings = 0;
return _DBErrorObj.PostHResult(E_INVALIDARG, IID_IAccessor);
}
*pdwAccessorFlags = DBACCESSOR_INVALID;
*pcBindings = 0;
*prgBindings = 0;
TRANSLATE_EXCEPTIONS;
TRY
{
CLock lck( _mtxCmd );
CAccessor * pAccessor = (CAccessor *)_aAccessors.Convert( hAccessor );
pAccessor->GetBindings(pdwAccessorFlags, pcBindings, prgBindings);
}
CATCH(CException, e)
{
sc = _DBErrorObj.PostHResult( e, IID_IAccessor );
_DBErrorObj.PostHResult( sc, IID_IAccessor );
}
END_CATCH;
UNTRANSLATE_EXCEPTIONS;
return sc;
}
//+---------------------------------------------------------------------------
//
// Member: CRootQuerySpec::AddRefAccessor, private
//
// Synopsis: Frees an accessor
//
// Arguments: [hAccessor] -- accessor being freed
// [pcRefCount] -- pointer to residual refcount (optional)
//
// Returns: SCODE error code
//
// History: 14 Dec 94 dlee Created
//
//----------------------------------------------------------------------------
STDMETHODIMP CRootQuerySpec::AddRefAccessor(
HACCESSOR hAccessor,
ULONG * pcRefCount )
{
_DBErrorObj.ClearErrorInfo();
SCODE sc = S_OK;
TRANSLATE_EXCEPTIONS;
TRY
{
CLock lck( _mtxCmd );
_aAccessors.AddRef( hAccessor, pcRefCount );
}
CATCH(CException, e)
{
sc = _DBErrorObj.PostHResult( e, IID_IAccessor );
_DBErrorObj.PostHResult(sc, IID_IAccessor);
}
END_CATCH;
UNTRANSLATE_EXCEPTIONS;
return sc;
}
//+---------------------------------------------------------------------------
//
// Member: CRootQuerySpec::ReleaseAccessor, private
//
// Synopsis: Frees an accessor
//
// Arguments: [hAccessor] -- accessor being freed
// [pcRefCount] -- pointer to residual refcount (optional)
//
// Returns: SCODE error code
//
// History: 14 Dec 94 dlee Created
//
//----------------------------------------------------------------------------
STDMETHODIMP CRootQuerySpec::ReleaseAccessor(
HACCESSOR hAccessor,
ULONG * pcRefCount )
{
_DBErrorObj.ClearErrorInfo();
SCODE sc = S_OK;
TRANSLATE_EXCEPTIONS;
TRY
{
CLock lck( _mtxCmd );
_aAccessors.Release( hAccessor, pcRefCount );
}
CATCH(CException, e)
{
sc = _DBErrorObj.PostHResult( e, IID_IAccessor );
_DBErrorObj.PostHResult(sc, IID_IAccessor);
}
END_CATCH;
UNTRANSLATE_EXCEPTIONS;
return sc;
}
//+---------------------------------------------------------------------------
//
// Member: CRootQuerySpec::CanConvert, public
//
// Synopsis: Indicate whether a type conversion is valid.
//
// Arguments: [wFromType] -- source type
// [wToType] -- destination type
// [dwConvertFlags] -- read/write access requested
//
// Returns: S_OK if the conversion is available, S_FALSE otherwise.
// E_FAIL, E_INVALIDARG or DB_E_BADCONVERTFLAG on errors.
//
// History: 20 Nov 96 AlanW Created
// 14 Jan 98 VikasMan Passed IDataConvert(OLE-DB data conv.
// interface) to CanConvertType
//
//----------------------------------------------------------------------------
STDMETHODIMP CRootQuerySpec::CanConvert( DBTYPE wFromType, DBTYPE wToType, DBCONVERTFLAGS dwConvertFlags )
{
_DBErrorObj.ClearErrorInfo();
SCODE sc = S_OK;
TRANSLATE_EXCEPTIONS;
TRY
{
if (((dwConvertFlags & DBCONVERTFLAGS_COLUMN) &&
(dwConvertFlags & DBCONVERTFLAGS_PARAMETER)) ||
(dwConvertFlags & ~(DBCONVERTFLAGS_COLUMN |
DBCONVERTFLAGS_PARAMETER |
DBCONVERTFLAGS_ISFIXEDLENGTH |
DBCONVERTFLAGS_ISLONG |
DBCONVERTFLAGS_FROMVARIANT)))
{
sc = DB_E_BADCONVERTFLAG;
}
else if ( ( dwConvertFlags & DBCONVERTFLAGS_FROMVARIANT ) &&
!IsValidFromVariantType(wFromType) )
{
sc = DB_E_BADTYPE;
}
else
{
// Allocate this on the stack
XInterface<IDataConvert> xDataConvert;
BOOL fOk = CAccessor::CanConvertType( wFromType,
wToType,
(_RowsetProps.GetPropertyFlags() & eExtendedTypes) != 0,
xDataConvert );
sc = fOk ? S_OK : S_FALSE;
}
if (FAILED(sc))
_DBErrorObj.PostHResult(sc, IID_IConvertType);
}
CATCH(CException, e)
{
sc = _DBErrorObj.PostHResult( e, IID_IConvertType );
_DBErrorObj.PostHResult( sc, IID_IConvertType );
}
END_CATCH;
UNTRANSLATE_EXCEPTIONS;
return sc;
}
//+---------------------------------------------------------------------------
//
// Member: CRootQuerySpec::BuildTree, public
//
// Synopsis: Takes a cached SQL Text and translates it into a Query Tree
// Called from Execute(), Prepare() and GetCommandTree().
//
// Arguments: [pIID] - IID of interface calling this function
//
//
// Returns: [S_OK] - a command tree was built successfully
// [DB_S_NORESULT] - the command didn't generate a tree
// (e.g SET PROPERTYNAME...)
// All error codes are thrown.
//
//
// History: 10-31-97 danleg Created
// 01-01-98 danleg Moved global views to CreateParser
//
//----------------------------------------------------------------------------
SCODE CRootQuerySpec::BuildTree( )
{
SCODE sc = S_OK;
DBCOMMANDTREE * pDBCOMMANDTREE = 0;
XInterface<IParserTreeProperties> xIPTProperties;
LCID lcidContent = GetLCIDFromString((LPWSTR)_RowsetProps.GetValString(
CMRowsetProps::eid_DBPROPSET_MSIDXS_ROWSET_EXT,
CMRowsetProps::eid_MSIDXSPROPVAL_COMMAND_LOCALE_STRING));
//
// Synch IParserSession's catalog cache with DBPROP_CURRENTCATALOG. If no session,
// catalogs are specified using the catalog..scope() syntax anyway.
//
if ( !_xSession.IsNull() )
{
LPCWSTR pwszCatalog = ((_xSession->GetDataSrcPtr())->GetDSPropsPtr())->GetValString(
CMDSProps::eid_DBPROPSET_DATASOURCE,
CMDSProps::eid_DBPROPVAL_CURRENTCATALOG );
_xpIPSession->SetCatalog( pwszCatalog );
}
// NOTE: For CREATE VIEW and SET PROPERTYNAME calls, ToTree will return DB_S_NORESULT
sc = _xpIPSession->ToTree( lcidContent,
_pwszSQLText,
&pDBCOMMANDTREE,
xIPTProperties.GetPPointer() );
if( S_OK == sc )
{
//
// Set flag to indicate that BuildTree is calling SetCommandTree
//
_dwStatus |= CMD_TEXT_TOTREE;
// Set the command tree
sc = SetCommandTree( &pDBCOMMANDTREE, DBCOMMANDREUSE_NONE, FALSE );
if ( SUCCEEDED(sc) )
{
VARIANT vVal;
VariantInit( &vVal );
// We need to set the QUERY_RESTRICTION so it can be cloned into
// the rowset properties object
if( SUCCEEDED( xIPTProperties->GetProperties(PTPROPS_CIRESTRICTION,
&vVal)) )
{
_RowsetProps.SetValString(
CMRowsetProps::eid_DBPROPSET_MSIDXS_ROWSET_EXT,
CMRowsetProps::eid_MSIDXSPROPVAL_QUERY_RESTRICTION,
(LPCWSTR)V_BSTR(&vVal) );
}
VariantClear( &vVal );
}
}
else if( FAILED(sc) && !xIPTProperties.IsNull() )
{
// Retrieve Error Information
SCODE sc2 = S_OK;
DISPPARAMS * pDispParams = NULL;
VARIANT vVal[3];
ULONG ul;
for(ul=0; ul<NUMELEM(vVal); ul++)
VariantInit(&vVal[ul]);
if( SUCCEEDED(sc2 = xIPTProperties->GetProperties(
PTPROPS_ERR_IDS, &vVal[0])) )
{
if( (V_I4(&vVal[0]) > 0) && SUCCEEDED(sc2 = xIPTProperties->GetProperties(
PTPROPS_ERR_HR, &vVal[1])) )
{
if( SUCCEEDED(sc2 = xIPTProperties->GetProperties(
PTPROPS_ERR_DISPPARAM, &vVal[2])) )
{
// Change safearray into DISPPARMS
SAFEARRAY* psa = V_ARRAY(&vVal[2]);
if( psa && psa->rgsabound[0].cElements)
{
pDispParams = (DISPPARAMS*)CoTaskMemAlloc(sizeof(DISPPARAMS));
if( pDispParams )
{
pDispParams->cNamedArgs = 0;
pDispParams->rgdispidNamedArgs = NULL;
pDispParams->cArgs = psa->rgsabound[0].cElements;
pDispParams->rgvarg =
(VARIANT*)CoTaskMemAlloc(sizeof(VARIANTARG) * psa->rgsabound[0].cElements);
if( pDispParams->rgvarg )
{
for (ULONG i=0; i<psa->rgsabound[0].cElements; i++)
{
VariantInit(&(pDispParams->rgvarg[i]));
V_VT(&(pDispParams->rgvarg[i])) = VT_BSTR;
V_BSTR(&(pDispParams->rgvarg[i])) = ((BSTR*)psa->pvData)[i];
((BSTR*)psa->pvData)[i] = NULL;
}
}
}
}
// Post a parser error
_DBErrorObj.PostParserError( V_I4(&vVal[1]),
V_I4(&vVal[0]),
&pDispParams );
// This is the SCODE of the error just posted.
sc2 = V_I4(&vVal[1]);
if ( FAILED(sc2) )
sc = sc2;
Win4Assert( pDispParams == NULL ); // Should be null after post
}
}
}
for ( ul=0; ul<NUMELEM(vVal); ul++ )
VariantClear( &vVal[ul] );
}
if( pDBCOMMANDTREE )
FreeCommandTree( &pDBCOMMANDTREE );
if ( FAILED(sc) )
THROW( CException(sc) );
return sc;
}
//+---------------------------------------------------------------------------
//
// Member: CRootQuerySpec::CreateParser, private
//
// Synopsis: Creates a parser object if one was not passed through a session
//
// History: 11-25-97 danleg Created
//
//----------------------------------------------------------------------------
void CRootQuerySpec::CreateParser()
{
SCODE sc = S_OK;
CLock lck( _mtxCmd );
sc = MakeIParser( _xIParser.GetPPointer() );
if ( FAILED(sc) )
{
Win4Assert( sc != E_INVALIDARG );
THROW( CException(sc) );
}
XInterface<IColumnMapperCreator> xColMapCreator;
XInterface<CImpIParserVerify> xIPVerify(new CImpIParserVerify());
//
// Create an IParserSession object
//
xIPVerify->GetColMapCreator( ((IColumnMapperCreator**)xColMapCreator.GetQIPointer()) );
sc = _xIParser->CreateSession( &DBGUID_MSSQLTEXT,
DEFAULT_MACHINE,
xIPVerify.GetPointer(),
xColMapCreator.GetPointer(),
(IParserSession**)_xpIPSession.GetQIPointer() );
if ( FAILED(sc) )
THROW( CException(sc) );
//
// Set default catalog in the parser session object
//
_xpwszCatalog.Init( MAX_PATH );
ULONG cOut;
sc = xIPVerify->GetDefaultCatalog( _xpwszCatalog.GetPointer(),
_xpwszCatalog.Count(),
&cOut);
//
// _xpwszCatalog isn't long enough
//
Win4Assert( E_INVALIDARG != sc );
if ( FAILED(sc) )
THROW( CException(sc) );
sc = _xpIPSession->SetCatalog( _xpwszCatalog.GetPointer() );
if( FAILED(sc) )
THROW( CException(sc) );
DBCOMMANDTREE * pDBCOMMANDTREE = 0;
XInterface<IParserTreeProperties> xIPTProperties;
LCID lcidContent = GetLCIDFromString((LPWSTR)_RowsetProps.GetValString(
CMRowsetProps::eid_DBPROPSET_MSIDXS_ROWSET_EXT,
CMRowsetProps::eid_MSIDXSPROPVAL_COMMAND_LOCALE_STRING));
//
// Predefined views
//
sc = _xpIPSession->ToTree( lcidContent,
s_pwszPredefinedViews,
&pDBCOMMANDTREE,
xIPTProperties.GetPPointer() );
if ( FAILED(sc) )
THROW( CException(sc) );
} //CreateParser