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(); }
int AbstractScanPlanNode::getColumnIndexFromGuid(int guid, const catalog::Database* db) const { // if the scan node has an inlined projection, then we // want to look up the column index in the projection AbstractPlanNode* projection = getInlinePlanNode(PLAN_NODE_TYPE_PROJECTION); if (projection != NULL) { return projection->getColumnIndexFromGuid(guid, db); } else { string name = ""; for (int i = 0; i < m_outputColumnGuids.size(); i++) { if (guid == m_outputColumnGuids[i]) { return getColumnIndexFromName(m_outputColumnNames[i], db); } } } return -1; }
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; }
bool UpdateExecutor::p_init(AbstractPlanNode* abstract_node, TempTableLimits* limits) { VOLT_TRACE("init Update Executor"); m_node = dynamic_cast<UpdatePlanNode*>(abstract_node); assert(m_node); assert(m_node->getInputTableCount() == 1); // input table should be temptable m_inputTable = dynamic_cast<TempTable*>(m_node->getInputTable()); assert(m_inputTable); // target table should be persistenttable PersistentTable*targetTable = dynamic_cast<PersistentTable*>(m_node->getTargetTable()); assert(targetTable); setDMLCountOutputTable(limits); AbstractPlanNode *child = m_node->getChildren()[0]; ProjectionPlanNode *proj_node = NULL; if (NULL == child) { VOLT_ERROR("Attempted to initialize update executor with NULL child"); return false; } PlanNodeType pnt = child->getPlanNodeType(); if (pnt == PLAN_NODE_TYPE_PROJECTION) { proj_node = dynamic_cast<ProjectionPlanNode*>(child); } else if (pnt == PLAN_NODE_TYPE_SEQSCAN || pnt == PLAN_NODE_TYPE_INDEXSCAN) { proj_node = dynamic_cast<ProjectionPlanNode*>(child->getInlinePlanNode(PLAN_NODE_TYPE_PROJECTION)); assert(NULL != proj_node); } vector<string> output_column_names = proj_node->getOutputColumnNames(); const vector<string> &targettable_column_names = targetTable->getColumnNames(); /* * The first output column is the tuple address expression and it isn't part of our output so we skip * it when generating the map from input columns to the target table columns. */ for (int ii = 1; ii < output_column_names.size(); ii++) { for (int jj=0; jj < targettable_column_names.size(); ++jj) { if (targettable_column_names[jj].compare(output_column_names[ii]) == 0) { m_inputTargetMap.push_back(pair<int,int>(ii, jj)); break; } } } assert(m_inputTargetMap.size() == (output_column_names.size() - 1)); m_inputTargetMapSize = (int)m_inputTargetMap.size(); m_inputTuple = TableTuple(m_inputTable->schema()); // for target table related info. m_partitionColumn = targetTable->partitionColumn(); return true; }
Table* ExecutorContext::executeExecutors(const std::vector<AbstractExecutor*>& executorList, int subqueryId) { // Walk through the list and execute each plannode. // The query planner guarantees that for a given plannode, // all of its children are positioned before it in this list, // therefore dependency tracking is not needed here. size_t ttl = executorList.size(); int ctr = 0; try { BOOST_FOREACH (AbstractExecutor *executor, executorList) { assert(executor); // Call the execute method to actually perform whatever action // it is that the node is supposed to do... if (!executor->execute(*m_staticParams)) { throw SerializableEEException(VOLT_EE_EXCEPTION_TYPE_EEEXCEPTION, "Unspecified execution error detected"); } ++ctr; } } catch (const SerializableEEException &e) { // Clean up any tempTables when the plan finishes abnormally. // This needs to be the caller's responsibility for normal returns because // the caller may want to first examine the final output table. cleanupAllExecutors(); // Normally, each executor cleans its memory pool as it finishes execution, // but in the case of a throw, it may not have had the chance. // So, clean up all the memory pools now. //TODO: This code singles out inline nodes for cleanup. // Is that because the currently active (memory pooling) non-inline // executor always cleans itself up before throwing??? // But if an active executor can be that smart, an active executor with // (potential) inline children could also be smart enough to clean up // after its inline children, and this post-processing would not be needed. BOOST_FOREACH (AbstractExecutor *executor, executorList) { assert (executor); AbstractPlanNode * node = executor->getPlanNode(); std::map<PlanNodeType, AbstractPlanNode*>::iterator it; std::map<PlanNodeType, AbstractPlanNode*> inlineNodes = node->getInlinePlanNodes(); for (it = inlineNodes.begin(); it != inlineNodes.end(); it++ ) { AbstractPlanNode *inlineNode = it->second; inlineNode->getExecutor()->cleanupMemoryPool(); } } if (subqueryId == 0) { VOLT_TRACE("The Executor's execution at position '%d' failed", ctr); } else { VOLT_TRACE("The Executor's execution at position '%d' in subquery %d failed", ctr, subqueryId); } throw; }
PlanNodeFragment * PlanNodeFragment::fromJSONObject(json_spirit::Object &obj) { json_spirit::Value planNodesValue = json_spirit::find_value( obj, "PLAN_NODES"); if (planNodesValue == json_spirit::Value::null) { throwFatalException("Failure attempting to load plan a plan node fragment from a " "json_spirit::Object. There was no value \"PLAN_NODES\""); } PlanNodeFragment * pnf = new PlanNodeFragment(); // read and construct plannodes from json object json_spirit::Array planNodesArray = planNodesValue.get_array(); for (int ii = 0; ii < planNodesArray.size(); ii++) { AbstractPlanNode *node = NULL; try { node = AbstractPlanNode::fromJSONObject(planNodesArray[ii].get_obj()); } catch (SerializableEEException &ex) { delete pnf; throw; } 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(); std::vector<AbstractPlanNode*> &children = (*node)->getChildren(); for (int zz = 0; zz < childIds.size(); zz++) { children.push_back(pnf->m_idToNodeMap[childIds[zz]]); } const std::vector<CatalogId> parentIds = (*node)->getParentIds(); std::vector<AbstractPlanNode*> &parents = (*node)->getParents(); for (int zz = 0; zz < parentIds.size(); zz++) { parents.push_back(pnf->m_idToNodeMap[parentIds[zz]]); } } try { pnf->loadFromJSONObject(obj); } catch (SerializableEEException &eeEx) { delete pnf; throw; } return pnf; }
int AbstractPlanNode::getColumnIndexFromGuid(int guid, const catalog::Database* db) const { if (m_children.size() != 1) { return -1; } AbstractPlanNode* child = m_children[0]; if (child == NULL) { return -1; } return child->getColumnIndexFromGuid(guid, db); }
bool UpdateExecutor::p_init(AbstractPlanNode *abstract_node, const catalog::Database* catalog_db, int* tempTableMemoryInBytes) { VOLT_TRACE("init Update Executor"); UpdatePlanNode* node = dynamic_cast<UpdatePlanNode*>(abstract_node); assert(node); assert(node->getTargetTable()); assert(node->getInputTables().size() == 1); m_inputTable = dynamic_cast<TempTable*>(node->getInputTables()[0]); //input table should be temptable assert(m_inputTable); m_targetTable = dynamic_cast<PersistentTable*>(node->getTargetTable()); //target table should be persistenttable assert(m_targetTable); assert(node->getTargetTable()); // Our output is just our input table (regardless if plan is single-sited or not) node->setOutputTable(node->getInputTables()[0]); // record if a full index update is needed, or if these checks can be skipped m_updatesIndexes = node->doesUpdateIndexes(); AbstractPlanNode *child = node->getChildren()[0]; ProjectionPlanNode *proj_node = NULL; if (NULL == child) { VOLT_ERROR("Attempted to initialize update executor with NULL child"); return false; } PlanNodeType pnt = child->getPlanNodeType(); if (pnt == PLAN_NODE_TYPE_PROJECTION) { proj_node = dynamic_cast<ProjectionPlanNode*>(child); } else if (pnt == PLAN_NODE_TYPE_SEQSCAN || pnt == PLAN_NODE_TYPE_INDEXSCAN) { proj_node = dynamic_cast<ProjectionPlanNode*>(child->getInlinePlanNode(PLAN_NODE_TYPE_PROJECTION)); assert(NULL != proj_node); } std::vector<std::string> output_column_names = proj_node->getOutputColumnNames(); std::string targetTableName = node->getTargetTableName(); catalog::Table *targetTable = NULL; catalog::CatalogMap<catalog::Table> tables = catalog_db->tables(); for ( catalog::CatalogMap<catalog::Table>::field_map_iter i = tables.begin(); i != tables.end(); i++) { catalog::Table *table = (*i).second; if (table->name().compare(targetTableName) == 0) { targetTable = table; break; } } assert(targetTable != NULL); catalog::CatalogMap<catalog::Column> columns = targetTable->columns(); /* * The first output column is the tuple address expression and it isn't part of our output so we skip * it when generating the map from input columns to the target table columns. */ for (int ii = 1; ii < output_column_names.size(); ii++) { std::string outputColumnName = output_column_names[ii]; catalog::Column *column = columns.get(outputColumnName); assert (column != NULL); m_inputTargetMap.push_back(std::pair<int, int>( ii, column->index())); } m_inputTargetMapSize = (int)m_inputTargetMap.size(); m_inputTuple = TableTuple(m_inputTable->schema()); m_targetTuple = TableTuple(m_targetTable->schema()); m_partitionColumn = m_targetTable->partitionColumn(); m_partitionColumnIsString = false; if (m_partitionColumn != -1) { if (m_targetTable->schema()->columnType(m_partitionColumn) == voltdb::VALUE_TYPE_VARCHAR) { m_partitionColumnIsString = true; } } return true; }
// ---------------------------------------------------- // Serialization Functions // ---------------------------------------------------- AbstractPlanNode* AbstractPlanNode::fromJSONObject(Object &obj, const catalog::Database *catalog_db) { Value typeValue = find_value(obj, "PLAN_NODE_TYPE"); if (typeValue == Value::null) { throw SerializableEEException(VOLT_EE_EXCEPTION_TYPE_EEEXCEPTION, "AbstractPlanNode::fromJSONObject:" " PLAN_NODE_TYPE value is null"); } string typeString = typeValue.get_str(); AbstractPlanNode* node = plannodeutil::getEmptyPlanNode(stringToPlanNode(typeString)); Value idValue = find_value(obj, "ID"); if (idValue == Value::null) { delete node; throw SerializableEEException(VOLT_EE_EXCEPTION_TYPE_EEEXCEPTION, "AbstractPlanNode::fromJSONObject:" " ID value is null"); } node->m_planNodeId = (int32_t) idValue.get_int(); VOLT_TRACE("Initializing PlanNode %s", node->debug().c_str()); Value inlineNodesValue = find_value(obj,"INLINE_NODES"); if (inlineNodesValue == Value::null) { delete node; throw SerializableEEException(VOLT_EE_EXCEPTION_TYPE_EEEXCEPTION, "AbstractPlanNode::fromJSONObject:" " INLINE_NODES value is null"); } Array inlineNodes = inlineNodesValue.get_array(); for (int ii = 0; ii < inlineNodes.size(); ii++) { AbstractPlanNode* newNode = NULL; try { Object obj = inlineNodes[ii].get_obj(); newNode = AbstractPlanNode::fromJSONObject(obj, catalog_db); } catch (SerializableEEException &ex) { delete newNode; delete node; throw; } // todo: if this throws, new Node can be leaked. // As long as newNode is not NULL, this will not throw. node->addInlinePlanNode(newNode); VOLT_TRACE("Adding inline PlanNode %s for %s", newNode->debug().c_str(), node->debug().c_str()); } Value parentNodeIdsValue = find_value(obj, "PARENT_IDS"); if (parentNodeIdsValue == Value::null) { delete node; throw SerializableEEException(VOLT_EE_EXCEPTION_TYPE_EEEXCEPTION, "AbstractPlanNode::fromJSONObject:" " PARENT_IDS value is null"); } Array parentNodeIdsArray = parentNodeIdsValue.get_array(); for (int ii = 0; ii < parentNodeIdsArray.size(); ii++) { int32_t parentNodeId = (int32_t) parentNodeIdsArray[ii].get_int(); node->m_parentIds.push_back(parentNodeId); } Value childNodeIdsValue = find_value(obj, "CHILDREN_IDS"); if (childNodeIdsValue == Value::null) { delete node; throw SerializableEEException(VOLT_EE_EXCEPTION_TYPE_EEEXCEPTION, "AbstractPlanNode::fromJSONObject:" " CHILDREN_IDS value is null"); } Array childNodeIdsArray = childNodeIdsValue.get_array(); for (int ii = 0; ii < childNodeIdsArray.size(); ii++) { int32_t childNodeId = (int32_t) childNodeIdsArray[ii].get_int(); node->m_childIds.push_back(childNodeId); } Value outputColumnsValue = find_value(obj, "OUTPUT_COLUMNS"); if (outputColumnsValue == Value::null) { delete node; throw SerializableEEException(VOLT_EE_EXCEPTION_TYPE_EEEXCEPTION, "AbstractPlanNode::loadFromJSONObject:" " Can't find OUTPUT_COLUMNS value"); } Array outputColumnsArray = outputColumnsValue.get_array(); for (int ii = 0; ii < outputColumnsArray.size(); ii++) { Value outputColumnValue = outputColumnsArray[ii]; PlanColumn outputColumn = PlanColumn(outputColumnValue.get_obj()); node->m_outputColumnGuids.push_back(outputColumn.getGuid()); } try { node->loadFromJSONObject(obj, catalog_db); } catch (SerializableEEException &ex) { delete node; throw; } return node; }
bool UpdateExecutor::p_init(AbstractPlanNode* abstract_node, TempTableLimits* limits) { VOLT_TRACE("init Update Executor"); m_node = dynamic_cast<UpdatePlanNode*>(abstract_node); assert(m_node); assert(m_node->getTargetTable()); assert(m_node->getInputTables().size() == 1); m_inputTable = dynamic_cast<TempTable*>(m_node->getInputTables()[0]); //input table should be temptable assert(m_inputTable); m_targetTable = dynamic_cast<PersistentTable*>(m_node->getTargetTable()); //target table should be persistenttable assert(m_targetTable); assert(m_node->getTargetTable()); setDMLCountOutputTable(limits); AbstractPlanNode *child = m_node->getChildren()[0]; ProjectionPlanNode *proj_node = NULL; if (NULL == child) { VOLT_ERROR("Attempted to initialize update executor with NULL child"); return false; } PlanNodeType pnt = child->getPlanNodeType(); if (pnt == PLAN_NODE_TYPE_PROJECTION) { proj_node = dynamic_cast<ProjectionPlanNode*>(child); } else if (pnt == PLAN_NODE_TYPE_SEQSCAN || pnt == PLAN_NODE_TYPE_INDEXSCAN) { proj_node = dynamic_cast<ProjectionPlanNode*>(child->getInlinePlanNode(PLAN_NODE_TYPE_PROJECTION)); assert(NULL != proj_node); } vector<string> output_column_names = proj_node->getOutputColumnNames(); const vector<string> &targettable_column_names = m_targetTable->getColumnNames(); /* * The first output column is the tuple address expression and it isn't part of our output so we skip * it when generating the map from input columns to the target table columns. */ for (int ii = 1; ii < output_column_names.size(); ii++) { for (int jj=0; jj < targettable_column_names.size(); ++jj) { if (targettable_column_names[jj].compare(output_column_names[ii]) == 0) { m_inputTargetMap.push_back(pair<int,int>(ii, jj)); break; } } } assert(m_inputTargetMap.size() == (output_column_names.size() - 1)); m_inputTargetMapSize = (int)m_inputTargetMap.size(); m_inputTuple = TableTuple(m_inputTable->schema()); m_targetTuple = TableTuple(m_targetTable->schema()); m_partitionColumn = m_targetTable->partitionColumn(); m_partitionColumnIsString = false; if (m_partitionColumn != -1) { if (m_targetTable->schema()->columnType(m_partitionColumn) == VALUE_TYPE_VARCHAR) { m_partitionColumnIsString = true; } } // determine which indices are updated by this executor // iterate through all target table indices and see if they contain // tables mutated by this executor std::vector<TableIndex*> allIndexes = m_targetTable->allIndexes(); BOOST_FOREACH(TableIndex *index, allIndexes) { bool indexKeyUpdated = false; BOOST_FOREACH(int colIndex, index->getColumnIndices()) { std::pair<int, int> updateColInfo; // needs to be here because of macro failure BOOST_FOREACH(updateColInfo, m_inputTargetMap) { if (updateColInfo.second == colIndex) { indexKeyUpdated = true; break; } } if (indexKeyUpdated) break; } if (indexKeyUpdated) { m_indexesToUpdate.push_back(index); } }