// *************************************************************************** // Is this column from a table that has an LOJ parent? // *************************************************************************** NABoolean DescriptorDetails::isColumnFromLojTable(const QRColumnPtr col) { const NAString& tableID = col->getTableID(); const QRElementPtr tableElem = getElementForID(tableID); assertLogAndThrow(CAT_MATCHTST_MVDETAILS, LL_MVQR_FAIL, tableElem, QRLogicException, "Table not found in JBB."); const QRTablePtr table = tableElem->downCastToQRTable(); return table->hasLOJParent(); }
void QRGroupLattice::insert(QRJoinSubGraphMapPtr map, const QRJBBPtr jbb) { QRTRACER("QRGroupLattice::insert()"); LatticeKeyList keyList; NABoolean ok = getGroupingLatticeKeys(keyList, map, map->getMVDetails(), jbb, FALSE, TRUE); assertLogAndThrow(CAT_GRP_LATTCE_INDX, LL_ERROR, ok, QRLogicException, "getGroupingLatticeKeys() failed in Insert mode (Inserting a multi-JBB MV?)."); lattice_->addMV(keyList, map); dumpLattice("1"); }
Visitor::VisitResult RefFinderVisitor::visit(QRElementPtr caller) { // Elements that don't reference other elements, should reference // themselves (the default value). if (caller->getRef() == "") return VR_Continue; QRElementPtr elem = descDetails_->getElementForID(caller->getRef()); assertLogAndThrow(CAT_MATCHTST_MVDETAILS, LL_MVQR_FAIL, elem != NULL, QRLogicException, "Element is referencing a non-existing element."); caller->setReferencedElement(elem); return VR_Continue; }
// *************************************************************************** // Initialize internal data structures. // *************************************************************************** void MVDetails::init(CollHeap* heap) { // Init the superclass DescriptorDetails::init(heap); const NAPtrList<QRJBBPtr>& jbbs = descriptor_->getJbbList(); assertLogAndThrow(CAT_MATCHTST_MVDETAILS, LL_MVQR_FAIL, jbbs.entries() == 1, QRLogicException, "Support single JBB MVs only for now."); // Init the shortcut to the first (and only) JBB in MV descriptors. jbbDetails_ = jbbDetailsList_[0]; // TBD - When we support multi-JBB MVs, we need to loop over the jbb list. const QRJBBPtr jbb = jbbs[0]; // Initialize the several hash tables that map output list elements. initOutputs(jbb, heap); // Initialize the hash of tables in the descriptor. initTables(jbb, heap); }
// *************************************************************** // * Prepare the object name to handle any double quotes // * within the delimited name // *************************************************************** void QRQueriesImpl::fixupDelimitedName(const char * inName, char * outName) { // Prepare the object name to handle any double quotes // within the delimited name assertLogAndThrow(CAT_SQL_COMP_QR_COMMON, LL_ERROR, inName!=NULL && outName!=NULL, QRLogicException, "Bad input parameters to fixupDelimitedName()."); Int32 j = 0; short len = strlen(inName); for (Int32 i = 0; i < len; i++) { if (inName[i] == '"') outName[j++] = '"'; outName[j++] = inName[i]; } outName[j] = '\0'; } // End of fixupDelimitedName
// *************************************************************** // * Obtain a catalog name for the catalog UID specified. // * // * A static SQL query is used to obtain the // * CATALOG name from the system metadata. // *************************************************************** const NAString* QRQueriesImpl::getCatalogName(Int64 catalogUID) { NAString* catalogName = new(heap_) NAString(heap_); Lng32 sqlCode = queries_->openCatalogName(catalogUID); assertLogAndThrow1(CAT_SQL_COMP_QR_COMMON, LL_ERROR, sqlCode==SQL_Success, QRDatabaseException, "Error %d performing get catalog name.", sqlCode); sqlCode = queries_->fetchCatalogName(*catalogName); // SQL_Eof means the catalog UID is wrong. assertLogAndThrow(CAT_SQL_COMP_QR_COMMON, LL_ERROR, sqlCode != SQL_Eof, QRDatabaseException, "Unable to obtain catalog name for UID."); // Otherwise its an SQL error. assertLogAndThrow1(CAT_SQL_COMP_QR_COMMON, LL_ERROR, sqlCode==SQL_Success, QRDatabaseException, "Error %d performing get catalog name.", sqlCode); // Always close to free up resources queries_->closeCatalogName(); return catalogName; } // End of getCatalogName
//***************************************************************************** // Implement the end of the state machine algorithm, closing the last segment. //***************************************************************************** void SelfJoinHandler::doneAddingTables() { // Which state in the state machine are we in? switch (state_) { case ST_SINGLE: case ST_UNIQUE: addSegment(SelfJoinSegment::UNIQUE_TABLE_SEGMENT); break; case ST_SELFJOIN: addSegment(SelfJoinSegment::SELF_JOIN_SEGMENT); break; case ST_START: case ST_END: assertLogAndThrow(CAT_MVMEMO_JOINGRAPH, LL_MVQR_FAIL, FALSE, QRLogicException, "calling SelfJoinHandler::end() in ST_START or ST_END state."); break; } state_ = ST_END; }
// *************************************************************************** // Initialize the several hash tables that map output list elements: // outputByIDHash_ - Using the ID of the output item as the key. // outputByColumnName_ - Using the "ID Qualified" column name as the key. // outputByExprText_ - Using the expression text as the key. // outputByInputColumns_ - Using the name of expression input column as the key. // *************************************************************************** void MVDetails::initOutputs(const QRJBBPtr jbb, CollHeap* heap) { // Match the output elements with their names. const QROutputListPtr outputList = jbb->getOutputList(); assertLogAndThrow(CAT_MATCHTST_MVDETAILS, LL_MVQR_FAIL, outputList != NULL, QRLogicException, "Output list is null."); // For each output list element for (CollIndex i=0; i<outputList->entries(); i++) { QROutputPtr output = (*outputList)[i]; // Set the ordinal number of the output element. output->setColPos(i+1); // Get the output item (column or expression). QRElementPtr outputItem = output->getOutputItem()->getReferencedElement(); const NAString& id = outputItem->getID(); // Insert the ID of the output item (whatever it is) into the ID hash. outputByIDHash_.insert(&id, output); // OK, now lets check what type of output item it is. switch (outputItem->getElementType()) { case ET_Column: { // If its an output column, add it to the output column by name hash. const QRColumnPtr col = outputItem->downCastToQRColumn(); const QRElementPtr tableElem = getElementForID(col->getTableID()); NABoolean hasLOJParent = tableElem->downCastToQRTable()->hasLOJParent(); if (!isFromJoin(col) || hasLOJParent) { // Compute the "ID Qualified" column name as the key. const NAString* idQualifiedColName = calcIDQualifiedColumnName(col->getTableID(), col->getColumnName(), heap); // Insert into the hash table. outputByColumnName_.insert(idQualifiedColName, output); } else { // If its an equality set, insert all the participating columns // as pointing to the same MV output column. const QRJoinPredPtr jp = getJoinPred(col); const QRJoinPredPtr joinPredElement = jp->downCastToQRJoinPred(); addEqualitySet(joinPredElement, FALSE, output, heap); } break; } case ET_JoinPred: { assertLogAndThrow(CAT_MATCHTST_MVDETAILS, LL_MVQR_FAIL, (outputItem->getElementType() == ET_JoinPred), QRLogicException, "Unexpected JoinPred element."); // If its an equality set, insert all the participating columns // as pointing to the same MV output column. const QRJoinPredPtr joinPredElement = outputItem->downCastToQRJoinPred(); addEqualitySet(joinPredElement, FALSE, output, heap); break; } case ET_Expr: { // This is an expression. Insert the expression text into the expression hash. const QRExprPtr expr = outputItem->downCastToQRExpr(); const NAString& exprText = expr->getExprText(); outputByExprText_.insert(&exprText, output); QRLogger::log(CAT_MATCHTST_MVDETAILS, LL_DEBUG, "Adding output expression: %s.", exprText.data()); // Also map the expressions's input columns to it. // This call to getInitColumns() also inits the input list using the // right heap. const ElementPtrList& inputs = expr->getInputColumns(heap); if (inputs.entries() > 0) { for (CollIndex j=0; j<inputs.entries(); j++) { QRElementPtr inputElem = inputs[j]->getReferencedElement(); if (inputElem->getElementType() == ET_Column) { QRColumnPtr inputCol = inputElem->downCastToQRColumn(); // Compute the "ID Qualified" column name as the key. const NAString* idQualifiedColName = calcIDQualifiedColumnName(inputCol->getTableID(), inputCol->getColumnName(), heap); outputByInputColumns_.insert(idQualifiedColName, output); } else { QRJoinPredPtr inputJoinPred = inputElem->downCastToQRJoinPred(); addEqualitySet(inputJoinPred, TRUE, output, heap); } } } // Check if this is the COUNT(*) function. QRExplicitExprPtr rootExpr = expr->getExprRoot(); if (rootExpr->getElementType() == ET_Function) { QRFunctionPtr func = rootExpr->downCastToQRFunction(); if (func->getAggregateFunc() == QRFunction::AFT_COUNTSTAR) { // Found it, now remember it for later use. // If a COUNT(*) equivalent function has already been found // (see below) - overwrite it. countStar_ = output; } else if (countStar_ == NULL && func->isCountStarEquivalent(heap) ) { // Well, we didn't find a COUNT(*) yet, but we found a COUNT(a) // and the input column is NOT NOLL, we can use it as a COUNT(*) column. countStar_ = output; } } } // end of case ET_Expr break; } // switch on element type } // for on output list elements } // MVDetails::initOutputs()
// *************************************************************************** // *************************************************************************** void JBBDetails::initRangePreds(CollHeap* heap) { // Initialize the Range predicates. const QRRangePredListPtr rangePreds = jbbDesc_->getHub()->getRangePredList(); if (rangePreds==NULL || rangePreds->entries() == 0) { hasNoRangePredicates_ = TRUE; return; } for (CollIndex k=0; k<rangePreds->entries(); k++) { const QRRangePredPtr rangePred = (*rangePreds)[k]; const QRElementPtr rangeElem = rangePred->getRangeItem()->getReferencedElement(); if (rangeElem->getElementType() == ET_Column && rangePred->isSingleValue()) { constColumns_.insert(&rangeElem->getID()); } // For query descriptors, only check for const columns. if (!isAnMV_) continue; if (rangeElem->getElementType() == ET_Column) { const QRColumnPtr rangeColumn = rangeElem->downCastToQRColumn(); if (!descDetails_->isFromJoin(rangeColumn)) { // This column is NOT part of a join predicate. BaseTableDetailsPtr rangeTable = getBaseTableByID(rangeColumn->getTableID()); rangeTable->addRangePredicateOnColumn(rangePred, rangeColumn); } else { // This column IS part of a join predicate. // Insert the range pred for all the participating columns. const QRJoinPredPtr jp = descDetails_->getJoinPred(rangeColumn); const ElementPtrList& equalitySet = jp->getEqualityList(); for (CollIndex i=0; i<equalitySet.entries(); i++) { const QRElementPtr halfPred = equalitySet[i]; // Now lets look at the equality set members // Ignore members that are not simple columns. if (halfPred->getElementType() != ET_Column) continue; const QRColumnPtr eqSetColumn = halfPred->getReferencedElement()->downCastToQRColumn(); BaseTableDetailsPtr rangeTable = getBaseTableByID(eqSetColumn->getTableID()); rangeTable->addRangePredicateOnColumn(rangePred, eqSetColumn); } } } else if (rangeElem->getElementType() == ET_JoinPred) { assertLogAndThrow(CAT_MATCHTST_MVDETAILS, LL_MVQR_FAIL, (rangeElem->getElementType() == ET_JoinPred), QRLogicException, "Unexpected JoinPred element."); // If its an equality set, insert the range pred for all the participating columns. const QRJoinPredPtr joinPredElement = rangeElem->downCastToQRJoinPred(); const ElementPtrList& equalitySet = joinPredElement->getEqualityList(); for (CollIndex i=0; i<equalitySet.entries(); i++) { const QRElementPtr halfPred = equalitySet[i]; // Now lets look at the equality set members // Ignore members that are not simple columns. if (halfPred->getElementType() != ET_Column) continue; const QRColumnPtr eqSetColumn = halfPred->getReferencedElement()->downCastToQRColumn(); BaseTableDetailsPtr rangeTable = getBaseTableByID(eqSetColumn->getTableID()); rangeTable->addRangePredicateOnColumn(rangePred, eqSetColumn); } } else { // The range pred is on an expression. Insert it for all the expression's input columns. const QRExprPtr rangeExpr = rangeElem->downCastToQRExpr(); if (rangeExpr->getExprRoot()->containsAnAggregate(heap)) { hasHavingPredicates_ = TRUE; } // Verify we have a single input column // No input columns mean its a COUNT(*), which is OK too. const ElementPtrList& inputs = rangeExpr->getInputColumns(heap); assertLogAndThrow(CAT_MATCHTST_MVDETAILS, LL_MVQR_FAIL, (inputs.entries() <= 1), QRLogicException, "Range predicate expression must have a single input at most."); const NAString& predText = rangeExpr->getExprText(); // Look it up in the hash table: do we already have a range pred with this text? RangePredPtrList* predList = getRangePredsOnExpression(predText); if (predList == NULL) { // No, its the first one. Create a new list. predList = new(heap) RangePredPtrList(heap); // And insert it into the hash table. rangePredicates_.insert(&predText, predList); } // Insert the predicate into the pred list. predList->insert(rangePred); } } } // JBBDetails::init()
LatticeIndexablePtr QRGroupLattice::elementToKey(const QRElementPtr element, QRJoinSubGraphMapPtr map, DescriptorDetailsPtr queryDetails, NABoolean insertMode, NABoolean isRecursive) { QRElementPtr elem = element->getReferencedElement(); QRColumnPtr col = NULL; if (elem->getElementType() == ET_Column) { col = elem->downCastToQRColumn(); if (queryDetails->isFromJoin(col) && !isRecursive) { // This groupby element is a join pred. // During insert, this loop inserts the first column. // During search, try each equality set element until we find one that // appears in our array. const QRJoinPredPtr eqSet = queryDetails->getJoinPred(col); const ElementPtrList& equalityList = eqSet->getEqualityList(); if (insertMode) { // Insert mode - pick the first column of the equality set. col = equalityList[0]->downCastToQRColumn(); } else { // Search mode - pick the first equality set entry that is found in this LatticeIndex. for (CollIndex i=0; i<equalityList.entries(); i++) { QRElementPtr entry = equalityList[i]->getReferencedElement(); // Call recursively for each join pred column. LatticeIndexablePtr entryKey = elementToKey(entry, map, queryDetails, insertMode, TRUE); if (entryKey == NULL) continue; if (lattice_->contains(entryKey)) { // Found it in the lattice index. col = entry->downCastToQRColumn(); break; } } // for ( ) // If none of the entries was found - give up now. if (col == NULL) return NULL; } // if insert mode } // if JoinPred } // if Column NAString* key = NULL; if (col != NULL) { col = col->getReferencedElement()->downCastToQRColumn(); const NAString& tableID = col->getTableID(); Int32 Inx = map->getIndexForTable(tableID); if (Inx == -1) { assertLogAndThrow(CAT_GRP_LATTCE_INDX, LL_ERROR, !insertMode, QRLogicException, "Table index not found in Insert mode (Inserting a multi-JBB MV?)."); return NULL; } char buffer[4096+5]; sprintf(buffer, "%d.%s", Inx, col->getColumnName().data() ); key = new(heap_) NAString(buffer, heap_); } else { // This is an expression. // TBD We still do not handle expressions using columns from self-join tables. key = new(heap_) NAString(element->getSortName(), heap_); } // The reverseKeyHash should always use the most recent MV inserted. if (insertMode) { reverseKeyHash_.remove(key); reverseKeyHash_.insert(key, element); } return key; }
//***************************************************************************** // Add a table to the Self-Join analysis. // This method, together with doneAddingTables(), implement a state machine // with the following states: // ST_START: The start state. No open segments. // ST_SINGLE: Got the first table of a new segment, don't know segment type yet. // ST_UNIQUE: Last two tables were different. // ST_SELFJOIN: Last two tables were the same. // ST_END: We are done. // The full state machine diagram is in the MVQR IS document. //***************************************************************************** void SelfJoinHandler::addTable(JoinGraphTablePtr table) { const NAString& newTable = table->getName(); // This is the current table index. UInt32 currentIndex = table->getTempNumber(); if (currentIndex == -1) { // This subgraph generation was not yet started, // This must be the initial self-join check. currentIndex = table->getOrdinalNumber(); } // Is this table the same as the last one? // Skip the string comparison if the table is unique in the full join graph. NABoolean isDifferent = !table->isSelfJoinTable() || newTable != *lastTable_; // Which state in the state machine are we in? switch (state_) { case ST_START: segmentStart_ = currentIndex; state_ = ST_SINGLE; break; case ST_SINGLE: if (isDifferent) state_ = ST_UNIQUE; else state_ = ST_SELFJOIN; break; case ST_SELFJOIN: if (isDifferent) { // We have an open selfjoin segment and got a different table. // The selfjoin segment ends with the previous table, and the next // segment (which can be either type) starts with this table. addSegment(SelfJoinSegment::SELF_JOIN_SEGMENT); segmentStart_ = currentIndex; state_ = ST_SINGLE; } else { // do nothing. } break; case ST_UNIQUE: if (isDifferent) { // do nothing. } else { // OK, we have an open unique segment, and the new table is the same // as the last one. So actually, the open unique segment ended with the // table before the prevous one, and the new self-join segment started // with the previous table. segmentEnd_--; addSegment(SelfJoinSegment::UNIQUE_TABLE_SEGMENT); segmentStart_ = currentIndex-1; state_ = ST_SELFJOIN; } break; case ST_END: assertLogAndThrow(CAT_MVMEMO_JOINGRAPH, LL_MVQR_FAIL, FALSE, QRLogicException, "Adding a table to SelfJoinHandler in ST_END state."); break; } lastTable_ = &table->getName(); segmentEnd_ = currentIndex; }