(file) Return to CQLSelectStatementRep.cpp CVS log (file) (dir) Up to [Pegasus] / pegasus / src / Pegasus / CQL

File: [Pegasus] / pegasus / src / Pegasus / CQL / CQLSelectStatementRep.cpp (download)
Revision: 1.16, Tue May 2 19:26:58 2006 UTC (18 years, 2 months ago) by a.dunfey
Branch: MAIN
CVS Tags: TASK_BUG_5314_IPC_REFACTORING_ROOT, TASK_BUG_5314_IPC_REFACTORING_BRANCH, TASK_BUG_5314_IPC_REFACTORING-V1, TASK_BUG_5191_QUEUE_CONSOLIDATION_ROOT, TASK_BUG_5191_QUEUE_CONSOLIDATION_BRANCH, TASK-TASK-BUG4011_WinLocalConnect-branch-New-root, TASK-TASK-BUG4011_WinLocalConnect-branch-New-merged_out_to_branch, TASK-TASK-BUG4011_WinLocalConnect-branch-New-merged_out_from_trunk, TASK-TASK-BUG4011_WinLocalConnect-branch-New-merged_in_to_trunk, TASK-TASK-BUG4011_WinLocalConnect-branch-New-merged_in_from_branch, TASK-TASK-BUG4011_WinLocalConnect-branch-New-branch, TASK-PEP268_SSLClientCertificatePropagation-root, TASK-PEP268_SSLClientCertificatePropagation-merged_out_to_branch, TASK-PEP268_SSLClientCertificatePropagation-merged_out_from_trunk, TASK-PEP268_SSLClientCertificatePropagation-merged_in_to_trunk, TASK-PEP268_SSLClientCertificatePropagation-merged_in_from_branch, TASK-PEP268_SSLClientCertificatePropagation-branch, TASK-PEP267_SLPReregistrationSupport-root, TASK-PEP267_SLPReregistrationSupport-merging_out_to_branch, TASK-PEP267_SLPReregistrationSupport-merging_out_from_trunk, TASK-PEP267_SLPReregistrationSupport-merged_out_to_branch, TASK-PEP267_SLPReregistrationSupport-merged_out_from_trunk, TASK-PEP267_SLPReregistrationSupport-merged_in_to_trunk, TASK-PEP267_SLPReregistrationSupport-merged_in_from_branch, TASK-PEP267_SLPReregistrationSupport-branch, TASK-Bugs5690_3913_RemoteCMPI-root, TASK-Bugs5690_3913_RemoteCMPI-merged_out_to_branch, TASK-Bugs5690_3913_RemoteCMPI-merged_out_from_trunk, TASK-Bugs5690_3913_RemoteCMPI-merged_in_to_trunk, TASK-Bugs5690_3913_RemoteCMPI-merged_in_from_branch, TASK-Bugs5690_3913_RemoteCMPI-branch, TASK-Bug2102_RCMPIWindows-root, TASK-Bug2102_RCMPIWindows-merged_out_to_branch, TASK-Bug2102_RCMPIWindows-merged_out_from_trunk, TASK-Bug2102_RCMPIWindows-merged_in_to_trunk, TASK-Bug2102_RCMPIWindows-merged_in_from_branch, TASK-Bug2102_RCMPIWindows-branch, TASK-Bug2021_RemoteCMPIonWindows-root, TASK-Bug2021_RemoteCMPIonWindows-merged_out_to_branch, TASK-Bug2021_RemoteCMPIonWindows-merged_out_from_trunk, TASK-Bug2021_RemoteCMPIonWindows-merged_in_to_trunk, TASK-Bug2021_RemoteCMPIonWindows-merged_in_from_branch, TASK-Bug2021_RemoteCMPIonWindows-branch, TASK-Bug2021_RCMPIonWindows-root, TASK-Bug2021_RCMPIonWindows-merged_out_to_branch, TASK-Bug2021_RCMPIonWindows-merged_out_from_trunk, TASK-Bug2021_RCMPIonWindows-merged_in_to_trunk, TASK-Bug2021_RCMPIonWindows-merged_in_from_branch, TASK-Bug2021_RCMPIonWindows-branch, TASK-BUG4011_WinLocalConnect-root, TASK-BUG4011_WinLocalConnect-merged_out_to_branch, TASK-BUG4011_WinLocalConnect-merged_out_from_trunk, TASK-BUG4011_WinLocalConnect-merged_in_to_trunk, TASK-BUG4011_WinLocalConnect-merged_in_from_branch, TASK-BUG4011_WinLocalConnect-branch-New, TASK-BUG4011_WinLocalConnect-branch, RELEASE_2_6_0-RC1, RELEASE_2_6_0-FC, RELEASE_2_6_0, RELEASE_2_6-root, PEP286_PRIVILEGE_SEPARATION_ROOT, PEP286_PRIVILEGE_SEPARATION_CODE_FREEZE, PEP286_PRIVILEGE_SEPARATION_BRANCH, PEP286_PRIVILEGE_SEPARATION_1
Branch point for: RELEASE_2_6-branch
Changes since 1.15: +38 -3 lines
BUG#: 4996
TITLE: PEP 233 Embedded Instance Support

DESCRIPTION:

Modifications to add support for the embedded instance type in Pegasus 2.6. Currently the functionality is off by default and must be enabled by defining at build time the variable PEGASUS_EMBEDDED_INSTANCE_SUPPORT with a value of "true".

//%2006////////////////////////////////////////////////////////////////////////
//
// Copyright (c) 2000, 2001, 2002 BMC Software; Hewlett-Packard Development
// Company, L.P.; IBM Corp.; The Open Group; Tivoli Systems.
// Copyright (c) 2003 BMC Software; Hewlett-Packard Development Company, L.P.;
// IBM Corp.; EMC Corporation, The Open Group.
// Copyright (c) 2004 BMC Software; Hewlett-Packard Development Company, L.P.;
// IBM Corp.; EMC Corporation; VERITAS Software Corporation; The Open Group.
// Copyright (c) 2005 Hewlett-Packard Development Company, L.P.; IBM Corp.;
// EMC Corporation; VERITAS Software Corporation; The Open Group.
// Copyright (c) 2006 Hewlett-Packard Development Company, L.P.; IBM Corp.;
// EMC Corporation; Symantec Corporation; The Open Group.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to
// deal in the Software without restriction, including without limitation the
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
// sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
// 
// THE ABOVE COPYRIGHT NOTICE AND THIS PERMISSION NOTICE SHALL BE INCLUDED IN
// ALL COPIES OR SUBSTANTIAL PORTIONS OF THE SOFTWARE. THE SOFTWARE IS PROVIDED
// "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT
// LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
// ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
//
//==============================================================================
//
// Authors: David Rosckes (rosckes@us.ibm.com)
//          Bert Rivero (hurivero@us.ibm.com)
//          Chuck Carmack (carmack@us.ibm.com)
//          Brian Lucier (lucier@us.ibm.com)
//
// Modified By: David Dillard, VERITAS Software Corp.
//                  (david.dillard@veritas.com)
//
//%/////////////////////////////////////////////////////////////////////////////

#include "CQLSelectStatement.h"
#include "CQLSelectStatementRep.h"

#include <Pegasus/Common/CIMValue.h>
#include <Pegasus/Common/CIMInstance.h>
#include <Pegasus/Common/CIMProperty.h>
#include <Pegasus/Query/QueryCommon/QueryException.h>
#include <Pegasus/Query/QueryCommon/QueryIdentifier.h>
#include <Pegasus/Query/QueryCommon/QueryChainedIdentifier.h>
#include <Pegasus/Common/Tracer.h>
#include <Pegasus/Common/InternalException.h>
#include <Pegasus/Common/CIMStatusCode.h>
#include <Pegasus/Common/AutoPtr.h>
#include "CQLValue.h"
#include "CQLIdentifier.h"
#include "CQLChainedIdentifier.h"
#include "Cql2Dnf.h"

// ATTN: TODOs -
// optimize
// documentation

PEGASUS_NAMESPACE_BEGIN

struct PropertyNode
{
  CIMName name;              // property name
  CIMName scope;             // class the property is on
  Boolean wildcard;          // true if this property is wildcarded
  Boolean endpoint;          // true if this property is an endpoint
                             // of a chained identifier
  AutoPtr<PropertyNode> sibling;
  AutoPtr<PropertyNode> firstChild;

  PropertyNode()
    : wildcard(false),
      endpoint(false),
      sibling(NULL),
      firstChild(NULL)
  {}

  ~PropertyNode() {}
};


CQLSelectStatementRep::CQLSelectStatementRep()
  :SelectStatementRep(),
   _hasWhereClause(false),
   _contextApplied(false)
{
  PEG_METHOD_ENTER (TRC_CQL, "CQLSelectStatementRep()");
  PEG_METHOD_EXIT();
}

CQLSelectStatementRep::CQLSelectStatementRep(String& inQlang,
                                             String& inQuery,
                                             QueryContext& inCtx)
  :SelectStatementRep(inQlang, inQuery, inCtx),
   _hasWhereClause(false),
   _contextApplied(false)
{
  PEG_METHOD_ENTER (TRC_CQL, "CQLSelectStatementRep(inQlang,inQuery,inCtx)");
  PEG_METHOD_EXIT();
}

CQLSelectStatementRep::CQLSelectStatementRep(String& inQlang,
                                             String& inQuery)

  :SelectStatementRep(inQlang, inQuery),
   _hasWhereClause(false),
   _contextApplied(false)
{
  PEG_METHOD_ENTER (TRC_CQL, "CQLSelectStatementRep(inQlang,inQuery)");
  PEG_METHOD_EXIT();
}

CQLSelectStatementRep::CQLSelectStatementRep(const CQLSelectStatementRep& rep)
  :SelectStatementRep(rep),
   _selectIdentifiers(rep._selectIdentifiers),
   _hasWhereClause(rep._hasWhereClause),
   _predicate(rep._predicate),
   _contextApplied(rep._contextApplied)
{
  PEG_METHOD_ENTER (TRC_CQL, "CQLSelectStatementRep(rep)");
  PEG_METHOD_EXIT();
}

CQLSelectStatementRep::~CQLSelectStatementRep()
{
  PEG_METHOD_ENTER (TRC_CQL, "~CQLSelectStatementRep()");
  PEG_METHOD_EXIT();
}

CQLSelectStatementRep& CQLSelectStatementRep::operator=(const CQLSelectStatementRep& rhs)
{
  PEG_METHOD_ENTER (TRC_CQL, "CQLSelectStatementRep::operator=");

  if (this ==  &rhs)
  {
    PEG_METHOD_EXIT();
    return *this;
  }

  SelectStatementRep::operator=(rhs);

  _selectIdentifiers = rhs._selectIdentifiers;
  _predicate = rhs._predicate;
  _contextApplied = rhs._contextApplied;
  _hasWhereClause = rhs._hasWhereClause;

  PEG_METHOD_EXIT();
  return *this;
}

Boolean CQLSelectStatementRep::evaluate(const CIMInstance& inCI)
{
  PEG_METHOD_ENTER (TRC_CQL, "CQLSelectStatementRep::evaluate");

  if(_ctx == NULL)
  {
    PEG_TRACE_STRING (TRC_CQL, Tracer::LEVEL4,"QC not set");
    PEG_METHOD_EXIT();
    MessageLoaderParms parms("CQL.CQLSelectStatementRep.QUERY_CONTEXT_IS_NULL",
                             "Trying to process a query with a NULL Query Context.");
    throw CQLRuntimeException(parms);
  }

  // Apply to class contexts to the identifiers.
  // This will check for a well-formed statement.
  if (!_contextApplied)
    applyContext();

  // Make sure the type of instance passed in is the FROM class,
  // or a subclass of the FROM class.
  if (!isFromChild(inCI.getClassName()))
  {
    PEG_METHOD_EXIT();
    return false;
  }

  if (!hasWhereClause())
  {
    PEG_METHOD_EXIT();
    return true;
  }
  else
  {
    try
    {
      PEG_METHOD_EXIT();
      return _predicate.evaluate(inCI, *_ctx);
    }
    catch (CQLNullContagionException& )
    {
      // The null contagion rule.
      PEG_TRACE_STRING (TRC_CQL, Tracer::LEVEL4,"null contagion");
      PEG_METHOD_EXIT();
      return false;
    }
  }

  PEGASUS_UNREACHABLE( PEGASUS_ASSERT(false); )
  PEGASUS_UNREACHABLE( PEG_TRACE_STRING (TRC_CQL, Tracer::LEVEL4,"should not get here in evaluate"); )
  PEGASUS_UNREACHABLE( return true; ) //should never get here
}

void CQLSelectStatementRep::applyProjection(CIMInstance& inCI,
    Boolean allowMissing)
{
  PEG_METHOD_ENTER (TRC_CQL, "CQLSelectStatementRep::applyProjection(inCI)");

  if(_ctx == NULL)
  {
    PEG_TRACE_STRING (TRC_CQL, Tracer::LEVEL4,"QC not set");
    PEG_METHOD_EXIT();
    MessageLoaderParms parms("CQL.CQLSelectStatementRep.QUERY_CONTEXT_IS_NULL",
                             "Trying to process a query with a NULL Query Context.");
    throw CQLRuntimeException(parms);
  }

  // Apply to class contexts to the identifiers.
  // This will check for a well-formed statement.
  if (!_contextApplied)
    applyContext();

  //
  // Build a tree to represent the projected properties from the select list
  // of chained identifiers.  This is needed because embedded instances below
  // the FROM class form a tree structure when projected.
  //
  // The design of the tree is to gather all the required properties for
  // an instance at a node as child nodes.  The root node
  // of the tree represents the instance passed in to this function.  Below the
  // root there can be nodes that are required embedded instance properties.
  // The child nodes of these embedded instance nodes represent the required
  // properties on the embedded instance (which may themselves be embedded instances).
  //
  // Each node has a name, which is in 2 parts -- the property name and the
  // scope (ie. the class the property is on).  This allows the scoping
  // operator to be handled correctly, so that the parent instance can be
  // checked to see if it is the right class to provide the property.
  // Note that the scoping is a base class; ie. the parent instance of a node
  // may be a subclass of the scope.
  //

  // Set up the root node of the tree.  This represents the instance
  // passed in.
  AutoPtr<PropertyNode> rootNode(new PropertyNode);
  Array<QueryIdentifier> fromList = _ctx->getFromList();
  rootNode->name = fromList[0].getName();  // not doing joins
  rootNode->scope = fromList[0].getName(); // not used on root, just to fill in the var
  rootNode->wildcard = false;

  // Build the tree below the root.
  for (Uint32 i = 0; i < _selectIdentifiers.size(); i++)
  {
    PEG_TRACE_STRING (TRC_CQL, Tracer::LEVEL4,"select chained id = " +
                      _selectIdentifiers[i].toString());

    // Get the chain elements
    Array<CQLIdentifier> ids = _selectIdentifiers[i].getSubIdentifiers();

    PEGASUS_ASSERT(ids.size() > 1);

    PropertyNode * curNode = rootNode.get();
    PropertyNode * curChild = curNode->firstChild.get();

    // Loop through the identifiers in the chain.
    // NOTE: this starts at the position *after* the FROM class
    // So, the loop index is always one position after the current node,
    // ie. it will become a child node of the current node.
    for (Uint32 j = 1; j < ids.size(); j++)
    {
      PEG_TRACE_STRING (TRC_CQL, Tracer::LEVEL4,"curNode = " + curNode->name.getString());
      PEG_TRACE_STRING (TRC_CQL, Tracer::LEVEL4,"id = " + ids[j].toString());

      // If the child is wildcarded, then every property exposed by the
      // class of the instance at the current node is required.
      // Mark the current node as wildcarded.
      if (ids[j].isWildcard())
      {
        PEG_TRACE_STRING (TRC_CQL, Tracer::LEVEL4,"id is wildcard");
        curNode->wildcard = true;
        break;
      }

      // Determine if this identifier is already a child node of
      // the current node.
      Boolean found = false;
      while (curChild != NULL && !found)
      {
        // The scoping class is either the scope of the identifier
        // or the FROM class if the identifier is not scoped.
        String scope = fromList[0].getName().getString();
        if (ids[j].isScoped())
        {
          scope = ids[j].getScope();
        }

        PEG_TRACE_STRING (TRC_CQL, Tracer::LEVEL4,"scope to compare = " + scope);

        if (curChild->name == ids[j].getName() &&
            String::equalNoCase(curChild->scope.getString(), scope))
        {
          // Name and scope match.  The identifier is already child node.
          PEG_TRACE_STRING (TRC_CQL, Tracer::LEVEL4,"id is already a child node");
          found = true;
        }
        else
        {
          curChild = curChild->sibling.get();
        }
      }

      if (!found)
      {
        // The identifier is not already a child node.
        // Create a node and add it as a child to the current node.
        PEG_TRACE_STRING (TRC_CQL, Tracer::LEVEL4,"new child" +
                          ids[j].getName().getString());
        curChild = new PropertyNode;
        curChild->sibling.reset(curNode->firstChild.release());
        curChild->name = ids[j].getName();
        curChild->wildcard = false;
        curChild->endpoint = false;
        curNode->firstChild.reset(curChild);  // safer than using the = operator
      }

      // Set the scope for the child node
      if (ids[j].isScoped())
      {
        // Child node has a scoping class
        PEGASUS_ASSERT(ids[j].getScope().size() > 0);
        PEG_TRACE_STRING (TRC_CQL, Tracer::LEVEL4,"child set with scoping class: " +
                          ids[j].getScope());
        curChild->scope =  CIMName(ids[j].getScope());
      }
      else
      {
        // Not scoped.  The scope is the FROM class.
        PEGASUS_ASSERT(j == 1);
        PEGASUS_ASSERT(fromList[0].getName().getString().size() > 0);
        PEG_TRACE_STRING (TRC_CQL, Tracer::LEVEL4,"child set with scoping class: " +
                          fromList[0].getName().getString());
        curChild->scope = fromList[0].getName();
      }

      // If the identifier is the last element of the chain,
      // then mark it as an endpoint
      if ((ids.size() - 1) == j)
      {
        curChild->endpoint = true;
      }

      curNode = curChild;
      curChild = curNode->firstChild.get();
    }
  }

  //
  // Do the projection.
  //

  Array<CIMName> requiredProps;
  Boolean allPropsRequired = rootNode->wildcard;

  // Loop through the children of the root node.
  // The root node represents the FROM class,
  // and the child nodes are the required properties on the FROM class.
  PropertyNode* childNode = rootNode->firstChild.get();
  while (childNode != NULL)
  {
    PEG_TRACE_STRING (TRC_CQL, Tracer::LEVEL4,"project childNode = " + childNode->name.getString());

    // Determine if the instance passed in meets the class scoping
    // rules for the current child node.
    Boolean filterable = isFilterable(inCI, childNode);

    // Indicates if the child node is still required after the recursive call.
    Boolean childRequired = true;

    // If the instance is filterable, and the child node has children,
    // or is wildcarded, then the child is assumed to be an embedded instance,
    // and we need to recurse to apply the projection on the embedded instance.
    // (the check for embedded instance is done in the recursive call)
    if (filterable &&
        (childNode->firstChild.get() != NULL || childNode->wildcard))
    {
      // We need to project on an embedded instance property. The steps are to
      // remove the embedded instance property from the instance passed in,
      // project on that embedded instance property, and then add the projected
      // embedded instance property back to the instance passed in.
      PEG_TRACE_STRING (TRC_CQL, Tracer::LEVEL4,"about to recurse: " + childNode->name.getString());
      Uint32 index = inCI.findProperty(childNode->name);
      if (index != PEG_NOT_FOUND)
      {
        // The instance passed in has the required embedded instance property.
        // Note: embedded instance property missing is caught below.

        // Remove the embedded instance property
        CIMProperty childProp = inCI.getProperty(index);
        inCI.removeProperty(index);

        // Project onto the embedded instance property.
        // If the last parameter is true, then the childNode
        // will not remove properties when filtering the embedded instance.
        // This call returns whether the embedded instance property
        // should be added to the required property list.
        childRequired = applyProjection(childNode, childProp, allPropsRequired,
            allowMissing);
        inCI.addProperty(childProp);
      }
    }

    // If the instance passed in is filterable,
    // then add the current child to the list if it is still required.
    if (filterable && childRequired)
    {
      PEG_TRACE_STRING (TRC_CQL, Tracer::LEVEL4,"add req prop: " + childNode->name.getString());
      requiredProps.append(childNode->name);
    }

    childNode = childNode->sibling.get();
  }

  // Remove the properties that are not in the projection.
  // This also checks for missing required properties.
  Boolean preserve = false;
  filterInstance(inCI,
                 allPropsRequired,
                 fromList[0].getName(),
                 requiredProps,
                 preserve,
                 allowMissing);
}

Boolean CQLSelectStatementRep::applyProjection(PropertyNode* node,
                                               CIMProperty& nodeProp,
                                               Boolean& preservePropsForParent,
                                               Boolean allowMissing)
{
  PEG_METHOD_ENTER (TRC_CQL, "CQLSelectStatementRep::applyProjection(node, nodeProp)");

  PEGASUS_ASSERT(node->firstChild.get() != NULL || node->wildcard);

  //
  // The property passed in must be an embedded instance. It is not
  // allowed to project properties on embedded classes.
  // Get the embedded instance from the property.
  //

  // First check that it is an embedded object
  CIMValue nodeVal = nodeProp.getValue();
  CIMType nodeValType = nodeVal.getType();
  if (nodeValType != CIMTYPE_OBJECT
#ifdef PEGASUS_EMBEDDED_INSTANCE_SUPPORT
    && nodeValType != CIMTYPE_INSTANCE
#endif // PEGASUS_EMBEDDED_INSTANCE_SUPPORT
    )
  {
    PEG_TRACE_STRING (TRC_CQL, Tracer::LEVEL4,"not emb");
    PEG_METHOD_EXIT();
    MessageLoaderParms parms("CQL.CQLSelectStatementRep.PROP_NOT_EMB",
                             "The property $0 must contain an embedded object.",
                             nodeProp.getName().getString());
    throw CQLRuntimeException(parms);
  }

  if (nodeVal.isNull())
  {
    // Since we will be projecting on the embedded object, it cannot be null
    PEG_TRACE_STRING (TRC_CQL, Tracer::LEVEL4,"value is null");
    PEG_METHOD_EXIT();
    MessageLoaderParms parms("CQL.CQLSelectStatementRep.NULL_EMB_OBJ",
                             "The embedded object property $0 cannot contain a null value.",
                             nodeProp.getName().getString());
    throw CQLRuntimeException(parms);
  }

  if (nodeVal.isArray() &&
      (node->firstChild.get() != NULL || node->wildcard))
  {
    // NOTE - since we are blocking projection of array elements, we can
    // assume that if we get here we were told to project a whole array (ie. no index used), as
    // an embedded object with properties or wildcard.
    // Examples not allowed:  SELECT fromClass.someArrayProp.scope::notAllowedProp FROM fromClass
    //                        SELECT fromClass.someArrayProp.* FROM fromClass
    PEG_TRACE_STRING (TRC_CQL, Tracer::LEVEL4,"array index needed");
    PEG_METHOD_EXIT();
    MessageLoaderParms parms("CQL.CQLSelectStatementRep.PROJ_WHOLE_ARRAY",
                  "CQL requires that array indexing is used on embedded object property $0.",
                   nodeProp.getName().getString());
    throw CQLRuntimeException(parms);
  }

  CIMObject nodeObj;  // this starts uninitialized
  CIMInstance nodeInst;
#ifdef PEGASUS_EMBEDDED_INSTANCE_SUPPORT
  if(nodeValType == CIMTYPE_OBJECT)
  {
#endif // PEGASUS_EMBEDDED_INSTANCE_SUPPORT
  nodeVal.get(nodeObj);
  if (nodeObj.isUninitialized())
  {
    // Not allowed to project on an uninitialized object
    PEG_TRACE_STRING (TRC_CQL, Tracer::LEVEL4,"is uninitialized");
    PEG_METHOD_EXIT();
    MessageLoaderParms parms("CQL.CQLSelectStatementRep.PROJ_UNINIT",
                             "The embedded object property $0 is uninitialized.",
                             nodeProp.getName().getString());
    throw CQLRuntimeException(parms);
  }

  if (!nodeObj.isInstance())
  {
    // Not allowed to project on a Class
    PEG_TRACE_STRING (TRC_CQL, Tracer::LEVEL4,"is a class");
    PEG_METHOD_EXIT();
    MessageLoaderParms parms("CQL.CQLSelectStatementRep.PROJ_CLASS",
                             "CQL does not allow properties to be projected on class $0.",
                             nodeProp.getName().getString());
    throw CQLRuntimeException(parms);
  }
  nodeInst = CIMInstance(nodeObj);
#ifdef PEGASUS_EMBEDDED_INSTANCE_SUPPORT
  }

  else if(nodeValType == CIMTYPE_INSTANCE)
  {
    nodeVal.get(nodeInst);
    if (nodeInst.isUninitialized())
    {
      // Not allowed to project on an uninitialized object
      PEG_TRACE_STRING (TRC_CQL, Tracer::LEVEL4,"is uninitialized");
      PEG_METHOD_EXIT();
      MessageLoaderParms parms("CQL.CQLSelectStatementRep.PROJ_UNINIT",
                              "The embedded object property $0 is uninitialized.",
                              nodeProp.getName().getString());
      throw CQLRuntimeException(parms);
    }
  }
#endif // PEGASUS_EMBEDDED_INSTANCE_SUPPORT
  //
  // Do the projection.
  //

  Array<CIMName> requiredProps;
  Boolean allPropsRequired = node->wildcard;

  // Loop through the children of the node.
  // The node represents an embedded instance,
  // and the child nodes are the required properties on the embedded instance.
  PropertyNode * curChild = node->firstChild.get();
  while (curChild != NULL)
  {
    PEG_TRACE_STRING (TRC_CQL, Tracer::LEVEL4,"project childNode = " + curChild->name.getString());

    // Determine if the embedded instance meets the class scoping
    // rules for the current child node
    Boolean filterable = isFilterable(nodeInst, curChild);

    // Indicates if the child node is still required after the recursive call.
    Boolean childRequired = true;

    // If the embedded instance is filterable, and the child node has children,
    // or is wildcarded, then the child is assumed to be an embedded instance,
    // and we need to recurse to apply the projection on the embedded instance.
    // (the check for embedded instance is done in the recursive call)
    if (filterable &&
        (curChild->firstChild.get() != NULL || curChild->wildcard))
    {
      // We need to project on an embedded instance property. The steps are to
      // remove the embedded instance property from the current instance,
      // project on that embedded instance property, and then add the projected
      // embedded instance property back to the current instance.
      PEG_TRACE_STRING (TRC_CQL, Tracer::LEVEL4,"about to recurse: " + curChild->name.getString());
      Uint32 index = nodeInst.findProperty(curChild->name);
      if (index != PEG_NOT_FOUND)
      {
        // The current instance has the required embedded instance property.
        // Note: embedded instance property missing is caught below.

        // Remove the embedded instance property
        CIMProperty childProp = nodeInst.getProperty(index);
        nodeInst.removeProperty(index);

        // Project onto the embedded instance property.
        // If the last parameter is true, then the childNode
        // will not remove properties when filtering the embedded instance.
        // This call returns whether the embedded instance property
        // should be added to the required property list.
        Boolean preserve =
          node->endpoint || allPropsRequired || preservePropsForParent;
        childRequired = applyProjection(curChild, childProp, preserve, 
            allowMissing);
        nodeInst.addProperty(childProp);
      }
    }

    // If the embedded instance is filterable,
    // then add the current child to the list if it is still required.
    if (filterable && childRequired)
    {
      // The instance is filterable, add the property to the required list.
      PEG_TRACE_STRING (TRC_CQL, Tracer::LEVEL4,"add req prop: " + curChild->name.getString());
      requiredProps.append(curChild->name);
    }

    curChild = curChild->sibling.get();
  }

  // Filter the instance.
  // This removes unneeded properties, unless this
  // embedded instance node was an endpoint (last element)
  // in a chained identifier.
  // This also checks for missing required properties on the instance.
  Boolean preserveProps = node->endpoint || preservePropsForParent;
  filterInstance(nodeInst,
                 allPropsRequired,
                 nodeInst.getClassName(),
                 requiredProps,
                 preserveProps,
                 allowMissing);

  // Put the projected instance back into the property.
#ifdef PEGASUS_EMBEDDED_INSTANCE_SUPPORT
  if(nodeValType == CIMTYPE_INSTANCE)
  {
    nodeProp.setValue(nodeInst);
  }
  else
#endif
  {
  CIMObject newNodeObj(nodeInst);
  CIMValue newNodeVal(newNodeObj);
  nodeProp.setValue(newNodeVal);
  }

  // Indicate to the caller whether the projected instance
  // is still a required property.  It is required if it is an endpoint
  // (ie. the last element of a chained identifier)
  // OR if it still has properties after being projected
  if (node->endpoint || nodeInst.getPropertyCount() > 0)
  {
    return true;
  }

  return false;
}

Boolean CQLSelectStatementRep::isFilterable(const  CIMInstance& inst,
                                            PropertyNode* node)
{
  PEG_METHOD_ENTER (TRC_CQL, "CQLSelectStatementRep::isFilterable");
  PEG_TRACE_STRING (TRC_CQL, Tracer::LEVEL4,"instance = " + inst.getClassName().getString());
  PEG_TRACE_STRING (TRC_CQL, Tracer::LEVEL4,"scope = " + node->scope.getString());

  //
  // Determine if an instance is filterable for a scoped property (ie. its
  // type is the scoping class or a subclass of the scoping class where the
  // property exists)
  //
  // Note that an instance that is unfilterable is not considered
  // an error.  In CQL, an instance that is not of the required scope
  // would cause a NULL to be placed in the result set column for the
  // property. However since we are not implementing result set in stage1,
  // just skip the property.  This can lead to an instance having
  // NO required properties even though it is derived from the FROM class.
  // This can easily happen if the scoping operator is used.
  //

  Boolean filterable = false;
  if (inst.getClassName() == node->scope)
  {
    // The instance's class is the same as the required scope
    PEG_TRACE_STRING (TRC_CQL, Tracer::LEVEL4,"instance matches scope");
    filterable = true;
  }
  else
  {
    try
    {
      if (_ctx->isSubClass(node->scope, inst.getClassName()))
      {
        // The instance's class is a subclass of the required scope.
        PEG_TRACE_STRING (TRC_CQL, Tracer::LEVEL4,"instance is subclass of scope");
        filterable = true;
      }
    }
    catch (const CIMException& ce)
    {
      if (ce.getCode() == CIM_ERR_INVALID_CLASS ||
          ce.getCode() == CIM_ERR_NOT_FOUND)
      {
        // The scoping class was not found in the schema.
        // Just swallow this error because according to the
        // spec we should be putting NULL in the result column,
        // which means skipping the property on the instance.
        PEG_TRACE_STRING (TRC_CQL, Tracer::LEVEL4,"scope class not in schema");
      }
      else
      {
        PEG_METHOD_EXIT();
        throw;
      }
    }
  }

  PEG_METHOD_EXIT();
  return filterable;
}

void CQLSelectStatementRep::filterInstance(CIMInstance& inst,
                                           Boolean& allPropsRequired,
                                           const CIMName& allPropsClass,
                                           Array<CIMName>& requiredProps,
                                           Boolean& preserveProps,
    Boolean allowMissing)
{
  PEG_METHOD_ENTER (TRC_CQL, "CQLSelectStatementRep::filterInstance");
  PEG_TRACE_STRING (TRC_CQL, Tracer::LEVEL4,"instance = " + inst.getClassName().getString());
  PEG_TRACE_STRING (TRC_CQL, Tracer::LEVEL4,"allPropsClass = " + allPropsClass.getString());

  // Implementation note:
  // Scoping operator before a wildcard is not allowed:
  // Example:
  // SELECT fromclass.embobj1.scope1::* FROM fromclass
  //
  // However, the following are allowed:
  // SELECT fromclass.embobj1.* FROM fromclass
  // (this means that all the properties on the class of instance embobj1
  //  are required)
  //
  // SELECT fromclass.* FROM fromclass
  // (this means that all the properties on class fromclass are required
  //  to be on the instance being projected, not including any
  //  properties on a subclass of fromclass)

  // If all properties are required (ie. wildcarded), then add
  // all the properties of allPropsClass to the required list.
  // The allPropsClass is either the FROM class or the class of an embedded instance.
  if (allPropsRequired)
  {
    PEG_TRACE_STRING (TRC_CQL, Tracer::LEVEL4,"all props required");
    CIMClass cls = _ctx->getClass(allPropsClass);
    Array<CIMName> clsProps;
    for (Uint32 i = 0; i < cls.getPropertyCount(); i++)
    {
      if (!containsProperty(cls.getProperty(i).getName(), requiredProps))
      {
        requiredProps.append(cls.getProperty(i).getName());
      }
    }
  }

  // Find out what properties are on the instance.
  Array<CIMName> supportedProps;
  for (Uint32 i = 0; i < inst.getPropertyCount(); i++)
  {
    supportedProps.append(inst.getProperty(i).getName());
  }

  // Check that all required properties are on the instance.
  if (!allowMissing)
  {
      for (Uint32 i = 0; i < requiredProps.size(); i++)
      {
          if (!containsProperty(requiredProps[i], supportedProps))
          {
              PEG_TRACE_STRING (TRC_CQL, Tracer::LEVEL4,"missing:" + 
                  requiredProps[i].getString());
              PEG_METHOD_EXIT();
              MessageLoaderParms parms
                  ("CQL.CQLSelectStatementRep.PROJ_MISSING_PROP",
                  "The property $0 is missing on the instance of class $1.",
                  requiredProps[i].getString(),
                  inst.getClassName().getString());
              throw QueryRuntimePropertyException(parms);
          }
      }
  }

  // If requested, remove the properties on the instance that are not required.
  if (!preserveProps)
  {
    for (Uint32 i = 0; i < supportedProps.size(); i++)
    {
      if (!containsProperty(supportedProps[i], requiredProps))
      {
        Uint32 index = inst.findProperty(supportedProps[i]);
        PEGASUS_ASSERT(index != PEG_NOT_FOUND);
        PEG_TRACE_STRING (TRC_CQL, Tracer::LEVEL4,"removing:" + supportedProps[i].getString());
        inst.removeProperty(index);
      }
    }
  }

  PEG_METHOD_EXIT();
}

//
// Validates that all the chained identifiers in the statement meet
// the rules in the CQL spec vs.the class definitions in the repository
//
void CQLSelectStatementRep::validate()
{
  PEG_METHOD_ENTER (TRC_CQL, "CQLSelectStatementRep::validate");

  if(_ctx == NULL)
  {
    PEG_TRACE_STRING (TRC_CQL, Tracer::LEVEL4,"null QC");
    PEG_METHOD_EXIT();
    MessageLoaderParms parms("CQL.CQLSelectStatementRep.QUERY_CONTEXT_IS_NULL",
                             "Trying to process a query with a NULL Query Context.");
    throw QueryValidationException(parms);
  }

  if (!_contextApplied)
    applyContext();

  for (Uint32 i = 0; i < _selectIdentifiers.size(); i++)
  {
    validateProperty(_selectIdentifiers[i]);
  }

  Array<QueryChainedIdentifier> _whereIdentifiers = _ctx->getWhereList();
  for (Uint32 i = 0; i < _whereIdentifiers.size(); i++)
  {
    validateProperty(_whereIdentifiers[i]);
  }

  PEG_METHOD_EXIT();
}

//
// Validates that the chained identifier meets all the rules in the CQL
// spec vs.the class definitions in the repository
//
void CQLSelectStatementRep::validateProperty(QueryChainedIdentifier& chainId)
{
  PEG_METHOD_ENTER (TRC_CQL, "CQLSelectStatementRep::validateProperty");

  // Note: applyContext has been called beforehand

  Array<QueryIdentifier> ids = chainId.getSubIdentifiers();

  Uint32 startingPos = 0;
  CIMName curContext;
  for (Uint32 pos = startingPos; pos < ids.size(); pos++)
  {
    // Determine the current class context
    if (ids[pos].isScoped())
    {
      // The chain element is scoped.  Use the scoping
      // class as the current context.  Note: this depends
      // on applyContext resolving the class aliases before we get here.
      curContext = CIMName(ids[pos].getScope());
    }
    else
    {
      // The chain element is not scoped.  Assume that we are
      // before a position that is required to be scoped.
      // (applyContext validates that the chained identifier
      // has scoped identifiers in the required positions).
      // The current context is the name at the first position,
      // which must be a classname (ie. right side of ISA where
      // the only element in the chain is a classname, or
      // cases where the FROM class is the first, and maybe only,
      // element of the chain).
      PEGASUS_ASSERT((pos < startingPos + 2) || ids[pos].isWildcard());
      curContext = ids[0].getName();
    }

    PEG_TRACE_STRING (TRC_CQL, Tracer::LEVEL4,"current context: " +
                       curContext.getString());

    // Get the class definition of the current class context
    // Note: keep this code here so that class existence is always
    // checked.  Eg. SELECT * FROM fromClass
    CIMClass classDef;
    try
    {
      classDef = _ctx->getClass(curContext);
    }
    catch (const CIMException& ce)
    {
      PEG_TRACE_STRING (TRC_CQL, Tracer::LEVEL4,"repository error");
      PEG_METHOD_EXIT();
      if (ce.getCode() == CIM_ERR_NOT_FOUND ||
          ce.getCode() == CIM_ERR_INVALID_CLASS)
      {
        MessageLoaderParms parms("CQL.CQLSelectStatementRep.VAL_CLASS_NOT_EXIST",
                               "The class $0 does not exist.",
                                curContext.getString());
        throw QueryValidationException(parms);
      }
      else
      {
        throw;
      }
    }

    // Now do the checks for properties existing on the current class context
    // and the class relationship rules in section 5.4.1.
    // Only do these checks if the chain id has a property.
    if (pos > startingPos)
    {
      if (ids[pos].isWildcard())
      {
        // The wildcard is at the end (verified by applyContext), so
        // no checking is required at this position.
        continue;
      }

      // Determine if the property name at the current position
      // exists on the current class context.
      Uint32 propertyIndex = classDef.findProperty(ids[pos].getName());
      if (propertyIndex == PEG_NOT_FOUND)
      {
        PEG_TRACE_STRING (TRC_CQL, Tracer::LEVEL4,"prop not on context " +
                          ids[pos].getName().getString());
        PEG_METHOD_EXIT();
        MessageLoaderParms parms("CQL.CQLSelectStatementRep.VAL_PROP_NOT_ON_CLASS",
                               "The property $0 does not exist on class $1.",
                                ids[pos].getName().getString(), classDef.getClassName().getString());
        throw QueryMissingPropertyException(parms);
      }

      // Checking class relationship rules in section 5.4.1.
      // For validateProperties, this only applies to the first
      // property in the chain.  This is because once we get into
      // embedded properties we don't know what the class will be
      // until we have an instance.
      if ((pos == (startingPos+1)) && !curContext.equal(ids[0].getName()))
      {
        // Its the first property, and the class context is not the FROM class.
        // Check the class relationship between the scoping class and the FROM class.
        if (_ctx->getClassRelation(ids[0].getName(), curContext) == QueryContext::NOTRELATED)
        {
          PEG_TRACE_STRING (TRC_CQL, Tracer::LEVEL4,"scope violation for:"+
                            ids[0].getName().getString());
          PEG_METHOD_EXIT();
          MessageLoaderParms parms("CQL.CQLSelectStatementRep.VAL_SCOPE_VIOLATION",
                               "The class $0 is not a superclass, subclass, or the same class as $1.",
                                curContext.getString(), ids[0].getName().getString());
          throw QueryValidationException(parms);
        }
      }

      // If the current position implies an embedded object, then
      // verify that the property is an embedded object
      if ((pos > startingPos) && (pos < (ids.size() - 1)))
      {
        CIMProperty embObj = classDef.getProperty(propertyIndex);
        CIMName qual("EmbeddedObject");
        if (embObj.findQualifier(qual) == PEG_NOT_FOUND)
        {
          PEG_TRACE_STRING (TRC_CQL, Tracer::LEVEL4,"prop not emb " +
                            embObj.getName().getString());
          PEG_METHOD_EXIT();
          MessageLoaderParms parms("CQL.CQLSelectStatementRep.PROP_NOT_EMB",
                             "The property $0 must be an embedded object.",
                             embObj.getName().getString());
          throw QueryValidationException(parms);
        }
      }
    }
  }

  PEG_METHOD_EXIT();
}

CIMName CQLSelectStatementRep::lookupFromClass(const String&  lookup)
{
  PEG_METHOD_ENTER (TRC_CQL, "CQLSelectStatementRep::lookupFromClass");

  QueryIdentifier id = _ctx->findClass(lookup);

  PEG_METHOD_EXIT();

  return id.getName();
}

Array<CIMObjectPath> CQLSelectStatementRep::getClassPathList()
{
  PEG_METHOD_ENTER (TRC_CQL, "CQLSelectStatementRep::getClassPathList");

  if(_ctx == NULL){
    PEG_TRACE_STRING (TRC_CQL, Tracer::LEVEL4,"null QC");
    PEG_METHOD_EXIT();
    MessageLoaderParms parms("CQL.CQLSelectStatementRep.QUERY_CONTEXT_IS_NULL",
                             "Trying to process a query with a NULL Query Context.");
    throw CQLRuntimeException(parms);
  }

  Array<QueryIdentifier> ids = _ctx->getFromList();
  PEGASUS_ASSERT(ids.size() == 1);  // no joins yet

  // No wbem-uri support yet.
  CIMObjectPath path(String::EMPTY, _ctx->getNamespace(), ids[0].getName());

  Array<CIMObjectPath> paths;
  paths.append(path);

  PEG_METHOD_EXIT();

  return paths;

}

CIMPropertyList CQLSelectStatementRep::getPropertyList(const CIMObjectPath& inClassName)
{
  PEG_METHOD_ENTER (TRC_CQL, "CQLSelectStatementRep::getPropertyList");
  try
  {
    return getPropertyListInternal(inClassName, true, true);
  }
  catch (const CIMException& ce)
  {
    PEG_TRACE_STRING (TRC_CQL, Tracer::LEVEL4,"cim exception");
    PEG_METHOD_EXIT();
    if (ce.getCode() == CIM_ERR_NOT_FOUND ||
        ce.getCode() == CIM_ERR_INVALID_CLASS)
    {
      MessageLoaderParms parms("CQL.CQLSelectStatementRep.GPL_CLASS_NOT_EXIST",
                               "A class required to determine the property list was not found.");
      throw CQLRuntimeException(parms);
    }
    else
    {
      throw;
    }
  }
}

CIMPropertyList CQLSelectStatementRep::getSelectPropertyList(const CIMObjectPath& inClassName)
{
  PEG_METHOD_ENTER (TRC_CQL, "CQLSelectStatementRep::getSelectPropertyList");
  try
  {
    return getPropertyListInternal(inClassName, true, false);
  }
  catch (const CIMException& ce)
  {
    PEG_TRACE_STRING (TRC_CQL, Tracer::LEVEL4,"cim exception");
    PEG_METHOD_EXIT();
    if (ce.getCode() == CIM_ERR_NOT_FOUND ||
        ce.getCode() == CIM_ERR_INVALID_CLASS)
    {
      MessageLoaderParms parms("CQL.CQLSelectStatementRep.GPL_CLASS_NOT_EXIST",
                               "A class required to determine the property list was not found.");
      throw CQLRuntimeException(parms);
    }
    else
    {
      throw;
    }
  }
}

CIMPropertyList CQLSelectStatementRep::getWherePropertyList(const CIMObjectPath& inClassName)
{
  PEG_METHOD_ENTER (TRC_CQL, "CQLSelectStatementRep::getWherePropertyList");
  try
  {
    return getPropertyListInternal(inClassName, false, true);
  }
  catch (const CIMException& ce)
  {
    PEG_TRACE_STRING (TRC_CQL, Tracer::LEVEL4,"cim exception");
    PEG_METHOD_EXIT();
    if (ce.getCode() == CIM_ERR_NOT_FOUND ||
        ce.getCode() == CIM_ERR_INVALID_CLASS)
    {
      MessageLoaderParms parms("CQL.CQLSelectStatementRep.GPL_CLASS_NOT_EXIST",
                               "A class required to determine the property list was not found.");
      throw CQLRuntimeException(parms);
    }
    else
    {
      throw;
    }
  }
}

CIMPropertyList CQLSelectStatementRep::getPropertyListInternal(const CIMObjectPath& inClassName,
                                                               Boolean includeSelect,
                                                               Boolean includeWhere)
{
  PEG_METHOD_ENTER (TRC_CQL, "CQLSelectStatementRep::getPropertyListInternal");

  if(_ctx == NULL)
  {
    PEG_TRACE_STRING (TRC_CQL, Tracer::LEVEL4,"null QC");
    PEG_METHOD_EXIT();
    MessageLoaderParms parms("CQL.CQLSelectStatementRep.QUERY_CONTEXT_IS_NULL",
                             "Trying to process a query with a NULL Query Context.");
    throw CQLRuntimeException(parms);
  }

  if (!_contextApplied)
    applyContext();

  // Get the FROM class.
  CIMName fromClass = _ctx->getFromList()[0].getName();

  // Get the classname passed in.  Note: since wbem-uri is not supported yet,
  // only use the classname part of the path.
  CIMName className = inClassName.getClassName();
  if (className.isNull())
  {
    // If the caller passed in an empty className, then the
    // FROM class is to be used.
    className = fromClass;
  }
  else
  {
    // The caller passed in some class name.  Verify that it is the FROM
    // class or a subclass of the FROM class.
    if(!(className == fromClass))
    {
      // Check if subclass of the FROM class
      if(!_ctx->isSubClass(fromClass, className))
      {
        MessageLoaderParms parms("CQL.CQLSelectStatementRep.CLASS_NOT_FROM_LIST_CLASS",
                    "Class $0 does not match the FROM class or any of its subclasses.",
                    className.getString());
        throw CQLRuntimeException(parms);
      }
    }
  }

  Boolean isWildcard;
  Array<CIMName> reqProps;
  Array<CIMName> matchedScopes;
  Array<CIMName> unmatchedScopes;

  // Add required properties from the select list.
  if (includeSelect)
  {
    for (Uint32 i = 0; i < _selectIdentifiers.size(); i++)
    {
      isWildcard = addRequiredProperty(reqProps,
                                       className,
                                       _selectIdentifiers[i],
                                       matchedScopes,
                                       unmatchedScopes);

      if (isWildcard)
      {
        // This indicates that a wildcard was found,
        // and the class passed in was the FROM class.
        // Return null property list to indicate all properties required.
        return CIMPropertyList();
      }
    }
  }

  // Add required properties from the WHERE clause.
  if (includeWhere)
  {
    Array<QueryChainedIdentifier> _whereIdentifiers = _ctx->getWhereList();
    for (Uint32 i = 0; i < _whereIdentifiers.size(); i++)
    {
      isWildcard = addRequiredProperty(reqProps,
                                       className,
                                       _whereIdentifiers[i],
                                       matchedScopes,
                                       unmatchedScopes);

      // Wildcards are not allowed in the WHERE clause
      PEGASUS_ASSERT(!isWildcard);
    }
  }

  // Check if every property on the class is required.
  CIMClass theClass = _ctx->getClass(className);
  Uint32 propCnt = theClass.getPropertyCount();
  Boolean allProps = true;
  for (Uint32 i = 0; i < propCnt; i++)
  {
    if (!containsProperty(theClass.getProperty(i).getName(), reqProps))
    {
      allProps = false;
      break;
    }
  }

  if (allProps)
  {
    // Return null property list to indicate all properties are required.
    PEG_TRACE_STRING (TRC_CQL, Tracer::LEVEL4,"all props req");
    PEG_METHOD_EXIT();
    return CIMPropertyList();
  }
  else
  {
    // Return the required property list.  Note that it is possible to return
    // an empty list in the case of no required properties for the classname
    // passed in.  This can happen when the scoping operator is used.
    PEG_METHOD_EXIT();
    return CIMPropertyList(reqProps);
  }
}

Boolean CQLSelectStatementRep::addRequiredProperty(Array<CIMName>& reqProps,
                                                   CIMName& className,
                                                   QueryChainedIdentifier& chainId,
                                                   Array<CIMName>& matchedScopes,
                                                   Array<CIMName>& unmatchedScopes)
{
  PEG_METHOD_ENTER (TRC_CQL, "CQLSelectStatementRep::addRequiredProperty");

  //
  // Implementation notes:
  // This function does not look for required properties on embedded objects.
  // This function assumes that applyContext has been called.
  //

  Array<QueryIdentifier> ids = chainId.getSubIdentifiers();

  // After applyContext has been called, a single element
  // chained identifier refers to either an instance of the
  // FROM class, or is the classname on the right side of ISA.
  if (ids.size() == 1)
  {
    // This identifier is not a property name
    PEG_METHOD_EXIT();
    return false;
  }

  PEG_TRACE_STRING (TRC_CQL, Tracer::LEVEL4,"id[1] = " + ids[1].toString());

  if (ids[1].isSymbolicConstant())
  {
    // Non-embedded symbolic constants are not properties
    // Note that an embedded symbolic constant like this:
    // fromclass.embobj.scope::someprop#'ok'
    // implies that embobj is a required property, because
    // embobj could be null, and that affects how the
    // identifier is evaluated.
    PEG_METHOD_EXIT();
    return false;
  }

  // Since applyContext has been called, the first chain element
  // will be the FROM class, so go to the 2nd chain element.
  if (ids[1].isScoped())
  {
    // The 2nd chain element is a scoped property.
    // Eg. fromclass.someclass::someprop

    // Determine the class that the property is being scoped to.
    // This could be the FROM class, or some other class not in the FROM list
    CIMName scopingClass = CIMName(ids[1].getScope());

    // Check if the scoping class is the same as the class passed in.
    if (scopingClass == className)
    {
      // The scoping class is the same as the class passed,
      // add the property if not already added
      if (!containsProperty(ids[1].getName(), reqProps))
      {
        reqProps.append(ids[1].getName());
      }
    }
    else
    {
      // The scoping class is not the same as the class passed in.
      // Check if we already know that the scoping class is a subclass
      // of the class passed in
      if (containsProperty(scopingClass, unmatchedScopes))
      {
        // Scoping class is a subclass.
        PEG_TRACE_STRING (TRC_CQL, Tracer::LEVEL4,"scoping class is a subclass");
        PEG_METHOD_EXIT();
        return false;
      }

      // Check if we already know that the scoping class is a superclass
      // of the class passed in
      Boolean isSuper = false;
      if (containsProperty(scopingClass, matchedScopes))
      {
        // Scoping class is a superclass.
        isSuper = true;
      }

      // Check if the scoping class is a superclass of the class passed in
      if (isSuper || _ctx->isSubClass(scopingClass, className))
      {
        PEG_TRACE_STRING (TRC_CQL, Tracer::LEVEL4,"scoping class is a superclass");

        // Scoping class is a superclass of the class passed in.
        if (!isSuper)
        {
          // Save this information
          matchedScopes.append(scopingClass);
        }

        // Add to the required property list if not already there.
        if (!containsProperty(ids[1].getName(), reqProps))
        {
          reqProps.append(ids[1].getName());
        }
      }
      else
      {
        PEG_TRACE_STRING (TRC_CQL, Tracer::LEVEL4,"scoping class is NOT a superclass");

        // Scoping class is not superclass of class passed in.
        // Save this information.
        unmatchedScopes.append(scopingClass);
      }
    }  // end else scoping class not == class passed in
  }  // end if first id is scoped
  else
  {
    // The 2nd chain element is an unscoped property
    // Check if it is wildcarded
    if (ids[1].isWildcard())
    {
      // Wildcard.
      // If the class passed in is the FROM class, then
      // all properties are required on the class passed in.
      CIMName fromClassName = _ctx->getFromList()[0].getName();
      if (fromClassName == className)
      {
        PEG_TRACE_STRING (TRC_CQL, Tracer::LEVEL4,"wildcard and = FROM");
        PEG_METHOD_EXIT();
        return true;
      }

      // Add all the properties on the FROM class to
      // the required property list.
      CIMClass fromClass = _ctx->getClass(fromClassName);
      for (Uint32 n = 0; n < fromClass.getPropertyCount(); n++)
      {
        // Add to the required property list if not already there.
        if (!containsProperty(fromClass.getProperty(n).getName(), reqProps))
        {
          reqProps.append(fromClass.getProperty(n).getName());
        }
      }

      PEG_METHOD_EXIT();
      return false;
    }

    // Implementation note:
    // Since this API assumes that the class passed in
    // is the FROM class or a subclass of the FROM class,
    // AND validateProperties can be called to check if
    // unscoped properties are on the FROM class,
    // we can just add the required property because
    // it is assumed to be on the FROM class.

    // Add to the required property list if not already there.
    if (!containsProperty(ids[1].getName(), reqProps))
    {
      reqProps.append(ids[1].getName());
    }
  }

  // Indicate the required property is not a wildcard
  PEG_METHOD_EXIT();
  return false;
}

Array<CQLChainedIdentifier> CQLSelectStatementRep::getSelectChainedIdentifiers()
{
  PEG_METHOD_ENTER (TRC_CQL, "CQLSelectStatementRep::getSelectChainedIdentifiers");

  if (!_contextApplied)
    applyContext();

  PEG_METHOD_EXIT();

  return _selectIdentifiers;
}

Array<CQLChainedIdentifier> CQLSelectStatementRep::getWhereChainedIdentifiers()
{
  PEG_METHOD_ENTER (TRC_CQL, "CQLSelectStatementRep::getWhereChainedIdentifiers");

  if(_ctx == NULL)
  {
    PEG_TRACE_STRING (TRC_CQL, Tracer::LEVEL4,"null QC");
    PEG_METHOD_EXIT();
    MessageLoaderParms parms("CQL.CQLSelectStatementRep.QUERY_CONTEXT_IS_NULL",
                             "Trying to process a query with a NULL Query Context.");
    throw CQLRuntimeException(parms);
  }

  if (!_contextApplied)
    applyContext();

  Array<QueryChainedIdentifier> qChainIds = _ctx->getWhereList();
  Array<CQLChainedIdentifier> cqlChainIds;
  for (Uint32 i = 0; i < qChainIds.size(); i++)
  {
    Array<QueryIdentifier> qSubs = qChainIds[i].getSubIdentifiers();
    CQLChainedIdentifier cqlChainId;
    for (Uint32 j = 0; j < qSubs.size(); j++)
    {
      cqlChainId.append(qSubs[j]);
    }

    cqlChainIds.append(cqlChainId);
  }

  PEG_METHOD_EXIT();

  return cqlChainIds;
}

Boolean CQLSelectStatementRep::containsProperty(const CIMName& name,
                                                const Array<CIMName>& props)
{
  PEG_METHOD_ENTER (TRC_CQL, "CQLSelectStatementRep::containsProperty");

  for (Uint32 i = 0; i < props.size(); i++)
  {
    if (props[i] == name)
    {
      PEG_METHOD_EXIT();
      return true;
    }
  }

  PEG_METHOD_EXIT();
  return false;
}

//
// Checks if the classname passed in is the FROM class, or
// a subclass of the FROM class
//
Boolean CQLSelectStatementRep::isFromChild(const CIMName& className)
{
  PEG_METHOD_ENTER (TRC_CQL, "CQLSelectStatementRep::isFromChild");

  QueryContext::ClassRelation rel =
    _ctx->getClassRelation(_ctx->getFromList()[0].getName(), className);

  PEG_METHOD_EXIT();
  return (rel == QueryContext::SAMECLASS || rel == QueryContext::SUBCLASS) ? true : false;
}

void CQLSelectStatementRep::appendClassPath(const CQLIdentifier& inIdentifier)
{
  PEG_METHOD_ENTER (TRC_CQL, "CQLSelectStatementRep::appendClassPath");

  if(_ctx == NULL)
  {
    PEG_TRACE_STRING (TRC_CQL, Tracer::LEVEL4,"null QC");
    PEG_METHOD_EXIT();
    MessageLoaderParms parms("CQL.CQLSelectStatementRep.QUERY_CONTEXT_IS_NULL",
                             "Trying to process a query with a NULL Query Context.");
    throw QueryValidationException(parms);
  }
  _ctx->insertClassPath(inIdentifier);

  PEG_METHOD_EXIT();
}

void CQLSelectStatementRep::setPredicate(const CQLPredicate& inPredicate)
{
  PEG_METHOD_ENTER (TRC_CQL, "CQLSelectStatementRep::setPredicate");
  _predicate = inPredicate;
  PEG_METHOD_EXIT();
}

CQLPredicate CQLSelectStatementRep::getPredicate() const
{
  PEG_METHOD_ENTER (TRC_CQL, "CQLSelectStatementRep::getPredicate");
  return _predicate;
}

void CQLSelectStatementRep::insertClassPathAlias(const CQLIdentifier& inIdentifier,
                                                 String inAlias)
{
  PEG_METHOD_ENTER (TRC_CQL, "CQLSelectStatementRep::insertClassPathAlias");

  if(_ctx == NULL)
  {
    PEG_TRACE_STRING (TRC_CQL, Tracer::LEVEL4,"null QC");
    PEG_METHOD_EXIT();
    MessageLoaderParms parms("CQL.CQLSelectStatementRep.QUERY_CONTEXT_IS_NULL",
                             "Trying to process a query with a NULL Query Context.");
    throw QueryValidationException(parms);
  }
  _ctx->insertClassPath(inIdentifier,inAlias);

  PEG_METHOD_EXIT();
}

void CQLSelectStatementRep::appendSelectIdentifier(const CQLChainedIdentifier& x)
{
  PEG_METHOD_ENTER (TRC_CQL, "CQLSelectStatementRep::appendSelectIdentifier");
  _selectIdentifiers.append(x);
  PEG_METHOD_EXIT();
}

void CQLSelectStatementRep::applyContext()
{
  PEG_METHOD_ENTER (TRC_CQL, "CQLSelectStatementRep::applyContext");

  if(_ctx == NULL)
  {
    PEG_TRACE_STRING (TRC_CQL, Tracer::LEVEL4,"null QC");
    PEG_METHOD_EXIT();
    MessageLoaderParms parms("CQL.CQLSelectStatementRep.QUERY_CONTEXT_IS_NULL",
                             "Trying to process a query with a NULL Query Context.");
    // throw syntax error to be consistent
    throw CQLSyntaxErrorException(parms);
  }

  for (Uint32 i = 0; i < _selectIdentifiers.size(); i++)
  {
    _selectIdentifiers[i].applyContext(*_ctx);
    checkWellFormedIdentifier(_selectIdentifiers[i], true);
  }

  if (hasWhereClause())
  {
    _predicate.applyContext(*_ctx);

    // Note: must be after call to predicate's applyContext
    Array<QueryChainedIdentifier> _whereIdentifiers = _ctx->getWhereList();
    for (Uint32 i = 0; i < _whereIdentifiers.size(); i++)
    {
      checkWellFormedIdentifier(_whereIdentifiers[i], false);
    }
  }

  _contextApplied = true;
  PEG_METHOD_EXIT();
}

void CQLSelectStatementRep::checkWellFormedIdentifier(const QueryChainedIdentifier& chainId,
                                                      Boolean isSelectListId)
{
  PEG_METHOD_ENTER (TRC_CQL, "CQLSelectStatementRep::checkWellFormedIdentifier");

  // This function assumes that applyContext has been called.
  Array<QueryIdentifier> ids = chainId.getSubIdentifiers();

  if (ids.size() == 0)
  {
    PEG_METHOD_EXIT();
    MessageLoaderParms parms("CQL.CQLSelectStatementRep.EMPTY_CHAIN",
                             "An empty chained identifier was found.");
    throw CQLSyntaxErrorException(parms);
  }

  PEG_TRACE_STRING (TRC_CQL, Tracer::LEVEL4,"chain =" + chainId.toString());

  if (ids.size() == 1 && isSelectListId)
  {
    // Single element chain ids are not allow in the select list.
    // The select list can only have properties.
    PEG_METHOD_EXIT();
    MessageLoaderParms parms("CQL.CQLSelectStatementRep.SINGLE_CHID_IN_SELECT",
                             "A property on the FROM class must be selected.");
    throw CQLSyntaxErrorException(parms);
  }

  if (ids[0].isScoped()
      || ids[0].isWildcard()
      || ids[0].isSymbolicConstant()
      || ids[0].isArray())
  {
    // The first identifier must be a classname (it could be the FROM class, or
    // some other class for the right side of ISA)
    PEG_METHOD_EXIT();
    MessageLoaderParms parms("CQL.CQLSelectStatementRep.FIRST_ID_ILLEGAL",
                             "The chained identifier $0 is illegal.",
                             chainId.toString());
    throw CQLSyntaxErrorException(parms);
  }

  Uint32 startingPos = 1;
  for (Uint32 pos = startingPos; pos < ids.size(); pos++)
  {
    if (ids[pos].isArray() && isSelectListId)
    {
      PEG_METHOD_EXIT();
      MessageLoaderParms parms("CQL.CQLSelectStatementRep.ARRAY_IN_SELECT",
                             "The identifier $0 of $1 in the SELECT list cannot use an array index.",
                             ids[pos].toString(), chainId.toString());
      throw CQLSyntaxErrorException(parms);
    }

    if (ids[pos].isSymbolicConstant() && isSelectListId)
    {
      PEG_METHOD_EXIT();
      MessageLoaderParms parms("CQL.CQLSelectStatementRep.SYMCONST_IN_SELECT",
                             "The identifier $0 of $1 in the SELECT list cannot use a symbolic constant.",
                             ids[pos].toString(), chainId.toString());
      throw CQLSyntaxErrorException(parms);
    }

    if (ids[pos].isSymbolicConstant() && pos != (ids.size() -1))
    {
      PEG_METHOD_EXIT();
      MessageLoaderParms parms("CQL.CQLSelectStatementRep.SYMCONST_NOT_LAST",
                             "The symbolic constant identifier $0 of $1 must be the last element.",
                             ids[pos].toString(), chainId.toString());
      throw CQLSyntaxErrorException(parms);
    }

    if (ids[pos].isWildcard())
    {
      if ( !isSelectListId)
      {
        PEG_METHOD_EXIT();
        MessageLoaderParms parms("CQL.CQLSelectStatementRep.WILD_IN_WHERE",
                             "The identifier $0 of $1 in the WHERE list cannot use a wildcard.",
                             ids[pos].toString(), chainId.toString());
        throw CQLSyntaxErrorException(parms);
      }

      if ( pos != ids.size() - 1)
      {
        PEG_METHOD_EXIT();
        MessageLoaderParms parms("CQL.CQLSelectStatementRep.WILD_NOT_END",
                             "The wildcard identifier $0 of $1 must be the last element.",
                             ids[pos].toString(), chainId.toString());
        throw CQLSyntaxErrorException(parms);
      }
    }

    if (pos > startingPos && !ids[pos].isWildcard())
    {
      if (!ids[pos].isScoped())
      {
        PEG_METHOD_EXIT();
        MessageLoaderParms parms("CQL.CQLSelectStatementRep.EMB_PROP_NOT_SCOPED",
                             "The identifier $0 of $1 must use the scope operator.",
                             ids[pos].toString(), chainId.toString());
        throw CQLSyntaxErrorException(parms);
      }
    }
  }

  PEG_METHOD_EXIT();
}

void CQLSelectStatementRep::normalizeToDOC()
{
  PEG_METHOD_ENTER (TRC_CQL, "CQLSelectStatementRep::normalizeToDOC");

  if (!_contextApplied)
    applyContext();

  if(_hasWhereClause){
    Cql2Dnf DNFer(_predicate);
    _predicate = DNFer.getDnfPredicate();
  }

  PEG_METHOD_EXIT();
}

String CQLSelectStatementRep::toString()
{
  PEG_METHOD_ENTER (TRC_CQL, "CQLSelectStatementRep::toString");

  if(_ctx == NULL)
  {
    PEG_TRACE_STRING (TRC_CQL, Tracer::LEVEL4,"null QC");
    PEG_METHOD_EXIT();
    MessageLoaderParms parms("CQL.CQLSelectStatementRep.QUERY_CONTEXT_IS_NULL",
                             "Trying to process a query with a NULL Query Context.");
    throw CQLRuntimeException(parms);
  }

  String s("SELECT ");
  for(Uint32 i = 0; i < _selectIdentifiers.size(); i++){
    if((i > 0) && (i < _selectIdentifiers.size()))
    {
      s.append(",");
    }
    s.append(_selectIdentifiers[i].toString());
  }

  s.append(" ");
  s.append(_ctx->getFromString());

  if(_hasWhereClause){
    s.append(" WHERE ");
    s.append(_predicate.toString());
  }

  PEG_METHOD_EXIT();
  return s;
}

void CQLSelectStatementRep::setHasWhereClause()
{
  PEG_METHOD_ENTER (TRC_CQL, "CQLSelectStatementRep::setHasWhereClause");
  _hasWhereClause = true;
  PEG_METHOD_EXIT();
}

Boolean CQLSelectStatementRep::hasWhereClause()
{
  PEG_METHOD_ENTER (TRC_CQL, "CQLSelectStatementRep::hasWhereClause");
  return _hasWhereClause;
}

void  CQLSelectStatementRep::clear()
{
  PEG_METHOD_ENTER (TRC_CQL, "CQLSelectStatementRep::clear");

    if(_ctx == NULL)
    {
     PEG_TRACE_STRING (TRC_CQL, Tracer::LEVEL4,"null QC");
     PEG_METHOD_EXIT();
     MessageLoaderParms parms("CQL.CQLSelectStatementRep.QUERY_CONTEXT_IS_NULL",
                              "Trying to process a query with a NULL Query Context.");
     throw CQLRuntimeException(parms);
    }

   _ctx->clear();
   _hasWhereClause = false;
   _contextApplied = false;
   _predicate = CQLPredicate();
   _selectIdentifiers.clear();

   PEG_METHOD_EXIT();
}

PEGASUS_NAMESPACE_END

No CVS admin address has been configured
Powered by
ViewCVS 0.9.2