PlanNodeFragment * PlanNodeFragment::fromJSONObject(PlannerDomValue obj) { // read and construct plannodes from json object auto_ptr<PlanNodeFragment> pnf; if (obj.hasNonNullKey("PLAN_NODES_LISTS")) { PlannerDomValue planNodesListArray = obj.valueForKey("PLAN_NODES_LISTS"); if (!obj.hasNonNullKey("EXECUTE_LISTS")) { throwFatalException("Failed to construct plan fragment. Missing EXECUTE_LISTS key"); } PlannerDomValue executeListArray = obj.valueForKey("EXECUTE_LISTS"); if (planNodesListArray.arrayLen() != executeListArray.arrayLen()) { throwFatalException("Failed to construct plan fragment. EXECUTE_LISTS and PLAN_NODES_LISTS do not match"); } int stmtCnt = planNodesListArray.arrayLen(); pnf.reset(new PlanNodeFragment()); for (int i = 0; i < stmtCnt; i++) { int stmtId = planNodesListArray.valueAtIndex(i).valueForKey("STATEMENT_ID").asInt(); PlannerDomValue planNodesList = planNodesListArray.valueAtIndex(i).valueForKey("PLAN_NODES"); PlannerDomValue executeList = executeListArray.valueAtIndex(i).valueForKey("EXECUTE_LIST"); PlanNodeFragment::nodeListFromJSONObject(pnf.get(), planNodesList, executeList, stmtId); } } else { pnf.reset(new PlanNodeFragment()); PlanNodeFragment::nodeListFromJSONObject(pnf.get(), obj.valueForKey("PLAN_NODES"), obj.valueForKey("EXECUTE_LIST"), 0); } PlanNodeFragment::loadParamsFromJSONObject(pnf.get(), obj); PlanNodeFragment *retval = pnf.get(); pnf.release(); assert(retval); return retval; }
/** Parse JSON parameters to create a subquery expression */ static AbstractExpression* subqueryFactory(ExpressionType subqueryType, PlannerDomValue obj, const std::vector<AbstractExpression*>* args) { int subqueryId = obj.valueForKey("SUBQUERY_ID").asInt(); std::vector<int> paramIdxs; if (obj.hasNonNullKey("PARAM_IDX")) { PlannerDomValue params = obj.valueForKey("PARAM_IDX"); int paramSize = params.arrayLen(); paramIdxs.reserve(paramSize); if (args == NULL || args->size() != paramSize) { throw SerializableEEException(VOLT_EE_EXCEPTION_TYPE_EEEXCEPTION, "subqueryFactory: parameter indexes/tve count mismatch"); } for (int i = 0; i < paramSize; ++i) { int paramIdx = params.valueAtIndex(i).asInt(); paramIdxs.push_back(paramIdx); } } std::vector<int> otherParamIdxs; if (obj.hasNonNullKey("OTHER_PARAM_IDX")) { PlannerDomValue otherParams = obj.valueForKey("OTHER_PARAM_IDX"); int otherParamSize = otherParams.arrayLen(); otherParamIdxs.reserve(otherParamSize); otherParamIdxs.reserve(otherParamSize); for (int i = 0; i < otherParamSize; ++i) { int paramIdx = otherParams.valueAtIndex(i).asInt(); otherParamIdxs.push_back(paramIdx); } } return new SubqueryExpression(subqueryType, subqueryId, paramIdxs, otherParamIdxs, args); }
void PlanNodeFragment::nodeListFromJSONObject(PlanNodeFragment *pnf, PlannerDomValue planNodesList, PlannerDomValue executeList, int stmtId) { assert(pnf->m_stmtExecutionListMap.find(stmtId) == pnf->m_stmtExecutionListMap.end()); // NODE_LIST std::vector<AbstractPlanNode*> planNodes; for (int i = 0; i < planNodesList.arrayLen(); i++) { AbstractPlanNode *node = AbstractPlanNode::fromJSONObject(planNodesList.valueAtIndex(i)); assert(node); assert(pnf->m_idToNodeMap.find(node->getPlanNodeId()) == pnf->m_idToNodeMap.end()); pnf->m_idToNodeMap[node->getPlanNodeId()] = node; planNodes.push_back(node); } // walk the plannodes and complete each plannode's id-to-node maps for (std::vector< AbstractPlanNode* >::const_iterator node = planNodes.begin(); node != planNodes.end(); ++node) { const std::vector<CatalogId>& childIds = (*node)->getChildIds(); for (int zz = 0; zz < childIds.size(); zz++) { (*node)->addChild(pnf->m_idToNodeMap[childIds[zz]]); } } // EXECUTE_LIST std::auto_ptr<std::vector<AbstractPlanNode*> > executeNodeList(new std::vector<AbstractPlanNode*>()); for (int i = 0; i < executeList.arrayLen(); i++) { executeNodeList->push_back(pnf->m_idToNodeMap[executeList.valueAtIndex(i).asInt()]); } pnf->m_stmtExecutionListMap.insert(std::make_pair(stmtId, executeNodeList.get())); executeNodeList.release(); }
// ---------------------------------------------------- // Serialization Functions // ---------------------------------------------------- AbstractPlanNode* AbstractPlanNode::fromJSONObject(PlannerDomValue obj) { string typeString = obj.valueForKey("PLAN_NODE_TYPE").asStr(); std::unique_ptr<AbstractPlanNode> node( plannodeutil::getEmptyPlanNode(stringToPlanNode(typeString))); node->m_planNodeId = obj.valueForKey("ID").asInt(); if (obj.hasKey("INLINE_NODES")) { PlannerDomValue inlineNodesValue = obj.valueForKey("INLINE_NODES"); for (int i = 0; i < inlineNodesValue.arrayLen(); i++) { PlannerDomValue inlineNodeObj = inlineNodesValue.valueAtIndex(i); AbstractPlanNode *newNode = AbstractPlanNode::fromJSONObject(inlineNodeObj); // todo: if this throws, new Node can be leaked. // As long as newNode is not NULL, this will not throw. assert(newNode); node->addInlinePlanNode(newNode); } } loadIntArrayFromJSONObject("CHILDREN_IDS", obj, node->m_childIds); // Output schema are optional -- when they can be determined by a child's copy. if (obj.hasKey("OUTPUT_SCHEMA")) { PlannerDomValue outputSchemaArray = obj.valueForKey("OUTPUT_SCHEMA"); for (int i = 0; i < outputSchemaArray.arrayLen(); i++) { PlannerDomValue outputColumnValue = outputSchemaArray.valueAtIndex(i); SchemaColumn* outputColumn = new SchemaColumn(outputColumnValue, i); node->m_outputSchema.push_back(outputColumn); } node->m_validOutputColumnCount = static_cast<int>(node->m_outputSchema.size()); } // Anticipate and mark the two different scenarios of missing output schema. // The actual output schema can be searched for on demand once the whole plan tree is loaded. // If there's an inline projection node, // one of its chief purposes is defining the parent's output schema. else if (node->getInlinePlanNode(PLAN_NODE_TYPE_PROJECTION)) { node->m_validOutputColumnCount = SCHEMA_UNDEFINED_SO_GET_FROM_INLINE_PROJECTION; } // Otherwise, the node is relying on a child's output schema, possibly several levels down, // OR it is just an inline node (e.g. a LIMIT) or a DML node, // whose output schema is known from its context or is otherwise not of any interest. else { node->m_validOutputColumnCount = SCHEMA_UNDEFINED_SO_GET_FROM_CHILD; } node->loadFromJSONObject(obj); AbstractPlanNode* retval = node.get(); node.release(); assert(retval); return retval; }
void AggregatePlanNode::loadFromJSONObject(PlannerDomValue obj) { PlannerDomValue aggregateColumnsArray = obj.valueForKey("AGGREGATE_COLUMNS"); for (int i = 0; i < aggregateColumnsArray.arrayLen(); i++) { PlannerDomValue aggregateColumnValue = aggregateColumnsArray.valueAtIndex(i); bool containsType = false; bool containsDistinct = false; bool containsOutputColumn = false; bool containsExpression = false; if (aggregateColumnValue.hasNonNullKey("AGGREGATE_TYPE")) { containsType = true; string aggregateColumnTypeString = aggregateColumnValue.valueForKey("AGGREGATE_TYPE").asStr(); m_aggregates.push_back(stringToExpression(aggregateColumnTypeString)); } if (aggregateColumnValue.hasNonNullKey("AGGREGATE_DISTINCT")) { containsDistinct = true; bool distinct = aggregateColumnValue.valueForKey("AGGREGATE_DISTINCT").asInt() == 1; m_distinctAggregates.push_back(distinct); } if (aggregateColumnValue.hasNonNullKey("AGGREGATE_OUTPUT_COLUMN")) { containsOutputColumn = true; int column = aggregateColumnValue.valueForKey("AGGREGATE_OUTPUT_COLUMN").asInt(); m_aggregateOutputColumns.push_back(column); } if (aggregateColumnValue.hasNonNullKey("AGGREGATE_EXPRESSION")) { containsExpression = true; PlannerDomValue exprDom = aggregateColumnValue.valueForKey("AGGREGATE_EXPRESSION"); m_aggregateInputExpressions.push_back(AbstractExpression::buildExpressionTree(exprDom)); } if(!(containsType && containsDistinct && containsOutputColumn)) { throw SerializableEEException(VOLT_EE_EXCEPTION_TYPE_EEEXCEPTION, "AggregatePlanNode::loadFromJSONObject:" " Missing type, distinct, or outputcolumn."); } if ( ! containsExpression) { m_aggregateInputExpressions.push_back(NULL); } } if (obj.hasNonNullKey("GROUPBY_EXPRESSIONS")) { PlannerDomValue groupByExpressionsArray = obj.valueForKey("GROUPBY_EXPRESSIONS"); for (int i = 0; i < groupByExpressionsArray.arrayLen(); i++) { m_groupByExpressions.push_back(AbstractExpression::buildExpressionTree(groupByExpressionsArray.valueAtIndex(i))); } } if (obj.hasNonNullKey("PRE_PREDICATE")) { m_prePredicate = AbstractExpression::buildExpressionTree(obj.valueForKey("PRE_PREDICATE")); } if (obj.hasNonNullKey("POST_PREDICATE")) { m_postPredicate = AbstractExpression::buildExpressionTree(obj.valueForKey("POST_PREDICATE")); } }
void PlanNodeFragment::loadParamsFromJSONObject(PlanNodeFragment *pnf, PlannerDomValue obj) { if (obj.hasKey("PARAMETERS")) { PlannerDomValue parametersArray = obj.valueForKey("PARAMETERS"); for (int i = 0; i < parametersArray.arrayLen(); i++) { PlannerDomValue parameterArray = parametersArray.valueAtIndex(i); int index = parameterArray.valueAtIndex(0).asInt(); std::string typeString = parameterArray.valueAtIndex(1).asStr(); pnf->m_parameters.push_back(std::pair< int, voltdb::ValueType>(index, stringToValue(typeString))); } } }
void PartitionByPlanNode::loadFromJSONObject(PlannerDomValue obj) { // Start with the base class. AggregatePlanNode::loadFromJSONObject(obj); // Read the sort expressions and directions. PlannerDomValue sortByColumnArray = obj.valueForKey("SORT_COLUMNS"); for (int idx = 0; idx < sortByColumnArray.arrayLen(); idx += 1) { PlannerDomValue sortColumnValue = sortByColumnArray.valueAtIndex(idx); if (sortColumnValue.hasNonNullKey("SORT_EXPRESSION")) { PlannerDomValue exprDom = sortColumnValue.valueForKey("SORT_EXPRESSION"); m_sortExpressions.push_back(AbstractExpression::buildExpressionTree(exprDom)); } else { throw SerializableEEException(VOLT_EE_EXCEPTION_TYPE_EEEXCEPTION, "PartitionByPlanNode::loadFromJSONObject:" " Missing sort expression."); } if (sortColumnValue.hasNonNullKey("SORT_DIRECTION")) { std::string dirStr = sortColumnValue.valueForKey("SORT_DIRECTION").asStr(); m_sortDirections.push_back(stringToSortDirection(dirStr)); } else { throw SerializableEEException(VOLT_EE_EXCEPTION_TYPE_EEEXCEPTION, "PartitionByPlanNode::loadFromJSONObject:" " Missing sort direction."); } } }
PlanNodeFragment * PlanNodeFragment::fromJSONObject(PlannerDomValue obj) { auto_ptr<PlanNodeFragment> pnf(new PlanNodeFragment()); // read and construct plannodes from json object PlannerDomValue planNodesArray = obj.valueForKey("PLAN_NODES"); for (int i = 0; i < planNodesArray.arrayLen(); i++) { AbstractPlanNode *node = NULL; node = AbstractPlanNode::fromJSONObject(planNodesArray.valueAtIndex(i)); assert(node); pnf->m_planNodes.push_back(node); pnf->m_idToNodeMap[node->getPlanNodeId()] = node; } // walk the plannodes and complete each plannode's id-to-node maps for (std::vector< AbstractPlanNode* >::const_iterator node = pnf->m_planNodes.begin(); node != pnf->m_planNodes.end(); ++node) { const std::vector<CatalogId> childIds = (*node)->getChildIds(); for (int zz = 0; zz < childIds.size(); zz++) { (*node)->addChild(pnf->m_idToNodeMap[childIds[zz]]); } } pnf->loadFromJSONObject(obj); PlanNodeFragment *retval = pnf.get(); pnf.release(); assert(retval); return retval; }
void AbstractPlanNode::loadSortListFromJSONObject(PlannerDomValue obj, std::vector<AbstractExpression*> *sortExprs, std::vector<SortDirectionType> *sortDirs) { PlannerDomValue sortColumnsArray = obj.valueForKey("SORT_COLUMNS"); for (int i = 0; i < sortColumnsArray.arrayLen(); i++) { PlannerDomValue sortColumn = sortColumnsArray.valueAtIndex(i); bool hasDirection = (sortDirs == NULL), hasExpression = (sortExprs == NULL); if (sortDirs && sortColumn.hasNonNullKey("SORT_DIRECTION")) { hasDirection = true; std::string sortDirectionStr = sortColumn.valueForKey("SORT_DIRECTION").asStr(); sortDirs->push_back(stringToSortDirection(sortDirectionStr)); } if (sortExprs && sortColumn.hasNonNullKey("SORT_EXPRESSION")) { hasExpression = true; PlannerDomValue exprDom = sortColumn.valueForKey("SORT_EXPRESSION"); sortExprs->push_back(AbstractExpression::buildExpressionTree(exprDom)); } if (!(hasExpression && hasDirection)) { throw SerializableEEException(VOLT_EE_EXCEPTION_TYPE_EEEXCEPTION, "OrderByPlanNode::loadFromJSONObject:" " Does not have expression and direction."); } } }
/** * Parse and save predicates. */ void ElasticContext::updatePredicates(const std::vector<std::string> &predicateStrings) { //If there is already a predicate and thus presumably an index, make sure the request is a subset of what exists //That should always be the case, but wrong answers will follow if we are wrong if (m_predicates.size() > 0 && dynamic_cast<HashRangeExpression*>(&m_predicates[0]) != NULL && predicateStrings.size() > 0) { PlannerDomRoot domRoot(predicateStrings[0].c_str()); if (!domRoot.isNull()) { PlannerDomValue predicateObject = domRoot.rootObject(); HashRangeExpression *expression = dynamic_cast<HashRangeExpression*>(&m_predicates[0]); if (predicateObject.hasKey("predicateExpression")) { PlannerDomValue predicateExpression = predicateObject.valueForKey("predicateExpression"); PlannerDomValue rangesArray = predicateExpression.valueForKey("RANGES"); for (int ii = 0; ii < rangesArray.arrayLen(); ii++) { PlannerDomValue arrayObject = rangesArray.valueAtIndex(ii); PlannerDomValue rangeStartValue = arrayObject.valueForKey("RANGE_START"); PlannerDomValue rangeEndValue = arrayObject.valueForKey("RANGE_END"); if (!expression->binarySearch(rangeStartValue.asInt()).isTrue()) { throwFatalException("ElasticContext activate failed because a context already existed with conflicting ranges, conflicting range start is %d", rangeStartValue.asInt()); } if (!expression->binarySearch(rangeEndValue.asInt()).isTrue()) { throwFatalException("ElasticContext activate failed because a context already existed with conflicting ranges, conflicting range end is %d", rangeStartValue.asInt()); } } } } } m_predicateStrings = predicateStrings; // retain for possible clone after TRUNCATE TABLE TableStreamerContext::updatePredicates(predicateStrings); }
void PlanNodeFragment::loadFromJSONObject(PlannerDomValue obj) { PlannerDomValue executeListArray = obj.valueForKey("EXECUTE_LIST"); for (int i = 0; i < executeListArray.arrayLen(); i++) { m_executionList.push_back(m_idToNodeMap[executeListArray.valueAtIndex(i).asInt()]); } if (obj.hasKey("PARAMETERS")) { PlannerDomValue parametersArray = obj.valueForKey("PARAMETERS"); for (int i = 0; i < parametersArray.arrayLen(); i++) { PlannerDomValue parameterArray = parametersArray.valueAtIndex(i); int index = parameterArray.valueAtIndex(0).asInt(); std::string typeString = parameterArray.valueAtIndex(1).asStr(); parameters.push_back(std::pair< int, voltdb::ValueType>(index, stringToValue(typeString))); } } }
void ExpressionUtil::loadIndexedExprsFromJson(std::vector<AbstractExpression*>& indexed_exprs, const std::string& jsonarraystring) { PlannerDomRoot domRoot(jsonarraystring.c_str()); PlannerDomValue expressionsArray = domRoot.rootObject(); for (int i = 0; i < expressionsArray.arrayLen(); i++) { PlannerDomValue exprValue = expressionsArray.valueAtIndex(i); AbstractExpression *expr = AbstractExpression::buildExpressionTree(exprValue); indexed_exprs.push_back(expr); } }
void AbstractPlanNode::loadIntArrayFromJSONObject(const char* label, PlannerDomValue obj, std::vector<int>& result) { if (obj.hasNonNullKey(label)) { PlannerDomValue intArray = obj.valueForKey(label); for (int i = 0; i < intArray.arrayLen(); i++) { result.push_back(intArray.valueAtIndex(i).asInt()); } } }
// Load boolean array from JSON object. // In IndexScanPlanNode (indexscannode.h and indexscannode.cpp), // we added a boolean vector "m_compare_not_distinct" // to indicate whether null values should be skipped for each search key column. // This function is used to deseralize that boolean vector. (ENG-11096) void AbstractPlanNode::loadBooleanArrayFromJSONObject(const char* label, PlannerDomValue obj, std::vector<bool>& result) { if (obj.hasNonNullKey(label)) { PlannerDomValue stringArray = obj.valueForKey(label); int len = stringArray.arrayLen(); for (int i = 0; i < len; ++i) { result.push_back(stringArray.valueAtIndex(i).asBool()); } } }
void AbstractPlanNode::OwningExpressionVector::loadExpressionArrayFromJSONObject(const char* label, PlannerDomValue obj) { clear(); if ( ! obj.hasNonNullKey(label)) { return; } PlannerDomValue arrayObj = obj.valueForKey(label); for (int i = 0; i < arrayObj.arrayLen(); i++) { AbstractExpression *expr = AbstractExpression::buildExpressionTree(arrayObj.valueAtIndex(i)); push_back(expr); } }
/** Parse JSON parameters to create a hash range expression */ static AbstractExpression* hashRangeFactory(PlannerDomValue obj) { PlannerDomValue hashColumnValue = obj.valueForKey("HASH_COLUMN"); PlannerDomValue rangesArray = obj.valueForKey("RANGES"); srange_type *ranges = new srange_type[rangesArray.arrayLen()]; for (int ii = 0; ii < rangesArray.arrayLen(); ii++) { PlannerDomValue arrayObject = rangesArray.valueAtIndex(ii); PlannerDomValue rangeStartValue = arrayObject.valueForKey("RANGE_START"); PlannerDomValue rangeEndValue = arrayObject.valueForKey("RANGE_END"); ranges[ii] = srange_type(rangeStartValue.asInt(), rangeEndValue.asInt()); } return new HashRangeExpression(hashColumnValue.asInt(), ranges, static_cast<int>(rangesArray.arrayLen())); }
void InsertPlanNode::loadFromJSONObject(PlannerDomValue obj) { AbstractOperationPlanNode::loadFromJSONObject(obj); m_multiPartition = obj.valueForKey("MULTI_PARTITION").asBool(); if (obj.hasNonNullKey("FIELD_MAP")) { PlannerDomValue fieldMap = obj.valueForKey("FIELD_MAP"); for (int i = 0; i < fieldMap.arrayLen(); ++i) { m_fieldMap.push_back(fieldMap.valueAtIndex(i).asInt()); } } m_isUpsert = false; if (obj.hasNonNullKey("UPSERT")) { m_isUpsert = true; } m_sourceIsPartitioned = false; if (obj.hasNonNullKey("SOURCE_IS_PARTITIONED")) { m_sourceIsPartitioned = true; } }
// ---------------------------------------------------- // Serialization Functions // ---------------------------------------------------- AbstractPlanNode* AbstractPlanNode::fromJSONObject(PlannerDomValue obj) { string typeString = obj.valueForKey("PLAN_NODE_TYPE").asStr(); //FIXME: EVEN if this leak guard is warranted -- // like we EXPECT to be catching plan deserialization exceptions // and our biggest concern will be the memory this may leak? -- // we don't need to be mediating all the node // pointer dereferences through the smart pointer. // Why not just get() it and forget it until .release() time? // As is, it just makes single-step debugging awkward. std::auto_ptr<AbstractPlanNode> node( plannodeutil::getEmptyPlanNode(stringToPlanNode(typeString))); node->m_planNodeId = obj.valueForKey("ID").asInt(); PlannerDomValue inlineNodesValue = obj.valueForKey("INLINE_NODES"); for (int i = 0; i < inlineNodesValue.arrayLen(); i++) { PlannerDomValue inlineNodeObj = inlineNodesValue.valueAtIndex(i); AbstractPlanNode *newNode = AbstractPlanNode::fromJSONObject(inlineNodeObj); // todo: if this throws, new Node can be leaked. // As long as newNode is not NULL, this will not throw. assert(newNode); node->addInlinePlanNode(newNode); } PlannerDomValue parentIdsArray = obj.valueForKey("PARENT_IDS"); for (int i = 0; i < parentIdsArray.arrayLen(); i++) { int32_t parentNodeId = parentIdsArray.valueAtIndex(i).asInt(); node->m_parentIds.push_back(parentNodeId); } PlannerDomValue childNodeIdsArray = obj.valueForKey("CHILDREN_IDS"); for (int i = 0; i < childNodeIdsArray.arrayLen(); i++) { int32_t childNodeId = childNodeIdsArray.valueAtIndex(i).asInt(); node->m_childIds.push_back(childNodeId); } // Output schema are optional -- when they can be determined by a child's copy. if (obj.hasKey("OUTPUT_SCHEMA")) { PlannerDomValue outputSchemaArray = obj.valueForKey("OUTPUT_SCHEMA"); for (int i = 0; i < outputSchemaArray.arrayLen(); i++) { PlannerDomValue outputColumnValue = outputSchemaArray.valueAtIndex(i); SchemaColumn* outputColumn = new SchemaColumn(outputColumnValue); node->m_outputSchema.push_back(outputColumn); } node->m_validOutputColumnCount = static_cast<int>(node->m_outputSchema.size()); } // Anticipate and mark the two different scenarios of missing output schema. // The actual output schema can be searched for on demand once the whole plan tree is loaded. // If there's an inline projection node, // one of its chief purposes is defining the parent's output schema. else if (node->getInlinePlanNode(PLAN_NODE_TYPE_PROJECTION)) { node->m_validOutputColumnCount = SCHEMA_UNDEFINED_SO_GET_FROM_INLINE_PROJECTION; } // Otherwise, the node is relying on a child's output schema, possibly several levels down, // OR it is just an inline node (e.g. a LIMIT) or a DML node, // whose output schema is known from its context or is otherwise not of any interest. else { node->m_validOutputColumnCount = SCHEMA_UNDEFINED_SO_GET_FROM_CHILD; } node->loadFromJSONObject(obj); AbstractPlanNode* retval = node.get(); node.release(); assert(retval); return retval; }
AbstractExpression* AbstractExpression::buildExpressionTree_recurse(PlannerDomValue obj) { // build a tree recursively from the bottom upwards. // when the expression node is instantiated, its type, // value and child types will have been discovered. ExpressionType peek_type = EXPRESSION_TYPE_INVALID; ValueType value_type = VALUE_TYPE_INVALID; bool inBytes = false; AbstractExpression *left_child = NULL; AbstractExpression *right_child = NULL; std::vector<AbstractExpression*>* argsVector = NULL; // read the expression type peek_type = static_cast<ExpressionType>(obj.valueForKey("TYPE").asInt()); assert(peek_type != EXPRESSION_TYPE_INVALID); if (obj.hasNonNullKey("VALUE_TYPE")) { int32_t value_type_int = obj.valueForKey("VALUE_TYPE").asInt(); value_type = static_cast<ValueType>(value_type_int); assert(value_type != VALUE_TYPE_INVALID); if (obj.hasNonNullKey("IN_BYTES")) { inBytes = true; } } // add the value size int valueSize = -1; if (obj.hasNonNullKey("VALUE_SIZE")) { valueSize = obj.valueForKey("VALUE_SIZE").asInt(); } else { // This value size should be consistent with VoltType.java valueSize = NValue::getTupleStorageSize(value_type); } // recurse to children try { if (obj.hasNonNullKey("LEFT")) { PlannerDomValue leftValue = obj.valueForKey("LEFT"); left_child = AbstractExpression::buildExpressionTree_recurse(leftValue); } if (obj.hasNonNullKey("RIGHT")) { PlannerDomValue rightValue = obj.valueForKey("RIGHT"); right_child = AbstractExpression::buildExpressionTree_recurse(rightValue); } // NULL argsVector corresponds to a missing ARGS value // vs. an empty argsVector which corresponds to an empty array ARGS value. // Different expression types could assert either a NULL or non-NULL argsVector initializer. if (obj.hasNonNullKey("ARGS")) { PlannerDomValue argsArray = obj.valueForKey("ARGS"); argsVector = new std::vector<AbstractExpression*>(); for (int i = 0; i < argsArray.arrayLen(); i++) { PlannerDomValue argValue = argsArray.valueAtIndex(i); AbstractExpression* argExpr = AbstractExpression::buildExpressionTree_recurse(argValue); argsVector->push_back(argExpr); } } // invoke the factory. obviously it has to handle null children. // pass it the serialization stream in case a subclass has more // to read. yes, the per-class data really does follow the // child serializations. AbstractExpression* finalExpr = ExpressionUtil::expressionFactory(obj, peek_type, value_type, valueSize, left_child, right_child, argsVector); finalExpr->setInBytes(inBytes); return finalExpr; } catch (const SerializableEEException &ex) { delete left_child; delete right_child; delete argsVector; throw; } }