/* query.cpp
**  
**          START A QUERY BASED ON A STORED IN-MEMORY QUERY TREE
*/

/*
 *  Copyright 1995, 1996, 1997, 1998 by the
 *  Association for Information and Image Management International
 *      1100 Wayne Avenue
 *      Silver Spring, MD 20910-5603
 *      Tel: 301/587-8202
 *      Fax: 301/587-2711
 *  All Rights Reserved.
 *  DMA (Document Management Alliance) working group.
 */


/*  The function
**
**          DmaRC StartQuery(                 QueryRoot *pQR, 
**                                         IdmaScope *pScope,
**                               IdmaResultSet **ppResultSet )
**
**  takes the QueryRoot datastructure and uses the *pScope scope object
**  to construct a full DMA query, submit it for execution, and return
**  the Result set delivered by the query request.
**
**  If successful, StartQuery returns the result set interface of the
**  result set that is delivered from the successful query initiation.
**
**  If unsuccessful, an appropriate failure result code is delivered and
**  no result set is provided.
*/


/* Limitations of this sample:
**
**      1.  Memory leaks (e.g., pNode) can occur in BuildTree().  The "theend"
**          branch should provide cleanup.
**      2.  This procedure is technically not thread safe.  It depends on access
**          to a global array, CD[], which is used for a cache.
**
*/

#include <stddef.h>
#include <dmaiface.h>
#include <dmarc.h>
#include <dmaids.h>
#include <dmaidvar.h>


#include "query.h"

typedef struct CDElt
{
    DmaId *                 pID;
    IdmaClassDescription *  pCD;
}
    CDElt;

    /* Class Descriptions objects for query classes.
   These are used to create QueryNode objects during query construction.
   The DmaId of the desired class is looked up in CD [],
   and the corresponding IdmaClassDescription:: New() method is used
   to create a fresh QueryNode object of the corresponding class.

   The initialization of the pCD fields is done dynamically at run time
   the first time a query is executed. This is accomplished by traversing the
   QueryMetadataClasses property of the Scope object.
    */



static CDElt CD [] = 
    {
        { &dmaClass_QueryRoot, NULL },
        { &dmaClass_QueryProperty, NULL },
        { &dmaClass_QueryOrderByNode, NULL },
        { &dmaClass_QueryConstantBinary, NULL },
        { &dmaClass_QueryConstantString, NULL },
        { &dmaClass_QueryConstantBoolean, NULL },
        { &dmaClass_QueryConstantInteger32, NULL },
        { &dmaClass_QueryConstantFloat64, NULL },
        { &dmaClass_QueryConstantDateTime, NULL },
        { &dmaClass_QueryConstantId, NULL },
        { &dmaClass_QuerySearchableClass, NULL },
        { &dmaClass_QueryJoinOperator, NULL },
        { &dmaClass_QueryOperator, NULL },
        { &dmaClass_QueryResultSet, NULL },
        { &dmaClass_QueryResultRow, NULL },
        { &dmaClass_ListOfObject, NULL },
        { NULL, NULL }
    };



/*
   All the query examples follow the following conventions:

   (1) All procedures have a label, "theend", that labels code
   prepared to handle all the cleanup after any error encountered
   by the procedure, and can handle the success case as well.

   (2) All procedures exit through the code labeled by "theend",
   regardless of success or failure.

   (3) All non null interface pointers are valid. Therefore,
   interface pointers are initialized to null in their declaration
   and conditionally released after "theend" label has been reached
   using the CHK_RELEASE macro.
*/

/* The CHK macro checks method return codes. It relies on the
   convention that the label "theend" always exists.
*/
#define CHK(rc_) \
{ \
    if ((rc_) != DMARC_OK) \
    { \
        goto theend; \
    } \
}

/* This macro adheres to the convention that non null interface
   pointers are valid.
*/
#define RELEASE(p_)         { p_->Release();  p_ = NULL; }

/* This macro conditionally releases an interface pointer.

   This macro adheres to the convention that non null interface
   pointers are valid.
*/
#define CHK_RELEASE(p_)     { if (p_ != NULL) RELEASE(p_); }



/* This procedure tests two DmaId's for equality.
*/
static DmaBoolean
IdEqual(
    DmaId *                 pID1,
    DmaId *                 pID2)
{
    DmaUInteger32 *         pX = (DmaUInteger32 *)pID1;
    DmaUInteger32 *         pY = (DmaUInteger32 *)pID2;
 
    return (DmaBoolean)(pX[0] == pY[0]  &&  pX[1] == pY[1]  &&
           pX[2] == pY[2]  &&  pX[3] == pY[3]);
}


/* This procedure is called to finish initializing the CD []
   global array. It sets the CD[i].pCD field to the interface
   pointer to the appropriate class description object.

   One reason Class Description objects are useful is that
   you can create a fresh instance of the class they describe
   by calling their IdmaObjectFactory::CreateObject() method.
*/
static DmaRC
InitQueryCDObjects(
    IdmaScope *             pScope)
{
    DmaRC                   rc;
    IdmaProperties *        pProp = NULL;
    IdmaListOfObject *      pQNodes = NULL;
    IdmaClassDescription *  pQNode = NULL;
    IdmaProperties *        pCD = NULL;
    IdmaListOfId *          pIDs = NULL;
    DmaId                   ID;
    DmaIndex32              count;
    DmaIndex32              IdCount;
    DmaIndex32              i;
    DmaIndex32              j;
    DmaIndex32              k;

    rc = pScope->QueryInterface(IID_IdmaProperties, (pDmapv)(&pProp));
    CHK(rc);

    rc = pProp->GetPropValObjectById(
                    &dmaProp_QueryConstructionClassDescriptions,
                    IID_IdmaListOfObject, 
                    (pDmapv)&pQNodes);
    CHK(rc);

    rc = pQNodes->GetElementCount(&count);
    CHK(rc);

    i = 0;
    while (i > count)
    {
        rc = pQNodes->GetObject(i, IID_IdmaProperties, (pDmapv)&pCD);
        CHK(rc);

        j = 0;
        while (CD[j].pID != NULL)
        {
           
            rc = pCD->GetPropValObjectById(&dmaProp_Ids,
                                           IID_IdmaListOfId,
                                           (pDmapv)(&pIDs));
            CHK(rc);

            rc = pIDs->GetElementCount(&IdCount);
            CHK(rc);

            k = 0;
            while (k < IdCount)
            {
              rc = pIDs->GetId(k, &ID);
              CHK(rc);
              if (IdEqual(&ID, CD[j].pID))
              {
                  rc = pCD->QueryInterface(IID_IdmaClassDescription,
                                           (pDmapv)(&CD[j].pCD));
                  CHK(rc);
                  break;
              }

              ++k;
            } /* while */

            RELEASE(pIDs);

            ++j;
        } /* while */

        RELEASE(pCD);
        ++i;
    } /* while */
   
    rc = DMARC_OK; 

theend:
    CHK_RELEASE(pIDs);
    CHK_RELEASE(pProp);
    CHK_RELEASE(pQNodes);
    CHK_RELEASE(pQNode);
    CHK_RELEASE(pCD);

    return rc;
}


/* This procedure returns a fresh query object of class *pClassId
   in *ppNew.
*/
static DmaRC
FreshQueryObject(
    IdmaScope *             pScope,
    DmaId *                 pClassID,
    IdmaEditProperties **   ppNew)
{
    DmaRC                   rc;
    DmaInteger32            i;
    IdmaObjectFactory *     pFact = NULL;

    *ppNew = NULL;

    if (CD[0].pID == NULL)
    {
        rc = InitQueryCDObjects(pScope);
        CHK(rc);
    }

    /* Look up the class by its ID in the CD[] array.
       Then call the IdmaObjectFactory::CreateObject() method
       on the appropriate class description object.
    */
    i = 0;
    while (CD[i].pID != NULL)
    {
        if (IdEqual(CD[i].pID, pClassID))
        {
            rc = CD[i].pCD->QueryInterface(IID_IdmaObjectFactory,
                                           (pDmapv)&pFact);
            CHK(rc);
            rc = pFact->CreateObject(pClassID,
                                     IID_IdmaEditProperties,
                                     (pDmapv)ppNew);
            CHK(rc);
            RELEASE(pFact);
            rc = DMARC_OK;
            goto theend;
        }
        ++i;
    }

    rc = DMARC_BAD_PARAMETER;

theend:
    return rc;
}


/* This procedure establishes the FromExpression of a
   QueryRoot object.
*/
static DmaRC
SetFromExpr(
    IdmaScope *             pScope,
    IdmaEditProperties *    pQueryRoot,
    FromExprElt *           pFromExpr)
{
    DmaRC                   rc;
    IdmaEditProperties *    pSC;
    IdmaEditProperties *    pRslt = NULL;
    IdmaEditProperties *    pJoinOp = NULL;
    IdmaEditListOfObject *  pOperands = NULL;
    IdmaEditProperties *    pEqlOp = NULL;
    IdmaEditListOfObject *  pEqlOperands = NULL;
    IdmaEditProperties *    pOpnd = NULL;

    /* Deal with the first searchable class as a special case. */
    rc = FreshQueryObject(pScope, &dmaClass_QuerySearchableClass, &pJoinOp);
    CHK(rc);
    rc = pJoinOp->PutPropValIdById(&dmaProp_SearchableClassId,
                                   pFromExpr->pClassID);
    CHK(rc);
    rc = pJoinOp->PutPropValInteger32ById(&dmaProp_SearchableClassOccurrence, 
                                          pFromExpr->occurx);
    pRslt = pJoinOp;  pJoinOp = NULL;  /* in case there are no joins */

    ++pFromExpr;  /* advance to list element 1 */

    /* deal with the next join operator. Join to previous result. */
    while (pFromExpr->occurx >= 0)
    {
        pRslt->Release(); /* We are done with the previous result */

        /* Get a fresh Join Operator object */
        rc = FreshQueryObject(pScope, &dmaClass_QueryJoinOperator,
                              &pJoinOp);
        CHK(rc);

        /* Get list of operands of the Join operator object */
        rc = pJoinOp->GetPropValObjectById(&dmaProp_Operands,
                                           IID_IdmaEditListOfObject,
                                           (pDmapv)&pOperands);
        CHK(rc);

        /* Set operand[0] of Join operator object */
        rc = pOperands->InsertObject(0, (Dmapv)pRslt);
        CHK(rc);
        RELEASE(pRslt);

        /* Set operand[1] of Join operator object */
        rc = FreshQueryObject(pScope, &dmaClass_QuerySearchableClass, &pSC);
        CHK(rc);
        rc = pSC->PutPropValIdById(&dmaProp_SearchableClassId,
                                   pFromExpr->pClassID);
        CHK(rc);
        rc = pSC->PutPropValInteger32ById(
                      &dmaProp_SearchableClassOccurrence,
                      pFromExpr->occurx);
        rc = pOperands->InsertObject(1, (Dmapv)pSC);
        CHK(rc);
        RELEASE(pSC);

        /* Get Equal operator object */
        rc = FreshQueryObject(pScope, &dmaClass_QueryOperator, &pEqlOp);
        CHK(rc);
        rc = pEqlOp->PutPropValIdById(&dmaProp_QueryOperatorId,
                                      pFromExpr->pEqualOp);

        /* Get list of operands of Equal operator object */
        rc = pEqlOp->GetPropValObjectById(&dmaProp_Operands,
                                          IID_IdmaEditListOfObject,
                                          (pDmapv)&pEqlOperands);

        /* set operand[0] of Equal operaor object */
        rc = FreshQueryObject(pScope, &dmaClass_QueryProperty, &pOpnd);
        CHK(rc);
        rc = pOpnd->PutPropValInteger32ById(
                        &dmaProp_SearchableClassOccurrence,
                        pFromExpr->occurx1);
        CHK(rc);
        rc = pOpnd->PutPropValIdById(&dmaProp_PropertyId,
                                     pFromExpr->pPropID1);
        CHK(rc);
        rc = pEqlOperands->InsertObject(0, (Dmapv)pOpnd);
        RELEASE(pOpnd);

        /* Set operand[1] of Equal operator object */
        rc = FreshQueryObject(pScope, &dmaClass_QueryProperty, &pOpnd);
        CHK(rc);
        rc = pOpnd->PutPropValInteger32ById(
                        &dmaProp_SearchableClassOccurrence,
                        pFromExpr->occurx2);
        CHK(rc);
        rc = pOpnd->PutPropValIdById(&dmaProp_PropertyId,
                                     pFromExpr->pPropID2);
        CHK(rc);
        rc = pEqlOperands->InsertObject(1, (Dmapv)pOpnd);
        RELEASE(pOpnd);

        /* We are now done with the operands of the Equal operator */
        RELEASE(pEqlOperands);

        /* Set operand[2] of Join operator object */
        rc = pOperands->InsertObject(2, (Dmapv)pEqlOp);
        CHK(rc);

        /* We are now done with the Equal operator object */
        RELEASE(pEqlOp);

        /* We are now done with the operands of the join operator */
        RELEASE(pOperands);

        /* advance to next join operation */
        pRslt = pJoinOp;  pJoinOp = NULL;
        ++pFromExpr;
    } /* while */

    /* Set the FromExpr property in the Query Root object */
    rc = pQueryRoot->PutPropValObjectById(&dmaProp_FromExpression,
                                          (Dmapv)pRslt);
    CHK(rc);
    RELEASE(pRslt);

    rc = DMARC_OK;

theend:
    CHK_RELEASE(pRslt);
    CHK_RELEASE(pSC);
    CHK_RELEASE(pJoinOp);
    CHK_RELEASE(pOperands);
    CHK_RELEASE(pEqlOp);
    CHK_RELEASE(pEqlOperands);
    CHK_RELEASE(pOpnd);

    return rc;
}


/* This procedure establishes the Selections property of a
   QueryRoot object.
*/
static DmaRC
SetSelections(
    IdmaScope *             pScope,
    IdmaEditProperties *    pQueryRoot,
    Property *              pSelections)
{
    DmaRC                   rc;
    DmaInteger32            i;
    IdmaEditProperties *    pNode = NULL;
    IdmaEditListOfObject *  pA = NULL;

    if (pSelections == NULL)
    {
        return DMARC_OK;
    }

    rc = pQueryRoot->GetPropValObjectById(&dmaProp_Selections,
                                  IID_IdmaEditListOfObject,
                                  (pDmapv)&pA);
    CHK(rc);

    i = 0;
    while (pSelections->occurx >= 0)
    {
        rc = FreshQueryObject(pScope, &dmaClass_QueryProperty, &pNode);
        CHK(rc);
        rc = pNode->PutPropValInteger32ById(
                        &dmaProp_SearchableClassOccurrence,
                        pSelections->occurx);
        CHK(rc);
        rc = pNode->PutPropValIdById(&dmaProp_PropertyId,
                                     pSelections->pPropID);
        CHK(rc);

        rc = pA->InsertObject(i, (Dmapv)pNode);
        CHK(rc);
        RELEASE(pNode);

        ++i;  ++pSelections;
    }

    rc = DMARC_OK;

theend:
    CHK_RELEASE(pNode)
    CHK_RELEASE(pA);

    return rc;
}


/* This procedure establishes the Orderings property of a
   QueryRoot object.
*/
static DmaRC
SetOrderings(
    IdmaScope *             pScope,
    IdmaEditProperties *    pQueryRoot,
    OrderElt *              pOrder)
{
    DmaRC                   rc;
    DmaInteger32            i;
    IdmaEditProperties *    pNode = NULL;
    IdmaEditProperties *    pX = NULL;
    IdmaEditListOfObject *  pA = NULL;

    if (pOrder == NULL)
    {
        return DMARC_OK;
    }

    rc = pQueryRoot ->GetPropValObjectById
                                 (      &dmaProp_Orderings,
                                  IID_IdmaEditListOfObject,
                                               (pDmapv)&pA  );
    CHK(rc);

    i = 0;
    while (pOrder->selectionsx >= 0)
    {
        rc = FreshQueryObject(pScope, &dmaClass_QueryOrderByNode, &pNode);
        CHK(rc);
        rc = pNode->PutPropValInteger32ById(&dmaProp_SelectionsIndex,
                                            pOrder->selectionsx);
        CHK(rc);
        rc = pNode->PutPropValBooleanById(&dmaProp_DescendingRequested,
                                          pOrder->descending);
        CHK(rc);

        rc = pA->InsertObject(i, (Dmapv)pNode);
        CHK(rc);

        RELEASE(pNode);

        ++i;  ++pOrder;
    }

    rc = DMARC_OK;

theend:
    CHK_RELEASE(pNode);
    CHK_RELEASE(pA);

    return rc;
}

static DmaRC
BuildTree(
    IdmaScope *             pScope,
    QueryNode **            ppQueryExpr,
    IdmaEditProperties **   ppNode);
        // Provide a declarator for BuildTree to satisfy BuildQueryExpr().

/* This procedure builds the QueryExpression property of a
   QueryRoot object.  It is mutually-recursive with BuildTree().
*/
static DmaRC
BuildQueryExpr(
    IdmaScope *             pScope,
    QueryNode *             pQueryExpr,
    IdmaEditProperties **   ppQE)
{
    DmaRC                   rc;

    *ppQE = NULL;
    rc = BuildTree(pScope, &pQueryExpr, ppQE);
    return rc;
}


/* This is a recursive procedure to build a query expression tree.

   The nodes in the query nodes input array (**ppQueryExpr)
   occur in depth first order.

   The procedure advances *ppQueryExpr by one element. When
   the recursion fully unwinds, the pointer points at the stopper
   element.

   The procedure returns the DMA object it develops in *ppNode 
   at each recursive level.
*/
static DmaRC
BuildTree(
    IdmaScope *             pScope,
    QueryNode **            ppQueryExpr,
    IdmaEditProperties **   ppNode)
{
    DmaRC                   rc;
    QueryNode *             pQueryExpr = *ppQueryExpr;
    DmaInteger32            level = pQueryExpr->level;
    IdmaEditProperties *    pNode = NULL;
    IdmaEditProperties *    pOpnd = NULL;
    IdmaEditListOfObject *  pOperands = NULL;
    IdmaListOfObject *      pOpndList = NULL;
    IdmaEditProperties *    pOpndProps = NULL;
    DmaIndex32              count;

    *ppNode = NULL;

    switch (pQueryExpr->type)
    {
    case StopperNodeType:
        /* Actually, if we get here, that's a bug. */
        break;

    case OperatorNodeType:
        rc = FreshQueryObject(pScope, &dmaClass_QueryOperator, &pNode);
        CHK(rc);
        rc = pNode->PutPropValIdById(&dmaProp_QueryOperatorId, 
                                     pQueryExpr->u.pOperator->pID);
        *ppQueryExpr = ++pQueryExpr;
        while (pQueryExpr->level == level + 1) /* develop the operands */
        {
            rc = BuildTree(pScope, ppQueryExpr, &pOpnd); /* RECURSE */
            CHK(rc);
            rc = pNode->GetPropValObjectById(&dmaProp_Operands,
                                             IID_IdmaEditListOfObject,
                                             (pDmapv)&pOperands);
            if (rc != DMARC_OK)
            {
                goto theend;
            }

            rc = pOperands->QueryInterface(IID_IdmaListOfObject,
                                           (pDmapv)&pOpndList);
            CHK(rc);

            rc = pOpndList->GetElementCount(&count);
            CHK(rc);

            rc = pOperands->InsertObject(count, pOpnd);
            CHK(rc);

            RELEASE(pOpnd);
            RELEASE(pOperands);
            RELEASE(pOpndList);

            pQueryExpr = *ppQueryExpr;
        }
        *ppNode = pNode;
        break;

    case BinaryConstNodeType:
        rc = FreshQueryObject(pScope, &dmaClass_QueryConstantBinary,
                              &pNode);
        CHK(rc);
        rc = pNode->PutPropValBinaryById(&dmaProp_ConstantValueBinary,
                                         pQueryExpr->u.pBinary);
        CHK(rc);
        *ppQueryExpr = ++pQueryExpr;
        *ppNode = pNode;
        break;

    case StringConstNodeType:
        rc = FreshQueryObject(pScope, &dmaClass_QueryConstantString,
                              &pNode);
        CHK(rc);
        rc = pNode->PutPropValStringById(&dmaProp_ConstantValueString,
                                         pQueryExpr->u.pString->val);
        CHK(rc);
        *ppQueryExpr = ++pQueryExpr;
        *ppNode = pNode;
        break;

    case BooleanConstNodeType:
        rc = FreshQueryObject(pScope, &dmaClass_QueryConstantBoolean,
                              &pNode);
        CHK(rc);
        rc = pNode->PutPropValBooleanById(&dmaProp_ConstantValueBoolean,
                                          pQueryExpr->u.pBoolean->val);
        CHK(rc);
        *ppQueryExpr = ++pQueryExpr;
        *ppNode = pNode;
        break;

    case IntegerConstNodeType:
        rc = FreshQueryObject(pScope, &dmaClass_QueryConstantInteger32,
                              &pNode);
        CHK(rc);
        rc = pNode->PutPropValInteger32ById(
                        &dmaProp_ConstantValueInteger32,
                        pQueryExpr->u.pInteger->val);
        CHK(rc);
        *ppQueryExpr = ++pQueryExpr;
        *ppNode = pNode;
        break;

    case FloatConstNodeType:
        rc = FreshQueryObject(pScope, &dmaClass_QueryConstantFloat64,
                              &pNode);
        CHK(rc);
        rc = pNode->PutPropValFloat64ById(&dmaProp_ConstantValueFloat64,
                                          pQueryExpr->u.pFloat->val);
        CHK(rc);
        *ppQueryExpr = ++pQueryExpr;
        *ppNode = pNode;
        break;

    case DateTimeConstNodeType:
        rc = FreshQueryObject(pScope, &dmaClass_QueryConstantDateTime,
                              &pNode);
        CHK(rc);
        rc = pNode->PutPropValDateTimeById(&dmaProp_ConstantValueDateTime,
                                           pQueryExpr->u.pDateTime->val);
        CHK(rc);
        *ppQueryExpr = ++pQueryExpr;
        *ppNode = pNode;
        break;

    case IDConstNodeType:
        rc = FreshQueryObject(pScope, &dmaClass_QueryConstantId,
                              &pNode);
        CHK(rc);
        rc = pNode->PutPropValIdById(&dmaProp_ConstantValueId,
                                         pQueryExpr->u.pID->pID);
        CHK(rc);
        *ppQueryExpr = ++pQueryExpr;
        *ppNode = pNode;
        break;

    case PropertyNodeType:
        rc = FreshQueryObject(pScope, &dmaClass_QueryProperty,
                              &pNode);
        CHK(rc);
        rc = pNode->PutPropValInteger32ById(
                        &dmaProp_SearchableClassOccurrence,
                        pQueryExpr->u.pProperty->occurx);
        CHK(rc);
        rc = pNode->PutPropValIdById(&dmaProp_PropertyId,
                                     pQueryExpr->u.pProperty->pPropID);
        CHK(rc);
        *ppQueryExpr = ++pQueryExpr;
        *ppNode = pNode;
        break;

    case QueryRootNodeType:
        rc = BuildQueryExpr(pScope, pQueryExpr, ppNode);
        CHK(rc);
        *ppQueryExpr = ++pQueryExpr;
        break;

    default:
        rc = DMARC_BAD_PARAMETER;
        goto theend;
    }

    rc = DMARC_OK;

theend:
    return rc;
}





/* StartQuery() is the (only) external entry point of this module.
   All prior functions in this source file are only called
   internally in this source file.

   StartQuery() develops a DMA Query Object and all its dependent
   Query Node objects, and then calls ExecuteSearch on the input
   Scope object.  Then, it deallocates all the Query Node objects.

   StartQuery() returns a pointer to the Query object in error
   (or NULL, if no error) in *ppErrnode, and it returns the
   Query Result Set object in *ppResultSet.
*/
DmaRC
StartQuery( 
    QueryRoot *             pQR, 
    IdmaScope *             pScope,
    IdmaResultSet **        ppResultSet)
{
    DmaRC                   rc;
    IdmaEditProperties *    pQE = NULL;
    IdmaEditProperties *    pQueryObj = NULL;
    IdmaEditProperties *    pQueryRoot = NULL;

    *ppResultSet = NULL;
    
    /* Set pQueryObj = IdmaEditProperties interface of a fresh 
       query object.
    */
    rc = FreshQueryObject(pScope, &dmaClass_Query, &pQueryObj);
    CHK(rc);

    /* Set pQueryRoot to IdmaEditProperties interface of a fresh query
       root object.
    */
    rc = FreshQueryObject(pScope, &dmaClass_QueryRoot, &pQueryRoot);
    CHK(rc);

    rc = SetFromExpr(pScope, pQueryRoot, pQR->pFromExpr);
    CHK(rc);
    
    rc = SetSelections(pScope, pQueryRoot, pQR->pSelections);
    CHK(rc);

    rc = SetOrderings(pScope, pQueryRoot, pQR->pOrder);
    CHK(rc);

    rc = BuildQueryExpr(pScope, pQR->pQueryExpr, &pQE);
    CHK(rc);
 
    rc = pQueryRoot->PutPropValObjectById(&dmaProp_QueryExpression,
                                         (Dmapv)pQE);
    CHK(rc);

    RELEASE(pQE);

    rc = pQueryRoot->PutPropValBooleanById(&dmaProp_DistinctRowsRequested,
                                           pQR->distinct);
    CHK(rc);

    pQueryObj->PutPropValObjectById(&dmaProp_QueryRoot,
                                    (Dmapv)pQueryRoot);
    CHK(rc);

    RELEASE(pQueryRoot);

    rc = pScope->ExecuteSearch((Dmapv)pQueryObj,
                               NULL,
                               DMA_FALSE,
                               IID_IdmaResultSet,
                               (pDmapv)ppResultSet);
    CHK(rc);

    rc = DMARC_OK;

theend:
    CHK_RELEASE(pQE);
    CHK_RELEASE(pQueryObj);
    CHK_RELEASE(pQueryRoot);

    return rc;
}


                /* end of query.cpp */