You've already forked mariadb-columnstore-engine
mirror of
https://github.com/mariadb-corporation/mariadb-columnstore-engine.git
synced 2025-07-30 19:23:07 +03:00
* Fix clang warnings * Remove vim tab guides * initialize variables * 'strncpy' output truncated before terminating nul copying as many bytes from a string as its length * Fix ISO C++17 does not allow 'register' storage class specifier for outdated bison * chars are unsigned on ARM, having if (ival < 0) always false * chars are unsigned by default on ARM and comparison with -1 if always true
878 lines
24 KiB
C++
878 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).c_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("");
|
|
cc->type(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, "", "")];
|
|
}
|
|
|
|
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
|