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