short ProbeCache::codeGen(Generator *generator)
{
  ExpGenerator * exp_gen = generator->getExpGenerator();
  Space * space = generator->getSpace();

  MapTable * last_map_table = generator->getLastMapTable();

  ex_cri_desc * given_desc
    = generator->getCriDesc(Generator::DOWN);

  ex_cri_desc * returned_desc
    = new(space) ex_cri_desc(given_desc->noTuples() + 1, space);

  // cri descriptor for work atp has 5 entries:
  // entry #0 for const
  // entry #1 for temp
  // entry #2 for hash value of probe input data in Probe Cache Manager
  // entry #3 for encoded probe input data in Probe Cache Manager
  // enrry #4 for inner table row data in this operator's cache buffer
  Int32 work_atp = 1;
  ex_cri_desc * work_cri_desc = new(space) ex_cri_desc(5, space);
  unsigned short hashValIdx          = 2; 
  unsigned short encodedProbeDataIdx = 3;
  unsigned short innerRowDataIdx     = 4;

    // generate code for child tree, and get its tdb and explain tuple.
  child(0)->codeGen(generator);
  ComTdb * child_tdb = (ComTdb *)(generator->getGenObj());
  ExplainTuple *childExplainTuple = generator->getExplainTuple();


  //////////////////////////////////////////////////////
  // Generate up to 4 runtime expressions.
  //////////////////////////////////////////////////////

  // Will use child's char. inputs (+ execution count) for the next
  // two runtime expressions.
  ValueIdList inputsToUse = child(0).getGroupAttr()->getCharacteristicInputs();
  
  inputsToUse.insert(generator->getOrAddStatementExecutionCount());

  // Expression #1 gets the hash value of the probe input data
  ValueIdList hvAsList;

  // Executor has hard-coded assumption that the result is long, 
  // so add a Cast node to convert result to a long.

  ItemExpr *probeHashAsIe = new (generator->wHeap())
    HashDistPartHash(inputsToUse.rebuildExprTree(ITM_ITEM_LIST));

  probeHashAsIe->bindNode(generator->getBindWA());

  NumericType &nTyp = (NumericType &)probeHashAsIe->getValueId().getType();
  GenAssert(nTyp.isSigned() == FALSE,
            "Unexpected signed HashDistPartHash.");

  GenAssert(probeHashAsIe->getValueId().getType().supportsSQLnullLogical()
            == FALSE, "Unexpected nullable HashDistPartHash.");

  ItemExpr *hvAsIe = new (generator->wHeap()) Cast(
       probeHashAsIe, 
       new (generator->wHeap()) 
            SQLInt(FALSE,   // false == unsigned.
                   FALSE    // false == not nullable.
                  ));

  hvAsIe->bindNode(generator->getBindWA());

  hvAsList.insert(hvAsIe->getValueId());

  ex_expr *hvExpr   = NULL;
  ULng32 hvLength;
  exp_gen->generateContiguousMoveExpr(
              hvAsList,
              0, // don't add convert node
              work_atp,
              hashValIdx,
              ExpTupleDesc::SQLARK_EXPLODED_FORMAT,
              hvLength,
              &hvExpr);

  GenAssert(hvLength == sizeof(Lng32),
            "Unexpected length of result of hash function.");

  // Expression #2 encodes the probe input data for storage in 
  // the ProbeCacheManager.

  ValueIdList encodeInputAsList;

  CollIndex inputListIndex;
  for (inputListIndex = 0; 
       inputListIndex < inputsToUse.entries(); 
       inputListIndex++) {   

    ItemExpr *inputIe = 
         (inputsToUse[inputListIndex].getValueDesc())->getItemExpr();

    if (inputIe->getValueId().getType().getVarLenHdrSize() > 0)
      {
        // This logic copied from Sort::codeGen().
        // Explode varchars by moving them to a fixed field
        // whose length is equal to the max length of varchar.
        // 5/8/98: add support for VARNCHAR

        const CharType& char_type =
          (CharType&)(inputIe->getValueId().getType());

	if (!CollationInfo::isSystemCollation(char_type.getCollation()))
	{
	  inputIe = new(generator->wHeap())
              Cast (inputIe,
                    (new(generator->wHeap())
                      SQLChar(
		          CharLenInfo(char_type.getStrCharLimit(), char_type.getDataStorageSize()),
                          char_type.supportsSQLnull(),
                          FALSE, FALSE, FALSE,
                          char_type.getCharSet(),
                          char_type.getCollation(),
                          char_type.getCoercibility()
                              )
                    )
                   );
	}
      }

    CompEncode * enode = new(generator->wHeap()) 
      CompEncode(inputIe, FALSE /* ascend/descend doesn't matter*/);

    enode->bindNode(generator->getBindWA());
    encodeInputAsList.insert(enode->getValueId());
  }

  ex_expr *encodeInputExpr = NULL;
  ULng32 encodedInputLength;
  exp_gen->generateContiguousMoveExpr(encodeInputAsList, 
                              0, //don't add conv nodes
                              work_atp, encodedProbeDataIdx,
                              ExpTupleDesc::SQLARK_EXPLODED_FORMAT,
                              encodedInputLength, &encodeInputExpr);


  // Expression #3 moves the inner table data into a buffer pool.  
  // This is also the tuple returned to ProbeCache's parent. 

  ex_expr * innerRecExpr = NULL;
  ValueIdList innerTableAsList = getGroupAttr()->getCharacteristicOutputs();

  //////////////////////////////////////////////////////
  // Describe the returned row and add the returned 
  // values to the map table.
  //////////////////////////////////////////////////////

  // determine internal format
  NABoolean useCif = FALSE;
  ExpTupleDesc::TupleDataFormat tupleFormat = generator->getInternalFormat();
  //tupleFormat = determineInternalFormat( innerTableAsList, this, useCif,generator);

  ULng32 innerRecLength = 0;
  ExpTupleDesc * innerRecTupleDesc = 0;
  MapTable * returnedMapTable = NULL;

  exp_gen->generateContiguousMoveExpr(innerTableAsList, 
                              -1, // do add conv nodes 
			      work_atp, innerRowDataIdx,
			      tupleFormat,
			      innerRecLength, &innerRecExpr,
			      &innerRecTupleDesc, ExpTupleDesc::SHORT_FORMAT,
                              &returnedMapTable);

  returned_desc->setTupleDescriptor(returned_desc->noTuples() - 1, 
        innerRecTupleDesc);

  // remove all appended map tables and return the returnedMapTable
  generator->removeAll(last_map_table);
  generator->appendAtEnd(returnedMapTable);
  // This returnedMapTable will contain the value ids that are being returned 
  // (the inner table probed).

  // Massage the atp and atp_index of the innerTableAsList.
  for (CollIndex i = 0; i < innerTableAsList.entries(); i++)
    {
      ValueId innerValId = innerTableAsList[i];

      Attributes *attrib =
	generator->getMapInfo(innerValId)->getAttr();

      // All reference to the returned values from this point on
      // will be at atp = 0, atp_index = last entry in returned desc.
      attrib->setAtp(0);
      attrib->setAtpIndex(returned_desc->noTuples() - 1);
    }

  // Expression #4 is a selection predicate, to be applied
  // before returning rows to the parent

  ex_expr * selectPred = NULL;

  if (!selectionPred().isEmpty())
    {
      ItemExpr * selPredTree =
        selectionPred().rebuildExprTree(ITM_AND,TRUE,TRUE);
      exp_gen->generateExpr(selPredTree->getValueId(),
                            ex_expr::exp_SCAN_PRED,
                            &selectPred);
    }

  //////////////////////////////////////////////////////
  // Prepare params for ComTdbProbeCache.
  //////////////////////////////////////////////////////

  queue_index pDownSize = (queue_index)getDefault(GEN_PROBE_CACHE_SIZE_DOWN);
  queue_index pUpSize   = (queue_index)getDefault(GEN_PROBE_CACHE_SIZE_UP);

  // Make sure that the ProbeCache queues can support the childs queues.
  if(pDownSize < child_tdb->getInitialQueueSizeDown()) {
    pDownSize = child_tdb->getInitialQueueSizeDown();
    pDownSize = MINOF(pDownSize, 32768);
  }
  if(pUpSize < child_tdb->getInitialQueueSizeUp()) {
    pUpSize = child_tdb->getInitialQueueSizeUp();
    pUpSize = MINOF(pUpSize, 32768);
  }

  ULng32 pcNumEntries = numCachedProbes_;
  
  // Number of entries in the probe cache cannot be less than 
  // max parent down queue size.  Before testing and adjusting the 
  // max queue size, it is necessary to make sure it is a power of
  // two, rounding up if necessary.  This is to match the logic in
  // ex_queue::resize.

  queue_index pdq2 = 1;
  queue_index bits = pDownSize;
  while (bits && pdq2 < pDownSize) {
    bits = bits  >> 1;
    pdq2 = pdq2 << 1;
  }
  if (pcNumEntries < pdq2)
    pcNumEntries = pdq2;

  numInnerTuples_ = getDefault(GEN_PROBE_CACHE_NUM_INNER);

  if (innerRecExpr == NULL)
    {
      // For semi-join and anti-semi-join, executor need not allocate
      // a buffer.  Set the tdb's buffer size to 0 to be consistent.
      numInnerTuples_ = 0;
    }
  else if (numInnerTuples_ == 0)
    {
      // Handle special value, 0, which tells code gen to 
      // decided on buffer size: i.e., large enough to accomodate
      // all parent up queue entries and all probe cache entries 
      // having a different inner table row.

      // As we did for the down queue, make sure the up queue size 
      // specified is a power of two.

      queue_index puq2 = 1;
      queue_index bits = pUpSize;
      while (bits && puq2 < pUpSize) {
        bits = bits  >> 1;
        puq2 = puq2 << 1;
      }
      numInnerTuples_ = puq2 + pcNumEntries;
    }
Exemplo n.º 2
0
short MergeUnion::codeGen(Generator * generator)
{
  ExpGenerator * exp_gen = generator->getExpGenerator();
  Space * space = generator->getSpace();
  
  MapTable * my_map_table = generator->appendAtEnd();

  ////////////////////////////////////////////////////////////////////////////
  //
  // Layout at this node:
  //
  // |------------------------------------------------------------------------|
  // | input data  |  Unioned data | left child's data | right child's data   |
  // | ( I tupps ) |  ( 1 tupp )   | ( L tupps )       |  ( R tupp )          |
  // |------------------------------------------------------------------------|
  // <-- returned row to parent --->
  // <------------ returned row from left child ------->
  // <-------------------- returned row from right child --------------------->
  //
  // input data:        the atp input to this node by its parent. 
  // unioned data:      tupp where the unioned result is moved
  // left child data:   tupps appended by the left child
  // right child data:  tupps appended by right child
  //
  // Input to left child:    I + 1 tupps
  // Input to right child:   I + 1 + L tupps
  //
  // Tupps returned from left and right child are only used to create the
  // unioned data. They are not returned to parent.
  //
  ////////////////////////////////////////////////////////////////////////////

  ex_cri_desc * given_desc
    = generator->getCriDesc(Generator::DOWN);

  ex_cri_desc * returned_desc = NULL;
  if(child(0) || child(1))
    returned_desc = new(space) ex_cri_desc(given_desc->noTuples() + 1, space);
  else
    returned_desc = given_desc;

  // expressions to move the left and right child's output to the
  // unioned row.
  ex_expr * left_expr = 0;
  ex_expr * right_expr = 0;

  // expression to compare left and right child's output to
  // evaluate merge union.
  ex_expr * merge_expr = 0;

  // Expression to conditionally execute the left or right child.
  ex_expr *cond_expr = NULL;

  // Expression to handle triggered action excpetion 
  ex_expr *trig_expr = NULL;

  // It is OK for neither child to exist when generating a merge union TDB
  // for index maintenenace. The children are filled in at build time.
  //
  GenAssert((child(0) AND child(1)) OR (NOT child(0) AND NOT (child(1))),
	    "MergeUnion -- missing one child");
  ComTdb * left_child_tdb = NULL;
  ComTdb * right_child_tdb = NULL;
  ExplainTuple *leftExplainTuple = NULL;
  ExplainTuple *rightExplainTuple = NULL;
  NABoolean afterUpdate = FALSE;
  NABoolean rowsFromLeft = TRUE;
  NABoolean rowsFromRight = TRUE;

  if(child(0) && child(1)) {
 
    // if an update operation is found  before the execution of the
  // IF statement, set afterUpdate to 1 indicating that an update operation
  // was performed before the execution of the IF statement.  Which 
  // is used at runtime to decide whether to set rollbackTransaction in the 
  // diagsArea

 
  if (generator->updateWithinCS() && getUnionForIF()) {
    afterUpdate = TRUE;
  }

    // generate the left child
    generator->setCriDesc(returned_desc, Generator::DOWN);
    child(0)->codeGen(generator);
    left_child_tdb = (ComTdb *)(generator->getGenObj());
    leftExplainTuple = generator->getExplainTuple();

  // MVs --
  // If the left child does not have any outputs, don't expect any rows.
  if (child(0)->getGroupAttr()->getCharacteristicOutputs().isEmpty())
    rowsFromLeft = FALSE;

  // if an update operation is found in the left subtree of this Union then
  // set rowsFromLeft to 0 which is passed on to execution tree indicating
  // that this Union node is not  expecting rows from the left child, then
  // foundAnUpdate_ is reset so it can be reused while doing codGen() on 
  // the right sub tree

    if (getUnionForIF()) {
      if (! getCondEmptyIfThen()) {
        if (generator->foundAnUpdate())   {
          rowsFromLeft = FALSE;
          generator->setFoundAnUpdate(FALSE);
        } 
      } 
      else {
        rowsFromLeft = FALSE;  
      }
    }
    
    // descriptor returned by left child is given to right child as input.
    generator->setCriDesc(generator->getCriDesc(Generator::UP), 
			  Generator::DOWN);
    child(1)->codeGen(generator);
    right_child_tdb = (ComTdb *)(generator->getGenObj());
    rightExplainTuple = generator->getExplainTuple();

  // MVs
  // If the right child does not have any outputs, don't expect any rows.
  if (child(1)->getGroupAttr()->getCharacteristicOutputs().isEmpty())
    rowsFromRight = FALSE;

  // if an update operation is found in the right subtree of this CS then
  // set rowsFromRight to 0 which is passed on to execution tree indicating
  // that this CS node is not  expecting rows from the right child, then
  // foundAnUpdate_ is reset so it can be reused while doing codGen() on
  // the left or right child of another CS node


  if (getUnionForIF()) {
    if (! getCondEmptyIfElse()) {
      if (generator->foundAnUpdate())  {
        rowsFromRight = FALSE;
      } 
    } 
    else {
      rowsFromRight = FALSE;  
    }


    // we cannot always expect a row from a conditional operator. If it is an
    // IF statement without an ELSE and the condition fails then we do not get
    // any rows back. So we allow a conditional union operator to handle all
    // errors below it and for the purposes of 8015 error / 8014 warning
    // treat it as an update node. In this way the nodes above it do not expect
    // any row from this child and do not raise an error if no row is returned.
    // 8014/8015 type errors within this IF statement are handled as in any
    // regular CS.

    generator->setFoundAnUpdate(TRUE);
  }

  }

  // Create the unioned row. 
  // colMapTable() is a list of ValueIdUnion nodes where each node points to
  // the corresponding left and the right output entries.
  // Generate expressions to move the left and right child's output to
  // the unioned row. 
  ValueIdList left_val_id_list;
  ValueIdList right_val_id_list;
  CollIndex   i;                                      

  for (i = 0; i < colMapTable().entries(); i++)       
    {
      ValueIdUnion * vidu_node = (ValueIdUnion *)(((colMapTable()[i]).getValueDesc())->getItemExpr());

      Cast * cnode;
      if (vidu_node->getResult().getType().getTypeQualifier() != NA_ROWSET_TYPE) {
        // move left child's output to result. The 'type' of Cast result is same
        // as that of the vidu_node.
        cnode = new(generator->wHeap())
	           Cast(((vidu_node->getLeftSource()).getValueDesc())->getItemExpr(),
	                &(vidu_node->getResult().getType()));
      }
      else {
	// We indicate that the whole array is to be copied
	SQLRowset *rowsetInfo = (SQLRowset *) &(vidu_node->getResult().getType());
        SQLRowset *newRowset =  new (generator->wHeap()) 
	                         SQLRowset(generator->wHeap(), rowsetInfo->getElementType(),
	                                   rowsetInfo->getMaxNumElements(),
                                           rowsetInfo->getNumElements());
	newRowset->useTotalSize() = TRUE;
        cnode = new(generator->wHeap())
	           Cast(((vidu_node->getLeftSource()).getValueDesc())->getItemExpr(),
	                newRowset);
      }

      cnode->bindNode(generator->getBindWA());
      
      left_val_id_list.insert(cnode->getValueId());

      if (vidu_node->getResult().getType().getTypeQualifier() != NA_ROWSET_TYPE) {
        // move left child's output to result. The 'type' of Cast result is same
        // as that of the vidu_node.
        cnode = new(generator->wHeap())
	           Cast(((vidu_node->getRightSource()).getValueDesc())->getItemExpr(),
	                &(vidu_node->getResult().getType()));
      }
      else {
	// We indicate that the whole array is to be copied
	SQLRowset *rowsetInfo = (SQLRowset *) &(vidu_node->getResult().getType());
        SQLRowset *newRowset =  new (generator->wHeap()) 
	                         SQLRowset(generator->wHeap(), rowsetInfo->getElementType(),
	                                   rowsetInfo->getMaxNumElements(),
                                           rowsetInfo->getNumElements());
	newRowset->useTotalSize() = TRUE;
        cnode = new(generator->wHeap())
	           Cast(((vidu_node->getRightSource()).getValueDesc())->getItemExpr(),
	                newRowset);
      }

      cnode->bindNode(generator->getBindWA());
      right_val_id_list.insert(cnode->getValueId());
    }
  
  ExpTupleDesc * tuple_desc = 0;
  ULng32 tuple_length = 0;
  if(child(0) && child(1)) {
    exp_gen->generateContiguousMoveExpr(left_val_id_list,
					0, // don't add convert nodes
					1, returned_desc->noTuples() - 1,
					ExpTupleDesc::SQLARK_EXPLODED_FORMAT,
					tuple_length,
					&left_expr,
					&tuple_desc,
					ExpTupleDesc::SHORT_FORMAT);
  

    exp_gen->generateContiguousMoveExpr(right_val_id_list,
					0, // don't add convert nodes
					1, returned_desc->noTuples() - 1,
					ExpTupleDesc::SQLARK_EXPLODED_FORMAT,
					tuple_length,
					&right_expr);
  }
  
  // add value ids for all vidu_nodes to my map table. This is the
  // the map table that will be returned. The attributes of the value ids
  // are same as that of left(or right) expression outputs.
  for (i = 0; i < colMapTable().entries(); i++)
    {
      ValueIdUnion * vidu_node = (ValueIdUnion *)(((colMapTable()[i]).getValueDesc())->getItemExpr());
      
      Attributes * attr =
	generator->addMapInfoToThis(my_map_table, vidu_node->getValueId(), 
				    generator->getMapInfo(left_val_id_list[i])->getAttr())->getAttr();
      attr->setAtp(0);
    }
  
  // describe the returned unioned row
  returned_desc->setTupleDescriptor(returned_desc->noTuples() - 1, tuple_desc);
  
  // if sort-merge union is being done, generate expression to
  // compare the left and the right values.
  // This predicate should return TRUE if the left value is
  // less than the right value.
  merge_expr = 0;
  if (getMergeExpr()) {
    // generate the merge predicate. 
    ItemExpr * mergeExpr = new(generator->wHeap()) BoolResult(getMergeExpr());
    mergeExpr->bindNode(generator->getBindWA());    
      
    exp_gen->generateExpr(mergeExpr->getValueId(),
			  ex_expr::exp_SCAN_PRED,
			  &merge_expr);   
  }

  // If conditional union, generate conditional expression, and ignore
  // right child if it was just being used as a no-op.
  cond_expr = 0;
  if (NOT condExpr().isEmpty()) {
    ItemExpr *condExp = condExpr().rebuildExprTree(ITM_AND, TRUE, TRUE);

    exp_gen->generateExpr(condExp->getValueId(), 
                          ex_expr::exp_SCAN_PRED, 
		          &cond_expr);
  }

  // If conditional union, generate triggered action exception error 
  if (NOT trigExceptExpr().isEmpty()) {
    ItemExpr *trigExp = trigExceptExpr().rebuildExprTree(ITM_AND, TRUE, TRUE);

    exp_gen->generateExpr(trigExp->getValueId(), 
                          ex_expr::exp_SCAN_PRED, 
		          &trig_expr);
  }

  // remove both children's map table. Nothing from child's context
  // should be visible from here on upwards.
  generator->removeAll(my_map_table);

  // Ensure the default buffer size is at least as large as the unioned output
  // row.
  UInt32 outputBuffSize = MAXOF( getDefault(GEN_UN_BUFFER_SIZE),
                                 tuple_length );
  outputBuffSize = SqlBufferNeededSize( 1,                 // # of tuples
                                        outputBuffSize,
                                        SqlBuffer::NORMAL_
                                        );

  ComTdbUnion * union_tdb 
    = new(space) ComTdbUnion(
			     left_child_tdb,
			     right_child_tdb,
			     left_expr,
			     right_expr,
			     merge_expr,
			     cond_expr,
			     trig_expr,
                             tuple_length, // unioned rowlen
			     returned_desc->noTuples()-1, // tupp index for
			                                  // unioned buffer
			     given_desc,
			     returned_desc, 
			     (queue_index)getDefault(GEN_UN_SIZE_DOWN),
			     (queue_index)getDefault(GEN_UN_SIZE_UP),
			     (Cardinality) (getInputCardinality() * getEstRowsUsed()).getValue(),
			     getDefault(GEN_UN_NUM_BUFFERS),
			     outputBuffSize,
			     getOrderedUnion(),
                             getBlockedUnion(),  //++ Triggers -
                             hasNoOutputs(),     //++ Triggers -
                             rowsFromLeft,
                             rowsFromRight,
                             afterUpdate,
			     getInNotAtomicStatement());

  generator->initTdbFields(union_tdb);

  // If it does not have two children, this is index maintenance code and
  // should not be Explained  
  if (!generator->explainDisabled()) {
    generator->setExplainTuple(addExplainInfo(union_tdb,
					    leftExplainTuple,
					    rightExplainTuple,
					    generator));
  }

  // restore the original down cri desc since this node changed it.
  generator->setCriDesc(given_desc, Generator::DOWN);

  // set the new up cri desc.
  generator->setCriDesc(returned_desc, Generator::UP);

  generator->setGenObj(this, union_tdb);

  return 0;  
}