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

708 lines
20 KiB
C++

//+---------------------------------------------------------------------------
//
// Microsoft Windows
// Copyright (C) Microsoft Corporation, 1996 - 2000.
//
// File: ixsquery.cxx
//
// Contents: Query SSO active query state class
//
// History: 29 Oct 1996 Alanw Created
//
//----------------------------------------------------------------------------
#include "pch.cxx"
#pragma hdrstop
#include "ixsso.hxx"
#include "ssodebug.hxx"
#include <strrest.hxx>
#include <strsort.hxx>
#include <qlibutil.hxx>
#if CIDBG
#include <stdio.h>
#endif // CIDBG
#include <initguid.h>
#include <nlimport.h>
static const DBID dbcolNull = { {0,0,0,{0,0,0,0,0,0,0,0}},DBKIND_GUID_PROPID,0};
static const GUID guidQueryExt = DBPROPSET_QUERYEXT;
static const GUID guidRowsetProps = DBPROPSET_ROWSET;
//+---------------------------------------------------------------------------
//
// Member: CixssoQuery::GetDefaultCatalog - private inline
//
// Synopsis: Initializes the _pwszCatalog member with the default catalog.
//
// Arguments: NONE
//
// Notes: The IS 2.0 implementation of ISCC::GetDefaultCatalog has two
// flaws that are worked around here. It should return the size
// of the required string when zero is passed in as the input
// length, and it should null terminate the output string.
//
// History: 18 Dec 1996 AlanW Created
// 21 Oct 1997 AlanW Modified to use ISimpleCommandCreator
//
//----------------------------------------------------------------------------
inline void CixssoQuery::GetDefaultCatalog( )
{
ixssoDebugOut(( DEB_ITRACE, "Using default catalog\n" ));
ULONG cchRequired = 0;
SCODE sc = _xCmdCreator->GetDefaultCatalog(0, 0, &cchRequired);
if ( cchRequired == 0 )
{
// IS 2.0 doesn't return required path length
cchRequired = MAX_PATH;
}
else if ( cchRequired > MAX_PATH )
{
THROW( CException(E_INVALIDARG) );
}
cchRequired++; // make room for termination
XArray<WCHAR> pwszCat ( cchRequired );
sc = _xCmdCreator->GetDefaultCatalog(pwszCat.GetPointer(), cchRequired, &cchRequired);
if (FAILED(sc))
THROW( CException(sc) );
Win4Assert( 0 == _pwszCatalog );
_pwszCatalog = pwszCat.Acquire();
// IS 2.0 does not transfer the null character at the end of the string.
_pwszCatalog[ cchRequired ] = L'\0';
return;
}
//+---------------------------------------------------------------------------
//
// Function: ParseCatalogs - public
//
// Synopsis: Parse a comma-separated catalog string, return the count
// of catalogs and the individual catalog and machine names.
//
// Arguments: [pwszCatalog] - input catalog string
// [aCatalogs] - array for returned catalog names
// [aMachines] - array for returned machine names
//
// Returns: ULONG - number of catalogs
//
// Notes:
//
// History: 18 Jun 1997 Alanw Created
//
//----------------------------------------------------------------------------
ULONG ParseCatalogs( WCHAR * pwszCatalog,
CDynArray<WCHAR> & aCatalogs,
CDynArray<WCHAR> & aMachines )
{
ULONG cCatalogs = 0;
while ( 0 != *pwszCatalog )
{
// eat space and commas
while ( L' ' == *pwszCatalog || L',' == *pwszCatalog )
pwszCatalog++;
if ( 0 == *pwszCatalog )
break;
WCHAR awchCat[MAX_PATH];
WCHAR * pwszOut = awchCat;
// is this a quoted path?
if ( L'"' == *pwszCatalog )
{
pwszCatalog++;
while ( 0 != *pwszCatalog &&
L'"' != *pwszCatalog &&
pwszOut < &awchCat[MAX_PATH] )
*pwszOut++ = *pwszCatalog++;
if ( L'"' != *pwszCatalog )
THROW( CIxssoException( MSG_IXSSO_BAD_CATALOG, 0 ) );
pwszCatalog++;
*pwszOut++ = 0;
}
else
{
while ( 0 != *pwszCatalog &&
L',' != *pwszCatalog &&
pwszOut < &awchCat[MAX_PATH] )
*pwszOut++ = *pwszCatalog++;
if ( pwszOut >= &awchCat[MAX_PATH] )
THROW( CIxssoException( MSG_IXSSO_BAD_CATALOG, 0 ) );
// back up over trailing spaces
while ( L' ' == * (pwszOut - 1) )
pwszOut--;
*pwszOut++ = 0;
}
XPtrST<WCHAR> xpCat;
XPtrST<WCHAR> xpMach;
SCODE sc = ParseCatalogURL( awchCat, xpCat, xpMach );
if (FAILED(sc) || xpCat.IsNull() )
THROW( CIxssoException( MSG_IXSSO_BAD_CATALOG, 0 ) );
aCatalogs.Add(xpCat.GetPointer(), cCatalogs);
xpCat.Acquire();
aMachines.Add(xpMach.GetPointer(), cCatalogs);
xpMach.Acquire();
cCatalogs++;
}
if ( 0 == cCatalogs )
THROW( CIxssoException( MSG_IXSSO_BAD_CATALOG, 0 ) );
return cCatalogs;
} //ParseCatalogs
//+---------------------------------------------------------------------------
//
// Member: CixssoQuery::GetDialect - private
//
// Synopsis: Parses the dialect string and returns ISQLANG_V*
//
// Returns: The dialect identifier
//
// History: 19 Nov 1997 dlee Created
// 03 Dec 1998 KrishnaN Defaulting to version 2
//
//----------------------------------------------------------------------------
ULONG CixssoQuery::GetDialect()
{
if ( 0 == _pwszDialect )
return ISQLANG_V2;
ULONG ul = _wtoi( _pwszDialect );
if ( ul < ISQLANG_V1 || ul > ISQLANG_V2 )
return ISQLANG_V2;
return ul;
} //GetDialect
//+---------------------------------------------------------------------------
//
// Member: CixssoQuery::ExecuteQuery - private
//
// Synopsis: Executes the query and builds an IRowset or IRowsetScroll
// as necessary.
//
// Arguments: NONE
//
// History: 29 Oct 1996 Alanw Created
//
//----------------------------------------------------------------------------
void CixssoQuery::ExecuteQuery( )
{
Win4Assert( 0 == _pIRowset ); // Should not have executed query
//
// Setup the variables needed to execute this query; including:
//
// Query
// MaxRecords
// SortBy
//
ixssoDebugOut(( DEB_TRACE, "ExecuteQuery:\n" ));
ixssoDebugOut(( DEB_TRACE, "\tQuery = '%ws'\n", _pwszRestriction ));
if ( 0 == _pwszRestriction || 0 == *_pwszRestriction )
{
THROW( CIxssoException(MSG_IXSSO_MISSING_RESTRICTION, 0) );
}
ixssoDebugOut(( DEB_TRACE, "\tMaxRecords = %d\n", _maxResults ));
ixssoDebugOut(( DEB_TRACE, "\tFirstRowss = %d\n", _cFirstRows ));
//
// Get the columns in the query
//
ixssoDebugOut(( DEB_TRACE, "\tColumns = '%ws'\n", _pwszColumns ));
if ( 0 == _pwszColumns || 0 == *_pwszColumns )
{
THROW( CIxssoException(MSG_IXSSO_MISSING_OUTPUTCOLUMNS, 0) );
}
if ( 0 != _pwszGroup && 0 != *_pwszGroup && _fSequential )
{
// Grouped queries are always non-sequential.
_fSequential = FALSE;
}
//
// Convert the textual form of the restriction, output columns and
// sort columns into a DBCOMMANDTREE.
//
if (InvalidLCID == _lcid)
{
THROW( CIxssoException(MSG_IXSSO_INVALID_LOCALE, 0) );
}
ULONG ulDialect = GetDialect();
CTextToTree textToTree( _pwszRestriction,
ulDialect,
_pwszColumns,
GetColumnMapper(),
_lcid,
_pwszSort,
_pwszGroup,
0,
_maxResults,
_cFirstRows,
TRUE // Keep the friendly column names
);
CDbCmdTreeNode * pDbCmdTree = (CDbCmdTreeNode *) (void *) textToTree.FormFullTree();
XPtr<CDbCmdTreeNode> xDbCmdTree( pDbCmdTree );
CDynArray<WCHAR> apCatalog;
CDynArray<WCHAR> apMachine;
//
// Get the location of the catalog. Use the default if the catalog
// property is not set.
//
if ( 0 == _pwszCatalog )
{
GetDefaultCatalog();
if ( 0 == _pwszCatalog )
THROW( CIxssoException(MSG_IXSSO_NO_SUCH_CATALOG, 0) );
}
ixssoDebugOut(( DEB_TRACE, "\tCatalog = '%ws'\n", _pwszCatalog ));
ULONG cCatalogs = ParseCatalogs( _pwszCatalog, apCatalog, apMachine );
//
// Get the scope specification(s) for the query
// CiScope
// CiFlags
//
Win4Assert( _cScopes <= _apwszScope.Size() );
for ( unsigned i = 0; i < _cScopes; i++)
{
# if CIDBG
char szIdx[10] = "";
if (_cScopes > 1)
sprintf( szIdx, " [%d]", i );
# endif // CIDBG
ixssoDebugOut(( DEB_TRACE, "\tCiScope%s = '%ws'\n", szIdx, _apwszScope[i] ));
//
// Get the query flags.
//
if (i >= _aulDepth.Count())
_aulDepth[i] = QUERY_DEEP;
if ( IsAVirtualPath( _apwszScope[i] ) )
_aulDepth[i] |= QUERY_VIRTUAL_PATH;
ixssoDebugOut(( DEB_TRACE, "\tCiFlags%s = '%ws'\n", szIdx,
_aulDepth.Get(i) & QUERY_DEEP ? L"DEEP":L"SHALLOW" ));
}
//
// We've setup all the parameters to run the query. Run the query
// now.
//
IUnknown * pIUnknown;
ICommand *pCommand = 0;
SCODE sc = _xCmdCreator->CreateICommand(&pIUnknown, 0);
if (SUCCEEDED (sc))
{
XInterface<IUnknown> xUnk( pIUnknown );
sc = pIUnknown->QueryInterface(IID_ICommand, (void **)&pCommand);
}
if ( 0 == pCommand )
{
THROW( CIxssoException(sc, 0) );
}
XInterface<ICommand> xICommand(pCommand);
if (0 == _cScopes)
{
//
// Default path: search everywhere
//
const WCHAR * pwszPath = L"\\";
DWORD dwDepth = QUERY_DEEP;
if ( 1 == cCatalogs )
{
SetScopeProperties( pCommand,
1,
&pwszPath,
&dwDepth,
apCatalog.GetPointer(),
apMachine.GetPointer() );
}
else
{
SetScopeProperties( pCommand,
1,
&pwszPath,
&dwDepth,
0,
0 );
SetScopeProperties( pCommand,
cCatalogs,
0,
0,
apCatalog.GetPointer(),
apMachine.GetPointer() );
}
}
else
{
SetScopeProperties( pCommand,
_cScopes,
(WCHAR const * const *)_apwszScope.GetPointer(),
_aulDepth.GetPointer(),
0,
0 );
SetScopeProperties( pCommand,
cCatalogs,
0,
0,
apCatalog.GetPointer(),
apMachine.GetPointer() );
}
ICommandTree * pICmdTree = 0;
sc = xICommand->QueryInterface(IID_ICommandTree, (void **)&pICmdTree);
if (FAILED (sc) )
{
THROW( CException( QUERY_EXECUTE_FAILED ) );
}
DBCOMMANDTREE * pDbCommandTree = pDbCmdTree->CastToStruct();
sc = pICmdTree->SetCommandTree(&pDbCommandTree, DBCOMMANDREUSE_NONE, FALSE);
pICmdTree->Release();
if ( FAILED(sc) )
{
THROW( CException(sc) );
}
xDbCmdTree.Acquire();
//
// Set properties on the command object.
//
const unsigned MAX_PROPS = 5;
DBPROPSET aPropSet[MAX_PROPS];
DBPROP aProp[MAX_PROPS];
ULONG cProp = 0;
ULONG iHitCountProp = MAX_PROPS;
// Set the property that says whether we want to enumerate
Win4Assert( cProp < MAX_PROPS );
ixssoDebugOut(( DEB_TRACE, "\tUseContentIndex = %s\n",
_fAllowEnumeration ? "FALSE" : "TRUE" ));
aProp[cProp].dwPropertyID = DBPROP_USECONTENTINDEX;
aProp[cProp].dwOptions = DBPROPOPTIONS_OPTIONAL;
aProp[cProp].dwStatus = 0; // Ignored
aProp[cProp].colid = dbcolNull;
aProp[cProp].vValue.vt = VT_BOOL;
aProp[cProp].vValue.boolVal = _fAllowEnumeration ? VARIANT_FALSE :
VARIANT_TRUE;
aPropSet[cProp].rgProperties = &aProp[cProp];
aPropSet[cProp].cProperties = 1;
aPropSet[cProp].guidPropertySet = guidQueryExt;
cProp++;
// Set the property for retrieving hit count
Win4Assert( cProp < MAX_PROPS );
ixssoDebugOut(( DEB_TRACE, "\tNlHitCount = %s\n",
( _dwOptimizeFlags & eOptHitCount ) ? "TRUE" :
"FALSE" ));
aProp[cProp].dwPropertyID = NLDBPROP_GETHITCOUNT;
aProp[cProp].dwOptions = DBPROPOPTIONS_OPTIONAL;
aProp[cProp].dwStatus = 0; // Ignored
aProp[cProp].colid = dbcolNull;
aProp[cProp].vValue.vt = VT_BOOL;
aProp[cProp].vValue.boolVal =
( _dwOptimizeFlags & eOptHitCount ) ? VARIANT_TRUE :
VARIANT_FALSE;
aPropSet[cProp].rgProperties = &aProp[cProp];
aPropSet[cProp].cProperties = 1;
aPropSet[cProp].guidPropertySet = DBPROPSET_NLCOMMAND;
iHitCountProp = cProp;
cProp++;
if ( _dwOptimizeFlags & eOptPerformance )
{
// Set the property for magically fast queries
Win4Assert( cProp < MAX_PROPS );
ixssoDebugOut(( DEB_TRACE, "\tCiDeferNonIndexedTrimming = TRUE\n" ));
aProp[cProp].dwPropertyID = DBPROP_DEFERNONINDEXEDTRIMMING;
aProp[cProp].dwOptions = DBPROPOPTIONS_OPTIONAL;
aProp[cProp].dwStatus = 0; // Ignored
aProp[cProp].colid = dbcolNull;
aProp[cProp].vValue.vt = VT_BOOL;
aProp[cProp].vValue.boolVal = VARIANT_TRUE;
aPropSet[cProp].rgProperties = &aProp[cProp];
aPropSet[cProp].cProperties = 1;
aPropSet[cProp].guidPropertySet = guidQueryExt;
cProp++;
}
// set the start hit property if it is set
if ( _StartHit.Get() )
{
// Set the start hit property
Win4Assert( cProp < MAX_PROPS );
ixssoDebugOut(( DEB_TRACE, "\tStartHit = %x\n", _StartHit.Get() ));
aProp[cProp].dwPropertyID = NLDBPROP_STARTHIT;
aProp[cProp].dwOptions = 0;
aProp[cProp].dwStatus = 0; // Ignored
aProp[cProp].colid = dbcolNull;
V_VT(&(aProp[cProp].vValue)) = VT_ARRAY | VT_I4;
V_ARRAY(&(aProp[cProp].vValue)) = _StartHit.Get();
aPropSet[cProp].rgProperties = &aProp[cProp];
aPropSet[cProp].cProperties = 1;
aPropSet[cProp].guidPropertySet = DBPROPSET_NLCOMMAND;
cProp++;
}
if ( 0 != _iResourceFactor )
{
// Set the query timeout in milliseconds
Win4Assert( cProp < MAX_PROPS );
aProp[cProp].dwPropertyID = DBPROP_COMMANDTIMEOUT;
aProp[cProp].dwOptions = DBPROPOPTIONS_OPTIONAL;
aProp[cProp].dwStatus = 0; // Ignored
aProp[cProp].colid = dbcolNull;
aProp[cProp].vValue.vt = VT_I4;
aProp[cProp].vValue.lVal = _iResourceFactor;
aPropSet[cProp].rgProperties = &aProp[cProp];
aPropSet[cProp].cProperties = 1;
aPropSet[cProp].guidPropertySet = guidRowsetProps;
cProp++;
}
if ( cProp > 0 )
{
ICommandProperties * pCmdProp = 0;
sc = xICommand->QueryInterface(IID_ICommandProperties,
(void **)&pCmdProp);
if (FAILED (sc) )
{
THROW( CException( QUERY_EXECUTE_FAILED ) );
}
sc = pCmdProp->SetProperties( cProp, aPropSet );
pCmdProp->Release();
if ( DB_S_ERRORSOCCURRED == sc ||
DB_E_ERRORSOCCURRED == sc )
{
// Ignore an 'unsupported' error trying to set the GetHitCount
// property.
unsigned cErrors = 0;
for (unsigned i = 0; i < cProp; i++)
{
if ( i == iHitCountProp &&
aProp[i].dwStatus == DBPROPSTATUS_NOTSUPPORTED )
continue;
if (aProp[i].dwStatus != DBPROPSTATUS_OK)
cErrors++;
}
if ( 0 == cErrors )
sc = S_OK;
}
if ( FAILED(sc) || DB_S_ERRORSOCCURRED == sc )
{
THROW( CException( QUERY_EXECUTE_FAILED ) );
}
}
//
// Execute the query
//
sc = xICommand->Execute( 0, // No aggr
IsSequential() ? IID_IRowset : IID_IRowsetExactScroll,
0, // disp params
0, // # rowsets returned
(IUnknown **) &_pIRowset );
if ( FAILED(sc) )
{
ERRORINFO ErrorInfo;
XInterface<IErrorInfo> xErrorInfo;
SCODE sc2 = GetOleDBErrorInfo(xICommand.GetPointer(),
IID_ICommand,
_lcid,
eMostDetailedCIError,
&ErrorInfo,
(IErrorInfo **)xErrorInfo.GetQIPointer());
// Post IErrorInfo only if we have a valid ptr to it
if (SUCCEEDED(sc2) && 0 != xErrorInfo.GetPointer())
{
sc = ErrorInfo.hrError;
THROW( CPostedOleDBException(sc, xErrorInfo.GetPointer()) );
}
else
THROW( CException(sc) );
}
xICommand.Acquire()->Release();
//
// Create some of the restriction specific variables.
//
//
// Get _pIRowsetQueryStatus interface
//
sc = _pIRowset->QueryInterface( IID_IRowsetQueryStatus,
(void **) &_pIRowsetQueryStatus );
if ( FAILED(sc) )
{
THROW( CException(sc) );
}
Win4Assert( 0 != _pIRowsetQueryStatus );
}
//+---------------------------------------------------------------------------
//
// Member: CixssoQuery::GetQueryStatus - private
//
// Synopsis: If a query is active, returns the query status
//
// Arguments: NONE
//
// Returns: DWORD - query status
//
// History: 15 Nov 1996 Alanw Created
//
//----------------------------------------------------------------------------
DWORD CixssoQuery::GetQueryStatus( )
{
DWORD dwStatus = 0;
SCODE sc;
if ( ! _pIRowsetQueryStatus )
THROW( CIxssoException(MSG_IXSSO_NO_ACTIVE_QUERY, 0) );
sc = _pIRowsetQueryStatus->GetStatus( &dwStatus );
if ( ! SUCCEEDED(sc) )
THROW( CException( sc ) );
return dwStatus;
}
//+---------------------------------------------------------------------------
//
// Member: CixssoQuery::IsAVirtualPath - private
//
// Synopsis: Determines if the path passed is a virtual or physical path.
// If it's a virtual path, then / are changed to \.
//
// History: 96-Feb-14 DwightKr Created
//
//----------------------------------------------------------------------------
BOOL CixssoQuery::IsAVirtualPath( WCHAR * wcsPath )
{
Win4Assert ( 0 != wcsPath );
if ( 0 == wcsPath[0] )
{
return TRUE;
}
if ( (L':' == wcsPath[1]) || (L'\\' == wcsPath[0]) )
{
return FALSE;
}
else
{
//
// Flip slashes to backslashes
//
for ( WCHAR *wcsLetter = wcsPath;
*wcsLetter != 0;
wcsLetter++
)
{
if ( L'/' == *wcsLetter )
{
*wcsLetter = L'\\';
}
}
}
return TRUE;
}