QSAMPLE.CXX

//+------------------------------------------------------------------------- 
//
// 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.
//
// Copyright 1997 Microsoft Corporation. All Rights Reserved.
//
// PROGRAM: qsample.cxx
//
// PURPOSE: Illustrates a minimal query using Microsoft Index Server.
// Uses CICreateCommand and CITextToFullTree helper functions.
//
// PLATFORM: Windows NT
//
//--------------------------------------------------------------------------

#define UNICODE

#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>

// This is found in disptree.cxx

extern void DisplayCommandTree( DBCOMMANDTREE * pNode, ULONG iLevel = 0 );

//+-------------------------------------------------------------------------
//
// 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 )
{
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;

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;

DBPROPSET aPropSet[1];

aPropSet[0].rgProperties = &aProp[0];
aPropSet[0].cProperties = 2;
aPropSet[0].guidPropertySet = guidQueryExt;

XInterface<ICommandProperties> xICommandProperties;
HRESULT hr = pICommand->QueryInterface( IID_ICommandProperties,
xICommandProperties.GetQIPointer() );
if ( FAILED( hr ) )
return hr;

return xICommandProperties->SetProperties( 1, // 1 property set
aPropSet ); // the properties
} //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
// [pwcQueryRestrition] - The actual query string
// [fDisplayTree] - TRUE to display the command tree
//
// Returns: HRESULT result of the query
//
//--------------------------------------------------------------------------

HRESULT DoQuery(
WCHAR const * pwcQueryCatalog,
WCHAR const * pwcQueryMachine,
WCHAR const * pwcQueryRestriction,
BOOL fDisplayTree )
{
// 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() );
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
0, // no sort. eg L"Rank[d]" for rank descending
0, // reserved
&pTree, // resulting tree
0, // no custom properties
0, // no custom properties
GetSystemDefaultLCID() ); // default locale
if ( FAILED( hr ) )
return hr;

// If directed, display the command tree

if ( fDisplayTree )
DisplayCommandTree( pTree );

// 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.

printf( " Size Path\n" );

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 )
printf( "%10I64d %ws\n",
aData[0]->hVal,
aData[1]->pwszVal );
else
printf( "could not retrieve a file's values\n" );
}

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 );

printf( "%d files matched the query '%ws'\n",
cRowsSoFar,
pwcQueryRestriction );

xIAccessor->ReleaseAccessor( hAccessor, 0 );

return hr;
} //DoQuery

//+-------------------------------------------------------------------------
//
// Function: Usage
//
// Synopsis: Displays information about how to use the app and exits
//
//--------------------------------------------------------------------------

void Usage()
{
printf( "usage: QSAMPLE query [/c:catalog] [/m:machine] [/d]\n\n" );
printf( " query a Microsoft Index Server query\n" );
printf( " /c:catalog name of the catalog, default is SYSTEM\n" );
printf( " /m:machine name of the machine, default is local machine\n" );
printf( " /d display the DBCOMMANDTREE, default is off\n" );
exit( -1 );
} //Usage

//+-------------------------------------------------------------------------
//
// Function: wmain
//
// Synopsis: Entry point for the app. Parses command line arguments
// and issues a query.
//
// Arguments: [argc] - Argument count
// [argv] - Arguments
//
//--------------------------------------------------------------------------

extern "C" int __cdecl wmain( int argc, WCHAR * argv[] )
{
WCHAR const * pwcCatalog = L"system"; // default: system catalog
WCHAR const * pwcMachine = L"."; // default: local machine
WCHAR const * pwcRestriction = 0; // no default restriction
BOOL fDisplayTree = FALSE; // don't display the tree

// Parse command line parameters

for ( int i = 1; i < argc; i++ )
{
if ( L'-' == argv[i][0] || L'/' == argv[i][0] )
{
WCHAR wc = toupper( argv[i][1] );

if ( ':' != argv[i][2] && 'D' != wc )
Usage();

if ( 'C' == wc )
pwcCatalog = argv[i] + 3;
else if ( 'M' == wc )
pwcMachine = argv[i] + 3;
else if ( 'D' == wc )
fDisplayTree = TRUE;
else
Usage();
}
else if ( 0 != pwcRestriction )
Usage();
else
pwcRestriction = argv[i];
}

// A query restriction is necessary. Fail if none is given.

if ( 0 == pwcRestriction )
Usage();

// Run the query

HRESULT hr = DoQuery( pwcCatalog,
pwcMachine,
pwcRestriction,
fDisplayTree );

if ( FAILED( hr ) )
{
printf( "the query '%ws' failed with error %#x\n",
pwcRestriction, hr );
return -1;
}

return 0;
} //wmain