blob: 4d1e6ae9686328993de7468cedc7548e72d1de73 [file] [log] [blame]
// **********************************************************************
// @@@ 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 @@@
//
#ifndef EX_MJ_H
#define EX_MJ_H
// -*-C++-*-
// **********************************************************************
// *
// * File: ex_mj.h
// * Description: Merge Join
// * (no repositioning of any child, we read the full data from
// * each child until reaching EOF. We store duplicate rows
// * from the inner table in memory, overflowing to temporary
// * storage when necessary.)
// * Created: 7/10/95
// * Language: C++
// *
// *
//
// **********************************************************************
// forward declarations
class Queue;
#include "ExOverflow.h"
#include "ExDupSqlBuffer.h"
#include "TupleSpace.h"
#include "exp_expr.h"
/////////////////////////////////////////////
// class ex_mj_tdb: Task Definition Block
/////////////////////////////////////////////
#include "ComTdbMj.h"
using namespace ExOverflow;
// -----------------------------------------------------------------------
// Classes defined in this file
// -----------------------------------------------------------------------
class ex_mj_tdb;
// -----------------------------------------------------------------------
// Classes referenced in this file
// -----------------------------------------------------------------------
class ex_tcb;
// -----------------------------------------------------------------------
// ex_mj_tdb
// -----------------------------------------------------------------------
class ex_mj_tdb : public ComTdbMj
{
public:
// ---------------------------------------------------------------------
// Constructor is only called to instantiate an object used for
// retrieval of the virtual table function pointer of the class while
// unpacking. An empty constructor is enough.
// ---------------------------------------------------------------------
ex_mj_tdb()
{}
virtual ~ex_mj_tdb()
{}
// ---------------------------------------------------------------------
// Build a TCB for this TDB. Redefined in the Executor project.
// ---------------------------------------------------------------------
virtual ex_tcb *build(ex_globals *globals);
private:
// ---------------------------------------------------------------------
// !!!!!!! IMPORTANT -- NO DATA MEMBERS ALLOWED IN EXECUTOR TDB !!!!!!!!
// *********************************************************************
// The Executor TDB's are only used for the sole purpose of providing a
// way to supplement the Compiler TDB's (in comexe) with methods whose
// implementation depends on Executor objects. This is done so as to
// decouple the Compiler from linking in Executor objects unnecessarily.
//
// When a Compiler generated TDB arrives at the Executor, the same data
// image is "cast" as an Executor TDB after unpacking. Therefore, it is
// a requirement that a Compiler TDB has the same object layout as its
// corresponding Executor TDB. As a result of this, all Executor TDB's
// must have absolutely NO data members, but only member functions. So,
// if you reach here with an intention to add data members to a TDB, ask
// yourself two questions:
//
// 1. Are those data members Compiler-generated?
// If yes, put them in the ComTdbMj instead.
// If no, they should probably belong to someplace else (like TCB).
//
// 2. Are the classes those data members belong defined in the executor
// project?
// If your answer to both questions is yes, you might need to move
// the classes to the comexe project.
// ---------------------------------------------------------------------
};
/////////////////////////////////////////////////////
// class ex_mj_tcb: Task control block
/////////////////////////////////////////////////////
class ex_mj_tcb : public ex_tcb
{
friend class ex_mj_tdb;
friend class ex_mj_private_state;
public:
// Step in processing the parent row
// Should really be private but ex_mj_private_state needs it and making it a
// a friend doesn't help
enum mj_step // would like a short
{
MJ_UNINITIALIZED,
MJ_EMPTY,
MJ_GET_LEFT,
MJ_GET_RIGHT,
MJ_RETURN_ROW, // unique merge join only
MJ_RETURN_ONE_ROW, // non-unique merge join only
MJ_SAVE_DUP_RIGHT, // non-unique merge join only
MJ_SAVE_DUP_RIGHT_TSPACE, // non-unique merge join only
MJ_REWIND_OVERFLOW, // non-unique merge join only
MJ_RETURN_SAVED_DUP_ROWS, // non-unique merge join only
MJ_RETURN_OVERFLOW_ROWS, // non-unique merge join only
MJ_FINISH_LEFT,
MJ_ERROR, // non-unique merge join only
MJ_CANCEL,
MJ_DONE,
MJ_DONE_NEVER_STARTED,
MJ_NUMBER_OF_STATES // number of mj_step enumerators
};
// Constructor
ex_mj_tcb(const ex_mj_tdb & mj_tdb,
const ex_tcb & left_tcb,
const ex_tcb & right_tcb,
ex_globals *glob);
~ex_mj_tcb();
void freeResources(); // free resources
virtual ex_tcb_private_state * allocatePstates(
Lng32 &numElems, // inout, desired/actual elements
Lng32 &pstateLength); // out, length of one element
virtual void registerSubtasks(); // register work procedures with scheduler
short work(); // when scheduled to do work
ex_queue_pair getParentQueue() const
{
return qparent;
}
void start(ex_mj_private_state& pstate);
short stop(ex_mj_private_state& pstate);
short cancel(ex_mj_private_state& pstate);
virtual const ex_tcb* getChild(Int32 pos) const;
virtual Int32 numChildren() const { return 2; }
protected:
// Opportunistic look ahead states for next left child row.
enum LookAheadState { LA_LEFT_UNCHECKED, // check not done
LA_LEFT_DUPLICATE, // next left is duplicate
LA_LEFT_DIFFERENT, // next left is different
LA_LEFT_NO_DATA, // next left is Q_NO_DATA
LA_LEFT_ERROR }; // Q_SQLERROR or Q_INVALID
// Tuple comparison results. Partial evaluation can return CMP_NOT_EQUAL
// (see ex_mj_tcb::compareTuples).
enum Comparison { CMP_ERROR, CMP_LESS, CMP_EQUAL, CMP_GREATER,
CMP_NOT_EQUAL };
// ATP index for contiguous saved duplicate right row tupp
#if defined(NA_HAS_ANSI_CONST)
static const Int16 DUP_ATP_INDEX = 2;
#else
enum { DUP_ATP_INDEX = 2 };
#endif
const ex_tcb * tcbLeft_; // left tcb
const ex_tcb * tcbRight_; // right tcb
ex_queue_pair qparent;
ex_queue_pair qleft;
ex_queue_pair qright;
atp_struct * dupAtp_; // for accessing rows in dupPool_
tupp * dupTupp_; // for setting dupAtp_
// The dupPool_ stores duplicate right child tuples referenced by
// the rows that merge join returns to its parent. If this pool has
// more space than is needed to store these right child tuples, then
// dupPool_ also stores duplicate right child rows that are
// candidates for being joined with the current left child row.
ExDupSqlBuffer * dupPool_; // duplicate right child row pool
char * leftEncodedKeyPtr_;
char * rightEncodedKeyPtr_;
LookAheadState lookAheadState_; // opportunistic look ahead state
// for next left child row
mj_step postIoStep_; // step to follow I/O completion
atp_struct * prevRightAtp_; // right child row for duplicate check
bool saveFirstDupAtp_; // if we save the first dup tuple
ex_expr::exp_return_type savedRetCode_;
// saved rightCheckDupExpr() result
// When the fixed-size dupPool_ lacks room to store candidate duplicate
// right child rows, these rows are stored in the tspace_ TupleSpace object.
ExOverflow::TupleSpace * tspace_; // overflow manager
atp_struct * workAtp_;
// Null data tupp for null instantiation.
// this tupp points to a tuple of all null values the size of the
// Null instantiated row (mjTdb().ljRecLen_)
sql_buffer_pool *nullPool_;
tupp nullData_;
inline ex_mj_tdb & mjTdb() const
{
return (ex_mj_tdb &) tdb;
}
inline Int32 isSemiJoin() const // True if we are doing a semi-join
{
return mjTdb().isSemiJoin();
}
inline Int32 isLeftJoin() const // True if we are doing a left-join
{
return mjTdb().isLeftJoin();
}
inline Int32 isAntiJoin() const // True if we are doing a Anti-join
{
return mjTdb().isAntiJoin();
}
inline bool isOverflowEnabled() const
{
return mjTdb().isOverflowEnabled();
}
inline ex_expr * mergeExpr() const
{
return mjTdb().mergeExpr_;
}
inline ex_expr * compExpr() const
{
return mjTdb().compExpr_;
}
inline ex_expr * preJoinExpr() const
{
return mjTdb().preJoinExpr_;
}
inline ex_expr * postJoinExpr() const
{
return mjTdb().postJoinExpr_;
}
inline ex_expr * leftCheckDupExpr() const
{
return mjTdb().leftCheckDupExpr_;
}
inline ex_expr * rightCheckDupExpr() const
{
return mjTdb().rightCheckDupExpr_;
}
inline ex_expr * ljExpr() const
{
return mjTdb().ljExpr_;
}
inline ex_expr * rightCopyDupExpr() const
{
return mjTdb().rightCopyDupExpr_;
}
inline ex_expr * leftEncodedKeyExpr() const
{
return mjTdb().leftEncodedKeyExpr();
}
inline ex_expr * rightEncodedKeyExpr() const
{
return mjTdb().rightEncodedKeyExpr();
}
Comparison compareTuples(ex_queue_entry * lentry,
ex_queue_entry * rentry,
bool doFullComparison = true);
bool processError(atp_struct * entryAtp = NULL);
// Reacquire resources (true => success)
bool reacquireResources(void);
ex_expr::exp_return_type returnRow(atp_struct * leftAtp,
atp_struct * rightAtp,
ex_mj_private_state & pstate,
bool isUniqueMj = false,
ExOperStats *statsEntry = NULL);
private:
void checkInit(void);
void createDiags(Int16 sqlCode = 0);
// No saved duplicate right rows?
bool noSavedDups(void);
};
inline const ex_tcb* ex_mj_tcb::getChild(Int32 pos) const
{
ex_assert((pos >= 0), "");
if (pos == 0)
return tcbLeft_;
else if (pos == 1)
return tcbRight_;
else
return NULL;
}
inline
bool
ex_mj_tcb::noSavedDups(void)
{
bool zeroDups = !dupPool_->hasDups();
if (zeroDups && tspace_)
{
zeroDups = tspace_->empty();
}
return zeroDups;
}
class ex_mj_unique_tcb : public ex_mj_tcb
{
public:
// Constructor
ex_mj_unique_tcb(const ex_mj_tdb & mj_tdb,
const ex_tcb & left_tcb,
const ex_tcb & right_tcb,
ex_globals *glob);
short work(); // when scheduled to do work
};
///////////////////////////////////////////////
// class ex_mj_private_state
//////////////////////////////////////////////
class ex_mj_private_state : public ex_tcb_private_state
{
friend class ex_mj_tcb;
friend class ex_mj_unique_tcb;
Int64 matchCount_; // number of rows returned for this parent row
ex_mj_tcb::mj_step step_;
bool outerMatched_; // no need to null instantiate?
static const char* const stateNames[ex_mj_tcb::MJ_NUMBER_OF_STATES];
public:
ex_mj_private_state();
~ex_mj_private_state();
void init(const ex_mj_tcb * tcb); // initialize state
// Return state name of current state
const char* currentState(void) const;
private:
// Return state name of mjStep#
static const char* stateName(ex_mj_tcb::mj_step mjStep);
};
#endif