CIQUERY.CXX
/*++ 
 
Copyright 1992 - 1998 Microsoft Corporation, All rights reserved. 
 
    THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF 
    ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO 
    THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A 
    PARTICULAR PURPOSE. 
 
Module Name: 
 
    ciquery.cxx 
 
Abstract: 
 
    Perform queries against the content index catalog generated by 
     the Microsoft Index Server. 
 
Platform: 
 
    Windows NT 
 
Revision History: 
 
    David Lee                      Wrote it. 
    Steve Firebaugh                Modified slightly to work in GUI context. 
 
--*/ 
 
#include <stdio.h> 
#include <windows.h> 
 
#define OLEDBVER 0x0200 // need the command tree definitions 
#define DBINITCONSTANTS 
 
#include <oledberr.h> 
#include <oledb.h> 
 
#include <ntquery.h> 
 
#include "catq.h" 
 
 
//+------------------------------------------------------------------------- 
// 
//  Template:   XInterface 
// 
//  Synopsis:   Template for managing ownership of interfaces 
// 
//-------------------------------------------------------------------------- 
 
template<class T> class XInterface 
{ 
public: 
    XInterface( T * p = 0 ) : _p( p ) {} 
    ~XInterface() { if ( 0 != _p ) _p->Release(); } 
    T * operator->() { return _p; } 
    T * GetPointer() const { return _p; } 
    IUnknown ** GetIUPointer() { return (IUnknown **) &_p; } 
    T ** GetPPointer() { return &_p; } 
    void ** GetQIPointer() { return (void **) &_p; } 
    T * Acquire() { T * p = _p; _p = 0; return p; } 
 
private: 
    T * _p; 
}; 
 
//+------------------------------------------------------------------------- 
// 
//  Function:   SetCommandProperties 
// 
//  Synopsis:   Sets the DBPROP_USEEXTENDEDDBTYPES property to TRUE, so 
//              data is returned in PROPVARIANTs, as opposed to the 
//              default, which is OLE automation VARIANTs.  PROPVARIANTS 
//              allow a superset of VARIANT data types.  Use of these 
//              types avoids costly coercions. 
// 
//              Also sets the DBPROP_USECONTENTINDEX property to TRUE, so 
//              the index will always be used to resolve the query (as 
//              opposed to enumerating all the files on the disk), even 
//              if the index is out of date. 
// 
//              Both of these properties are unique to Index Server's OLE DB 
//              implementation. 
// 
//  Arguments:  [pICommand] - The ICommand used to set the property 
// 
//  Returns:    HRESULT result of setting the properties 
// 
//-------------------------------------------------------------------------- 
 
HRESULT SetCommandProperties( ICommand * pICommand, WCHAR const * pwcQueryScope ) 
{ 
    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 guidFSCIFRMWRK = DBPROPSET_FSCIFRMWRK_EXT; 
 
    DBPROP aProp[2]; 
 
    aProp[0].dwPropertyID = DBPROP_USEEXTENDEDDBTYPES; 
    aProp[0].dwOptions = DBPROPOPTIONS_SETIFCHEAP; 
    aProp[0].dwStatus = 0; 
    aProp[0].colid = dbcolNull; 
    aProp[0].vValue.vt = VT_BOOL; 
    aProp[0].vValue.boolVal = VARIANT_TRUE; 
 
    aProp[1] = aProp[0]; 
    aProp[1].dwPropertyID = DBPROP_USECONTENTINDEX; 
 
 
    SAFEARRAYBOUND rgBound[1]; 
    rgBound[0].lLbound = 0; 
    rgBound[0].cElements = 1; 
    DBPROP aScopeProperties[2]; 
    LONG lFlags = QUERY_DEEP; 
    long i = 0; 
 
    SAFEARRAY * pScopes = SafeArrayCreate( VT_BSTR, 1, rgBound ); 
    if (!pScopes) return GetLastError(); 
    SafeArrayPutElement( pScopes, &i, SysAllocString( pwcQueryScope ) ); 
 
    SAFEARRAY * pFlags = SafeArrayCreate( VT_I4, 1, rgBound ); 
    if (!pFlags) return GetLastError(); 
    SafeArrayPutElement( pFlags, &i, &lFlags ); 
 
    memset( aScopeProperties, 0, sizeof aScopeProperties ); 
    aScopeProperties[0].dwPropertyID = DBPROP_CI_INCLUDE_SCOPES; 
    aScopeProperties[0].vValue.vt = VT_BSTR | VT_ARRAY; 
    aScopeProperties[0].vValue.parray = pScopes; 
 
    aScopeProperties[1].dwPropertyID = DBPROP_CI_SCOPE_FLAGS; 
    aScopeProperties[1].vValue.vt = VT_I4 | VT_ARRAY; 
    aScopeProperties[1].vValue.parray = pFlags; 
 
 
 
    DBPROPSET aPropSet[2]; 
 
    aPropSet[1].rgProperties = &aProp[0]; 
    aPropSet[1].cProperties = 2; 
    aPropSet[1].guidPropertySet = guidQueryExt; 
 
    aPropSet[0].rgProperties = &aScopeProperties[0]; 
    aPropSet[0].cProperties = 2; 
    aPropSet[0].guidPropertySet = guidFSCIFRMWRK; 
 
    XInterface<ICommandProperties> xICommandProperties; 
    HRESULT hr = pICommand->QueryInterface( IID_ICommandProperties, 
                                            xICommandProperties.GetQIPointer() ); 
    if ( FAILED( hr ) ) 
        return hr; 
 
    hr = xICommandProperties->SetProperties( 2,          // 2 properties set 
                                             aPropSet ); // the properties 
 
 
    SafeArrayDestroy( pScopes ); 
    SafeArrayDestroy( pFlags ); 
 
    return hr; 
 
} //SetCommandProperties 
 
//+------------------------------------------------------------------------- 
// 
//  Function:   DoQuery 
// 
//  Synopsis:   Creates and executes a query, then displays the results. 
// 
//  Arguments:  [pwcQueryCatalog]    - Catalog name over which query is run 
//              [pwcQueryMachine]    - Machine name on which query is run 
//              [pwcQueryScope]      - Directory scope to constrain the search 
//              [pwcQueryRestrition] - The actual query string 
// 
//  Returns:    HRESULT result of the query 
// 
//-------------------------------------------------------------------------- 
 
HRESULT DoQuery( 
    WCHAR const * pwcQueryCatalog, 
    WCHAR const * pwcQueryMachine, 
    WCHAR const * pwcQueryScope, 
    WCHAR const * pwcQueryRestriction) 
{ 
    // Create an ICommand object.  The default scope for the query is the 
    // entire catalog.  CICreateCommand is a shortcut for making an 
    // ICommand.  The ADVQUERY sample shows the OLE DB equivalent. 
 
    XInterface<ICommand> xICommand; 
    HRESULT hr = CICreateCommand( xICommand.GetIUPointer(), // result 
                                  0,                  // controlling unknown 
                                  IID_ICommand,       // IID requested 
                                  pwcQueryCatalog,    // catalog name 
                                  pwcQueryMachine );  // machine name 
 
    if ( FAILED( hr ) ) 
        return hr; 
 
    // Set required properties on the ICommand 
 
    hr = SetCommandProperties( xICommand.GetPointer(), pwcQueryScope ); 
    if ( FAILED( hr ) ) 
        return hr; 
 
 
    // Create an OLE DB query tree from a text restriction, column 
    // set, and sort order. 
 
    DBCOMMANDTREE * pTree; 
    hr = CITextToFullTree( pwcQueryRestriction,      // the query itself 
                           L"Size,Path",             // columns to return 
                           L"Rank[d]",               // sort rank-descending 
                           0,                        // reserved 
                           &pTree,                   // resulting tree 
                           0,                        // no custom properties 
                           0,                        // no custom properties 
                           GetSystemDefaultLCID() ); // default locale 
    if ( FAILED( hr ) ) 
        return hr; 
 
    // Set the tree in the ICommandTree 
 
    XInterface<ICommandTree> xICommandTree; 
    hr = xICommand->QueryInterface( IID_ICommandTree, 
                                    xICommandTree.GetQIPointer() ); 
    if ( FAILED( hr ) ) 
        return hr; 
     
    hr = xICommandTree->SetCommandTree( &pTree, 
                                        DBCOMMANDREUSE_NONE, 
                                        FALSE ); 
    if ( FAILED( hr ) ) 
        return hr; 
 
    // Execute the query.  The query is complete when Execute() returns 
 
    XInterface<IRowset> xIRowset; 
    hr = xICommand->Execute( 0,            // no aggregating IUnknown 
                             IID_IRowset,  // IID for interface to return 
                             0,            // no DBPARAMs 
                             0,            // no rows affected 
                             xIRowset.GetIUPointer() ); // result 
    if ( FAILED( hr ) ) 
        return hr; 
     
    // Create an accessor, so data can be retrieved from the rowset 
     
    XInterface<IAccessor> xIAccessor; 
    hr = xIRowset->QueryInterface( IID_IAccessor, 
                                   xIAccessor.GetQIPointer() ); 
    if ( FAILED( hr ) ) 
        return hr; 
 
    // Column iOrdinals are parallel with those passed to CiTextToFullTree, 
    // so MapColumnIDs isn't necessary.  These binding values for dwPart, 
    // dwMemOwner, and wType are the most optimal bindings for Index Server. 
 
    const ULONG cColumns = 2; // 2 for Size and Path 
    DBBINDING aColumns[ cColumns ]; 
    memset( aColumns, 0, sizeof aColumns ); 
 
    aColumns[0].iOrdinal   = 1; // first column specified above (size) 
    aColumns[0].obValue    = 0; // offset where value is written in GetData 
    aColumns[0].dwPart     = DBPART_VALUE;  // retrieve value, not status 
    aColumns[0].dwMemOwner = DBMEMOWNER_PROVIDEROWNED; // Index Server owned 
    aColumns[0].wType      = DBTYPE_VARIANT | DBTYPE_BYREF; // VARIANT * 
 
    aColumns[1] = aColumns[0]; 
    aColumns[1].iOrdinal   = 2; // second column specified above (path) 
    aColumns[1].obValue    = sizeof (PROPVARIANT *); // offset for value 
 
    HACCESSOR hAccessor; 
    hr = xIAccessor->CreateAccessor( DBACCESSOR_ROWDATA, // rowdata accessor 
                                     cColumns,           // # of columns 
                                     aColumns,           // columns 
                                     0,                  // ignored 
                                     &hAccessor,         // result 
                                     0 );                // no status 
    if ( FAILED( hr ) ) 
        return hr; 
 
    // Display the results of the query.  Print file size and file path. 
 
     
    ULONG cRowsSoFar = 0; 
 
    do 
    { 
        ULONG cRowsReturned = 0; 
        const ULONG cRowsAtATime = 10; 
        HROW aHRow[cRowsAtATime]; 
        HROW * pgrHRows = aHRow; 
        hr = xIRowset->GetNextRows( 0,              // no chapter 
                                    0,              // no rows to skip 
                                    cRowsAtATime,   // # rows to get 
                                    &cRowsReturned, // # rows returned 
                                    &pgrHRows);     // resulting hrows 
     
        if ( FAILED( hr ) ) 
            break; 
 
 
        for ( ULONG iRow = 0; iRow < cRowsReturned; iRow++ ) 
        { 
            PROPVARIANT * aData[cColumns]; 
            hr = xIRowset->GetData( aHRow[iRow],  // hrow being accessed 
                                    hAccessor,    // accessor to use 
                                    &aData );     // resulting data 
            if ( FAILED( hr ) ) 
                break; 
 
 
            if ( VT_I8 ==     aData[0]->vt && 
                 VT_LPWSTR == aData[1]->vt ) 
                catqReportResultsVariable( 
                        TEXT("%s"), 
                        aData[1]->pwszVal ); 
            else 
                catqLogComment( TEXT("could not retrieve a file's values" )); 
        } 
     
        if ( 0 != cRowsReturned ) 
            xIRowset->ReleaseRows( cRowsReturned, // # of rows to release 
                                   aHRow,         // rows to release 
                                   0,             // no options 
                                   0,             // no refcounts 
                                   0 );           // no status 
 
        if ( DB_S_ENDOFROWSET == hr ) 
        { 
            hr = S_OK; // succeeded, return S_OK from DoQuery 
            break; 
        } 
 
        if ( FAILED( hr ) ) 
            break; 
 
        cRowsSoFar += cRowsReturned; 
    } while ( TRUE ); 
 
    catqLogCommentVariable( 
            TEXT("%d files matched the query '%s'"), 
            cRowsSoFar, 
            pwcQueryRestriction ); 
 
    xIAccessor->ReleaseAccessor( hAccessor, 0 ); 
 
    return hr; 
} //DoQuery