mirror of https://github.com/tongzx/nt5src
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
2442 lines
74 KiB
2442 lines
74 KiB
//+---------------------------------------------------------------------------
|
|
//
|
|
// 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
|
|
|