| /********************************************************************** |
| // @@@ START COPYRIGHT @@@ |
| // |
| // Licensed to the Apache Software Foundation (ASF) under one |
| // or more contributor license agreements. See the NOTICE file |
| // distributed with this work for additional information |
| // regarding copyright ownership. The ASF licenses this file |
| // to you under the Apache License, Version 2.0 (the |
| // "License"); you may not use this file except in compliance |
| // with the License. You may obtain a copy of the License at |
| // |
| // http://www.apache.org/licenses/LICENSE-2.0 |
| // |
| // Unless required by applicable law or agreed to in writing, |
| // software distributed under the License is distributed on an |
| // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY |
| // KIND, either express or implied. See the License for the |
| // specific language governing permissions and limitations |
| // under the License. |
| // |
| // @@@ END COPYRIGHT @@@ |
| **********************************************************************/ |
| /* -*-C++-*- |
| ****************************************************************************** |
| * |
| * File: Rel3GL.cpp |
| * Description: PSM/3GL operators. |
| * |
| * Created: 12/8/97 |
| * Language: C++ |
| * |
| * |
| ****************************************************************************** |
| */ |
| |
| #include "AllRelExpr.h" |
| #include "AllItemExpr.h" |
| #include "CmpContext.h" |
| #include "GroupAttr.h" |
| #include "NormWA.h" |
| #include "opt.h" |
| #include "PhyProp.h" |
| #include "BindWA.h" |
| #include "Collections.h" |
| |
| ////////////////////////////////////////////////////////////////////////// |
| // |
| // CompoundStmt methods. |
| // |
| ////////////////////////////////////////////////////////////////////////// |
| |
| RelExpr *CompoundStmt::bindNode(BindWA *bindWA) |
| { |
| if (nodeIsBound()) |
| return this; |
| |
| // Bind the children. |
| // rowset's modifyTree and modifyTupleNode rely on finding HostArraysArea |
| // in bindWA. |
| for (Int32 i = 0; i < getArity(); i++) { |
| if (child(i)) { |
| // If doing a non-first child and the operator is |
| // NOT one in which values/names can flow from one scope |
| // the sibling scope, then we must clear the current RETDesc |
| // (so as to disallow the illegal query in the Binder internals document, |
| // section 1.5.3, also in TEST028). |
| if (i && !getOperator().match(REL_ANY_TSJ)) |
| bindWA->getCurrentScope()->setRETDesc(NULL); |
| |
| if (child(i)->getOperatorType() == REL_ROOT) |
| // each RelRoot child of a compoundstmt has its own hostArraysArea. |
| // tell bindWA about it. |
| bindWA->setHostArraysArea |
| (((RelRoot*)getChild(i))->getHostArraysArea()); |
| |
| child(i) = child(i)->bindNode(bindWA); |
| if (bindWA->errStatus()) return this; |
| } |
| } |
| |
| if (bindWA->errStatus()) return this; |
| |
| // QSTUFF |
| synthPropForBindChecks(); |
| |
| if (getGroupAttr()->isStream()){ |
| *CmpCommon::diags() << DgSqlCode(-4200); |
| bindWA->setErrStatus(); |
| return this; |
| } |
| if (getGroupAttr()->isEmbeddedUpdateOrDelete() || |
| getGroupAttr()->isEmbeddedInsert()){ |
| *CmpCommon::diags() << DgSqlCode(-4201) |
| << DgString0(getGroupAttr()->getOperationWithinGroup()); |
| bindWA->setErrStatus(); |
| return this; |
| } |
| // QSTUFF |
| |
| |
| AssignmentStHostVars *ptrAssign = bindWA->getAssignmentStArea()->getAssignmentStHostVars(); |
| // Setup the RETDesc. |
| RETDesc *leftTable = child(0)->getRETDesc(); |
| RETDesc *rightTable = child(1)->getRETDesc(); |
| |
| leftTable = new (bindWA->wHeap()) RETDesc(bindWA, *leftTable); |
| rightTable = new (bindWA->wHeap()) RETDesc(bindWA, *rightTable); |
| |
| RETDesc *resultTable = new (bindWA->wHeap()) RETDesc(bindWA); |
| |
| if (!ptrAssign) { |
| |
| resultTable->addColumns(bindWA, *leftTable); |
| resultTable->addColumns(bindWA, *rightTable); |
| setRETDesc(resultTable); |
| bindWA->getCurrentScope()->setRETDesc(resultTable); |
| |
| // Bind the base class. |
| return bindSelf(bindWA); |
| } |
| |
| // In case we have assignment statements, we want our RetDesc to contain |
| // only the columns that will appear in the select list, so we examine |
| // each column to return to see if it is in the AssignmentStHostVars area. |
| // Note: it is very important that the columns in the resultTable appear |
| // in the same order as they are in the list pointed by ptrAssign |
| |
| ColumnDescList leftColumnList = *(leftTable->getColumnList()); |
| ColumnDescList rightColumnList = *(rightTable->getColumnList()); |
| |
| while (ptrAssign) { |
| |
| Int32 found = FALSE; |
| UInt32 j = 0; |
| for (j = 0; j < leftColumnList.entries(); j++) { |
| if (ptrAssign->currentValueId() == leftColumnList[j]->getValueId()) { |
| resultTable->addColumn(bindWA, leftColumnList[j]->getColRefNameObj(), |
| leftColumnList[j]->getValueId(),USER_COLUMN, |
| leftColumnList[j]->getHeading()); |
| found = TRUE; |
| break; |
| } |
| } |
| |
| if (!found) { |
| for (j = 0; j < rightColumnList.entries(); j++) { |
| if (ptrAssign->currentValueId() == rightColumnList[j]->getValueId()) { |
| resultTable->addColumn(bindWA, rightColumnList[j]->getColRefNameObj(), |
| rightColumnList[j]->getValueId(),USER_COLUMN, |
| rightColumnList[j]->getHeading()); |
| break; |
| } |
| } |
| } |
| |
| ptrAssign = ptrAssign->next(); |
| |
| } |
| |
| setRETDesc(resultTable); |
| bindWA->getCurrentScope()->setRETDesc(resultTable); |
| |
| // Bind the base class. |
| return bindSelf(bindWA); |
| |
| } // CompoundStmt::bindNode() |
| |
| |
| void CompoundStmt::transformNode(NormWA &normWARef, |
| ExprGroupId & locationOfPointerToMe) |
| { |
| CMPASSERT(this == locationOfPointerToMe); |
| |
| if (nodeIsTransformed()) |
| return; |
| |
| ValueIdSet availableValues = getGroupAttr()->getCharacteristicInputs(); |
| |
| child(0)->getGroupAttr()->addCharacteristicInputs(availableValues); |
| |
| child(0)->transformNode(normWARef, child(0)); |
| |
| // Values may flow from left to right |
| availableValues += child(0)->getGroupAttr()->getCharacteristicOutputs(); |
| |
| child(1)->getGroupAttr()->addCharacteristicInputs(availableValues); |
| |
| child(1)->transformNode(normWARef, child(1)); |
| |
| // Pull up predicates and recompute the required inputs of children. |
| pullUpPreds(); |
| |
| // Transform the selection predicates. |
| transformSelectPred(normWARef, locationOfPointerToMe); |
| |
| markAsTransformed(); |
| |
| } // CompoundStmt::transformNode |
| |
| void |
| CompoundStmt::pushdownCoveredExpr(const ValueIdSet &outputExpr, |
| const ValueIdSet &newExternalInputs, |
| ValueIdSet &predicatesOnParent, |
| const ValueIdSet *setOfValuesReqdByParent, |
| Lng32 childIndex |
| ) |
| { |
| ValueIdSet requiredValues; |
| if (setOfValuesReqdByParent) |
| requiredValues = *setOfValuesReqdByParent; |
| |
| // We may flow values from left to right |
| requiredValues += child(1)->getGroupAttr()->getCharacteristicInputs(); |
| |
| // Push expressions computable by children, updating their group attrs. |
| RelExpr::pushdownCoveredExpr(outputExpr, |
| newExternalInputs, |
| predicatesOnParent, |
| &requiredValues); |
| } |
| |
| |
| RelExpr* CompoundStmt::normalizeNode(NormWA & normWARef) |
| { |
| if (nodeIsNormalized()) |
| return this; |
| |
| // Push expressions computable by children, updating their group attrs. |
| pushdownCoveredExpr(getGroupAttr()->getCharacteristicOutputs(), |
| getGroupAttr()->getCharacteristicInputs(), |
| selectionPred()); |
| |
| // Normalize children |
| for (Int32 i = 0; i < getArity(); i++) |
| { |
| child(i) = child(i)->normalizeNode(normWARef); |
| } |
| |
| fixEssentialCharacteristicOutputs(); |
| |
| markAsNormalized(); |
| |
| return this; |
| |
| } // CompoundStmt::normalizeNode |
| |
| void CompoundStmt::pullUpPreds() |
| { |
| for (Int32 i = 0; i < getArity(); i++) { |
| child(i)->pullUpPreds(); |
| } |
| |
| } // CompoundStmt::pullUpPreds |
| |
| void CompoundStmt::rewriteNode(NormWA & normWARef) |
| { |
| // Rewrite children |
| for (Int32 i = 0; i < getArity(); i++) |
| { |
| child(i)->rewriteNode(normWARef); |
| } |
| |
| // Rewrite the predicates. |
| if (selectionPred().normalizeNode(normWARef)) |
| { |
| } |
| |
| // Rewrite the Group Attributes. |
| getGroupAttr()->normalizeInputsAndOutputs(normWARef); |
| |
| } // CompoundStmt::rewriteNode |
| |
| void CompoundStmt::recomputeOuterReferences() |
| { |
| // --------------------------------------------------------------------- |
| // Delete all those input values that are no longer referenced on |
| // this operator because the predicates that reference them have |
| // been pulled up. |
| // --------------------------------------------------------------------- |
| ValueIdSet outerRefs = getGroupAttr()->getCharacteristicInputs(); |
| |
| // Remove from outerRefs those valueIds that are not needed |
| // by my selection predicate |
| selectionPred().weedOutUnreferenced(outerRefs); |
| |
| // Add to outerRefs those that my children need. |
| Int32 arity = getArity(); |
| for (Int32 i = 0; i < arity; i++) |
| { |
| outerRefs += child(i).getPtr()->getGroupAttr()->getCharacteristicInputs(); |
| } |
| |
| // If some of those values are produced by the left child, then we do not |
| // need them from above |
| outerRefs -= child(0)->getGroupAttr()->getCharacteristicOutputs(); |
| |
| // set my Character Inputs to this new minimal set. |
| getGroupAttr()->setCharacteristicInputs(outerRefs); |
| } // RelExpr::recomputeOuterReferences() |
| |
| RelExpr *CompoundStmt::copyTopNode(RelExpr *derivedNode, CollHeap* outHeap) |
| { |
| RelExpr *result; |
| |
| if ( derivedNode == NULL) |
| result = new (outHeap) CompoundStmt(NULL, NULL, getOperatorType(), outHeap); |
| else |
| result = (CompoundStmt*) derivedNode; |
| |
| return RelExpr::copyTopNode(result, outHeap); |
| |
| } // CompoundStmt::copyTopNode |
| |
| |
| void CompoundStmt::enterVEGRegion(NormWA &normWARef, Int32 id, NABoolean create) |
| { |
| if (child(id)->getOperatorType() != REL_COMPOUND_STMT) |
| { |
| if (create) |
| normWARef.allocateAndSetVEGRegion(IMPORT_AND_EXPORT, this, id); |
| else |
| normWARef.locateAndSetVEGRegion(this, id); |
| } |
| |
| } // CompoundStmt::enterVEGRegion |
| |
| |
| void CompoundStmt::leaveVEGRegion(NormWA & normWARef, Int32 id) |
| { |
| if (child(id)->getOperatorType() != REL_COMPOUND_STMT) |
| normWARef.restoreOriginalVEGRegion(); |
| |
| } // CompoundStmt::leaveVEGRegion |
| |
| // *********************************************************************** |
| // Methods for class AssignmentStHostVars. |
| // This class is used by assignment statements in compound statements. |
| // A list of this type is kept in class BindWA so that it is globally |
| // accessible at binding time. This class mantains a current and previous |
| // valueId, so that when we find SET <var> = <select statement>, we update |
| // the value id of <var> to that returned by the select statement. See |
| // assignment statements internal spec for details. |
| // *********************************************************************** |
| |
| // Adds var to end of this list with new value id; if it is already there, it |
| // updates the value id |
| AssignmentStHostVars & AssignmentStHostVars::addVar(HostVar *var, const ValueId &id) |
| { |
| AssignmentStHostVars *ptr, *current; |
| |
| current = ptr = this; |
| |
| // Empty list |
| if (!(ptr->var())) { |
| ptr->var() = var; |
| ptr->setCurrentValueId(id); |
| return *ptr; |
| } |
| |
| // Search for variable name and update value id |
| // Note that ptr can become null when this loop ends |
| while (ptr && ptr->var()) { |
| |
| if (ptr->var()->getName() == var->getName()) { |
| |
| // New value id must replace the latest one |
| ptr->setCurrentValueId(id); |
| return *ptr; |
| } |
| |
| current = ptr; |
| ptr = ptr->next_; |
| } |
| |
| |
| // Now add variable to the end of the list |
| ptr = current->next_ = new (bindWA_->wHeap()) AssignmentStHostVars(bindWA_); |
| ptr->var() = var; |
| ptr->setCurrentValueId(id); |
| return *ptr; |
| } |
| |
| // Update the latest value id of this host var. |
| void AssignmentStHostVars::setCurrentValueId(const ValueId &id) |
| { |
| |
| if (valueIds_.entries() == 0) { |
| valueIds_.insert(id); |
| return; |
| } |
| |
| AssignmentStArea * ptrArea = bindWA_->getAssignmentStArea(); |
| Union *ifNode = ptrArea->getCurrentIF(); |
| AssignmentStHostVars *varList = NULL; |
| |
| // If we are not inside an IF statement, then we replace the |
| // value id this variable has with the one provided in the parameter |
| // to this function. |
| if (!ifNode) { |
| valueIds_[valueIds_.entries() - 1] = id; |
| return; |
| } |
| |
| // If we are within an IF statement, we must figure out if this is the |
| // first time we are setting the value id of this variable. If it is so, |
| // then we insert a new value id at the end of the list of value ids for |
| // this variable. Otherwise we overwrite the current value id |
| varList = ifNode->getCurrentList(bindWA_); |
| if (varList && (varList->findVar(var()->getName()))) { |
| valueIds_[valueIds_.entries() - 1] = id; |
| } |
| else { |
| valueIds_.insert(id); |
| } |
| } |
| |
| // Gets current value id |
| const ValueId AssignmentStHostVars::currentValueId() |
| { |
| if (valueIds_.entries() > 0) { |
| return valueIds_[valueIds_.entries() - 1]; |
| } |
| else { |
| return NULL_VALUE_ID; |
| } |
| } |
| |
| // Gets the variable |
| HostVar *& AssignmentStHostVars::var() |
| { |
| return var_; |
| } |
| |
| // Finds the variable whose name is given |
| AssignmentStHostVars * AssignmentStHostVars::findVar(const NAString & name) |
| { |
| |
| AssignmentStHostVars *ptr = this; |
| |
| while (ptr && ptr->var_) { |
| |
| // We must consider the cases when the variable has or does not have an indicator |
| if ((ptr->var_->getName() == name) || |
| ((ptr->var_->getName() + " " + ptr->var_->getIndName()) == name)) { |
| return ptr; |
| } |
| |
| ptr = ptr->next_; |
| } |
| |
| return NULL; |
| |
| } |
| |
| // When we reach a Root node that contains a list of host variables on the |
| // left hand side of an assignment statement (assignmentStTree_), we update |
| // the value ids of such variables with the value ids returned from the |
| // subtree below the Root node. This function is called in RelRoot::bindNode |
| NABoolean AssignmentStHostVars::updateValueIds(const ValueIdList &returnedList, ItemExpr *listInRootNode) |
| { |
| UInt32 listSize = 0; |
| ItemExpr *hostVarList = listInRootNode; |
| |
| // returnedList contains a list of the value ids returned from below the node we are in. |
| // listInRootNode contains a list of the variables being assigned to |
| CollIndex i = 0; |
| for (i = 0; i < returnedList.entries() && hostVarList; i++) { |
| ItemExpr *targetExpr = hostVarList->child(0); |
| CMPASSERT(targetExpr); |
| |
| // |
| // Check that the operands are compatible. |
| // |
| ValueId sourceId = returnedList[listSize]; |
| const NAType& targetType = *(((HostVar *) targetExpr)->getType()); |
| sourceId.coerceType(targetType); |
| const NAType& sourceType = sourceId.getType(); |
| |
| if (NOT targetType.isCompatible(sourceType)) { |
| |
| // Relaxing Characet Data Type matching rule. |
| NABoolean relax = FALSE; |
| if ( (targetType.getTypeQualifier() == NA_CHARACTER_TYPE) && |
| (sourceType.getTypeQualifier() == NA_CHARACTER_TYPE) && |
| ((const CharType&)targetType).getCharSet() == CharInfo::UNICODE && |
| ((const CharType&)sourceType).getCharSet() == CharInfo::ISO88591 |
| ) { |
| relax = TRUE; |
| } |
| |
| if ( !relax ) { |
| if (CmpCommon::getDefault(IMPLICIT_DATETIME_INTERVAL_HOSTVAR_CONVERSION) == DF_OFF) |
| { |
| // Incompatible assignment from type $0~String0 to type $1~String1 |
| *CmpCommon::diags() << DgSqlCode(-30007) |
| << DgString0(sourceType.getTypeSQLname(TRUE /*terse*/)) |
| << DgString1(targetType.getTypeSQLname(TRUE /*terse*/)); |
| bindWA_->setErrStatus(); |
| return FALSE; |
| } |
| } |
| } |
| |
| // temp contains the variable; returnedList[listSize] contains its new value id |
| addVar((HostVar *) targetExpr, sourceId); |
| hostVarList = hostVarList->child(1); |
| listSize++; |
| } |
| |
| if (hostVarList || (listSize != returnedList.entries())) { |
| // Mismatch in number of variables in SET list and the returned list |
| *CmpCommon::diags() << DgSqlCode(-4094) |
| << DgInt0(listSize) << DgInt1(returnedList.entries()); |
| bindWA_->setErrStatus(); |
| return FALSE; |
| } |
| |
| // We keep track of all HostVars on the left side of the SET statement |
| // and that appear below the Root node |
| |
| AssignmentStArea *ptrArea = bindWA_->getAssignmentStArea(); |
| Union *currentIF = ptrArea->getCurrentIF(); |
| |
| // We get the list of host vars in the current IF node associated with the |
| // subtree (left or right) that we are currently visiting |
| if (currentIF) { |
| AssignmentStHostVars *listOfVarsInIF = currentIF->getCurrentList(bindWA_); |
| |
| listSize = 0; |
| hostVarList = listInRootNode; |
| |
| // And we insert into that list all the variables that have been assigned in this |
| // subtree |
| for (i = 0; i < returnedList.entries(); i++) { |
| if (hostVarList) { |
| ItemExpr *temp = hostVarList->child(0); |
| HostVar *var = (HostVar *) temp; |
| |
| // temp contains the variable; returnedList[listSize] contains its new value id |
| listOfVarsInIF->addToListInIF(var, returnedList[i]); |
| |
| // Get next variable |
| hostVarList = hostVarList->child(1); |
| } |
| } |
| } |
| |
| return TRUE; |
| } |
| |
| // To know if there are rowsets in the given list |
| NABoolean AssignmentStHostVars::containsRowsets(ItemExpr * list) |
| { |
| |
| if (!list) { |
| return FALSE; |
| } |
| |
| while (list) { |
| |
| if (list->getChild(0) && list->getChild(0)->getOperatorType() == ITM_HOSTVAR) { |
| |
| HostVar *hostVar = (HostVar *) (list->getChild(0)); |
| |
| if (hostVar->getType()->getTypeQualifier() == NA_ROWSET_TYPE) { |
| return TRUE; |
| } |
| } |
| |
| list = list->child(1); |
| } |
| |
| return FALSE; |
| } |
| |
| |
| // Updates listOfVars_ when we exit a child of Union node at binding time. For |
| // each host variable, it determines whether its value id is no longer |
| // valid outside the branch of the IF statement we are exiting, and removes it if so. |
| // In this way, the value id this variable had before entering the branch of the IF |
| // statement will be the current one |
| void AssignmentStArea::removeLastValueIds(AssignmentStHostVars * listInUnion, Union * node) |
| { |
| |
| while (listInUnion && (listInUnion->var())) { |
| |
| // Get the variable in the Union node |
| HostVar * var = listInUnion->var(); |
| |
| // Now find it in the global list of Host variables |
| AssignmentStHostVars * theVar = listOfVars_->findVar(var->getName()); |
| |
| // The last value id of theVar is no longer valid |
| CMPASSERT(theVar); |
| theVar->removeLastValueId(); |
| |
| listInUnion = listInUnion->next(); |
| |
| } |
| } |
| |
| |
| |
| // Adds var to the given list with new value id; if it is already there, it |
| // overwrites the value. This functio is used only for lists contained in |
| // an IF node (i.e. left list_ and rightList_ in class Union). |
| AssignmentStHostVars & AssignmentStHostVars::addToListInIF(HostVar *var, const ValueId &id) |
| { |
| AssignmentStHostVars *ptr = NULL; |
| AssignmentStHostVars *previous = NULL; |
| |
| ValueIdList list; |
| list.clear(); |
| list.insert(id); |
| |
| ptr = this; |
| NABoolean found = FALSE; |
| |
| while (ptr && ptr->var_) { |
| |
| if (ptr->var_->getName() == var->getName()) { |
| found = TRUE; |
| break; |
| } |
| |
| previous = ptr; |
| ptr = ptr->next_; |
| } |
| |
| if (found) { |
| ptr->valueIds() = list; |
| } |
| else { |
| if (ptr) { |
| ptr->var_ = var; |
| ptr->valueIds_ = list; |
| } |
| else { |
| ptr = new (bindWA_->wHeap()) AssignmentStHostVars(bindWA_); |
| ptr->var_ = var; |
| ptr->valueIds_ = list; |
| previous->next_ = ptr; |
| ptr->next_ = NULL; |
| } |
| } |
| |
| return *ptr; |
| } |
| |
| |
| // Adds all the hostvars and their associated value ids from the AssignmentStHostVars |
| // that is passed as an argument to this method to the AssignmentStHostVars pointed to |
| // this pointer. |
| void AssignmentStHostVars::addAllToListInIF(AssignmentStHostVars * copyFromList) |
| { |
| |
| while (copyFromList) { |
| HostVar *var = copyFromList->var(); |
| ValueId id = copyFromList->currentValueId(); |
| addToListInIF(var, id); |
| copyFromList = copyFromList->next(); |
| } |
| } |