1
0
mirror of https://github.com/mariadb-corporation/mariadb-columnstore-engine.git synced 2025-07-30 19:23:07 +03:00
Files
mariadb-columnstore-engine/dbcon/joblist/jlf_subquery.cpp
Serguey Zefirov bd1622f331 feat(MCOL-5886): support InnoDB's table partitions in cross-engine joins
The purpose of this changeset is to obtain list of partitions from
SELECT_LEX structure and pass it down to joblist and then to
CrossEngineStep to pass to InnoDB.
2025-04-23 08:24:10 +03:00

877 lines
24 KiB
C++

/* Copyright (C) 2014 InfiniDB, Inc.
Copyright (C) 2019 MariaDB Corporation
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; version 2 of
the License.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
MA 02110-1301, USA. */
// $Id: jlf_subquery.cpp 6419 2010-03-30 04:28:32Z xlou $
#include <iostream>
#include <stack>
#include <iterator>
//#define NDEBUG
#include <cassert>
#include <vector>
using namespace std;
#include <boost/shared_ptr.hpp>
using namespace boost;
#include "calpontsystemcatalog.h"
#include "logicoperator.h"
#include "constantcolumn.h"
#include "existsfilter.h"
#include "predicateoperator.h"
#include "pseudocolumn.h"
#include "selectfilter.h"
#include "simplefilter.h"
#include "simplescalarfilter.h"
#include "windowfunctioncolumn.h"
using namespace execplan;
#include "rowgroup.h"
using namespace rowgroup;
#include "idberrorinfo.h"
#include "errorids.h"
#include "exceptclasses.h"
#include "dataconvert.h"
using namespace logging;
#include "elementtype.h"
#include "jobstep.h"
#include "jlf_common.h"
#include "jlf_execplantojoblist.h"
#include "expressionstep.h"
#include "tupleconstantstep.h"
#include "tuplehashjoin.h"
#include "subquerystep.h"
#include "subquerytransformer.h"
#include "jlf_subquery.h"
using namespace joblist;
namespace
{
void getColumnValue(ConstantColumn** cc, uint64_t i, const Row& row, const long timeZone)
{
ostringstream oss;
int64_t data = 0;
switch (row.getColTypes()[i])
{
case CalpontSystemCatalog::TINYINT:
case CalpontSystemCatalog::SMALLINT:
case CalpontSystemCatalog::MEDINT:
case CalpontSystemCatalog::INT:
case CalpontSystemCatalog::BIGINT:
if (row.getScale(i) == 0)
{
oss << row.getIntField(i);
*cc = new ConstantColumn(oss.str(), row.getIntField(i));
break;
}
/* fall through */
/* else > 0 */
case CalpontSystemCatalog::DECIMAL:
case CalpontSystemCatalog::UDECIMAL:
if (row.getColumnWidth(i) == datatypes::MAXDECIMALWIDTH)
{
int128_t val;
row.getInt128Field(i, val);
IDB_Decimal dec(0, row.getScale(i), row.getPrecision(i), val);
*cc = new ConstantColumn(dec.toString(true), dec);
}
else
{
data = row.getIntField(i);
IDB_Decimal dec(data, row.getScale(i), row.getPrecision(i));
*cc = new ConstantColumn(dec.toString(), dec);
}
break;
case CalpontSystemCatalog::UTINYINT:
case CalpontSystemCatalog::USMALLINT:
case CalpontSystemCatalog::UMEDINT:
case CalpontSystemCatalog::UINT:
case CalpontSystemCatalog::UBIGINT:
oss << row.getUintField(i);
*cc = new ConstantColumn(oss.str(), row.getUintField(i));
break;
case CalpontSystemCatalog::FLOAT:
case CalpontSystemCatalog::UFLOAT:
oss << fixed << row.getFloatField(i);
*cc = new ConstantColumn(oss.str(), (double)row.getFloatField(i));
break;
case CalpontSystemCatalog::DOUBLE:
oss << fixed << row.getDoubleField(i);
*cc = new ConstantColumn(oss.str(), row.getDoubleField(i));
break;
case CalpontSystemCatalog::LONGDOUBLE:
oss << fixed << row.getLongDoubleField(i);
*cc = new ConstantColumn(oss.str(), row.getLongDoubleField(i));
break;
case CalpontSystemCatalog::DATE:
oss << dataconvert::DataConvert::dateToString(row.getUintField<4>(i));
*cc = new ConstantColumn(oss.str());
break;
case CalpontSystemCatalog::DATETIME:
oss << dataconvert::DataConvert::datetimeToString(row.getUintField<8>(i));
*cc = new ConstantColumn(oss.str());
break;
case CalpontSystemCatalog::TIMESTAMP:
oss << dataconvert::DataConvert::timestampToString(row.getUintField<8>(i), timeZone);
*cc = new ConstantColumn(oss.str());
break;
case CalpontSystemCatalog::TIME:
oss << dataconvert::DataConvert::timeToString(row.getUintField<8>(i));
*cc = new ConstantColumn(oss.str());
break;
case CalpontSystemCatalog::CHAR:
case CalpontSystemCatalog::VARCHAR:
case CalpontSystemCatalog::TEXT:
case CalpontSystemCatalog::BLOB:
oss << (char*)(row.getStringField(i).str());
*cc = new ConstantColumn(oss.str());
break;
default:
oss << "Unsupported data type: " << row.getColTypes()[i];
throw QueryDataExcept(oss.str(), dataTypeErr);
}
}
void sfInHaving(ParseTree* pt, void*)
{
SelectFilter* sf = dynamic_cast<SelectFilter*>(pt->data());
if (sf != NULL)
throw IDBExcept(ERR_NON_SUPPORT_HAVING);
}
void ssfInHaving(ParseTree* pt, void* obj)
{
JobInfo* jobInfo = reinterpret_cast<JobInfo*>(obj);
SimpleScalarFilter* ssf = dynamic_cast<SimpleScalarFilter*>(pt->data());
if (ssf != NULL)
{
ParseTree* parseTree = NULL;
if (simpleScalarFilterToParseTree(ssf, parseTree, *jobInfo))
{
// replace simple scalar filter with simple filters
delete pt->data();
pt->left(parseTree->left());
pt->right(parseTree->right());
pt->data(parseTree->data());
jobInfo->dynamicParseTreeVec.push_back(parseTree);
// don't delete the parseTree, it has been placed in the plan.
// Instead, we use the dynamicParseTreeVec above for deletion
// in ~csep() or csep.unserialize().
// delete parseTree;
}
else
{
// not a scalar result
// replace simple scalar filter with simple filters
delete pt->data();
pt->data(NULL);
delete parseTree;
jobInfo->constantFalse = true;
}
}
}
void getCorrelatedFilters(ParseTree* pt, void* obj)
{
SimpleFilter* sf = dynamic_cast<SimpleFilter*>(pt->data());
if (sf != NULL)
{
ReturnedColumn* rc1 = dynamic_cast<ReturnedColumn*>(sf->lhs());
ReturnedColumn* rc2 = dynamic_cast<ReturnedColumn*>(sf->rhs());
bool correlated = false;
if (rc1 != NULL && rc1->joinInfo() != 0)
correlated = true;
if (rc2 != NULL && rc2->joinInfo() != 0)
correlated = true;
if (correlated)
{
ParseTree** correlatedFilters = reinterpret_cast<ParseTree**>(obj);
if (*correlatedFilters == NULL)
{
*correlatedFilters = new ParseTree(sf);
}
else
{
ParseTree* left = *correlatedFilters;
*correlatedFilters = new ParseTree(new LogicOperator("and"));
(*correlatedFilters)->left(left);
(*correlatedFilters)->right(new ParseTree(sf));
}
pt->data(NULL);
}
}
}
ParseTree* trim(ParseTree*& pt)
{
ParseTree* lhs = pt->left();
if (lhs)
pt->left(trim(lhs));
ParseTree* rhs = pt->right();
if (rhs)
pt->right(trim(rhs));
if ((lhs == NULL) && (rhs == NULL) && (pt->data() == NULL))
{
delete pt;
pt = NULL;
}
else if ((lhs == NULL || rhs == NULL) && dynamic_cast<LogicOperator*>(pt->data()))
{
idbassert(dynamic_cast<LogicOperator*>(pt->data())->data() == "and");
ParseTree* br = pt;
ParseTree* nl = NULL; // the left()/right() are overloaded
if (lhs == NULL && rhs != NULL)
pt = rhs;
else if (lhs != NULL && rhs == NULL)
pt = lhs;
else
pt = NULL;
br->left(nl);
br->right(nl);
delete br;
}
return pt;
}
void handleNotIn(JobStepVector& jsv, JobInfo* jobInfo)
{
// convert CORRELATED join (but not MATCHNULLS) to expression.
for (JobStepVector::iterator i = jsv.begin(); i != jsv.end(); i++)
{
TupleHashJoinStep* thjs = dynamic_cast<TupleHashJoinStep*>(i->get());
if (!thjs || !(thjs->getJoinType() & CORRELATED) || (thjs->getJoinType() & MATCHNULLS))
continue;
ReturnedColumn* lhs = thjs->column1()->clone();
ReturnedColumn* rhs = thjs->column2()->clone();
SOP sop(new PredicateOperator("="));
sop->setOpType(lhs->resultType(), rhs->resultType());
sop->resultType(sop->operationType());
SimpleFilter* sf = new SimpleFilter(sop, lhs, rhs);
ExpressionStep* es = new ExpressionStep(*jobInfo);
if (es == NULL)
throw runtime_error("Failed to create ExpressionStep 2");
es->expressionFilter(sf, *jobInfo);
es->resetJoinInfo(); // Not to be done as join
i->reset(es);
delete sf;
}
}
bool isNotInSubquery(JobStepVector& jsv)
{
// use MATCHNULLS(execplan::JOIN_NULL_MATCH) to identify NOT IN and NOT EXIST
bool notIn = false;
for (JobStepVector::iterator i = jsv.begin(); i != jsv.end(); i++)
{
TupleHashJoinStep* thjs = dynamic_cast<TupleHashJoinStep*>(i->get());
if (thjs)
{
if (thjs->getJoinType() & MATCHNULLS)
{
// only NOT IN will be specially treated.
notIn = true;
break;
}
}
}
return notIn;
}
// This fcn is currently unused. Will keep it in the code for now.
#if 0
void alterCsepInExistsFilter(CalpontSelectExecutionPlan* csep, JobInfo& jobInfo)
{
// This is for window function in IN/EXISTS sub-query.
// Replace an expression of window functions with indivisual window functions.
RetColsVector& retCols = csep->returnedCols();
RetColsVector wcs;
for (RetColsVector::iterator i = retCols.begin(); i < retCols.end(); i++)
{
const vector<WindowFunctionColumn*>& wcList = (*i)->windowfunctionColumnList();
if (!wcList.empty())
{
vector<WindowFunctionColumn*>::const_iterator j = wcList.begin();
while (j != wcList.end())
wcs.push_back(SRCP((*j++)->clone()));
// a little optimization, eliminate the expression in select clause
// replace the window function expression with the 1st windowfunction
(*i) = wcs[0];
}
}
if (wcs.size() > 1)
retCols.insert(retCols.end(), wcs.begin() + 1, wcs.end());
}
#endif
void doCorrelatedExists(const ExistsFilter* ef, JobInfo& jobInfo)
{
// Transformer sub to a subquery step.
SErrorInfo errorInfo(jobInfo.errorInfo);
SubQueryTransformer transformer(&jobInfo, errorInfo);
CalpontSelectExecutionPlan* csep = ef->sub().get();
// alterCsepInExistsFilter(csep, jobInfo);
SJSTEP subQueryStep = transformer.makeSubQueryStep(csep);
// @bug3524, special handling of not in.
JobStepVector& jsv = transformer.correlatedSteps();
if (isNotInSubquery(jsv) == true)
handleNotIn(jsv, transformer.subJobInfo());
transformer.updateCorrelateInfo();
jsv.push_back(subQueryStep);
JLF_ExecPlanToJobList::addJobSteps(jsv, jobInfo, false);
}
void doNonCorrelatedExists(const ExistsFilter* ef, JobInfo& jobInfo)
{
// Assume: exists-subquery without a from clause, like exists (select 1)
// always evalustes to true
bool noFrom = (ef->sub()->tableList().size() == 0);
bool exists = !ef->notExists();
if (!noFrom)
{
// Transformer sub to a scalar result set.
SErrorInfo errorInfo(new ErrorInfo());
SimpleScalarTransformer transformer(&jobInfo, errorInfo, true);
transformer.makeSubQueryStep(ef->sub().get());
// @bug 2839. error out in-relelvant correlated column case
if (!transformer.correlatedSteps().empty())
{
JobStepVector::const_iterator it = transformer.correlatedSteps().begin();
string tn;
for (; it != transformer.correlatedSteps().end(); ++it)
{
// 1. if tuplehashjoin, check alias1 and alias2; otherwise, check alias
// 2. alias start with "$sub_"
TupleHashJoinStep* thjs = dynamic_cast<TupleHashJoinStep*>(it->get());
if (thjs)
{
if (thjs->alias1().empty() || thjs->alias1().compare(0, 5, "$sub_"))
tn = thjs->alias2();
else
tn = thjs->alias1();
}
else
{
tn = it->get()->alias();
}
}
Message::Args args;
if (tn.empty() || tn.compare(0, 5, "$sub_"))
tn = "sub-query";
args.add(tn);
throw IDBExcept(ERR_MISS_JOIN_IN_SUB, args);
}
// Catch more_than_1_row exception only
try
{
transformer.run();
}
catch (MoreThan1RowExcept&)
{
// no-op
};
// Check if the exists condition is satisfied.
//((!transformer.emptyResultSet() && !ef->notExists()) ||
// ( transformer.emptyResultSet() && ef->notExists()))
exists = (transformer.emptyResultSet() == ef->notExists());
}
JobStepVector jsv;
SJSTEP tcs(new TupleConstantBooleanStep(jobInfo, exists));
jsv.push_back(tcs);
JLF_ExecPlanToJobList::addJobSteps(jsv, jobInfo, false);
}
const SRCP doSelectSubquery(CalpontExecutionPlan* ep, SRCP& sc, JobInfo& jobInfo)
{
CalpontSelectExecutionPlan* csep = dynamic_cast<CalpontSelectExecutionPlan*>(ep);
SRCP rc;
SErrorInfo errorInfo(jobInfo.errorInfo);
string subView = dynamic_cast<SimpleColumn*>(sc.get())->viewName();
SubQueryTransformer transformer(&jobInfo, errorInfo, subView);
transformer.setVarbinaryOK();
SJSTEP subQueryStep = transformer.makeSubQueryStep(csep);
if (transformer.correlatedSteps().size() > 0)
{
transformer.updateCorrelateInfo();
JobStepVector jsv = transformer.correlatedSteps();
jsv.push_back(subQueryStep);
jobInfo.selectAndFromSubs.insert(jobInfo.selectAndFromSubs.end(), jsv.begin(), jsv.end());
const RetColsVector& retCol = csep->returnedCols();
for (uint64_t i = 0; i < retCol.size(); i++)
{
if (retCol[i]->colSource() == 0)
{
rc = transformer.virtualTable().columns()[i];
break;
}
}
}
else
{
// Non-correlated subquery
// Do not catch exceptions here, let caller handle them.
SimpleScalarTransformer simpleTransformer(transformer);
simpleTransformer.run();
// Costruct a simple column based on the scalar result.
ConstantColumn* cc = NULL;
if (simpleTransformer.emptyResultSet() == false)
{
// set value for cc
const Row& row = simpleTransformer.resultRow();
if (!row.isNullValue(0))
getColumnValue(&cc, 0, row, jobInfo.timeZone);
}
// Empty set or null value
if (cc == NULL)
{
cc = new ConstantColumn("", ConstantColumn::NULLDATA);
}
rc.reset(cc);
}
return rc;
}
} // namespace
namespace joblist
{
bool simpleScalarFilterToParseTree(SimpleScalarFilter* sf, ParseTree*& pt, JobInfo& jobInfo)
{
const vector<SRCP>& cols = sf->cols();
CalpontSelectExecutionPlan* csep = sf->sub().get();
SOP sop = sf->op();
// For row construct, supports only =, <> in Release 1.1.
// Other operators are errored out by connector.
string lop("and");
if ((cols.size() > 1) && (sop->data() == "<>"))
lop = "or";
// Transformer sub to a scalar result.
SErrorInfo errorInfo(jobInfo.errorInfo);
SimpleScalarTransformer transformer(&jobInfo, errorInfo, false);
transformer.makeSubQueryStep(csep);
// Do not catch exceptions here, let caller handle them.
transformer.run();
// if subquery errored out
if (errorInfo->errCode)
{
ostringstream oss;
oss << "Sub-query failed: ";
if (errorInfo->errMsg.empty())
{
oss << "error code " << errorInfo->errCode;
errorInfo->errMsg = oss.str();
}
throw runtime_error(errorInfo->errMsg);
}
// Construct simple filters based on the scalar result.
bool isScalar = false;
if (transformer.emptyResultSet() == false)
{
const Row& row = transformer.resultRow();
uint64_t i = 0;
for (; i < cols.size(); i++)
{
// = null is always false
if (row.isNullValue(i) == true)
break;
// set fResult for cc
ConstantColumn* cc = NULL;
getColumnValue(&cc, i, row, jobInfo.timeZone);
sop->setOpType(cols[i]->resultType(), cc->resultType());
SimpleFilter* sf = new SimpleFilter(sop, cols[i]->clone(), cc);
if (i == 0)
{
pt = new ParseTree(sf);
}
else
{
ParseTree* left = pt;
pt = new ParseTree(new LogicOperator(lop));
pt->left(left);
pt->right(new ParseTree(sf));
}
}
if (i >= cols.size())
isScalar = true;
}
return isScalar;
}
void doSimpleScalarFilter(ParseTree* p, JobInfo& jobInfo)
{
SimpleScalarFilter* sf = dynamic_cast<SimpleScalarFilter*>(p->data());
idbassert(sf != NULL);
ParseTree* parseTree = NULL;
// Parse filters to job step.
if (simpleScalarFilterToParseTree(sf, parseTree, jobInfo))
{
// update the plan for supporting OR in future.
ParseTree* ccp = (p);
delete ccp->data();
ccp->left(parseTree->left());
ccp->right(parseTree->right());
ccp->data(parseTree->data());
// create job steps for each simple filter
JLF_ExecPlanToJobList::walkTree(parseTree, jobInfo);
jobInfo.dynamicParseTreeVec.push_back(parseTree);
// don't delete the parseTree, it has been placed in the plan.
// Instead, we use the dynamicParseTreeVec above for deletion
// in ~csep() or csep.unserialize().
// delete parseTree;
}
else
{
// not a scalar result
delete parseTree;
JobStepVector jsv;
SJSTEP tcs(new TupleConstantBooleanStep(jobInfo, false));
jsv.push_back(tcs);
JLF_ExecPlanToJobList::addJobSteps(jsv, jobInfo, false);
}
}
void doExistsFilter(const ParseTree* p, JobInfo& jobInfo)
{
const ExistsFilter* ef = dynamic_cast<const ExistsFilter*>(p->data());
idbassert(ef != NULL);
if (ef->correlated())
doCorrelatedExists(ef, jobInfo);
else
doNonCorrelatedExists(ef, jobInfo);
}
void doSelectFilter(const ParseTree* p, JobInfo& jobInfo)
{
const SelectFilter* sf = dynamic_cast<const SelectFilter*>(p->data());
idbassert(sf != NULL);
SErrorInfo errorInfo(jobInfo.errorInfo);
SubQueryTransformer transformer(&jobInfo, errorInfo);
SJSTEP subQueryStep = transformer.makeSubQueryStep(sf->sub().get());
transformer.updateCorrelateInfo();
JobStepVector jsv = transformer.correlatedSteps();
jsv.push_back(subQueryStep);
const vector<SRCP>& cols = sf->cols();
SOP sop = sf->op();
// For row construct, supports only =, <> in Release 1.1.
// Other operators are errored out by connector.
string lop("and");
if ((cols.size() > 1) && (sop->data() == "<>"))
lop = "or";
// @bug3780, select filters are not additional comparison, but fe2.
// When parsing the sub query, correlated columns may be added as group by column,
// s is the start position of the original selected columns.
uint64_t s = sf->returnedColPos();
ParseTree* pt = NULL;
const VirtualTable& vt = transformer.virtualTable();
for (uint64_t i = 0; i < cols.size(); i++)
{
ReturnedColumn* lhs = cols[i].get()->clone();
ReturnedColumn* rhs = vt.columns()[s + i].get()->clone();
sop->setOpType(lhs->resultType(), rhs->resultType());
if (i == 0)
{
pt = new ParseTree(new SimpleFilter(sop, lhs, rhs));
}
else
{
ParseTree* left = pt;
pt = new ParseTree(new LogicOperator(lop));
pt->left(left);
pt->right(new ParseTree(new SimpleFilter(sop, lhs, rhs)));
}
}
if (pt != NULL)
{
ExpressionStep* es = new ExpressionStep(jobInfo);
es->expressionFilter(pt, jobInfo);
es->selectFilter(true);
delete pt;
jsv.push_back(SJSTEP(es));
}
JLF_ExecPlanToJobList::addJobSteps(jsv, jobInfo, false);
}
void preprocessHavingClause(CalpontSelectExecutionPlan* csep, JobInfo& jobInfo)
{
ParseTree* havings = (csep->having());
idbassert(havings != NULL); // check having exists before calling this function.
// check select filter in having
havings->walk(sfInHaving, &jobInfo);
// check simple scalar filters in having
havings->walk(ssfInHaving, &jobInfo);
// check correlated columns in having
ParseTree* correlatedFilters = NULL;
havings->walk(getCorrelatedFilters, &correlatedFilters);
trim(havings);
if (havings == NULL)
{
csep->having(NULL);
}
if (correlatedFilters != NULL)
{
ParseTree* newFilters = new ParseTree(new LogicOperator("and"));
newFilters->left(csep->filters());
newFilters->right(correlatedFilters);
csep->filters(newFilters);
csep->having(havings);
}
}
int doFromSubquery(CalpontExecutionPlan* ep, const string& alias, const string& view, JobInfo& jobInfo)
{
CalpontSelectExecutionPlan* csep = dynamic_cast<CalpontSelectExecutionPlan*>(ep);
SErrorInfo errorInfo(jobInfo.errorInfo);
SubQueryTransformer transformer(&jobInfo, errorInfo, alias, view);
transformer.setVarbinaryOK();
SJSTEP subQueryStep = transformer.makeSubQueryStep(csep, true);
subQueryStep->view(view);
SJSTEP subAd(new SubAdapterStep(subQueryStep, jobInfo));
jobInfo.selectAndFromSubs.push_back(subAd);
return CNX_VTABLE_ID;
}
void addOrderByAndLimit(CalpontSelectExecutionPlan* csep, JobInfo& jobInfo)
{
jobInfo.limitStart = csep->limitStart();
jobInfo.limitCount = csep->limitNum();
jobInfo.orderByThreads = csep->orderByThreads();
CalpontSelectExecutionPlan::OrderByColumnList& orderByCols = csep->orderByCols();
for (uint64_t i = 0; i < orderByCols.size(); i++)
{
// skip constant columns
if (dynamic_cast<ConstantColumn*>(orderByCols[i].get()) != NULL)
continue;
uint32_t tupleKey = -1;
SimpleColumn* sc = dynamic_cast<SimpleColumn*>(orderByCols[i].get());
if (sc != NULL)
{
CalpontSystemCatalog::OID oid = sc->oid();
CalpontSystemCatalog::OID tblOid = tableOid(sc, jobInfo.csc);
CalpontSystemCatalog::OID dictOid = 0;
CalpontSystemCatalog::ColType ct;
string alias(extractTableAlias(sc));
string view(sc->viewName());
string schema(sc->schemaName());
// string name(sc->columnName());
if (!sc->schemaName().empty())
{
ct = sc->colType();
// XXX use this before connector sets colType in sc correctly.
// type of pseudo column is set by connector
if (sc->isColumnStore() && !(dynamic_cast<PseudoColumn*>(sc)))
{
ct = jobInfo.csc->colType(sc->oid());
ct.charsetNumber = sc->colType().charsetNumber;
}
// X
dictOid = isDictCol(ct);
}
else
{
if (sc->colPosition() == -1)
{
sc = dynamic_cast<SimpleColumn*>(jobInfo.deliveredCols[sc->orderPos()].get());
// If sc is NULL it's most likely a scaler subquery
if (sc == NULL)
{
const ReturnedColumn* rc = dynamic_cast<const ReturnedColumn*>(orderByCols[i].get());
uint32_t eid = rc->expressionId();
// If eid is -1, then there's no corresponding
// entry in tupleKeyMap and it will assert down the line
// Don't add the order by. It won't work and ordering on
// a singleton is a waste anyway.
if ((int32_t)eid == -1)
continue;
CalpontSystemCatalog::ColType ct = rc->resultType();
tupleKey = getExpTupleKey(jobInfo, eid);
jobInfo.orderByColVec.push_back(make_pair(tupleKey, orderByCols[i]->asc()));
continue;
}
}
else
{
sc->oid((tblOid + 1) + sc->colPosition());
}
oid = sc->oid();
ct = jobInfo.vtableColTypes[UniqId(oid, alias, "", "", execplan::Partitions())];
}
tupleKey = getTupleKey(jobInfo, sc);
// for dictionary columns, replace the token oid with string oid
if (dictOid > 0)
{
tupleKey = jobInfo.keyInfo->dictKeyMap[tupleKey];
}
}
else
{
const ReturnedColumn* rc = dynamic_cast<const ReturnedColumn*>(orderByCols[i].get());
uint64_t eid = rc->expressionId();
CalpontSystemCatalog::ColType ct = rc->resultType();
tupleKey = getExpTupleKey(jobInfo, eid);
}
jobInfo.orderByColVec.push_back(make_pair(tupleKey, orderByCols[i]->asc()));
}
}
void preprocessSelectSubquery(CalpontSelectExecutionPlan* csep, JobInfo& jobInfo)
{
RetColsVector& retCols = (csep->returnedCols());
CalpontSelectExecutionPlan::SelectList::const_iterator sub = csep->selectSubList().begin();
for (RetColsVector::iterator i = retCols.begin(); i != retCols.end(); i++)
{
if ((*i)->colSource() == execplan::SELECT_SUB)
{
(*i) = doSelectSubquery(sub->get(), *i, jobInfo);
sub++;
}
}
}
SJSTEP doUnionSub(CalpontExecutionPlan* ep, JobInfo& jobInfo)
{
CalpontSelectExecutionPlan* csep = dynamic_cast<CalpontSelectExecutionPlan*>(ep);
SErrorInfo errorInfo(jobInfo.errorInfo);
SubQueryTransformer transformer(&jobInfo, errorInfo);
transformer.setVarbinaryOK();
SJSTEP subQueryStep = transformer.makeSubQueryStep(csep, false);
SJSTEP subAd(new SubAdapterStep(subQueryStep, jobInfo));
return subAd;
}
} // namespace joblist