Legal Information

[This is preliminary documentation and subject to change.]

Microsoft Index Server Version 3.0 Beta 1

This is a preliminary document and may be changed substantially prior to final commercial release. This document is provided for informational purposes only and Microsoft makes no warranties, either express or implied, in this document. Information in this document is subject to change without notice. The entire risk of the use or the results of the use of this document remains with the user. The names of companies, products, people, characters, and/or data mentioned herein are fictitious and are in no way intended to represent any real individual, company, product, or event, unless otherwise noted. Complying with all applicable copyright laws is the responsibility of the user. No part of this document may be reproduced or transmitted in any form or by any means, electronic or mechanical, for any purpose, without the express written permission of Microsoft Corporation. If, however, your only means of access is electronic, permission to print one copy is hereby granted.

Portions of this document specify and accompany software that is still in development. Some of the information in this documentation may be inaccurate or may not be an accurate representation of the functionality of final documentation or software. Microsoft assumes no responsibility for any damages that might occur directly or indirectly from these inaccuracies.

Microsoft may have patents, patent applications, trademarks, copyrights, or other intellectual property rights covering subject matter in this document. Except as expressly provided in any written license agreement from Microsoft, the furnishing of this document does not give you any license to these patents, trademarks, copyrights, or other intellectual property.

© 1997-1998 Microsoft Corporation. All rights reserved.

Microsoft, MS-DOS, MS, Windows, and Windows NT are either registered trademarks or trademarks of Microsoft Corporation in the U.S.A. and/or other countries.

International CorrectSpell is a trademark of INSO Corporation.

Other product and company names mentioned herein may be the trademarks of their respective owners.


Programmer's Guide for Index Server, Version 3.0

Microsoft® Index Server is designed to perform content and property searches over file systems.

This guide provides information on how you can exploit Index Server features programmatically:

  • Applications can use the Index Server API to add features that exploit an indexed file system. Using the API, an application can issue queries over file systems and present the results to the user. For a description of this API, see Programming Index Server.
  • The IFilter interface methods are used to extract text from objects for placement in the Microsoft Index Server content index. By supplying implementations of IFilter, you can extend the amount of material that is accessible using Index Server. For more information on customizing Index Server with implementations of IFilter, see Customizing Index Server.

You can also use Index Server administrative tools to configure the server and end-user tools to search interactively. Additional information on Index Server can be found in the Microsoft Index Server documentation included in the Microsoft® Internet Information Server documentation.


Programming Index Server

This section describes the API exposed by Microsoft® Index Server Version 3.0.

Microsoft Index Server is designed to perform content and property searches over file systems. The Index Server API enables applications to add features that exploit an indexed file system. Using the API, an application can issue queries over file systems and present the results to the user.

Most programmatic access to Index Server is through Microsoft® OLE DB. Index Server supports all required interfaces to be an OLE DB provider. For a list of OLE DB interfaces supported, refer to the section OLE DB Interfaces Supported by Index Server. For a list of defaults for OLE DB properties, refer to the section Supported OLE DB Properties. For information about OLE DB, refer to the OLE DB Programmer's Reference.

Index Server supports command trees as they are currently planned to be implemented in OLE DB Version 2.0. To enable command tree definitions in oledb.h, define the constant OLEDBVER in program source files before including oledb.h:

#define OLEDBVER 0x200

Where necessary or convenient, Index Server has defined API functions, ICommandProperty properties, and property set extensions to OLE DB. These extensions make it easier to write applications that make use of Index Server. This section describes these extensions.


Index Server API

Index Server provides functions to find catalogs (indexes), create Microsoft® OLE DB ICommand objects for catalogs, and parse queries using the Index Server query language.

These functions are defined in ntquery.h and are exported from ntquery.lib.


LocateCatalogs

LocateCatalogs finds indexes that can be used to process queries for a files in a specified path. The machine and catalog parameter values returned by LocateCatalogs can be used to create an OLE DB ICommand object for issuing queries over a given scope.

STDAPI LocateCatalogs(
  WCHAR const * pwszScope,
  ULONG iBmk,
  WCHAR * pwszMachine,
  ULONG * pcMachine,
  WCHAR * pwszCatalog,
  ULONG * pcCatalog
);

Parameters

pwszScope
[in] Points to a null-terminated string that specifies the scope for an Index Server query. The scope can be local (e.g., C:\directory) or a remote UNC (e.g., \\MACHINE\SHARE\directory). The scope cannot be a redirected drive letter, that is, a drive letter that refers to a drive on a remote machine. Scopes must be physical, not Internet Information Server virtual scopes.
iBmk
[in] Specifies the 0-based bookmark of the result to be retrieved. Pass 0 to retrieve the first machine and catalog that index pwszScope, 1 to retrieve the second machine and catalog that index pwszScope, and so on. If no index for the bookmark is available, LocateCatalogs returns S_FALSE.
pwszMachine
[out] Points to a buffer where a null-terminated string is written if the function is successful. The result string is the machine name on which a query over the scope pwszScope can be executed.
pcMachine
[in/out] On input, points to a wide character count that specifies the size of pwszMachine. On output, specifies the count of characters used in pwszMachine if the function is successful, or the count of characters needed to store the name of the machine if the buffer is too small. If the buffer is too small, LocateCatalogs returns S_OK.
pwszCatalog
[out] Points to a buffer where a null-terminated string is written if the function is successful. The result string is the machine name on which a query over the scope pwszScope can be executed.
pcCatalog
[in/out] On input, points to a wide character count that specifies the size of pwszCatalog. On output, specifies the count of characters used in pwszCatalog if the function is successful, or the count of characters needed to store the name of the catalog if the buffer is too small. If the buffer is too small, LocateCatalogs returns S_OK.

Return Values

An HRESULT, S_OK if successful. If no machine and catalog can be found that index the scope, or iBmk is beyond the count of machines and catalogs that index the scope, the function returns S_FALSE. If there is an error, LocateCatalogs returns E_FAIL.

If a machine and catalog match is found but the machine and catalog buffers aren't big enough, LocateCatalogs returns S_OK, and fills pcCatalog and pcMachine with the wide character required. Callers of LocateCatalogs must check the return code, pcMachine, and pcCatalog to determine if the call was successful.

Remarks

LocateCatalogs is useful when it is not known what machine and catalog index a scope. If the machine and catalog are known, it's more efficient to execute a query without calling LocateCatalogs.

LocateCatalogs does not verify that the machine and catalog returned are available. If an application fails to issue a query with the machine and catalog returned, it should increment iBmk and call LocateCatalogs again to get the next machine and catalog that index the scope.

Example

This example enumerates all machines and catalogs capable of resolving queries over the scope "C:\directory".

HRESULT hr = S_OK;

for ( ULONG iBmk = 0; S_OK == hr; iBmk++ )
{
    WCHAR awcMachine[ MAX_COMPUTERNAME_LENGTH + 1 ];
    const ULONG cwcMachineBuffer = sizeof awcMachine / sizeof WCHAR;
    ULONG cwcMachine = cwcMachineBuffer;
    WCHAR awcCatalog[ MAX_PATH + 1 ];
    const ULONG cwcCatalogBuffer = sizeof awcCatalog / sizeof WCHAR;
    ULONG cwcCatalog = cwcCatalogBuffer;

    hr = LocateCatalogs( L"c:\\directory",
                         iBmk,
                         awcMachine,
                         &cwcMachine,
                         awcCatalog,
                         &cwcCatalog );
    if ( ( hr == S_OK ) &&
         ( cwcMachine <= cwcMachineBuffer ) &&
         ( cwcCatalog <= cwcCatalogBuffer ) )
    {
        wprintf( L"matching machine and catalog: '%s', '%s'\n",
                awcMachine, awcCatalog );
    }
}


CICreateCommand

CICreateCommand creates an ICommand object for Index Server, and sets the catalog and machine properties on the object. The ICommand object can be used for issuing queries.

STDAPI CICreateCommand(
  IUnknown ** ppICommand,
  IUnknown * pUnkOuter,
  REFIID riid,
  WCHAR const * pwcsCatalog,
  WCHAR const * pwcsMachine
);

Parameters

ppICommand
[out] Address of output variable that receives the interface pointer requested in riid.
pUnkOuter
[in] Points to an optional outer unknown. Can be 0 for no aggregation, in which case riid can be other than IID_IUnknown.
riid
[in] Specifies the IID of the interface returned in ppICommand. Must be IID_IUnknown unless pUnkOuter is 0. Pass IID_ICommand to get an ICommand interface if aggregation isn't needed and pUnkOuter is 0.
pwcsCatalog
[in] Points to a null-terminated string that specifies the name of the catalog used to execute queries. This is the value for the DBPROP_CI_CATALOG_NAME ICommand property.
pwcsMachine
[in] Points to a null-terminated string that specifies the name of the machine on which the query is executed. This is the value for the DBPROP_CI_MACHINE_NAME ICommand property. Specify L"." for the local machine.

Return Values

An HRESULT, S_OK if successful.

Remarks

If interface aggregation isn't required, pass IID_ICommand for riid and 0 for pUnkOuter. Otherwise, call IUnknown::QueryInterface on the returned object to get an ICommand interface.

CICreateCommand does not return an error if the catalog or machine do not exist or are not available. The connection to the catalog and machine are established when ICommand::Execute() is called, and connection errors are returned at that time.

Additional catalog, machine, and scope parameters can be specified after an ICommand is created using the ICommandProperties interface.

Multiple queries can be executed on the ICommand object returned by CICreateCommand, but only one query can exist at a time. Issuing multiple queries on a single ICommand object is much more efficient than creating a new ICommand object for each query.

CICreateCommand is a shortcut for creating OLE DB ICommand objects. It obviates code to create an OLE DB data source object, a session object, an ICommand creation object, then an ICommand object.

Example

This example creates an ICommand object for the system catalog on the local machine.

ICommand * pICommand;
HRESULT hr = CICreateCommand( (IUnknown **) &pICommand, 0, IID_ICommand, L"system", L"." );
if ( SUCCEEDED( hr ) )
{
    // ...
    // execute one or more queries with the ICommand
    // ...
    pICommand->Release();
}


CITextToSelectTree

CITextToSelectTree creates a DBCOMMANDTREE from an Index Server query language string. The command tree returned by this function can be used as the next sibling under a DBOP_table_name node. A DBOP_project node is also required to form a complete command tree.

STDAPI CITextToSelectTree(
  WCHAR const * pwszRestriction,
  DBCOMMANDTREE * * ppTree,
  ULONG cProperties,
  CIPROPERTYDEF * pProperties,
  LCID LocaleID
);

Parameters

pwszRestriction
[in] Points to a null-terminated string specifying an Index Server query. The syntax for queries is described in the Index Server documentation.
ppTree
[out] Address of output variable that receives the command tree built by the function.
cProperties
[in] Count of properties in the pProperties array, or 0 if pProperties is 0.
pProperties
[in] Address of an array of properties that can be referred to by friendly name in pwszRestriction. Column names in the wcsFriendlyName field of each CIPROPERTYDEF must be specified in uppercase. Index Server's built-in properties do not need to be defined to be used. It is an error to define a property with the same friendly name as a built-in property.
LocaleID
[in] The locale ID used for nodes in the tree returned in ppTree that contain an LCID field, including content restrictions and sort order.

Return Values

An HRESULT, S_OK if successful.

Remarks

Command trees created by CITextToSelectTree contain the select portion of a DBCOMMANDTREE. A tree returned by CITextToSelectTree can be combined with project and sort nodes to form a complete command tree. Use CITextToSelectTree instead of CITextToFullTree if the sort order and project columns tree nodes are already available.

The query tree allocated by CITextToSelectTree must be freed either with ICommandTree::FreeCommandTree or passed to ICommandTree::SetCommandTree with the fCopy parameter set to FALSE.

Example

This example creates a DBCOMMANDTREE. A custom property from a Microsoft® Word document named "IssueNumber" of type "Number" is defined and used in the query.

DBCOMMANDTREE * pCompleteTree;
DBCOMMANDTREE * pTableNode;

// ...
// Insert code here to make pCompleteTree a complete tree using pTableNode
// as the DBOP_table_name node that has no query restriction (yet).
// User CoTaskMemAlloc to allocate memory for the nodes.
// ...
//

CIPROPERTYDEF aProperties[1];
const GUID guidOffice = { 0xd5cdd505, 0x2e9c, 0x101b,
                          0x93, 0x97, 0x08, 0x00, 0x2b, 0x2c, 0xf9, 0xae }
                            };
aProperties[0].wcsFriendlyName = L"ISSUENUMBER";
aProperties[0].dbType = DBTYPE_R8;
aProperties[0].dbCol.uGuid.guid = guidOffice;
aProperties[0].dbCol.eKind = DBKIND_GUID_NAME;
aProperties[0].dbCol.pwszName.ulPropid = L"ISSUENUMBER";
DBCOMMANDTREE * pSelectTree;
HRESULT hr = CiTextToSelectTree( L"microsoft and @issuenumber=2",
                                 &pSelectTree,
                                 1,
                                 aProperties,
                                 GetSystemDefaultLCID() );
if ( SUCCEEDED( hr ) )
{
    pTableNode->pctNextSibling = pSelectTree;
    hr = pICommand->SetCommandTree( pCompleteTree,
                                    DBCOMMANDREUSE_NONE,
                                    FALSE );
    if ( SUCCEEDED( hr ) )
    {
        // ...
        // execute a query
        // ...
    }
}

This is the DBCOMMANDTREE pSelectTree created by the example code:

DBCOMMANDTREE pSelectTree Created by Example Code


CITextToFullTree

CITextToFullTree creates a complete DBCOMMANDTREE from a query string, output columns, sort columns, and a set of custom properties.

STDAPI CITextToFullTree(
  WCHAR const * pwszRestriction,
  WCHAR const * pwszColumns,
  WCHAR const * pwszSortColumns,
  WCHAR const * pwszReserved,
  DBCOMMANDTREE * * ppTree,
  ULONG cProperties,
  CIPROPERTYDEF * pProperties,
  LCID LocaleID
);

Parameters

pwszRestriction
[in] Points to a null-terminated string that specifies an Index Server query. The syntax for queries is described in the Index Server documentation.
pwszColumns
[in] Points to a null-terminated string that specifies a comma separated list of column names that are returned in the query results. These columns can be bound by OLE DB accessors.
pwszSortColumns
[in] Points to a null-terminated string that contains a comma separated list of column names that specify the sort order for the query results. A sort direction can be appended to each column name. Use [d] for descending, and [a] for ascending. If no sort order is specified, ascending is the default. Can be 0 for no sort order.
pwszReserved
[in] Must be 0.
ppTree
[out] Address of output variable that receives the command tree built by the function.
cProperties
[in] Count of properties in the pProperties array, or 0 if pProperties is 0.
pProperties
[in] Address of an array of properties that can be referred to by friendly name in pwszRestriction. Column names in the wcsFriendlyName field of each CIPROPERTYDEF must be specified in uppercase. Can be 0 if no properties are being defined and cProperties is 0. Index Server's built-in properties do not need to be defined to be used. It is an error to define a property with the same friendly name as a built-in property.
LocaleID
[in] The locale ID used for nodes in ppTree that contain an LCID field, including content restrictions, sort order, and others.

Return Values

An HRESULT, S_OK if successful.

Remarks

The query tree allocated by CITextToSelectTree must be freed either with ICommandTree::FreeCommandTree or passed to ICommandTree::SetCommandTree with the fCopy parameter set to FALSE.

Example

This sample code creates a command tree. The list of properties returned by the query include myproperty, path, and size. The sort order is first by rank descending, then by path ascending. The default system locale is used to create the command tree.

CIPROPERTYDEF aProperties[1];
const GUID guidOffice = { 0xd5cdd505, 0x2e9c, 0x101b,
                          0x93, 0x97, 0x08, 0x00, 0x2b, 0x2c, 0xf9, 0xae }
                            };
aProperties[0].wcsFriendlyName = L"ISSUENUMBER";
aProperties[0].dbType = DBTYPE_R8;
aProperties[0].dbCol.uGuid.guid = guidOffice;
aProperties[0].dbCol.eKind = DBKIND_GUID_NAME;
aProperties[0].dbCol.pwszName.ulPropid = L"ISSUENUMBER";
DBCOMMANDTREE * pTree;
HRESULT hr = CiTextToFullTree( L"microsoft and @issuenumber=2",
                               L"size,path,issuenumber",
                               L"rank[d],path[a]",
                               &pTree,
                               1,
                               aProperties,
                               GetSystemDefaultLCID() );
if ( SUCCEEDED( hr ) )
{
    hr = pICommand->SetCommandTree( pTree,
                                    DBCOMMANDREUSE_NONE,
                                    FALSE );
    if ( SUCCEEDED( hr ) )
    {
        // ...
        // execute a query
        // ...
    }
}

This is the DBCOMMANDTREE pTree created by the example code:

DBCOMMANDTREE pTree Created by Example Code


ICommand Property Extensions

Index Server defines a number of ICommand properties that can be used to customize queries. The properties are specific to Index Server, and may not apply to other OLE DB providers.

ICommand properties are set using OLE DB's ICommandProperties interface. The properties must be set before a query is executed to have an effect on the query.


Force Use of the Index

DBPROP_USECONTENTINDEX controls the use of the index when processing queries.

If the value is VARIANT_TRUE, the index is always used to resolve the query, even if the index is not up to date. If the value is VARIANT_FALSE and the index is not up to date, the query engine may enumerate the file system to process the query.

This setting affects property value queries like "@size > 1000".

Enumerating the file system can be disk intensive and slow.

Content queries always use the index and never enumerate regardless of the setting of this property.

Certain types of queries require DBPROP_USECONTENTINDEX to be VARIANT_FALSE to return results, for example "#filename *.dll OR #filename *.exe". These queries require enumeration and can't use the index even if the index is up to date.

This property corresponds to the CiForceUseCi variable in Index Server's IDQ files.

This property corresponds to the AllowEnumeration variable in Index Server's ASP Query object.

Property Set | DBPROPSET_QUERYEXT
Property ID | DBPROP_USECONTENTINDEX
Value Type | DBTYPE_BOOL
Default | VARIANT_FALSE


Defer Non-Indexed Trimming

DBPROP_DEFERNONINDEXEDTRIMMING optimizes query execution for a specific type of query.

If the value is VARIANT_TRUE, a maximum number of rows for the result is specified, and the sort order is by rank descending, then scope and security checking are deferred until either the maximum number of rows is reached or the query is completed. This may result in less than the expected number of hits to be returned for queries in which scope checks or security trimming removes results. The default is to optimize for recall; the result set trimming occurs in the course of executing the query.

This flag is best used when the query scope encompasses the entire catalog and all files in the result set have the same security Access Control Lists.

This property corresponds to the CiDeferNonIndexedTrimming variable in Index Server's IDQ files.

This property corresponds to the OptimizeFor variable in Index Server's ASP Query object.

Property Set | DBPROPSET_QUERYEXT
Property ID | DBPROP_DEFERNONINDEXEDTRIMMING
Value Type | DBTYPE_BOOL
Default | VARIANT_FALSE


Use Extended DB Types

DBPROP_USEEXTENDEDDBTYPES controls the data type of columns bound by OLE DB accessors.

If the value is VARIANT_TRUE, accessor column bindings can be based on PROPVARIANTs. By default, only OLE Automation VARIANTs and any directory binding are allowed for accessor column bindings.

Many commonly used properties are stored using types that are not OLE Automation VARIANTs. This causes considerable overhead in the OLE DB accessor used to retrieve the data, since the data must be coerced into a different type.

Clients that can make use of PROPVARIANTS should set DBPROP_USEEXTENDEDDBTYPES to VARIANT_TRUE and use OLE DB accessors with bindings that use PROPVARIANT types.

Property Set | DBPROPSET_QUERYEXT
Property ID | DBPROP_USEEXTENDEDDBTYPES
Value Type | DBTYPE_BOOL
Default | VARIANT_FALSE


Machine Name

The DBPROP_MACHINE property specifies the names of the machine(s) on which a query is to be processed.

If more than one catalog or machine are specified, the properties DBPROP_MACHINE, DBPROP_CI_CATALOG_NAME, DBPROP_CI_INCLUDE_SCOPES, and DBPROP_CI_SCOPE_FLAGS must all have the same number of entries. The exception is that if more than one catalog or machine are specified, the count of scopes and flags can either be the count of catalogs and machines or 1.

If more than one machine or catalog are specified, the query is executed on multiple machines, and the results are merged on the local machine.

Machine names should not include preceding slashes, e.g., \\machine.

The machine name "." refers to the local machine. If the name of the local machine is specified, the query is treated as a remote query.

Property Set | DBPROPSET_CIFRMWRKCORE_EXT
Property ID | DBPROP_MACHINE
Value Type | DBTYPE_ARRAY | DBTYPE_BSTR
Default | "." (the local machine)


Catalog Name

The DBPROP_CI_CATALOG_NAME property specifies the names of the catalog(s) over which the query is processed.

If more than one catalog or machine are specified, the properties DBPROP_MACHINE, DBPROP_CI_CATALOG_NAME, DBPROP_CI_INCLUDE_SCOPES, and DBPROP_CI_SCOPE_FLAGS must all have the same number of entries. The exception is that if more than one catalog or machine are specified, the count of scopes and flags can either be the count of catalogs and machines or 1.

If more than one machine or catalog are specified, the query is executed on multiple machines, and the results are merged on the local machine.

The default catalog installed with Windows NT® is named "SYSTEM". Note that the default catalog may change after Windows NT Version 5.0 Beta 1.

This property corresponds to the CiCatalog variable in Index Server's IDQ files.

Property Set | DBPROPSET_ FSCIFRMWRK_EXT
Property ID | DBPROP_CI_CATALOG_NAME
Value Type | DBTYPE_ARRAY | DBTYPE_BSTR
Default | None


Include Scopes

The DBPROP_CI_INCLUDE_SCOPES property specifies the names of the file path(s) over which the query is processed.

If more than one catalog or machine are specified, the properties DBPROP_MACHINE, DBPROP_CI_CATALOG_NAME, DBPROP_CI_INCLUDE_SCOPES, and DBPROP_CI_SCOPE_FLAGS must all have the same number of entries. The exception is that if more than one catalog or machine are specified, the count of scopes and flags can either be the count of catalogs and machines or 1.

If more than one machine or catalog are specified, the query is executed on multiple machines, and the results are merged on the local machine.

The scope "\" covers the entire catalog. The scope "/" covers the entire Internet Information Server virtual path name space. Scopes can be local drive letter based paths or remote UNC paths.

This property corresponds to the CiScope variable in Index Server's IDQ files.

Property Set | DBPROPSET_ FSCIFRMWRK_EXT
Property ID | DBPROP_CI_INCLUDE_SCOPES
Value Type | DBTYPE_ARRAY | DBTYPE_BSTR
Default | "\" (the entire catalog)


Scope Flags

The DBPROP_CI_SCOPE_FLAGS property specifies attributes of the scopes specified for DBPROP_CI_INCLUDE_SCOPES.

If more than one catalog or machine are specified, the properties DBPROP_MACHINE, DBPROP_CI_CATALOG_NAME, DBPROP_CI_INCLUDE_SCOPES, and DBPROP_CI_SCOPE_FLAGS must all have the same number of entries. The exception is that if more than one catalog or machine are specified, the count of scopes and flags can either be the count of catalogs and machines or 1.

If more than one machine or catalog are specified, the query is executed on multiple machines, and the results are merged on the local machine.

This property corresponds to the CiFlags variable in Index Server's IDQ files.

Values

QUERY_SHALLOW
Only files in the scope directory are included in the results. Cannot be combined with QUERY_DEEP.
QUERY_DEEP
Files in the scope directory and all subdirectories are included in the results. Cannot be combined with QUERY_SHALLOW.
QUERY_PHYSICAL_PATH
The scope is a physical directory. Cannot be combined with QUERY_VIRTUAL_PATH.
QUERY_VIRTUAL_PATH
The scope is a virtual (Internet Information Server) directory. Cannot be combined with QUERY_PHYSICAL_PATH.
Property Set | DBPROPSET_FSCIFRMWRK_EXT
Property ID | DBPROP_CI_SCOPE_FLAGS
Value Type | DBTYPE_UI4
Default | QUERY_DEEP | QUERY_PHYSICAL_PATH


ICommand Property Example

This example demonstrates how to set an ICommand property. SetUseContentIndex sets Index Server's DBPROP_USECONTENTINDEX to VARIANT_TRUE.

HRESULT SetUseContentIndex( 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[1];
    aProp[0].dwPropertyID = DBPROP_USECONTENTINDEX;
    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;

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

    ICommandProperties * pICommandProperties;
    HRESULT hr = pICommand->QueryInterface( IID_ICommandProperties,
                                            (IUnknown **) &pICommandProperties );
    if ( FAILED( hr ) )
        return hr;

    hr = pICommandProperties->SetProperties( 1, aPropSet );
    pICommandProperties->Release();

    return hr;
}


Data Types and Constants

This section describes structures and enumeration values.


CIPROPERTYDEF

The CIPROPERTYDEF structure contains information about the friendly name, type, and DBID of a property.

typedef struct tagCIPROPERTYDEF
{
    LPWSTR wcsFriendlyName;
    DWORD  dbType;
    DBID   dbCol;
} CIPROPERTYDEF;

Members

wcsFriendlyName
Points to a null-terminated string that specifies the friendly name for a property. The friendly name can be used in an Index Server query, column list, or sort order parsed by CITextToSelectTree and CITextToFullTree. Friendly names must be defined in uppercase.
dbType
The data type for the property. This type is used when building a DBCOMMANDTREE restriction node. The same property with different friendly names can have different types. Its value must either be an OLE DB DBTYPEENUM or a PROPVARIANT variant type.
dbCol
The property identifier for the property. Refer to the OLE DB Programmer's Reference for information about DBID structures. Index Server properties must be either DBKIND_GUID_NAME or DBKIND_GUID_PROPID.


Storage Property Set

The storage property set defines properties found on files in a file system. The GUID for storage properties is PSGUID_STORAGE, which is defined in ntquery.h as:

#define PSGUID_STORAGE \
    { 0xb725f130, 0x47ef, 0x101a, \
        { 0xa5, 0xf1, 0x02, 0x60, 0x8c, 0x9e, 0xeb, 0xac } }
PID_STG_DIRECTORY
The directory in which a file is located. Default type is VT_LPWSTR. The Index Server friendly name is "directory".
PID_STG_CLASSID
Class ID of a file. Default type is VT_CLSID. The Index Server friendly name is "classid".
PID_STG_NAME
The name of the file. Default type is VT_LPWSTR. The Index Server friendly name is "filename".
PID_STG_PATH
The complete path for a file. Default type is VT_LPWSTR. The Index Server friendly name is "path".
PID_STG_SIZE
The size of a file. Default type is VT_I8. The Index Server friendly name is "size".
PID_STG_ATTRIBUTES
The attribute flags for a file. Default type is VT_UI4. The Index Server friendly name is "attrib".
PID_STG_WRITETIME
The date and time of the last write to the file. Default type is VT_FILETIME. The Index Server friendly name is "write".
PID_STG_CREATETIME
The date and time the file was created. Default type is VT_FILETIME. The Index Server friendly name is "create".
PID_STG_ACCESSTIME
The time of the last access to the file. Default type is VT_FILETIME. The Index Server friendly name is "access".
PID_STG_CONTENTS
The contents of the file. This property is for query restrictions only; it cannot be retrieved in a query result. Default type is VT_LPWSTR. The Index Server friendly name is "contents".
PID_STG_SHORTNAME
The short (8.3) filename for the file. Default type is VT_LPWSTR. The Index Server friendly name is "shortname".


Query Property Set

The query property set contains properties computed or made available by the query engine in Index Server. The GUID is PSGUID_QUERY, which is defined in oledb.h as:

extern const GUID PSGUID_QUERY = {0x49691c90,0x7e17,0x101a,{0xa9,0x1c,0x08,0x00,0x2b,0x2e,0xcd,0xa9}};

Some of these properties are defined in oledb.h, the rest in ntquery.h.

PROPID_QUERY_RANKVECTOR
The array of ranks for each term in a vector query. Default type is VT_VECTOR | VT_UI4. The Index Server friendly name is "rankvector".
PROPID_QUERY_RANK
The rank of a file, that indicates how well the query matched the file. This value is between 0 and 1000. Default type is VT_I4. The Index Server friendly name is "rank".
PROPID_QUERY_HITCOUNT
The count of query hits in a file. Default type is VT_I4. The Index Server friendly name is "hitcount".
PROPID_QUERY_ALL
Refers to all properties of the file. This property is for query restrictions only; it cannot be retrieved in a query result. Default type is VT_LPWSTR. The Index Server friendly name is "all".
PROPID_QUERY_UNFILTERED
Filtered state of the file. Default type is VT_BOOL. There is no Index Server name for this property; it must be defined manually.
PROPID_QUERY_VPATH
The Index Information Server virtual path of a file. The path can be in the WWW, NNTP, or IMAP name spaces. Default type is VT_LPWSTR. The Index Server friendly name is "vpath".


Data Source Object CLSID

The Index Server DSO CLSID is used to create an Index Server DSO using CoCreateInstance. It is defined in ntquery.h

CLSID_INDEX_SERVER_DSO

The CLSID constant for the Index Server DSO.

Example

This example creates an Index Server Data Source Object, from which a query can be executed.

const GUID CLSID_IndexServerDSO = CLSID_INDEX_SERVER_DSO;
IDBInitialize *pIDBInit;
HRESULT hr = CoCreateInstance( CLSID_IndexServerDSO,
                               0,
                               CLSCTX_INPROC_SERVER,
                               IID_IDBInitialize,
                               (void **) &pIDBInit );


OLE DB Interfaces Supported by Index Server

This section describes interfaces supported on command objects and rowset objects.


Interfaces Supported on Command Objects

These interfaces are supported on command objects:

ICommand

ICommandProperties

IAccessor

IConvertType

ISupportErrorInfo

IColumnsInfo

ICommandTree


Interfaces Supported on Rowset Objects

These interfaces are supported on rowset objects.

IRowset

IRowsetLocate

IRowsetScroll

IRowsetExactScroll

IAccessor

IRowsetInfo

IRowsetIdentity

IConvertType

IColumnsInfo

ISupportErrorInfo


Supported OLE DB Properties

The OLE DB properties in this table are supported by Index Server. Other properties are not supported. Refer to the OLE DB Programmer's Reference for a complete description of these properties.

Property | Settable | Default
DBPROP_BLOCKINGSTORAGEOBJECTS | FALSE | VARIANT_FALSE
DBPROP_BOOKMARKS | TRUE | VARIANT_TRUE if the rowset is locatable.
DBPROP_BOOKMARKSKIPPED | FALSE | VARIANT_FALSE
DBPROP_BOOKMARKTYPE | FALSE | Bookmarks are DBPROPVAL_BMK_NUMERIC
DBPROP_CANFETCHBACKWARDS | FALSE | VARIANT_FALSE
DBPROP_CANHOLDROWS | TRUE | VARIANT_TRUE if the rowset is locatable
DBPROP_CANSCROLLBACKWARDS | FALSE | VARIANT_TRUE if the rowset is locatable
DBPROP_COLUMNRESTRICT | FALSE | VARIANT_FALSE
DBPROP_COMMANDTIMEOUT | TRUE | The default is 0, which means no timeout.
DBPROP_LITERALBOOKMARKS | FALSE | VARIANT_FALSE
DBPROP_LITERALIDENTITY | FALSE | VARIANT_TRUE if the rowset is locatable
DBPROP_MAXOPENROWS | FALSE | The default is 0, which means no limit.
DBPROP_MAXROWS | TRUE | The default is 0, which means no limit.
DBPROP_MEMORYUSAGE | TRUE | The default is 0, which means no limit. Not implemented.
DBPROP_ORDEREDBOOKMARKS | FALSE | VARIANT_TRUE if the rowset is locatable
DBPROP_OTHERINSERT | FALSE | VARIANT_TRUE if the rowset is asynchronous
DBPROP_OTHERUPDATEDELETE | FALSE | VARIANT_TRUE if the rowset is asynchronous
DBPROP_QUICKRESTART | FALSE | VARIANT_TRUE if the rowset is locatable
DBPROP_REENTRANTEVENTS | FALSE | VARIANT_FALSE
DBPROP_REMOVEDELETED | FALSE | VARIANT_TRUE
DBPROP_ROWRESTRICT | FALSE | VARIANT_TRUE
DBPROP_ROWTHREADMODEL | FALSE | The default is DBPROPVAL_RT_FREETHREAD.
DBPROP_SERVERCURSOR | FALSE | VARIANT_TRUE
DBPROP_STRONGIDENTITY | FALSE | VARIANT_TRUE if the rowset is locatable
DBPROP_UPDATABILITY | FALSE | The default is 0, which means updates aren't supported.


Customizing Index Server

This section describes the use of the IFilter interface to customize Microsoft® Index Server. Included are sections on:

  • using the IFilter interface
  • IFilter reference information
  • testing custom implementations of IFilter


Using the IFilter Interface

The IFilter interface is used to extract text from objects for placement in the Microsoft® Index Server content index.

The primary purpose of the IFilter interface is to extract text, without formatting, from documents. IFilter is the foundation upon which higher level operations such as document indexing or application-independent viewers can be built.

Although clients of IFilter may use the interface in any way they see fit, it was designed to meet the specific needs of full text search engines, such as Index Server. An implementation of the IFilter interface scans objects for plain text and properties (attributes). The search engine must break the results of IFilter::GetText into words, normalize them and store the results in an index. It may use the locale identifier specified with a text chunk to perform proper language-specific word breaking and normalization.


IFilter Chunks

Each filter object can be asked to produce the "chunks" of Unicode text contained within itself. Text within one chunk is intended to be a linear, sequential flow of text with the same attribute and locale. Thus, two pieces of text that do not have such a relationship between each other would be in different chunks. Separate text boxes in a graphics file, labels and titles on charts, and possibly even text in separate cells of a spreadsheet are all examples of text in separate chunks.

Each chunk is given a chunk identifier that uniquely identifies the chunk. These identifiers are guaranteed to remain constant until the IFilter interface is released. Repeated instantiations of the IFilter interface with the same initial parameters produces the same set of chunks. Multiple instantiations with different initial parameters may produce a different set of chunks. Changing the set of attributes (see following section) may re-partition the chunks of an object. Chunk identifier 0 is invalid.

Chunks may overlap, but a specific attribute should be applied to a given character at most one time.


IFilter Properties and Pseudo-Properties

Text extracted using the IFilter interface may be tagged with many attributes, but only one attribute at a time. When these attributes refer to textual chunks they are treated as properties by the content index but are not treated as properties by the system. They are known as pseudo-properties.

Pseudo-properties are not accessible through the standard OLE IPropertyStorage interface. Pseudo-properties allow the user to search for documents based on the value of some internal field in the document that has not been exposed as a property to the system. For example, a spreadsheet describing monthly sales for an employee might export employee-id and total-sales pseudo-properties. This would enable a query for all spreadsheets (months) in which some employee sold more than x dollars.

Pseudo-property names must follow OLE property naming conventions. Each pseudo-property must be specified as property set\property. Failure to follow this naming convention results in unpredictable query behavior. Specifying a pseudo-property name which matches a true property name may also result in undefined query behavior.

The IFilter implemention may also publish OLE style properties through IFilter. These properties are retrieved using the IFilter::GetValue method call. Logically, they should be considered external annotations of a document. For example, this mechanism can be used to publish HTML anchors. If a class supports retrieval of OLE properties through IPropertyStorage, the IFilter implementation has the option of requesting the caller of IFilter to use IPropertyStorage to enumerate OLE properties, either in lieu of or to supplement properties emitted via IFilter::GetValue.


Embedded and Linked Objects

An object must enumerate the chunks of text in its embeddings. These nested chunks appear to the original caller as chunks of the outer object. There is no operating system support provided for this operation. IFilter implementations are responsible for binding to the IFilter interface of embedded objects. If the current chunk is within an embedding, all GetText and GetValue calls should be passed directly to the embedding's IFilter and the return values from the embedding should be returned to the client. Other calls require some additional work. GetChunk, for example, may require renumbering chunk identifiers to make them unique.

An object can optionally be asked to enumerate the chunks of text 'contained' in its linked objects. As with embedded objects, IFilter implementations are responsible for binding to the IFilter interface of linked objects, then renumbering the chunks of the linked objects so they appear to the original caller as chunks of the outer object. The same rules that apply to an embedding's chunks apply to a link's chunks.

The original source of a chunk (embedding, link, or top-level object) is not exposed by IFilter.


Proposed Uses of IFilter

Although clients of IFilter may use the interface in any way they see fit, it was designed to meet the specific needs for two tasks: filtering and viewing (browsing / hit-highlighting). They are described below.


Full Text Search

Full text search engines are the simpler of the two filter clients. They merely scan objects for plain text, pseudo properties and full properties. They break the result of IFilter::GetText into words, normalize them and then store the result in an inverted index. The locale identifier specified with a text chunk is used to perform proper language-specific word breaking and normalization. Full properties are retrieved via IFilter::GetValue and are (optionally) stored in a lookaside property cache.


Viewing

Document viewing is used to display the results of full text queries. A simplistic model of the viewing process is that documents matching a query are be 'indexed' on-the-fly and the resulting in-memory index is searched to locate query hits. A document viewer highlights and navigates between these hits.


Persistent Handlers

Microsoft® Index Server assumes implementations of the IFilter interface are registered as persistent handlers for OLE classes. The specifics of the persistent handler mechanism may change in future releases of Windows NT® or Index Server, but the API described below will continue to be supported.

A persistent handler provides the same service for the persistent image of an object that the OLE running object table provides for the loaded, in-memory image. The persistent handler is the broker between the persistent state of an object and clients wishing to access that state. It is responsible for coordinating multiple client access to the persistent state of an object.

Multiple handlers may be defined to provide behavior for a single persistent object. Each interface may specify a unique handler for only that interface. Potentially, multiple handlers which are ignorant of each other may all operate on the persistent image of the same object. It is the responsibility of the persistent handler to load the correct implementation for each interface.

The following registry entries are sufficient to load the implementation of IFilter for objects of type SampleObject.

\Registry\MACHINE\SOFTWARE\Classes

    .sam
        = REG_SZ SampleFile

    SampleFile
        = REG_SZ Class for Sample Files
        CLSID
           = REG_SZ {40A45370-0386-11D0-AE19-00AA004B9986}
    CLSID
        {40A45370-0386-11D0-AE19-00AA004B9986}
           = REG_SZ Sample Files
           InprocServer32
              = REG_SZ sample.dll
           PersistentHandler
               = REG_SZ {A6317C60-0386-11D0-AE19-00AA004B9986}

        {A6317C60-0386-11D0-AE19-00AA004B9986}
           = REG_SZ Sample file persistent handler
           PersistentAddinsRegistered
              {89BCB740-6119-101A-BCB7-00DD010655AF}
                 = REG_SZ {D7455400-0386-11D0-AE19-00AA004B9986}

        {D7455400-0386-11D0-AE19-00AA004B9986}
           = REG_SZ C and C++ filter
           InprocServer32
              = REG_SZ sampfilt.dll
              ThreadingModel = Apartment

Up through the entry for the class (CLSID\40A45370-0386-11D0-AE19-00AA004B9986), these entries are standard OLE registry entries. The DLL sample.dll implements running object behavior for the .sam class. But note the extra entry, PersistentHandler. This entry specifies the class responsible for brokering requests to the persistent objects of the sample class. The entry under PersistentAddinsRegistered identifies the implementation responsible for the interface named 89BCB740-6119-101A-BCB7-00DD010655AF (IID_IFilter). Finally, we come to the class implementing IID_IFilter and again, the entries are standard OLE registry entries. The InprocServer32 is loaded through the standard OLE mechanism. Microsoft Index Server observes the threading model specified for the filter. When the threading model is Both, the filter must be thread safe. If the filter is not thread safe, specify Apartment.


Pseudo-Persistent Handler API

Three functions are provided with Microsoft Index Server which are sufficient to instantiate the IFilter implementation for use by Microsoft Index Server. These functions are not a full implementation of OLE persistent handlers. The three functions are:

SCODE LoadIFilter( WCHAR const * pwcsPath, IUnknown * pUnkOuter, void ** ppIUnk );

SCODE BindIFilterFromStorage( IStorage * pStg, IUnknown * pUnkOuter, void ** ppIUnk );

SCODE BindIFilterFromStream( IStream * pStm, IUnknown * pUnkOuter, void ** ppIUnk );

Each takes an object or object name (as a path, IStorage, and IStream respectively) plus an optional controlling unknown and return an interface pointer bound to the IFilter interface. The latter two functions are especially useful when binding to the filter of an embedded object.

These functions are declared in the file ntquery.h, and exported from ntquery.lib.


Recommended Properties

The IFilter interface does not require any particular property to be exposed, but Index Server takes advantage of the existence of some properties. Filter writers are encouraged to expose the following properties. Exposing the same properties from multiple object formats allows the client of Index Server to make a common query across many (or all) types of object.

The properties below are named by the format identifier of the property set, followed by either a number corresponding to the PROPID of the PROPSPEC for properties of type PRSPEC_PROPID or a string corresponding to the string name for properties of type PRSPEC_LPWSTR.


Title

F29F85E0-4FF9-1068-AB91-08002B27B3D9\2

The title property is the title property from the Microsoft® Office summary information property set. By default, this property is cached by the index server for quick retrieval. The HTML filter also exposes the title property.


Description

D1B5D3F0-C0B3-11CF-9A92-00A0C908DBF1\"DESCRIPTION"

The description property is exposed by the HTML filter. It corresponds to the <META NAME="DESCRIPTION" ...> tag. If this property exists, it is used exclusively to generate the document abstract. Note that meta descriptions are not emitted by the sample HTML filter.

Note that other properties of type PRSPEC_LPWSTR in the same property set as the description property correspond to other HTML meta tags with the equivalent name.


Office Summary Information

F29F85E0-4FF9-1068-AB91-08002B27B3D9\2 (title)

F29F85E0-4FF9-1068-AB91-08002B27B3D9\3 (subject)

F29F85E0-4FF9-1068-AB91-08002B27B3D9\4 (author)

F29F85E0-4FF9-1068-AB91-08002B27B3D9\5 (keywords)

F29F85E0-4FF9-1068-AB91-08002B27B3D9\6 (comments)

In addition to title, several other properties from the Microsoft® Office summary information property set may be useful. Their use is listed above after the PROPID.


Microsoft Index Server Automatic Filter Registration

When Microsoft® Index Server 2.0 or later starts running, it reads a list of DLLs to register from a MultipleSZ value in HKLM\system\currentcontrolset\control\contentindex\DllsToRegister. For each DLL in this list, Microsoft Index Server calls DLLRegisterServer.

To make sure filters are always properly registered, add the complete path to the filter to this registry value when your filter is installed.


IFilter Reference

interface IFilter : public IUnknown
{
    SCODE Init( ULONG grfFlags,
                ULONG cAttributes,
                FULLPROPSPEC const * aAttributes,
                DWORD * pdwFlags );

    SCODE GetChunk( STAT_CHUNK * pStat );

    SCODE GetText( ULONG * pcwcBuffer,
                   WCHAR * awcBuffer );

    SCODE GetValue( PROPVARIANT ** ppPropValue );

    SCODE BindRegion( FILTERREGION origPos,
                      REFIID riid,
                      void * * ppunk );

}


IFilter::Init

SCODE IFilter::Init( ULONG grfFlags,
                     ULONG cAttributes,
                     FULLPROPSPEC const * aAttributes,
                     DWORD * pdwFlags );

This method is called to initialize the filtering session. If the interface is successfully initialized then calls to other IFilter functions may be made. Once a successful call has been made to Init the object must appear frozen from the point of view of the caller. The object is frozen until the filter object is released.

Multiple calls to Init reset the state of the filter object. The filter is re-positioned to the beginning of the object and the object state is frozen. Chunk identifiers must remain consistent across multiple calls to Init with the same parameters.

grfFlags is a group of flags taken from the enumeration IFILTER_INIT. They control text canonicalization, attribute output, embedding scope and IFilter access patterns.

typedef enum tagIFILTER_INIT
{
    IFILTER_INIT_CANON_PARAGRAPHS         = 1,
    IFILTER_INIT_HARD_LINE_BREAKS         = 2,
    IFILTER_INIT_CANON_HYPHENS            = 4,
    IFILTER_INIT_CANON_SPACES             = 8,

    IFILTER_INIT_APPLY_INDEX_ATTRIBUTES        = 16,
    IFILTER_INIT_APPLY_OTHER_ATTRIBUTES        = 32,
    IFILTER_INIT_INDEXING_ONLY                 = 64,

    IFILTER_INIT_SEARCH_LINKS            = 128
} IFILTER_INIT;

typedef enum tagIFILTER_FLAGS
{
    IFILTER_FLAGS_OLE_PROPERTIES                 = 1
} IFILTER_FLAGS;


Text canonicalization

Generally the text output from a call to GetText should exactly match the actual text of the document, but in order to achieve maximum interoperability some canonicalization of common features is desirable. These features include paragraph breaks, line breaks, hyphens and spaces.

The four flags controlling canonicalization of the output text are defined as follows:

IFILTER_INIT_CANON_PARAGRAPHS
Paragraph breaks should be marked with the Unicode PARAGRAPH SEPARATOR (0x2029).
IFILTER_INIT_HARD_LINE_BREAKS
Soft line breaks (such as end of line in Microsoft® Word) should be replaced by hard line breaks, LINE SEPARATOR (0x2028). Existing hard line breaks may be doubled. Any of carriage return (0x000D), line feed (0x000A), or the carriage return and line feed combination should be considered a hard line break. The intent is to enable pattern-expression matchers that match against the observed line breaks.
IFILTER_INIT_CANON_HYPHENS
Various word processors have forms of hyphens that are not represented in the host character set, such as optional hyphens (appearing only at end of line) and non-breaking hyphens. This flag indicates that optional hyphens are to be nulled out, and non-breaking hyphens are to be converted to normal, plain hyphens (0x2010), or HYPHEN-MINUSES (0x002D).
IFILTER_INIT_CANON_SPACES
As the previous flag canonicalizes hyphens, this one canonicalizes spaces. All special space characters, such as non-breaking spaces, etc., are to be converted to the standard SPACE character (0x0020).

IFilter servers are also allowed to embed null characters in the text, which are typically ignored by clients. Unicode character 0x0000 are completely ignored, and 0x0001 is treated as a word break.

The intent is to provide implementors of IFilter an efficient means to 'remove' embedded formatting from text without modifying positional information. A scrap of HTML such as:

<p>This is a paragraph with <em>emphasized</em> text.</p>

could be filtered as:

***This is a paragraph with ****emphasized**** text.****

where the '*' represents Unicode 0x0000.


Attributes

Different clients of the IFilter interface need different views of an object. Two flags in grfFlags control the set of attributes that should be applied to chunks. They are defined as follows:

IFILTER_INIT_APPLY_INDEX_ATTRIBUTES
The client wants text split into chunks representing pseudo-properties.
IFILTER_INIT_APPLY_OTHER_ATTRIBUTES
Any attributes not covered by the previous two flags should be emitted.

In addition to the attribute categories specified above, specific attributes may also be requested as an array of size cAttributes, stored in aAttributes. If cAttributes is non-zero this takes precedence over attribute specification in grfFlags and only the attributes in aAttributes are returned.

If no attribute flags are specified and cAttributes is zero then PSGUID_STORAGE\PID_STG_CONTENTS.


Chunk Information

Because the IFilter implementation needs to store some chunk information when operations other than content indexing occur, the following flag has been added to allow IFilter to be optimized for the indexing situation:

IFILTER_INIT_INDEXING_ONLY
The client only calls IFilter::Init once. The client does not use the function BindRegion eliminating the possibility of accessing a chunk both before and after accessing another chunk.


Links

For viewing purposes, it may be desirable to search across links as well as in the document and its embeddings. The following flag helps perform this function:

IFILTER_INIT_SEARCH_LINKS
This flag indicates that the text extraction process must recursively search all links within the document. If a link is unavailable, the GetChunk call where the first chunk of the link would have been returned should return FILTER_E_LINK_UNAVAILABLE.


Flags

The pdwFlags parameter allows the IFilter implementation to pass information back to the caller. Currently, the only valid flag is:

IFILTER_FLAGS_OLE_PROPERTIES
This flag indicates that the caller should use the IPropertySetStorage and IPropertyStorage interfaces to locate additional properties. When this flag is set, properties available via OLE property enumeration should not be returned from IFilter.

If OLE properties should not be enumerated, then pdwFlags should be set to zero.


Access Failure

For some implementations of IFilter, it may not be possible (or may be computationally expensive) to detect failure to access a document until Init is called, or possibly even later. If access is denied due to password protection or other similar security measures then FILTER_E_PASSWORD should be returned. Other access failures can be reported via FILTER_E_ACCESS.


IFilter::GetChunk

SCODE IFilter::GetChunk( STAT_CHUNK * pStat );

GetChunk positions the filter at the beginning of the next chunk and returns a description of the chunk in pStat. After this call, the chunk described in pStat is the current chunk. The chunk descriptor is owned by the caller, but the property name pointer which may be set in the property specification is owned by the callee and should not be freed. Several operations (see below) can only be applied to the current chunk. Before GetChunk. has been called for the first time, there is no current chunk. When the current chunk is the last chunk, additional call(s) to GetChunk return FILTER_E_END_OF_CHUNKS. If the next chunk is an embedding for which a filter is not available, this call returns FILTER_E_EMBEDDING_UNAVAILABLE. If the next chunk is in an unavailable link, this call returns FILTER_E_LINK_UNAVAILABLE. Access failure may also be reported with FILTER_E_PASSWORD and FILTER_E_ACCESS. After an error return code of anything other than FILTER_E_END_OF_CHUNKS the next call to IFilter still fetches the next chunk after the unavailable one.

A description of the active chunk is placed in *pStat. This structure is defined as follows:

typedef enum tagCHUNKSTATE
{
    CHUNK_TEXT       = 0x1,
    CHUNK_VALUE      = 0x2
} CHUNKSTATE;

typedef enum tagCHUNK_BREAKTYPE
{
    CHUNK_NO_BREAK = 0,
    CHUNK_EOW      = 1,
    CHUNK_EOS      = 2,
    CHUNK_EOP      = 3,
    CHUNK_EOC      = 4
} CHUNK_BREAKTYPE;

typedef tagSTAT_CHUNK
{
    ULONG              idChunk;
    CHUNK_BREAKTYPE    breakType;
    CHUNKSTATE         flags;
    LCID               locale;
    FULLPROPSPEC       attribute;
    ULONG              idChunkSource;
    ULONG              cwcStartSource;
    ULONG              cwcLenSource;
} STAT_CHUNK;

The chunk identifier for this chunk is returned in idChunk. It must be unique from every other chunk identifier returned by GetChunk during a single instantiation of IFilter. Chunk identifiers must be in increasing order. The order in which chunks are returned should correspond to the order of their text in the source document. Some search engines may take advantage of the inter-attribute proximity exposed between chunks of various attributes.

The breakType field contains the type of break that precedes this chunk. These are defined as follows:

CHUNK_NO_BREAK
This means that there is no break placed between this chunk and the previous chunk — the chunks are glued together. All of the information in pStat except for breakType and idChunk are taken from the most recent STAT_CHUNK that did not specify CHUNK_NO_BREAK. The other fields in pStat are not modified. On exit, they contain whatever value was in them on entry to GetChunk. Derived chunks cannot be glued using CHUNK_NO_BREAK. A single word cannot span more than two glued chunks.
CHUNK_EOW
This means that there is a word break placed between this chunk and the previous chunk that had the same attribute. Use of CHUNK_EOW should be minimized.

Clients of IFilter may choose a word breaking algorithm that is in conflict with CHUNK_EOW decisions made in an IFilter implementation. A content query returns optimal results when the word breaking algorithm used to split phrases in the user's query matches the algorithm used to split words in the documents. The former is always provided by the search engine. The search engine algorithm is also used to split words within a chunk, but many small chunks separated by CHUNK_EOW may affect its accuracy.

CHUNK_EOS
This means that there is a sentence break placed between this chunk and the previous chunk that had the same attribute.
CHUNK_EOP
This means that there is a paragraph break placed between this chunk and the previous chunk that had the same attribute.
CHUNK_EOC
This means that there is a chapter break placed between this chunk and the previous chunk that had the same attribute.

A change in attribute implies a word, sentence, paragraph or chapter break.

The flags field indicates whether this chunk should be treated as text (for example, a sequence of words) or value. If flags is CHUNK_TEXT then IFilter::GetText should be used to retrieve the contents of the chunk and parse it as a series of words. If flags is CHUNK_VALUE then IFilter::GetValue should be used to retrieve the value and treat it as a single property value. If the filter wishes the same text to be treated as both text and value it should be emitted twice in two different chunks.

The locale field specifies the language and sub-language of this text. Chunk locale is used by document indexers to perform proper normalization of text. If the chunk is not text or a value of type VT_LPWSTR, VT_LPSTR or VT_BSTR. then this field is ignored.

The attribute field specifies which attribute should be applied to this chunk. If the filter wishes the same text to have more than one attribute, the filter needs to emit the text once for each attribute in separate chunks.

Take the following example that might appear in a book:

The small detective exclaimed, "C'est finis!"

    Confessions

    The room was silent for several minutes. After thinking very hard
    about it, the young woman asked, "But how did you know?"

This section might be broken into chunks as follows:

id | Text | breakType | flags | locale | attribute
1 | The small dete | N/A | CHUNK_TEXT | ENGLISH_UK | CONTENT
2 | ctive exclaimed, | CHUNK_NO_BREAK | N/A | N/A | N/A
3 | "C'est finis!" | CHUNK_EOW | CHUNK_TEXT | FRENCH_BELGIAN | CONTENT
4 | Confessions | CHUNK_EOC | CHUNK_TEXT | ENGLISH_UK | CHAPTER_ NAMES
5 | Confessions | CHUNK_EOP | CHUNK_TEXT | ENGLISH_UK | CONTENT
6 | The room was silent for several minutes. | CHUNK_EOP | CHUNK_TEXT | ENGLISH_UK | CONTENT
7 | After thinking very hard about it, the young woman asked, "But how did you know?" | CHUNK_EOS | CHUNK_TEXT | ENGLISH_UK | CONTENT

If a GetChunk call to an IFilter implementation of an embedding or link returns FILTER_E_END_OF_CHUNKS, then it is the responsibility of the outer IFilter implementation to check to see if there are any more chunks outside of that embedding or link to be returned. For example, if a document has two embeddings and the first has returned FILTER_E_END_OF_CHUNKS, then the outer IFilter must call GetChunk on the IFilter for the next embedding.

In addition, before returning the results of a call to GetChunk of an embedded or linked object, the implementation must check to make sure that the chunk identifier is unique, and if it is not, renumber the chunk and keep a mapping of the new chunk identifier.

The fields

ULONG    idChunkSource;
    ULONG    cwcStartSource;
    ULONG    cwcLenSource;

are used. to describe the source of a derived chunk. If the text of the current non-contents chunk (psuedo-property or property) is derived from some contents chunk, the idChunkSource is set to the identifier of the source chunk, cwcStartSource is set to the offset at which the source text for the chunk starts in the source chunk, and finally cwcLenSource is either set to zero or to the length of. the source text from which the current chunk was derived. Zero signifies that there is character-by-character correspondence between the source text and the derived text. A non-zero value means that there is no such direct correspondence. This information is useful for the search engine when it wants to highlight the hits. If the query is done for a pseudo-property, the search engine highlights the original text from which the text of the property has been derived. For instance, for a C++ code filter, when searching for SampleFunction in a pseudo-property "function definitions," the browser highlights the function header inside the contents of a file. If the chunk is not derived, idChunkSource must be the same as idChunk. If the filter attributes specify a pseudo-property only, then there is no content chunk from which the current pseudo-property chunk is derived. In this case, idChunkSource must be set to 0, which is an invalid chunk identifier.


IFilter::GetText

SCODE IFilter::GetText( ULONG * pcwcBuffer,
                        WCHAR * awcBuffer );

GetText is used to retrieve the text from the current chunk. If the current chunk does not have a flags value of CHUNK_TEXT then the error FILTER_E_NO_TEXT is returned.

On entry, pcwcBuffer contains the size, in (Unicode) characters, of awcBuffer. On exit, pcwcBuffer contains the number of characters actually written to awcBuffer. More than one call to GetText may be required to retrieve all the text in the current chunk. Each call to GetText retrieves text immediately following the last call to GetText. Note that it is perfectly legitimate for the last character in one call to be in the middle of a word, and the first character in the next call to continue the word. Search engines must handle this situation.

When all text in a chunk has been returned the next call to GetText should return FILTER_E_NO_MORE_TEXT. All additional calls to GetText should return this error until GetChunk has been called successfully, advancing to the next chunk. As an optimization, the last call that returns text may return FILTER_S_LAST_TEXT, indicating the next call to GetText returns FILTER_E_NO_MORE_TEXT. This can save the client a call.


IFilter::GetValue

SCODE IFilter::GetValue( PROPVARIANT ** ppPropValue );

GetValue is the analog to GetText for non-textual attribute values. GetValue should only be called once per chunk. Any additional calls to GetValue should return FILTER_E_NO_MORE_VALUES until GetChunk has been called successfully, advancing to the next chunk.

Indirect values must be allocated with CoTaskMemAlloc. They are owned by the caller of GetValue, who must free them with CoTaskMemFree.

If the current chunk does not have a flags value of CHUNK_VALUE then the error FILTER_E_NO_VALUES is returned. The effect of publishing the same value from more than 1 chunk is undefined.


IFilter::BindRegion

SCODE IFilter::BindRegion ( FILTERREGION origPos,
                            REFIID iid
                            void ** ppunk );

BindRegion is used to retrieve an interface representing the specified portion of the object. This method is for future use, for example, to enable document viewing.

A FILTERREGION structure is filled in to describe the position of the text. The structure is defined as:

typedef struct tagFILTERREGION
{
    ULONG idChunk;
    ULONG cwcStart;
    ULONG cwcExtent;
} FILTERREGION;

If cwcExtent characters beginning at offset cwcStart in chunk idChunk extends beyond the end of the chunk then the continuation should be in the next chunk with an identical attribute. If this operation is impossible then FILTER_W_REGION_CLIPPED should be returned. This situation arises when the next identical chunk is in a link or embedding.

It is acceptable not to implement BindRegion and return E_NOTIMPL. Not all filters are capable of supporting this method in a rational way. Filters which are implemented by viewing applications benefit the most from IFilter::BindRegion.


Testing IFilter Implementations

The IFilter Test Suite helps validate IFilter implementations. This section describes some of the significant features of the tool, and how to use it.


IFilter Test Suite Overview

The IFilter Test Suite validates IFilter implementations by calling IFilter methods and checking the returned values for compliance with the IFilter specification. Some examples of things the test looks for are: that chunk identifiers are unique and increasing, that the IFilter behaves consistently after re-initialization, and that IFilter method calls with invalid parameters return expected error codes.

The test consists of a single executable, ifilttst.exe, and a initialization file, ifilttst.ini. ifilttst.exe is a command-line application.


Command Line Parameters

Parameter | Meaning
/i <filename> | The input file or directory to be filtered. The filename can contain the wildcard characters * and ?.
/l | Log messages are directed to a file, instead of the screen. Log messages describe the individual tests performed and the pass/fail result of the test. The log filename is the same as the input filename with a .log extension.
/d | Dump messages are directed to a file instead of the screen. Dump messages describe the contents of the chunks. The chunk structure is dumped when the verbosity level is 3. The dump filename is the same as the input filename with a .dmp extension.
/-l | Disable logging. This flag overrides /l
/-d | Disable dumping. This flag overrides /d
/v <integer> | The verbosity level. The test logs only messages concerning specific IFilter failures. The test dumps the chunk contents. The test logs warning messages. The test logs messages concerning tests which passed. The test logs informational messages. In addition, the test dumps the structure of the chunks.
/t <integer> | The number of threads to launch. One by default.
/r [<integer>] | Recursively filter subdirectories. The optional integer parameter specifies the depth to which to recurse. If no integer is specified, or if the integer is 0, full recursion is assumed. By default, the recursion depth is 1.
/c <integer> | The number of times to loop. If the integer is 0, the test loops infinitely. By default, the test loops only once.

Note You must include a space between the command line switch and the value.

You can invoke the IFilter Test Suite from the command line as follows:

ifilttst /i test.htm /l /d /v 1

The above command line tells the program to filter the file "test.htm", redirect the log messages to "test.htm.log", redirect the dump messages to "test.htm.dmp" and to set the verbosity to 1.

For the above command to work, three files must be located in the current working directory: ifilttst.exe, ifilttst.ini and test.htm. The significance of ifilttst.ini is described in the next section.


System Requirements

To run the filter test suite on your machine, you must have IIS installed on Windows NT® Server or IIS Personal Web Server installed on Windows NT Workstation, and Microsoft® Index Server, version 1.0 or later.


The ifilttst.ini File

An IFilter object is initialized by calling IFilter::Init(). Init() takes four parameters, grfFlags, cAttributes, aAttributes and pdwFlags. The user of the IFilter Test Suite can specify the values for these parameters in a file called ifilttst.ini. Page 6 shows a sample ifilttst.ini file.

The file is broken up into sections, with the section name enclosed in square brackets. In the example, the sections are named Test1, Test2, etc.. All section names must be unique. The test reads the values from the first section and initialize the filter with them. Then it runs all of its tests using this filter configuration. It then releases the filter and reinitializes it, using parameters from the next section. It repeats the process until there are no more configurations left to test.

Each section contains a complete description of the parameters for IFilter::Init(). The parameters are described in separate fields, called entries. The entries are described on the next page.

Entry | Value of entry
Flags | the names of the IFILTER_INIT flags that are to be OR'ed together to form the grfFlags parameter. They must be in all caps, and they must all be on the same line.
Cattributes | A decimal integer representing the value of the cAttributes parameter.
Aattributes | The name of this entry must start with "aAttributes" and must be different from the other aAttributes entries within the section. Legal names for the aAttributes entry are: aAttributes, aAttributes1, aAttributes2, etc. The first token must be a GUID. The GUID must be formatted exactly as shown in section [Test3]. The second token may be either a propid (consisting of a number in hexadecimal notation) or a lpwstr (a pointer to a wide character string). A lpwstr can be specified by enclosing the string in double quotes (see section [Test6]).

If the Flags and cAttributes entries are not specified, they default to zero. If you set cAttributes equal to 2, you usually want to specify 2 aAttributes; however, you do not have to. In section [Test5], cAttributes is 1, but no aAttributes have been specified. The test then calls IFilter::Init() with cAttributes equal to 1, and aAttributes equal to NULL. This is a useful test case, since it is likely to cause an access violation in IFilter::Init().

There is no entry for the pdwFlags parameter since this parameter is used solely to return a value to the caller; it does not need to have any special value prior to the call to Init().

If ifilttst.exe cannot find a file named ifilttst.ini in the present working directory, it uses a default configuration to initialize the filter object. Below is the default configuration:

[default]
grfFlags = IFILTER_INIT_APPLY_INDEX_ATTRIBUTES
cAttributes = 0


Sample Ifilttst.ini file

; Only extract text from the object
[Test1]
Flags =
cAttributes = 0

; Get all attributes (text and pseudo-properties)
[Test2]
Flags = IFILTER_INIT_APPLY_INDEX_ATTRIBUTES
cAttributes = 0

; This also extracts just text from the object (the GUID is PSGUID_STORAGE, and the propid is
; PID_STG_CONTENTS)
[Test3]
Flags = IFILTER_INIT_CANON_PARAGRAPHS IFILTER_INIT_HARD_LINE_BREAKS
cAttributes = 1
aAttributes1 = b725f130-47ef-101a-a5f1-02608c9eebac 13

; Only extract requested attribute from the html object (the GUID corresponds to the html filter)
[Test4]
Flags = IFILTER_INIT_CANON_HYPHENS IFILTER_INIT_CANON_SPACES
cAttributes = 1
aAttributes1 = 70eb7a10-55d9-11cf-b75b-00aa0051fe20 2

; What happens if cAttributes is non-zero, but aAttributes is empty?
[Test5]
Flags = IFILTER_INIT_CANON_SPACES IFILTER_INIT_APPLY_INDEX_ATTRIBUTES IFILTER_INIT_APPLY_OTHER_ATTRIBUTES
cAttributes = 1

; Here is an attribute with a lpwstr instead of a propid (the lpwstr is enclosed in quotes)
; The GUID corresponds to the meta tag clsid for the HTML filter.
[Test6]
Flags =
cAttributes = 1
aAttributes1 = D1B5D3F0-C0B3-11CF-9A92-00A0C908DBF1 "GENERATOR"


IFilter Test Procedure

Once the filter has been initialized, the program conducts a series of tests on the filter. These tests are described below.


Validation Test

The validation test steps through the object one chunk at a time, verifying each individual chunk and all return codes. This function saves all returned STAT_CHUNK objects in a list. The validation test verifies the following:

  • Chunk Identifiers are unique and increasing
  • STAT_CHUNK.flags is a recognized chunk state (CHUNK_TEXT or CHUNK_VALUE)
  • STAT_CHUNK.breakType is a recognized break type (0,1,2,3,4)
  • If the filter initialization attributes specify that the filter should only return chunks containing pseudo-properties, idChunkSource must equal 0.
  • If chunk is not derived, i.e., it is not a pseudo-property, idChunkSource must equal idChunk.
  • GetChunk() returns S_OK or an accepted value (FILTER_E_END_OF_CHUNKS, FILTER_E_LINK_UNAVAILABLE, etc.)
  • If the chunk contains text, GetText() returns S_OK, FILTER_S_LAST_TEXT, or FILTER_E_NO_MORE_TEXT.
  • If GetText() returns FILTER_S_LAST_TEXT, the next call to GetText() should return FILTER_E_NO_MORE_TEXT.
  • If the chunk contains a value, GetValue() returns S_OK or FILTER_E_NO_MORE_VALUES.


Consistency Test

The test suite reinitializes the IFilter with the same parameters as before and performs a consistency test. If the IFilter implementation has been initialized with the IFILTER_INIT_INDEXING_ONLY flag, the test releases the IFilter object and re-binds it before making another call to IFilter::Init(). This test checks to make sure that the STAT_CHUNK structures returned are identical to those returned earlier in the validation test. The consistency test verifies the following:

  • Each STAT_CHUNK returned by GetChunk() is exactly identical to the corresponding STAT_CHUNK returned in the validation test.
  • GetChunk() returns S_OK or an accepted value (FILTER_E_END_OF_CHUNKS, FILTER_E_LINK_UNAVAILABLE, etc.)


Invalid Input Test

The test suite reinitializes the IFilter with the same parameters and performs an invalid input test, which steps through the document one chunk at a time making incorrect function calls, such as GetValue() when the current chuck contains text. The test checks all return codes for compliance with the IFilter specification. The invalid input test verifies the following:

  • If the current chunk contains text, GetValue() returns FILTER_E_NO_VALUES. Then, a call to GetText() should succeed.
  • If the current chunk contains a value, GetText() should return FILTER_E_NO_TEXT. Then a call to GetValue() should succeed.
  • If the previous call to GetText() returned FILTER_E_NO_MORE_TEXT, successive calls to GetText() should return FILTER_E_NO_MORE_TEXT.
  • If the previous call to GetValue() returned FILTER_E_NO_MORE_VALUES, successive calls to GetValue() should return FILTER_E_NO_MORE_VALUES.
  • If the previous call to GetChunk() returned FILTER_E_END_OF_CHUNKS, successive calls to GetChunk() should return FILTER_E_END_OF_CHUNKS.
  • In addition, the test compares the current chunk structures to those returned in the validation test to make sure they are identical.


Testing Different IFilter Configurations

The test suite releases the IFilter object and rebinds, this time initializing it with the next set of parameters. The test repeats the cycle: validation test, consistency test, and invalid input test, until all the desired IFilter configurations, as specified in ifilttst.ini, have been tested.


Sample Log File

Here is some excerpts from a log file, with the verbosity set to 3 (highest verbosity):

1. INFO----**** New configuration ****
2.
3. Section name : Test2
4. grfFlags     : 63
5. cAttributes  : 0
6. aAttributes  : NONE
7. pdwFlags     : 0
8.
9. INFO----Successfully bound filter.
10.
11. PASS----Init() returned a valid value for pdwFlags.
12.
13. INFO----Successfully initialized filter.
14.
15. INFO----Performing validation test. In this part of the test, the chunks structures
16.         returned by the filter are checked for correctness, and the return values
17.         of the filter calls are checked.
18.
19. PASS----GetChunk() succeeded.
20.
21. PASS----The current chunk has a legal value for the flags field.

The first line is an informational message, indicating that a new configuration has been loaded from the ifilttst.ini file. Line (3) indicates the section name in the ifilttst.ini file from which the current configuration has been read. Lines (4) through (7) list the parameters to IFilter::Init(). The lines starting with "INFO" are informational messages about the binding of the filter and start of the validation test. Lines starting with "PASS" are messages regarding specific tests which have passed.

WARNING-First call to GetText() returned FILTER_E_NO_MORE_TEXT.

The line above is a warning. Warnings call attention to filter behavior that is suspicious, although legal. The above warning indicates that GetChunk() has returned a text chunk with no text in it.

ERROR---The filter has emitted a chunk which it was not requested to emit. Check
    the initialization parameters in section Test1 of the initialization file.

INFO----Current chunk propid : 0x5

Here is an example of an error message. In this case, the filter has emitted a chunk with a propid of 0x5. Inspection of section [Test1] in ifilttst.ini would show that the filter was configured to not emit chunks with this propid. For instance, if neither IFILTER_INIT_ APPLY_INDEX_ATTRIBUTES nor IFILTER_INIT_APPLY_OTHER_ATTRIBUTES were specified in the Flags entry and cAttributes is 0, the filter should only emit chunks with a propid of 0x13, corresponding to PID_STG_CONTENTS.


Sample Dump File

Here is an excerpt from a dump file:

1. Chunk ID: ........... 2
2. Chunk Break Type: ... END OF SENTENCE
3. Chunk State: ........ TEXT
4. Chunk Locale: ....... 0x411
5. Chunk Source ID: .... 2
6. Chunk Start Source .. 0x0
7. Chunk Length Source . 0x0
8. GUID ................ b725f130-47ef-101a-a5f1-02608c9eebac
9. Property ID ......... 0x13

10. This is a HTML filter test page

11. Chunk ID: ........... 3
12. Chunk Break Type: ... END OF SENTENCE
13. Chunk State: ........ TEXT
14. Chunk Locale: ....... 0x411
15. Chunk Source ID: .... 2
16. Chunk Start Source .. 0x0
17. Chunk Length Source . 0x0
18. GUID ................ f29f85e0-4ff9-1068-ab91-08002b27b3d9
19. Property ID ......... 0x2

20. This is a HTML filter test page

21. Chunk ID: ........... 4
22. Chunk Break Type: ... END OF SENTENCE
23. Chunk State: ........ VALUE
24. Chunk Locale: ....... 0x411
25. Chunk Source ID: .... 2
26. Chunk Start Source .. 0x0
27. Chunk Length Source . 0x0
28. GUID ................ f29f85e0-4ff9-1068-ab91-08002b27b3d9
29. Property ID ......... 0x2

30. This is a HTML filter test page

The first 9 lines describe the current chunk structure. Note that the GUID and the Property ID correspond to PSGUID_STORAGE / PID_STG_CONTENTS. This is a chunk containing plain text. The text follows the chunk structure: "This is a HTML filter test page".

The next chunk, starting at line (11), has a different GUID, corresponding to the HTML filter, and a different propid, corresponding to a HTML Href. This is a pseudo-property, exported by the HTML filter. The next chunk, starting at line (21), has the same GUID and propid, but its chunk state is VALUE instead of TEXT. Note that the text in these last two chunks are the same as for the first chunk. The filter wanted three attributes (plain text, HTML Href as text, and HTML Href as a value) to be applied to this particular phrase, so it was emitted in three separate chunks.


Command Trees

Command trees are used to represent data manipulation statements in DML and data definition statements in DDL. This section describes the operators represented by command nodes used to build command trees.


Data Manipulation Operators

This section concentrates on the description of the semantics of the operators including their inputs and outputs. Section 3 provides a table describing how to connect the command nodes.


Constants, Identifiers, Names, and Parameters

Note that for the various name operators, unlike names in SQL statements, names in operators that contain special characters do not need to be quoted. If a name in an operator is quoted, these quotes will be considered literals and part of the name.

DBOP_scalar_constant
Represents a scalar constant whose value can use any element of the value unionof the DBCOMMANDTREE structure. A scalar value can be of any OLE DB type. This node has no inputs. The result type is the type of the scalar value represented by the node.
DBOP_DEFAULT, DBOP_NULL
SQL's "DEFAULT" and "NULL" value indicators. There are no inputs. The result type can be any type of scalar.
DBOP_bookmark_name
Represents the bookmark column of a table. For example, SELECT T.*, T.bookmark FROM T. It takes one optional input, a DBOP_table_name, to which the bookmark belongs. A string representing the name of the column can optionally be assigned in pwszValue.
DBOP_catalog_name, DBOP_column_name
Represents the name of a catalog and column, respectively. No inputs. A name may be specified by a pwszValue that is not a null pointer, DBID, or moniker in the node's value field. For column names, the column may be denoted as an unsigned integer value (e.g., SELECT A, B FROM T ORDER BY 2).
DBOP_schema_name
Represents the name of a schema. A name may be specified by a pwszValue that is not a null pointer, a DBID, or moniker in the node's value field. A schema name node has an optional input DBOP_catalog_name node.
DBOP_outall_name
Specifies that all input columns (e.g., SQL's "SELECT *") must be output. No inputs. It contains no other DBCOMMANDTREE value. DBOP_outall_name is used as an input to the DBOP_project_list_element node, but not to the set_list_element, sort_list_element, column_list_element or from_list_element nodes.
DBOP_qualifier_name
Specifies all columns in a table (e.g., SQL's "table.*"). No inputs. The table name is represented by a pwszValue that is not a null pointer. DBOP_qualifier_name is used as an input to the DBOP_project_list_element node, but not to the set_list_element, sort_list_element, column_list_element or from_list_element nodes.
DBOP_qualified_column_name
Represents a column name. It takes two inputs: a DBOP_table_name, and a DBOP_column_name, in that order. It contains no other DBCOMMANDTREE value.
DBOP_table_name
Represents a table name, possibly in another catalog or schema. A table name may be specified by a pwszValue that is not a null pointer, DBID, or moniker in the node's value field. This node takes an optional input: DBOP_schema_name.
DBOP_nested_table name
Represents a nested table name. A nested table is a table containing at least one chaptered column. The component of the nested name is stored as a pwszValue that is not a null pointer, DBID, or moniker in the node's value field. This node takes a DBOP_nested_table_name or a DBOP_table_name input. For example, the nested table Customers.Orders with chapter Detail is represented as follows:
DBOP_nested_table_name (pwsz = "Detail")
            |
    DBOP_nested_table_name (pwsz = "Orders" )
        |
DBOP_table_name (pwsz = "Customers")
DBOP_nested_column_name
Specifies a column in a nested table. This node takes one DBOP_nested_table_name and a DBOP_column_name as inputs, in that order.


Constructors and Coercion Operators

DBOP_row
Constructs a row from a list of scalar values. Similar to SQL-2's row value constructor from column values or literals, as in "(t.a, t.b, t.c) < VALUES (2, 3, 5)." It takes a scalar_list_anchor as input and produces a single row.
DBOP_table
Constructs a table from a list of union-compatible row constructors. It takes a row_list_anchor as input and produces a table.
DBOP_sort
Sorts a table in increasing or decreasing order according to a list of columns or expressions over its columns. A DBOP_sort has two required child nodes. The first child represents the input table to be sorted. The second child is a DBOP_sort_list_anchor node representing the list of sort keys. Each element of the sort_list, specified by a DBOP_sort_list_element, contains the order of sort in the DBSORTINFO field, and points to the column name or expression on which to sort. This operator produces an ordered table. If the input to sort is a unique table, it produces an ordered-unique table. If the input to sort is a hierarchical table, then the sort operator applies only to the root rowset in that table; that is, the list of columns on which to sort should reference only non-chaptered columns of the root rowset; otherwise, the sort specification is invalid. The output of sort in this case is a hierarchical table with the rows in the root rowset odered. For example: Let T1->T2->T3 be a hierarchical rowset, where the columns of each rowset are T1(A,B,C,X), T2(D,E,F,Y), and T3(G,H), assume X, and Y represent the chaptered columns of T1 and T2, respectively. Assume DBOP_sort specifies a list W of columns on which to sort. W must reference only subsets of {A,B,C}.
DBOP_distinct, DBOP_distinct_order_preserving
These operators remove duplicate rows from a table. The DBOP_distinct operator takes a table as input and produces a unique table. The DBOP_distinct_order_preserving operator takes an ordered or unordered table as input and produces a unique table maintaining the row order of the input table. If the input table is ordered, then the output is an ordered-unique table.


Operators on Tables

DBOP_alias
The alias facilities for a table. DBOP_alias takes one mandatory and one optional inputs. The first is a table-valued subtree, often a table_name, representing the table being aliased. The alias name for the table is provided in the pwszValue of the DBOP_alias command. A second optional input is a column_list_anchor containing column elements representing aliases to be assigned to the columns of the input table. SQL examples: "SELECT a.* FROM table AS a" and "SELECT x, z FROM table AS a (x, y, z)." The output table is of the same table type as the input table. The new name(s) replaces the old name(s) for the output of this operator. If the second input is provided, then the size of the column_list must be the same as the number of columns in the first input.
DBOP_cross_join
Represents the Cartesian product of two tables, i.e., SQL's cross join. This operator takes two tables T1 and T2 as input and produces a table R. The columns of R are all the columns of T1 and T2. Let t1, t2, and r denote a row in T1, T2, and R, respectively. Let r[Ti] denote the projection of row r on the columns of Ti, i=1,2. Each row r in R is formed by taking each row t1 in T1 and concatenating it with each row t2 in T2, so that r[T1]=t1 and r[T2]=t2.The cardinality of R is the product of the cardinalities of T1 and T2. R is a unique table if both T1 and T2 are unique tables; otherwise, it is a table.
DBOP_union_join
Represents SQL's union join. This operator takes two tables T1 and T2 as input and produces a table R. The columns of R are all the columns of T1 and T2. Let ti_null denote a row of Ti, i=1,2, containing NULL values in every column. Each row r in R is formed by taking every row from T1 and T2 as follows: (a) r[T1]= t in T1 and r[T2] = t2_null, or (b) r[T1] = t1_null, and r[T2] = t in T2. The cardinality of R is the sum of the cardinalities of T1 and T2.

The output is a unique table if both inputs are unique tables; otherwise, it is a table.

DBOP_inner_join, DBOP_left_semi_join, DBOP_right_semi_join, DBOP_left_anti_semi_join, DBOP_right_anti_semi_join, DBOP_left_outer_join, DBOP_right_outer_join, DBOP_full_outer_join
All join operators take two required table inputs, T1 and T2, and produce a table R as output. A third required input is a Boolean-valued expression specifying the join condition (i.e, SQL's ON or WHERE clause). The columns of R are specified as follows:
  • For inner_join and all outer_joins, the columns of R are all the columns of T1 and T2, in that order;
  • For left_semi_join and left_anti_semi_join, the columns of R are the columns of T1; and
  • For right_semi_join and right_anti_semi_join, the columns of R are the columns of T2.

The rows in R are specified as follows:

  • For inner_join, R contains the subset of rows from the Cartesian product of T1 and T2 satisfying the join condition. If the join condition is the predicate TRUE or a tautology (e.g., 5=5), then R is exactly the Cartesian product of T1 and T2;
  • For left_semi_join, R contains all rows of T1 that have a matching row in T2 satisfying the join condition. If the join condition is always TRUE, then R contains the same rows as T1. If the join condition is always FALSE, then R is empty;
  • For left_anti_semi_join, R contains all rows of T1 that do not have a matching row in T2 satisfying the join condition. If the join condition is always TRUE, then R is empty. If the join condition is always FALSE, then R contains the same rows as T1;
  • For right_semi_join, R contains all rows in T2 that have a matching row in T1 satisfying the join condition. If the join condition is always TRUE, then R contains the same rows as T2. If the join condition is always FALSE, then R is empty;
  • For right_anti_semi_join, R contains all rows in T2 that do not have a matching row in T1 satisfying the join condition. If the join condition is always TRUE, then R is empty. If the join condition is always FALSE, then R contains the same rows as T2;
  • For left_outer_join, R contains all tuples in the inner_join of T1 and T2, and all tuples in the left_anti_semi_join of T1 and T2 concatenated with t2_null;
  • For right_outer_join, R contains all tuples in the inner_join of T1 and T2, and all tuples in the right_anti_semi_join of T1 and T2 concatenated with t1_null; and
  • For full_outer_join, R contains all tuples in the inner_join of T1 and T2, all tuples in the left_anti_semi_join of T1 and T2 concatenated with t2_null, and all tuples in the right_anti_semi_join of T1 and T2 concatenated with t1_null.

The output R is a table in general; it is a unique table (1) for inner join and all outer joins, if both inputs are unique tables, (2) for left semi-join and left anti-semi-join, if the left input is a unique table, and (3) for right semi-join and right anti-semi-join, if the right input is a unique table.

DBOP_natural_join, DBOP_natural_left_outer_join, DBOP_natural_right_outer_join, DBOP_natural_full_outer_join
All natural join operators take two required table inputs, T1 and T2, and produce a table R as output. A third optional input is a column_list specifying the columns on which to test the join predicate (i.e, SQL's USING clause). Natural joins are different to the join operations described above in that they have an implicit projection and an implicit join condition. Let CC represent the columns that are common to both T1 and T2, T1C represent the columns of T1 not in CC, and T2C represent the columns of T2 not in CC. The columns of R are T1C, CC, and T2C. Producing the set of columns CC only once in R represents the implied projection. The implied join condition is the conjunction of terms T1.Ci = T2.Ci, for every Ci in CC. If the column_list input of these operators is included, then the join condition is restricted to the terms involving only the columns in the column list, which can only reference columns in CC. Other than the implied projection and join condition, the semantics of the natural join operators is exactly as their non-natural counterparts. That is, the cardinality of R is exactly the cardinality of the non-natural join operators. If CC is the empty set, then the implied join predicate is TRUE and R contains the cross join of T1 and T2.
DBOP_set_intersection, DBOP_set_union, DBOP_set_left_difference, DBOP_set_right_difference, DBOP_set_anti_difference
Set operations, requiring "union compatible" input tables. These operators take two required inputs T1 and T2 and produce a result table R. Let A1, A2,..., An denote the columns of T1, and B1, B2,..., Bm denote the columns of T2. T1 and T2 are union compatible if: (a) n=m, and (b) the types of Ai and Bi, i=1,..,n, are compatible. The rules determining when the types of two columns are compatible and what the type of the resulting columns will be are provider-specific. If Ai,Bi, have the same name, that name is propagated to the output; otherwise, the resulting name is provider-specific. Left-difference is the usual difference operator; right-difference is the opposite (T1 right-difference T2 == T2 left-difference T1). The anti-difference is the union of the left- and right-differences. For SQL's "union corresponding," the tree node requires a third optional input, which must be a non-empty column_list_anchor. If the third input is present, the input tables are projected on the columns named in that list before the set operation proceeds. If the third input is not included and the Boolean-valued field fValue of the DBCOMMANDTREE node is set to TRUE, then the command processor decides the maximal set of columns from the two inputs that are union-compatible and should therefore be preserved. The output is a unique table.
DBOP_bag_intersection, DBOP_bag_union, DBOP_bag_left_difference, DBOP_bag_right_difference, DBOP_bag_anti_difference
Multi-set (bag) operations, requiring "union compatible" input tables. The number of occurrences of a row in the output table is the minimum (intersection), sum (union), or difference respectively of that row's number of occurrences in the two input tables. Variants are as for set operations. DBOP_bag_union represents SQL's UNION ALL operator. Two or more table inputs, the output is a table.

Given table A with 2 occurrences of row X and table B with 1 occurrence of row X, then:

  • (A bag_intersection B) will contain 1 (min(2,1)) occurrence of row X;
  • (A bag_union B) will contain 3 (2+1) occurrences of row X;
  • (A bag_left_difference B) will contain 1 (2-1) occurrences of row X;
  • (A bag_right_difference B) will contain 0 ocurrences of row X, since 1-2 is a negative number.
  • (A bag_anti_difference B) will contain 1 (1+0) occurrences of row X corresponding to the bag_union of the bag_left_difference and bag_right_difference.
DBOP_division
Represents the relational division operator. This operator takes two required table inputs T1 and T2, and produces a unique table R as result. The first input, T1, is the dividend table, the second input, T2, is the divisor table. The set of columns of T2 must be a subset of the columns of T1. Let CC be the columns that are common to T1 and T2, and T1C be the columns in T1 not in T2. Table R has all the columns in T1C. Let t1 denote a row in T1 and t2 a row in T2. The result R contains the set of rows r such that for every row t1 in T1, there is a row t2 in T2 with r=t1[T1C] and t1[T2]= t2.
DBOP_relative_sampling
A random sampling of rows from a table based on a reduction factor (given in percent, must be an integer between 0 and 100). Randomness is guaranteed to the provider's ability. The only required input is a table. The integer between 0 and 100 is represented as an integer in the ulValue field of the DBCOMMANDTREE node. If the input table is unique, so is the output table; otherwise, the output is a table.
DBOP_absolute_sampling
A random sampling of rows from a table with a known, fixed output cardinality. The output cardinality is given as a positive integer stored in the ulValue field of the DBCOMMANDTREE node (values larger than the input cardinality raise an error). Randomness is guaranteed to the provider's ability. The only required input is a table. If the input table is unique, so is the output table; otherwise, the output is a table.
DBOP_transitive_closure
The transitive closure operator performs recursive computations that involve traversal of all paths in a directed graph starting from a set of initial nodes as defined by Agrawal [IEEE TSE, 14:7, 1988]. Suppose the input table T is of the form T(K1, K2, P1, P2, ..., Pm), where K1 and K2 are columns belonging to the same domain. K1 and K2 can consist of more than one, but an equal number of columns. T can be represented as a directed graph in which each row t in T is represented as an link (arc) from node t[K1] to node t[K2]. The values t[Pi], i=1,...,m, represent link properties. Semantically, the transitive_closure operator computes a table representing all paths in that graph as the following iterative program:
let R = T;
do
  let R = select * from T
          union
          select R.K1, T.K2 from R inner join T on R.K2 = T.K1;
while "R changes";

The operator takes 4 mandatory inputs. First, a table T representing directed links in a graph. Second, a join predicate to determine when a link and a path can be concatenated (e.g., R.K2=T.K1). Third, a project_list representing the columns to be preserved after such a concatenation, each column may be the result of a scalar computation (including aggregate functions). In the above program, it is represented by R.K1, T.K2, but it may also include Pi columns from R and T. Fourth, a predicate to select result tuples based on their derivation history (see below). A fifth optional input represents a selection condition on the initial table. The derivation path that led to the existence of a row in the transitive_closure of T is captured in an additional chaptered column, called Delta, that gets implicitly attached to each result row. Thus, the operator's output is a hierarchical table R, having the same columns as T plus the chaptered column. For example, if a relation T is defined as:
T | K1 | K2
a | b
b | c
c | d

The result R of the transitive closure of T would be:
R | K1 | K2 | Delta
A | b | {<a,b>}
B | c | {<b,c>}
C | d | {<c,d>}
A | c | {<a,b>, <b,c>}
B | d | {<b,c>, <c,d>}
A | d | {<a,b>, <b,c>, <c,d>}

Thus, the fourth input to this operator is a predicate involving Delta.

DBOP_recursive_union
As required by SQL-3 (but not SQL-2), the recursive union starts with an input table and adds to that table (in the result, not in the stored database) rows that can be computed by a predicate from rows already in the table. When viewed as a computation on graphs, this operation starts with a set of nodes and adds nodes reachable by one or more steps in the graph. The starting points are specified in the input table, and the steps are specified by the predicate. Alternatively, the starting table's rows can represent a set of arcs in a graph, and the predicate can express how links connect and can be combined into new links (paths).

DBOP_aggregate
This operator combines grouping of rows from an input table T into equivalence classes, and aggregate function computation within each group to produce a table R. The operator first builds equivalence classes based on equal values in a set of (grouping) expressions (handling NULLs as required in SQL), then collapses each equivalence class into a single row by computing an aggregate function on the rows in the class. The operator takes 3 required inputs. The first input is a table. The second is a list of grouping expressions represented by a project_list_anchor (as for the "project" operator). The grouping expressions form the unique key for the output table. If the list of grouping expressions is empty, the entire input is presumed to be a single equivalence class and the output is a table with a single row. The list of grouping expressions may include all columns of the input table. The aggregation expressions, third input, are passed as a second project_list_anchor (similar to "project" operator), with the requirement that references to input columns other than those in the grouping list must be nested inside of aggregate functions (e.g., "sum"). Note that both lists are "project" lists, thus permitting arithmetic and assignment of new column names (expressions in the grouping list require a name!). The new name(s) replaces the old name(s) for the output of this operator. The table R contains all the columns mentioned in the two project lists. The output is a unique table.


Operators with Two Variants for Ordered and Non-Ordered Tables

DBOP_select, DBOP_order_preserving_select
Subset of rows based on a predicate. The required inputs are one table and one Boolean expression. If the input table is unique, so is the output table. For the "order-preserving-select" operator, if the input is an ordered table, so is the output.
DBOP_project, DBOP_project_order_preserving
Preserve a subset of columns, possibly with some arithmetic computation within each single row. No duplicate elimination of rows implied. The two required inputs are a table and a project_list_anchor, and the output is a table. For the "order-preserving-project" operator, if the input is an ordered table, so is the output. If the provider permits, some of the ordering columns may be removed by the projection operator, such that the ordering could not be restored if it were to be lost in a subsequent operation. Only unqualified names are available above a projection level; use an intervening alias node to avoid ambiguous references.


Operators for Ordered Tables

DBOP_top, DBOP_top_percent, DBOP_top_plus_ties, DBOP_top_percent_plus_ties
Take the first rows from an ordered table. The operators are different from the sampling operators because they prohibit randomness. The "top" operations require a fixed count of rows. The "top_percent" operations require a fraction of the input rows (not an approximation). The "plus ties" operators include all rows that are equal (with respect to the input table's sort order) to the last one explicitly requested. The first required input to all operators is an ordered table. The top, and top_plus_ties operators stored the count of rows as a positive integer in the ulValue field of the command node. The top_percent* operators take a second required scalar_list_anchor input. The elements of that list contain two scalar_constant members representing a pair of positive integers expressing a fraction between 0 and 1. The output is an ordered table.
DBOP_rank, DBOP_rank_ties_equally, DBOP_rank_ties_equally_and_skip
Add a new column "rank" to the table, counting from 1 to the table's cardinality. If the column name "rank" already exists, "rank2" ("rank3" etc.) is added instead. The "ties" operations assign the same rank to items that are equal (with respect to the input table's sort order). The "and skip" variant skips an appropriate number of values in the counting sequence. Examples: without skipping — 1, 2, 2, 2, 3, 4, 4, 5; with skipping — 1, 2, 2, 2, 5, 6, 6, 8. The input is an ordered table. If the operator is "rank," the output is an ordered unique table; for the "ties" operators, it is an ordered table.


Operators for Hierarchical Tables

DBOP_navigate
given two tables, one or both of which may be hierarchical tables, compute their join. however, instead of creating large rows with columns from both join inputs, attach to each row of the first table all matching rows from the second table, thus creating a hierarchy of rowsets. Also adds expression-valued columns to both the new parent and the new child rowset.

There are six inputs:

  • Two table-valued subtrees representing the input to the operator
  • A join predicate to make the connection
  • A list element with a name for the new column representing the nested column. The alias name for child levels is mandatory and must be unique across the hierarchy.
  • A list specifying expression-valued columns to be added to the parent level (e.g. sub-totals).
  • A list adding expression-valued columns to the child level (e.g. running sums)"
DBOP_nesting
Given a flat table, create a hierarchical table. First, classify the table's rows as in a grouping operation. For each resulting group, produce a single output row to become part of the parent table. This output row contains columns defined in the parent list plus one nested column. The nested column represents a sub-table, which contains columns defined in the child list. The grouping list is a project list, i.e., includes an column name for each element. The parent list is also a project list; its values are computed from the grouping list and aggregates over the input (same grouping). The child list is also a project list, whose values are computed from the input columns. In addition to these three lists, the inputs include an input table and a name for the column representing the sub-table.

given a hierarchical table, create an additional hierarchical column some of the columns. note that a sub-table already existing in the input may become part of the new top level or the new lower level. in the former case, set equality is required for two input rows to become members of the same group. in the latter case, multi-level nesting is created.

If the input to nesting is an ordered table, it does not affect the behavior of nesting. DBOP_nesting will produce what is specified by the semantics of nesting regardless of the order of the input. An optimizer might take the ordering into consideration when defining the execution strategy for DBOP_nesting to reduce the cost of the grouping, but to the consumer it does not make any difference.

the inputs to this operator are a flat or hierarchical input table, a column name for the nested column (sub-rowset) to be created (the alias name for child levels is mandatory and must be unique across the hierarchy), and the three lists of columns described above.

The order of the parent and child tables produced as output of DBOP_nesting is undefined. This allows for more flexibility of implementation by providers (e.g., the grouping can be done by hashing, and the computation of the chapters can be based on traversing some index or computing some parameterized query.

NULLs are handled during the grouping as in SQL "GROUP BY" clauses.

DBOP_unnesting
given a hierarchical table, flatten one nested column by repeating the top-level row as many times as there are values in the nested column (rows in the sub-rowset)

the inputs are a hierarchical table and the column to be flattened.

DBOP_nested_apply
given a hierarchical table, apply any table operation to a nested column, and replace the nested column with the resulting value (in the query result, not in the underlying database), or add the query result as a new column. if multiple levels of nesting exist, the operation to be applied may be "nested apply" itself. the operations "nesting" and "unnesting" are also permitted, as well as operations that result in a scalar, e.g., "aggregate" and "exists."

the inputs are a hierarchical table and a table operation, a column to which the table operation is to be applied, and (optionally) a column name for a new column. the output is a table, which may be nested or not, depending on the input table and the result type of the table operation applied.

DBOP_cross_tab
This node takes as an input a command tree that specifies a hierarchy of rowset chapters. It outputs an augmented result that contains additional rowsets that generate 'column membership' and 'column aggregates' that are useful for cross-tabulation reports.

To specify the crosstab, an alias is given to name the top level of the input hierarchy. Also, two lists of aliases are given: RowHierarchy and ColumnHierarchy. The RowHierarchy followed by the ColumnHierarchy must indicate a continuous descending path. The first element of the RowHierarchy is required to be the top level of the input hierarchy.

The crosstab node augments the input hierarchy such that each level of the RowHierarchy contains a nested rowset that is an instance of the ColumnHierarchy. The augmented hierarchy is then nested inside a 'top' rowset. This 'top' rowset also contains a nested rowset corresponding to the ColumnHierarchy and a list of aggregate values that are computed over the entire input tree. Aggregates are specified via a 'project_list' structure and follow the same binding rules as specified by the nesting node.

Fields of a given instance of a level of the ColumnHierarchy are defined as follows:

  • aggregates (such as Sum, Avg, Min, Max) are computed over the set of records matching the criteria imposed by the exact drilldown path. For example, drilling down from the 'top' rowset will show aggregates that are computed over all the levels of the RowHierarchy.
  • upward references resolve correctly when possible. If the reference doesn't exist on the path that was taken from the 'top' rowset to this instance of a level of the RowHierarchy, then E_OUTOFSCOPE is returned for the evaluation of that field.
  • nested rowsets other than those specified in the ColumnHierarchy return E_UNAVAILABLE unless the path that was taken from the 'top' rowset includes the last level of the RowHierarchy.

The exact list of parameters for this node are: a command tree, an alias for the input, a list of aliases for the RowHierarchy, a list of aliases for the ColumnHierarchy, and an optional project_list of aggregates.


Comparison Operators

DBOP_is_NULL, DBOP_is_NOT_NULL
Comparison to NULL value. Both operators are needed, since "NOT is_NULL" and "is_NOT_NULL" differ in SQL if the operand is a row with some (but not all) NULL values. One scalar input, Boolean result.
DBOP_equal, DBOP_not_equal, DBOP_less, DBOP_less_equal, DBOP_greater, DBOP_greater_equal, DBOP_equal_all, DBOP_not_equal_all, DBOP_less_all, DBOP_less_equal_all, DBOP_greater_all, DBOP_greater_equal_all, DBOP_equal_any, DBOP_not_equal_any, DBOP_less_any, DBOP_less_equal_any, DBOP_greater_any, DBOP_greater_equal_any
Comparison operators with semantics as in SQL. For the normal comparison operators, the two inputs rows must both be scalars, or both be rows. Otherwise, the scalar is converted into a one-column row as necessary. The "_all" and "_any" operators require a scalar left input and a table-valued right input, where a column value in a chaptered column is a special case of a table. Boolean output.

Row-valued comparison refers to the comparison between two row instances. Multi-valued comparison refers to the comparison between a row instance and a table (i.e., _all, any operators). The rules for row-valued comparisons are based on ANSI SQL semantics. The rules for multi-valued comparisons are an extension of the rule-valued ones.

The weight of this node is stored as a DBVALUEKIND_I4 in the value.lValue field.

DBOP_anybits, DBOP_allbits, DBOP_anybits_any, DBOP_allbits_any, DBOP_anybits_all, DBOP_allbits_all
These operators are exactly like other comparison operators in terms of syntax: they take two inputs, the first being a column, the second being a scalar value to compare with the column. The specific semantics of these nodes are as follows. DBOP_any_bits is true if the column value bitwise-ANDed with the constant is non-zero. DBOP_all_bits is true if the column value bitwise-ANDed with the constant equals the constant. There are also "_any" and "_all" versions of these nodes, as with other comparison operators.

The weight of this node is stored as a DBVALUEKIND_I4 in the value.lValue field.

DBOP_between, DBOP_between_unordered
SQL comparison operations. "Between_unordered" first sorts the bounding values; "a between_unordered b and c" is equivalent to "a >= min (b, c) and a <= max (b, c)", whereas "a between b and c" is equivalent to "a >= b and a <= c." All variants of "between" are inclusive, meaning that a value equal to one of the end-points is acceptable. There is no exclusive "between" variant. Three scalar inputs (which may be rows), Boolean result.
DBOP_match, DBOP_match_unique, DBOP_match_partial, DBOP_match_partial_unique, DBOP_match_full, DBOP_match_full_unique
SQL's match predicate. Two scalar inputs (which may be rows), Boolean output.
DBOP_and, DBOP_or, DBOP_xor, DBOP_equivalent
These Boolean operators representing the logical AND, OR, exclusive OR, and equivalence, take two or more Boolean inputs (except DBOP_equivalent which takes exactly two), and produce a Boolean result. The weight of this node is stored as a DBVALUEKIND_I4 in the value.lValue field.
DBOP_not
One Boolean input, Boolean result. The weight of this node is stored as a DBVALUEKIND_I4 in the value.lValue field.
DBOP_implies

This operator represents Boolean implication. It takes two mandatory inputs of type Boolean and produces a Boolean output according to the following table:
Input1 | Input2 | Result
False | False | True
False | True | True
True | False | False
True | True | True


Operators for Scalars

DBOP_scalar_parameter
Scalar parameter, named by a string. The name must be unique within all parameters for the command, including the parameters of SQL commands in textual nodes in the same command tree. No inputs, scalar output.
DBOP_scalar_function
A scalar function identified by name, GUID, etc. similar to a column-name. The number of arguments (inputs) depends on the function. Scalar output.
DBOP_plus, DBOP_minus, DBOP_times, DBOP_over, DBOP_div, DBOP_modulo, DBOP_power
Standard arithmetic. "div" always returns an integer result, such that "(a div b) times b plus a modulo b==a" for all non-zero values for b. Two scalar inputs, scalar result.
DBOP_like, DBOP_sounds_like
Regular expression matching. Two string inputs. For DBOP_like, the operator node includes a GUID to indicate the language or system whose regular expression syntax is meant. Fixed GUIDs are assigned for SQL, DOS, OFS, and MAPI. Boolean result. The DBOP_like node uses the DBLIKE structure to hold the GUID and a weight.
typedef struct  tagDBLIKE{
    LONG lWeight;        // weight of the node
    GUID guidDialect;    // dialect's guid
    }    DBLIKE;
DBOP_like_any, DBOP_like_all
Regular expression matching. Take a scalar first input and a table-valued second input and produce a Boolean valued output.
DBOP_is_INVALID, DBOP_is_TRUE, DBOP_is_FALSE
SQL's tests on predicates. One scalar input, Boolean result.
DBOP_overlaps, DBOP_case_condition, DBOP_case_value, DBOP_nullif, DBOP_cast, DBOP_coalesce, DBOP_position, DBOP_extract, DBOP_char_length, DBOP_octet_length, DBOP_bit_length, DBOP_substring, DBOP_upper, DBOP_lower, DBOP_trim, DBOP_translate, DBOP_convert, DBOP_string_concat, DBOP_current_date, DBOP_current_time, DBOP_current_timestamp


Content Search Operators

The following operators describe search operations on columns containing text. These operators behave like any other boolean operators, however, when used in conjunction with the DBOP_content_select operator, the resulting tables include two special columns describing the hit count and rank.

DBOP_content_select
This operator is similar to the DBOP_select operator. It takes a table T and boolean expression (selection condition) as inputs, and produces a table R as output. The rows in R are those rows that satisfy the selection condition. However, the columns of R include all the columns of T plus three additional columns representing the hit count, a rank, and a rank vector which together represent the quality of the match.
DBOP_content, DBOP_content_freetext
These operators are used to express content searches for words or phrases found in a column. The operators take one required input, a column name, specifying the column in which to search. There are two special column identifiers. The first, PROPID_STG_CONTENTS, means "look in the object contents" for data providers whose rows are actually objects. The second, PROPID_ QUERY_ALL, means "look in all columns." The text search pattern is stored in the DBCONTENT structure within the node (see below). DBOP_content_freetext differs from DBOP_content in that it instructs the query engine to use all of its text-processing tricks in evaluating the restriction. The DBOP_content node may specify a method to generate words matching a pattern (this field is ignored for DBOP_content_ freetext), which determines the manner in which the initial query term is expanded before searching text. GENERATE_METHOD_EXACT matches only the word or phrase specified in the content node. GENERATE_METHOD_PREFIX matches all words that begin with the text specified in the node. GENERATE_METHOD_INFLECT causes linguistic inflection of the specified word. The English word "run", for example, may expand to "run", "running", "ran", "runs" etc. Providers may support other generation methods (e.g., GENERATE_METHOD_MISSPELLING, GENERATE_METHOD_REGEX). The generation methods specified in this node are mutually exclusive. Not all of the levels specified above are available for every language. If GENERATE_METHOD_PREFIX or GENERATE_METHOD_INFLECT are used with a phrase (multiple words), the expansion applies to each word separately. The exact method of splitting a phrase into words is implementation-specific. These nodes also have a weight field, which may be used by a sophisticated consumer to give more or less weight in hit ranking to matches on this particular content restriction. The output of these operators is Boolean. The arguments internal to this node are specified using the following structure:
typedef struct tagDBCONTENT {
    DWORD      dwGenerateMethod;    // exact, prefix, inflect
    LONG       lWeight;             // weight of node
    LCID       lcid;                // locale
    LPWSTR     pwszPhrase;          // text
} DBCONTENT;

#define GENERATE_METHOD_EXACT     ( 0 )
#define GENERATE_METHOD_PREFIX    ( 1 )
#define GENERATE_METHOD_INFLECT   ( 2 )

PSGUID_QUERY is the guid for the property set for special content columns. The following are some of the interesting columns in this set.

#define PROPID_QUERY_RANKVECTOR (0x2) // column used to return rank
                                      // values of the
                                      // content_vector_or operator

#define PROPID_QUERY_RANK (0x3) // column used to return the final
                                // rank of each row

#define PROPID_QUERY_HITCOUNT (0x4) // column used to return the
                                    // number of content hits found
                                    // in a row

#define PROPID_QUERY_ALL    (0x6) // search in all text associated
                                  // with a row

#define PROPID_STG_CONTENTS (0x13) // search inside the contents on
                                   // an object
DBOP_content_proximity
Proximity nodes are used to rank hits within queries that search for several different pieces of text. The rank of an object matched by a proximity node increases as the words and phrases move closer together. The formula used to compute rank is specific to a given implementation. Generally a proximity node should be used as an n-ary AND node among multiple content restrictions. The internal arguments of this node are a proximity unit (DWORD), a proximity distance (ULONG), and a weight on the node (LONG), all specified with the DBCONTENTPROXIMITY structure.
typedef struct  tagDBCONTENTPROXIMITY    {
    DWORD dwProximityUnit;     // units
    ULONG ulProximityDistance; // how near is near?
    LONG lWeight;              // node weight
}    DBCONTENTPROXIMITY;

The following proximity units may be supported:

#define PROXIMITY_UNIT_WORD          ( 0 )
#define PROXIMITY_UNIT_SENTENCE      ( 1 )
#define PROXIMITY_UNIT_PARAGRAPH     ( 2 )
#define PROXIMITY_UNIT_CHAPTER       ( 3 )

The node takes two or more input subtrees, each of which should contain DBOP_content, DBOP_content_freetext, DBOP_content_proximity, DBOP_and, DBOP_or, or DBOP_not nodes. The output of the node is Boolean. It is logically similar to an AND, in the sense that, to produce a true value, all input subtrees must evaluate to true.

DBOP_content_vector_or
Used to support advanced content retrieval models. This node acts as an n-ary OR node, with two main differences. First, when a vector node is used the programmer has the option of retrieving a rank vector for each object, as a special, well-known column. This vector contains the rank of each child node in the vector. Second, the formula used to compute the overall rank of the vector may be specified. The following ranking formulas may be supported:

Minimum/AND (VECTOR_RANK_MIN)

Maximum/OR (VECTOR_RANK_MAX)

Inner product (VECTOR_RANK_INNER)

Dice coefficient (VECTOR_RANK_DICE)

Jaccard coefficient (VECTOR_RANK_JACCARD)

Cosine (VECTOR_RANK_COSINE)

There may be at most one vector node in the tree. The internal arguments of this node are a ranking method (DWORD) and a weight on the node (LONG), specified within the DBCONTENTVECTOR structure.

typedef struct tagDBCONTENTVECTOR {
    DWORD dwRankingMethod;    // jaccard, cosine, etc.
    LONG lWeight;             // weight of the vector node.
}DBCONTENTVECTOR;

There are two or more children, of exactly the same types as with DBOP_content_proximity above. The output is Boolean. It acts as an n-ary OR of the input subtrees.


Update Operators

DBOP_delete
Remove rows from a table. Delete takes two inputs. The first required input is a DBOP_table_name node describing the table being updated. The second optional input is a table-valued expression representing the rows to be deleted. If the second input is not provided, then all rows of the table specified in the first input are deleted. The requirement as to whether the input table being updated must be a base table is provider-specific.
DBOP_update
Modify the values of one or more columns in rows from a table which satisfy a condition. Update takes two required inputs. The first required input is a table-valued expression describing the set of rows being updated. The second required input is a DBOP_set_list_anchor node containing the update expressions (e.g., SET A=A+1, B=B-5). The column names represented in the DBOP_set_list_elements must be unambiguous. That is, in cases where the tuples to be updated are defined by a join expression over multiple tables, and a column being updated appears in more than one table, the reference to the updated column must be fully qualified (DBOP_qualified_column_name). For example, the update statement
UPDATE A INNER JOIN B ON A.F1=B.F1 SET A.X = 1, B.Y = 2 WHERE A.Z =10

is represented by an update node whose first input is a subtree representing the expression

A INNER JOIN B ON A.F1=B.F1 AND A.Z = 10

and the second input is a DBOP_set_list_anchor containing two list elements for A.X = 1, and B.Y = 2. The requirement as to whether the input table being updated must be a base table is provider specific.

DBOP_insert
Add rows to a table, specifying three arguments. One required row_constructor or table_constructor. One required table, one required column_list. Number of elements of column_list must match number of values in the constructed row(s). Columns not listed in the column_list must have default values or be nullable. Requirements as to whether table input must be a base table are provider specific.


Aggregate Functions

DBOP_min, DBOP_max, DBOP_count, DBOP_sum, DBOP_avg, DBOP_any_sample, DBOP_stddev, DBOP_stddev_pop, DBOP_var, DBOP_var_pop, DBOP_first, DBOP_last
Aggregate functions, to be used in the aggregation operation, including standard deviation and variance for samples (using division by "N-1") and complete populations (division by "N"). All operators (except count) take one mandatory input representing a column_name on which the function will be computed. A count node with no inputs indicates SQL's COUNT(*). The DISTINCT and ALL SQL set quantifiers, e.g., SELECT SUM (DISTINCT X) FROM T, SELECT SUM (ALL X) FROM T), are indicated by setting the appropriate bits of the dwSetQuantifier field of DBSETFUNC struct as follows:
#define DBSETFUNC_NONE        = 0x00,
#define DBSETFUNC_ALL         = 0x01,
#define DBSETFUNC_DISTINCT    = 0x02

The node produces a scalar output.


Mixed Operators

DBOP_in
Membership test of a scalar or row in a table. Inputs are a row and a table, Boolean result.
DBOP_exists, DBOP_unique
These operators take a table T as input and produce a Boolean result as output. DBOP_exists evaluates to TRUE if the cardinality of T is greater than zero; otherwise it evaluates to FALSE. DBOP_unique detects duplicate rows in T. If any two rows in T are equal to one another, DBOP_unique evaluates to FALSE; otherwise it evaluates to TRUE.
DBOP_subset, DBOP_proper_subset, DBOP_superset, DBOP_proper_superset, DBOP_disjoint,
These operators take two tables T1 and T2 as input and determine whether T1 is a subset, proper_subset, superset, proper_superset, or disjoint set of T2. The output is Boolean.


Special Operators

DBOP_pass_through
Pass the entire subtree to a provider. All operations on the subtree are to be delegated to the provider, including parts of global tree operations such as validation, and can therefore be unexpectedly expensive. The command tree must be self-contained. The provider is identified with a string or a GUID. One input tree with any result type, output type is the same as input type.
DBOP_defined_by_GUID
This is a major means of extensibility: An operator not included explicitly in this enumerated type, known to both consumer and provider by its GUID. Consumer and provider must also agree on count, type, etc. for the operator. The cost of leaving the set of possible operators mostly undefined here is that type checking is entirely left to the provider implementation. Additional node information may be provided in the DBBYGUID struct inside the node. The information stored in pbInfo is provider-specific.
typedef struct tagDBBYGUID {
    BYTE    *    pbInfo;    // extra node information
    ULONG        cbInfo;    // size of the data in pbInfo
    GUID         guid;      // this node's GUID
} DBBYGUID;
DBOP_text_command
A command in a textual command language, typically an SQL expression. The language and its dialect are indicated in the guidDialect of the DBTEXT struct stored in the value field in the node structure. Fixed values are defined for SQL-92, SQL-92-intermediate, SQL-92-entry, T-SQL, and Access SQL. In SQL, possible subtrees in the OLE DB command tree are references with strings "::1", "::2", etc. The textual command is stored in the pwszText field of the DBTEXT structure. Scalar- or table-valued output. This operator is used by OLE DB consumers to communicate complete or partial SQL-statements. If the operator has no inputs, then the text stored in pwszText should contain a complete SQL statement. If the operator has an input, then the DBOP_text_command contains a partial SQL statement and its input is the operator to which the partial statement corresponds. For example, if an OLE DB consumer wants to communicate just the FROM clause of a SQL statement to some other consumer, then the partial text will contain a string like "FROM T1, T2" and the input to the operator will be a DBOP_from_list_anchor.
DBOP_SQL_select
The equivalent of a SQL SELECT statement, with its component clauses SELECT, FROM, WHERE, GROUP BY, HAVING, and ORDER BY, in this sequence. The SELECT and GROUP-BY clauses are represented by project lists. The FROM clause input, represented by a from_list_anchor, contains from_list_elements that take a table-valued expression as input. The table-valued expression may refer to a base table, a subquery, or a table-valued result generated by an OLE DB query processor. The WHERE and HAVING clauses are represented by a Boolean expression each. The ORDER BY clause is represented by a sort list. All clauses must be present in the node, but may be empty. An empty list is denoted by a *_anchor node with no *_element nodes attached to it. SQL scoping (visibility) rules apply. The SQL DISTINCT option is specified by setting the fValue field of the SQL_select node as TRUE. The output is a table. If an ORDER BY clause is given and DISTINCT is not specified, then the output is an ordered table. If DISTINCT is specified and no ORDER BY clause is specified, then the output is a unique table. If both DISTINCT and an ORDER BY clause are specified, then the output is an ordered-unique table.
DBOP_remote_table
Refer directly to a table in an external data source without creating a synonym for it beforehand. Its children are identical to that of the DBOP_create_synonym node and the external data source is identified in the same way.


Operators for "AddPostProcessing"

DBOP_prior_command_tree
Used when adding post-processing operations. This operator occurs in the post-processing tree but never in a complete command tree. No inputs, table- or scalar-valued, depending on the previous command tree.
DBOP_add_columns
Used when adding post-processing operations, this operation is the only operation that can add columns "projected away" by earlier operations, i.e. columns from tables that already participate in the query. The inputs are a table and a projection list. The output is a table.


Lists of Arguments

An empty list is denoted by the corresponding anchor node with no element nodes.

DBOP_column_list_anchor, DBOP_column_list_element
These operators are used to define a list of column aliases (see DBOP_alias). Each element represents the name alias as a string value in the pwszValue field of the command node. Elements take no inputs.
DBOP_command_list_anchor, DBOP_command_list_element
These operators are used to define a sequence of OLE DB commands. Each element takes one command tree input representing either a DML or DDL command.
DBOP_from_list_anchor, DBOP_from_list_element
The list of tables in a SQL "FROM" clause. Each element requires a table-valued expression as input, which can be represented by a table_name or alias to serve as SQL "correlation name."
DBOP_project_list_anchor, DBOP_project_list_element
The anchor and an element in a list of columns for a projection. The anchor has an element as its first child, but no other information. Each element assigns a column name, which is stored in the string portion of the tree node, to a scalar expression, which is given as the element node's input tree. In addition, outall_name and qualifier_name must be in the project_list of a project node (as opposed to a project_list of an aggregate node). If a project list includes the member outall_name and a column_name that is a member of the columns implied by outall_name, then that column will appear twice in the result.
DBOP_row_list_anchor, DBOP_row_list_element
The anchor and element in a list of rows for constructing a table. Each element has one DBOP_row input. At least one row_list_element must be in each list.
DBOP_scalar_list_anchor, DBOP_scalar_list_element
The anchor and element in a list of scalar constants for constructing a row. Each element takes one DBOP_scalar_constant as input. At least one scalar list element must be in each list.
DBOP_set_list_anchor, DBOP_set_list_element
The anchor and element in a list of columns for updating. Each element has two inputs, one a column name and one an expression of the same type as the column. At least one set_list_element must be in each list.
DBOP_sort_list_anchor, DBOP_sort_list_element
The anchor and an element in a list of columns for sorting. This list is different from a projection list in that no new column name is assigned but a ascending/descending flag and an optional locale id are indicated. Each element includes a scalar input expression.


Data Definition Operators

All scalar-, row-, table-, and Boolean-valued expressions required by DDL operators are represented in the same way as in DML expressions.

DBOP_alter_character_set
Node used to alter the definition of an existing character set. The name of the character set to be altered is specified directly by this node.
DBOP_alter_collation
Node used to alter the definition of an existing collation. The name of the collation to be altered is specified directly by this node.
DBOP_alter_domain
Node used to alter the definition of an existing domain. The name of the domain to be altered is specified directly by this node.
DBOP_alter_index
Node used to alter the definition of an existing index. The name of the index to be altered is specified directly by this node.
DBOP_alter_procedure
Node used to alter the definition of an existing procedure. The name of the procedure to be altered is specified directly by this node.
DBOP_alter_schema
Node used to alter the definition of an existing schema. The name of the schema to be altered is specified directly by this node.
DBOP_alter_table
Node used to alter the definition of an existing table. The name of the table to be altered is specified directly by this node.
DBOP_alter_trigger
Node used to alter the definition of an existing trigger. The name of the trigger to be altered is specified directly by this node.
DBOP_alter_view
Node used to alter the definition of an existing view. The name of the view to be altered is specified directly by this node.
DBOP_coldef_list_anchor
Node identifying the start of a list of coldef_list_element nodes.
DBOP_coldef_list_element
Node used to specify the definition of a column (typically in conjunction with a create_table node). Note that this node does not attempt to describe all of the properties that may be associated with a column, but just the basic, immutable properties. More elaborate properties can be added to the column definition by adding a property_list_anchor and subsequent property_list_element nodes.
DBOP_create_assertion
Node used to specify creation of an assertion. An assertion is a standalone constraint in a schema and is typically used to specify a constraint that affects more than one table. The name of the assertion to be created is specified directly by this node.
DBOP_create_character_set
Node used to specify creation of a character set. A character set is a collection of characters used to create character strings. is The name of the character set to be created is specified directly by this node.
DBOP_create_collation
Node used to specify creation of a collation. A collation is a set of rules that control how strings in a character set are compared. The name of the collation to be created is specified directly by this node.
DBOP_create_domain
Node used to specify creation of a domain. A domain is a named meta data type which can be used to specify a data type length as well as a default value, constraints, and collations. The name of the domain to be created is specified directly by this node.
DBOP_create_index
Node used to specify creation of an index. The name of the index to be created is specified directly by this node.
DBOP_create_procedure
Node used to specify creation of a procedure. The name of the procedure to be created is specified directly by this node.
DBOP_create_schema
Node used to specify creation of a schema.
DBOP_create_synonym
Defines a synonym (attached table) in the local schema for a table in an outside data source. The node takes one required and one optional input. The required input is a DBOP_table_name operator denoting the synonym name. The local synonym name is represented in the pwszValue field of the DBOP_create_synonym node. The first required input is a DBOP_table_name defining the name of the table in the external data source. The naming follows the scheme used by table names. The second optional input is a DBOP_property_list_anchor which contains a list of DBOP_property_list_elements defining the external data source. If there is no second child, the synonym is assumed to be local.
DBOP_create_table
Node used to specify creation of a table. The name of the table to be created is specified directly by this node.
DBOP_create_temporary_table
Node used to specify creation of a temporary table. The name of the table to be created is specified directly by this node.
DBOP_create_translation
Node used to specify creation of a translation. A translation specifies how to translate from one character set to another. The name of the translation to be created is specified directly by this node.
DBOP_create_trigger
Node used to specify creation of a trigger. A trigger specifies what actions to perform after certain actions have occurred in the DBMS. The name of the trigger to be created is specified directly by this node.
DBOP_create_view
Node used to specify creation of a view. The name of the view to be created is specified directly by this node. The node takes two inputs. The first required input is a table-valued expression defining the contents of the view. The columns resulting from this expression are exactly the columns of the view. The second optional input is a DBOP_property_list_anchor node with one DBOP_property_list_element node to specify the DB_PROPERTY_CHECK_OPTION property. If the value of this property is TRUE, this is equivalent to the WITH LOCAL CHECK OPTION clause; if the value of this property is FALSE, this is equivalent to the WITH CASCADED CHECK OPTION clause (which is the default).
DBOP_drop_assertion
Node used to specify deletion of a assertion. The name of the assertion to be deleted is specified directly by this node.
DBOP_drop_character_set
Node used to specify deletion of a character set. The name of the character set to be deleted is specified directly by this node.
DBOP_drop_collation
Node used to specify deletion of a collation. The name of the collation to be deleted is specified directly by this node.
DBOP_drop_domain
Node used to specify deletion of a domain. The name of the domain to be deleted is specified directly by this node.
DBOP_drop_index
Node used to specify deletion of a index. The name of the index to be deleted is specified directly by this node.
DBOP_drop_procedure
Node used to specify deletion of a procedure. The name of the procedure to be deleted is specified directly by this node.
DBOP_drop_schema
Node used to specify deletion of a schema. The name of the schema to be deleted is specified directly by this node.
DBOP_drop_synonym
Removes a synonym from the local schema; the name of the synonym is included in the node in the pwszValue field.
DBOP_drop_table
Node used to specify deletion of a table. The name of the table to be deleted is specified directly by this node.
DBOP_drop_translation
Node used to specify deletion of a translation. The name of the translation to be deleted is specified directly by this node.
DBOP_drop_trigger
Node used to specify deletion of a trigger. The name of the trigger to be deleted is specified directly by this node.
DBOP_drop_view
Node used to specify deletion of a view. The name of the view to be deleted is specified directly by this node.
DBOP_foreign_key
Node used to define a Foreign Key.
DBOP_grant_privileges
Node used to grant privileges on an object.
DBOP_index_list_anchor
Node identifying the start of a list of DBOP_index_list_element nodes.
DBOP_index_list_element
Each index list element contains the name of an index and an optional table name.
DBOP_primary_key
Node used to define a Primary Key.
DBOP_property_list_anchor
Node identifying the start of a list of DBOP_property_list_element nodes.
DBOP_property_list_element
Node used to specify a property for an object. This is a generic node consisting of a GUID and a VARIANT. The GUID represents the property to set and the VARIANT the value to set the property to. OLE DB specifies a set of "well-known" properties (see below) that can be used to define properties for an object. Individual providers may also define GUIDs representing "extended properties" that the provider supports that can also be set using this node.
DBOP_referenced_table
Node used to specify the referenced table in a Primary Key-Foreign Key relationship.
DBOP_rename_object
Node used to specify the new name of an object being renamed via a DBOP_alter_objecttype node.
DBOP_revoke_privileges
Node used to revoke privileges from an object.
DBOP_schema_authorization
Node used to specify the authorization name for schema creation.
DBOP_schema_name
Node used to specify the user name associated with a schema for schema creation.
DBOP_unique_key
Node used to define a Unique Key.

Connecting DML and DDL Command Nodes


Catalog of DML Nodes

The following table summarizes all DML command nodes. For each node, the table describes the information stored within the node, its required and optional input types, and the types of the output produced by the node. This table, along with the description of the semantics of each node in previous section, represents a first step to defining the correct way to build OLE DB command trees.

The supported output types are and minor types are listed in the form

output-type:minor-type

where output-type and minor-type are as follows. The minor type is not listed if it is not required by the operator. For more information, see IDBInfo::GetOperatorInfo.

Output Type | output-type | minor-type | Description
Table1 | T | O U OU H | Ordered Unique Ordered unique Hierarchical
Row | R | -- | --
Scalar | S | AF BMK COL CON DEF EXP NUL PAR SF | Aggregate function Bookmark Column Constant Default Expression NULL Parameter Scalar function
Boolean | B | -- | --
List anchor | LA_CD LA_COL LA_CMD LA_FRM LA_IDX LA_PRJ LA_PRP LA_ROW LA_SC LA_SET LA_SRT | -- | Column definition list anchor Column list anchor Command list anchor From list anchor Index list anchor Project list anchor Property list anchor Row list anchor Scalar list anchor Set list anchor Sort list anchor
List element | LE_CD LE_COL LE_CMD LE_FRM LE_IDX LE_PRJ LE_PRP LE_ROW LE_SC LE_SET LE_SRT | -- | Column definition list element Column list element Command list element From list element Index list element Project list element Property list element Row list element Scalar list element Set list element Sort list element
Catalog name | CN | -- | --
Schema name | SN | -- | --
Outall name | ON | -- | --
DDL operator | DDL | -- | --
Update operator | U | U D I | Update operator Delete operator Insert operator
Any legal output type | Any | -- | --
1 UT, OT, and OUT imply T, but not the other way around. UT, OT, and OUT are specializations of T. UT and OT do not imply each other. OUT implies UT and OT.

Node | DBVALUEKIND | R | O | Input Types | Output Types
absolute_sampling | see relative_sampling |  |  |  |
add_columns | DBVALUEKIND_EMPTY | 2 | 0 | 1st: T 2nd: LA_PRJ | T
aggregate | DBVALUEKIND_EMPTY | 3 | 0 | 1st: T 2,3rd: LA_PRJ | T
alias | PwszValue | 1 | 1 | 1st: T 2nd: LA_COL | T
allbits | see anybits |  |  |  |
allbits_all | see anybits |  |  |  |
allbits_any | see anybits |  |  |  |
and, or, xor, equivalent | Node weight is stored as DBVALUEKIND_I4. | 2 | N | 1st: B 2nd: B | B
any_sample | see min |  |  |  |
anybits, allbits, anybits_any, allbits_any, anybits_all, allbits_all | Node weight is store as DBVALUEKIND_I4. | 2 | 0 | 1st: S:COL 2nd: S | B
anybits_all | see anybits |  |  |  |
anybits_any | see anybits |  |  |  |
avg | see min |  |  |  |
bag_anti_difference | see set_intersection |  |  |  |
bag _intersection | see set_intersection |  |  |  |
bag_left_difference | see set_intersection |  |  |  |
bag_right_difference | see set_intersection |  |  |  |
bag_union | see set_intersection |  |  |  |
between, between_unordered | DBVALUEKIND_EMPTY | 3 | 0 | 1st: S or R 2nd: S or R 3rd: S, or R | B
between_unordered | see between |  |  |  |
bit_length | see overlaps |  |  |  |
bookmark_name | Optional pwszValue | 0 | 1 | 1st: table_name | S:BMK
case_condition | see overlaps |  |  |  |
case_value | see overlaps |  |  |  |
Cast | see overlaps |  |  |  |
catalog_name | Catalog name as: DBID, pwszValue, pMoniker | 0 | 0 |  | CN
char_length | see overlaps |  |  |  |
Coalesce | see overlaps |  |  |  |
column_list_anchor | DBVALUEKIND_EMPTY | 0 | N | 1st-nth: LE_COL | LA_COL
column_list_element | PwszValue | 0 | 0 |  | LE_COL
column_name | DBID, pwszValue, pMoniker, ulValue | 0 | 0 |  | S:COL
command_list_anchor | DBVALUEKIND_EMPTY | 0 | 1 | 1st-nth: LE_CMD | LA_CMD
command_list_element | DBVALUEKIND_EMPTY | 1 | 0 | Any DML or DDL command node | LE_CMD
content, content_freetext | DBCONTENT | 1 | 0 | 1st: S:COL | B
content_freetext | see content |  |  |  |
content_proximity | DBCONTENTPROXIMITY | 2 | n | 1st: content or content_freetext 2nd: content or content_freetext nth: Boolean expression formed from and, or, not, content_proximity, and content. | B
content_vector | DBCONTENTVECTOR | 2 | n | 1st: content or content_freetext 2nd: content or content_freetext nth: Boolean expression formed from and, or, not, content_proximity, content, and content_freetext?? | B
convert | see overlaps |  |  |  |
count | dwSetQuantifier field of DBSETFUNC | 0 | 1 | 1st: S:COL | S:AF
cross_join |  | 2 | 0 | 1st: T 2nd: T | T
cross_tab | DBVALUEKIND_EMPTY | 4 | 1 | 1st: T:H 2nd: LE_COL 3rd: LA_COL 4th: LA_COL 5th: LA_PRJ | T:H
current_date | see overlaps |  |  |  |
current_time | see overlaps |  |  |  |
current_timestamp | see overlaps |  |  |  |
DEFAULT, NULL | DBVALUEKIND_EMPTY | 0 | 0 |  | S:DEF, S:NUL
defined_by_GUID | DBBYGUID | * | * | * defined by implementation | *
delete | DBVALUEKIND_EMPTY | 1 | 1 | 1st: table_name 2nd: T | U:D
disjoint | see subset |  |  |  |
distinct | DBVALUEKIND_EMPTY | 1 | 0 | 1st: T | T:U
distinct_order_preserving | DBVALUEKIND_EMPTY | 1 | 0 | 1st: T or T:O | T:U or T:OU
div | see plus |  |  |  |
division | DBVALUEKIND_EMPTY | 2 | 0 | 1st: T 2nd: T | T
equal, not_equal, less, less_equal, greater, greater_equal | Node weight is stored as DBVALUEKIND_I4. | 2 | 0 |  | B
equal_all, not_equal_all, less_all, less_equal_all, greater_all, greater_equal_all, equal_any, not_equal_any, less_any, less_equal_any, greater_any, greater_equal_any | Node weight is stored as DBVALUEKIND_I4. | 2 | 0 | 1st: S or R 2nd: T | B
equal_any | see equal_all |  |  |  |
equivalent | see and |  |  |  |
exists, unique | DBVALUEKIND_EMPTY | 1 | 0 | 1st: T | B
extract | see overlaps |  |  |  |
first | see min |  |  |  |
from_list_anchor | DBVALUEKIND_EMPTY | 0 | N | 1st-nth: LE_FRM | LA_FRM
from_list_element | DBVALUEKIND_EMPTY | 1 | 0 | 1st: T [including table_name or alias nodes] | LE_FRM
full_outer_join | see inner_join |  |  |  |
greater | see equal |  |  |  |
greater_all | see equal_all |  |  |  |
greater_any | see equal_all |  |  |  |
greater_equal | see equal |  |  |  |
greater_equal_all | see equal_all |  |  |  |
greater_equal_any | see equal_all |  |  |  |
implies | DBVALUEKIND_EMPTY | 2 | 0 | 1st: B 2nd: B | B
in | DBVALUEKIND_EMPTY | 2 | 0 | 1st: R 2nd: T | B
inner_join, left_semi_join, right_semi_join, left_anti_semi_join, right_anti_semi_join, left_outer_join, right_outer_join, full_outer_join | DBVALUEKIND_EMPTY | 3 | 0 | 1st: T 2nd: T 3rd: B [condition join] | T
insert | DBVALUEKIND_EMPTY | 3 | 0 | 1st: R or T 2nd: T 3rd: LA_COL | U:I
is_FALSE | see is_INVALID |  |  |  |
is_INVALID, is_TRUE, is_FALSE | DBVALUEKIND_EMPTY | 1 | 0 | 1st: S | B
is_NOT_NULL | see is_NULL |  |  |  |
is_NULL, is_NOT_NULL | DBVALUEKIND_EMPTY | 0 | 0 |  | B
is_TRUE | see is_INVALID |  |  |  |
last | see min |  |  |  |
left_anti_semi_join | see inner_join |  |  |  |
left_outer_join | see inner_join |  |  |  |
left_semi_join | see inner_join |  |  |  |
less | see equal |  |  |  |
less_all | see equal_all |  |  |  |
less_any | see equal_all |  |  |  |
less_equal | see equal |  |  |  |
less_equal_all | see equal_all |  |  |  |
less_equal_any | see equal_all |  |  |  |
like | DBLIKE | 2 | 0 | 1st: S 2nd: S | B
like_all | see like_any |  |  |  |
like_any, like_all | DBVALUEKIND_EMPTY | 2 | 0 | 1st: S 2nd: T | B
lower | see overlaps |  |  |  |
match, match_unique, match_partial, match_partial_unique, match_full, match_full_unique | DBVALUEKIND_EMPTY | 2 | 0 | 1st: S or R 2nd: S or R | B
match_full | see match |  |  |  |
match_full_unique | see match |  |  |  |
match_partial | see match |  |  |  |
match_partial_unique | see match |  |  |  |
match_unique | see match |  |  |  |
max | see min |  |  |  |
min, max, sum, avg, any_sample, stddev, stddev_pop, var, var_pop, first, last | dwSetQuantifier field of DBSETFUNC | 1 | 0 | 1st: S:COL | S:AF
minus | see plus |  |  |  |
module | see plus |  |  |  |
natural_full_outer_join | see natural_join |  |  |  |
natural_join, natural_left_outer_join, natural_right_outer_join, natural_full_outer_join | DBVALUEKIND_EMPTY | 2 | 1 | 1st: T 2nd: T 3rd: LA_COL [using list] | T
natural_left_outer_join | see natural_join |  |  |  |
natural_right_outer_join | see natural_join |  |  |  |
navigate | DBVALUEKIND_EMPTY | 6 | 0 | 1st: T:H or T 2nd: T:H or T 3rd: B 4th: LE_COL 5th: LE_COL 6th: LE_COL | T:H
nested_column_name | Column name as: DBID, pwszValue, pMoniker | 2 | 0 | 1st: nested_table_name 2nd: S:COL | T:H
nested_table_name | Table name as: DBID, pwszValue, pMoniker | 0 | 1 | 1st: nested_table_name or table_name | T:H
nested_apply | DBVALUEKIND_EMPTY | 2 | 1 | 1st: T:H ? 3rd: LE_COL | T:H or T
nesting | DBVALUEKIND_EMPTY | 5 | 0 | 1st: T:H or T 2nd: LE_COL 3rd: LE_COL | T:H
not | Node weight is stored as DBVALUEKIND_I4. | 1 | 0 | 1st: B | B
not_equal | see equal |  |  |  |
not_equal_all | see equal_all |  |  |  |
not_equal_any | see equal_all |  |  |  |
NULL | see DEFAULT |  |  |  |
nullif | see overlaps |  |  |  |
octet_length | see overlaps |  |  |  |
or | see and |  |  |  |
order_preserve_select | DBVALUEKIND_EMPTY | 2 | 0 | 1st: T or T:O 2nd: B | T or T:O
outall_name | DBVALUEKIND_EMPTY | 0 | 0 |  | ON
over | see plus |  |  |  |
overlaps, case_condition, case_value, nullif, cast, coalesce, position, extract, char_length, octet_length, bit_length, substring, upper, lower, trim, translate, convert, string_concat, current_date, current_time, current_timestamp | TBD | TBD | TBD | TBD | B or S:SF
pass_through | DBVALUEKIND_EMPTY | 1 | 0 | 1st: any tree input | Any
plus, minus, times, over, div, module, power | DBVALUEKIND_EMPTY | 2 | 0 | 1st: S 2nd: S | S:EXP
position | see overlaps |  |  |  |
power | see plus |  |  |  |
prior_command_tree | DBVALUEKIND_EMPTY | 0 | 0 |  | S or T
project | DBVALUEKIND_EMPTY | 2 | 0 | 1st: T 2nd: LA_PRJ | T
project_list_anchor | DBVALUEKIND_EMPTY | 0 | N | 1st-nth: LE_PRJ | LA_PRJ
project_list_element | PwszValue | 1 | 0 | 1st: S, qualifier_name, or ON | LE_PRJ
project_order_preserving | DBVALUEKIND_EMPTY | 2 | 0 | 1st: T or T:O 2nd: LA_PRJ | T or T:O
proper_subset | see subset |  |  |  |
proper_superset | see subset |  |  |  |
qualified_column_name | DBVALUEKIND_EMPTY | 2 | 0 | 1st: table_name 2nd: S:COL | S:COL
qualifier_name | Table name as: DBID, pwszValue, pMoniker | 0 | 0 |  | qualifier_name
rank, rank_ties_equally, rank_ties_equally_and_skip | DBVALUEKIND_EMPTY | 1 | 0 | 1st: T:O | T:O
rank_ties_equally | see rank |  |  |  |
rank_ties_equally_and_skip | see rank |  |  |  |
recursive_union, recursive_union_join | DBVALUEKIND_EMPTY | 2 | 0 | 1st: T 2nd: B | T
recursive_union_join | see recursive_union |  |  |  |
relative_sampling, absolute_sampling | integer in ulValue | 1 | 0 | 1st: T | T
remote_table | DBVALUEKIND_EMPTY | 2 | 0 | REQ 1: table_name OPT 1: property_list_anchor, property_list_element: DBINIT_OPT_ CLSID, DBINIT_OPT_ MONIKER DBINIT_OPT_ LOCATION DBINIT_OPT_ NAME DBINIT_OPT_ USERID, DBINIT_OPT_ PASSWORD DBINIT_OPT_ STRING | T
right_anti_semi_join | see inner_join |  |  |  |
right_outer_join | see inner_join |  |  |  |
right_semi_join | see inner_join |  |  |  |
row | DBVALUEKIND_EMPTY | 1 | 0 | 1st: LA_SC | R
row_list_anchor | DBVALUEKIND_EMPTY | 0 | N | 1st-nth: LE_ROW | LA_ROW
row_list_element | DBVALUEKIND_EMPTY | 1 | 0 | 1st: R | LE_ROW
scalar_constant | any value | 0 | 0 |  | S:CON
scalar_function | DBID, pwszValue, Imoniker | 0 | N | 1st-nth: S | S:SF
scalar_list_anchor | DBVALUEKIND_EMPTY | 0 | N | 1st-nth: S:CON | LA_SC
scalar_list_element | DBVALUEKIND_EMPTY | 1 | 0 | 1st: S:CON | LE_SC
scalar_parameter | DBPARAMETER | 0 | 0 |  | S:PAR
schema_name | Schema name as: DBID, pwszValue, pMoniker | 0 | 1 | 1st: CN | SN
select | DBVALUEKIND_EMPTY | 2 | 0 | 1st: T 2nd: B | T
set_anti_difference | see set_intersection |  |  |  |
set_intersection, set_union, set_left_difference, set_right_difference, set_anti_difference, bag_intersection, bag_union, bag_left_difference, bag_right_difference, bag_anti_difference | fValue = TRUE represents "SQL's union corresponding" | 2 | 1 | 1st: T 2nd: T 3rd: LA_COL | T
set_left_difference | see set_intersection |  |  |  |
set_right_difference | see set_intersection |  |  |  |
set_union | see set_intersection |  |  |  |
sort | DBVALUEKIND_EMPTY | 2 | 0 | 1st: T. 2nd: LA_SRT | T:O
sort_list_anchor | DBVALUEKIND_EMPTY | 0 | N | 1st-nth:LE_SRT | LA_SRT
sortalistaelement | DBSORTINFO | 1 | 0 | 1st: S | LEaSRT
soundsalike | DBVALUEKINDaEMPTY | 2 | 0 | 1st:S 2nd: S | B
SQLaselect | fValue=TRUE denotes DISTINCT | 6 | 0 | 1st: LAaPRJ 2nd: LAaFRM 3rd: B 4th: LAaPRJ 5th: B 6th: LAaSRT NOTE: Lists may be empty | T, T:O, T:U, T:OU
stddev | see min |  |  |  |
stddevapop | see min |  |  |  |
stringaconcat | see overlaps |  |  |  |
subset, properasubset, superset, properasuperset, disjoint | DBVALUEKINDaEMPTY | 2 | 0 | 1st: T 2nd: T | B
substring | see overlaps |  |  |  |
sum | see min |  |  |  |
superset | see subset |  |  |  |
table | DBVALUEKINDaEMPTY | 1 | 0 | 1st: LAaROW | T
tableaname | Table name as: DBID, pwszValue, pMoniker | 0 | 1 | 1st: SN | T
textacommand | DBTEXT | 0 | 1 | Any command node. [When used it indicates a "partial" text command.] | S, T
times | see plus |  |  |  |
top, topaplusaties | ulValue field contains the count of rows | 1 | 0 | 1st: T:O | T:O
topa%, topa%aplusaties | DBVALUEKINDaEMPTY | 2 | 0 | 1st: T:O 2nd: LAaSC | T:O
topa%aplusaties | see topa% |  |  |  |
topaplusaties | see top |  |  |  |
transitiveaclosure | DBVALUEKINDaEMPTY | 4 | 1 | 1st: T 2nd: B [join condition] 3rd: LAaPRJ 4th: B [predicate on Delta] 5th: B [initial selection condition] | T
translate | see overlaps |  |  |  |
trim | see overlaps |  |  |  |
unionajoin | DBVALUEKINDaEMPTY | 2 | 0 | 1st: T 2nd: T | T
unique | see exists |  |  |  |
unnesting | DBVALUEKINDaEMPTY | 2 | 0 | 1st: T:H 2nd: LEaCOL | T:H, T
update | DBVALUEKINDaEMPTY | 2 | 0 | 1st: T 2nd: LAaSET | U:U
upper | see overlaps |  |  |  |
var | see min |  |  |  |
varapop | see min |  |  |  |
xor | see and |  |  |  |


Catalog of DDL command nodes

Node | DBTREEVALUE | R | O | Children | Output type
alteracharacteraset | pwszValue | 0 | 1 | OPT 1: renameaobject |
alteracollation | pwszValue | 0 | 1 | OPT 1: renameaobject |
alteradomain | pwszValue | 0 | 1 | OPT 1: renameaobject |
alteraindex | pwszValue | 0 | 1 | OPT 1: renameaobject |
alteraprocedure | pwszValue | 0 | 1 | OPT 1: renameaobject |
alteraschema | pwszValue | 0 | 1 | OPT 1: renameaobject |
alteratable | pwszValue | 0 | 1 | OPT 1: renameaobject |
alteratrigger | pwszValue | 0 | 1 | OPT 1: renameaobject |
alteraview | pwszValue | 0 | 1 | OPT 1: renameaobject |
characterasetaname | pwszValue | 0 | 0 |  |
coldefalistaanchor | DBVALUEKINDa EMPTY | 0 | 1 | OPT 1: coldefalistaelement |
coldefalistaelement | DBVALUEKINDa COLDESC | 0 | 1 | OPT 1: propertyalistaanchor1, propertyalistaelement: DBaPROPERTYa DEFAULTaVALUE DBaPROPERTYa DESCRIPTION DBaPROPERTYa NULLABLE DBPROPaUNIQUE DBPROPa PRIMARYKEY DBPROPa AUTOINCREMENT |
createaassertion | pwszValue | 1 | 1 | REQ 1: select OPT 2: propertyalistaanchor, propertyalistaelement: DBaPROPERTYaCONSTRAINTaCHECKaDEFERRED, DBaPROPERTYa DESCRIPTION |
createacharacteraset | pwszValue | 1 | 2 | REQ 1: characterasetaname OPT 1: collateaclause OPT 2: propertyalistaanchor, propertyalistaelement: DBaPROPERTYa DESCRIPTION |
createacollation | pwszValue | 2 | 1 | REQ 1: characterasetaname REQ 2: TBD OPT 1: TBD |
createadomain | pwszValue | 1 | 3 | REQ: dataatype OPT 1: propertyalistaanchor, propertyalistaelement: DBaPROPERTYa DEFAULTaVALUE OPT 2: domainaconstraint OPT 3: collateaclause |
createaindex | pwszValue | 2 | 1 | REQ 1: name (table name)REQ 2: columnalistaanchor, columnalistaelement The column list elements contain the name of a column and a sort order (ascending or descending). OPT 1: propertyalistaanchor, propertyalistaelement: DBPROPaINDEXa PRIMARYKEY DBPROPaINDEXa UNIQUE DBPROPaINDEXa CLUSTERED DBPROPaINDEXa TYPE DBPROPaINDEXa FILLFACTOR DBPROPaINDEXa INITIALSIZE DBPROPaINDEXa NULLS DBPROPaINDEXa SORTBOOKMARKS DBPROPaINDEXa AUTOUPDATE DBPROPaINDEXa NULLCOLLATION |
createaprocedure | pwszValue | TBD | TBD | TBD |
createaschema | DBVALUEKINDa EMPTY | 0 | 3 | OPT 1: schemaaauthorization OPT 2: schemaaname OPT 3: propertyalistaanchor, propertyalistaelement: DBaPROPERTYa DESCRIPTION |
create_synonym | pwszValue (local name for synonym) | 1 | 1 | REQ 1: table_name OPT 1: property_list_anchor, property_list_element: DBINIT_OPT_ CLSID, DBINIT_OPT_ MONIKER DBINIT_OPT_ LOCATION DBINIT_OPT_ NAME DBINIT_OPT_ USERID, DBINIT_OPT_ PASSWORD DBINIT_OPT_ STRING |
create_table | pwszValue | 1 | 5 | REQ 1: coldef_list_anchor OPT 1: primary_key OPT 2: foreign_key OPT 3: unique_key OPT 4: check_constraint OPT 5: property_list_anchor, property_list_element: DB_PROPERTY_ DESCRIPTION |
create_temporary_table | pwszValue | 1 | 1 | REQ 1: coldef_list_anchor OPT 2: property_list_anchor, property_list_element: DB_PROPERTY_ DESCRIPTION |
create_translation | pwszValue | 3 | 1 | REQ 1: character_set_name REQ 2: character_set_name REQ 3: TBD OPT 2: property_list_anchor, property_list_element: DB_PROPERTY_ DESCRIPTION |
create_trigger | pwszValue | TBD | TBD | OPT 1: property_list_anchor, property_list_element: DB_PROPERTY_ DESCRIPTION |
create_view | pwszValue | 1 | 1 | REQ: T OPT: property_list_anchor, property_list_element: DB_PROPERTY_ CHECK_OPTION |
drop_assertion | pwszValue | 0 | 1 | OPT 1: property_list_anchor, property_list_element: DB_PROPERTY_ DROP_CASCADE |
drop_character_set | pwszValue | 0 | 1 | OPT 1: property_list_anchor, property_list_element: DB_PROPERTY_ DROP_CASCADE |
drop_collation | pwszValue | 0 | 1 | OPT 1: property_list_anchor, property_list_element: DB_PROPERTY_ DROP_CASCADE |
drop_domain | pwszValue | 0 | 1 | OPT 1: property_list_anchor, property_list_element: DB_PROPERTY_ DROP_CASCADE |
drop_index | pwszValue | 0 | 0 | OPT 1: property_list_anchor, property_list_element: DB_PROPERTY_ DROP_CASCADE |
drop_procedure | pwszValue | 0 | 1 | OPT 1: property_list_anchor, property_list_element: DB_PROPERTY_ DROP_CASCADE |
drop_schema | pwszValue | 0 | 1 | OPT 1: property_list_anchor, property_list_element: DB_PROPERTY_ DROP_CASCADE |
drop_synonym | pwszValue (local name for synonym) | 0 | 1 | OPT 1: property_list_anchor property_list_element: DB_PROPERTY_ DROP_CASCADE |
drop_table | pwszValue | 0 | 1 | OPT 1: property_list_anchor, property_list_element: DB_PROPERTY_ DROP_CASCADE |
drop_translation | pwszValue | 0 | 1 | OPT 1: property_list_anchor, property_list_element: DB_PROPERTY_ DROP_CASCADE |
drop_trigger | pwszValue | 0 | 1 | OPT 1: property_list_anchor, property_list_element: DB_PROPERTY_ DROP_CASCADE |
drop_view | pwszValue | 0 | 1 | OPT 1: property_list_anchor, property_list_element: DB_PROPERTY_DROP_CASCADE |
foreign_key | pwszValue2 | 2 | 1 | REQ 1: column_list_anchor REQ 2: referenced_table OPT 1: column_list_anchor |
grant_privileges |  | 1 | 1 | 1st: TBD 2nd: TBD |
index_list_anchor |  | 0 | 1 | OPT: index_list_element |
index_list_element |  |  |  |  |
primary_key | pwszValue2 | 1 | 0 | REQ 1: column_list_anchor |
property_list_anchor | DBVALUEKIND_ EMPTY | 0 | 1 | OPT: property_list_element |
property_list_element | DBVALUEKIND_ EMPTY | 0 | 0 |  |
referenced_table | pwszValue | 0 | 0 |  |
rename_object | pwszValue | 0 | 0 |  |
revoke_privileges |  | 1 | 1 | 1st: TBD 2nd: property_list_anchor, property_list_element: DB_PROPERTY_ DROP_CASCADE |
schema_authorization | pwszValue | 0 | 0 |  |
schema_name | pwszValue | 0 | 0 |  |
unique_key | pwszValue | 1 | 0 | REQ 1: column_list_anchor |
1 Only required if multiple property_list_element nodes are specified. 2 Optional.


"Well-Known" Properties

There are a set of "well-known" properties that are pre-defined in OLE DB. These properties are identified by GUIDs and used in property_list_element nodes.

Property | Description | DBTYPE | Allowable values
DB_PROPERTY_CHECK_ OPTION | Specifies whether updates are allowed that affect rows other than the one specified (i.e., update via view affects more than one row in a participating base table). | BOOL | TRUE - Local update checking only. FALSE - Cascaded update checking.
DB_PROPERTY_CONSTRAINT_ CHECK_DEFERRED | Specifies when a constraint is checked for violation. | BOOL | FALSE - Constraint checked immediately (INITIALLY IMMEDIATE). TRUE - Constraint check deferred (INITIALLY DEFERRED).
DB_PROPERTY_DEFAULT_ VALUE | A variant specifying the default value for an object (typically a domain or column). If the default value is a string, the string must be quoted (with the value returned for DBLITERAL_QUOTE in DBInfo::GetLiteralInfo) so it can be distinguished from an object of the same name. For example, 'Salary' is a string, Salary is an object (such as a column). | VARIANT | Any value.
DB_PROPERTY_DESCRIPTION | A string specifying a "human-readable" description (of the specified object). | LPWSTR | Any text string.
DB_PROPERTY_DROP_ CASCADE | Specifies the drop behavior when a database object is dropped. A value of FALSE(RESTRICT) will cause the drop command to fail if other referencing objects exist in the database. A value of TRUE(CASCADE) will drop all objects, related to the object being dropped, that exist in the database. | BOOL | FALSE - RESTRICT drop behavior TRUE - CASCADE drop behavior
DB_PROPERTY_ON_ COMMIT_PRESERVE_ROWS |  | BOOL | TRUE - Rows in a temporary table and not deleted when transaction is committed. FALSE - Rows in a temporary table are deleted when transaction is committed.
DB_PROPERTY_NULLABLE | Specifies whether a set of values can contain a NULL value. | BOOL | FALSE - NULL is not allowable TRUE - NULL is allowable
DBINIT_OPT _CLSID | CLSID of the data provider's DSO. | GUID |
DBINIT_OPT_MONIKER | Pointer to a moniker to the data provider. | *pMoniker |
DBINIT_OPT_LOCATION | The location of the database to connect to. Typically, a server name. | LPWSTR |
DBINIT_OPT_NAME | The name of the database to connect to. This could be either the full path-name of the database file or some other name that identifies the database within the provider. | LPWSTR |
DBINIT_OPT_USERID | User-Id to be used when connecting to the provider. | LPWSTR |
DBINIT_OPT_PASSWORD | Password to be used when connecting to the provider. | LPWSTR |
DBINIT_OPT_STRING | A general purpose string which includes all the connection parameters in a format understood by the provider (e.g. ODBC connect string). | LPWSTR |
DBPROP_AUTOINCREMENT | Specifies whether the values of the column are autoincrementing. | BOOL | TRUE - The values of the column are autoincrementing. FALSE - The values of the column are not autoincrementing.
DBPROP_UNIQUE | Whether values of the column must be unique in the table. | BOOL | TRUE - The values of the column must be unique within the table. FALSE - The values of the column can be repeated within the table.
DBPROP_PRIMARYKEY | Whether the column is part of the primary key. | BOOL | TRUE - The column is part of the primary key of the table. FALSE - The column is not part of the primary key of the table.


DBCOMMANDTREE Structures

This structure is used for each DML or DDL node in an OLE DB command tree. Many operations create a binding environment. For example, a DBOP_select operation has two inputs: a table and a Boolean predicate. By virtue of the "select" operation, the table becomes the binding environment for the predicate. That means that the predicate may freely reference column names defined in the table. Note that not all bindings must come from the nearest table operation. For example, there might be multiple table operations within an "exist" expression, and any predicate may reference a column defined outside the "exist" expression (In SQL, this is called a "correlated subquery.").

DBCOMMANDTREE Structure

This is the primary data structure used to represent any node in an OLE DB tree as described in the Data Manipulation and Data Definition Operator Sections above.

typedef WORD DBCOMMANDOP;

typedef struct tagDBCOMMANDTREE {
    DBCOMMANDOP op;      // Operator identifier (2 bytes)
    WORD            wKind;   // Discriminator for the following union (2 bytes)
    struct tagDBCOMMANDTREE * pctFirstChild;  // Pointer to first child (4 bytes)
    struct tagDBCOMMANDTREE * pctNextSibling; // Pointer to sibling (4 bytes)
    union {     // Union direclty represents fields that fit within 8 bytes.
            // Comments list DBVALUEKIND and DBTYPE corresponding to each field.
        BOOL                    fValue;       // DBVALUEKIND_BOOL      (DBTYPE_BOOL)
        unsigned char           uchValue;     // DBVALUEKIND_UI1       (DBTYPE_UI1)
        signed char             schValue;     // DBVALUEKIND_I1        (DBTYPE_I1)
        unsigned short          usValue;      // DBVALUEKIND_UI2       (DBTYPE_UI2)
        short                   sValue;       // DBVALUEKIND_I2        (DBTYPE_I2)
        LPWSTR                  pwszValue;    // DBVALUEKIND_WSTR      (DBTYPE_WSTR)
        LONG                    lValue;       // DBVALUEKIND_I4        (DBTYPE_I4)
        ULONG                   ulValue;      // DBVALUEKIND_UI4       (DBTYPE_UI4)
        float                   flValue;      // DBVALUEKIND_R4        (DBTYPE_R4)
        double                  dblValue;     // DBVALUEKIND_R8        (DBTYPE_R8)
        CY                      cyValue;      // DBVALUEKIND_CY        (DBTYPE_CY)
        DATE                    dateValue;    // DBVALUEKIND_DATE      (DBTYPE_DATE)
        DBDATE                  dbdateValue;  // DBVALUEKIND_DBDATE    (DBTYPE_DBDATE)
        DBTIME                  dbtimeValue;  //    DBVALUEKIND_DBTIME (DBTYPE_DBTIME)
        SCODE                   scodeValue;   // DBVALUEKIND_ERROR     (DBTYPE_ERROR)
        __int64                 llValue;      // DBVALUEKIND_I8        (DBTYPE_I8)
        unsigned __int64        ullValue;     // DBVALUEKIND_UI8       (DBTYPE_UI8)
        BSTR     *              pbstrValue;   // DBVALUEKIND_BSTR          (DBTYPE_BSTR)
        ICommand     *          pCommand;     // DBVALUEKIND_COMMAND   (DBTYPE_IUNKNOWN)
        IDispatch *             pDispatch;    // DBVALUEKIND_IDISPATCH (DBTYPE_IDISPATCH)
        IMoniker     *          pMoniker;     // DBVALUEKIND_MONIKER   (DBTYPE_MONIKER)
        IRowset *               pRowset;      // DBVALUEKIND_ROWSET    (DBTYPE_ROWSET)
        IUnknown     *          pUnknown;     // DBVALUEKIND_IUNKNOWN  (DBTYPE_IUNKNOWN)
        DBBYGUID     *       pdbbygdValue;    // DBVALUEKIND_BYGUID
        DBCOLUMNDESC*        pcoldescValue;   // DBVALUEKIND_COLDESC
        DBID *               pdbidValue;      // DBVALUEKIND_ID
        DBLIKE *             pdblikeValue;    // DBVALUEKIND_LIKE
        DBCONTENT *          pdbcntntValue;   // DBVALUEKIND_CONTENT
        DBCONTENTVECTOR *    pdbcntntvcValue; // DBVALUEKIND_CONTENTVECTOR
        DBCONTENTPROXIMITY *    pdbcntntproxValue;    // DBVALUEKIND_CONTENTPROXIMITY
        DBGROUPINFO *           pdbgrpinfValue;  // DBVALUEKIND_GROUPINFO
        DBPARAMETER *           pdbparamValue;   // DBVALUEKIND_PARAMETER
        DBPROPSET *             pdbpropValue;    // DBVALUEKIND_PROPERTY
        DBSETFUNC *             pdbstfncValue;   // DBVALUEKIND_SETFUNC
        DBSORTINFO *            pdbsrtinfValue;  // DBVALUEKIND_SORTINFO
        DBTEXT *                pdbtxtValue;     // DBVALUEKIND_TEXT
        DBVECTOR     *          pdbvectorValue;  // DBVALUEKIND_VECTOR | *
        SAFEARRAY *             parrayValue;     // DBVALUEKIND_ARRAY | *
        VARIANT *               pvarValue;       // DBVALUEKIND_VARIANT  (DBTYPE_VARIANT)
        GUID     *              pGuid;           // DBVALUEKIND_GUID     (DBTYPE_GUID)
        BYTE     *              pbValue;         // DBVALUEKIND_BYTES    (DBTYPE_BYTES)
        char     *              pzValue;         // DBVALUEKIND_STR      (DBTYPE_STR)
        DBNUMERIC *             pdbnValue;       // DBVALUEKIND_NUMERIC  (DBTYPE_NUMERIC)
        DBTIMESTAMP *           pdbtsValue;      // DBVALUEKIND_DBTIMESTAMP (DBTYPE_DBTIMESTAMP)
        void *                  pvValue;         // a generic DBVALUEKIND_BYREF
    } value;
    HRESULT     hrError;     // Error indicator, details in Extended Error info (4 bytes)
} DBCOMMANDTREE;

The typical size of a DBCOMMANDTREE node is 24 bytes, however, operators may store some specific information inside the value field of the node. For programming convenience, the union field includes branches representing some common types that can fit within 8 bytes. Variable-length types are referenced via a pointer to the corresponding structure (e.g., DBTEXT). The discriminator for the union is of type WORD rather than DBVALUEKIND so that it is possible to store node values such as DBVALUEKIND_VECTOR | DBVALUEKIND_GUID, DBVALUEKIND_BYREF | DBVALUEKIND_UI4, or DBVALUEKIND_SAFEARRAY | DBVALUEKIND_I4;

The pwszValue field is just a string; its interpretation is left to the provider. In other words, the consumer must understand the provider's method of name resolution. For command components "passed-through" to another provider, it is left to that bottom provider to interpret the string.

The pMoniker field is used for unresolved linked objects, in particular tables and functions.

The pRowset field is used to reference a currently open rowset as input for a command. Most providers will fail if some IPersist method is invoked while the command tree refers to an open rowset.

The pCommand field references another command object as input for a command. Most providers will fail if some IPersist method is invoked while the command tree refers to a command object that does not support monikers.

DBVALUEKIND Enumerated Type

This enumerated type is used to discriminate the union field inside a DBCOMMANDTREE struct. For programming convenience, the values in this enumeration correspond exactly to the OLE Automation VARENUM and OLE DB DBTYPE (see Appendix A for description) enumerations. The comments associated with each enumeration value represents the type and branch of the union type inside the command structure containing the value. Nodes that do not assign a value to the union member should assign a DBVALUEKIND_EMPTY to wKind.

enum DBVALUEKIND {
    // List of structure values     union contains a pointer to:
    DBVALUEKIND_BYGUID            = 256.                            // DBBYGUID struct
    DBVALUEKIND_COLDESC            = DBVALUEKIND_BYGUID + 1,        // DBCOLDESC struct     DBVALUEKIND_ID                = DBVALUEKIND_COLDESC + 1,        // DBID struct
    DBVALUEKIND_CONTENT            = DBVALUEKIND_ID + 1,            // DBCONTENT struct
    DBVALUEKIND_CONTENTVECTOR     = DBVALUEKIND_CONTENT + 1,        // DBCONTENTVECTOR struct
    DBVALUEKIND_GROUPINFO        = DBVALUEKIND_CONTENTVECTOR + 1,    // DBGROUPINFO struct
    DBVALUEKIND_PARAMETER        = DBVALUEKIND_GROUPINFO + 1,        // DBPARAMETER struct
    DBVALUEKIND_PROPERTY            = DBVALUEKIND_PARAMETER + 1,        // DBPROPERTY struct
    DBVALUEKIND_SETFUNC            = DBVALUEKIND_PROPERTY + 1,        // DBSETFUNC struct
    DBVALUEKIND_SORTINFO            = DBVALUEKIND_SETFUNC + 1,        // DBSORTINFO struct
    DBVALUEKIND_TEXT                = DBVALUEKIND_SORTINFO + 1,        // DBTEXT struct

    // List of OLE interface pointer values
    DBVALUEKIND_COMMAND            = DBVALUEKIND_TEXT + 1,            // pCommand value
    DBVALUEKIND_MONIKER            = DBVALUEKIND_COMMAND + 1,        // pMoniker value
    DBVALUEKIND_ROWSET            = DBVALUEKIND_MONIKER + 1,        // pRowset value
    DBVALUEKIND_LIKE            = DBVALUEKIND_ROWSET + 1,        // DBLIKE struct
    DBVALUEKIND_CONTENTPROXIMITY    =    DBVALUEKIND_LIKE + 1,    // DBCONTENTPROXIMITY structure
    DBVALUEKIND_IDISPATCH        = 9,                                // pDispatch value
    DBVALUEKIND_IUNKNOWN            = 13,                            // pUnknown value

    // List of OLE DB type values
    DBVALUEKIND_EMPTY            = 0,                                // No value assigned to
                                                                // the union
    DBVALUEKIND_NULL                = 1,                                // NULL value
    DBVALUEKIND_I2                = 2,                                // sValue
    DBVALUEKIND_I4                = 3,                                // lValue
    DBVALUEKIND_R4                = 4,                                // flValue
    DBVALUEKIND_R8                = 5,                                // dblValue
    DBVALUEKIND_CY                = 6,                                // cyValue
    DBVALUEKIND_DATE                = 7,                                // dateValue
    DBVALUEKIND_BSTR                = 8,                                // pbstrValue

    DBVALUEKIND_ERROR            = 10,                            // scodeValue;
    DBVALUEKIND_BOOL                = 11,                            // fValue
    DBVALUEKIND_VARIANT            = 12,                            // pvarValue to an OLE
                                                                // Aut. variant

    DBVALUEKIND_VECTOR            = 0x1000,                        // pdbvectorValue
    DBVALUEKIND_ARRAY            = 0x2000,                        // parrayValue
    DBVALUEKIND_BYREF            = 0x4000,                        // pvValue

    DBVALUEKIND_I1                = 16,                            // bValue
    DBVALUEKIND_UI1                = 17,                            // bValue
    DBVALUEKIND_UI2                = 18,                            // sValue
    DBVALUEKIND_UI4                = DBVALUEKIND_UI2 + 1,            // ulValue
    DBVALUEKIND_I8                = DBVALUEKIND_UI4 + 1,            // llValue
    DBVALUEKIND_UI8                = DBVALUEKIND_UI8 + 1,            // ullValue
    DBVALUEKIND_GUID                = 72,                            // pGuid

    DBVALUEKIND_BYTES            = 128,                            // pbValue
    DBVALUEKIND_STR                = 129,                            // pszValue
    DBVALUEKIND_WSTR                = DBVALUEKIND_STR + 1,            // pwszValue
    DBVALUEKIND_NUMERIC            = DBVALUEKIND_WSTR + 1            // pdbnValue
    DBVALUEKIND_DBDATE            = DBVALUEKIND_NUMERIC + 2        // dbdateValue
    DBVALUEKIND_DBTIME            = DBVALUEKIND_DBDATE + 1                // dbtimeValue
    DBVALUEKIND_DBTIMESTAMP        = DBVALUEKIND_DBTIME + 1                // pdbtsValue
};

DBBYGUID Structure

tyepdef struct tagDBBYGUID {
    GUID      guid;      // this node's GUID
    ULONG     cbInfo;    // size of the data in pbInfo
    BYTE *    pbInfo;    // extra node information, provider-specific
} DBBYGUID;

DBID Structure

This structure encapsulates various ways of identifying a database object. It is used by nodes that need to represent a column name, i.e., column_name, index_name, table_name, schema_name, catalog_name. The DBID struct is also used to define bindings.

typedef struct  tagDBID {
    union {
        GUID guid;
        GUID *pguid;
    } uGuid;
    DBKIND eKind;
    union {
        LPWSTR pwszName;
        ULONG ulPropid;
    } uName;
} DBID;

typedef DWORD DBKIND;

enum DBKINDENUM {
    DBKIND_GUID_NAME = 0,  // use the guid and pwszName members
    DBKIND_GUID_PROPID,    // use the guid and ulPropid members
    DBKIND_NAME,           // use only the pwszName member, ignore the
                           // uGuid member
    DBKIND_PGUID_NAME,     // use the pGuid and pwszName members
    DBKIND_PGUID_PROPID,   // use the pGuid and ulPropid members
    DBKIND_PROPID,         // use only the ulPropid member, ignore the
                           // uGuid member
    DBKIND_GUID,           // use only the guid member, ignore the
};

DBCONTENT Structure

This structure represents specific information required by the DBOP_content operator.

typedef struct tagDBCONTENT {
    LPWSTR    pwszPhrase;          // text
    DWORD     dwGenerateMethod;    // exact, prefix, stemmed
    LONG      lWeight;             // weight of node
    LCID      lcid;                // locale
} DBCONTENT;

#define GENERATE_METHOD_EXACT      ( 0 )    // size is 4
#define GENERATE_METHOD_PREFIX     ( 1 )    // size is 4
#define GENERATE_METHOD_INFLECT    ( 2 )    // size is 4

DBCONTENTVECTOR Structure

This structure represents specific information required by the DBOP_content_vector_or operator.

typedef struct tagDBCONTENTVECTOR {
    DWORD  dwRankingMethod;        // jaccard, cosine, etc.
    LONG   lWeights;             // node weight
} DBCONTENTVECTOR ;

// Ranking methods:
#define VECTOR_RANK_MIN        ( 0 )    // size is 4
#define VECTOR_RANK_MAX        ( 1 )    // size is 4
#define VECTOR_RANK_INNER      ( 2 )    // size is 4
#define VECTOR_RANK_DICE       ( 3 )    // size is 4
#define VECTOR_RANK_JACCARD    ( 4 )    // size is 4

DBCONTENTVECTORPROXIMITY Structure

This structure represents specific information required by the DBOP_content_proximity operator.

typedef struct  tagDBCONTENTPROXIMITY    {
    DWORD dwProximityUnit;        // words, paras, chapters etc.
    ULONG ulProximityDistance;    // how near is "near"?
    LONG lWeight;                 // weight of the proximity node
}    DBCONTENTPROXIMITY;

// Proximity Units
#define PROXIMITY_UNIT_WORD         ( 0 )
#define PROXIMITY_UNIT_SENTENCE     ( 1 )
#define PROXIMITY_UNIT_PARAGRAPH    ( 2 )
#define PROXIMITY_UNIT_CHAPTER      ( 3 )

DBGROUPINFO Structure

This structure represents specific information required by grouping operators, i.e., project_list_ elements used for grouping.

typedef struct tagDBGROUPINFO {
    LCID        lcid;
} DBGROUPINFO ;

DBLIKE Structure

This structure represents specific information required by DBOP_like operator.

typedef struct  tagDBLIKE    {
    LONG lWeight;        // weight on the dbop_like node
    GUID guidDialect;    // system to use for "likeness". E.g. Reg. Ex.
}    DBLIKE;

DBPARAMETER Structure

This structure is used to define values for scalar parameters. Note that there is no entry for ordinal. The assumption is that the ordinal will be determined by the provider after evaluating the tree as a whole, and not by assigning a specific value to an individual member within the tree. Consumers can determine the ordinal based on the name using ICommandWithParameters::MapParameterNames.

typedef struct tagDBPARAMETER {
    LPWSTR            pwszName;        // parameter name
    ITypeInfo *        pTypeInfo;        // if not a null pointer, type is described
                                    // by the ITypeInfo
    DBNUMERIC *        pNum;            // Structure describing the
                                    // precision, scale and value of
                                    // the numeric value (See Appx A).
    ULONG            cbMaxLength;        // the maximum length of the
                                    // parameter
    DBPARAMFLAGS        dwFlags;            // bitmask describing parameter
                                    // characteristics
    DBTYPE            wType;            // type of the parameter
} DBPARAMETER;

DBPROPERTY Structure

This structure is used to define values for properties. For example, DDL commands will use it to define a default value for a column via the DB_PROPERTY_DEFAULT_VALUE property.

typedef struct tagDBPROPERTY {
    GUID       guid;      // the guid for the property
    VARIANT    vValue;    // the value of the property
} DBPROPERTY;

DBSETFUNC Structure

tyepdef struct tagDBSETFUNC {
    DWORD    dwSetQuantifier;
}  DBSETFUNC;

#define DBSETFUNC_NONE        = 0x0
#define DBSETFUNC_ALL         = 0x1
#define DBSETFUNC_DISTINCT    = 0x2

DBSORTINFO Structure

This structure stores the order in which a column will be sorted (i.e., ascending or descending). This information is stored inside a sort_list_element node.

typedef struct  tagDBSORTINFO {
    BOOL        fDesc;    // TRUE = asc, FALSE = desc
    LCID        lcid;
} DBSORTINFO;

DBTEXT Structure

This structure is used by the DBOP_text_command node. It stores the dialect that should be used to interpret the string stored in pwszText. The error locator is filled in by the provider, i.e., the first offending token is indicated as index into the text array, together with its length.

typedef struct tagDBTEXT {
    LPWSTR    pwszText;
    ULONG     ulErrorLocator; //set by validation routines
    ULONG     ulTokenLength;  //length of offending token
    GUID      guidDialect;
} DBTEXT;

Collection of GUIDs for the Command Text Node

DBGUID_SQL92
DBGUID_DBSQL       // This covers SQL used by OLE DB and ODBC.
DBGUID_TSQL
DBGUID_ACCESSSQL

To determine what dialects a provider supports, a consumer calls IDBInfo::GetPropertyInfo for the DBPROP_SQLDIALECTS property.

Command Node Example

The following is a sample helper function to allocate a new DBCOMMANDTREE structure.

DBCOMMANDTREE* PctAllocNode(
    DBOP op,
    DBVALUEKIND eKind,
    ULONG cbValueSize
    )
    {
    DBCOMMANDTREE* pct;
    //get the OLE IMalloc interface
    IMalloc* pim;
    HRESULT hr = CoGetMalloc(MEMCTX_TASK, &pim);
    if (FAILED(hr))
        return NULL;

    // Allocate fixed size part of the node
    pct = (DBCOMMANDTREE*)pim->Alloc((sizeof DBCOMMANDTREE));

    //initialize fields
    pct->op = op;
    pct->pctFirstChild = NULL;
    pct->pctNextSibling = NULL;
    pct->hrError = S_OK;
    pct->dwKind = eKind;

    // Allocate variable size part of the node
    if ( 0 != cbValueSize )
        pct.value.pvValue = (void *) pim->Alloc(cbValueSize);

    //Initialize value space
    memset(pct->value.pvValue, cbValueSize, 0);

    return pct;
    }

The following is sample code illustrating how to set up a condition subtree representing content search restriction. The condition searches for the ocurrence of the word "Database" within a column named Text.

DBCOMMANDTREE *pct1, pct2;
DBCONTENT* pdbc;

//Allocate content node
pct1 = PctAllocNode(DBOP_content, DBVALUEKIND_CONTENT, (sizeof DBCONTENT));

//set up value field
pdbc = (DBCONTENT*)(pct1->value.pvValue);
pdbc->dwGenerateMethod = GENERATE_METHOD_PREFIX;
pdbc->lWeight = 200;
pdbc->lcid = DEFAULT_LCID;
pdbc->pwszPhrase = L"Database";

//allocate column node
pct2 = PctAllocNode(DBOP_simple_name, DBVALUEKIND_WSTR,
    (sizeof LPWSTR));
pct2->value.pwszValue = L"Text";

//Make the column node a child of the Content node
pct1->pctFirstChild = pct2;

Collection of GUIDs for the "like" Operator

DBGUID_LIKE_SQL
DBGUID_LIKE_DOS
DBGUID_LIKE_OFS
DBGUID_LIKE_MAPI


Memory Allocation

Ownership of command trees may be transferred via several different methods in OLE-DB. A transfer of ownership occurs when a component that allocates a command tree is no longer responsible for freeing the allocated resources. For example, command trees may be obtained by consumers via, ICommand::GetCommandTree. In order for this transfer to be successful, a standard for allocation and deallocation must be identified.

In general, the standard OLE Task Allocator interface, obtained by calling CoGetMalloc should be used for allocation and release of the memory. DBCOMMANDTREE nodes and the values embedded therein are allocated using IMalloc::Alloc and released via IMalloc::Free, with the following exceptions:

  • BSTRs: SysAllocString, SysFreeString family of functions
  • OLE interfaces: IUnknown::AddRef, IUnknown::Release
  • SafeArrays: SafeArrayAlloc, SafeArrayDestroy family of functions

All other pointers embedded in a command tree are assumed to have been dynamically allocated with IMalloc::Alloc and will be released with IMalloc::Free.

When allocating a tree that will not change ownership, any method of allocation is acceptable except for the three exception cases listed above since those values also have special copy functions.

Providers should have separate allocations for the fixed (24 bytes) and the variable parts of the node to reduce memory fragmentation and to enable in place substitution of values in the node.


ICommandTree Interface

This section contains the reference material for the ICommandTree interface.


ICommandTree : ICommand

This interface is optional for providers that support commands. It contains methods for manipulating query trees. Providers that support Command Trees must also support specifying the same functionality through ICommandText.

A command object can have only one command; that command can be in the form of a command tree (specified in ICommandTree) or a text command (specified in ICommandText). Thus, if a command is specified through SetCommandTree or ICommandText::SetCommandText, it replaces the command object's command, regardless of whether that command was in text or tree form. If a command is retrieved through GetCommandTree or ICommandText::GetCommandText, it is retrieved in the specified form, regardless of how the command was set. Thus, GetCommandText must be able to convert a command tree into command text, and GetCommandTree must be able to convert command text into a command tree. Note that in the latter conversion, the provider should return a navigable command tree representation of the text, which is not necessarily in optimized form; if the provider cannot create a fuller representation, the command tree can consist of a single text node.

Most providers will not permit setting a new command tree while there is a rowset open that was created by the command object (and thus directly reflects the result table of the current command tree). Some providers, however, may support this operation even while a rowset is open. If so, the new command tree's output schema (set of columns) must include all column for which there currently are accessors, and all rowsets' accessors remain valid. Currently open rowsets must be modified dynamically to reflect the new command tree's result table. HROWs remain valid, meaning that a new sort order or a new selection predicate are not reflected for those rows, and that all accessors that may be created after the command tree modification will work with HROWs obtained before the command tree modification. If an error occurs while replacing or modifying a command tree with open rowsets, the command object, its command tree, the rowsets, HROWs and accessors remain unchanged.

Method | Description
FindErrorNodes |
FreeCommandTree |
GetCommandTree |
SetCommandTree |


ICommandTree::FindErrorNodes

Walks a command tree and returns an array of nodes with errors in them.

HRESULT FindErrorNodes(
  const DBCOMMANDTREE * pRoot,
  ULONG * pcErrorNodes,
  DBCOMMANDTREE ** prgErrorNodes
);

Parameters

pRoot
[in] Pointer to the root of the command tree.
pcErrorNodes
[out] Pointer to memory in which to return the number of nodes containing errors.
prgErrorNodes
[out] Pointer to memory in which to return an array of pointers to nodes that have errors in them. The command object allocates memory for this array and returns the address to this memory; the consumer releases this memory with IMalloc::Free when it no longer needs the array. If *pcErrorNodes is 0 on output, the provider does not allocate any memory and ensures that *prgErrorNodes is a null pointer on output.

Return Value

S_OK
The method succeeded.
E_FAIL
A provider-specific error occurred.
E_INVALIDARG
pRoots, pcErrorNodes, or prgErrorNodes was a null pointer.
E_OUTOFMEMORY
The provider was unable to allocate sufficient memory in which to return the array of pointers to nodes containing errors.


ICommandTree::FreeCommandTree

Walks a command tree and deallocates all DBCOMMANDTREE node structures as well as all variants in those structures, and finally sets the root pointer to a null pointer.

HRESULT FreeCommandTree(
  DBCOMMANDTREE ** ppRoot
);

Parameters

ppRoot
[in] Address of a variable that receives the pointer to the root of the command tree on successful exit.

Return Value

S_OK
The method succeeded.
E_FAIL
A provider-specific error occurred.
E_INVALIDARG
ppRoot was a null pointer.
DB_E_CANNOTFREE
The consumer called SetCommandTree with fCopy = FALSE, therefore relinquishing ownership of the memory to the provider.

Comments

FreeCommandTree can be used by a consumer to free their copy of the command tree constructed locally or obtained by GetCommandTree. It does not free the copy of the tree owned by the command object. When a consumer calls SetCommandTree with fCopy = FALSE, the consumer relinquishes ownership of the memory to the provider. Therefore, if the consumer calls FreeCommandTree after having called SetCommandTree with fCopy = FALSE, FreeCommandTree returns an error code DB_E_CANNOTFREE meaning the consumer does not have ownership of the tree to free it.


ICommandTree::GetCommandTree

Echoes the current command as a tree, including all post-processing operations added.

HRESULT GetCommandTree(
  DBCOMMANDTREE ** ppRoot
);

Parameters

ppRoot
[out] The command object allocates memory for the command tree and returns the address to this memory; the consumer releases this memory with IMalloc::Free, one node at a time, when it no longer needs the command tree. The provider sets ppRoot to a null pointer if an error occurs.

Return Value

S_OK
The method succeeded.
DB_S_ERRORSINTREE
The provider discovered non-fatal errors in the command text previously set by ICommandText::SetCommandText while building the command tree.
E_FAIL
A provider-specific error occurred.
E_INVALIDARG
ppRoot was a null pointer.
E_OUTOFMEMORY
The provider was unable to allocate sufficient memory in which to return the command tree.
DB_E_CANTTRANSLATE
The provider cannot represent the command text previously set by ICommandText::SetCommandText as a tree.

Comments

The returned tree reflects exactly the command set by the last invocation of ICommandTree::SetCommandTree or ICommandText::SetCommandText, as modified by subsequent calls to IQuery::AddPostProcessing. If the command is stored as a tree, the returned tree is a copy of the one stored in the command object. If a tree node was passed in with text, it is also echoed as text. If the command is stored as text, the provider should return a "navigable" command tree representation of the text, which is not necessarily in optimized form. If the provider cannot create a fuller representation, the command tree can consist of a single text node. For example, if the tree can be represented as a DBOP_SQL_select node, and the provider supports that node, it must be returned in that format. However, if the tree cannot be represented as a DBOP_SQL_select node, but can be represented in a non-trivial command tree (that is, other than the DBOP_text_command), the provider must return it as that non-trivial tree; the provider may only return the tree as the trivial DBOP_text_command node if that is the only command node it supports; otherwise, it must return a valid, non-trivial navigable tree or return DB_E_CANTTRANSLATE if the text cannot be represented in such a tree. The provider should not do any unnecessary validation, such as binding, at this time, but if in the course of parsing it discovers non-fatal errors in building the tree it should put the error information in the tree and return DB_S_ERRORSINTREE.

This method does not reveal a provider's internal, optimized translation (which may be different from a DBCOMMANDTREE structure) of text to (non-text) tree operations.

The returned tree includes the markings set by the validation methods invoked so far (ICommandValidate::ValidateSyntax and ICommandValidate::ValidateCompletely).


ICommandTree::SetCommandTree

Sets a command object's command tree, replacing the existing one or replacing a text command specified with ICommandText. The provided command tree is copied into the command object; thus, the consumer may delete the original tree or text without affecting the command object. Most error checking is deferred until one of the validation methods, optimization (see ICommandPrepare), or the ICommand::Execute method are invoked. This method only verifies that the command tree can indeed be copied into the command object's space.

HRESULT SetCommandTree(
  DBCOMMANDTREE ** ppRoot,
  DBCOMMANDREUSE dwCommandReuse,
  BOOL fCopy
);

Parameters

ppRoot
[in] The root of the command tree.
dwCommandReuse
[in] A bitmask that specifies whether state from the previous command is retained. If state that was not previously specified is marked for reuse, the flag is ignored and no error occurs. See ICommandText::SetCommandText for a description of these flags.
fCopy
[in] If TRUE, the command tree is copied, and the caller retains ownership of the command tree's memory. If FALSE, the provider takes the entire tree, without copying, and set the caller's root pointer to a null pointer. When the command object needs to deallocate the tree, it will call IMalloc::Free once for each node in the tree.

If fCopy is FALSE, the consumer must not change the command tree without another call to SetCommandTree. The affect of any such changes is undefined. In particular, the provider can assume that the command tree has not changed between the calls that use the tree, such as SetCommandTree, ICommandValidate::ValidateSyntax or ValidateCompletely, ICommandPrepare::Prepare, and ICommand::Execute.

Return Value

S_OK
The method succeeded.
E_FAIL
A provider-specific error occurred.
E_INVALIDARG
pRoot was a null pointer. dwCommandReuse was invalid.
DB_E_OBJECTOPEN
A rowset was open on the command object.

Comments

The following example shows how to build and set a simple command.

hr = pICreateCommand->CreateCommand(IID_ICommand,
                                    (IUnknown **) &pICommand);
pICreateCommand->Release();

// Build a command for the following query:
//    SELECT * FROM CUSTOMERS ORDER BY CITY
CreateSelectNode(&pctSelect);
CreateProjectListAnchor(&pctPLListAnchor);

pctSelectNode->pctFirsChild = pctPLListAnchor;

CreateProjectListNode("*", &pctPLListNode);
CreateFromListAnchor(&pctFListAnchor);
CreateFromListNode("CUSTOMERS", &pctFLNode);

pctPLListAnchor->pctFirstChild = pctPLListNode;
pctPLListAnchor->pctNextSibling = pctFListAnchor;

CreateSortListAnchorNode(&pctSLAnchor);
CreateSortListElementNode("ascending",&pctSLNode);
CreateScalarIdNode("CITY", &pctScalar);

pctFListAnchor->pctFirstChild = pctFLNode;
pctFListAnchor->pctNextSibling = pctSLAnchor;

pctSLAnchor->pctFirstChild = pctSLNode;
pctSLNode->pctFirstChild = pctScalar;

// Get  ICommandTree Interface
hr = pICommand->QueryInterface(IID_ICommandTree,
                               (IUnknown **) &pICommandTree);

// Set Command Tree in Command Object
pICommandTree->SetCommandTree(&pctSort, FALSE);
pICommandTree->Release();

The functions used to create the various nodes (e.g. CreateSelectNode) are simple wrappers that set values in the DBCOMMANDTREE structure; they are not part of the OLE DB specification. For example, the code for CreateFromListNode might be:

void CreateFromListNode(
   PWSTR           szFromList,
   DBCOMMANDTREE ** ppctFLNode);
{
   // Allocate DBCOMMANDTREE structure. Place pointer to it in
   // *ppctFLNode. Code not shown.

   *ppctFLNode->op             = DBOP_from_list;
   *ppctFLNode->hrOperatorOk   = S_OK;
   *ppctFLNode->hrContextOk    = S_OK;
   *ppctFLNode->pctFirstChild  = NULL;
   *ppctFLNode->pctNextSibling = NULL;
   *ppctFLNode->eKind          = DBVALUEKIND_NAME;
   *ppctFLNode->pwszName       = szFromList;
}


Topic Not Found

No matching topic was found. Some possible reasons and remedies are as follows:

  • The file (.chm) that contains the destination topic is not in the expected location.

Remedy: Rerun the MSDN Library Setup.

  • The file (.chm) that contains the current topic or destination topic is out-of-date and the link is no longer valid.

Remedy: Install the latest version of the MSDN Library.

  • The MSDN Library CD is not in the CD-ROM drive.

Remedy: Insert the MSDN Library CD into the CD-ROM drive.