//+--------------------------------------------------------------------------- // // 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 #include #include #if CIDBG #include #endif // CIDBG #include #include 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 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 & aCatalogs, CDynArray & 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 xpCat; XPtrST 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 xDbCmdTree( pDbCmdTree ); CDynArray apCatalog; CDynArray 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 xUnk( pIUnknown ); sc = pIUnknown->QueryInterface(IID_ICommand, (void **)&pCommand); } if ( 0 == pCommand ) { THROW( CIxssoException(sc, 0) ); } XInterface 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 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; }