1
0
mirror of https://github.com/mariadb-corporation/mariadb-columnstore-engine.git synced 2025-04-18 21:44:02 +03:00
mariadb-columnstore-engine/dbcon/mysql/ha_mcs_execplan.cpp
Serguey Zefiov 6e539b8336 fix(MCOL-5889): Improper handle of DOUBLE result type with DECIMAL arguments
Sometimes server assigns DOUBLE type for arithmetic operations over
DECIMAL arguments. In this rare case width of result was incorrectly
adjusted and it triggered an assertion.

Now width of result gets adjusted only if result type is also DECIMAL.
2025-02-12 18:41:55 +04:00

11124 lines
335 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. */
#include <strings.h>
#include <string>
#include <iostream>
#include <stack>
#include <tr1/unordered_map>
#include <tr1/unordered_set>
#include <fstream>
#include <sstream>
#include <cerrno>
#include <cstring>
#include <time.h>
#include <cassert>
#include <vector>
#include <map>
#include <limits>
#include "messagelog.h"
#include <string.h>
using namespace std;
#include <boost/scoped_ptr.hpp>
#include <boost/shared_ptr.hpp>
#include <boost/algorithm/string/case_conv.hpp>
#include <boost/thread.hpp>
#include "errorids.h"
using namespace logging;
#define PREFER_MY_CONFIG_H
#include <my_config.h>
#include "idb_mysql.h"
#include "mcsv1_udaf.h"
#include "ha_mcs_impl_if.h"
#include "ha_mcs_sysvars.h"
#include "ha_subquery.h"
#include "ha_mcs_pushdown.h"
#include "ha_tzinfo.h"
#include "ha_mcs_logging.h"
using namespace cal_impl_if;
#include "aggregatecolumn.h"
#include "arithmeticcolumn.h"
#include "arithmeticoperator.h"
#include "calpontselectexecutionplan.h"
#include "calpontsystemcatalog.h"
#include "constantcolumn.h"
#include "constantfilter.h"
#include "existsfilter.h"
#include "functioncolumn.h"
#include "groupconcatcolumn.h"
#include "intervalcolumn.h"
#include "jsonarrayaggcolumn.h"
#include "logicoperator.h"
#include "outerjoinonfilter.h"
#include "predicateoperator.h"
#include "rewrites.h"
#include "rowcolumn.h"
#include "selectfilter.h"
#include "simplecolumn_decimal.h"
#include "simplecolumn_int.h"
#include "simplecolumn_uint.h"
#include "simplefilter.h"
#include "udafcolumn.h"
using namespace execplan;
#include "funcexp.h"
#include "functor.h"
using namespace funcexp;
#include "vlarray.h"
const uint64_t AGG_BIT = 0x01;
const uint64_t SUB_BIT = 0x02;
const uint64_t AF_BIT = 0x04;
const uint64_t CORRELATED = 0x08;
// In certain cases, gp_walk is called recursively. When done so,
// we need to bookmark the rcWorkStack for those cases where a constant
// expression such as 1=1 is used in an if statement or function call.
// This is a seriously bad kludge for MariaDB bug 750.
//
// BM => BookMark
// HWM => HighWaterMark
class RecursionCounter
{
private:
RecursionCounter()
{
}
public:
RecursionCounter(gp_walk_info* gwip) : fgwip(gwip)
{
++fgwip->recursionLevel;
if (fgwip->recursionLevel > fgwip->recursionHWM)
{
fgwip->rcBookMarkStack.push(fgwip->rcWorkStack.size());
fgwip->recursionHWM = fgwip->recursionLevel;
}
}
~RecursionCounter()
{
--fgwip->recursionLevel;
if (fgwip->recursionLevel < fgwip->recursionHWM - 1)
{
fgwip->rcBookMarkStack.pop();
--fgwip->recursionHWM;
}
}
gp_walk_info* fgwip;
};
#include "ha_view.h"
namespace cal_impl_if
{
// This is taken from Item_cond::fix_fields in sql/item_cmpfunc.cc.
void calculateNotNullTables(const std::vector<COND*>& condList, table_map& not_null_tables)
{
for (Item* item : condList)
{
if (item->can_eval_in_optimize() && !item->with_sp_var() && !cond_has_datetime_is_null(item))
{
if (item->eval_const_cond())
{
}
else
{
not_null_tables = (table_map)0;
}
}
else
{
not_null_tables |= item->not_null_tables();
}
}
}
bool itemDisablesWrapping(Item* item, gp_walk_info& gwi);
void pushReturnedCol(gp_walk_info& gwi, Item* from, SRCP rc)
{
uint32_t i;
for ( i = 0; i < gwi.processed.size(); i++)
{
Item* ith = gwi.processed[i].first;
bool same = ith->eq(from, false);
if (same && ith->type() == Item::FUNC_ITEM)
{
// an exception for cast(column as decimal(X,Y)) - they are equal (in the eq() call sense)
// even if Xs and Ys are different.
string funcName = ((Item_func*)ith)->func_name();
if (funcName == "decimal_typecast")
{
Item_decimal_typecast* ithdtc = (Item_decimal_typecast*)ith;
Item_decimal_typecast* fromdtc = (Item_decimal_typecast*)from;
same = ithdtc->decimals == fromdtc->decimals && ithdtc->max_length == fromdtc->max_length;
}
}
if (same)
{
break;
}
}
bool needChange = true;
if (dynamic_cast<SimpleColumn*>(rc.get()) == nullptr && gwi.select_lex && !itemDisablesWrapping(from, gwi))
{
needChange = false;
}
if (needChange && i < gwi.processed.size())
{
rc->expressionId(gwi.processed[i].second);
}
else
{
gwi.processed.push_back(std::make_pair(from, rc->expressionId()));
}
gwi.returnedCols.push_back(rc);
}
// Recursively iterate through the join_list and store all non-null
// TABLE_LIST::on_expr items to a hash map keyed by the TABLE_LIST ptr.
// This is then used by convertOuterJoinToInnerJoin().
void buildTableOnExprList(List<TABLE_LIST>* join_list, TableOnExprList& tableOnExprList)
{
TABLE_LIST* table;
NESTED_JOIN* nested_join;
List_iterator<TABLE_LIST> li(*join_list);
while ((table = li++))
{
if ((nested_join = table->nested_join))
{
buildTableOnExprList(&nested_join->join_list, tableOnExprList);
}
if (table->on_expr)
tableOnExprList[table].push_back(table->on_expr);
}
}
// This is a trimmed down version of simplify_joins() in sql/sql_select.cc.
// Refer to that function for more details. But we have customized the
// original implementation in simplify_joins() to avoid any changes to
// the SELECT_LEX::JOIN::conds. Specifically, in some cases, simplify_joins()
// would create new Item_cond_and objects. We want to avoid such changes to
// the SELECT_LEX in a scenario where the select_handler execution has failed
// and we want to fallback to the server execution (MCOL-4525). Here, we mimick
// the creation of Item_cond_and using tableOnExprList and condList.
void convertOuterJoinToInnerJoin(List<TABLE_LIST>* join_list, TableOnExprList& tableOnExprList,
std::vector<COND*>& condList, TableOuterJoinMap& tableOuterJoinMap)
{
TABLE_LIST* table;
NESTED_JOIN* nested_join;
List_iterator<TABLE_LIST> li(*join_list);
while ((table = li++))
{
table_map used_tables;
table_map not_null_tables = (table_map)0;
if ((nested_join = table->nested_join))
{
auto iter = tableOnExprList.find(table);
if ((iter != tableOnExprList.end()) && !iter->second.empty())
{
convertOuterJoinToInnerJoin(&nested_join->join_list, tableOnExprList, tableOnExprList[table],
tableOuterJoinMap);
}
nested_join->used_tables = (table_map)0;
nested_join->not_null_tables = (table_map)0;
convertOuterJoinToInnerJoin(&nested_join->join_list, tableOnExprList, condList, tableOuterJoinMap);
used_tables = nested_join->used_tables;
not_null_tables = nested_join->not_null_tables;
}
else
{
used_tables = table->get_map();
if (!condList.empty())
{
if (condList.size() == 1)
{
not_null_tables = condList[0]->not_null_tables();
}
else
{
calculateNotNullTables(condList, not_null_tables);
}
}
}
if (table->embedding)
{
table->embedding->nested_join->used_tables |= used_tables;
table->embedding->nested_join->not_null_tables |= not_null_tables;
}
if (!(table->outer_join & (JOIN_TYPE_LEFT | JOIN_TYPE_RIGHT)) || (used_tables & not_null_tables))
{
if (table->outer_join)
{
tableOuterJoinMap[table] = table->outer_join;
}
table->outer_join = 0;
auto iter = tableOnExprList.find(table);
if (iter != tableOnExprList.end() && !iter->second.empty())
{
// The original implementation in simplify_joins() creates
// an Item_cond_and object here. Instead of doing so, we
// append the table->on_expr to the condList and hence avoid
// making any permanent changes to SELECT_LEX::JOIN::conds.
condList.insert(condList.end(), tableOnExprList[table].begin(), tableOnExprList[table].end());
iter->second.clear();
}
}
}
}
CalpontSystemCatalog::TableAliasName makeTableAliasName(TABLE_LIST* table)
{
return make_aliasview(
(table->db.length ? table->db.str : ""), (table->table_name.length ? table->table_name.str : ""),
(table->alias.length ? table->alias.str : ""), getViewName(table), true, lower_case_table_names);
}
//@bug5228. need to escape backtick `
string escapeBackTick(const char* str)
{
if (!str)
return "";
string ret;
for (uint32_t i = 0; str[i] != 0; i++)
{
if (str[i] == '`')
ret.append("``");
else
ret.append(1, str[i]);
}
return ret;
}
cal_impl_if::gp_walk_info::~gp_walk_info()
{
while (!rcWorkStack.empty())
{
delete rcWorkStack.top();
rcWorkStack.pop();
}
while (!ptWorkStack.empty())
{
delete ptWorkStack.top();
ptWorkStack.pop();
}
for (uint32_t i=0;i<viewList.size();i++) {
delete viewList[i];
}
viewList.clear();
}
void clearStacks(gp_walk_info& gwi, bool andViews = true, bool mayDelete = false)
{
while (!gwi.rcWorkStack.empty())
{
if (mayDelete)
{
delete gwi.rcWorkStack.top();
}
gwi.rcWorkStack.pop();
}
while (!gwi.ptWorkStack.empty())
{
if (mayDelete)
{
delete gwi.ptWorkStack.top();
}
gwi.ptWorkStack.pop();
}
if (andViews)
{
gwi.viewList.clear();
}
}
void clearDeleteStacks(gp_walk_info& gwi)
{
while (!gwi.rcWorkStack.empty())
{
delete gwi.rcWorkStack.top();
gwi.rcWorkStack.pop();
}
while (!gwi.ptWorkStack.empty())
{
delete gwi.ptWorkStack.top();
gwi.ptWorkStack.pop();
}
for (uint32_t i=0;i<gwi.viewList.size();i++) {
delete gwi.viewList[i];
}
gwi.viewList.clear();
}
bool nonConstFunc(Item_func* ifp)
{
if (strcasecmp(ifp->func_name(), "rand") == 0 || strcasecmp(ifp->func_name(), "sysdate") == 0 ||
strcasecmp(ifp->func_name(), "idblocalpm") == 0)
return true;
for (uint32_t i = 0; i < ifp->argument_count(); i++)
{
if (ifp->arguments()[i]->type() == Item::FUNC_ITEM && nonConstFunc(((Item_func*)ifp->arguments()[i])))
return true;
}
return false;
}
/*@brief getColNameFromItem - builds a name from an Item */
/***********************************************************
* DESCRIPTION:
* This f() looks for a first proper Item_ident and populate
* ostream with schema, table and column names.
* Used to build db.table.field tuple for debugging output
* in getSelectPlan(). TBD getGroupPlan must use this also.
* PARAMETERS:
* item source Item*
* ostream output stream
* RETURNS
* void
***********************************************************/
void getColNameFromItem(std::ostringstream& ostream, Item* item)
{
// Item_func doesn't have proper db.table.field values
// inherited from Item_ident. TBD what is the valid output.
// !!!dynamic_cast fails compilation
ostream << "'";
if (item->type() != Item::FIELD_ITEM)
{
ostream << "unknown db" << '.';
ostream << "unknown table" << '.';
ostream << "unknown field";
}
else
{
Item_ident* iip = static_cast<Item_ident*>(item);
if (iip->db_name.str)
ostream << iip->db_name.str << '.';
else
ostream << "unknown db" << '.';
if (iip->table_name.str)
ostream << iip->table_name.str << '.';
else
ostream << "unknown table" << '.';
if (iip->field_name.length)
ostream << iip->field_name.str;
else
ostream << "unknown field";
}
ostream << "'";
return;
}
/*@brf sortItemIsInGroupRec - seeks for an item in grouping*/
/***********************************************************
* DESCRIPTION:
* This f() recursively traverses grouping items and looks
* for an FUNC_ITEM, REF_ITEM or FIELD_ITEM.
* f() is used by sortItemIsInGrouping().
* PARAMETERS:
* sort_item Item* used to build aggregation.
* group_item GROUP BY item.
* RETURNS
* bool
***********************************************************/
bool sortItemIsInGroupRec(Item* sort_item, Item* group_item)
{
bool found = false;
// If ITEM_REF::ref is NULL
if (sort_item == NULL)
{
return found;
}
// base cases for Item_field and Item_ref. The second arg is binary cmp switch
found = group_item->eq(sort_item, false);
if (!found && sort_item->type() == Item::REF_ITEM)
{
Item_ref* ifp_sort_ref = static_cast<Item_ref*>(sort_item);
found = sortItemIsInGroupRec(*ifp_sort_ref->ref, group_item);
}
else if (sort_item->type() == Item::FIELD_ITEM)
{
return found;
}
Item_func* ifp_sort = static_cast<Item_func*>(sort_item);
// seeking for a group_item match
for (uint32_t i = 0; !found && i < ifp_sort->argument_count(); i++)
{
Item* ifp_sort_arg = ifp_sort->arguments()[i];
if (ifp_sort_arg->type() == Item::FUNC_ITEM || ifp_sort_arg->type() == Item::FIELD_ITEM)
{
Item* ifp_sort_arg = ifp_sort->arguments()[i];
found = sortItemIsInGroupRec(ifp_sort_arg, group_item);
}
else if (ifp_sort_arg->type() == Item::REF_ITEM)
{
// dereference the Item
Item_ref* ifp_sort_ref = static_cast<Item_ref*>(ifp_sort_arg);
found = sortItemIsInGroupRec(*ifp_sort_ref->ref, group_item);
}
}
return found;
}
/*@brief check_sum_func_item - This traverses Item */
/**********************************************************
* DESCRIPTION:
* This f() walks Item looking for the existence of
* a Item::REF_ITEM, which references an item of
* type Item::SUM_FUNC_ITEM
* PARAMETERS:
* Item * Item to traverse
* RETURN:
*********************************************************/
void check_sum_func_item(const Item* item, void* arg)
{
bool* found = static_cast<bool*>(arg);
if (*found)
return;
if (item->type() == Item::REF_ITEM)
{
const Item_ref* ref_item = static_cast<const Item_ref*>(item);
Item* ref_item_item = (Item*)*ref_item->ref;
if (ref_item_item->type() == Item::SUM_FUNC_ITEM)
{
*found = true;
}
}
else if (item->type() == Item::CONST_ITEM)
{
*found = true;
}
}
/*@brief sortItemIsInGrouping- seeks for an item in grouping*/
/***********************************************************
* DESCRIPTION:
* This f() traverses grouping items and looks for an item.
* only Item_fields, Item_func are considered. However there
* could be Item_ref down the tree.
* f() is used in sorting parsing by getSelectPlan().
* PARAMETERS:
* sort_item Item* used to build aggregation.
* groupcol GROUP BY items from this unit.
* RETURNS
* bool
***********************************************************/
bool sortItemIsInGrouping(Item* sort_item, ORDER* groupcol)
{
bool found = false;
if (sort_item->type() == Item::SUM_FUNC_ITEM)
{
found = true;
return found;
}
{
// as we now can warp ORDER BY or SELECT expression into
// an aggregate, we can pass FIELD_ITEM as "found" as well.
Item* item = sort_item;
while (item->type() == Item::REF_ITEM)
{
const Item_ref* ref_item = static_cast<const Item_ref*>(item);
item = (Item*)*ref_item->ref;
}
if (item->type() == Item::FIELD_ITEM || item->type() == Item::CONST_ITEM || item->type() == Item::NULL_ITEM)
{
return true;
}
}
// A function that contains an aggregate function
// can be included in the ORDER BY clause
// e.g. select a, if (sum(b) > 1, 2, 1) from t1 group by 1 order by 2;
if (sort_item->type() == Item::FUNC_ITEM)
{
Item_func* ifp = static_cast<Item_func*>(sort_item);
ifp->traverse_cond(check_sum_func_item, &found, Item::POSTFIX);
}
else if (sort_item->type() == Item::CONST_ITEM || sort_item->type() == Item::WINDOW_FUNC_ITEM)
{
found = true;
}
for (; !found && groupcol; groupcol = groupcol->next)
{
Item* group_item = *(groupcol->item);
found = (group_item->eq(sort_item, false)) ? true : false;
// Detect aggregation functions first then traverse
// if sort field is a Func and group field
// is either Field or Func
// Consider nonConstFunc() check here
if (!found && sort_item->type() == Item::FUNC_ITEM &&
(group_item->type() == Item::FUNC_ITEM || group_item->type() == Item::FIELD_ITEM ||
group_item->type() == Item::REF_ITEM))
{
// MCOL-5236: see @bug5993 and @bug5916.
Item* item = group_item;
while (item->type() == Item::REF_ITEM)
{
Item_ref* item_ref = static_cast<Item_ref*>(item);
item = *item_ref->ref;
}
found = sortItemIsInGroupRec(sort_item, item);
}
}
return found;
}
/*@brief buildAggFrmTempField- build aggr func from extSELECT list item*/
/***********************************************************
* DESCRIPTION:
* Server adds additional aggregation items to extended SELECT list and
* references them in projection and HAVING. This f() finds
* corresponding item in extSelAggColsItems and builds
* ReturnedColumn using the item.
* PARAMETERS:
* item Item* used to build aggregation
* gwi main structure
* RETURNS
* ReturnedColumn* if corresponding Item has been found
* NULL otherwise
***********************************************************/
ReturnedColumn* buildAggFrmTempField(Item* item, gp_walk_info& gwi)
{
ReturnedColumn* result = NULL;
Item_field* ifip = NULL;
Item_ref* irip;
Item_func_or_sum* isfp;
switch (item->type())
{
case Item::FIELD_ITEM: ifip = static_cast<Item_field*>(item); break;
default:
irip = static_cast<Item_ref*>(item);
if (irip)
ifip = static_cast<Item_field*>(irip->ref[0]);
break;
}
if (ifip && ifip->field)
{
std::vector<Item*>::iterator iter = gwi.extSelAggColsItems.begin();
for (; iter != gwi.extSelAggColsItems.end(); iter++)
{
isfp = static_cast<Item_func_or_sum*>(*iter);
if (isfp->type() == Item::SUM_FUNC_ITEM && isfp->result_field == ifip->field)
{
ReturnedColumn* rc = buildAggregateColumn(isfp, gwi);
if (rc)
result = rc;
break;
}
}
}
return result;
}
/*@brief isDuplicateSF - search for a duplicate SimpleFilter*/
/***********************************************************
* DESCRIPTION:
* As of 1.4 CS potentially has duplicate filter predicates
* in both join->conds and join->cond_equal. This utility f()
* searches for semantically equal SF in the list of already
* applied equi predicates.
* TODO
* We must move find_select_handler to either future or
* later execution phase.
* PARAMETERS:
* gwi main structure
* sf SimpleFilter * to find
* RETURNS
* true if sf has been found in the applied SF list
* false otherwise
* USED
* buildPredicateItem()
***********************************************************/
bool isDuplicateSF(gp_walk_info* gwip, execplan::SimpleFilter* sfp)
{
List_iterator<execplan::SimpleFilter> it(gwip->equiCondSFList);
execplan::SimpleFilter* isfp;
while ((isfp = it++))
{
if (sfp->semanticEq(*isfp))
{
return true;
}
}
return false;
}
// DESCRIPTION:
// This f() checks if the arguments is a UDF Item
// PARAMETERS:
// Item * Item to traverse
// RETURN:
// bool
inline bool isUDFSumItem(const Item_sum* isp)
{
return typeid(*isp) == typeid(Item_sum_udf_int) || typeid(*isp) == typeid(Item_sum_udf_float) ||
typeid(*isp) == typeid(Item_sum_udf_decimal) || typeid(*isp) == typeid(Item_sum_udf_str);
}
string getViewName(TABLE_LIST* table_ptr)
{
string viewName = "";
if (!table_ptr)
return viewName;
TABLE_LIST* view = table_ptr->referencing_view;
if (view)
{
if (!view->derived)
viewName = view->alias.str;
while ((view = view->referencing_view))
{
if (view->derived)
continue;
viewName = view->alias.str + string(".") + viewName;
}
}
return viewName;
}
#ifdef DEBUG_WALK_COND
void debug_walk(const Item* item, void* arg)
{
switch (item->type())
{
case Item::FIELD_ITEM:
{
Item_field* ifp = (Item_field*)item;
cerr << "FIELD_ITEM: " << (ifp->db_name.str ? ifp->db_name.str : "") << '.' << bestTableName(ifp) << '.'
<< ifp->field_name.str << endl;
break;
}
case Item::CONST_ITEM:
{
switch (item->cmp_type())
{
case INT_RESULT:
{
Item_int* iip = (Item_int*)item;
cerr << "INT_ITEM: ";
if (iip->name.length)
cerr << iip->name.str << " (from name string)" << endl;
else
cerr << iip->val_int() << endl;
break;
}
case STRING_RESULT:
{
Item_string* isp = (Item_string*)item;
String val, *str = isp->val_str(&val);
string valStr;
valStr.assign(str->ptr(), str->length());
cerr << "STRING_ITEM: >" << valStr << '<' << endl;
break;
}
case REAL_RESULT:
{
cerr << "REAL_ITEM" << endl;
break;
}
case DECIMAL_RESULT:
{
cerr << "DECIMAL_ITEM" << endl;
break;
}
case TIME_RESULT:
{
String val, *str = NULL;
Item_temporal_literal* itp = (Item_temporal_literal*)item;
str = itp->val_str(&val);
cerr << "DATE ITEM: ";
if (str)
cerr << ": (" << str->ptr() << ')' << endl;
else
cerr << ": <NULL>" << endl;
break;
}
default:
{
cerr << ": Unknown cmp_type" << endl;
break;
}
}
break;
}
case Item::FUNC_ITEM:
{
Item_func* ifp = (Item_func*)item;
Item_func_opt_neg* inp;
cerr << "FUNC_ITEM: ";
switch (ifp->functype())
{
case Item_func::UNKNOWN_FUNC: // 0
cerr << ifp->func_name() << " (" << ifp->functype() << ")" << endl;
break;
case Item_func::GT_FUNC: // 7
cerr << '>' << " (" << ifp->functype() << ")" << endl;
break;
case Item_func::EQ_FUNC: // 1
cerr << '=' << " (" << ifp->functype() << ")" << endl;
break;
case Item_func::GE_FUNC:
cerr << ">="
<< " (" << ifp->functype() << ")" << endl;
break;
case Item_func::LE_FUNC:
cerr << "<="
<< " (" << ifp->functype() << ")" << endl;
break;
case Item_func::LT_FUNC: cerr << '<' << " (" << ifp->functype() << ")" << endl; break;
case Item_func::NE_FUNC:
cerr << "<>"
<< " (" << ifp->functype() << ")" << endl;
break;
case Item_func::NEG_FUNC: // 45
cerr << "unary minus"
<< " (" << ifp->functype() << ")" << endl;
break;
case Item_func::IN_FUNC: // 16
inp = (Item_func_opt_neg*)ifp;
if (inp->negated)
cerr << "not ";
cerr << "in"
<< " (" << ifp->functype() << ")" << endl;
break;
case Item_func::BETWEEN:
inp = (Item_func_opt_neg*)ifp;
if (inp->negated)
cerr << "not ";
cerr << "between"
<< " (" << ifp->functype() << ")" << endl;
break;
case Item_func::ISNULL_FUNC: // 10
cerr << "is null"
<< " (" << ifp->functype() << ")" << endl;
break;
case Item_func::ISNOTNULL_FUNC: // 11
cerr << "is not null"
<< " (" << ifp->functype() << ")" << endl;
break;
case Item_func::NOT_ALL_FUNC:
cerr << "not_all"
<< " (" << ifp->functype() << ")" << endl;
break;
case Item_func::NOT_FUNC:
cerr << "not_func"
<< " (" << ifp->functype() << ")" << endl;
break;
case Item_func::TRIG_COND_FUNC:
cerr << "trig_cond_func"
<< " (" << ifp->functype() << ")" << endl;
break;
case Item_func::ISNOTNULLTEST_FUNC:
cerr << "isnotnulltest_func"
<< " (" << ifp->functype() << ")" << endl;
break;
case Item_func::MULT_EQUAL_FUNC:
{
cerr << "mult_equal_func:"
<< " (" << ifp->functype() << ")" << endl;
Item_equal* item_eq = (Item_equal*)ifp;
Item_equal_fields_iterator it(*item_eq);
Item* item;
while ((item = it++))
{
Field* equal_field = it.get_curr_field();
cerr << equal_field->field_name.str << endl;
}
break;
}
case Item_func::EQUAL_FUNC:
cerr << "equal func"
<< " (" << ifp->functype() << ")" << endl;
break;
case Item_func::FT_FUNC:
cerr << "ft func"
<< " (" << ifp->functype() << ")" << endl;
break;
case Item_func::LIKE_FUNC:
cerr << "like func"
<< " (" << ifp->functype() << ")" << endl;
break;
case Item_func::COND_AND_FUNC:
cerr << "cond and func"
<< " (" << ifp->functype() << ")" << endl;
break;
case Item_func::COND_OR_FUNC:
cerr << "cond or func"
<< " (" << ifp->functype() << ")" << endl;
break;
case Item_func::XOR_FUNC:
cerr << "xor func"
<< " (" << ifp->functype() << ")" << endl;
break;
case Item_func::INTERVAL_FUNC:
cerr << "interval func"
<< " (" << ifp->functype() << ")" << endl;
break;
case Item_func::SP_EQUALS_FUNC:
cerr << "sp equals func"
<< " (" << ifp->functype() << ")" << endl;
break;
case Item_func::SP_DISJOINT_FUNC:
cerr << "sp disjoint func"
<< " (" << ifp->functype() << ")" << endl;
break;
case Item_func::SP_INTERSECTS_FUNC:
cerr << "sp intersects func"
<< " (" << ifp->functype() << ")" << endl;
break;
case Item_func::SP_TOUCHES_FUNC:
cerr << "sp touches func"
<< " (" << ifp->functype() << ")" << endl;
break;
case Item_func::SP_CROSSES_FUNC:
cerr << "sp crosses func"
<< " (" << ifp->functype() << ")" << endl;
break;
case Item_func::SP_WITHIN_FUNC:
cerr << "sp within func"
<< " (" << ifp->functype() << ")" << endl;
break;
case Item_func::SP_CONTAINS_FUNC:
cerr << "sp contains func"
<< " (" << ifp->functype() << ")" << endl;
break;
case Item_func::SP_OVERLAPS_FUNC:
cerr << "sp overlaps func"
<< " (" << ifp->functype() << ")" << endl;
break;
case Item_func::SP_STARTPOINT:
cerr << "sp startpoint func"
<< " (" << ifp->functype() << ")" << endl;
break;
case Item_func::SP_ENDPOINT:
cerr << "sp endpoint func"
<< " (" << ifp->functype() << ")" << endl;
break;
case Item_func::SP_EXTERIORRING:
cerr << "sp exteriorring func"
<< " (" << ifp->functype() << ")" << endl;
break;
case Item_func::SP_POINTN:
cerr << "sp pointn func"
<< " (" << ifp->functype() << ")" << endl;
break;
case Item_func::SP_GEOMETRYN:
cerr << "sp geometryn func"
<< " (" << ifp->functype() << ")" << endl;
break;
case Item_func::SP_INTERIORRINGN:
cerr << "sp exteriorringn func"
<< " (" << ifp->functype() << ")" << endl;
break;
case Item_func::SP_RELATE_FUNC:
cerr << "sp relate func"
<< " (" << ifp->functype() << ")" << endl;
break;
case Item_func::NOW_FUNC:
cerr << "now func"
<< " (" << ifp->functype() << ")" << endl;
break;
case Item_func::SUSERVAR_FUNC:
cerr << "suservar func"
<< " (" << ifp->functype() << ")" << endl;
break;
case Item_func::GUSERVAR_FUNC:
cerr << "guservar func"
<< " (" << ifp->functype() << ")" << endl;
break;
case Item_func::COLLATE_FUNC:
cerr << "collate func"
<< " (" << ifp->functype() << ")" << endl;
break;
case Item_func::EXTRACT_FUNC:
cerr << "extract func"
<< " (" << ifp->functype() << ")" << endl;
break;
case Item_func::CHAR_TYPECAST_FUNC:
cerr << "char typecast func"
<< " (" << ifp->functype() << ")" << endl;
break;
case Item_func::FUNC_SP:
cerr << "func sp func"
<< " (" << ifp->functype() << ")" << endl;
break;
case Item_func::UDF_FUNC:
cerr << "udf func"
<< " (" << ifp->functype() << ")" << endl;
break;
case Item_func::GSYSVAR_FUNC:
cerr << "gsysvar func"
<< " (" << ifp->functype() << ")" << endl;
break;
case Item_func::DYNCOL_FUNC:
cerr << "dyncol func"
<< " (" << ifp->functype() << ")" << endl;
break;
default: cerr << "type=" << ifp->functype() << endl; break;
}
break;
}
case Item::COND_ITEM:
{
Item_cond* icp = (Item_cond*)item;
cerr << "COND_ITEM: " << icp->func_name() << endl;
break;
}
case Item::SUM_FUNC_ITEM:
{
Item_sum* isp = (Item_sum*)item;
char* item_name = const_cast<char*>(item->name.str);
// MCOL-1052 This is an extended SELECT list item
if (!item_name && isp->get_arg_count() && isp->get_arg(0)->name.length)
{
item_name = const_cast<char*>(isp->get_arg(0)->name.str);
}
else if (!item_name && isp->get_arg_count() && isp->get_arg(0)->type() == Item::CONST_ITEM &&
isp->get_arg(0)->cmp_type() == INT_RESULT)
{
item_name = (char*)"INT||*";
}
else if (!item_name)
{
item_name = (char*)"<NULL>";
}
switch (isp->sum_func())
{
case Item_sum::SUM_FUNC: cerr << "SUM_FUNC: " << item_name << endl; break;
case Item_sum::SUM_DISTINCT_FUNC: cerr << "SUM_DISTINCT_FUNC: " << item_name << endl; break;
case Item_sum::AVG_FUNC: cerr << "AVG_FUNC: " << item_name << endl; break;
case Item_sum::COUNT_FUNC: cerr << "COUNT_FUNC: " << item_name << endl; break;
case Item_sum::COUNT_DISTINCT_FUNC: cerr << "COUNT_DISTINCT_FUNC: " << item_name << endl; break;
case Item_sum::MIN_FUNC: cerr << "MIN_FUNC: " << item_name << endl; break;
case Item_sum::MAX_FUNC: cerr << "MAX_FUNC: " << item_name << endl; break;
case Item_sum::UDF_SUM_FUNC: cerr << "UDAF_FUNC: " << item_name << endl; break;
default: cerr << "SUM_FUNC_ITEM type=" << isp->sum_func() << endl; break;
}
break;
}
case Item::SUBSELECT_ITEM:
{
Item_subselect* sub = (Item_subselect*)item;
cerr << "SUBSELECT Item: ";
switch (sub->substype())
{
case Item_subselect::EXISTS_SUBS: cerr << "EXISTS"; break;
case Item_subselect::IN_SUBS: cerr << "IN"; break;
default: cerr << sub->substype(); break;
}
cerr << endl;
JOIN* join = sub->get_select_lex()->join;
if (join)
{
Item_cond* cond = static_cast<Item_cond*>(join->conds);
if (cond)
cond->traverse_cond(debug_walk, arg, Item::POSTFIX);
}
cerr << "Finish subselect item traversing" << endl;
break;
}
case Item::REF_ITEM:
{
Item_ref* ref = (Item_ref*)item;
if (ref->real_item()->type() == Item::CACHE_ITEM)
{
Item* field = ((Item_cache*)ref->real_item())->get_example();
if (field->type() == Item::FIELD_ITEM)
{
Item_field* ifp = (Item_field*)field;
// ifp->cached_table->select_lex->select_number gives the select level.
// could be used on alias.
// could also be used to tell correlated join (equal level).
cerr << "CACHED REF FIELD_ITEM: " << ifp->db_name.str << '.' << bestTableName(ifp) << '.'
<< ifp->field_name.str << endl;
break;
}
else if (field->type() == Item::FUNC_ITEM)
{
Item_func* ifp = (Item_func*)field;
cerr << "CACHED REF FUNC_ITEM " << ifp->func_name() << endl;
}
else if (field->type() == Item::REF_ITEM)
{
Item_ref* ifr = (Item_ref*)field;
string refType;
string realType;
switch (ifr->ref_type())
{
case Item_ref::REF: refType = "REF"; break;
case Item_ref::DIRECT_REF: refType = "DIRECT_REF"; break;
case Item_ref::VIEW_REF: refType = "VIEW_REF"; break;
case Item_ref::OUTER_REF: refType = "OUTER_REF"; break;
case Item_ref::AGGREGATE_REF: refType = "AGGREGATE_REF"; break;
default: refType = "UNKNOWN"; break;
}
switch (ifr->real_type())
{
case Item::FIELD_ITEM:
{
Item_field* ifp = (Item_field*)(*(ifr->ref));
realType = "FIELD_ITEM ";
realType += ifp->db_name.str;
realType += '.';
realType += bestTableName(ifp);
realType += '.';
realType += ifp->field_name.str;
break;
}
case Item::SUM_FUNC_ITEM:
{
Item_sum* isp = (Item_sum*)(*(ifr->ref));
if (isp->sum_func() == Item_sum::GROUP_CONCAT_FUNC)
realType = "GROUP_CONCAT_FUNC";
else
realType = "SUM_FUNC_ITEM";
break;
}
case Item::REF_ITEM:
// Need recursion here
realType = "REF_ITEM";
break;
case Item::FUNC_ITEM:
{
Item_func* ifp = (Item_func*)(*(ifr->ref));
realType = "FUNC_ITEM ";
realType += ifp->func_name();
break;
}
default:
{
realType = "UNKNOWN";
}
}
cerr << "CACHED REF_ITEM: ref type " << refType.c_str() << " real type " << realType.c_str()
<< endl;
break;
}
else
{
cerr << "REF_ITEM with CACHE_ITEM type unknown " << field->type() << endl;
}
}
else if (ref->real_item()->type() == Item::FIELD_ITEM)
{
Item_field* ifp = (Item_field*)ref->real_item();
// MCOL-1052 The field referenced presumable came from
// extended SELECT list.
if (!ifp->field_name.str)
{
cerr << "REF extra FIELD_ITEM: " << ifp->name.str << endl;
}
else
{
cerr << "REF FIELD_ITEM: " << ifp->db_name.str << '.' << bestTableName(ifp) << '.'
<< ifp->field_name.str << endl;
}
break;
}
else if (ref->real_item()->type() == Item::FUNC_ITEM)
{
Item_func* ifp = (Item_func*)ref->real_item();
cerr << "REF FUNC_ITEM " << ifp->func_name() << endl;
}
else if (ref->real_item()->type() == Item::WINDOW_FUNC_ITEM)
{
Item_window_func* ifp = (Item_window_func*)ref->real_item();
cerr << "REF WINDOW_FUNC_ITEM " << ifp->window_func()->func_name() << endl;
}
else
{
cerr << "UNKNOWN REF ITEM type " << ref->real_item()->type() << endl;
}
break;
}
case Item::ROW_ITEM:
{
Item_row* row = (Item_row*)item;
cerr << "ROW_ITEM: " << endl;
for (uint32_t i = 0; i < row->cols(); i++)
debug_walk(row->element_index(i), 0);
break;
}
case Item::EXPR_CACHE_ITEM:
{
cerr << "Expr Cache Item" << endl;
((Item_cache_wrapper*)item)->get_orig_item()->traverse_cond(debug_walk, arg, Item::POSTFIX);
break;
}
case Item::CACHE_ITEM:
{
Item_cache* isp = (Item_cache*)item;
// MCOL-46 isp->val_str() can cause a call to execute a subquery. We're not set up
// to execute yet.
#if 0
switch (item->result_type())
{
case STRING_RESULT:
cerr << "CACHE_STRING_ITEM" << endl;
break;
case REAL_RESULT:
cerr << "CACHE_REAL_ITEM " << isp->val_real() << endl;
break;
case INT_RESULT:
cerr << "CACHE_INT_ITEM " << isp->val_int() << endl;
break;
case ROW_RESULT:
cerr << "CACHE_ROW_ITEM" << endl;
break;
case DECIMAL_RESULT:
cerr << "CACHE_DECIMAL_ITEM " << isp->val_decimal() << endl;
break;
default:
cerr << "CACHE_UNKNOWN_ITEM" << endl;
break;
}
#endif
Item* field = isp->get_example();
if (field->type() == Item::FIELD_ITEM)
{
Item_field* ifp = (Item_field*)field;
// ifp->cached_table->select_lex->select_number gives the select level.
// could be used on alias.
// could also be used to tell correlated join (equal level).
cerr << "CACHED FIELD_ITEM: " << ifp->db_name.str << '.' << bestTableName(ifp) << '.'
<< ifp->field_name.str << endl;
break;
}
else if (field->type() == Item::REF_ITEM)
{
Item_ref* ifr = (Item_ref*)field;
string refType;
string realType;
switch (ifr->ref_type())
{
case Item_ref::REF: refType = "REF"; break;
case Item_ref::DIRECT_REF: refType = "DIRECT_REF"; break;
case Item_ref::VIEW_REF: refType = "VIEW_REF"; break;
case Item_ref::OUTER_REF: refType = "OUTER_REF"; break;
case Item_ref::AGGREGATE_REF: refType = "AGGREGATE_REF"; break;
default: refType = "UNKNOWN"; break;
}
switch (ifr->real_type())
{
case Item::FIELD_ITEM:
{
Item_field* ifp = (Item_field*)(*(ifr->ref));
realType = "FIELD_ITEM ";
realType += ifp->db_name.str;
realType += '.';
realType += bestTableName(ifp);
realType += '.';
realType += ifp->field_name.str;
break;
}
case Item::SUM_FUNC_ITEM:
{
Item_sum* isp = (Item_sum*)(*(ifr->ref));
if (isp->sum_func() == Item_sum::GROUP_CONCAT_FUNC)
realType = "GROUP_CONCAT_FUNC";
else
realType = "SUM_FUNC_ITEM";
break;
}
case Item::REF_ITEM:
// Need recursion here
realType = "REF_ITEM";
break;
case Item::FUNC_ITEM:
{
Item_func* ifp = (Item_func*)(*(ifr->ref));
realType = "FUNC_ITEM ";
realType += ifp->func_name();
break;
}
default:
{
realType = "UNKNOWN";
}
}
cerr << "CACHE_ITEM ref type " << refType.c_str() << " real type " << realType.c_str() << endl;
break;
}
else if (field->type() == Item::FUNC_ITEM)
{
Item_func* ifp = (Item_func*)field;
cerr << "CACHE_ITEM FUNC_ITEM " << ifp->func_name() << endl;
break;
}
else
{
cerr << "CACHE_ITEM type unknown " << field->type() << endl;
}
break;
}
case Item::WINDOW_FUNC_ITEM:
{
Item_window_func* ifp = (Item_window_func*)item;
cerr << "Window Function Item " << ifp->window_func()->func_name() << endl;
break;
}
case Item::NULL_ITEM:
{
cerr << "NULL item" << endl;
break;
}
case Item::TYPE_HOLDER:
{
cerr << "TYPE_HOLDER item with cmp_type " << item->cmp_type() << endl;
break;
}
default:
{
cerr << "UNKNOWN_ITEM type " << item->type() << endl;
break;
}
}
}
#endif
void buildNestedJoinLeafTables(List<TABLE_LIST>& join_list,
std::set<execplan::CalpontSystemCatalog::TableAliasName>& leafTables)
{
TABLE_LIST* table;
List_iterator<TABLE_LIST> li(join_list);
while ((table = li++))
{
if (table->nested_join)
buildNestedJoinLeafTables(table->nested_join->join_list, leafTables);
else
{
CalpontSystemCatalog::TableAliasName tan = makeTableAliasName(table);
leafTables.insert(tan);
}
}
}
uint32_t buildJoin(gp_walk_info& gwi, List<TABLE_LIST>& join_list,
std::stack<execplan::ParseTree*>& outerJoinStack)
{
TABLE_LIST* table;
List_iterator<TABLE_LIST> li(join_list);
while ((table = li++))
{
// Make sure we don't process the derived table nests again,
// they were already handled by FromSubQuery::transform()
if (table->nested_join && !table->derived)
buildJoin(gwi, table->nested_join->join_list, outerJoinStack);
std::vector<COND*> tableOnExprList;
auto iter = gwi.tableOnExprList.find(table);
// Check if this table's on_expr is available in the hash map
// built/updated during convertOuterJoinToInnerJoin().
if ((iter != gwi.tableOnExprList.end()) && !iter->second.empty())
{
tableOnExprList = iter->second;
}
// This table's on_expr has not been seen/processed before.
else if ((iter == gwi.tableOnExprList.end()) && table->on_expr)
{
tableOnExprList.push_back(table->on_expr);
}
if (!tableOnExprList.empty())
{
if (table->outer_join & (JOIN_TYPE_LEFT | JOIN_TYPE_RIGHT))
{
// inner tables block
gp_walk_info gwi_outer = gwi;
gwi_outer.subQuery = NULL;
gwi_outer.hasSubSelect = false;
gwi_outer.innerTables.clear();
clearStacks(gwi_outer);
// recursively build the leaf tables for this nested join node
if (table->nested_join)
buildNestedJoinLeafTables(table->nested_join->join_list, gwi_outer.innerTables);
else // this is a leaf table
{
CalpontSystemCatalog::TableAliasName tan = makeTableAliasName(table);
gwi_outer.innerTables.insert(tan);
}
#ifdef DEBUG_WALK_COND
cerr << "inner tables: " << endl;
set<CalpontSystemCatalog::TableAliasName>::const_iterator it;
for (it = gwi_outer.innerTables.begin(); it != gwi_outer.innerTables.end(); ++it)
cerr << (*it) << " ";
cerr << endl;
cerr << " outer table expression: " << endl;
for (Item* expr : tableOnExprList)
expr->traverse_cond(debug_walk, &gwi_outer, Item::POSTFIX);
#endif
for (Item* expr : tableOnExprList)
{
expr->traverse_cond(gp_walk, &gwi_outer, Item::POSTFIX);
// Error out subquery in outer join on filter for now
if (gwi_outer.hasSubSelect)
{
gwi.fatalParseError = true;
gwi.parseErrorText = IDBErrorInfo::instance()->errorMsg(ERR_OUTER_JOIN_SUBSELECT);
setError(gwi.thd, ER_INTERNAL_ERROR, gwi.parseErrorText);
return -1;
}
}
// build outerjoinon filter
ParseTree *filters = NULL, *ptp = NULL, *rhs = NULL;
while (!gwi_outer.ptWorkStack.empty())
{
filters = gwi_outer.ptWorkStack.top();
gwi_outer.ptWorkStack.pop();
if (gwi_outer.ptWorkStack.empty())
break;
ptp = new ParseTree(new LogicOperator("and"));
ptp->left(filters);
rhs = gwi_outer.ptWorkStack.top();
gwi_outer.ptWorkStack.pop();
ptp->right(rhs);
gwi_outer.ptWorkStack.push(ptp);
}
// should have only 1 pt left in stack.
if (filters)
{
SPTP on_sp(filters);
OuterJoinOnFilter* onFilter = new OuterJoinOnFilter(on_sp);
ParseTree* pt = new ParseTree(onFilter);
outerJoinStack.push(pt);
}
}
else // inner join
{
for (Item* expr : tableOnExprList)
{
expr->traverse_cond(gp_walk, &gwi, Item::POSTFIX);
}
#ifdef DEBUG_WALK_COND
cerr << " inner join expression: " << endl;
for (Item* expr : tableOnExprList)
expr->traverse_cond(debug_walk, &gwi, Item::POSTFIX);
#endif
}
}
}
return 0;
}
ParseTree* buildRowPredicate(gp_walk_info* gwip, RowColumn* lhs, RowColumn* rhs, string predicateOp)
{
PredicateOperator* po = new PredicateOperator(predicateOp);
boost::shared_ptr<Operator> sop(po);
LogicOperator* lo = NULL;
if (predicateOp == "=")
lo = new LogicOperator("and");
else
lo = new LogicOperator("or");
ParseTree* pt = new ParseTree(lo);
sop->setOpType(lhs->columnVec()[0]->resultType(), rhs->columnVec()[0]->resultType());
SimpleFilter* sf = new SimpleFilter(sop, lhs->columnVec()[0]->clone(), rhs->columnVec()[0]->clone());
sf->timeZone(gwip->timeZone);
pt->left(new ParseTree(sf));
for (uint32_t i = 1; i < lhs->columnVec().size(); i++)
{
sop.reset(po->clone());
sop->setOpType(lhs->columnVec()[i]->resultType(), rhs->columnVec()[i]->resultType());
SimpleFilter* sf = new SimpleFilter(sop, lhs->columnVec()[i]->clone(), rhs->columnVec()[i]->clone());
sf->timeZone(gwip->timeZone);
pt->right(new ParseTree(sf));
if (i + 1 < lhs->columnVec().size())
{
ParseTree* lpt = pt;
pt = new ParseTree(lo->clone());
pt->left(lpt);
}
}
return pt;
}
bool buildRowColumnFilter(gp_walk_info* gwip, RowColumn* rhs, RowColumn* lhs, Item_func* ifp)
{
// rhs and lhs are being dismembered here and then get thrown away, leaking.
// So we create two scoped pointers to get them delete'd automatically at
// return.
// Also look below into heldoutVals vector - it contains values produced from
// ifp's arguments.
const std::unique_ptr<RowColumn> rhsp(rhs), lhsp(lhs);
if (ifp->functype() == Item_func::EQ_FUNC || ifp->functype() == Item_func::NE_FUNC)
{
// (c1,c2,..) = (v1,v2,...) transform to: c1=v1 and c2=v2 and ...
assert(!lhs->columnVec().empty() && lhs->columnVec().size() == rhs->columnVec().size());
gwip->ptWorkStack.push(buildRowPredicate(gwip, rhs, lhs, ifp->func_name()));
}
else if (ifp->functype() == Item_func::IN_FUNC)
{
// (c1,c2,...) in ((v11,v12,...),(v21,v22,...)...) transform to:
// ((c1 = v11 and c2 = v12 and ...) or (c1 = v21 and c2 = v22 and ...) or ...)
// and c1 in (v11,v21,...) and c2 in (v12,v22,...) => to favor CP
Item_func_opt_neg* inp = (Item_func_opt_neg*)ifp;
string predicateOp, logicOp;
if (inp->negated)
{
predicateOp = "<>";
logicOp = "and";
}
else
{
predicateOp = "=";
logicOp = "or";
}
boost::scoped_ptr<LogicOperator> lo(new LogicOperator(logicOp));
// 1st round. build the equivalent filters
// two entries have been popped from the stack already: lhs and rhs
stack<ReturnedColumn*> tmpStack;
vector<RowColumn*> valVec;
vector<SRCP> heldOutVals; // these vals are not rhs/lhs and need to be freed
tmpStack.push(rhs);
tmpStack.push(lhs);
assert(gwip->rcWorkStack.size() >= ifp->argument_count() - 2);
for (uint32_t i = 2; i < ifp->argument_count(); i++)
{
tmpStack.push(gwip->rcWorkStack.top());
heldOutVals.push_back(SRCP(gwip->rcWorkStack.top()));
if (!gwip->rcWorkStack.empty())
gwip->rcWorkStack.pop();
}
RowColumn* columns = dynamic_cast<RowColumn*>(tmpStack.top());
tmpStack.pop();
RowColumn* vals = dynamic_cast<RowColumn*>(tmpStack.top());
valVec.push_back(vals);
tmpStack.pop();
ParseTree* pt = buildRowPredicate(gwip, columns, vals, predicateOp);
while (!tmpStack.empty())
{
ParseTree* pt1 = new ParseTree(lo->clone());
pt1->left(pt);
vals = dynamic_cast<RowColumn*>(tmpStack.top());
valVec.push_back(vals);
tmpStack.pop();
pt1->right(buildRowPredicate(gwip, columns, vals, predicateOp));
pt = pt1;
}
gwip->ptWorkStack.push(pt);
// done for NOTIN clause
if (predicateOp == "<>")
return true;
// 2nd round. add the filter to favor casual partition for IN clause
boost::shared_ptr<Operator> sop;
boost::shared_ptr<SimpleColumn> ssc;
stack<ParseTree*> tmpPtStack;
for (uint32_t i = 0; i < columns->columnVec().size(); i++)
{
std::unique_ptr<ConstantFilter> cf(new ConstantFilter());
sop.reset(lo->clone());
cf->op(sop);
SimpleColumn* sc = dynamic_cast<SimpleColumn*>(columns->columnVec()[i].get());
// no optimization for non-simple column because CP won't apply
if (!sc)
{
continue;
}
ssc.reset(sc->clone());
cf->col(ssc);
uint32_t j = 0;
for (; j < valVec.size(); j++)
{
sop.reset(new PredicateOperator(predicateOp));
ConstantColumn* cc = dynamic_cast<ConstantColumn*>(valVec[j]->columnVec()[i].get());
// no optimization for non-constant value because CP won't apply
if (!cc)
break;
sop->setOpType(sc->resultType(), valVec[j]->columnVec()[i]->resultType());
cf->pushFilter(
new SimpleFilter(sop, sc->clone(), valVec[j]->columnVec()[i]->clone(), gwip->timeZone));
}
if (j < valVec.size())
{
continue;
}
tmpPtStack.push(new ParseTree(cf.release()));
}
// "and" all the filters together
ParseTree* ptp = NULL;
pt = NULL;
while (!tmpPtStack.empty())
{
pt = tmpPtStack.top();
tmpPtStack.pop();
if (tmpPtStack.empty())
break;
ptp = new ParseTree(new LogicOperator("and"));
ptp->left(pt);
pt = tmpPtStack.top();
tmpPtStack.pop();
ptp->right(pt);
tmpPtStack.push(ptp);
}
if (pt)
{
tmpPtStack.push(pt);
// AND with the pt built from the first round
if (!gwip->ptWorkStack.empty() && !tmpPtStack.empty())
{
pt = new ParseTree(new LogicOperator("and"));
pt->left(gwip->ptWorkStack.top());
gwip->ptWorkStack.pop();
pt->right(tmpPtStack.top());
tmpPtStack.pop();
}
// Push the final pt tree for this IN clause
gwip->ptWorkStack.push(pt);
}
}
else
{
gwip->fatalParseError = true;
gwip->parseErrorText = IDBErrorInfo::instance()->errorMsg(ERR_FUNC_MULTI_COL);
return false;
}
return true;
}
void checkOuterTableColumn(gp_walk_info* gwip, const CalpontSystemCatalog::TableAliasName& tan,
execplan::ReturnedColumn* col)
{
set<CalpontSystemCatalog::TableAliasName>::const_iterator it;
bool notInner = true;
for (it = gwip->innerTables.begin(); it != gwip->innerTables.end(); ++it)
{
if (tan.alias == it->alias && tan.view == it->view)
notInner = false;
}
if (notInner)
{
col->returnAll(true);
IDEBUG(cerr << "setting returnAll on " << tan << endl);
}
}
bool buildEqualityPredicate(execplan::ReturnedColumn* lhs, execplan::ReturnedColumn* rhs, gp_walk_info* gwip,
boost::shared_ptr<Operator>& sop, const Item_func::Functype& funcType,
const vector<Item*>& itemList, bool isInSubs)
{
cal_connection_info* ci = static_cast<cal_connection_info*>(get_fe_conn_info_ptr());
// push the column that is associated with the correlated column to the returned
// column list, so the materialized view have the complete projection list.
// e.g. tout.c1 in (select tin.c1 from tin where tin.c2=tout.c2);
// the projetion list of subquery will have tin.c1, tin.c2.
ReturnedColumn* correlatedCol = nullptr;
ReturnedColumn* localCol = nullptr;
if (rhs->joinInfo() & JOIN_CORRELATED)
{
correlatedCol = rhs;
localCol = lhs;
}
else if (lhs->joinInfo() & JOIN_CORRELATED)
{
correlatedCol = lhs;
localCol = rhs;
}
if (correlatedCol && localCol)
{
ConstantColumn* cc = dynamic_cast<ConstantColumn*>(localCol);
if ((!cc || (cc && funcType == Item_func::EQ_FUNC)) && !(localCol->joinInfo() & JOIN_CORRELATED))
{
if (isInSubs)
localCol->sequence(0);
else
localCol->sequence(gwip->returnedCols.size());
localCol->expressionId(ci->expressionId++);
ReturnedColumn* rc = localCol->clone();
rc->colSource(rc->colSource() | CORRELATED_JOIN);
gwip->additionalRetCols.push_back(SRCP(rc));
gwip->localCols.push_back(localCol);
if (rc->hasWindowFunc() && !isInSubs)
gwip->windowFuncList.push_back(rc);
}
// push the correlated join partner to the group by list only when there's aggregate
// and we don't push aggregate column to the group by
// @bug4756. mysql does not always give correct information about whether there is
// aggregate on the SELECT list. Need to figure that by ourselves and then decide
// to add the group by or not.
if (gwip->subQuery)
{
if (!localCol->hasAggregate() && !localCol->hasWindowFunc())
gwip->subGroupByCols.push_back(SRCP(localCol->clone()));
}
if (sop->op() == OP_EQ)
{
if (gwip->subSelectType == CalpontSelectExecutionPlan::IN_SUBS ||
gwip->subSelectType == CalpontSelectExecutionPlan::EXISTS_SUBS)
correlatedCol->joinInfo(correlatedCol->joinInfo() | JOIN_SEMI);
else if (gwip->subSelectType == CalpontSelectExecutionPlan::NOT_IN_SUBS ||
gwip->subSelectType == CalpontSelectExecutionPlan::NOT_EXISTS_SUBS)
correlatedCol->joinInfo(correlatedCol->joinInfo() | JOIN_ANTI);
}
}
SimpleFilter* sf = new SimpleFilter();
sf->timeZone(gwip->timeZone);
//@bug 2101 for when there are only constants in a delete or update where clause (eg "where 5 < 6").
// There will be no field column and it will get here only if the comparison is true.
if (gwip->columnMap.empty() && ((current_thd->lex->sql_command == SQLCOM_UPDATE) ||
(current_thd->lex->sql_command == SQLCOM_UPDATE_MULTI) ||
(current_thd->lex->sql_command == SQLCOM_DELETE) ||
(current_thd->lex->sql_command == SQLCOM_DELETE_MULTI)))
{
IDEBUG(cerr << "deleted func with 2 const columns" << endl);
delete rhs;
delete lhs;
return false;
}
// handle noop (only for table mode)
if (rhs->data() == "noop" || lhs->data() == "noop")
{
sop.reset(new Operator("noop"));
}
else
{
for (uint32_t i = 0; i < itemList.size(); i++)
{
if (isPredicateFunction(itemList[i], gwip))
{
gwip->fatalParseError = true;
gwip->parseErrorText = IDBErrorInfo::instance()->errorMsg(ERR_SUB_EXPRESSION);
}
}
}
sf->op(sop);
sf->lhs(lhs);
sf->rhs(rhs);
sop->setOpType(lhs->resultType(), rhs->resultType());
sop->resultType(sop->operationType());
if (sop->op() == OP_EQ)
{
CalpontSystemCatalog::TableAliasName tan_lhs;
CalpontSystemCatalog::TableAliasName tan_rhs;
bool outerjoin = (rhs->singleTable(tan_rhs) && lhs->singleTable(tan_lhs));
// @bug 1632. Alias should be taken account to the identity of tables for selfjoin to work
if (outerjoin && tan_lhs != tan_rhs) // join
{
if (!gwip->condPush) // vtable
{
if (!gwip->innerTables.empty())
{
checkOuterTableColumn(gwip, tan_lhs, lhs);
checkOuterTableColumn(gwip, tan_rhs, rhs);
}
if (funcType == Item_func::EQ_FUNC)
{
gwip->equiCondSFList.push_back(sf);
}
ParseTree* ptp = new ParseTree(sf);
gwip->ptWorkStack.push(ptp);
}
}
else
{
ParseTree* ptp = new ParseTree(sf);
gwip->ptWorkStack.push(ptp);
}
}
else
{
ParseTree* ptp = new ParseTree(sf);
gwip->ptWorkStack.push(ptp);
}
return true;
}
bool buildPredicateItem(Item_func* ifp, gp_walk_info* gwip)
{
boost::shared_ptr<Operator> sop(new PredicateOperator(ifp->func_name()));
if (ifp->functype() == Item_func::LIKE_FUNC)
{
// Starting with MariaDB 10.2, LIKE uses a negated flag instead of FUNC_NOT
// Further processing is done below as before for LIKE
if (((Item_func_like*)ifp)->get_negated())
{
sop->reverseOp();
}
}
if (get_fe_conn_info_ptr() == NULL)
{
set_fe_conn_info_ptr((void*)new cal_connection_info());
thd_set_ha_data(current_thd, mcs_hton, get_fe_conn_info_ptr());
}
if (ifp->functype() == Item_func::BETWEEN)
{
idbassert(gwip->rcWorkStack.size() >= 3);
ReturnedColumn* rhs = gwip->rcWorkStack.top();
gwip->rcWorkStack.pop();
ReturnedColumn* lhs = gwip->rcWorkStack.top();
gwip->rcWorkStack.pop();
ReturnedColumn* filterCol = gwip->rcWorkStack.top();
gwip->rcWorkStack.pop(); // pop gwip->scsp;
Item_func_opt_neg* inp = (Item_func_opt_neg*)ifp;
SimpleFilter* sfr = 0;
SimpleFilter* sfl = 0;
if (inp->negated)
{
sop.reset(new PredicateOperator(">"));
sop->setOpType(filterCol->resultType(), rhs->resultType());
sfr = new SimpleFilter(sop, filterCol, rhs);
sfr->timeZone(gwip->timeZone);
sop.reset(new PredicateOperator("<"));
sop->setOpType(filterCol->resultType(), lhs->resultType());
sfl = new SimpleFilter(sop, filterCol->clone(), lhs);
sfl->timeZone(gwip->timeZone);
ParseTree* ptp = new ParseTree(new LogicOperator("or"));
ptp->left(sfr);
ptp->right(sfl);
gwip->ptWorkStack.push(ptp);
}
else
{
sop.reset(new PredicateOperator("<="));
sop->setOpType(filterCol->resultType(), rhs->resultType());
sfr = new SimpleFilter(sop, filterCol, rhs);
sfr->timeZone(gwip->timeZone);
sop.reset(new PredicateOperator(">="));
sop->setOpType(filterCol->resultType(), lhs->resultType());
sfl = new SimpleFilter(sop, filterCol->clone(), lhs);
sfl->timeZone(gwip->timeZone);
ParseTree* ptp = new ParseTree(new LogicOperator("and"));
ptp->left(sfr);
ptp->right(sfl);
gwip->ptWorkStack.push(ptp);
}
return true;
}
else if (ifp->functype() == Item_func::IN_FUNC)
{
idbassert(gwip->rcWorkStack.size() >= 2);
ReturnedColumn* rhs = gwip->rcWorkStack.top();
gwip->rcWorkStack.pop();
ReturnedColumn* lhs = gwip->rcWorkStack.top();
gwip->rcWorkStack.pop();
// @bug3038
RowColumn* rrhs = dynamic_cast<RowColumn*>(rhs);
RowColumn* rlhs = dynamic_cast<RowColumn*>(lhs);
if (rrhs && rlhs)
{
return buildRowColumnFilter(gwip, rrhs, rlhs, ifp);
}
ConstantColumn* crhs = dynamic_cast<ConstantColumn*>(rhs);
ConstantColumn* clhs = dynamic_cast<ConstantColumn*>(lhs);
if (!crhs || !clhs)
{
gwip->fatalParseError = true;
gwip->parseErrorText = "non constant value in IN clause";
return false;
}
string eqop;
string cmbop;
Item_func_opt_neg* inp = (Item_func_opt_neg*)ifp;
if (inp->negated)
{
eqop = "<>";
cmbop = "and";
}
else
{
eqop = "=";
cmbop = "or";
}
sop.reset(new PredicateOperator(eqop));
SRCP scsp = gwip->scsp;
idbassert(scsp.get() != nullptr);
//sop->setOpType(gwip->scsp->resultType(), rhs->resultType());
sop->setOpType(scsp->resultType(), rhs->resultType());
ConstantFilter* cf = 0;
cf = new ConstantFilter(sop, scsp->clone(), lhs);
sop.reset(new LogicOperator(cmbop));
cf->op(sop);
sop.reset(new PredicateOperator(eqop));
sop->setOpType(scsp->resultType(), rhs->resultType());
cf->pushFilter(new SimpleFilter(sop, scsp->clone(), rhs->clone(), gwip->timeZone));
while (!gwip->rcWorkStack.empty())
{
lhs = gwip->rcWorkStack.top();
if (dynamic_cast<ConstantColumn*>(lhs) == 0)
break;
gwip->rcWorkStack.pop();
sop.reset(new PredicateOperator(eqop));
sop->setOpType(scsp->resultType(), lhs->resultType());
cf->pushFilter(new SimpleFilter(sop, scsp->clone(), lhs->clone(), gwip->timeZone));
}
if (!gwip->rcWorkStack.empty())
{
delete gwip->rcWorkStack.top();
gwip->rcWorkStack.pop(); // pop gwip->scsp
}
if (cf->filterList().size() < inp->argument_count() - 1)
{
gwip->fatalParseError = true;
gwip->parseErrorText = "non constant value in IN clause";
delete cf;
return false;
}
cf->functionName(gwip->funcName);
String str;
// @bug5811. This filter string is for cross engine to use.
// Use real table name.
ifp->print(&str, QT_ORDINARY);
IDEBUG(cerr << str.ptr() << endl);
if (str.ptr())
cf->data(str.c_ptr());
ParseTree* ptp = new ParseTree(cf);
gwip->ptWorkStack.push(ptp);
}
else if (ifp->functype() == Item_func::ISNULL_FUNC || ifp->functype() == Item_func::ISNOTNULL_FUNC)
{
ReturnedColumn* rhs = NULL;
if (!gwip->rcWorkStack.empty() && !gwip->inCaseStmt)
{
rhs = gwip->rcWorkStack.top();
gwip->rcWorkStack.pop();
}
else
{
rhs = buildReturnedColumn(ifp->arguments()[0], *gwip, gwip->fatalParseError);
}
if (rhs && !gwip->fatalParseError)
buildConstPredicate(ifp, rhs, gwip);
else if (!rhs) // @bug 3802
{
gwip->fatalParseError = true;
gwip->parseErrorText = IDBErrorInfo::instance()->errorMsg(ERR_FILTER_COND_EXP);
return false;
}
}
else if (ifp->functype() == Item_func::GUSERVAR_FUNC)
{
Item_func_get_user_var* udf = static_cast<Item_func_get_user_var*>(ifp);
String buf;
if (udf->result_type() == INT_RESULT)
{
if (udf->unsigned_flag)
{
gwip->rcWorkStack.push(new ConstantColumn((uint64_t)udf->val_uint()));
}
else
{
gwip->rcWorkStack.push(new ConstantColumn((int64_t)udf->val_int()));
}
(dynamic_cast<ConstantColumn*>(gwip->rcWorkStack.top()))->timeZone(gwip->timeZone);
}
else
{
// const String* str = udf->val_str(&buf);
udf->val_str(&buf);
if (!buf.ptr())
{
ostringstream oss;
oss << "Unknown user variable: " << udf->name.str;
gwip->parseErrorText = oss.str();
gwip->fatalParseError = true;
return false;
}
if (udf->result_type() == STRING_RESULT)
gwip->rcWorkStack.push(
new ConstantColumn(buf.ptr())); // XXX: constantcolumn from string = can it be NULL?
else
{
gwip->rcWorkStack.push(new ConstantColumn(buf.ptr(), ConstantColumn::NUM));
}
(dynamic_cast<ConstantColumn*>(gwip->rcWorkStack.top()))->timeZone(gwip->timeZone);
return false;
}
}
#if 0
else if (ifp->functype() == Item_func::NEG_FUNC)
{
//peek at the (hopefully) ConstantColumn on the top of stack, negate it in place
ConstantColumn* ccp = dynamic_cast<ConstantColumn*>(gwip->rcWorkStack.top());
if (!ccp)
{
ostringstream oss;
oss << "Attempt to negate a non-constant column";
gwip->parseErrorText = oss.str();
gwip->fatalParseError = true;
return false;
}
string cval = ccp->constval();
string newval;
if (cval[0] == '-')
newval.assign(cval, 1, string::npos);
else
newval = "-" + cval;
ccp->constval(newval);
}
#endif
else if (ifp->functype() == Item_func::NOT_FUNC)
{
if (gwip->condPush && ifp->next->type() == Item::SUBSELECT_ITEM)
return false;
if (ifp->next && ifp->next->type() == Item::SUBSELECT_ITEM && gwip->lastSub)
{
gwip->lastSub->handleNot();
return false;
}
idbassert(ifp->argument_count() == 1);
ParseTree* ptp = 0;
if (((Item_func*)(ifp->arguments()[0]))->functype() == Item_func::EQUAL_FUNC)
{
// negate it in place
// Note that an EQUAL_FUNC ( a <=> b) was converted to
// ( a = b OR ( a is null AND b is null) )
// NOT of the above expression is: ( a != b AND (a is not null OR b is not null )
if (!gwip->ptWorkStack.empty())
ptp = gwip->ptWorkStack.top();
if (ptp)
{
ParseTree* or_ptp = ptp;
ParseTree* and_ptp = or_ptp->right();
ParseTree* equal_ptp = or_ptp->left();
ParseTree* nullck_left_ptp = and_ptp->left();
ParseTree* nullck_right_ptp = and_ptp->right();
SimpleFilter* sf_left_nullck = dynamic_cast<SimpleFilter*>(nullck_left_ptp->data());
SimpleFilter* sf_right_nullck = dynamic_cast<SimpleFilter*>(nullck_right_ptp->data());
SimpleFilter* sf_equal = dynamic_cast<SimpleFilter*>(equal_ptp->data());
if (sf_left_nullck && sf_right_nullck && sf_equal)
{
// Negate the null checks
sf_left_nullck->op()->reverseOp();
sf_right_nullck->op()->reverseOp();
sf_equal->op()->reverseOp();
// Rehook the nodes
ptp = and_ptp;
ptp->left(equal_ptp);
ptp->right(or_ptp);
or_ptp->left(nullck_left_ptp);
or_ptp->right(nullck_right_ptp);
gwip->ptWorkStack.pop();
gwip->ptWorkStack.push(ptp);
}
else
{
gwip->fatalParseError = true;
gwip->parseErrorText = IDBErrorInfo::instance()->errorMsg(ERR_ASSERTION_FAILURE);
return false;
}
}
}
else if (isPredicateFunction(ifp->arguments()[0], gwip) || ifp->arguments()[0]->type() == Item::COND_ITEM)
{
// negate it in place
if (!gwip->ptWorkStack.empty())
ptp = gwip->ptWorkStack.top();
SimpleFilter* sf = 0;
if (ptp)
{
sf = dynamic_cast<SimpleFilter*>(ptp->data());
if (sf)
sf->op()->reverseOp();
}
}
else
{
// transfrom the not item to item = 0
ReturnedColumn* rhs = 0;
if (!gwip->rcWorkStack.empty())
{
rhs = gwip->rcWorkStack.top();
gwip->rcWorkStack.pop();
}
else
{
rhs = buildReturnedColumn(ifp->arguments()[0], *gwip, gwip->fatalParseError);
}
if (rhs && !gwip->fatalParseError)
return buildConstPredicate(ifp, rhs, gwip);
else if (!rhs) // @bug3802
{
gwip->fatalParseError = true;
gwip->parseErrorText = IDBErrorInfo::instance()->errorMsg(ERR_FILTER_COND_EXP);
return false;
}
}
}
/*else if (ifp->functype() == Item_func::MULT_EQUAL_FUNC)
{
Item_equal *cur_item_eq = (Item_equal*)ifp;
Item *lhs_item, *rhs_item;
// There must be at least two items
Item_equal_fields_iterator lhs_it(*cur_item_eq);
Item_equal_fields_iterator rhs_it(*cur_item_eq); rhs_it++;
while ((lhs_item = lhs_it++) && (rhs_item = rhs_it++))
{
ReturnedColumn* lhs = buildReturnedColumn(lhs_item, *gwip, gwip->fatalParseError);
ReturnedColumn* rhs = buildReturnedColumn(rhs_item, *gwip, gwip->fatalParseError);
if (!rhs || !lhs)
{
gwip->fatalParseError = true;
gwip->parseErrorText = "Unsupport elements in MULT_EQUAL item";
delete rhs;
delete lhs;
return false;
}
PredicateOperator* po = new PredicateOperator("=");
boost::shared_ptr<Operator> sop(po);
sop->setOpType(lhs->resultType(), rhs->resultType());
SimpleFilter* sf = new SimpleFilter(sop, lhs, rhs);
// Search in ptWorkStack for duplicates of the SF
// before we push the SF
if (!isDuplicateSF(gwip, sf))
{
ParseTree* pt = new ParseTree(sf);
gwip->ptWorkStack.push(pt);
}
}
}*/
else if (ifp->functype() == Item_func::EQUAL_FUNC)
{
// Convert "a <=> b" to (a = b OR (a IS NULL AND b IS NULL))"
idbassert(gwip->rcWorkStack.size() >= 2);
ReturnedColumn* rhs = gwip->rcWorkStack.top();
gwip->rcWorkStack.pop();
ReturnedColumn* lhs = gwip->rcWorkStack.top();
gwip->rcWorkStack.pop();
SimpleFilter* sfn1 = 0;
SimpleFilter* sfn2 = 0;
SimpleFilter* sfo = 0;
// b IS NULL
ConstantColumn* nlhs1 = new ConstantColumn("", ConstantColumn::NULLDATA);
nlhs1->timeZone(gwip->timeZone);
sop.reset(new PredicateOperator("isnull"));
sop->setOpType(lhs->resultType(), rhs->resultType());
sfn1 = new SimpleFilter(sop, rhs, nlhs1);
sfn1->timeZone(gwip->timeZone);
ParseTree* ptpl = new ParseTree(sfn1);
// a IS NULL
ConstantColumn* nlhs2 = new ConstantColumn("", ConstantColumn::NULLDATA);
nlhs2->timeZone(gwip->timeZone);
sop.reset(new PredicateOperator("isnull"));
sop->setOpType(lhs->resultType(), rhs->resultType());
sfn2 = new SimpleFilter(sop, lhs, nlhs2);
sfn2->timeZone(gwip->timeZone);
ParseTree* ptpr = new ParseTree(sfn2);
// AND them both
ParseTree* ptpn = new ParseTree(new LogicOperator("and"));
ptpn->left(ptpl);
ptpn->right(ptpr);
// a = b
sop.reset(new PredicateOperator("="));
sop->setOpType(lhs->resultType(), rhs->resultType());
sfo = new SimpleFilter(sop, lhs->clone(), rhs->clone());
sfo->timeZone(gwip->timeZone);
// OR with the NULL comparison tree
ParseTree* ptp = new ParseTree(new LogicOperator("or"));
ptp->left(sfo);
ptp->right(ptpn);
gwip->ptWorkStack.push(ptp);
return true;
}
else // std rel ops (incl "like")
{
if (gwip->rcWorkStack.size() < 2)
{
idbassert(ifp->argument_count() == 2);
if (isPredicateFunction(ifp->arguments()[0], gwip) || ifp->arguments()[0]->type() == Item::COND_ITEM ||
isPredicateFunction(ifp->arguments()[1], gwip) || ifp->arguments()[1]->type() == Item::COND_ITEM)
{
gwip->fatalParseError = true;
gwip->parseErrorText = IDBErrorInfo::instance()->errorMsg(ERR_FILTER_COND_EXP);
}
return false;
}
ReturnedColumn* rhs = gwip->rcWorkStack.top();
gwip->rcWorkStack.pop();
ReturnedColumn* lhs = gwip->rcWorkStack.top();
gwip->rcWorkStack.pop();
// @bug3038. rowcolumn filter
RowColumn* rrhs = dynamic_cast<RowColumn*>(rhs);
RowColumn* rlhs = dynamic_cast<RowColumn*>(lhs);
if (rrhs && rlhs)
{
return buildRowColumnFilter(gwip, rrhs, rlhs, ifp);
}
vector<Item*> itemList;
for (uint32_t i = 0; i < ifp->argument_count(); i++)
itemList.push_back(ifp->arguments()[i]);
return buildEqualityPredicate(lhs, rhs, gwip, sop, ifp->functype(), itemList);
}
return true;
}
bool buildConstPredicate(Item_func* ifp, ReturnedColumn* rhs, gp_walk_info* gwip)
{
SimpleFilter* sf = new SimpleFilter();
sf->timeZone(gwip->timeZone);
boost::shared_ptr<Operator> sop(new PredicateOperator(ifp->func_name()));
ConstantColumn* lhs = 0;
if (ifp->functype() == Item_func::ISNULL_FUNC)
{
lhs = new ConstantColumn("", ConstantColumn::NULLDATA);
sop.reset(new PredicateOperator("isnull"));
}
else if (ifp->functype() == Item_func::ISNOTNULL_FUNC)
{
lhs = new ConstantColumn("", ConstantColumn::NULLDATA);
sop.reset(new PredicateOperator("isnotnull"));
}
else // if (ifp->functype() == Item_func::NOT_FUNC)
{
lhs = new ConstantColumn((int64_t)0, ConstantColumn::NUM);
sop.reset(new PredicateOperator("="));
}
lhs->timeZone(gwip->timeZone);
CalpontSystemCatalog::ColType opType = rhs->resultType();
if ((opType.colDataType == CalpontSystemCatalog::CHAR && opType.colWidth <= 8) ||
(opType.colDataType == CalpontSystemCatalog::VARCHAR && opType.colWidth < 8) ||
(opType.colDataType == CalpontSystemCatalog::VARBINARY && opType.colWidth < 8))
{
opType.colDataType = execplan::CalpontSystemCatalog::BIGINT;
opType.colWidth = 8;
}
sop->operationType(opType);
sf->op(sop);
// yes, these are backwards
assert(lhs);
sf->lhs(rhs);
sf->rhs(lhs);
ParseTree* ptp = new ParseTree(sf);
gwip->ptWorkStack.push(ptp);
return true;
}
SimpleColumn* buildSimpleColFromDerivedTable(gp_walk_info& gwi, Item_field* ifp)
{
SimpleColumn* sc = NULL;
// view name
string viewName = getViewName(ifp->cached_table);
// Check from the end because local scope resolve is preferred
for (int32_t i = gwi.tbList.size() - 1; i >= 0; i--)
{
if (sc)
break;
if (!gwi.tbList[i].schema.empty() && !gwi.tbList[i].table.empty() &&
(!ifp->table_name.str || strcasecmp(ifp->table_name.str, gwi.tbList[i].alias.c_str()) == 0))
{
CalpontSystemCatalog::TableName tn(gwi.tbList[i].schema, gwi.tbList[i].table);
CalpontSystemCatalog::RIDList oidlist = gwi.csc->columnRIDs(tn, true);
for (unsigned int j = 0; j < oidlist.size(); j++)
{
CalpontSystemCatalog::TableColName tcn = gwi.csc->colName(oidlist[j].objnum);
CalpontSystemCatalog::ColType ct = gwi.csc->colType(oidlist[j].objnum);
if (strcasecmp(ifp->field_name.str, tcn.column.c_str()) == 0)
{
// @bug4827. Remove the checking because outside tables could be the same
// name as inner tables. This function is to identify column from a table,
// as long as it matches the next step in predicate parsing will tell the scope
// of the column.
/*if (sc)
{
gwi.fatalParseError = true;
Message::Args args;
args.add(ifp->name);
gwi.parseErrorText = IDBErrorInfo::instance()->errorMsg(ERR_AMBIGUOUS_COL, args);
return NULL;
}*/
sc = new SimpleColumn();
sc->columnName(tcn.column);
sc->tableName(tcn.table);
sc->schemaName(tcn.schema);
sc->oid(oidlist[j].objnum);
// @bug 3003. Keep column alias if it has.
sc->alias(!ifp->is_explicit_name() ? tcn.column : ifp->name.str);
sc->tableAlias(gwi.tbList[i].alias);
sc->viewName(viewName, lower_case_table_names);
sc->resultType(ct);
sc->timeZone(gwi.timeZone);
break;
}
}
}
}
if (sc)
return sc;
// Check from the end because local scope resolve is preferred
for (int32_t i = gwi.derivedTbList.size() - 1; i >= 0; i--)
{
if (sc)
break;
CalpontSelectExecutionPlan* csep = dynamic_cast<CalpontSelectExecutionPlan*>(gwi.derivedTbList[i].get());
string derivedName = csep->derivedTbAlias();
if (!ifp->table_name.str || strcasecmp(ifp->table_name.str, derivedName.c_str()) == 0)
{
CalpontSelectExecutionPlan::ReturnedColumnList cols = csep->returnedCols();
for (uint32_t j = 0; j < cols.size(); j++)
{
// @bug 3167 duplicate column alias is full name
SimpleColumn* col = dynamic_cast<SimpleColumn*>(cols[j].get());
string alias = cols[j]->alias();
// MCOL-5357 For the following query:
// select item from (
// select item from (select a as item from t1) tt
// union all
// select item from (select a as item from t1) tt
// ) ttt;
// When the execution reaches the outermost item (ttt.item),
// alias = "`tt`.`item`" and ifp->field_name.str = "item".
// To make the execution enter the if block below, we strip off
// the backticks from alias.
boost::erase_all(alias, "`");
if (strcasecmp(ifp->field_name.str, alias.c_str()) == 0 ||
(col && alias.find(".") != string::npos &&
(strcasecmp(ifp->field_name.str, col->columnName().c_str()) == 0 ||
strcasecmp(ifp->field_name.str, (alias.substr(alias.find_last_of(".") + 1)).c_str()) ==
0))) //@bug6066
{
// @bug4827. Remove the checking because outside tables could be the same
// name as inner tables. This function is to identify column from a table,
// as long as it matches the next step in predicate parsing will tell the scope
// of the column.
/*if (sc)
{
gwi.fatalParseError = true;
Message::Args args;
args.add(ifp->name);
gwi.parseErrorText = IDBErrorInfo::instance()->errorMsg(ERR_AMBIGUOUS_COL, args);
return NULL;
}*/
sc = new SimpleColumn();
if (!col)
sc->columnName(cols[j]->alias());
else
sc->columnName(col->columnName());
// @bug 3003. Keep column alias if it has.
sc->alias(!ifp->is_explicit_name() ? cols[j]->alias() : ifp->name.str);
sc->tableName(csep->derivedTbAlias());
sc->colPosition(j);
sc->tableAlias(csep->derivedTbAlias());
sc->timeZone(gwi.timeZone);
if (!viewName.empty())
{
sc->viewName(viewName, lower_case_table_names);
}
else
{
sc->viewName(csep->derivedTbView());
}
sc->resultType(cols[j]->resultType());
sc->hasAggregate(cols[j]->hasAggregate());
if (col)
sc->isColumnStore(col->isColumnStore());
// @bug5634, @bug5635. mark used derived col on derived table.
// outer join inner table filter can not be moved in
// MariaDB 10.1: cached_table is never available for derived tables.
// Find the uncached object in table_list
TABLE_LIST* tblList = ifp->context ? ifp->context->table_list : NULL;
while (tblList)
{
if (strcasecmp(tblList->alias.str, ifp->table_name.str) == 0)
{
if (!tblList->outer_join)
{
sc->derivedTable(derivedName);
sc->derivedRefCol(cols[j].get());
}
break;
}
tblList = tblList->next_local;
}
cols[j]->incRefCount();
break;
}
}
}
}
if (!sc)
{
gwi.fatalParseError = true;
Message::Args args;
string name;
if (ifp->db_name.str)
name += string(ifp->db_name.str) + ".";
if (ifp->table_name.str)
name += string(ifp->table_name.str) + ".";
if (ifp->name.length)
name += ifp->name.str;
args.add(name);
gwi.parseErrorText = IDBErrorInfo::instance()->errorMsg(ERR_UNKNOWN_COL, args);
}
if (ifp->depended_from)
{
sc->joinInfo(sc->joinInfo() | JOIN_CORRELATED);
if (gwi.subQuery)
gwi.subQuery->correlated(true);
// for error out non-support select filter case (comparison outside semi join tables)
gwi.correlatedTbNameVec.push_back(make_aliastable(sc->schemaName(), sc->tableName(), sc->tableAlias()));
// imply semi for scalar for now.
if (gwi.subSelectType == CalpontSelectExecutionPlan::SINGLEROW_SUBS)
sc->joinInfo(sc->joinInfo() | JOIN_SCALAR | JOIN_SEMI);
if (gwi.subSelectType == CalpontSelectExecutionPlan::SELECT_SUBS)
sc->joinInfo(sc->joinInfo() | JOIN_SCALAR | JOIN_OUTER_SELECT);
}
return sc;
}
// for FROM clause subquery. get all the columns from real tables and derived tables.
void collectAllCols(gp_walk_info& gwi, Item_field* ifp)
{
// view name
string viewName = getViewName(ifp->cached_table);
if (gwi.derivedTbList.empty())
{
gwi.fatalParseError = true;
Message::Args args;
args.add("*");
gwi.parseErrorText = IDBErrorInfo::instance()->errorMsg(ERR_UNKNOWN_COL, args);
}
string tableName = (ifp->table_name.str ? string(ifp->table_name.str) : "");
CalpontSelectExecutionPlan::SelectList::const_iterator it = gwi.derivedTbList.begin();
for (uint32_t i = 0; i < gwi.tbList.size(); i++)
{
SRCP srcp;
// derived table
if (gwi.tbList[i].schema.empty())
{
CalpontSelectExecutionPlan* csep = dynamic_cast<CalpontSelectExecutionPlan*>((*it).get());
++it;
if (!tableName.empty() && strcasecmp(tableName.c_str(), csep->derivedTbAlias().c_str()) != 0)
continue;
CalpontSelectExecutionPlan::ReturnedColumnList cols = csep->returnedCols();
for (uint32_t j = 0; j < cols.size(); j++)
{
SimpleColumn* sc = new SimpleColumn();
sc->columnName(cols[j]->alias());
sc->colPosition(j);
sc->tableAlias(csep->derivedTbAlias());
sc->viewName(gwi.tbList[i].view);
sc->resultType(cols[j]->resultType());
sc->timeZone(gwi.timeZone);
// @bug5634 derived table optimization
cols[j]->incRefCount();
sc->derivedTable(sc->tableAlias());
sc->derivedRefCol(cols[j].get());
srcp.reset(sc);
gwi.returnedCols.push_back(srcp);
gwi.columnMap.insert(CalpontSelectExecutionPlan::ColumnMap::value_type(sc->columnName(), srcp));
}
}
// real tables
else
{
CalpontSystemCatalog::TableName tn(gwi.tbList[i].schema, gwi.tbList[i].table);
if (lower_case_table_names)
{
if (!tableName.empty() && (strcasecmp(tableName.c_str(), tn.table.c_str()) != 0 &&
strcasecmp(tableName.c_str(), gwi.tbList[i].alias.c_str()) != 0))
continue;
}
else
{
if (!tableName.empty() && (strcmp(tableName.c_str(), tn.table.c_str()) != 0 &&
strcmp(tableName.c_str(), gwi.tbList[i].alias.c_str()) != 0))
continue;
}
if (lower_case_table_names)
{
boost::algorithm::to_lower(tn.schema);
boost::algorithm::to_lower(tn.table);
}
CalpontSystemCatalog::RIDList oidlist = gwi.csc->columnRIDs(tn, true);
for (unsigned int j = 0; j < oidlist.size(); j++)
{
SimpleColumn* sc = new SimpleColumn();
CalpontSystemCatalog::TableColName tcn = gwi.csc->colName(oidlist[j].objnum);
CalpontSystemCatalog::ColType ct = gwi.csc->colType(oidlist[j].objnum);
sc->columnName(tcn.column);
sc->tableName(tcn.table, lower_case_table_names);
sc->schemaName(tcn.schema, lower_case_table_names);
sc->oid(oidlist[j].objnum);
sc->alias(tcn.column);
sc->resultType(ct);
sc->tableAlias(gwi.tbList[i].alias, lower_case_table_names);
sc->viewName(viewName, lower_case_table_names);
sc->timeZone(gwi.timeZone);
srcp.reset(sc);
gwi.returnedCols.push_back(srcp);
gwi.columnMap.insert(CalpontSelectExecutionPlan::ColumnMap::value_type(sc->columnName(), srcp));
}
}
}
}
void buildSubselectFunc(Item_func* ifp, gp_walk_info* gwip)
{
// @bug 3035
if (!isPredicateFunction(ifp, gwip))
{
gwip->fatalParseError = true;
gwip->parseErrorText = IDBErrorInfo::instance()->errorMsg(ERR_NON_SUPPORT_FUNC_SUB);
return;
}
WhereSubQuery* subquery = NULL;
for (uint32_t i = 0; i < ifp->argument_count(); i++)
{
#if MYSQL_VERSION_ID >= 50172
// changes comply with mysql 5.1.72
if (ifp->arguments()[i]->type() == Item::FUNC_ITEM &&
string(((Item_func*)ifp->arguments()[i])->func_name()) == "<in_optimizer>")
{
if (ifp->functype() == Item_func::NOT_FUNC)
{
if (gwip->lastSub)
gwip->lastSub->handleNot();
}
}
#endif
if (ifp->arguments()[i]->type() == Item::SUBSELECT_ITEM)
{
Item_subselect* sub = (Item_subselect*)ifp->arguments()[i];
switch (sub->substype())
{
case Item_subselect::SINGLEROW_SUBS: subquery = new ScalarSub(*gwip, ifp); break;
case Item_subselect::IN_SUBS: subquery = new InSub(*gwip, ifp); break;
case Item_subselect::EXISTS_SUBS:
// exists sub has been handled earlier. here is for not function
if (ifp->functype() == Item_func::NOT_FUNC)
{
if (gwip->lastSub)
gwip->lastSub->handleNot();
}
break;
default:
Message::Args args;
gwip->fatalParseError = true;
gwip->parseErrorText = "non supported subquery";
return;
}
}
}
if (subquery)
{
gwip->hasSubSelect = true;
SubQuery* orig = gwip->subQuery;
gwip->subQuery = subquery;
// no need to check NULL for now. error will be handled in gp_walk
gwip->ptWorkStack.push(subquery->transform());
// recover original sub. Save current sub for Not handling.
gwip->lastSub = subquery;
gwip->subQuery = orig;
}
return;
}
bool isPredicateFunction(Item* item, gp_walk_info* gwip)
{
if (item->type() == Item::COND_ITEM)
return true;
if (item->type() != Item::FUNC_ITEM)
return false;
Item_func* ifp = (Item_func*)item;
return ( ifp->functype() == Item_func::EQ_FUNC ||
ifp->functype() == Item_func::NE_FUNC ||
ifp->functype() == Item_func::LT_FUNC ||
ifp->functype() == Item_func::LE_FUNC ||
ifp->functype() == Item_func::GE_FUNC ||
ifp->functype() == Item_func::GT_FUNC ||
ifp->functype() == Item_func::LIKE_FUNC ||
ifp->functype() == Item_func::BETWEEN ||
ifp->functype() == Item_func::IN_FUNC ||
(ifp->functype() == Item_func::ISNULL_FUNC &&
(gwip->clauseType == WHERE || gwip->clauseType == HAVING)) ||
(ifp->functype() == Item_func::ISNOTNULL_FUNC &&
(gwip->clauseType == WHERE || gwip->clauseType == HAVING)) ||
ifp->functype() == Item_func::NOT_FUNC ||
ifp->functype() == Item_func::ISNOTNULLTEST_FUNC ||
ifp->functype() == Item_func::TRIG_COND_FUNC ||
string(ifp->func_name()) == "<in_optimizer>"/* ||
string(ifp->func_name()) == "xor"*/);
}
void setError(THD* thd, uint32_t errcode, string errmsg)
{
thd->get_stmt_da()->set_overwrite_status(true);
if (errmsg.empty())
errmsg = "Unknown error";
if (errcode < ER_ERROR_FIRST || errcode > ER_ERROR_LAST)
{
errcode = ER_UNKNOWN_ERROR;
}
thd->raise_error_printf(errcode, errmsg.c_str());
// reset expressionID
if (get_fe_conn_info_ptr() == NULL)
{
set_fe_conn_info_ptr((void*)new cal_connection_info());
thd_set_ha_data(current_thd, mcs_hton, get_fe_conn_info_ptr());
}
cal_connection_info* ci = static_cast<cal_connection_info*>(get_fe_conn_info_ptr());
ci->expressionId = 0;
}
void setError(THD* thd, uint32_t errcode, string errmsg, gp_walk_info& gwi)
{
setError(thd, errcode, errmsg);
}
int setErrorAndReturn(gp_walk_info& gwi)
{
// if this is dervied table process phase, mysql may have not developed the plan
// completely. Do not error and eventually mysql will call JOIN::exec() again.
// related to bug 2922. Need to find a way to skip calling rnd_init for derived table
// processing.
if (gwi.thd->derived_tables_processing)
{
gwi.cs_vtable_is_update_with_derive = true;
return -1;
}
setError(gwi.thd, ER_INTERNAL_ERROR, gwi.parseErrorText, gwi);
return ER_INTERNAL_ERROR;
}
const string bestTableName(const Item_field* ifp)
{
idbassert(ifp);
if (!ifp->table_name.str)
return "";
if (!ifp->field)
return ifp->table_name.str;
string table_name(ifp->table_name.str);
string field_table_table_name;
if (ifp->cached_table)
field_table_table_name = ifp->cached_table->table_name.str;
else if (ifp->field->table && ifp->field->table->s && ifp->field->table->s->table_name.str)
field_table_table_name = ifp->field->table->s->table_name.str;
string tn;
if (!field_table_table_name.empty())
tn = field_table_table_name;
else
tn = table_name;
return tn;
}
uint32_t setAggOp(AggregateColumn* ac, Item_sum* isp)
{
Item_sum::Sumfunctype agg_type = isp->sum_func();
uint32_t rc = 0;
switch (agg_type)
{
case Item_sum::COUNT_FUNC: ac->aggOp(AggregateColumn::COUNT); return rc;
case Item_sum::SUM_FUNC: ac->aggOp(AggregateColumn::SUM); return rc;
case Item_sum::AVG_FUNC: ac->aggOp(AggregateColumn::AVG); return rc;
case Item_sum::MIN_FUNC: ac->aggOp(AggregateColumn::MIN); return rc;
case Item_sum::MAX_FUNC: ac->aggOp(AggregateColumn::MAX); return rc;
case Item_sum::COUNT_DISTINCT_FUNC:
ac->aggOp(AggregateColumn::DISTINCT_COUNT);
ac->distinct(true);
return rc;
case Item_sum::SUM_DISTINCT_FUNC:
ac->aggOp(AggregateColumn::DISTINCT_SUM);
ac->distinct(true);
return rc;
case Item_sum::AVG_DISTINCT_FUNC:
ac->aggOp(AggregateColumn::DISTINCT_AVG);
ac->distinct(true);
return rc;
case Item_sum::STD_FUNC:
{
Item_sum_variance* var = (Item_sum_variance*)isp;
if (var->sample)
ac->aggOp(AggregateColumn::STDDEV_SAMP);
else
ac->aggOp(AggregateColumn::STDDEV_POP);
return rc;
}
case Item_sum::VARIANCE_FUNC:
{
Item_sum_variance* var = (Item_sum_variance*)isp;
if (var->sample)
ac->aggOp(AggregateColumn::VAR_SAMP);
else
ac->aggOp(AggregateColumn::VAR_POP);
return rc;
}
case Item_sum::GROUP_CONCAT_FUNC:
{
Item_func_group_concat* gc = (Item_func_group_concat*)isp;
ac->aggOp(AggregateColumn::GROUP_CONCAT);
ac->distinct(gc->get_distinct());
return rc;
}
case Item_sum::JSON_ARRAYAGG_FUNC:
{
Item_func_json_arrayagg* gc = (Item_func_json_arrayagg*)isp;
ac->aggOp(AggregateColumn::JSON_ARRAYAGG);
ac->distinct(gc->get_distinct());
return rc;
}
case Item_sum::SUM_BIT_FUNC:
{
string funcName = isp->func_name();
if (funcName.compare("bit_and(") == 0)
ac->aggOp(AggregateColumn::BIT_AND);
else if (funcName.compare("bit_or(") == 0)
ac->aggOp(AggregateColumn::BIT_OR);
else if (funcName.compare("bit_xor(") == 0)
ac->aggOp(AggregateColumn::BIT_XOR);
else
return ER_CHECK_NOT_IMPLEMENTED;
return rc;
}
case Item_sum::UDF_SUM_FUNC: ac->aggOp(AggregateColumn::UDAF); return rc;
default: return ER_CHECK_NOT_IMPLEMENTED;
}
}
/* get the smallest column of a table. Used for filling columnmap */
SimpleColumn* getSmallestColumn(boost::shared_ptr<CalpontSystemCatalog> csc,
CalpontSystemCatalog::TableName& tn,
CalpontSystemCatalog::TableAliasName& tan, TABLE* table, gp_walk_info& gwi)
{
if (lower_case_table_names)
{
boost::algorithm::to_lower(tn.schema);
boost::algorithm::to_lower(tn.table);
boost::algorithm::to_lower(tan.schema);
boost::algorithm::to_lower(tan.table);
boost::algorithm::to_lower(tan.alias);
boost::algorithm::to_lower(tan.view);
}
// derived table
if (tan.schema.empty())
{
for (uint32_t i = 0; i < gwi.derivedTbList.size(); i++)
{
CalpontSelectExecutionPlan* csep =
dynamic_cast<CalpontSelectExecutionPlan*>(gwi.derivedTbList[i].get());
if (tan.alias == csep->derivedTbAlias())
{
const CalpontSelectExecutionPlan::ReturnedColumnList& cols = csep->returnedCols();
CalpontSelectExecutionPlan::ReturnedColumnList::const_iterator iter;
ReturnedColumn* rc = nullptr;
for (iter = cols.begin(); iter != cols.end(); iter++)
{
if ((*iter)->refCount() != 0)
{
rc = dynamic_cast<ReturnedColumn*>(iter->get());
break;
}
}
if (iter == cols.end())
{
assert(!cols.empty());
// We take cols[0] here due to the optimization happening in
// derivedTableOptimization. All cols with refCount 0 from
// the end of the cols list are optimized out, until the
// first column with non-zero refCount is encountered. So
// here, if instead of cols[0], we take cols[1] (based on
// some logic) and increment it's refCount, then cols[0] is
// not optimized out in derivedTableOptimization and is
// added as a ConstantColumn to the derived table's returned
// column list. This later causes an ineffective row group
// with row of the form (1, cols[1]_value1) to be created in ExeMgr.
rc = dynamic_cast<ReturnedColumn*>(cols[0].get());
// @bug5634 derived table optimization.
rc->incRefCount();
}
SimpleColumn* sc = new SimpleColumn();
sc->columnName(rc->alias());
sc->sequence(0);
sc->tableAlias(tan.alias);
sc->timeZone(gwi.timeZone);
sc->derivedTable(csep->derivedTbAlias());
sc->derivedRefCol(rc);
return sc;
}
}
throw runtime_error("getSmallestColumn: Internal error.");
}
// check engine type
if (!tan.fisColumnStore)
{
// get the first column to project. @todo optimization to get the smallest one for foreign engine.
Field* field = *(table->field);
SimpleColumn* sc = new SimpleColumn(table->s->db.str, table->s->table_name.str, field->field_name.str,
tan.fisColumnStore, gwi.sessionid, lower_case_table_names);
sc->tableAlias(table->alias.ptr(), lower_case_table_names);
sc->isColumnStore(false);
sc->timeZone(gwi.timeZone);
sc->resultType(fieldType_MysqlToIDB(field));
sc->oid(field->field_index + 1);
return sc;
}
CalpontSystemCatalog::RIDList oidlist = csc->columnRIDs(tn, true);
idbassert(oidlist.size() == table->s->fields);
CalpontSystemCatalog::TableColName tcn;
int minColWidth = -1;
int minWidthColOffset = 0;
for (unsigned int j = 0; j < oidlist.size(); j++)
{
CalpontSystemCatalog::ColType ct = csc->colType(oidlist[j].objnum);
if (ct.colDataType == CalpontSystemCatalog::VARBINARY)
continue;
if (minColWidth == -1 || ct.colWidth < minColWidth)
{
minColWidth = ct.colWidth;
minWidthColOffset = j;
}
}
tcn = csc->colName(oidlist[minWidthColOffset].objnum);
SimpleColumn* sc = new SimpleColumn(tcn.schema, tcn.table, tcn.column, csc->sessionID());
sc->tableAlias(tan.alias);
sc->viewName(tan.view);
sc->timeZone(gwi.timeZone);
sc->resultType(csc->colType(oidlist[minWidthColOffset].objnum));
sc->charsetNumber(table->field[minWidthColOffset]->charset()->number);
return sc;
}
CalpontSystemCatalog::ColType fieldType_MysqlToIDB(const Field* field)
{
CalpontSystemCatalog::ColType ct;
ct.precision = 4;
switch (field->result_type())
{
case INT_RESULT:
ct.colDataType = CalpontSystemCatalog::BIGINT;
ct.colWidth = 8;
break;
case STRING_RESULT:
ct.colDataType = CalpontSystemCatalog::VARCHAR;
ct.colWidth = field->field_length;
break;
case DECIMAL_RESULT:
{
Field_decimal* idp = (Field_decimal*)field;
ct.colDataType = CalpontSystemCatalog::DECIMAL;
ct.colWidth = 8;
ct.scale = idp->dec;
if (ct.scale == 0)
ct.precision = idp->field_length - 1;
else
ct.precision = idp->field_length - idp->dec;
break;
}
case REAL_RESULT:
ct.colDataType = CalpontSystemCatalog::DOUBLE;
ct.colWidth = 8;
break;
default:
IDEBUG(cerr << "fieldType_MysqlToIDB:: Unknown result type of MySQL " << field->result_type() << endl);
break;
}
return ct;
}
CalpontSystemCatalog::ColType colType_MysqlToIDB(const Item* item)
{
CalpontSystemCatalog::ColType ct;
ct.precision = 4;
switch (item->result_type())
{
case INT_RESULT:
if (item->unsigned_flag)
{
ct.colDataType = CalpontSystemCatalog::UBIGINT;
}
else
{
ct.colDataType = CalpontSystemCatalog::BIGINT;
}
ct.colWidth = 8;
break;
case STRING_RESULT:
ct.colDataType = CalpontSystemCatalog::VARCHAR;
// MCOL-4758 the longest TEXT we deal with is 16777215 so
// limit to that.
if (item->max_length < 16777215)
ct.colWidth = item->max_length;
else
ct.colWidth = 16777215;
// force token
if (item->type() == Item::FUNC_ITEM)
{
if (ct.colWidth < 20)
ct.colWidth = 20; // for infinidb date length
}
// @bug5083. MySQL gives string type for date/datetime column.
// need to adjust here.
if (item->type() == Item::FIELD_ITEM)
{
if (item->field_type() == MYSQL_TYPE_DATE)
{
ct.colDataType = CalpontSystemCatalog::DATE;
ct.colWidth = 4;
}
else if (item->field_type() == MYSQL_TYPE_DATETIME || item->field_type() == MYSQL_TYPE_DATETIME2)
{
ct.colDataType = CalpontSystemCatalog::DATETIME;
ct.colWidth = 8;
}
else if (item->field_type() == MYSQL_TYPE_TIMESTAMP || item->field_type() == MYSQL_TYPE_TIMESTAMP2)
{
ct.colDataType = CalpontSystemCatalog::TIMESTAMP;
ct.colWidth = 8;
}
else if (item->field_type() == MYSQL_TYPE_TIME)
{
ct.colDataType = CalpontSystemCatalog::TIME;
ct.colWidth = 8;
}
if (item->field_type() == MYSQL_TYPE_BLOB)
{
// We default to BLOB, but then try to correct type,
// because many textual types in server have type_handler_blob
// (and variants) as their type.
ct.colDataType = CalpontSystemCatalog::BLOB;
}
}
break;
/* FIXME:
case xxxBINARY_RESULT:
ct.colDataType = CalpontSystemCatalog::VARBINARY;
ct.colWidth = item->max_length;
// force token
if (item->type() == Item::FUNC_ITEM)
{
if (ct.colWidth < 20)
ct.colWidth = 20; // for infinidb date length
}
break;
*/
case DECIMAL_RESULT:
{
// decimal result do not shows us Item is Item_decimal
ct.colDataType = CalpontSystemCatalog::DECIMAL;
unsigned int precision = item->decimal_precision();
unsigned int scale = item->decimal_scale();
ct.setDecimalScalePrecision(precision, scale);
break;
}
case REAL_RESULT:
ct.colDataType = CalpontSystemCatalog::DOUBLE;
ct.colWidth = 8;
break;
default:
IDEBUG(cerr << "colType_MysqlToIDB:: Unknown result type of MySQL " << item->result_type() << endl);
break;
}
ct.charsetNumber = item->collation.collation->number;
return ct;
}
bool itemDisablesWrapping(Item* item, gp_walk_info& gwi)
{
if (gwi.select_lex == nullptr)
{
return true;
}
ORDER* groupcol = static_cast<ORDER*>(gwi.select_lex->group_list.first);
while (groupcol)
{
Item* gci = *groupcol->item;
while (gci->type() == Item::REF_ITEM)
{
if (item->eq(gci, false))
{
return true;
}
Item_ref* ref = (Item_ref*)gci;
gci = *(ref->ref);
}
if (item->eq(gci, false))
{
return true;
}
groupcol = groupcol->next;
}
return false;
}
ReturnedColumn* wrapIntoAggregate(ReturnedColumn* rc, gp_walk_info& gwi, Item* baseItem)
{
if (!gwi.implicitExplicitGroupBy || gwi.disableWrapping || !gwi.select_lex)
{
return rc;
}
if (dynamic_cast<AggregateColumn*>(rc) != nullptr || dynamic_cast<ConstantColumn*>(rc) != nullptr)
{
return rc;
}
if (itemDisablesWrapping(baseItem, gwi))
{
return rc;
}
cal_connection_info* ci = static_cast<cal_connection_info*>(get_fe_conn_info_ptr());
AggregateColumn* ac = new AggregateColumn(gwi.sessionid);
ac->timeZone(gwi.timeZone);
ac->alias(rc->alias());
ac->aggOp(AggregateColumn::SELECT_SOME);
ac->asc(rc->asc());
ac->charsetNumber(rc->charsetNumber());
ac->orderPos(rc->orderPos());
uint32_t i;
for(i=0; i < gwi.processed.size() && !gwi.processed[i].first->eq(baseItem, false);i++)
{ }
if (i < gwi.processed.size())
{
ac->expressionId(gwi.processed[i].second);
}
else
{
ac->expressionId(ci->expressionId++);
}
ac->aggParms().push_back(SRCP(rc));
ac->resultType(rc->resultType());
return ac;
}
ReturnedColumn* buildReturnedColumnNull(gp_walk_info& gwi)
{
if (gwi.condPush)
return new SimpleColumn("noop");
ConstantColumn* rc = new ConstantColumnNull();
if (rc)
rc->timeZone(gwi.timeZone);
return rc;
}
class ValStrStdString : public string
{
bool mIsNull;
public:
ValStrStdString(Item* item)
{
String val, *str = item->val_str(&val);
mIsNull = (str == nullptr);
DBUG_ASSERT(mIsNull == item->null_value);
if (!mIsNull)
assign(str->ptr(), str->length());
}
bool isNull() const
{
return mIsNull;
}
};
/*
Create a ConstantColumn according to cmp_type().
But do not set the time zone yet.
Handles NOT NULL values.
Three ways of value extraction are used depending on the data type:
1. Using a native val_xxx().
2. Using val_str() with further convertion to the native representation.
3. Using both val_str() and a native val_xxx().
We should eventually get rid of N2 and N3 and use N1 for all data types:
- N2 contains a redundant code for str->native conversion.
It should be replaced to an existing code (a Type_handler method call?).
- N3 performs double evalation of the value, which may cause
various negative effects (double side effects or double warnings).
*/
static ConstantColumn* newConstantColumnNotNullUsingValNativeNoTz(Item* item, gp_walk_info& gwi)
{
DBUG_ASSERT(item->const_item());
switch (item->cmp_type())
{
case INT_RESULT:
{
if (item->unsigned_flag)
return new ConstantColumnUInt((uint64_t)item->val_uint(), (int8_t)item->decimal_scale(),
(uint8_t)item->decimal_precision());
ValStrStdString str(item);
DBUG_ASSERT(!str.isNull());
return new ConstantColumnSInt(colType_MysqlToIDB(item), str, (int64_t)item->val_int());
}
case STRING_RESULT:
{
// Special handling for 0xHHHH literals
if (item->type_handler() == &type_handler_hex_hybrid)
return new ConstantColumn((int64_t)item->val_int(), ConstantColumn::NUM);
ValStrStdString str(item);
DBUG_ASSERT(!str.isNull());
return new ConstantColumnString(str);
}
case REAL_RESULT:
{
ValStrStdString str(item);
DBUG_ASSERT(!str.isNull());
return new ConstantColumnReal(colType_MysqlToIDB(item), str, item->val_real());
}
case DECIMAL_RESULT:
{
ValStrStdString str(item);
DBUG_ASSERT(!str.isNull());
return buildDecimalColumn(item, str, gwi);
}
case TIME_RESULT:
{
ValStrStdString str(item);
DBUG_ASSERT(!str.isNull());
return new ConstantColumnTemporal(colType_MysqlToIDB(item), str);
}
default:
{
gwi.fatalParseError = true;
gwi.parseErrorText = "Unknown item type";
break;
}
}
return nullptr;
}
/*
Create a ConstantColumn according to cmp_type().
But do not set the time zone yet.
Handles NULL and NOT NULL values.
Uses a simplified logic regarding to data types:
always extracts the value through val_str().
Should probably be joined with the previous function, to have
a single function which can at the same time:
a. handle both NULL and NOT NULL values
b. extract values using a native val_xxx() method,
to avoid possible negative effects mentioned in the comments
to newConstantColumnNotNullUsingValNativeNoTz().
*/
static ConstantColumn* newConstantColumnMaybeNullFromValStrNoTz(const Item* item,
const ValStrStdString& valStr,
gp_walk_info& gwi)
{
if (valStr.isNull())
return new ConstantColumnNull();
switch (item->result_type())
{
case STRING_RESULT: return new ConstantColumnString(valStr);
case DECIMAL_RESULT: return buildDecimalColumn(item, valStr, gwi);
case TIME_RESULT:
case INT_RESULT:
case REAL_RESULT:
case ROW_RESULT: return new ConstantColumnNum(colType_MysqlToIDB(item), valStr);
}
return nullptr;
}
// Create a ConstantColumn from a previously evaluated val_str() result,
// Supports both NULL and NOT NULL values.
// Sets the time zone according to gwi.
static ConstantColumn* buildConstantColumnMaybeNullFromValStr(const Item* item, const ValStrStdString& valStr,
gp_walk_info& gwi)
{
ConstantColumn* rc = newConstantColumnMaybeNullFromValStrNoTz(item, valStr, gwi);
if (rc)
rc->timeZone(gwi.timeZone);
return rc;
}
// Create a ConstantColumn by calling val_str().
// Supports both NULL and NOT NULL values.
// Sets the time zone according to gwi.
static ConstantColumn* buildConstantColumnMaybeNullUsingValStr(Item* item, gp_walk_info& gwi)
{
return buildConstantColumnMaybeNullFromValStr(item, ValStrStdString(item), gwi);
}
// Create a ConstantColumn for a NOT NULL expression.
// Sets the time zone according to gwi.
static ConstantColumn* buildConstantColumnNotNullUsingValNative(Item* item, gp_walk_info& gwi)
{
ConstantColumn* rc = newConstantColumnNotNullUsingValNativeNoTz(item, gwi);
if (rc)
rc->timeZone(gwi.timeZone);
return rc;
}
ReturnedColumn* buildReturnedColumnBody(Item* item, gp_walk_info& gwi, bool& nonSupport, bool isRefItem)
{
ReturnedColumn* rc = NULL;
if (gwi.thd)
{
// if ( ((gwi.thd->lex)->sql_command == SQLCOM_UPDATE ) || ((gwi.thd->lex)->sql_command ==
// SQLCOM_UPDATE_MULTI ))
{
if (!item->fixed())
{
item->fix_fields(gwi.thd, (Item**)&item);
}
}
}
switch (item->type())
{
case Item::FIELD_ITEM:
{
Item_field* ifp = (Item_field*)item;
return wrapIntoAggregate(buildSimpleColumn(ifp, gwi), gwi, ifp);
}
case Item::NULL_ITEM: return buildReturnedColumnNull(gwi);
case Item::CONST_ITEM:
{
rc = buildConstantColumnNotNullUsingValNative(item, gwi);
break;
}
case Item::FUNC_ITEM:
{
if (item->const_item())
{
rc = buildConstantColumnMaybeNullUsingValStr(item, gwi);
break;
}
Item_func* ifp = (Item_func*)item;
string func_name = ifp->func_name();
// try to evaluate const F&E. only for select clause
vector<Item_field*> tmpVec;
// bool hasAggColumn = false;
uint16_t parseInfo = 0;
parse_item(ifp, tmpVec, gwi.fatalParseError, parseInfo, &gwi);
if (parseInfo & SUB_BIT)
{
gwi.fatalParseError = true;
gwi.parseErrorText = IDBErrorInfo::instance()->errorMsg(ERR_NON_SUPPORT_SELECT_SUB);
setError(gwi.thd, ER_CHECK_NOT_IMPLEMENTED, gwi.parseErrorText, gwi);
break;
}
if (!gwi.fatalParseError && !nonConstFunc(ifp) && !(parseInfo & AF_BIT) && (tmpVec.size() == 0))
{
rc = buildConstantColumnMaybeNullUsingValStr(item, gwi);
break;
}
if (func_name == "+" || func_name == "-" || func_name == "*" || func_name == "/")
return buildArithmeticColumn(ifp, gwi, nonSupport);
else
{
return buildFunctionColumn(ifp, gwi, nonSupport);
}
}
case Item::SUM_FUNC_ITEM:
{
return buildAggregateColumn(item, gwi);
}
case Item::REF_ITEM:
{
Item_ref* ref = (Item_ref*)item;
switch ((*(ref->ref))->type())
{
case Item::SUM_FUNC_ITEM: return buildAggregateColumn(*(ref->ref), gwi);
case Item::FIELD_ITEM: return buildReturnedColumn(*(ref->ref), gwi, nonSupport);
case Item::REF_ITEM: return buildReturnedColumn(*(((Item_ref*)(*(ref->ref)))->ref), gwi, nonSupport);
case Item::FUNC_ITEM: return buildFunctionColumn((Item_func*)(*(ref->ref)), gwi, nonSupport);
case Item::WINDOW_FUNC_ITEM: return buildWindowFunctionColumn(*(ref->ref), gwi, nonSupport);
case Item::SUBSELECT_ITEM:
gwi.fatalParseError = true;
gwi.parseErrorText = IDBErrorInfo::instance()->errorMsg(ERR_NON_SUPPORT_SELECT_SUB);
break;
default:
if (ref->ref_type() == Item_ref::DIRECT_REF)
{
return buildReturnedColumn(ref->real_item(), gwi, nonSupport);
}
gwi.fatalParseError = true;
gwi.parseErrorText = "Unknown REF item";
break;
}
return buildReturnedColumnNull(gwi);
}
case Item::CACHE_ITEM:
{
Item* col = ((Item_cache*)item)->get_example();
rc = buildReturnedColumn(col, gwi, nonSupport);
if (rc)
{
ConstantColumn* cc = dynamic_cast<ConstantColumn*>(rc);
if (!cc)
{
rc->joinInfo(rc->joinInfo() | JOIN_CORRELATED);
if (gwi.subQuery)
gwi.subQuery->correlated(true);
}
}
break;
}
case Item::EXPR_CACHE_ITEM:
{
// TODO: item is a Item_cache_wrapper
printf("EXPR_CACHE_ITEM in buildReturnedColumn\n");
cerr << "EXPR_CACHE_ITEM in buildReturnedColumn" << endl;
break;
}
case Item::WINDOW_FUNC_ITEM:
{
return buildWindowFunctionColumn(item, gwi, nonSupport);
}
#if INTERVAL_ITEM
case Item::INTERVAL_ITEM:
{
Item_interval* interval = (Item_interval*)item;
SRCP srcp;
srcp.reset(buildReturnedColumn(interval->item, gwi, nonSupport));
if (!srcp)
return NULL;
rc = new IntervalColumn(srcp, (int)interval->interval);
rc->resultType(srcp->resultType());
break;
}
#endif
case Item::SUBSELECT_ITEM:
{
gwi.hasSubSelect = true;
break;
}
case Item::COND_ITEM:
{
// MCOL-1196: Allow COND_ITEM thru. They will be picked up
// by further logic. It may become desirable to add code here.
break;
}
default:
{
gwi.fatalParseError = true;
gwi.parseErrorText = "Unknown item type";
break;
}
}
if (rc && item->name.length)
rc->alias(item->name.str);
if (rc)
rc->charsetNumber(item->collation.collation->number);
return rc;
}
ReturnedColumn* buildReturnedColumn(Item* item, gp_walk_info& gwi, bool& nonSupport, bool isRefItem)
{
bool disableWrapping = gwi.disableWrapping;
gwi.disableWrapping = gwi.disableWrapping || itemDisablesWrapping(item, gwi);
ReturnedColumn* rc = buildReturnedColumnBody(item, gwi, nonSupport, isRefItem);
gwi.disableWrapping = disableWrapping;
return rc;
}
// parse the boolean fields to string "true" or "false"
ReturnedColumn* buildBooleanConstantColumn(Item* item, gp_walk_info& gwi, bool& nonSupport)
{
ConstantColumn* cc = NULL;
if (gwi.thd)
{
{
if (!item->fixed())
{
item->fix_fields(gwi.thd, (Item**)&item);
}
}
}
int64_t val = static_cast<int64_t>(item->val_int());
cc = new ConstantColumnSInt(colType_MysqlToIDB(item), val ? "true" : "false", val);
if (cc)
cc->timeZone(gwi.timeZone);
if (cc && item->name.length)
cc->alias(item->name.str);
if (cc)
cc->charsetNumber(item->collation.collation->number);
return cc;
}
ReturnedColumn* buildArithmeticColumnBody(Item_func* item, gp_walk_info& gwi, bool& nonSupport)
{
if (get_fe_conn_info_ptr() == NULL)
{
set_fe_conn_info_ptr((void*)new cal_connection_info());
thd_set_ha_data(current_thd, mcs_hton, get_fe_conn_info_ptr());
}
cal_connection_info* ci = static_cast<cal_connection_info*>(get_fe_conn_info_ptr());
ArithmeticColumn* ac = new ArithmeticColumn();
Item** sfitempp = item->arguments();
ArithmeticOperator* aop = new ArithmeticOperator(item->func_name());
aop->timeZone(gwi.timeZone);
aop->setOverflowCheck(get_decimal_overflow_check(gwi.thd));
ParseTree* pt = new ParseTree(aop);
// ReturnedColumn *lhs = 0, *rhs = 0;
ParseTree *lhs = 0, *rhs = 0;
SRCP srcp;
if (item->name.length)
ac->alias(item->name.str);
// argument_count() should generally be 2, except negate expression
if (item->argument_count() == 2)
{
if (gwi.clauseType == SELECT || /*gwi.clauseType == HAVING || */ gwi.clauseType == GROUP_BY ||
gwi.clauseType == FROM) // select list
{
lhs = new ParseTree(buildReturnedColumn(sfitempp[0], gwi, nonSupport));
if (!lhs->data() && (sfitempp[0]->type() == Item::FUNC_ITEM))
{
delete lhs;
lhs = buildParseTree(sfitempp[0], gwi, nonSupport);
}
else if (!lhs->data() && (sfitempp[0]->type() == Item::REF_ITEM))
{
// There must be an aggregation column in extended SELECT
// list so find the corresponding column.
// Could have it set if there are aggregation funcs as this function arguments.
gwi.fatalParseError = false;
//ReturnedColumn* rc = buildAggFrmTempField(sfitempp[0], gwi);
ReturnedColumn* rc = buildReturnedColumn(sfitempp[0], gwi, nonSupport);
if (rc)
lhs = new ParseTree(rc);
}
rhs = new ParseTree(buildReturnedColumn(sfitempp[1], gwi, nonSupport));
if (!rhs->data() && (sfitempp[1]->type() == Item::FUNC_ITEM))
{
delete rhs;
rhs = buildParseTree(sfitempp[1], gwi, nonSupport);
}
else if (!rhs->data() && (sfitempp[1]->type() == Item::REF_ITEM))
{
// There must be an aggregation column in extended SELECT
// list so find the corresponding column.
// Could have it set if there are aggregation funcs as this function arguments.
gwi.fatalParseError = false;
//ReturnedColumn* rc = buildAggFrmTempField(sfitempp[1], gwi);
ReturnedColumn* rc = buildReturnedColumn(sfitempp[1], gwi, nonSupport);
if (rc)
rhs = new ParseTree(rc);
}
}
else // where clause SZ: XXX: is it also HAVING clause??? it appears so judging from condition above.
{
if (isPredicateFunction(sfitempp[1], &gwi))
{
if (gwi.ptWorkStack.empty())
{
rhs = new ParseTree(buildReturnedColumn(sfitempp[1], gwi, nonSupport));
}
else
{
rhs = gwi.ptWorkStack.top();
gwi.ptWorkStack.pop();
}
}
else
{
if (gwi.rcWorkStack.empty())
{
rhs = new ParseTree(buildReturnedColumn(sfitempp[1], gwi, nonSupport));
}
else
{
rhs = new ParseTree(gwi.rcWorkStack.top());
gwi.rcWorkStack.pop();
}
}
if (isPredicateFunction(sfitempp[0], &gwi))
{
if (gwi.ptWorkStack.empty())
{
lhs = new ParseTree(buildReturnedColumn(sfitempp[0], gwi, nonSupport));
}
else
{
lhs = gwi.ptWorkStack.top();
gwi.ptWorkStack.pop();
}
}
else
{
if (gwi.rcWorkStack.empty())
{
lhs = new ParseTree(buildReturnedColumn(sfitempp[0], gwi, nonSupport));
}
else
{
lhs = new ParseTree(gwi.rcWorkStack.top());
gwi.rcWorkStack.pop();
}
}
}
if (nonSupport || !lhs->data() || !rhs->data())
{
gwi.fatalParseError = true;
if (gwi.parseErrorText.empty())
gwi.parseErrorText = "Un-recognized Arithmetic Operand";
delete lhs;
delete rhs;
return NULL;
}
// aop->operationType(lhs->resultType(), rhs->resultType());
pt->left(lhs);
pt->right(rhs);
}
else
{
ConstantColumn* cc = new ConstantColumn(string("0"), (int64_t)0);
cc->timeZone(gwi.timeZone);
if (gwi.clauseType == SELECT || gwi.clauseType == HAVING || gwi.clauseType == GROUP_BY) // select clause
{
rhs = new ParseTree(buildReturnedColumn(sfitempp[0], gwi, nonSupport));
}
else
{
if (gwi.rcWorkStack.empty())
{
rhs = new ParseTree(buildReturnedColumn(sfitempp[0], gwi, nonSupport));
}
else
{
rhs = new ParseTree(gwi.rcWorkStack.top());
gwi.rcWorkStack.pop();
}
}
if (nonSupport || !rhs->data())
{
gwi.fatalParseError = true;
if (gwi.parseErrorText.empty())
gwi.parseErrorText = "Un-recognized Arithmetic Operand";
delete rhs;
return NULL;
}
pt->left(cc);
pt->right(rhs);
}
// @bug5715. Use InfiniDB adjusted coltype for result type.
// decimal arithmetic operation gives double result when the session variable is set.
CalpontSystemCatalog::ColType mysqlType = colType_MysqlToIDB(item);
const CalpontSystemCatalog::ColType& leftColType = pt->left()->data()->resultType();
const CalpontSystemCatalog::ColType& rightColType = pt->right()->data()->resultType();
if (datatypes::isDecimal(leftColType.colDataType) || datatypes::isDecimal(rightColType.colDataType))
{
int32_t leftColWidth = leftColType.colWidth;
int32_t rightColWidth = rightColType.colWidth;
if ((leftColWidth == datatypes::MAXDECIMALWIDTH || rightColWidth == datatypes::MAXDECIMALWIDTH)
&& datatypes::isDecimal(mysqlType.colDataType))
{
mysqlType.colWidth = datatypes::MAXDECIMALWIDTH;
string funcName = item->func_name();
int32_t scale1 = leftColType.scale;
int32_t scale2 = rightColType.scale;
mysqlType.precision = datatypes::INT128MAXPRECISION;
if (funcName == "/" && (mysqlType.scale - (scale1 - scale2)) > datatypes::INT128MAXPRECISION)
{
Item_decimal* idp = (Item_decimal*)item;
unsigned int precision = idp->decimal_precision();
unsigned int scale = idp->decimal_scale();
mysqlType.setDecimalScalePrecisionHeuristic(precision, scale);
if (mysqlType.scale < scale1)
mysqlType.scale = scale1;
if (mysqlType.precision < mysqlType.scale)
mysqlType.precision = mysqlType.scale;
}
}
}
if (get_double_for_decimal_math(current_thd) == true)
aop->adjustResultType(mysqlType);
else
aop->resultType(mysqlType);
// adjust decimal result type according to internalDecimalScale
if (gwi.internalDecimalScale >= 0 && aop->resultType().colDataType == CalpontSystemCatalog::DECIMAL)
{
CalpontSystemCatalog::ColType ct = aop->resultType();
ct.scale = gwi.internalDecimalScale;
aop->resultType(ct);
}
aop->operationType(aop->resultType());
ac->expression(pt);
ac->resultType(aop->resultType());
ac->operationType(aop->operationType());
ac->expressionId(ci->expressionId++);
// @3391. optimization. try to associate expression ID to the expression on the select list
bool isOnSelectList = false;
if (gwi.clauseType != SELECT || gwi.havingDespiteSelect)
{
for (uint32_t i = 0; i < gwi.returnedCols.size(); i++)
{
if ((!ac->alias().empty()) &&
strcasecmp(ac->alias().c_str(), gwi.returnedCols[i]->alias().c_str()) == 0)
{
ac->expressionId(gwi.returnedCols[i]->expressionId());
isOnSelectList = true;
break;
}
}
}
// For function join. If any argument has non-zero joininfo, set it to the function.
ac->setSimpleColumnList();
std::vector<SimpleColumn*> simpleColList = ac->simpleColumnList();
for (uint i = 0; i < simpleColList.size(); i++)
{
if (simpleColList[i]->joinInfo() != 0)
{
ac->joinInfo(simpleColList[i]->joinInfo());
break;
}
}
if (isOnSelectList && gwi.havingDespiteSelect)
{
SimpleColumn* sc = new SimpleColumn(*ac);
delete ac;
return sc;
}
return ac;
}
ReturnedColumn* buildArithmeticColumn(Item_func* item, gp_walk_info& gwi, bool& nonSupport)
{
bool disableWrapping = gwi.disableWrapping;
gwi.disableWrapping = gwi.disableWrapping || itemDisablesWrapping(item, gwi);
ReturnedColumn* rc = buildArithmeticColumnBody(item, gwi, nonSupport);
gwi.disableWrapping = disableWrapping;
return rc;
}
ReturnedColumn* buildFunctionColumnBody(Item_func* ifp, gp_walk_info& gwi, bool& nonSupport, bool selectBetweenIn)
{
if (get_fe_conn_info_ptr() == NULL)
{
set_fe_conn_info_ptr((void*)new cal_connection_info());
thd_set_ha_data(current_thd, mcs_hton, get_fe_conn_info_ptr());
}
cal_connection_info* ci = static_cast<cal_connection_info*>(get_fe_conn_info_ptr());
string funcName = ifp->func_name();
if ( nullptr != dynamic_cast<Item_func_concat_operator_oracle*>(ifp))
{
// the condition above is the only way to recognize this particular case.
funcName = "concat_operator_oracle";
}
else
{
const Schema* funcSchema = ifp->schema();
if (funcSchema)
{
idbassert(funcSchema->name().str);
string funcSchemaName(funcSchema->name().str, funcSchema->name().length);
if (funcSchemaName == "oracle_schema")
{
// XXX: this is a shortcut.
funcName = funcName + "_oracle";
}
}
}
FuncExp* funcExp = FuncExp::instance();
Func* functor;
FunctionColumn* fc = NULL;
// Pseudocolumn
uint32_t pseudoType = PSEUDO_UNKNOWN;
if (ifp->functype() == Item_func::UDF_FUNC)
pseudoType = PseudoColumn::pseudoNameToType(funcName);
if (pseudoType != PSEUDO_UNKNOWN)
{
ReturnedColumn* rc = buildPseudoColumn(ifp, gwi, gwi.fatalParseError, pseudoType);
if (!rc || gwi.fatalParseError)
{
if (gwi.parseErrorText.empty())
gwi.parseErrorText = "Unsupported function.";
setError(gwi.thd, ER_CHECK_NOT_IMPLEMENTED, gwi.parseErrorText, gwi);
return NULL;
}
return rc;
}
// Item_equal is supported by buildPredicateItem()
if (funcName == "multiple equal")
return NULL;
// Arithmetic exp
if (funcName == "+" || funcName == "-" || funcName == "*" || funcName == "/")
{
return buildArithmeticColumn(ifp, gwi, nonSupport);
}
else if (funcName == "case")
{
fc = buildCaseFunction(ifp, gwi, nonSupport);
}
else if ((funcName == "charset" || funcName == "collation") && ifp->argument_count() == 1 &&
ifp->arguments()[0]->type() == Item::FIELD_ITEM)
{
Item_field* item = static_cast<Item_field*>(ifp->arguments()[0]);
CHARSET_INFO* info = item->charset_for_protocol();
ReturnedColumn* rc;
string val;
if (funcName == "charset")
{
val = info->cs_name.str;
}
else // collation
{
val = info->coll_name.str;
}
rc = new ConstantColumn(val, ConstantColumn::LITERAL);
return rc;
}
else if ((functor = funcExp->getFunctor(funcName)))
{
// where clause isnull still treated as predicate operator
// MCOL-686: between also a predicate operator so that extent elimination can happen
if ((funcName == "isnull" || funcName == "isnotnull" || funcName == "between") &&
(gwi.clauseType == WHERE || gwi.clauseType == HAVING))
return NULL;
if (funcName == "in" || funcName == " IN " || funcName == "between")
{
// if F&E involved, build function. otherwise, fall to the old path.
// @todo need more checks here
if (ifp->arguments()[0]->type() == Item::ROW_ITEM)
{
return NULL;
}
if (ifp->argument_count() > std::numeric_limits<uint16_t>::max())
{
nonSupport = true;
gwi.fatalParseError = true;
Message::Args args;
string info =
funcName + " with argument count > " + std::to_string(std::numeric_limits<uint16_t>::max());
args.add(info);
gwi.parseErrorText = IDBErrorInfo::instance()->errorMsg(ERR_NON_SUPPORTED_FUNCTION, args);
return NULL;
}
if (!selectBetweenIn && (ifp->arguments()[0]->type() == Item::FIELD_ITEM ||
(ifp->arguments()[0]->type() == Item::REF_ITEM &&
(*(((Item_ref*)ifp->arguments()[0])->ref))->type() == Item::FIELD_ITEM)))
{
bool fe = false;
for (uint32_t i = 1; i < ifp->argument_count(); i++)
{
if (!(ifp->arguments()[i]->type() == Item::NULL_ITEM ||
(ifp->arguments()[i]->type() == Item::CONST_ITEM &&
(ifp->arguments()[i]->cmp_type() == INT_RESULT ||
ifp->arguments()[i]->cmp_type() == STRING_RESULT ||
ifp->arguments()[i]->cmp_type() == REAL_RESULT ||
ifp->arguments()[i]->cmp_type() == DECIMAL_RESULT))))
{
if (ifp->arguments()[i]->type() == Item::FUNC_ITEM)
{
// try to identify const F&E. fall to primitive if parms are constant F&E.
vector<Item_field*> tmpVec;
uint16_t parseInfo = 0;
parse_item(ifp->arguments()[i], tmpVec, gwi.fatalParseError, parseInfo, &gwi);
if (!gwi.fatalParseError && !(parseInfo & AF_BIT) && tmpVec.size() == 0)
continue;
}
fe = true;
break;
}
}
if (!fe)
return NULL;
}
Item_func_opt_neg* inp = (Item_func_opt_neg*)ifp;
if (inp->negated)
funcName = "not" + funcName;
}
// @todo non-support function as argument. need to do post process. Assume all support for now
fc = new FunctionColumn();
fc->data(funcName);
FunctionParm funcParms;
SPTP sptp;
ClauseType clauseType = gwi.clauseType;
if (gwi.clauseType == SELECT ||
/*gwi.clauseType == HAVING || */ gwi.clauseType == GROUP_BY) // select clause
{
for (uint32_t i = 0; i < ifp->argument_count(); i++)
{
// group by clause try to see if the arguments are alias
if (gwi.clauseType == GROUP_BY && ifp->arguments()[i]->name.length)
{
uint32_t j = 0;
for (; j < gwi.returnedCols.size(); j++)
{
if (string(ifp->arguments()[i]->name.str) == gwi.returnedCols[j]->alias())
{
ReturnedColumn* rc = gwi.returnedCols[j]->clone();
rc->orderPos(j);
sptp.reset(new ParseTree(rc));
funcParms.push_back(sptp);
break;
}
}
if (j != gwi.returnedCols.size())
continue;
}
// special handling for function that takes a filter arguments, like if().
// @todo. merge this logic to buildParseTree().
if ((funcName == "if" && i == 0) || funcName == "xor")
{
// make sure the rcWorkStack is cleaned.
gwi.clauseType = WHERE;
sptp.reset(buildParseTree(ifp->arguments()[i], gwi, nonSupport));
gwi.clauseType = clauseType;
if (!sptp)
{
nonSupport = true;
delete fc;
return NULL;
}
funcParms.push_back(sptp);
continue;
}
// @bug 3039
// if (isPredicateFunction(ifp->arguments()[i], &gwi) || ifp->arguments()[i]->with_subquery())
if (ifp->arguments()[i]->with_subquery())
{
nonSupport = true;
gwi.fatalParseError = true;
gwi.parseErrorText = IDBErrorInfo::instance()->errorMsg(ERR_SUB_EXPRESSION);
delete fc;
return NULL;
}
ReturnedColumn* rc = NULL;
// Special treatment for json functions
// All boolean arguments will be parsed as boolean string true(false)
// E.g. the result of `SELECT JSON_ARRAY(true, false)` should be [true, false] instead of [1, 0]
bool mayHasBoolArg =
((funcName == "json_insert" || funcName == "json_replace" || funcName == "json_set" ||
funcName == "json_array_append" || funcName == "json_array_insert") &&
i != 0 && i % 2 == 0) ||
(funcName == "json_array") || (funcName == "json_object" && i % 2 == 1);
bool isBoolType =
(ifp->arguments()[i]->const_item() && ifp->arguments()[i]->type_handler()->is_bool_type());
if (mayHasBoolArg && isBoolType)
rc = buildBooleanConstantColumn(ifp->arguments()[i], gwi, nonSupport);
else
{
rc = buildReturnedColumn(ifp->arguments()[i], gwi, nonSupport);
}
// MCOL-1510 It must be a temp table field, so find the corresponding column.
if (!rc && ifp->arguments()[i]->type() == Item::REF_ITEM)
{
gwi.fatalParseError = false;
rc = buildAggFrmTempField(ifp->arguments()[i], gwi);
}
if (!rc || nonSupport)
{
nonSupport = true;
delete fc;
return NULL;
}
sptp.reset(new ParseTree(rc));
funcParms.push_back(sptp);
}
}
else // where clause
{
stack<SPTP> tmpPtStack;
for (int32_t i = ifp->argument_count() - 1; i >= 0; i--)
{
if (isPredicateFunction((ifp->arguments()[i]), &gwi) && !gwi.ptWorkStack.empty())
{
sptp.reset(gwi.ptWorkStack.top());
tmpPtStack.push(sptp);
gwi.ptWorkStack.pop();
}
else if (!isPredicateFunction((ifp->arguments()[i]), &gwi) && !gwi.rcWorkStack.empty())
{
sptp.reset(new ParseTree(gwi.rcWorkStack.top()));
tmpPtStack.push(sptp);
gwi.rcWorkStack.pop();
}
else
{
nonSupport = true;
delete fc;
return NULL;
}
}
while (!tmpPtStack.empty())
{
funcParms.push_back(tmpPtStack.top());
tmpPtStack.pop();
}
}
// the followings are special treatment of some functions
if (funcName == "week" && funcParms.size() == 1)
{
THD* thd = current_thd;
sptp.reset(
new ParseTree(new ConstantColumn(static_cast<uint64_t>(thd->variables.default_week_format))));
(dynamic_cast<ConstantColumn*>(sptp->data()))->timeZone(gwi.timeZone);
funcParms.push_back(sptp);
}
// add the keyword unit argument for interval function
if (funcName == "date_add_interval" || funcName == "extract" || funcName == "timestampdiff")
{
addIntervalArgs(&gwi, ifp, funcParms);
}
// add the keyword unit argument and char length for cast functions
if (funcName == "cast_as_char")
{
castCharArgs(&gwi, ifp, funcParms);
}
// add the length and scale arguments
if (funcName == "decimal_typecast")
{
castDecimalArgs(&gwi, ifp, funcParms);
}
// add the type argument
if (funcName == "get_format")
{
castTypeArgs(&gwi, ifp, funcParms);
}
// add my_time_zone
if (funcName == "unix_timestamp")
{
time_t tmp_t = 1;
struct tm tmp;
localtime_r(&tmp_t, &tmp);
sptp.reset(new ParseTree(new ConstantColumn(static_cast<int64_t>(tmp.tm_gmtoff), ConstantColumn::NUM)));
(dynamic_cast<ConstantColumn*>(sptp->data()))->timeZone(gwi.timeZone);
funcParms.push_back(sptp);
}
// add the default seed to rand function without arguments
if (funcName == "rand")
{
if (funcParms.size() == 0)
{
sptp.reset(new ParseTree(new ConstantColumn((int64_t)gwi.thd->rand.seed1, ConstantColumn::NUM)));
(dynamic_cast<ConstantColumn*>(sptp->data()))->timeZone(gwi.timeZone);
funcParms.push_back(sptp);
sptp.reset(new ParseTree(new ConstantColumn((int64_t)gwi.thd->rand.seed2, ConstantColumn::NUM)));
(dynamic_cast<ConstantColumn*>(sptp->data()))->timeZone(gwi.timeZone);
funcParms.push_back(sptp);
gwi.no_parm_func_list.push_back(fc);
}
else
{
ConstantColumn* cc = dynamic_cast<ConstantColumn*>(funcParms[0]->data());
if (cc)
gwi.no_parm_func_list.push_back(fc);
}
}
// Take the value of to/from TZ data and check if its a description or offset via
// my_tzinfo_find. Offset value will leave the corresponding tzinfo funcParms empty.
// while descriptions will lookup the time_zone_info structure and serialize for use
// in primproc func_convert_tz
if (funcName == "convert_tz")
{
messageqcpp::ByteStream bs;
string tzinfo;
SimpleColumn* scCheck;
// MCOL-XXXX There is no way currently to perform this lookup when the timezone description
// comes from another table of timezone descriptions.
// 1. Move proc code into plugin where it will have access to this table data
// 2. Create a library that primproc can use to access the time zone data tables.
// for now throw a message that this is not supported
scCheck = dynamic_cast<SimpleColumn*>(funcParms[1]->data());
if (scCheck)
{
gwi.fatalParseError = true;
gwi.parseErrorText = "convert_tz with parameter column_name in a Columnstore table";
setError(gwi.thd, ER_NOT_SUPPORTED_YET, gwi.parseErrorText, gwi);
return NULL;
}
scCheck = dynamic_cast<SimpleColumn*>(funcParms[2]->data());
if (scCheck)
{
gwi.fatalParseError = true;
gwi.parseErrorText = "convert_tz with parameter column_name in a Columnstore table";
setError(gwi.thd, ER_NOT_SUPPORTED_YET, gwi.parseErrorText, gwi);
return NULL;
}
dataconvert::TIME_ZONE_INFO* from_tzinfo = my_tzinfo_find(gwi.thd, ifp->arguments()[1]->val_str());
dataconvert::TIME_ZONE_INFO* to_tzinfo = my_tzinfo_find(gwi.thd, ifp->arguments()[2]->val_str());
if (from_tzinfo)
{
serializeTimezoneInfo(bs, from_tzinfo);
messageqcpp::BSSizeType length = bs.length();
uint8_t* buf = new uint8_t[length];
bs >> buf;
tzinfo = string((char*)buf, length);
}
sptp.reset(new ParseTree(new ConstantColumn(tzinfo)));
(dynamic_cast<ConstantColumn*>(sptp->data()))->timeZone(gwi.timeZone);
funcParms.push_back(sptp);
tzinfo.clear();
if (to_tzinfo)
{
serializeTimezoneInfo(bs, to_tzinfo);
messageqcpp::BSSizeType length = bs.length();
uint8_t* buf = new uint8_t[length];
bs >> buf;
tzinfo = string((char*)buf, length);
}
sptp.reset(new ParseTree(new ConstantColumn(tzinfo)));
(dynamic_cast<ConstantColumn*>(sptp->data()))->timeZone(gwi.timeZone);
funcParms.push_back(sptp);
tzinfo.clear();
}
if (funcName == "sysdate")
{
gwi.no_parm_func_list.push_back(fc);
}
// func name is addtime/subtime in 10.3.9
// note: this means get_time() can now go away in our server fork
if ((funcName == "addtime") || (funcName == "subtime"))
{
int64_t sign = 1;
if (funcName == "subtime")
{
sign = -1;
}
sptp.reset(new ParseTree(new ConstantColumn(sign)));
(dynamic_cast<ConstantColumn*>(sptp->data()))->timeZone(gwi.timeZone);
funcParms.push_back(sptp);
}
fc->functionName(funcName);
fc->functionParms(funcParms);
fc->resultType(colType_MysqlToIDB(ifp));
// if the result type is DECIMAL and any function parameter is a wide decimal
// column, set the result colwidth to wide
if (fc->resultType().colDataType == CalpontSystemCatalog::DECIMAL)
{
for (size_t i = 0; i < funcParms.size(); i++)
{
if (funcParms[i]->data()->resultType().isWideDecimalType())
{
fc->resultType().colWidth = datatypes::MAXDECIMALWIDTH;
break;
}
}
}
// MySQL give string result type for date function, but has the flag set.
// we should set the result type to be datetime for comparision.
if (ifp->field_type() == MYSQL_TYPE_DATETIME || ifp->field_type() == MYSQL_TYPE_DATETIME2)
{
CalpontSystemCatalog::ColType ct;
ct.colDataType = CalpontSystemCatalog::DATETIME;
ct.colWidth = 8;
fc->resultType(ct);
}
if (ifp->field_type() == MYSQL_TYPE_TIMESTAMP || ifp->field_type() == MYSQL_TYPE_TIMESTAMP2)
{
CalpontSystemCatalog::ColType ct;
ct.colDataType = CalpontSystemCatalog::TIMESTAMP;
ct.colWidth = 8;
fc->resultType(ct);
}
else if (ifp->field_type() == MYSQL_TYPE_DATE)
{
CalpontSystemCatalog::ColType ct;
ct.colDataType = CalpontSystemCatalog::DATE;
ct.colWidth = 4;
fc->resultType(ct);
}
else if (ifp->field_type() == MYSQL_TYPE_TIME)
{
CalpontSystemCatalog::ColType ct;
ct.colDataType = CalpontSystemCatalog::TIME;
ct.colWidth = 8;
fc->resultType(ct);
}
#if 0
if (is_temporal_type_with_date(ifp->field_type()))
{
CalpontSystemCatalog::ColType ct;
ct.colDataType = CalpontSystemCatalog::DATETIME;
ct.colWidth = 8;
fc->resultType(ct);
}
if (funcName == "cast_as_date")
{
CalpontSystemCatalog::ColType ct;
ct.colDataType = CalpontSystemCatalog::DATE;
ct.colWidth = 4;
fc->resultType(ct);
}
#endif
execplan::CalpontSystemCatalog::ColType& resultType = fc->resultType();
resultType.setTimeZone(gwi.timeZone);
fc->operationType(functor->operationType(funcParms, resultType));
// For floor/ceiling/truncate/round functions applied on TIMESTAMP columns, set the
// function result type to TIMESTAMP
if ((funcName == "floor" || funcName == "ceiling" || funcName == "truncate" || funcName == "round") &&
fc->operationType().colDataType == CalpontSystemCatalog::TIMESTAMP)
{
CalpontSystemCatalog::ColType ct = fc->resultType();
ct.colDataType = CalpontSystemCatalog::TIMESTAMP;
ct.colWidth = 8;
fc->resultType(ct);
}
fc->expressionId(ci->expressionId++);
// A few functions use a different collation than that found in
// the base ifp class
if (funcName == "locate" || funcName == "find_in_set" || funcName == "strcmp" || funcName == "regexp_instr")
{
DTCollation dt;
ifp->Type_std_attributes::agg_arg_charsets_for_comparison(dt, ifp->func_name_cstring(),
ifp->arguments(), 1, 1);
fc->charsetNumber(dt.collation->number);
}
else
{
fc->charsetNumber(ifp->collation.collation->number);
}
}
else if (ifp->type() == Item::COND_ITEM || ifp->functype() == Item_func::EQ_FUNC ||
ifp->functype() == Item_func::NE_FUNC || ifp->functype() == Item_func::LT_FUNC ||
ifp->functype() == Item_func::LE_FUNC || ifp->functype() == Item_func::GE_FUNC ||
ifp->functype() == Item_func::GT_FUNC || ifp->functype() == Item_func::LIKE_FUNC ||
ifp->functype() == Item_func::BETWEEN || ifp->functype() == Item_func::IN_FUNC ||
ifp->functype() == Item_func::ISNULL_FUNC || ifp->functype() == Item_func::ISNOTNULL_FUNC ||
ifp->functype() == Item_func::NOT_FUNC || ifp->functype() == Item_func::EQUAL_FUNC)
{
return NULL;
}
else
{
nonSupport = true;
gwi.fatalParseError = true;
Message::Args args;
args.add(funcName);
gwi.parseErrorText = IDBErrorInfo::instance()->errorMsg(ERR_NON_SUPPORTED_FUNCTION, args);
return NULL;
}
// adjust decimal result type according to internalDecimalScale
if (!fc)
return NULL;
if (gwi.internalDecimalScale >= 0 && fc->resultType().colDataType == CalpontSystemCatalog::DECIMAL)
{
CalpontSystemCatalog::ColType ct = fc->resultType();
ct.scale = gwi.internalDecimalScale;
fc->resultType(ct);
}
if (ifp->name.length)
fc->alias(ifp->name.str);
// @3391. optimization. try to associate expression ID to the expression on the select list
if (gwi.clauseType != SELECT)
{
for (uint32_t i = 0; i < gwi.returnedCols.size(); i++)
{
if ((!fc->alias().empty()) &&
strcasecmp(fc->alias().c_str(), gwi.returnedCols[i]->alias().c_str()) == 0)
fc->expressionId(gwi.returnedCols[i]->expressionId());
}
}
// For function join. If any argument has non-zero joininfo, set it to the function.
fc->setSimpleColumnList();
std::vector<SimpleColumn*> simpleColList = fc->simpleColumnList();
for (uint i = 0; i < simpleColList.size(); i++)
{
if (simpleColList[i]->joinInfo() != 0)
{
fc->joinInfo(simpleColList[i]->joinInfo());
break;
}
}
fc->timeZone(gwi.timeZone);
return fc;
}
ReturnedColumn* buildFunctionColumn(Item_func* ifp, gp_walk_info& gwi, bool& nonSupport, bool selectBetweenIn)
{
bool disableWrapping = gwi.disableWrapping;
gwi.disableWrapping = gwi.disableWrapping || itemDisablesWrapping(ifp, gwi);
ReturnedColumn* rc = buildFunctionColumnBody(ifp, gwi, nonSupport, selectBetweenIn);
gwi.disableWrapping = disableWrapping;
return rc;
}
FunctionColumn* buildCaseFunction(Item_func* item, gp_walk_info& gwi, bool& nonSupport)
{
if (get_fe_conn_info_ptr() == NULL)
{
set_fe_conn_info_ptr((void*)new cal_connection_info());
thd_set_ha_data(current_thd, mcs_hton, get_fe_conn_info_ptr());
}
cal_connection_info* ci = static_cast<cal_connection_info*>(get_fe_conn_info_ptr());
FunctionColumn* fc = new FunctionColumn();
FunctionParm funcParms;
SPTP sptp;
stack<SPTP> tmpPtStack;
FuncExp* funcexp = FuncExp::instance();
string funcName = "case_simple";
if (item->functype() == Item_func::CASE_SEARCHED_FUNC)
{
funcName = "case_searched";
}
/* if (dynamic_cast<Item_func_case_searched*>(item))
{
funcName = "case_searched";
}*/
funcParms.reserve(item->argument_count());
// so buildXXXcolumn function will not pop stack.
ClauseType realClauseType = gwi.clauseType;
gwi.clauseType = SELECT;
// We ought to be able to just build from the stack, and would
// be able to if there were any way to know which stack had the
// next case item. Unfortunately, parameters may have been pushed
// onto the ptWorkStack or rcWorkStack or neither, depending on type
// and position. We can't tell which at this point, so we
// rebuild the item from the arguments directly and then try to
// figure what to pop, if anything, in order to sync the stacks.
//
// MCOL-1341 - With MariaDB 10.2.14 onwards CASE is now in the order:
// [case,]when1,when2,...,then1,then2,...[,else]
// See server commit bf1ca14ff3f3faa9f7a018097b25aa0f66d068cd for more
// information.
int32_t arg_offset = 0;
if ((item->argument_count() - 1) % 2)
{
arg_offset = (item->argument_count() - 1) / 2;
}
else
{
arg_offset = item->argument_count() / 2;
}
for (int32_t i = item->argument_count() - 1; i >= 0; i--)
{
// For case_searched, we know the items for the WHEN clause will
// not be ReturnedColumns. We do this separately just to save
// some cpu cycles trying to build a ReturnedColumn as below.
// Every even numbered arg is a WHEN. In between are the THEN.
// An odd number of args indicates an ELSE residing in the last spot.
if ((item->functype() == Item_func::CASE_SEARCHED_FUNC) && (i < arg_offset))
{
// MCOL-1472 Nested CASE with an ISNULL predicate. We don't want the predicate
// to pull off of rcWorkStack, so we set this inCaseStmt flag to tell it
// not to.
gwi.inCaseStmt = true;
sptp.reset(buildParseTree(item->arguments()[i], gwi, nonSupport));
gwi.inCaseStmt = false;
if (!gwi.ptWorkStack.empty() && *gwi.ptWorkStack.top() == *sptp.get())
{
delete gwi.ptWorkStack.top();
gwi.ptWorkStack.pop();
}
}
else
{
// First try building a ReturnedColumn. It may or may not succeed
// depending on the types involved. There's also little correlation
// between buildReturnedColumn and the existance of the item on
// rwWorkStack or ptWorkStack.
// For example, simple predicates, such as 1=1 or 1=0, land in the
// ptWorkStack but other stuff might land in the rwWorkStack
ReturnedColumn* parm = buildReturnedColumn(item->arguments()[i], gwi, nonSupport);
if (parm)
{
sptp.reset(new ParseTree(parm));
// We need to pop whichever stack is holding it, if any.
if ((!gwi.rcWorkStack.empty()) && *gwi.rcWorkStack.top() == parm)
{
delete gwi.rcWorkStack.top();
gwi.rcWorkStack.pop();
}
else if (!gwi.ptWorkStack.empty())
{
ReturnedColumn* ptrc = dynamic_cast<ReturnedColumn*>(gwi.ptWorkStack.top()->data());
if (ptrc && *ptrc == *parm)
{
delete gwi.ptWorkStack.top();
gwi.ptWorkStack.pop();
}
}
}
else
{
sptp.reset(buildParseTree(item->arguments()[i], gwi, nonSupport));
// We need to pop whichever stack is holding it, if any.
if ((!gwi.ptWorkStack.empty()) && *gwi.ptWorkStack.top()->data() == sptp->data())
{
delete gwi.ptWorkStack.top();
gwi.ptWorkStack.pop();
}
else if (!gwi.rcWorkStack.empty())
{
// Probably won't happen, but it might have been on the
// rcWorkStack all along.
ReturnedColumn* ptrc = dynamic_cast<ReturnedColumn*>(sptp->data());
if (ptrc && *ptrc == *gwi.rcWorkStack.top())
{
delete gwi.rcWorkStack.top();
gwi.rcWorkStack.pop();
}
}
}
}
funcParms.insert(funcParms.begin(), sptp);
}
// recover clause type
gwi.clauseType = realClauseType;
if (gwi.fatalParseError)
{
setError(gwi.thd, ER_CHECK_NOT_IMPLEMENTED, gwi.parseErrorText, gwi);
return NULL;
}
Func* functor = funcexp->getFunctor(funcName);
fc->resultType(colType_MysqlToIDB(item));
execplan::CalpontSystemCatalog::ColType& resultType = fc->resultType();
resultType.setTimeZone(gwi.timeZone);
fc->operationType(functor->operationType(funcParms, resultType));
fc->functionName(funcName);
fc->functionParms(funcParms);
fc->expressionId(ci->expressionId++);
fc->timeZone(gwi.timeZone);
// For function join. If any argument has non-zero joininfo, set it to the function.
fc->setSimpleColumnList();
std::vector<SimpleColumn*> simpleColList = fc->simpleColumnList();
for (uint i = 0; i < simpleColList.size(); i++)
{
if (simpleColList[i]->joinInfo() != 0)
{
fc->joinInfo(simpleColList[i]->joinInfo());
break;
}
}
return fc;
}
ConstantColumn* buildDecimalColumn(const Item* idp, const std::string& valStr, gp_walk_info& gwi)
{
IDB_Decimal columnstore_decimal;
ostringstream columnstore_decimal_val;
uint32_t i = 0;
if (valStr[0] == '+' || valStr[0] == '-')
{
columnstore_decimal_val << valStr[0];
i = 1;
}
bool specialPrecision = false;
// handle the case when the constant value is 0.12345678901234567890123456789012345678
// In this case idp->decimal_precision() = 39, but we can
if (((i + 1) < valStr.length()) && valStr[i] == '0' && valStr[i + 1] == '.')
specialPrecision = true;
for (; i < valStr.length(); i++)
{
if (valStr[i] == '.')
continue;
columnstore_decimal_val << valStr[i];
}
if (idp->decimal_precision() <= datatypes::INT64MAXPRECISION)
columnstore_decimal.value = strtoll(columnstore_decimal_val.str().c_str(), 0, 10);
else if (idp->decimal_precision() <= datatypes::INT128MAXPRECISION ||
(idp->decimal_precision() <= datatypes::INT128MAXPRECISION + 1 && specialPrecision))
{
bool dummy = false;
columnstore_decimal.s128Value = dataconvert::strtoll128(columnstore_decimal_val.str().c_str(), dummy, 0);
}
// TODO MCOL-641 Add support here
if (gwi.internalDecimalScale >= 0 && idp->decimals > (uint)gwi.internalDecimalScale)
{
columnstore_decimal.scale = gwi.internalDecimalScale;
uint32_t diff = (uint32_t)(idp->decimals - gwi.internalDecimalScale);
columnstore_decimal.value = columnstore_decimal.TDecimal64::toSInt64Round(diff);
}
else
columnstore_decimal.scale = idp->decimal_scale();
columnstore_decimal.precision = (idp->decimal_precision() > datatypes::INT128MAXPRECISION)
? datatypes::INT128MAXPRECISION
: idp->decimal_precision();
ConstantColumn* cc = new ConstantColumn(valStr, columnstore_decimal);
cc->charsetNumber(idp->collation.collation->number);
return cc;
}
SimpleColumn* buildSimpleColumn(Item_field* ifp, gp_walk_info& gwi)
{
if (!gwi.csc)
{
gwi.csc = CalpontSystemCatalog::makeCalpontSystemCatalog(gwi.sessionid);
gwi.csc->identity(CalpontSystemCatalog::FE);
}
bool isInformationSchema = false;
// @bug5523
if (ifp->cached_table && ifp->cached_table->db.length > 0 &&
strcmp(ifp->cached_table->db.str, "information_schema") == 0)
isInformationSchema = true;
// support FRPM subquery. columns from the derived table has no definition
if ((!ifp->field || !ifp->db_name.str || strlen(ifp->db_name.str) == 0) && !isInformationSchema)
return buildSimpleColFromDerivedTable(gwi, ifp);
CalpontSystemCatalog::ColType ct;
datatypes::SimpleColumnParam prm(gwi.sessionid, true);
try
{
// check foreign engine
if (ifp->cached_table && ifp->cached_table->table)
prm.columnStore(isMCSTable(ifp->cached_table->table));
// @bug4509. ifp->cached_table could be null for myisam sometimes
else if (ifp->field && ifp->field->table)
prm.columnStore(isMCSTable(ifp->field->table));
if (prm.columnStore())
{
ct = gwi.csc->colType(
gwi.csc->lookupOID(make_tcn(ifp->db_name.str, bestTableName(ifp), ifp->field_name.str)));
}
else
{
ct = colType_MysqlToIDB(ifp);
}
}
catch (std::exception& ex)
{
gwi.fatalParseError = true;
gwi.parseErrorText = ex.what();
return NULL;
}
const datatypes::DatabaseQualifiedColumnName name(ifp->db_name.str, bestTableName(ifp),
ifp->field_name.str);
const datatypes::TypeHandler* h = ct.typeHandler();
SimpleColumn* sc = h->newSimpleColumn(name, ct, prm);
sc->resultType(ct);
sc->charsetNumber(ifp->collation.collation->number);
string tbname(ifp->table_name.str);
if (isInformationSchema)
{
sc->schemaName("information_schema");
sc->tableName(tbname, lower_case_table_names);
}
sc->tableAlias(tbname, lower_case_table_names);
// view name
sc->viewName(getViewName(ifp->cached_table), lower_case_table_names);
sc->alias(ifp->name.str);
sc->isColumnStore(prm.columnStore());
sc->timeZone(gwi.timeZone);
if (!prm.columnStore() && ifp->field)
sc->oid(ifp->field->field_index + 1); // ExeMgr requires offset started from 1
if (ifp->depended_from)
{
sc->joinInfo(sc->joinInfo() | JOIN_CORRELATED);
if (gwi.subQuery)
gwi.subQuery->correlated(true);
// for error out non-support select filter case (comparison outside semi join tables)
gwi.correlatedTbNameVec.push_back(make_aliastable(sc->schemaName(), sc->tableName(), sc->tableAlias()));
// imply semi for scalar for now.
if (gwi.subSelectType == CalpontSelectExecutionPlan::SINGLEROW_SUBS)
sc->joinInfo(sc->joinInfo() | JOIN_SCALAR | JOIN_SEMI);
if (gwi.subSelectType == CalpontSelectExecutionPlan::SELECT_SUBS)
sc->joinInfo(sc->joinInfo() | JOIN_SCALAR | JOIN_OUTER_SELECT);
}
return sc;
}
ParseTree* buildParseTree(Item* item, gp_walk_info& gwi, bool& nonSupport)
{
ParseTree* pt = 0;
#ifdef DEBUG_WALK_COND
// debug
cerr << "Build Parsetree: " << endl;
item->traverse_cond(debug_walk, &gwi, Item::POSTFIX);
#endif
//@bug5044. PPSTFIX walking should always be treated as WHERE clause filter
ClauseType clauseType = gwi.clauseType;
gwi.clauseType = WHERE;
item->traverse_cond(gp_walk, &gwi, Item::POSTFIX);
gwi.clauseType = clauseType;
if (gwi.fatalParseError)
return NULL;
// bug 2840. if the filter/function is constant, result is in rcWorkStack
if (!gwi.ptWorkStack.empty())
{
pt = gwi.ptWorkStack.top();
gwi.ptWorkStack.pop();
}
else if (!gwi.rcWorkStack.empty())
{
pt = new ParseTree(gwi.rcWorkStack.top());
gwi.rcWorkStack.pop();
}
return pt;
}
class ConstArgParam
{
public:
unsigned int precision;
unsigned int scale;
bool bIsConst;
bool hasDecimalConst;
ConstArgParam() : precision(0), scale(0), bIsConst(false), hasDecimalConst(false)
{
}
};
static bool isSupportedAggregateWithOneConstArg(const Item_sum* item, Item** orig_args)
{
if (item->argument_count() != 1 || !orig_args[0]->const_item())
return false;
switch (orig_args[0]->cmp_type())
{
case INT_RESULT:
case STRING_RESULT:
case REAL_RESULT:
case DECIMAL_RESULT: return true;
default: break;
}
return false;
}
static void processAggregateColumnConstArg(gp_walk_info& gwi, SRCP& parm, AggregateColumn* ac, Item* sfitemp,
ConstArgParam& constParam)
{
DBUG_ASSERT(sfitemp->const_item());
switch (sfitemp->cmp_type())
{
case INT_RESULT:
case STRING_RESULT:
case REAL_RESULT:
case DECIMAL_RESULT:
{
ReturnedColumn* rt = buildReturnedColumn(sfitemp, gwi, gwi.fatalParseError);
if (!rt)
{
gwi.fatalParseError = true;
return;
}
ConstantColumn* cc;
if ((cc = dynamic_cast<ConstantColumn*>(rt)) && cc->isNull())
{
// Explicit NULL or a const function that evaluated to NULL
cc = new ConstantColumnNull();
cc->timeZone(gwi.timeZone);
parm.reset(cc);
ac->constCol(SRCP(rt));
return;
}
// treat as count(*)
if (ac->aggOp() == AggregateColumn::COUNT)
ac->aggOp(AggregateColumn::COUNT_ASTERISK);
parm.reset(rt);
ac->constCol(parm);
constParam.bIsConst = true;
if (sfitemp->cmp_type() == DECIMAL_RESULT)
{
constParam.hasDecimalConst = true;
constParam.precision = sfitemp->decimal_precision();
constParam.scale = sfitemp->decimal_scale();
}
break;
}
case TIME_RESULT:
// QQ: why temporal constants are not handled?
case ROW_RESULT:
{
gwi.fatalParseError = true;
}
}
}
void analyzeForImplicitGroupBy(Item* item, gp_walk_info& gwi)
{
if (gwi.implicitExplicitGroupBy)
{
return;
}
while (item->type() == Item::REF_ITEM)
{
Item_ref* ref = static_cast<Item_ref*>(item);
item = *ref->ref;
}
if (item->type() == Item::SUM_FUNC_ITEM)
{
// definitely an aggregate and thus needs an implicit group by.
gwi.implicitExplicitGroupBy = true;
return;
}
if (item->type() == Item::FUNC_ITEM)
{
Item_func* ifp = static_cast<Item_func*>(item);
for(uint32_t i = 0;i<ifp->argument_count() && !gwi.implicitExplicitGroupBy;i++)
{
analyzeForImplicitGroupBy(ifp->arguments()[i], gwi);
}
}
}
ReturnedColumn* buildAggregateColumnBody(Item* item, gp_walk_info& gwi)
{
// MCOL-1201 For UDAnF multiple parameters
vector<SRCP> selCols;
vector<SRCP> orderCols;
ConstArgParam constArgParam;
if (get_fe_conn_info_ptr() == NULL)
{
set_fe_conn_info_ptr((void*)new cal_connection_info());
thd_set_ha_data(current_thd, mcs_hton, get_fe_conn_info_ptr());
}
cal_connection_info* ci = static_cast<cal_connection_info*>(get_fe_conn_info_ptr());
Item_sum* isp = static_cast<Item_sum*>(item);
Item** sfitempp = isp->get_orig_args();
SRCP parm;
// @bug4756
if (gwi.clauseType == SELECT)
gwi.aggOnSelect = true;
// Argument_count() is the # of formal parms to the agg fcn. Columnstore
// only supports 1 argument except UDAnF, COUNT(DISTINC), GROUP_CONCAT and JSON_ARRAYAGG
if (isp->argument_count() != 1 && isp->sum_func() != Item_sum::COUNT_DISTINCT_FUNC &&
isp->sum_func() != Item_sum::GROUP_CONCAT_FUNC && isp->sum_func() != Item_sum::UDF_SUM_FUNC &&
isp->sum_func() != Item_sum::JSON_ARRAYAGG_FUNC)
{
gwi.fatalParseError = true;
gwi.parseErrorText = IDBErrorInfo::instance()->errorMsg(ERR_MUL_ARG_AGG);
return NULL;
}
AggregateColumn* ac = NULL;
if (isp->sum_func() == Item_sum::GROUP_CONCAT_FUNC)
{
ac = new GroupConcatColumn(gwi.sessionid);
}
else if (isp->sum_func() == Item_sum::JSON_ARRAYAGG_FUNC)
{
ac = new JsonArrayAggColumn(gwi.sessionid);
}
else if (isp->sum_func() == Item_sum::UDF_SUM_FUNC)
{
ac = new UDAFColumn(gwi.sessionid);
}
else
{
ac = new AggregateColumn(gwi.sessionid);
}
ac->timeZone(gwi.timeZone);
if (isp->name.length)
ac->alias(isp->name.str);
if ((setAggOp(ac, isp)))
{
gwi.fatalParseError = true;
gwi.parseErrorText = "Non supported aggregate type on the select clause";
if (ac)
delete ac;
return NULL;
}
try
{
// special parsing for group_concat
if (isp->sum_func() == Item_sum::GROUP_CONCAT_FUNC)
{
Item_func_group_concat* gc = (Item_func_group_concat*)isp;
vector<SRCP> orderCols;
RowColumn* rowCol = new RowColumn();
vector<SRCP> selCols;
uint32_t select_ctn = gc->get_count_field();
ReturnedColumn* rc = NULL;
for (uint32_t i = 0; i < select_ctn; i++)
{
rc = buildReturnedColumn(sfitempp[i], gwi, gwi.fatalParseError);
if (!rc || gwi.fatalParseError)
{
if (ac)
delete ac;
return NULL;
}
selCols.push_back(SRCP(rc));
}
ORDER **order_item, **end;
for (order_item = gc->get_order(), end = order_item + gc->get_order_field(); order_item < end;
order_item++)
{
Item* ord_col = *(*order_item)->item;
if (ord_col->type() == Item::CONST_ITEM && ord_col->cmp_type() == INT_RESULT)
{
Item_int* id = (Item_int*)ord_col;
int64_t index = id->val_int();
if (index > (int)selCols.size() || index < 1)
{
gwi.fatalParseError = true;
if (ac)
delete ac;
return NULL;
}
rc = selCols[index - 1]->clone();
rc->orderPos(index - 1);
}
else
{
rc = buildReturnedColumn(ord_col, gwi, gwi.fatalParseError);
if (!rc || gwi.fatalParseError)
{
if (ac)
delete ac;
return NULL;
}
}
// 10.2 TODO: direction is now a tri-state flag
rc->asc((*order_item)->direction == ORDER::ORDER_ASC ? true : false);
orderCols.push_back(SRCP(rc));
}
rowCol->columnVec(selCols);
(dynamic_cast<GroupConcatColumn*>(ac))->orderCols(orderCols);
parm.reset(rowCol);
ac->aggParms().push_back(parm);
if (gc->get_separator())
{
string separator;
separator.assign(gc->get_separator()->ptr(), gc->get_separator()->length());
(dynamic_cast<GroupConcatColumn*>(ac))->separator(separator);
}
}
else if (isp->sum_func() == Item_sum::JSON_ARRAYAGG_FUNC)
{
Item_func_json_arrayagg* gc = (Item_func_json_arrayagg*)isp;
vector<SRCP> orderCols;
RowColumn* rowCol = new RowColumn();
vector<SRCP> selCols;
uint32_t select_ctn = gc->get_count_field();
ReturnedColumn* rc = NULL;
for (uint32_t i = 0; i < select_ctn; i++)
{
rc = buildReturnedColumn(sfitempp[i], gwi, gwi.fatalParseError);
if (!rc || gwi.fatalParseError)
{
if (ac)
delete ac;
return NULL;
}
selCols.push_back(SRCP(rc));
}
ORDER **order_item, **end;
for (order_item = gc->get_order(), end = order_item + gc->get_order_field(); order_item < end;
order_item++)
{
Item* ord_col = *(*order_item)->item;
if (ord_col->type() == Item::CONST_ITEM && ord_col->cmp_type() == INT_RESULT)
{
Item_int* id = (Item_int*)ord_col;
if (id->val_int() > (int)selCols.size())
{
gwi.fatalParseError = true;
if (ac)
delete ac;
return NULL;
}
rc = selCols[id->val_int() - 1]->clone();
rc->orderPos(id->val_int() - 1);
}
else
{
rc = buildReturnedColumn(ord_col, gwi, gwi.fatalParseError);
if (!rc || gwi.fatalParseError)
{
if (ac)
delete ac;
return NULL;
}
}
// 10.2 TODO: direction is now a tri-state flag
rc->asc((*order_item)->direction == ORDER::ORDER_ASC ? true : false);
orderCols.push_back(SRCP(rc));
}
rowCol->columnVec(selCols);
(dynamic_cast<JsonArrayAggColumn*>(ac))->orderCols(orderCols);
parm.reset(rowCol);
ac->aggParms().push_back(parm);
if (gc->get_separator())
{
string separator;
separator.assign(gc->get_separator()->ptr(), gc->get_separator()->length());
(dynamic_cast<JsonArrayAggColumn*>(ac))->separator(separator);
}
}
else if (isSupportedAggregateWithOneConstArg(isp, sfitempp))
{
processAggregateColumnConstArg(gwi, parm, ac, sfitempp[0], constArgParam);
}
else
{
for (uint32_t i = 0; i < isp->argument_count(); i++)
{
Item* sfitemp = sfitempp[i];
Item::Type sfitype = sfitemp->type();
switch (sfitype)
{
case Item::FIELD_ITEM:
{
Item_field* ifp = static_cast<Item_field*>(sfitemp);
SimpleColumn* sc = buildSimpleColumn(ifp, gwi);
if (!sc)
{
gwi.fatalParseError = true;
break;
}
parm.reset(sc);
gwi.columnMap.insert(
CalpontSelectExecutionPlan::ColumnMap::value_type(string(ifp->field_name.str), parm));
TABLE_LIST* tmp = (ifp->cached_table ? ifp->cached_table : 0);
gwi.tableMap[make_aliastable(sc->schemaName(), sc->tableName(), sc->tableAlias(),
sc->isColumnStore())] = make_pair(1, tmp);
break;
}
case Item::CONST_ITEM:
case Item::NULL_ITEM:
{
processAggregateColumnConstArg(gwi, parm, ac, sfitemp, constArgParam);
break;
}
case Item::FUNC_ITEM:
{
Item_func* ifp = (Item_func*)sfitemp;
ReturnedColumn* rc = 0;
// check count(1+1) case
vector<Item_field*> tmpVec;
uint16_t parseInfo = 0;
parse_item(ifp, tmpVec, gwi.fatalParseError, parseInfo, &gwi);
if (parseInfo & SUB_BIT)
{
gwi.fatalParseError = true;
break;
}
else if (!gwi.fatalParseError && !(parseInfo & AGG_BIT) && !(parseInfo & AF_BIT) &&
tmpVec.size() == 0)
{
rc = buildFunctionColumn(ifp, gwi, gwi.fatalParseError);
FunctionColumn* fc = dynamic_cast<FunctionColumn*>(rc);
if ((fc && fc->functionParms().empty()) || !fc)
{
ReturnedColumn* rc = buildReturnedColumn(sfitemp, gwi, gwi.fatalParseError);
if (dynamic_cast<ConstantColumn*>(rc))
{
//@bug5229. handle constant function on aggregate argument
ac->constCol(SRCP(rc));
// XXX: this skips restoration of clauseType.
break;
}
// the "rc" can be in gwi.no_parm_func_list. erase it from that list and
// then delete it.
// kludge, I know.
uint32_t i;
for (i = 0; gwi.no_parm_func_list[i] != rc && i < gwi.no_parm_func_list.size(); i++) { }
if (i < gwi.no_parm_func_list.size())
{
gwi.no_parm_func_list.erase(gwi.no_parm_func_list.begin() + i);
delete rc;
}
}
}
// MySQL carelessly allows correlated aggregate function on the WHERE clause.
// Here is the work around to deal with that inconsistence.
// e.g., SELECT (SELECT t.c FROM t1 AS t WHERE t.b=MAX(t1.b + 0)) FROM t1;
ClauseType clauseType = gwi.clauseType;
if (gwi.clauseType == WHERE)
gwi.clauseType = HAVING;
// @bug 3603. for cases like max(rand()). try to build function first.
if (!rc)
{
rc = buildFunctionColumn(ifp, gwi, gwi.fatalParseError);
}
if (!rc)
{
gwi.fatalParseError = true;
}
parm.reset(rc);
gwi.clauseType = clauseType;
break;
}
case Item::REF_ITEM:
{
ReturnedColumn* rc = buildReturnedColumn(sfitemp, gwi, gwi.fatalParseError);
if (rc)
{
parm.reset(rc);
break;
}
}
/* fall through */
default:
{
gwi.fatalParseError = true;
}
}
if (gwi.fatalParseError)
{
if (gwi.parseErrorText.empty())
{
Message::Args args;
if (item->name.length)
args.add(item->name.str);
else
args.add("");
gwi.parseErrorText = IDBErrorInfo::instance()->errorMsg(ERR_NON_SUPPORT_AGG_ARGS, args);
}
if (ac)
delete ac;
return NULL;
}
if (parm)
{
// MCOL-1201 multi-argument aggregate
ac->aggParms().push_back(parm);
}
}
}
bool isAvg = (isp->sum_func() == Item_sum::AVG_FUNC || isp->sum_func() == Item_sum::AVG_DISTINCT_FUNC);
// Get result type
// Modified for MCOL-1201 multi-argument aggregate
if (!constArgParam.bIsConst && ac->aggParms().size() > 0)
{
// These are all one parm functions, so we can safely
// use the first parm for result type.
parm = ac->aggParms()[0];
if (isAvg || isp->sum_func() == Item_sum::SUM_FUNC || isp->sum_func() == Item_sum::SUM_DISTINCT_FUNC)
{
CalpontSystemCatalog::ColType ct = parm->resultType();
if (ct.isWideDecimalType())
{
uint32_t precision = ct.precision;
uint32_t scale = ct.scale;
if (isAvg)
{
datatypes::Decimal::setScalePrecision4Avg(precision, scale);
}
ct.precision = precision;
ct.scale = scale;
}
else if (datatypes::hasUnderlyingWideDecimalForSumAndAvg(ct.colDataType))
{
uint32_t precision = datatypes::INT128MAXPRECISION;
uint32_t scale = ct.scale;
ct.colDataType = CalpontSystemCatalog::DECIMAL;
ct.colWidth = datatypes::MAXDECIMALWIDTH;
if (isAvg)
{
datatypes::Decimal::setScalePrecision4Avg(precision, scale);
}
ct.scale = scale;
ct.precision = precision;
}
else
{
ct.colDataType = CalpontSystemCatalog::LONGDOUBLE;
ct.colWidth = sizeof(long double);
if (isAvg)
{
ct.scale += datatypes::MAXSCALEINC4AVG;
}
ct.precision = datatypes::INT64MAXPRECISION;
}
ac->resultType(ct);
}
else if (isp->sum_func() == Item_sum::COUNT_FUNC || isp->sum_func() == Item_sum::COUNT_DISTINCT_FUNC)
{
CalpontSystemCatalog::ColType ct;
ct.colDataType = CalpontSystemCatalog::BIGINT;
ct.colWidth = 8;
ct.scale = 0;
ac->resultType(ct);
}
else if (isp->sum_func() == Item_sum::STD_FUNC || isp->sum_func() == Item_sum::VARIANCE_FUNC)
{
CalpontSystemCatalog::ColType ct;
ct.colDataType = CalpontSystemCatalog::DOUBLE;
ct.colWidth = 8;
ct.scale = 0;
ac->resultType(ct);
}
else if (isp->sum_func() == Item_sum::SUM_BIT_FUNC)
{
CalpontSystemCatalog::ColType ct;
ct.colDataType = CalpontSystemCatalog::UBIGINT;
ct.colWidth = 8;
ct.scale = 0;
ct.precision = -16; // borrowed to indicate skip null value check on connector
ac->resultType(ct);
}
else if (isp->sum_func() == Item_sum::GROUP_CONCAT_FUNC ||
isp->sum_func() == Item_sum::JSON_ARRAYAGG_FUNC)
{
// Item_func_group_concat* gc = (Item_func_group_concat*)isp;
CalpontSystemCatalog::ColType ct;
ct.colDataType = CalpontSystemCatalog::VARCHAR;
// MCOL-5429 CalpontSystemCatalog::ColType::colWidth is currently
// stored as an int32_t (see calpontsystemcatalog.h). However,
// Item_sum::max_length is an uint32_t. This means there will be an
// integer overflow when Item_sum::max_length > colWidth. This ultimately
// causes an array index out of bound in GroupConcator::swapStreamWithStringAndReturnBuf()
// in groupconcat.cpp when ExeMgr processes groupconcat. As a temporary
// fix, we cap off the max groupconcat length to std::numeric_limits<int32_t>::max().
// The proper fix would be to change colWidth type to uint32_t.
if (isp->max_length <= static_cast<uint32_t>(std::numeric_limits<int32_t>::max()))
{
ct.colWidth = isp->max_length;
}
else
{
ct.colWidth = std::numeric_limits<int32_t>::max();
}
ct.precision = 0;
ac->resultType(ct);
}
// Setting the ColType in the resulting RowGroup
// according with what MDB expects.
// Internal processing UDAF result type will be set below.
else if (isUDFSumItem(isp))
{
ac->resultType(colType_MysqlToIDB(isp));
}
// Using the first param to deduce ac data type
else if (ac->aggParms().size() == 1)
{
ac->resultType(parm->resultType());
}
else
{
gwi.fatalParseError = true;
gwi.parseErrorText =
"Can not deduce Aggregate Column resulting type \
because it has multiple arguments.";
if (ac)
delete ac;
return nullptr;
}
}
else if (constArgParam.bIsConst && constArgParam.hasDecimalConst && isAvg)
{
CalpontSystemCatalog::ColType ct = parm->resultType();
if (datatypes::Decimal::isWideDecimalTypeByPrecision(constArgParam.precision))
{
ct.precision = constArgParam.precision;
ct.scale = constArgParam.scale;
ct.colWidth = datatypes::MAXDECIMALWIDTH;
}
ac->resultType(ct);
}
else
{
ac->resultType(colType_MysqlToIDB(isp));
}
// adjust decimal result type according to internalDecimalScale
bool isWideDecimal = ac->resultType().isWideDecimalType();
if (!isWideDecimal && gwi.internalDecimalScale >= 0 &&
(ac->resultType().colDataType == CalpontSystemCatalog::DECIMAL ||
ac->resultType().colDataType == CalpontSystemCatalog::UDECIMAL))
{
CalpontSystemCatalog::ColType ct = ac->resultType();
ct.scale = gwi.internalDecimalScale;
ac->resultType(ct);
}
// check for same aggregate on the select list
ac->expressionId(ci->expressionId++);
if (gwi.clauseType != SELECT)
{
for (uint32_t i = 0; i < gwi.returnedCols.size(); i++)
{
if (*ac == gwi.returnedCols[i].get() && ac->alias() == gwi.returnedCols[i].get()->alias())
ac->expressionId(gwi.returnedCols[i]->expressionId());
}
}
// @bug5977 @note Temporary fix to avoid mysqld crash. The permanent fix will
// be applied in ExeMgr. When the ExeMgr fix is available, this checking
// will be taken out.
if (isp->sum_func() != Item_sum::UDF_SUM_FUNC)
{
if (ac->constCol() && gwi.tbList.empty() && gwi.derivedTbList.empty())
{
gwi.fatalParseError = true;
gwi.parseErrorText = "No project column found for aggregate function";
if (ac)
delete ac;
return NULL;
}
else if (ac->constCol())
{
gwi.count_asterisk_list.push_back(ac);
}
}
// For UDAF, populate the context and call the UDAF init() function.
// The return type is (should be) set in context by init().
if (isp->sum_func() == Item_sum::UDF_SUM_FUNC)
{
UDAFColumn* udafc = dynamic_cast<UDAFColumn*>(ac);
if (udafc)
{
mcsv1sdk::mcsv1Context& context = udafc->getContext();
context.setName(isp->func_name());
// Get the return type as defined by CREATE AGGREGATE FUNCTION
// Most functions don't care, but some may.
context.setMariaDBReturnType((mcsv1sdk::enum_mariadb_return_type)isp->field_type());
// Set up the return type defaults for the call to init()
context.setResultType(udafc->resultType().colDataType);
context.setColWidth(udafc->resultType().colWidth);
context.setScale(udafc->resultType().scale);
context.setPrecision(udafc->resultType().precision);
context.setParamCount(udafc->aggParms().size());
utils::VLArray<mcsv1sdk::ColumnDatum> colTypes(udafc->aggParms().size());
// Build the column type vector.
// Modified for MCOL-1201 multi-argument aggregate
for (uint32_t i = 0; i < udafc->aggParms().size(); ++i)
{
const execplan::CalpontSystemCatalog::ColType& resultType = udafc->aggParms()[i]->resultType();
mcsv1sdk::ColumnDatum& colType = colTypes[i];
colType.dataType = resultType.colDataType;
colType.precision = resultType.precision;
colType.scale = resultType.scale;
colType.charsetNumber = resultType.charsetNumber;
}
// Call the user supplied init()
mcsv1sdk::mcsv1_UDAF* udaf = context.getFunction();
if (!udaf)
{
gwi.fatalParseError = true;
gwi.parseErrorText =
"Aggregate Function " + context.getName() + " doesn't exist in the ColumnStore engine";
if (ac)
delete ac;
return NULL;
}
if (udaf->init(&context, colTypes) == mcsv1sdk::mcsv1_UDAF::ERROR)
{
gwi.fatalParseError = true;
gwi.parseErrorText = udafc->getContext().getErrorMessage();
if (ac)
delete ac;
return NULL;
}
// UDAF_OVER_REQUIRED means that this function is for Window
// Function only. Reject it here in aggregate land.
if (udafc->getContext().getRunFlag(mcsv1sdk::UDAF_OVER_REQUIRED))
{
gwi.fatalParseError = true;
gwi.parseErrorText =
logging::IDBErrorInfo::instance()->errorMsg(logging::ERR_WINDOW_FUNC_ONLY, context.getName());
if (ac)
delete ac;
return NULL;
}
// Set the return type as set in init()
CalpontSystemCatalog::ColType ct;
ct.colDataType = context.getResultType();
ct.colWidth = context.getColWidth();
ct.scale = context.getScale();
ct.precision = context.getPrecision();
udafc->resultType(ct);
}
}
}
catch (std::logic_error& e)
{
gwi.fatalParseError = true;
gwi.parseErrorText = "error building Aggregate Function: ";
gwi.parseErrorText += e.what();
if (ac)
delete ac;
return NULL;
}
catch (...)
{
gwi.fatalParseError = true;
gwi.parseErrorText = "error building Aggregate Function: Unspecified exception";
if (ac)
delete ac;
return NULL;
}
ac->charsetNumber(item->collation.collation->number);
return ac;
}
ReturnedColumn* buildAggregateColumn(Item* item, gp_walk_info& gwi)
{
bool disableWrapping = gwi.disableWrapping;
gwi.disableWrapping = true;
ReturnedColumn* rc = buildAggregateColumnBody(item, gwi);
gwi.disableWrapping = disableWrapping;
return rc;
}
void addIntervalArgs(gp_walk_info* gwip, Item_func* ifp, FunctionParm& functionParms)
{
string funcName = ifp->func_name();
int interval_type = -1;
if (funcName == "date_add_interval")
interval_type = ((Item_date_add_interval*)ifp)->int_type;
else if (funcName == "timestampdiff")
interval_type = ((Item_func_timestamp_diff*)ifp)->get_int_type();
else if (funcName == "extract")
interval_type = ((Item_extract*)ifp)->int_type;
functionParms.push_back(getIntervalType(gwip, interval_type));
SPTP sptp;
if (funcName == "date_add_interval")
{
if (((Item_date_add_interval*)ifp)->date_sub_interval)
{
sptp.reset(new ParseTree(new ConstantColumn((int64_t)OP_SUB)));
(dynamic_cast<ConstantColumn*>(sptp->data()))->timeZone(gwip->timeZone);
functionParms.push_back(sptp);
}
else
{
sptp.reset(new ParseTree(new ConstantColumn((int64_t)OP_ADD)));
(dynamic_cast<ConstantColumn*>(sptp->data()))->timeZone(gwip->timeZone);
functionParms.push_back(sptp);
}
}
}
SPTP getIntervalType(gp_walk_info* gwip, int interval_type)
{
SPTP sptp;
sptp.reset(new ParseTree(new ConstantColumn((int64_t)interval_type)));
(dynamic_cast<ConstantColumn*>(sptp->data()))->timeZone(gwip->timeZone);
return sptp;
}
void castCharArgs(gp_walk_info* gwip, Item_func* ifp, FunctionParm& functionParms)
{
Item_char_typecast* idai = (Item_char_typecast*)ifp;
SPTP sptp;
sptp.reset(new ParseTree(new ConstantColumn((int64_t)idai->get_cast_length())));
(dynamic_cast<ConstantColumn*>(sptp->data()))->timeZone(gwip->timeZone);
functionParms.push_back(sptp);
}
void castDecimalArgs(gp_walk_info* gwip, Item_func* ifp, FunctionParm& functionParms)
{
Item_decimal_typecast* idai = (Item_decimal_typecast*)ifp;
SPTP sptp;
sptp.reset(new ParseTree(new ConstantColumn((int64_t)idai->decimals)));
(dynamic_cast<ConstantColumn*>(sptp->data()))->timeZone(gwip->timeZone);
functionParms.push_back(sptp);
// max length including sign and/or decimal points
if (idai->decimals == 0)
sptp.reset(new ParseTree(new ConstantColumn((int64_t)idai->max_length - 1)));
else
sptp.reset(new ParseTree(new ConstantColumn((int64_t)idai->max_length - 2)));
(dynamic_cast<ConstantColumn*>(sptp->data()))->timeZone(gwip->timeZone);
functionParms.push_back(sptp);
}
void castTypeArgs(gp_walk_info* gwip, Item_func* ifp, FunctionParm& functionParms)
{
Item_func_get_format* get_format = (Item_func_get_format*)ifp;
SPTP sptp;
if (get_format->type == MYSQL_TIMESTAMP_DATE)
sptp.reset(new ParseTree(new ConstantColumn("DATE")));
else
sptp.reset(new ParseTree(new ConstantColumn("DATETIME")));
(dynamic_cast<ConstantColumn*>(sptp->data()))->timeZone(gwip->timeZone);
functionParms.push_back(sptp);
}
bool isSecondArgumentConstItem(Item_func* ifp)
{
return (ifp->argument_count() == 2 && ifp->arguments()[1]->type() == Item::CONST_ITEM);
}
// SELECT ... WHERE <col> NOT IN (SELECT <const_item>);
bool isNotFuncAndConstScalarSubSelect(Item_func* ifp, const std::string& funcName)
{
return (ifp->with_subquery() && funcName == "not" && ifp->argument_count() == 1 &&
ifp->arguments()[0]->type() == Item::FUNC_ITEM &&
std::string(((Item_func*)ifp->arguments()[0])->func_name()) == "=" &&
isSecondArgumentConstItem((Item_func*)ifp->arguments()[0]));
}
void gp_walk(const Item* item, void* arg)
{
gp_walk_info* gwip = static_cast<gp_walk_info*>(arg);
idbassert(gwip);
// Bailout...
if (gwip->fatalParseError)
return;
RecursionCounter r(gwip); // Increments and auto-decrements upon exit.
Item::Type itype = item->type();
// Allow to process XOR(which is Item_func) like other logical operators (which are Item_cond)
if (itype == Item::FUNC_ITEM && ((Item_func*)item)->functype() == Item_func::XOR_FUNC)
itype = Item::COND_ITEM;
switch (itype)
{
case Item::CACHE_ITEM:
{
// The item or condition is cached as per MariaDB server view but
// for InfiniDB it need to be parsed and executed.
// MCOL-1188 and MCOL-1029
Item* orig_item = ((Item_cache*)item)->get_example();
orig_item->traverse_cond(gp_walk, gwip, Item::POSTFIX);
break;
}
case Item::FIELD_ITEM:
{
Item_field* ifp = (Item_field*)item;
if (ifp)
{
// XXX: this looks awfuly wrong.
SimpleColumn* scp = buildSimpleColumn(ifp, *gwip);
if (!scp)
break;
string aliasTableName(scp->tableAlias());
scp->tableAlias(aliasTableName);
gwip->rcWorkStack.push(scp->clone());
boost::shared_ptr<SimpleColumn> scsp(scp);
gwip->scsp = scsp;
gwip->funcName.clear();
gwip->columnMap.insert(
CalpontSelectExecutionPlan::ColumnMap::value_type(string(ifp->field_name.str), scsp));
//@bug4636 take where clause column as dummy projection column, but only on local column.
// varbinary aggregate is not supported yet, so rule it out
if (!((scp->joinInfo() & JOIN_CORRELATED) ||
scp->colType().colDataType == CalpontSystemCatalog::VARBINARY))
{
TABLE_LIST* tmp = (ifp->cached_table ? ifp->cached_table : 0);
gwip->tableMap[make_aliastable(scp->schemaName(), scp->tableName(), scp->tableAlias(),
scp->isColumnStore())] = make_pair(1, tmp);
}
}
break;
}
case Item::CONST_ITEM:
{
switch (item->cmp_type())
{
case INT_RESULT:
{
Item* non_const_item = const_cast<Item*>(item);
gwip->rcWorkStack.push(buildReturnedColumn(non_const_item, *gwip, gwip->fatalParseError));
break;
}
case STRING_RESULT:
{
// Special handling for 0xHHHH literals
if (item->type_handler() == &type_handler_hex_hybrid)
{
Item_hex_hybrid* hip = static_cast<Item_hex_hybrid*>(const_cast<Item*>(item));
gwip->rcWorkStack.push(new ConstantColumn((int64_t)hip->val_int(), ConstantColumn::NUM));
ConstantColumn* cc = dynamic_cast<ConstantColumn*>(gwip->rcWorkStack.top());
cc->timeZone(gwip->timeZone);
break;
}
if (item->result_type() == STRING_RESULT)
{
// dangerous cast here
Item* isp = const_cast<Item*>(item);
String val, *str = isp->val_str(&val);
if (str)
{
string cval;
if (str->ptr())
{
cval.assign(str->ptr(), str->length());
}
gwip->rcWorkStack.push(new ConstantColumn(cval));
(dynamic_cast<ConstantColumn*>(gwip->rcWorkStack.top()))->timeZone(gwip->timeZone);
break;
}
else
{
gwip->rcWorkStack.push(new ConstantColumn("", ConstantColumn::NULLDATA));
(dynamic_cast<ConstantColumn*>(gwip->rcWorkStack.top()))->timeZone(gwip->timeZone);
break;
}
gwip->rcWorkStack.push(buildReturnedColumn(isp, *gwip, gwip->fatalParseError));
}
break;
}
case REAL_RESULT:
case DECIMAL_RESULT:
case TIME_RESULT:
{
Item* nonConstItem = const_cast<Item*>(item);
gwip->rcWorkStack.push(buildReturnedColumn(nonConstItem, *gwip, gwip->fatalParseError));
break;
}
default:
{
if (gwip->condPush)
{
// push noop for unhandled item
SimpleColumn* rc = new SimpleColumn("noop");
rc->timeZone(gwip->timeZone);
gwip->rcWorkStack.push(rc);
break;
}
ostringstream oss;
oss << "Unhandled Item type(): " << item->type();
gwip->parseErrorText = oss.str();
gwip->fatalParseError = true;
break;
}
}
break;
}
case Item::NULL_ITEM:
{
if (gwip->condPush)
{
// push noop for unhandled item
SimpleColumn* rc = new SimpleColumn("noop");
rc->timeZone(gwip->timeZone);
gwip->rcWorkStack.push(rc);
break;
}
gwip->rcWorkStack.push(new ConstantColumn("", ConstantColumn::NULLDATA));
(dynamic_cast<ConstantColumn*>(gwip->rcWorkStack.top()))->timeZone(gwip->timeZone);
break;
}
case Item::FUNC_ITEM:
{
Item* ncitem = const_cast<Item*>(item);
Item_func* ifp = static_cast<Item_func*>(ncitem);
string funcName = ifp->func_name();
if (!gwip->condPush)
{
if (!ifp->fixed())
{
ifp->fix_fields(gwip->thd, &ncitem);
}
// Special handling for queries of the form:
// SELECT ... WHERE col1 NOT IN (SELECT <const_item>);
if (isNotFuncAndConstScalarSubSelect(ifp, funcName))
{
idbassert(!gwip->ptWorkStack.empty());
ParseTree* pt = gwip->ptWorkStack.top();
SimpleFilter* sf = dynamic_cast<SimpleFilter*>(pt->data());
if (sf)
{
boost::shared_ptr<Operator> sop(new PredicateOperator("<>"));
sf->op(sop);
return;
}
}
// Do not call buildSubselectFunc() if the subquery is a const scalar
// subselect of the form:
// (SELECT <const_item>)
// As an example: SELECT col1 FROM t1 WHERE col2 = (SELECT 2);
if ((ifp->with_subquery() && !isSecondArgumentConstItem(ifp)) || funcName == "<in_optimizer>")
{
buildSubselectFunc(ifp, gwip);
return;
}
if (ifp->argument_count() > 0 && ifp->arguments())
{
for (uint32_t i = 0; i < ifp->argument_count(); i++)
{
if (ifp->arguments()[i]->type() == Item::SUBSELECT_ITEM)
{
// This is probably NOT IN subquery with derived table in it.
// for some reason, MySQL has not fully optimized the plan at this point.
// noop here, and eventually MySQL will continue its optimization and get
// to rnd_init again.
if (ifp->functype() == Item_func::NOT_FUNC)
return;
buildSubselectFunc(ifp, gwip);
return;
}
}
}
if (ifp->functype() == Item_func::TRIG_COND_FUNC && gwip->subQuery)
{
gwip->subQuery->handleFunc(gwip, ifp);
break;
}
// having clause null function added by MySQL
if (ifp->functype() == Item_func::ISNOTNULLTEST_FUNC)
{
// @bug 4215. remove the argument in rcWorkStack.
if (!gwip->rcWorkStack.empty())
{
delete gwip->rcWorkStack.top();
gwip->rcWorkStack.pop();
}
break;
}
}
// try to evaluate const F&E
vector<Item_field*> tmpVec;
uint16_t parseInfo = 0;
parse_item(ifp, tmpVec, gwip->fatalParseError, parseInfo, gwip);
// table mode takes only one table filter
if (gwip->condPush)
{
set<string> tableSet;
for (uint32_t i = 0; i < tmpVec.size(); i++)
{
if (tmpVec[i]->table_name.str)
tableSet.insert(tmpVec[i]->table_name.str);
}
if (tableSet.size() > 1)
break;
}
if (!gwip->fatalParseError && !(parseInfo & AGG_BIT) && !(parseInfo & SUB_BIT) && !nonConstFunc(ifp) &&
!(parseInfo & AF_BIT) && tmpVec.size() == 0 && ifp->functype() != Item_func::MULT_EQUAL_FUNC)
{
ValStrStdString valStr(ifp);
ConstantColumn* cc = buildConstantColumnMaybeNullFromValStr(ifp, valStr, *gwip);
for (uint32_t i = 0; i < ifp->argument_count() && !gwip->rcWorkStack.empty(); i++)
{
delete gwip->rcWorkStack.top();
gwip->rcWorkStack.pop();
}
// bug 3137. If filter constant like 1=0, put it to ptWorkStack
// MariaDB bug 750. Breaks if compare is an argument to a function.
// if ((int32_t)gwip->rcWorkStack.size() <=
//(gwip->rcBookMarkStack.empty()
//?
// 0
//: gwip->rcBookMarkStack.top())
// && isPredicateFunction(ifp, gwip))
if (isPredicateFunction(ifp, gwip))
gwip->ptWorkStack.push(new ParseTree(cc));
else
gwip->rcWorkStack.push(cc);
if (!valStr.isNull())
IDEBUG(cerr << "Const F&E " << item->full_name() << " evaluate: " << valStr << endl);
break;
}
ReturnedColumn* rc = NULL;
// @bug4488. Process function for table mode also, not just vtable mode.
rc = buildFunctionColumn(ifp, *gwip, gwip->fatalParseError);
if (gwip->fatalParseError)
{
if (gwip->clauseType == SELECT)
return;
// @bug 2585
if (gwip->parseErrorText.empty())
{
Message::Args args;
args.add(funcName);
gwip->parseErrorText = IDBErrorInfo::instance()->errorMsg(ERR_NON_SUPPORTED_FUNCTION, args);
}
return;
}
// predicate operators fall in the old path
if (rc)
{
// @bug 2383. For some reason func_name() for "in" gives " IN " always
if (funcName == "between" || funcName == "in" || funcName == " IN ")
gwip->ptWorkStack.push(new ParseTree(rc));
else
gwip->rcWorkStack.push(rc);
}
else
{
// push to pt or rc stack is handled inside the function
buildPredicateItem(ifp, gwip);
}
break;
}
case Item::SUM_FUNC_ITEM:
{
Item_sum* isp = (Item_sum*)item;
ReturnedColumn* rc = buildAggregateColumn(isp, *gwip);
if (rc)
gwip->rcWorkStack.push(rc);
break;
}
case Item::COND_ITEM:
{
// All logical functions are handled here, most of them are Item_cond,
// but XOR (it is Item_func_boolean2)
Item_func* func = (Item_func*)item;
enum Item_func::Functype ftype = func->functype();
bool isOr = (ftype == Item_func::COND_OR_FUNC);
bool isXor = (ftype == Item_func::XOR_FUNC);
List<Item>* argumentList;
List<Item> xorArgumentList;
if (isXor)
{
for (unsigned i = 0; i < func->argument_count(); i++)
{
xorArgumentList.push_back(func->arguments()[i]);
}
argumentList = &xorArgumentList;
}
else
{
argumentList = ((Item_cond*)item)->argument_list();
}
// @bug2932. if ptWorkStack contains less items than the condition's arguments,
// the missing one should be in the rcWorkStack, unless the it's subselect.
// @todo need to figure out a way to combine these two stacks while walking.
// if (gwip->ptWorkStack.size() < icp->argument_list()->elements)
{
List_iterator_fast<Item> li(*argumentList);
while (Item* it = li++)
{
//@bug3495, @bug5865 error out non-supported OR with correlated subquery
if (isOr)
{
vector<Item_field*> fieldVec;
uint16_t parseInfo = 0;
parse_item(it, fieldVec, gwip->fatalParseError, parseInfo, gwip);
if (parseInfo & CORRELATED)
{
gwip->fatalParseError = true;
gwip->parseErrorText = IDBErrorInfo::instance()->errorMsg(ERR_CORRELATED_SUB_OR);
return;
}
}
if ((it->type() == Item::FIELD_ITEM ||
(it->type() == Item::CONST_ITEM &&
(it->cmp_type() == INT_RESULT || it->cmp_type() == DECIMAL_RESULT ||
it->cmp_type() == STRING_RESULT || it->cmp_type() == REAL_RESULT)) ||
it->type() == Item::NULL_ITEM ||
(it->type() == Item::FUNC_ITEM && !isPredicateFunction(it, gwip))) &&
!gwip->rcWorkStack.empty())
{
gwip->ptWorkStack.push(new ParseTree(gwip->rcWorkStack.top()));
gwip->rcWorkStack.pop();
}
}
}
// @bug1603. MySQL's filter tree is a multi-tree grouped by operator. So more than
// two filters saved on the stack so far might belong to this operator.
uint32_t leftInStack = gwip->ptWorkStack.size() - argumentList->elements + 1;
while (true)
{
if (gwip->ptWorkStack.size() < 2)
break;
ParseTree* lhs = gwip->ptWorkStack.top();
gwip->ptWorkStack.pop();
SimpleFilter* lsf = dynamic_cast<SimpleFilter*>(lhs->data());
if (lsf && lsf->op()->data() == "noop")
{
if (isOr)
{
gwip->parseErrorText = "Unhandled item in WHERE or HAVING clause";
gwip->fatalParseError = true;
break;
}
else
continue;
}
ParseTree* rhs = gwip->ptWorkStack.top();
gwip->ptWorkStack.pop();
SimpleFilter* rsf = dynamic_cast<SimpleFilter*>(rhs->data());
if (rsf && rsf->op()->data() == "noop")
{
if (isOr)
{
gwip->parseErrorText = "Unhandled item in WHERE or HAVING clause";
gwip->fatalParseError = true;
break;
}
else
{
delete rhs;
gwip->ptWorkStack.push(lhs);
continue;
}
}
Operator* op = new LogicOperator(func->func_name());
ParseTree* ptp = new ParseTree(op);
ptp->left(lhs);
ptp->right(rhs);
gwip->ptWorkStack.push(ptp);
if (gwip->ptWorkStack.size() == leftInStack)
break;
}
// special handling for subquery with aggregate. MySQL adds isnull function to the selected
// column. InfiniDB will remove it and set nullmatch flag if it's NOT_IN sub.
// @todo need more checking here to make sure it's not a user input OR operator
if (isOr && gwip->subQuery)
gwip->subQuery->handleFunc(gwip, func);
break;
}
case Item::REF_ITEM:
{
Item* col = *(((Item_ref*)item)->ref);
ReturnedColumn* rc = NULL;
// ref item is not pre-walked. force clause type to SELECT
ClauseType clauseType = gwip->clauseType;
gwip->clauseType = SELECT;
if (col->type() != Item::COND_ITEM)
{
rc = buildReturnedColumn(col, *gwip, gwip->fatalParseError, true);
if (col->type() == Item::FIELD_ITEM)
gwip->fatalParseError = false;
}
SimpleColumn* sc = clauseType == HAVING ? nullptr : dynamic_cast<SimpleColumn*>(rc);
if (sc)
{
boost::shared_ptr<SimpleColumn> scsp(sc->clone());
gwip->scsp = scsp;
if (col->type() == Item::FIELD_ITEM)
{
const Item_ident* ident_field = dynamic_cast<const Item_ident*>(item);
if (ident_field)
{
const auto& field_name = string(ident_field->field_name.str);
auto colMap = CalpontSelectExecutionPlan::ColumnMap::value_type(field_name, scsp);
gwip->columnMap.insert(colMap);
}
}
}
bool cando = true;
gwip->clauseType = clauseType;
if (rc)
{
if (((Item_ref*)item)->depended_from)
{
rc->joinInfo(rc->joinInfo() | JOIN_CORRELATED);
if (gwip->subQuery)
gwip->subQuery->correlated(true);
SimpleColumn* scp = dynamic_cast<SimpleColumn*>(rc);
if (scp)
gwip->correlatedTbNameVec.push_back(
make_aliastable(scp->schemaName(), scp->tableName(), scp->tableAlias()));
if (gwip->subSelectType == CalpontSelectExecutionPlan::SINGLEROW_SUBS)
rc->joinInfo(rc->joinInfo() | JOIN_SCALAR | JOIN_SEMI);
if (gwip->subSelectType == CalpontSelectExecutionPlan::SELECT_SUBS)
rc->joinInfo(rc->joinInfo() | JOIN_SCALAR | JOIN_OUTER_SELECT);
}
gwip->rcWorkStack.push(rc);
}
else if (col->type() == Item::FUNC_ITEM)
{
// sometimes mysql treat having filter items inconsistently. In such cases,
// which are always predicate operator, the function (gp_key>3) comes in as
// one item.
Item_func* ifp = (Item_func*)col;
for (uint32_t i = 0; i < ifp->argument_count(); i++)
{
ReturnedColumn* operand = NULL;
if (ifp->arguments()[i]->type() == Item::REF_ITEM)
{
Item* op = *(((Item_ref*)ifp->arguments()[i])->ref);
operand = buildReturnedColumn(op, *gwip, gwip->fatalParseError);
}
else
operand = buildReturnedColumn(ifp->arguments()[i], *gwip, gwip->fatalParseError);
if (operand)
{
gwip->rcWorkStack.push(operand);
if (i == 0 && gwip->scsp == NULL) // first item is the WHEN LHS
{
SimpleColumn* sc = dynamic_cast<SimpleColumn*>(operand);
if (sc)
{
gwip->scsp.reset(sc->clone()); // We need to clone else sc gets double deleted. This code is
// rarely executed so the cost is acceptable.
}
}
}
else
{
cando = false;
break;
}
}
if (cando)
buildPredicateItem(ifp, gwip);
}
else if (col->type() == Item::COND_ITEM)
{
gwip->ptWorkStack.push(buildParseTree(col, *gwip, gwip->fatalParseError));
}
else if (col->type() == Item::FIELD_ITEM && gwip->clauseType == HAVING)
{
//ReturnedColumn* rc = buildAggFrmTempField(const_cast<Item*>(item), *gwip);
ReturnedColumn* rc = buildReturnedColumn(const_cast<Item*>(item), *gwip, gwip->fatalParseError);
if (rc)
gwip->rcWorkStack.push(rc);
break;
}
else
{
cando = false;
}
SimpleColumn* thisSC = dynamic_cast<SimpleColumn*>(rc);
if (thisSC)
{
gwip->scsp.reset(thisSC->clone());
}
if (!rc && !cando)
{
ostringstream oss;
oss << "Unhandled Item type(): " << item->type();
gwip->parseErrorText = oss.str();
gwip->fatalParseError = true;
}
break;
}
case Item::SUBSELECT_ITEM:
{
if (gwip->condPush) // table mode
break;
Item_subselect* sub = (Item_subselect*)item;
if (sub->substype() == Item_subselect::EXISTS_SUBS)
{
SubQuery* orig = gwip->subQuery;
ExistsSub* existsSub = new ExistsSub(*gwip, sub);
gwip->hasSubSelect = true;
gwip->subQuery = existsSub;
gwip->ptWorkStack.push(existsSub->transform());
// MCOL-2178 isUnion member only assigned, never used
// MIGR::infinidb_vtable.isUnion = true; // only temp. bypass the 2nd phase.
// recover original
gwip->subQuery = orig;
gwip->lastSub = existsSub;
}
else if (sub->substype() == Item_subselect::IN_SUBS)
{
if (!((Item_in_subselect*)sub)->optimizer && gwip->thd->derived_tables_processing)
{
ostringstream oss;
oss << "Invalid In_optimizer: " << item->type();
gwip->parseErrorText = oss.str();
gwip->fatalParseError = true;
break;
}
}
// store a dummy subselect object. the transform is handled in item_func.
SubSelect* subselect = new SubSelect();
gwip->rcWorkStack.push(subselect);
break;
}
case Item::ROW_ITEM:
{
Item_row* row = (Item_row*)item;
RowColumn* rowCol = new RowColumn();
vector<SRCP> cols;
// temp change clause type because the elements of row column are not walked yet
gwip->clauseType = SELECT;
for (uint32_t i = 0; i < row->cols(); i++)
cols.push_back(SRCP(buildReturnedColumn(row->element_index(i), *gwip, gwip->fatalParseError)));
gwip->clauseType = WHERE;
rowCol->columnVec(cols);
gwip->rcWorkStack.push(rowCol);
break;
}
case Item::EXPR_CACHE_ITEM:
{
((Item_cache_wrapper*)item)->get_orig_item()->traverse_cond(gp_walk, arg, Item::POSTFIX);
break;
}
case Item::WINDOW_FUNC_ITEM:
{
gwip->hasWindowFunc = true;
Item_window_func* ifa = (Item_window_func*)item;
ReturnedColumn* af = buildWindowFunctionColumn(ifa, *gwip, gwip->fatalParseError);
if (af)
gwip->rcWorkStack.push(af);
break;
}
case Item::COPY_STR_ITEM: printf("********** received COPY_STR_ITEM *********\n"); break;
case Item::FIELD_AVG_ITEM: printf("********** received FIELD_AVG_ITEM *********\n"); break;
case Item::DEFAULT_VALUE_ITEM: printf("********** received DEFAULT_VALUE_ITEM *********\n"); break;
case Item::PROC_ITEM: printf("********** received PROC_ITEM *********\n"); break;
case Item::FIELD_STD_ITEM: printf("********** received FIELD_STD_ITEM *********\n"); break;
case Item::FIELD_VARIANCE_ITEM: printf("********** received FIELD_VARIANCE_ITEM *********\n"); break;
case Item::INSERT_VALUE_ITEM: printf("********** received INSERT_VALUE_ITEM *********\n"); break;
case Item::PARAM_ITEM: printf("********** received PARAM_ITEM *********\n"); break;
case Item::TRIGGER_FIELD_ITEM: printf("********** received TRIGGER_FIELD_ITEM *********\n"); break;
case Item::TYPE_HOLDER: std::cerr << "********** received TYPE_HOLDER *********" << std::endl; break;
default:
{
if (gwip->condPush)
{
// push noop for unhandled item
SimpleColumn* rc = new SimpleColumn("noop");
rc->timeZone(gwip->timeZone);
gwip->rcWorkStack.push(rc);
break;
}
ostringstream oss;
oss << "Unhandled Item type (2): " << item->type();
gwip->parseErrorText = oss.str();
gwip->fatalParseError = true;
break;
}
}
return;
}
/** @info this function recursivly walks an item's arguments and push all
* the involved item_fields to the passed in vector. It's used in parsing
* functions or arithmetic expressions for vtable post process.
*/
void parse_item(Item* item, vector<Item_field*>& field_vec, bool& hasNonSupportItem, uint16_t& parseInfo,
gp_walk_info* gwi)
{
Item::Type itype = item->type();
switch (itype)
{
case Item::FIELD_ITEM:
{
Item_field* ifp = static_cast<Item_field*>(item);
field_vec.push_back(ifp);
return;
}
case Item::SUM_FUNC_ITEM:
{
// hasAggColumn = true;
parseInfo |= AGG_BIT;
Item_sum* isp = static_cast<Item_sum*>(item);
Item** sfitempp = isp->arguments();
for (uint32_t i = 0; i < isp->argument_count(); i++)
parse_item(sfitempp[i], field_vec, hasNonSupportItem, parseInfo, gwi);
break;
}
case Item::FUNC_ITEM:
{
Item_func* isp = static_cast<Item_func*>(item);
if (string(isp->func_name()) == "<in_optimizer>")
{
parseInfo |= SUB_BIT;
parseInfo |= CORRELATED;
break;
}
for (uint32_t i = 0; i < isp->argument_count(); i++)
parse_item(isp->arguments()[i], field_vec, hasNonSupportItem, parseInfo, gwi);
break;
}
case Item::COND_ITEM:
{
Item_cond* icp = static_cast<Item_cond*>(item);
List_iterator_fast<Item> it(*(icp->argument_list()));
Item* cond_item;
while ((cond_item = it++))
parse_item(cond_item, field_vec, hasNonSupportItem, parseInfo, gwi);
break;
}
case Item::REF_ITEM:
{
Item_ref* ref = (Item_ref*)item;
if (ref->ref_type() == Item_ref::DIRECT_REF)
{
parse_item(ref->real_item(), field_vec, hasNonSupportItem, parseInfo, gwi);
break;
}
while (true)
{
ref = (Item_ref*)item;
if ((*(ref->ref))->type() == Item::SUM_FUNC_ITEM)
{
parseInfo |= AGG_BIT;
Item_sum* isp = static_cast<Item_sum*>(*(ref->ref));
Item** sfitempp = isp->arguments();
// special handling for count(*). This should not be treated as constant.
if (isSupportedAggregateWithOneConstArg(isp, sfitempp))
{
field_vec.push_back(nullptr); // dummy
}
for (uint32_t i = 0; i < isp->argument_count(); i++)
parse_item(sfitempp[i], field_vec, hasNonSupportItem, parseInfo, gwi);
break;
}
else if ((*(ref->ref))->type() == Item::FIELD_ITEM)
{
// MCOL-1510. This could be a non-supported function
// argument in form of a temp_table_field, so check
// and set hasNonSupportItem if it is so.
//ReturnedColumn* rc = NULL;
//if (gwi)
// rc = buildAggFrmTempField(ref, *gwi);
//if (!rc)
//{
Item_field* ifp = static_cast<Item_field*>(*(ref->ref));
field_vec.push_back(ifp);
//}
break;
}
else if ((*(ref->ref))->type() == Item::FUNC_ITEM)
{
Item_func* isp = static_cast<Item_func*>(*(ref->ref));
Item** sfitempp = isp->arguments();
for (uint32_t i = 0; i < isp->argument_count(); i++)
parse_item(sfitempp[i], field_vec, hasNonSupportItem, parseInfo, gwi);
break;
}
else if ((*(ref->ref))->type() == Item::CACHE_ITEM)
{
Item_cache* isp = static_cast<Item_cache*>(*(ref->ref));
parse_item(isp->get_example(), field_vec, hasNonSupportItem, parseInfo, gwi);
break;
}
else if ((*(ref->ref))->type() == Item::REF_ITEM)
{
item = (*(ref->ref));
continue;
}
else if ((*(ref->ref))->type() == Item::WINDOW_FUNC_ITEM)
{
parseInfo |= AF_BIT;
break;
}
else
{
cerr << "UNKNOWN REF Item" << endl;
break;
}
}
break;
}
case Item::SUBSELECT_ITEM:
{
parseInfo |= SUB_BIT;
Item_subselect* sub = (Item_subselect*)item;
if (sub->is_correlated)
parseInfo |= CORRELATED;
break;
}
case Item::ROW_ITEM:
{
Item_row* row = (Item_row*)item;
for (uint32_t i = 0; i < row->cols(); i++)
parse_item(row->element_index(i), field_vec, hasNonSupportItem, parseInfo, gwi);
break;
}
case Item::EXPR_CACHE_ITEM:
{
// item is a Item_cache_wrapper. Shouldn't get here.
// DRRTUY TODO Why
IDEBUG(std::cerr << "EXPR_CACHE_ITEM in parse_item\n" << std::endl);
gwi->fatalParseError = true;
// DRRTUY The questionable error text. I've seen
// ERR_CORRELATED_SUB_OR
string parseErrorText = IDBErrorInfo::instance()->errorMsg(ERR_NON_SUPPORT_SUB_QUERY_TYPE);
setError(gwi->thd, ER_CHECK_NOT_IMPLEMENTED, parseErrorText);
break;
}
case Item::WINDOW_FUNC_ITEM: parseInfo |= AF_BIT; break;
default: break;
}
}
bool isMCSTable(TABLE* table_ptr)
{
#if (defined(_MSC_VER) && defined(_DEBUG)) || defined(SAFE_MUTEX)
if (!(table_ptr->s && (*table_ptr->s->db_plugin)->name.str))
#else
if (!(table_ptr->s && (table_ptr->s->db_plugin)->name.str))
#endif
return true;
#if (defined(_MSC_VER) && defined(_DEBUG)) || defined(SAFE_MUTEX)
string engineName = (*table_ptr->s->db_plugin)->name.str;
#else
string engineName = table_ptr->s->db_plugin->name.str;
#endif
if (engineName == "Columnstore" || engineName == "Columnstore_cache")
return true;
else
return false;
}
bool isForeignTableUpdate(THD* thd)
{
LEX* lex = thd->lex;
if (!isUpdateStatement(lex->sql_command))
return false;
Item_field* item;
List_iterator_fast<Item> field_it(lex->first_select_lex()->item_list);
while ((item = (Item_field*)field_it++))
{
if (item->field && item->field->table && !isMCSTable(item->field->table))
return true;
}
return false;
}
bool isMCSTableUpdate(THD* thd)
{
LEX* lex = thd->lex;
if (!isUpdateStatement(lex->sql_command))
return false;
Item_field* item;
List_iterator_fast<Item> field_it(lex->first_select_lex()->item_list);
while ((item = (Item_field*)field_it++))
{
if (item->field && item->field->table && isMCSTable(item->field->table))
return true;
}
return false;
}
bool isMCSTableDelete(THD* thd)
{
LEX* lex = thd->lex;
if (!isDeleteStatement(lex->sql_command))
return false;
TABLE_LIST* table_ptr = lex->first_select_lex()->get_table_list();
if (table_ptr && table_ptr->table && isMCSTable(table_ptr->table))
return true;
return false;
}
// This function is different from isForeignTableUpdate()
// above as it only checks if any of the tables involved
// in the multi-table update statement is a foreign table,
// irrespective of whether the update is performed on the
// foreign table or not, as in isForeignTableUpdate().
bool isUpdateHasForeignTable(THD* thd)
{
LEX* lex = thd->lex;
if (!isUpdateStatement(lex->sql_command))
return false;
TABLE_LIST* table_ptr = lex->first_select_lex()->get_table_list();
for (; table_ptr; table_ptr = table_ptr->next_local)
{
if (table_ptr->table && !isMCSTable(table_ptr->table))
return true;
}
return false;
}
/*@brief set some runtime params to run the query */
/***********************************************************
* DESCRIPTION:
* This function just sets a number of runtime params that
* limits resource consumed.
***********************************************************/
void setExecutionParams(gp_walk_info& gwi, SCSEP& csep)
{
gwi.internalDecimalScale = (get_use_decimal_scale(gwi.thd) ? get_decimal_scale(gwi.thd) : -1);
// @bug 2123. Override large table estimate if infinidb_ordered hint was used.
// @bug 2404. Always override if the infinidb_ordered_only variable is turned on.
if (get_ordered_only(gwi.thd))
csep->overrideLargeSideEstimate(true);
// @bug 5741. Set a flag when in Local PM only query mode
csep->localQuery(get_local_query(gwi.thd));
// @bug 3321. Set max number of blocks in a dictionary file to be scanned for filtering
csep->stringScanThreshold(get_string_scan_threshold(gwi.thd));
csep->stringTableThreshold(get_stringtable_threshold(gwi.thd));
csep->djsSmallSideLimit(get_diskjoin_smallsidelimit(gwi.thd) * 1024ULL * 1024);
csep->djsLargeSideLimit(get_diskjoin_largesidelimit(gwi.thd) * 1024ULL * 1024);
csep->djsPartitionSize(get_diskjoin_bucketsize(gwi.thd) * 1024ULL * 1024);
csep->djsMaxPartitionTreeDepth(get_diskjoin_max_partition_tree_depth(gwi.thd));
csep->djsForceRun(get_diskjoin_force_run(gwi.thd));
csep->maxPmJoinResultCount(get_max_pm_join_result_count(gwi.thd));
if (get_um_mem_limit(gwi.thd) == 0)
csep->umMemLimit(numeric_limits<int64_t>::max());
else
csep->umMemLimit(get_um_mem_limit(gwi.thd) * 1024ULL * 1024);
}
/*@brief Process FROM part of the query or sub-query */
/***********************************************************
* DESCRIPTION:
* This function processes elements of List<TABLE_LIST> in
* FROM part of the query.
* isUnion tells that CS processes FROM taken from UNION UNIT.
* The notion is described in MDB code.
* RETURNS
* error id as an int
***********************************************************/
int processFrom(bool& isUnion, SELECT_LEX& select_lex, gp_walk_info& gwi, SCSEP& csep,
bool isSelectHandlerTop, bool isSelectLexUnit)
{
// populate table map and trigger syscolumn cache for all the tables (@bug 1637).
// all tables on FROM list must have at least one col in colmap
TABLE_LIST* table_ptr = select_lex.get_table_list();
#ifdef DEBUG_WALK_COND
List_iterator<TABLE_LIST> sj_list_it(select_lex.sj_nests);
TABLE_LIST* sj_nest;
while ((sj_nest = sj_list_it++))
{
cerr << sj_nest->db.str << "." << sj_nest->table_name.str << endl;
}
#endif
try
{
for (; table_ptr; table_ptr = table_ptr->next_local)
{
// Until we handle recursive cte:
// Checking here ensures we catch all with clauses in the query.
if (table_ptr->is_recursive_with_table())
{
gwi.fatalParseError = true;
gwi.parseErrorText = "Recursive CTE";
setError(gwi.thd, ER_CHECK_NOT_IMPLEMENTED, gwi.parseErrorText, gwi);
return ER_CHECK_NOT_IMPLEMENTED;
}
string viewName = getViewName(table_ptr);
if (lower_case_table_names)
{
boost::algorithm::to_lower(viewName);
}
// @todo process from subquery
if (table_ptr->derived)
{
SELECT_LEX* select_cursor = table_ptr->derived->first_select();
FromSubQuery* fromSub = new FromSubQuery(gwi, select_cursor);
string alias(table_ptr->alias.str);
if (lower_case_table_names)
{
boost::algorithm::to_lower(alias);
}
fromSub->alias(alias);
CalpontSystemCatalog::TableAliasName tn = make_aliasview("", "", alias, viewName);
// @bug 3852. check return execplan
SCSEP plan = fromSub->transform();
if (!plan)
{
setError(gwi.thd, ER_INTERNAL_ERROR, fromSub->gwip().parseErrorText, gwi);
CalpontSystemCatalog::removeCalpontSystemCatalog(gwi.sessionid);
return ER_INTERNAL_ERROR;
}
gwi.derivedTbList.push_back(plan);
gwi.tbList.push_back(tn);
CalpontSystemCatalog::TableAliasName tan = make_aliastable("", alias, alias);
gwi.tableMap[tan] = make_pair(0, table_ptr);
// MCOL-2178 isUnion member only assigned, never used
// MIGR::infinidb_vtable.isUnion = true; //by-pass the 2nd pass of rnd_init
}
else if (table_ptr->view)
{
View* view = new View(*table_ptr->view->first_select_lex(), &gwi);
CalpontSystemCatalog::TableAliasName tn = make_aliastable(
table_ptr->db.str, table_ptr->table_name.str, table_ptr->alias.str, true, lower_case_table_names);
view->viewName(tn);
gwi.viewList.push_back(view);
view->transform();
}
else
{
// check foreign engine tables
bool columnStore = (table_ptr->table ? isMCSTable(table_ptr->table) : true);
// trigger system catalog cache
if (columnStore)
gwi.csc->columnRIDs(
make_table(table_ptr->db.str, table_ptr->table_name.str, lower_case_table_names), true);
string table_name = table_ptr->table_name.str;
// @bug5523
if (table_ptr->db.length && strcmp(table_ptr->db.str, "information_schema") == 0)
table_name =
(table_ptr->schema_table_name.length ? table_ptr->schema_table_name.str : table_ptr->alias.str);
CalpontSystemCatalog::TableAliasName tn =
make_aliasview(table_ptr->db.str, table_name, table_ptr->alias.str, viewName, columnStore,
lower_case_table_names);
gwi.tbList.push_back(tn);
CalpontSystemCatalog::TableAliasName tan = make_aliastable(
table_ptr->db.str, table_name, table_ptr->alias.str, columnStore, lower_case_table_names);
gwi.tableMap[tan] = make_pair(0, table_ptr);
#ifdef DEBUG_WALK_COND
cerr << tn << endl;
#endif
}
}
if (gwi.fatalParseError)
{
setError(gwi.thd, ER_INTERNAL_ERROR, gwi.parseErrorText, gwi);
return ER_INTERNAL_ERROR;
}
}
catch (IDBExcept& ie)
{
setError(gwi.thd, ER_INTERNAL_ERROR, ie.what(), gwi);
CalpontSystemCatalog::removeCalpontSystemCatalog(gwi.sessionid);
// @bug 3852. set error status for gwi.
gwi.fatalParseError = true;
gwi.parseErrorText = ie.what();
return ER_INTERNAL_ERROR;
}
catch (...)
{
string emsg = IDBErrorInfo::instance()->errorMsg(ERR_LOST_CONN_EXEMGR);
// @bug3852 set error status for gwi.
gwi.fatalParseError = true;
gwi.parseErrorText = emsg;
setError(gwi.thd, ER_INTERNAL_ERROR, emsg, gwi);
CalpontSystemCatalog::removeCalpontSystemCatalog(gwi.sessionid);
return ER_INTERNAL_ERROR;
}
csep->tableList(gwi.tbList);
// Send this recursively to getSelectPlan
bool unionSel = false;
// UNION master unit check
// Existed pushdown handlers won't get in this scope
// MDEV-25080 Union pushdown would enter this scope
// is_unit_op() give a segv for derived_handler's SELECT_LEX
// check INTERSECT or EXCEPT, that are not implemented
if (select_lex.master_unit() && select_lex.master_unit()->first_select())
{
for (auto nextSelect = select_lex.master_unit()->first_select()->next_select(); nextSelect;
nextSelect = nextSelect->next_select())
{
if (nextSelect->get_linkage() == INTERSECT_TYPE)
{
setError(gwi.thd, ER_INTERNAL_ERROR, "INTERSECT is not supported by Columnstore engine", gwi);
return ER_INTERNAL_ERROR;
}
else if (nextSelect->get_linkage() == EXCEPT_TYPE)
{
setError(gwi.thd, ER_INTERNAL_ERROR, "EXCEPT is not supported by Columnstore engine", gwi);
return ER_INTERNAL_ERROR;
}
}
}
if (!isUnion && (!isSelectHandlerTop || isSelectLexUnit) && select_lex.master_unit()->is_unit_op())
{
// MCOL-2178 isUnion member only assigned, never used
// MIGR::infinidb_vtable.isUnion = true;
CalpontSelectExecutionPlan::SelectList unionVec;
SELECT_LEX* select_cursor = select_lex.master_unit()->first_select();
unionSel = true;
uint8_t distUnionNum = 0;
for (SELECT_LEX* sl = select_cursor; sl; sl = sl->next_select())
{
SCSEP plan(new CalpontSelectExecutionPlan());
plan->txnID(csep->txnID());
plan->verID(csep->verID());
plan->sessionID(csep->sessionID());
plan->traceFlags(csep->traceFlags());
plan->data(csep->data());
// gwi for the union unit
gp_walk_info union_gwi(gwi.timeZone, gwi.subQueriesChain);
union_gwi.thd = gwi.thd;
uint32_t err = 0;
if ((err = getSelectPlan(union_gwi, *sl, plan, unionSel)) != 0)
return err;
unionVec.push_back(SCEP(plan));
// distinct union num
if (sl == select_lex.master_unit()->union_distinct)
distUnionNum = unionVec.size();
}
csep->unionVec(unionVec);
csep->distinctUnionNum(distUnionNum);
}
return 0;
}
/*@brief Process WHERE part of the query or sub-query */
/***********************************************************
* DESCRIPTION:
* This function processes conditions from either JOIN->conds
* or SELECT_LEX->where|prep_where
* RETURNS
* error id as an int
***********************************************************/
int processWhere(SELECT_LEX& select_lex, gp_walk_info& gwi, SCSEP& csep, const std::vector<COND*>& condStack)
{
JOIN* join = select_lex.join;
Item* icp = 0;
bool isUpdateDelete = false;
// Flag to indicate if this is a prepared statement
bool isPS = gwi.thd->stmt_arena && gwi.thd->stmt_arena->is_stmt_execute();
if (join != 0 && !isPS)
icp = join->conds;
else if (isPS && select_lex.prep_where)
icp = select_lex.prep_where;
// if icp is null, try to find the where clause other where
if (!join && gwi.thd->lex->derived_tables)
{
if (select_lex.prep_where)
icp = select_lex.prep_where;
else if (select_lex.where)
icp = select_lex.where;
}
else if (!join && isUpdateOrDeleteStatement(gwi.thd->lex->sql_command))
{
isUpdateDelete = true;
}
if (icp)
{
// MariaDB bug 624 - without the fix_fields call, delete with join may error with "No query step".
//@bug 3039. fix fields for constants
if (!icp->fixed())
{
icp->fix_fields(gwi.thd, (Item**)&icp);
}
gwi.fatalParseError = false;
#ifdef DEBUG_WALK_COND
std::cerr << "------------------ WHERE -----------------------" << std::endl;
icp->traverse_cond(debug_walk, &gwi, Item::POSTFIX);
std::cerr << "------------------------------------------------\n" << std::endl;
#endif
icp->traverse_cond(gp_walk, &gwi, Item::POSTFIX);
if (gwi.fatalParseError)
{
return setErrorAndReturn(gwi);
}
}
else if (isUpdateDelete)
{
// MCOL-4023 For updates/deletes, we iterate over the pushed down condStack
if (!condStack.empty())
{
std::vector<COND*>::const_iterator condStackIter = condStack.begin();
while (condStackIter != condStack.end())
{
COND* cond = *condStackIter++;
cond->traverse_cond(gp_walk, &gwi, Item::POSTFIX);
if (gwi.fatalParseError)
{
return setErrorAndReturn(gwi);
}
}
}
// if condStack is empty(), check the select_lex for where conditions
// as a last resort
else if ((icp = select_lex.where) != 0)
{
icp->traverse_cond(gp_walk, &gwi, Item::POSTFIX);
if (gwi.fatalParseError)
{
return setErrorAndReturn(gwi);
}
}
}
else if (join && join->zero_result_cause)
{
gwi.rcWorkStack.push(new ConstantColumn((int64_t)0, ConstantColumn::NUM));
(dynamic_cast<ConstantColumn*>(gwi.rcWorkStack.top()))->timeZone(gwi.timeZone);
}
for (Item* item : gwi.condList)
{
if (item && (item != icp))
{
item->traverse_cond(gp_walk, &gwi, Item::POSTFIX);
if (gwi.fatalParseError)
{
return setErrorAndReturn(gwi);
}
}
}
// ZZ - the followinig debug shows the structure of nested outer join. should
// use a recursive function.
#ifdef OUTER_JOIN_DEBUG
List<TABLE_LIST>* tables = &(select_lex.top_join_list);
List_iterator_fast<TABLE_LIST> ti(*tables);
TABLE_LIST** table = (TABLE_LIST**)gwi.thd->alloc(sizeof(TABLE_LIST*) * tables->elements);
for (TABLE_LIST** t = table + (tables->elements - 1); t >= table; t--)
*t = ti++;
DBUG_ASSERT(tables->elements >= 1);
TABLE_LIST** end = table + tables->elements;
for (TABLE_LIST** tbl = table; tbl < end; tbl++)
{
TABLE_LIST* curr;
while ((curr = ti++))
{
TABLE_LIST* curr = *tbl;
if (curr->table_name.length)
cerr << curr->table_name.str << " ";
else
cerr << curr->alias.str << endl;
if (curr->outer_join)
cerr << " is inner table" << endl;
else if (curr->straight)
cerr << "straight_join" << endl;
else
cerr << "join" << endl;
if (curr->nested_join)
{
List<TABLE_LIST>* inners = &(curr->nested_join->join_list);
List_iterator_fast<TABLE_LIST> li(*inners);
TABLE_LIST** inner = (TABLE_LIST**)gwi.thd->alloc(sizeof(TABLE_LIST*) * inners->elements);
for (TABLE_LIST** t = inner + (inners->elements - 1); t >= inner; t--)
*t = li++;
TABLE_LIST** end1 = inner + inners->elements;
for (TABLE_LIST** tb = inner; tb < end1; tb++)
{
TABLE_LIST* curr1 = *tb;
cerr << curr1->alias.str << endl;
if (curr1->sj_on_expr)
{
curr1->sj_on_expr->traverse_cond(debug_walk, &gwi, Item::POSTFIX);
}
}
}
if (curr->sj_on_expr)
{
curr->sj_on_expr->traverse_cond(debug_walk, &gwi, Item::POSTFIX);
}
}
}
#endif
uint32_t failed = 0;
// InfiniDB bug5764 requires outer joins to be appended to the
// end of the filter list. This causes outer join filters to
// have a higher join id than inner join filters.
// TODO MCOL-4680 Figure out why this is the case, and possibly
// eliminate this requirement.
std::stack<execplan::ParseTree*> outerJoinStack;
if ((failed = buildJoin(gwi, select_lex.top_join_list, outerJoinStack)))
{
while (!outerJoinStack.empty())
{
delete outerJoinStack.top();
outerJoinStack.pop();
}
return failed;
}
if (gwi.subQuery)
{
for (uint i = 0; i < gwi.viewList.size(); i++)
{
if ((failed = gwi.viewList[i]->processJoin(gwi, outerJoinStack)))
{
while (!outerJoinStack.empty())
{
delete outerJoinStack.top();
outerJoinStack.pop();
}
return failed;
}
}
}
ParseTree* filters = NULL;
ParseTree* outerJoinFilters = NULL;
ParseTree* ptp = NULL;
ParseTree* rhs = NULL;
// @bug 2932. for "select * from region where r_name" case. if icp not null and
// ptWorkStack empty, the item is in rcWorkStack.
// MySQL 5.6 (MariaDB?). when icp is null and zero_result_cause is set, a constant 0
// is pushed to rcWorkStack.
if (gwi.ptWorkStack.empty() && !gwi.rcWorkStack.empty())
{
gwi.ptWorkStack.push(new ParseTree(gwi.rcWorkStack.top()));
gwi.rcWorkStack.pop();
}
while (!gwi.ptWorkStack.empty())
{
filters = gwi.ptWorkStack.top();
gwi.ptWorkStack.pop();
if (gwi.ptWorkStack.empty())
break;
ptp = new ParseTree(new LogicOperator("and"));
ptp->left(filters);
rhs = gwi.ptWorkStack.top();
gwi.ptWorkStack.pop();
ptp->right(rhs);
gwi.ptWorkStack.push(ptp);
}
while (!outerJoinStack.empty())
{
outerJoinFilters = outerJoinStack.top();
outerJoinStack.pop();
if (outerJoinStack.empty())
break;
ptp = new ParseTree(new LogicOperator("and"));
ptp->left(outerJoinFilters);
rhs = outerJoinStack.top();
outerJoinStack.pop();
ptp->right(rhs);
outerJoinStack.push(ptp);
}
config::Config* cf = config::Config::makeConfig();
string rewriteEnabled = cf->getConfig("Rewrites", "CommonLeafConjunctionsToTop");
if (filters && rewriteEnabled != "OFF")
{
filters = extractCommonLeafConjunctionsToRoot(filters);
}
uint64_t limit = get_max_allowed_in_values(gwi.thd);
if (filters && !checkFiltersLimit(filters, limit))
{
gwi.fatalParseError = true;
setError(gwi.thd, ER_CHECK_NOT_IMPLEMENTED,
" in clauses of this length. Number of values in the IN clause exceeded "
"columnstore_max_allowed_in_values "
"threshold.",
gwi);
delete filters;
return ER_CHECK_NOT_IMPLEMENTED;
}
// Append outer join filters at the end of inner join filters.
// JLF_ExecPlanToJobList::walkTree processes ParseTree::left
// before ParseTree::right which is what we intend to do in the
// below.
if (filters && outerJoinFilters)
{
ptp = new ParseTree(new LogicOperator("and"));
ptp->left(filters);
ptp->right(outerJoinFilters);
filters = ptp;
}
else if (outerJoinFilters)
{
filters = outerJoinFilters;
}
if (filters)
{
csep->filters(filters);
}
if (!gwi.rcWorkStack.empty())
{
while(!gwi.rcWorkStack.empty())
{
ReturnedColumn* t = gwi.rcWorkStack.top();
delete t;
gwi.rcWorkStack.pop();
}
}
if (!gwi.ptWorkStack.empty())
{
while(!gwi.ptWorkStack.empty())
{
ParseTree* t = gwi.ptWorkStack.top();
delete t;
gwi.ptWorkStack.pop();
}
}
return 0;
}
/*@brief Process LIMIT part of a query or sub-query */
/***********************************************************
* DESCRIPTION:
* Processes LIMIT and OFFSET parts
* RETURNS
* error id as an int
***********************************************************/
int processLimitAndOffset(SELECT_LEX& select_lex, gp_walk_info& gwi, SCSEP& csep, bool unionSel, bool isUnion,
bool isSelectHandlerTop)
{
// LIMIT processing part
uint64_t limitNum = std::numeric_limits<uint64_t>::max();
// non-MAIN union branch
if (unionSel || gwi.subSelectType != CalpontSelectExecutionPlan::MAIN_SELECT)
{
/* Consider the following query:
"select a from t1 where exists (select b from t2 where a=b);"
CS first builds a hash table for t2, then pushes down the hash to
PrimProc for a distributed hash join execution, with t1 being the
large-side table. However, the server applies an optimization in
Item_exists_subselect::fix_length_and_dec in sql/item_subselect.cc
(see server commit ae476868a5394041a00e75a29c7d45917e8dfae8)
where it sets explicit_limit to true, which causes csep->limitNum set to 1.
This causes the hash table for t2 to only contain a single record for the
hash join, giving less number of rows in the output result set than expected.
We therefore do not allow limit set to 1 here for such queries.
*/
if (gwi.subSelectType != CalpontSelectExecutionPlan::IN_SUBS &&
gwi.subSelectType != CalpontSelectExecutionPlan::EXISTS_SUBS &&
select_lex.master_unit()->global_parameters()->limit_params.explicit_limit)
{
if (select_lex.master_unit()->global_parameters()->limit_params.offset_limit)
{
Item_int* offset =
(Item_int*)select_lex.master_unit()->global_parameters()->limit_params.offset_limit;
csep->limitStart(offset->val_int());
}
if (select_lex.master_unit()->global_parameters()->limit_params.select_limit)
{
Item_int* select =
(Item_int*)select_lex.master_unit()->global_parameters()->limit_params.select_limit;
csep->limitNum(select->val_int());
// MCOL-894 Activate parallel ORDER BY
csep->orderByThreads(get_orderby_threads(gwi.thd));
}
}
}
// union with explicit select at the top level
else if (isUnion && select_lex.limit_params.explicit_limit)
{
if (select_lex.braces)
{
if (select_lex.limit_params.offset_limit)
csep->limitStart(((Item_int*)select_lex.limit_params.offset_limit)->val_int());
if (select_lex.limit_params.select_limit)
csep->limitNum(((Item_int*)select_lex.limit_params.select_limit)->val_int());
}
}
// other types of queries that have explicit LIMIT
else if (select_lex.limit_params.explicit_limit)
{
uint32_t limitOffset = 0;
if (select_lex.join)
{
JOIN* join = select_lex.join;
// @bug5729. After upgrade, join->unit sometimes is uninitialized pointer
// (not null though) and will cause seg fault. Prefer checking
// select_lex->limit_params.offset_limit if not null.
if (join->select_lex && join->select_lex->limit_params.offset_limit &&
join->select_lex->limit_params.offset_limit->fixed() &&
join->select_lex->limit_params.select_limit && join->select_lex->limit_params.select_limit->fixed())
{
limitOffset = join->select_lex->limit_params.offset_limit->val_int();
limitNum = join->select_lex->limit_params.select_limit->val_int();
}
else if (join->unit)
{
limitOffset = join->unit->lim.get_offset_limit();
limitNum = join->unit->lim.get_select_limit() - limitOffset;
}
}
else
{
if (select_lex.master_unit()->global_parameters()->limit_params.offset_limit)
{
Item_int* offset =
(Item_int*)select_lex.master_unit()->global_parameters()->limit_params.offset_limit;
limitOffset = offset->val_int();
}
if (select_lex.master_unit()->global_parameters()->limit_params.select_limit)
{
Item_int* select =
(Item_int*)select_lex.master_unit()->global_parameters()->limit_params.select_limit;
limitNum = select->val_int();
}
}
csep->limitStart(limitOffset);
csep->limitNum(limitNum);
}
// If an explicit limit is not specified, use the system variable value
else
{
csep->limitNum(gwi.thd->variables.select_limit);
}
// We don't currently support limit with correlated subquery
if (csep->limitNum() != (uint64_t)-1 && gwi.subQuery && !gwi.correlatedTbNameVec.empty())
{
gwi.fatalParseError = true;
gwi.parseErrorText = IDBErrorInfo::instance()->errorMsg(ERR_NON_SUPPORT_LIMIT_SUB);
setError(gwi.thd, ER_INTERNAL_ERROR, gwi.parseErrorText, gwi);
return ER_CHECK_NOT_IMPLEMENTED;
}
return 0;
}
/*@brief Create in-to-exists predicate for an IN subquery */
/***********************************************************
* DESCRIPTION:
* This function processes the lhs and rhs of an IN predicate
* for a query such as:
* select col1 from t1 where col2 in (select col2' from t2);
* here, lhs is col2 and rhs is the in subquery "select col2' from t2".
* It creates a new predicate of the form "col2=col2'" which then later
* gets injected into the execution plan of the subquery.
* If lhs is of type Item::ROW_ITEM instead, such as:
* select col1 from t1 where (col2,col3) in (select col2',col3' from t2);
* the function builds an "and" filter of the form "col2=col2' and col3=col3'".
* RETURNS
* none
***********************************************************/
void buildInToExistsFilter(gp_walk_info& gwi, SELECT_LEX& select_lex)
{
RowColumn* rlhs = dynamic_cast<RowColumn*>(gwi.inSubQueryLHS);
size_t additionalRetColsBefore = gwi.additionalRetCols.size();
if (rlhs)
{
idbassert(gwi.inSubQueryLHSItem->type() == Item::ROW_ITEM);
Item_row* row = (Item_row*)gwi.inSubQueryLHSItem;
idbassert(!rlhs->columnVec().empty() && (rlhs->columnVec().size() == gwi.returnedCols.size()) &&
row->cols() && (row->cols() == select_lex.item_list.elements) &&
(row->cols() == gwi.returnedCols.size()));
List_iterator_fast<Item> it(select_lex.item_list);
Item* item;
int i = 0;
ParseTree* rowFilter = nullptr;
while ((item = it++))
{
boost::shared_ptr<Operator> sop(new PredicateOperator("="));
vector<Item*> itemList = {row->element_index(i), item};
ReturnedColumn* rhs = gwi.returnedCols[i]->clone();
buildEqualityPredicate(rlhs->columnVec()[i]->clone(), rhs, &gwi, sop, Item_func::EQ_FUNC, itemList,
true);
if (gwi.fatalParseError)
{
delete rlhs;
return;
}
ParseTree* tmpFilter = nullptr;
if (!gwi.ptWorkStack.empty())
{
tmpFilter = gwi.ptWorkStack.top();
gwi.ptWorkStack.pop();
}
if (i == 0 && tmpFilter)
{
rowFilter = tmpFilter;
}
else if (i != 0 && tmpFilter && rowFilter)
{
ParseTree* ptp = new ParseTree(new LogicOperator("and"));
ptp->left(rowFilter);
ptp->right(tmpFilter);
rowFilter = ptp;
}
i++;
}
delete rlhs;
if (rowFilter)
gwi.ptWorkStack.push(rowFilter);
}
else
{
idbassert((gwi.returnedCols.size() == 1) && (select_lex.item_list.elements == 1));
boost::shared_ptr<Operator> sop(new PredicateOperator("="));
vector<Item*> itemList = {gwi.inSubQueryLHSItem, select_lex.item_list.head()};
ReturnedColumn* rhs = gwi.returnedCols[0]->clone();
buildEqualityPredicate(gwi.inSubQueryLHS, rhs, &gwi, sop, Item_func::EQ_FUNC, itemList, true);
if (gwi.fatalParseError)
return;
}
size_t additionalRetColsAdded = gwi.additionalRetCols.size() - additionalRetColsBefore;
if (gwi.returnedCols.size() && (gwi.returnedCols.size() == additionalRetColsAdded))
{
for (size_t i = 0; i < gwi.returnedCols.size(); i++)
{
gwi.returnedCols[i]->expressionId(gwi.additionalRetCols[additionalRetColsBefore + i]->expressionId());
gwi.returnedCols[i]->colSource(gwi.additionalRetCols[additionalRetColsBefore + i]->colSource());
}
// Delete the duplicate copy of the returned cols
auto iter = gwi.additionalRetCols.begin();
std::advance(iter, additionalRetColsBefore);
gwi.additionalRetCols.erase(iter, gwi.additionalRetCols.end());
}
}
/*@brief Translates SELECT_LEX into CSEP */
/***********************************************************
* DESCRIPTION:
* This function takes SELECT_LEX and tries to produce
* a corresponding CSEP out of it. It is made of parts that
* process parts of the query, e.g. FROM, WHERE, SELECT,
* HAVING, GROUP BY, ORDER BY. FROM, WHERE, LIMIT are processed
* by corresponding methods. CS calls getSelectPlan()
* recursively to process subqueries.
* ARGS
* isUnion if true CS processes UNION unit now
* isSelectHandlerTop removes offset at the top of SH query.
* RETURNS
* error id as an int
***********************************************************/
int getSelectPlan(gp_walk_info& gwi, SELECT_LEX& select_lex, SCSEP& csep, bool isUnion,
bool isSelectHandlerTop, bool isSelectLexUnit, const std::vector<COND*>& condStack)
{
#ifdef DEBUG_WALK_COND
cerr << "getSelectPlan()" << endl;
#endif
int rc = 0;
// rollup is currently not supported
bool withRollup = select_lex.olap == ROLLUP_TYPE;
setExecutionParams(gwi, csep);
gwi.subSelectType = csep->subType();
uint32_t sessionID = csep->sessionID();
gwi.sessionid = sessionID;
boost::shared_ptr<CalpontSystemCatalog> csc = CalpontSystemCatalog::makeCalpontSystemCatalog(sessionID);
csc->identity(CalpontSystemCatalog::FE);
csep->timeZone(gwi.timeZone);
gwi.csc = csc;
CalpontSelectExecutionPlan::SelectList derivedTbList;
// @bug 1796. Remember table order on the FROM list.
gwi.clauseType = FROM;
if ((rc = processFrom(isUnion, select_lex, gwi, csep, isSelectHandlerTop, isSelectLexUnit)))
{
return rc;
}
gwi.clauseType = WHERE;
if ((rc = processWhere(select_lex, gwi, csep, condStack)))
{
return rc;
}
gwi.clauseType = SELECT;
SELECT_LEX* oldSelectLex = gwi.select_lex; // XXX: SZ: should it be restored in case of error return?
gwi.select_lex = &select_lex;
#ifdef DEBUG_WALK_COND
{
cerr << "------------------- SELECT --------------------" << endl;
List_iterator_fast<Item> it(select_lex.item_list);
Item* item;
while ((item = it++))
{
debug_walk(item, 0);
}
cerr << "-----------------------------------------------\n" << endl;
}
#endif
// analyze SELECT and ORDER BY parts - do they have implicit GROUP BY induced by aggregates?
{
if (select_lex.group_list.first)
{
// we have an explicit GROUP BY.
gwi.implicitExplicitGroupBy = true;
}
else
{
// do we have an implicit GROUP BY?
List_iterator_fast<Item> it(select_lex.item_list);
Item* item;
while ((item = it++))
{
analyzeForImplicitGroupBy(item, gwi);
}
SQL_I_List<ORDER> order_list = select_lex.order_list;
ORDER* ordercol = static_cast<ORDER*>(order_list.first);
for (; ordercol; ordercol = ordercol->next)
{
analyzeForImplicitGroupBy(*(ordercol->item), gwi);
}
}
}
// populate returnedcolumnlist and columnmap
List_iterator_fast<Item> it(select_lex.item_list);
Item* item;
vector<Item_field*> funcFieldVec;
// empty rcWorkStack and ptWorkStack. They should all be empty by now.
clearStacks(gwi, false, true);
// indicate the starting pos of scalar returned column, because some join column
// has been inserted to the returned column list.
if (gwi.subQuery)
{
ScalarSub* scalar = dynamic_cast<ScalarSub*>(gwi.subQuery);
if (scalar)
scalar->returnedColPos(gwi.additionalRetCols.size());
}
CalpontSelectExecutionPlan::SelectList selectSubList;
while ((item = it++))
{
string itemAlias = (item->name.length ? item->name.str : "<NULL>");
// @bug 5916. Need to keep checking until getting concret item in case
// of nested view.
Item* baseItem = item;
while (item->type() == Item::REF_ITEM)
{
Item_ref* ref = (Item_ref*)item;
item = (*(ref->ref));
}
Item::Type itype = item->type();
switch (itype)
{
case Item::FIELD_ITEM:
{
Item_field* ifp = (Item_field*)item;
SimpleColumn* sc = NULL;
if (ifp->field_name.length && string(ifp->field_name.str) == "*")
{
collectAllCols(gwi, ifp);
break;
}
sc = buildSimpleColumn(ifp, gwi);
if (sc)
{
string fullname;
String str;
ifp->print(&str, QT_ORDINARY);
fullname = str.c_ptr();
if (!ifp->is_explicit_name()) // no alias
{
sc->alias(fullname);
}
else // alias
{
if (!itemAlias.empty())
sc->alias(itemAlias);
}
// We need to look into GROUP BY columns to decide if we need to wrap a column.
ReturnedColumn* rc = wrapIntoAggregate(sc, gwi, baseItem);
SRCP sprc(rc);
pushReturnedCol(gwi, baseItem, sprc);
gwi.columnMap.insert(
CalpontSelectExecutionPlan::ColumnMap::value_type(string(ifp->field_name.str), sprc));
TABLE_LIST* tmp = 0;
if (ifp->cached_table)
tmp = ifp->cached_table;
gwi.tableMap[make_aliastable(sc->schemaName(), sc->tableName(), sc->tableAlias(),
sc->isColumnStore())] = make_pair(1, tmp);
}
else
{
setError(gwi.thd, ER_INTERNAL_ERROR, gwi.parseErrorText, gwi);
delete sc;
return ER_INTERNAL_ERROR;
}
break;
}
// aggregate column
case Item::SUM_FUNC_ITEM:
{
ReturnedColumn* ac = buildAggregateColumn(item, gwi);
if (gwi.fatalParseError)
{
// e.g., non-support ref column
setError(gwi.thd, ER_CHECK_NOT_IMPLEMENTED, gwi.parseErrorText, gwi);
delete ac;
return ER_CHECK_NOT_IMPLEMENTED;
}
// add this agg col to returnedColumnList
boost::shared_ptr<ReturnedColumn> spac(ac);
pushReturnedCol(gwi, item, spac);
break;
}
case Item::FUNC_ITEM:
{
Item_func* ifp = static_cast<Item_func*>(item);
// @bug4383. error out non-support stored function
if (ifp->functype() == Item_func::FUNC_SP)
{
gwi.fatalParseError = true;
gwi.parseErrorText = IDBErrorInfo::instance()->errorMsg(ERR_SP_FUNCTION_NOT_SUPPORT);
setError(gwi.thd, ER_CHECK_NOT_IMPLEMENTED, gwi.parseErrorText, gwi);
return ER_CHECK_NOT_IMPLEMENTED;
}
if (string(ifp->func_name()) == "xor")
{
gwi.fatalParseError = true;
gwi.parseErrorText = IDBErrorInfo::instance()->errorMsg(ERR_FILTER_COND_EXP);
setError(gwi.thd, ER_CHECK_NOT_IMPLEMENTED, gwi.parseErrorText, gwi);
return ER_CHECK_NOT_IMPLEMENTED;
}
uint16_t parseInfo = 0;
vector<Item_field*> tmpVec;
bool hasNonSupportItem = false;
parse_item(ifp, tmpVec, hasNonSupportItem, parseInfo, &gwi);
if (ifp->with_subquery() || string(ifp->func_name()) == string("<in_optimizer>") ||
ifp->functype() == Item_func::NOT_ALL_FUNC || parseInfo & SUB_BIT)
{
gwi.fatalParseError = true;
gwi.parseErrorText = IDBErrorInfo::instance()->errorMsg(ERR_NON_SUPPORT_SELECT_SUB);
setError(gwi.thd, ER_CHECK_NOT_IMPLEMENTED, gwi.parseErrorText, gwi);
return ER_CHECK_NOT_IMPLEMENTED;
}
// if "IN" or "BETWEEN" are in the SELECT clause, build function column
string funcName = ifp->func_name();
ReturnedColumn* rc;
if (funcName == "in" || funcName == " IN " || funcName == "between")
{
rc = buildFunctionColumn(ifp, gwi, hasNonSupportItem, true);
}
else
{
rc = buildFunctionColumn(ifp, gwi, hasNonSupportItem);
}
SRCP srcp(rc);
if (rc)
{
// MCOL-2178 CS has to process determenistic functions with constant arguments.
if (!hasNonSupportItem && ifp->const_item() && !(parseInfo & AF_BIT) && tmpVec.size() == 0)
{
srcp.reset(buildReturnedColumn(item, gwi, gwi.fatalParseError));
pushReturnedCol(gwi, item, srcp);
if (ifp->name.length)
srcp->alias(ifp->name.str);
continue;
}
pushReturnedCol(gwi, item, srcp);
}
else // This was a vtable post-process block
{
hasNonSupportItem = false;
uint32_t before_size = funcFieldVec.size();
parse_item(ifp, funcFieldVec, hasNonSupportItem, parseInfo, &gwi);
uint32_t after_size = funcFieldVec.size();
// pushdown handler projection functions
// @bug3881. set_user_var can not be treated as constant function
// @bug5716. Try to avoid post process function for union query.
if (!hasNonSupportItem && (after_size - before_size) == 0 && !(parseInfo & AGG_BIT) &&
!(parseInfo & SUB_BIT))
{
ConstantColumn* cc = buildConstantColumnMaybeNullUsingValStr(ifp, gwi);
SRCP srcp(cc);
if (ifp->name.length)
cc->alias(ifp->name.str);
pushReturnedCol(gwi, ifp, srcp);
// clear the error set by buildFunctionColumn
gwi.fatalParseError = false;
gwi.parseErrorText = "";
break;
}
else if (hasNonSupportItem || parseInfo & AGG_BIT || parseInfo & SUB_BIT ||
(gwi.fatalParseError && gwi.subQuery))
{
if (gwi.parseErrorText.empty())
{
Message::Args args;
args.add(ifp->func_name());
gwi.parseErrorText = IDBErrorInfo::instance()->errorMsg(ERR_NON_SUPPORTED_FUNCTION, args);
}
setError(gwi.thd, ER_CHECK_NOT_IMPLEMENTED, gwi.parseErrorText, gwi);
return ER_CHECK_NOT_IMPLEMENTED;
}
else if (gwi.subQuery && (isPredicateFunction(ifp, &gwi) || ifp->type() == Item::COND_ITEM))
{
gwi.fatalParseError = true;
gwi.parseErrorText = IDBErrorInfo::instance()->errorMsg(ERR_FILTER_COND_EXP);
setError(gwi.thd, ER_CHECK_NOT_IMPLEMENTED, gwi.parseErrorText, gwi);
return ER_CHECK_NOT_IMPLEMENTED;
}
//@Bug 3030 Add error check for dml statement
if (isUpdateOrDeleteStatement(gwi.thd->lex->sql_command))
{
if (after_size - before_size != 0)
{
gwi.parseErrorText = ifp->func_name();
return -1;
}
}
else
{
Message::Args args;
args.add(ifp->func_name());
gwi.parseErrorText = IDBErrorInfo::instance()->errorMsg(ERR_NON_SUPPORTED_FUNCTION, args);
setError(gwi.thd, ER_CHECK_NOT_IMPLEMENTED, gwi.parseErrorText, gwi);
return ER_CHECK_NOT_IMPLEMENTED;
}
}
break;
} // End of FUNC_ITEM
// DRRTUY Replace the whole section with typeid() checks or use
// static_cast here
case Item::CONST_ITEM:
{
switch (item->cmp_type())
{
case INT_RESULT:
case STRING_RESULT:
case DECIMAL_RESULT:
case REAL_RESULT:
case TIME_RESULT:
{
if (isUpdateOrDeleteStatement(gwi.thd->lex->sql_command))
{
}
else
{
// do not push the dummy column (mysql added) to returnedCol
if (item->name.length && string(item->name.str) == "Not_used")
continue;
// @bug3509. Constant column is sent to ExeMgr now.
SRCP srcp(buildReturnedColumn(item, gwi, gwi.fatalParseError));
if (item->name.length)
srcp->alias(item->name.str);
pushReturnedCol(gwi, item, srcp);
}
break;
}
// MCOL-2178 This switch doesn't handl
// ROW_
default:
{
IDEBUG(cerr << "Warning unsupported cmp_type() in projection" << endl);
// noop
}
}
break;
} // CONST_ITEM ends here
case Item::NULL_ITEM:
{
if (isUpdateOrDeleteStatement(gwi.thd->lex->sql_command))
{
}
else
{
SRCP srcp(buildReturnedColumn(item, gwi, gwi.fatalParseError));
pushReturnedCol(gwi, item, srcp);
if (item->name.length)
srcp->alias(item->name.str);
}
break;
}
case Item::SUBSELECT_ITEM:
{
Item_subselect* sub = (Item_subselect*)item;
if (sub->substype() != Item_subselect::SINGLEROW_SUBS)
{
gwi.fatalParseError = true;
gwi.parseErrorText = IDBErrorInfo::instance()->errorMsg(ERR_NON_SUPPORT_SELECT_SUB);
setError(gwi.thd, ER_CHECK_NOT_IMPLEMENTED, gwi.parseErrorText, gwi);
return ER_CHECK_NOT_IMPLEMENTED;
}
#ifdef DEBUG_WALK_COND
cerr << "SELECT clause SUBSELECT Item: " << sub->substype() << endl;
JOIN* join = sub->get_select_lex()->join;
if (join)
{
Item_cond* cond = static_cast<Item_cond*>(join->conds);
if (cond)
cond->traverse_cond(debug_walk, &gwi, Item::POSTFIX);
}
cerr << "Finish SELECT clause subselect item traversing" << endl;
#endif
SelectSubQuery* selectSub = new SelectSubQuery(gwi, sub);
// selectSub->gwip(&gwi);
SCSEP ssub = selectSub->transform();
if (!ssub || gwi.fatalParseError)
{
if (gwi.parseErrorText.empty())
gwi.parseErrorText = "Unsupported Item in SELECT subquery.";
setError(gwi.thd, ER_CHECK_NOT_IMPLEMENTED, gwi.parseErrorText, gwi);
return ER_CHECK_NOT_IMPLEMENTED;
}
selectSubList.push_back(ssub);
SimpleColumn* rc = new SimpleColumn();
rc->colSource(rc->colSource() | SELECT_SUB);
rc->timeZone(gwi.timeZone);
if (sub->get_select_lex()->get_table_list())
{
rc->viewName(getViewName(sub->get_select_lex()->get_table_list()), lower_case_table_names);
}
if (sub->name.length)
rc->alias(sub->name.str);
gwi.returnedCols.push_back(SRCP(rc));
break;
}
case Item::COND_ITEM:
{
gwi.fatalParseError = true;
gwi.parseErrorText = IDBErrorInfo::instance()->errorMsg(ERR_FILTER_COND_EXP);
setError(gwi.thd, ER_CHECK_NOT_IMPLEMENTED, gwi.parseErrorText, gwi);
return ER_CHECK_NOT_IMPLEMENTED;
}
case Item::EXPR_CACHE_ITEM:
{
printf("EXPR_CACHE_ITEM in getSelectPlan\n");
gwi.fatalParseError = true;
gwi.parseErrorText = IDBErrorInfo::instance()->errorMsg(ERR_UNKNOWN_COL);
setError(gwi.thd, ER_CHECK_NOT_IMPLEMENTED, gwi.parseErrorText, gwi);
return ER_CHECK_NOT_IMPLEMENTED;
}
case Item::WINDOW_FUNC_ITEM:
{
SRCP srcp(buildWindowFunctionColumn(item, gwi, gwi.fatalParseError));
if (!srcp || gwi.fatalParseError)
{
if (gwi.parseErrorText.empty())
gwi.parseErrorText = "Unsupported Item in SELECT subquery.";
setError(gwi.thd, ER_CHECK_NOT_IMPLEMENTED, gwi.parseErrorText, gwi);
return ER_CHECK_NOT_IMPLEMENTED;
}
pushReturnedCol(gwi, item, srcp);
break;
}
case Item::TYPE_HOLDER:
{
if (!gwi.tbList.size())
{
gwi.parseErrorText = "subquery with VALUES";
gwi.fatalParseError = true;
setError(gwi.thd, ER_CHECK_NOT_IMPLEMENTED, gwi.parseErrorText, gwi);
return ER_CHECK_NOT_IMPLEMENTED;
}
else
{
std::cerr << "********** received TYPE_HOLDER *********" << std::endl;
}
break;
}
default:
{
break;
}
}
}
// @bug4388 normalize the project coltypes for union main select list
if (!csep->unionVec().empty())
{
unsigned int unionedTypeRc = 0;
for (uint32_t i = 0; i < gwi.returnedCols.size(); i++)
{
vector<CalpontSystemCatalog::ColType> coltypes;
for (uint32_t j = 0; j < csep->unionVec().size(); j++)
{
CalpontSelectExecutionPlan* unionCsep =
dynamic_cast<CalpontSelectExecutionPlan*>(csep->unionVec()[j].get());
coltypes.push_back(unionCsep->returnedCols()[i]->resultType());
// @bug5976. set hasAggregate true for the main column if
// one corresponding union column has aggregate
if (unionCsep->returnedCols()[i]->hasAggregate())
gwi.returnedCols[i]->hasAggregate(true);
}
gwi.returnedCols[i]->resultType(
CalpontSystemCatalog::ColType::convertUnionColType(coltypes, unionedTypeRc));
if (unionedTypeRc != 0)
{
gwi.parseErrorText = IDBErrorInfo::instance()->errorMsg(unionedTypeRc);
setError(gwi.thd, ER_CHECK_NOT_IMPLEMENTED, gwi.parseErrorText, gwi);
return ER_CHECK_NOT_IMPLEMENTED;
}
}
}
// Having clause handling
gwi.clauseType = HAVING;
gwi.havingDespiteSelect = true;
clearStacks(gwi, false, true);
std::unique_ptr<ParseTree> havingFilter;
// clear fatalParseError that may be left from post process functions
gwi.fatalParseError = false;
gwi.parseErrorText = "";
gwi.disableWrapping = false;
gwi.havingDespiteSelect = true;
if (select_lex.having != 0)
{
#ifdef DEBUG_WALK_COND
cerr << "------------------- HAVING ---------------------" << endl;
select_lex.having->traverse_cond(debug_walk, &gwi, Item::POSTFIX);
cerr << "------------------------------------------------\n" << endl;
#endif
select_lex.having->traverse_cond(gp_walk, &gwi, Item::POSTFIX);
if (gwi.fatalParseError)
{
setError(gwi.thd, ER_INTERNAL_ERROR, gwi.parseErrorText, gwi);
return ER_INTERNAL_ERROR;
}
ParseTree* ptp = 0;
ParseTree* rhs = 0;
// @bug 4215. some function filter will be in the rcWorkStack.
if (gwi.ptWorkStack.empty() && !gwi.rcWorkStack.empty())
{
gwi.ptWorkStack.push(new ParseTree(gwi.rcWorkStack.top()));
gwi.rcWorkStack.pop();
}
while (!gwi.ptWorkStack.empty())
{
havingFilter.reset(gwi.ptWorkStack.top());
gwi.ptWorkStack.pop();
if (gwi.ptWorkStack.empty())
break;
ptp = new ParseTree(new LogicOperator("and"));
ptp->left(havingFilter.release());
rhs = gwi.ptWorkStack.top();
gwi.ptWorkStack.pop();
ptp->right(rhs);
gwi.ptWorkStack.push(ptp);
}
}
gwi.havingDespiteSelect = false;
gwi.disableWrapping = false;
// MCOL-4617 If this is an IN subquery, then create the in-to-exists
// predicate and inject it into the csep
if (gwi.subQuery && gwi.subSelectType == CalpontSelectExecutionPlan::IN_SUBS && gwi.inSubQueryLHS &&
gwi.inSubQueryLHSItem)
{
// create the predicate
buildInToExistsFilter(gwi, select_lex);
if (gwi.fatalParseError)
{
setError(gwi.thd, ER_INTERNAL_ERROR, gwi.parseErrorText, gwi);
return ER_INTERNAL_ERROR;
}
// now inject the created predicate
if (!gwi.ptWorkStack.empty())
{
ParseTree* inToExistsFilter = gwi.ptWorkStack.top();
gwi.ptWorkStack.pop();
if (havingFilter)
{
ParseTree* ptp = new ParseTree(new LogicOperator("and"));
ptp->left(havingFilter.release());
ptp->right(inToExistsFilter);
havingFilter.reset(ptp);
}
else
{
if (csep->filters())
{
ParseTree* ptp = new ParseTree(new LogicOperator("and"));
ptp->left(csep->filters());
ptp->right(inToExistsFilter);
csep->filters(ptp);
}
else
{
csep->filters(inToExistsFilter);
}
}
}
}
// for post process expressions on the select list
// error out post process for union and sub select unit
if (isUnion || gwi.subSelectType != CalpontSelectExecutionPlan::MAIN_SELECT)
{
if (funcFieldVec.size() != 0 && !gwi.fatalParseError)
{
string emsg("Fatal parse error in vtable mode: Unsupported Items in union or sub select unit");
setError(gwi.thd, ER_CHECK_NOT_IMPLEMENTED, emsg);
return ER_CHECK_NOT_IMPLEMENTED;
}
}
for (uint32_t i = 0; i < funcFieldVec.size(); i++)
{
SimpleColumn* sc = buildSimpleColumn(funcFieldVec[i], gwi);
if (!sc || gwi.fatalParseError)
{
string emsg;
if (gwi.parseErrorText.empty())
{
emsg = "un-recognized column";
if (funcFieldVec[i]->name.length)
emsg += string(funcFieldVec[i]->name.str);
}
else
{
emsg = gwi.parseErrorText;
}
setError(gwi.thd, ER_INTERNAL_ERROR, emsg, gwi);
return ER_INTERNAL_ERROR;
}
String str;
funcFieldVec[i]->print(&str, QT_ORDINARY);
sc->alias(string(str.c_ptr()));
sc->tableAlias(sc->tableAlias());
SRCP srcp(wrapIntoAggregate(sc, gwi, funcFieldVec[i]));
uint32_t j = 0;
for (; j < gwi.returnedCols.size(); j++)
{
if (sc->sameColumn(gwi.returnedCols[j].get()))
{
SimpleColumn* field = dynamic_cast<SimpleColumn*>(gwi.returnedCols[j].get());
if (field && field->alias() == sc->alias())
break;
}
}
if (j == gwi.returnedCols.size())
{
gwi.returnedCols.push_back(srcp);
// XXX: SZ: deduplicate here?
gwi.columnMap.insert(
CalpontSelectExecutionPlan::ColumnMap::value_type(string(funcFieldVec[i]->field_name.str), srcp));
string fullname;
fullname = str.c_ptr();
TABLE_LIST* tmp = (funcFieldVec[i]->cached_table ? funcFieldVec[i]->cached_table : 0);
gwi.tableMap[make_aliastable(sc->schemaName(), sc->tableName(), sc->tableAlias(),
sc->isColumnStore())] = make_pair(1, tmp);
}
}
SRCP minSc; // min width projected column. for count(*) use
bool unionSel = (!isUnion && select_lex.master_unit()->is_unit_op()) ? true : false;
// Group by list. not valid for union main query
if (!unionSel)
{
gwi.clauseType = GROUP_BY;
Item* nonSupportItem = NULL;
ORDER* groupcol = static_cast<ORDER*>(select_lex.group_list.first);
// check if window functions are in order by. InfiniDB process order by list if
// window functions are involved, either in order by or projection.
bool hasWindowFunc = gwi.hasWindowFunc;
gwi.hasWindowFunc = false;
for (; groupcol; groupcol = groupcol->next)
{
if ((*(groupcol->item))->type() == Item::WINDOW_FUNC_ITEM)
gwi.hasWindowFunc = true;
}
if (gwi.hasWindowFunc)
{
gwi.fatalParseError = true;
gwi.parseErrorText = IDBErrorInfo::instance()->errorMsg(ERR_WF_NOT_ALLOWED, "GROUP BY clause");
setError(gwi.thd, ER_CHECK_NOT_IMPLEMENTED, gwi.parseErrorText, gwi);
return ER_CHECK_NOT_IMPLEMENTED;
}
gwi.hasWindowFunc = hasWindowFunc;
groupcol = static_cast<ORDER*>(select_lex.group_list.first);
gwi.disableWrapping = true;
for (; groupcol; groupcol = groupcol->next)
{
Item* groupItem = *(groupcol->item);
// @bug5993. Could be nested ref.
while (groupItem->type() == Item::REF_ITEM)
groupItem = (*((Item_ref*)groupItem)->ref);
if (groupItem->type() == Item::FUNC_ITEM)
{
Item_func* ifp = (Item_func*)groupItem;
// call buildFunctionColumn here mostly for finding out
// non-support column on GB list. Should be simplified.
ReturnedColumn* fc = buildFunctionColumn(ifp, gwi, gwi.fatalParseError);
if (!fc || gwi.fatalParseError)
{
nonSupportItem = ifp;
break;
}
if (groupcol->in_field_list && groupcol->counter_used)
{
delete fc;
fc = gwi.returnedCols[groupcol->counter - 1].get();
SRCP srcp(fc->clone());
// check if no column parm
for (uint32_t i = 0; i < gwi.no_parm_func_list.size(); i++)
{
if (gwi.no_parm_func_list[i]->expressionId() == fc->expressionId())
{
gwi.no_parm_func_list.push_back(dynamic_cast<FunctionColumn*>(srcp.get()));
break;
}
}
srcp->orderPos(groupcol->counter - 1);
gwi.groupByCols.push_back(srcp);
continue;
}
else if (groupItem->is_explicit_name()) // alias
{
uint32_t i = 0;
for (; i < gwi.returnedCols.size(); i++)
{
if (string(groupItem->name.str) == gwi.returnedCols[i]->alias())
{
ReturnedColumn* rc = gwi.returnedCols[i]->clone();
rc->orderPos(i);
gwi.groupByCols.push_back(SRCP(rc));
delete fc;
break;
}
}
if (i == gwi.returnedCols.size())
{
nonSupportItem = groupItem;
break;
}
}
else
{
uint32_t i = 0;
for (; i < gwi.returnedCols.size(); i++)
{
if (fc->operator==(gwi.returnedCols[i].get()))
{
ReturnedColumn* rc = gwi.returnedCols[i]->clone();
rc->orderPos(i);
gwi.groupByCols.push_back(SRCP(rc));
delete fc;
break;
}
}
if (i == gwi.returnedCols.size())
{
gwi.groupByCols.push_back(SRCP(fc));
break;
}
}
}
else if (groupItem->type() == Item::FIELD_ITEM)
{
Item_field* ifp = (Item_field*)groupItem;
// this GB col could be an alias of F&E on the SELECT clause, not necessarily a field.
ReturnedColumn* rc = buildSimpleColumn(ifp, gwi);
SimpleColumn* sc = dynamic_cast<SimpleColumn*>(rc);
if (sc)
{
bool found = false;
for (uint32_t j = 0; j < gwi.returnedCols.size(); j++)
{
if (sc->sameColumn(gwi.returnedCols[j].get()))
{
sc->orderPos(j);
found = true;
break;
}
}
for (uint32_t j = 0; !found && j < gwi.returnedCols.size(); j++)
{
if (strcasecmp(sc->alias().c_str(), gwi.returnedCols[j]->alias().c_str()) == 0)
{
delete rc;
rc = gwi.returnedCols[j].get()->clone();
rc->orderPos(j);
break;
}
}
}
else
{
for (uint32_t j = 0; j < gwi.returnedCols.size(); j++)
{
if (ifp->name.length && string(ifp->name.str) == gwi.returnedCols[j].get()->alias())
{
delete rc;
rc = gwi.returnedCols[j].get()->clone();
rc->orderPos(j);
break;
}
}
}
if (!rc)
{
nonSupportItem = ifp;
break;
}
SRCP srcp(rc);
// bug 3151
AggregateColumn* ac = dynamic_cast<AggregateColumn*>(rc);
if (ac)
{
nonSupportItem = ifp;
break;
}
gwi.groupByCols.push_back(srcp);
gwi.columnMap.insert(
CalpontSelectExecutionPlan::ColumnMap::value_type(string(ifp->field_name.str), srcp));
}
// @bug5638. The group by column is constant but not counter, alias has to match a column
// on the select list
else if (!groupcol->counter_used &&
(groupItem->type() == Item::CONST_ITEM &&
(groupItem->cmp_type() == INT_RESULT || groupItem->cmp_type() == STRING_RESULT ||
groupItem->cmp_type() == REAL_RESULT || groupItem->cmp_type() == DECIMAL_RESULT)))
{
ReturnedColumn* rc = 0;
for (uint32_t j = 0; j < gwi.returnedCols.size(); j++)
{
if (groupItem->name.length && string(groupItem->name.str) == gwi.returnedCols[j].get()->alias())
{
rc = gwi.returnedCols[j].get()->clone();
rc->orderPos(j);
break;
}
}
if (!rc)
{
nonSupportItem = groupItem;
break;
}
gwi.groupByCols.push_back(SRCP(rc));
}
else if ((*(groupcol->item))->type() == Item::SUBSELECT_ITEM)
{
if (!groupcol->in_field_list || !groupItem->name.length)
{
nonSupportItem = groupItem;
}
else
{
uint32_t i = 0;
for (; i < gwi.returnedCols.size(); i++)
{
if (string(groupItem->name.str) == gwi.returnedCols[i]->alias())
{
ReturnedColumn* rc = gwi.returnedCols[i]->clone();
rc->orderPos(i);
gwi.groupByCols.push_back(SRCP(rc));
break;
}
}
if (i == gwi.returnedCols.size())
{
nonSupportItem = groupItem;
}
}
}
// @bug 3761.
else if (groupcol->counter_used)
{
if (gwi.returnedCols.size() <= (uint32_t)(groupcol->counter - 1))
{
nonSupportItem = groupItem;
}
else
{
gwi.groupByCols.push_back(SRCP(gwi.returnedCols[groupcol->counter - 1]->clone()));
}
}
else
{
nonSupportItem = groupItem;
}
}
gwi.disableWrapping = false;
// @bug 4756. Add internal groupby column for correlated join to the groupby list
if (gwi.aggOnSelect && !gwi.subGroupByCols.empty())
gwi.groupByCols.insert(gwi.groupByCols.end(), gwi.subGroupByCols.begin(), gwi.subGroupByCols.end());
// this is window func on SELECT becuase ORDER BY has not been processed
if (!gwi.windowFuncList.empty() && !gwi.subGroupByCols.empty())
{
for (uint32_t i = 0; i < gwi.windowFuncList.size(); i++)
{
if (gwi.windowFuncList[i]->hasWindowFunc())
{
vector<WindowFunctionColumn*> windowFunctions = gwi.windowFuncList[i]->windowfunctionColumnList();
for (uint32_t j = 0; j < windowFunctions.size(); j++)
windowFunctions[j]->addToPartition(gwi.subGroupByCols);
}
}
}
if (nonSupportItem)
{
if (gwi.parseErrorText.length() == 0)
{
Message::Args args;
if (nonSupportItem->name.length)
args.add("'" + string(nonSupportItem->name.str) + "'");
else
args.add("");
gwi.parseErrorText = IDBErrorInfo::instance()->errorMsg(ERR_NON_SUPPORT_GROUP_BY, args);
}
setError(gwi.thd, ER_CHECK_NOT_IMPLEMENTED, gwi.parseErrorText, gwi);
return ER_CHECK_NOT_IMPLEMENTED;
}
if (withRollup)
{
SRCP rc(new RollupMarkColumn());
gwi.groupByCols.insert(gwi.groupByCols.end(), rc);
}
}
// ORDER BY processing
{
SQL_I_List<ORDER> order_list = select_lex.order_list;
ORDER* ordercol = static_cast<ORDER*>(order_list.first);
// check if window functions are in order by. InfiniDB process order by list if
// window functions are involved, either in order by or projection.
for (; ordercol; ordercol = ordercol->next)
{
if ((*(ordercol->item))->type() == Item::WINDOW_FUNC_ITEM)
gwi.hasWindowFunc = true;
// XXX: TODO: implement a proper analysis of what we support.
// MCOL-2166 Looking for this sorting item in GROUP_BY items list.
// Shouldn't look into this if query doesn't have GROUP BY or
// aggregations
if (select_lex.agg_func_used() && select_lex.group_list.first &&
!sortItemIsInGrouping(*ordercol->item, select_lex.group_list.first))
{
std::ostringstream ostream;
std::ostringstream& osr = ostream;
getColNameFromItem(osr, *ordercol->item);
Message::Args args;
args.add(ostream.str());
string emsg = IDBErrorInfo::instance()->errorMsg(ERR_NOT_SUPPORTED_GROUPBY_ORDERBY_EXPRESSION, args);
gwi.parseErrorText = emsg;
setError(gwi.thd, ER_INTERNAL_ERROR, emsg, gwi);
return ERR_NOT_SUPPORTED_GROUPBY_ORDERBY_EXPRESSION;
}
}
// re-visit the first of ordercol list
ordercol = static_cast<ORDER*>(order_list.first);
{
for (; ordercol; ordercol = ordercol->next)
{
ReturnedColumn* rc = NULL;
if (ordercol->in_field_list && ordercol->counter_used)
{
rc = gwi.returnedCols[ordercol->counter - 1]->clone();
rc->orderPos(ordercol->counter - 1);
// can not be optimized off if used in order by with counter.
// set with self derived table alias if it's derived table
gwi.returnedCols[ordercol->counter - 1]->incRefCount();
}
else
{
Item* ord_item = *(ordercol->item);
// ignore not_used column on order by.
if ((ord_item->type() == Item::CONST_ITEM && ord_item->cmp_type() == INT_RESULT) &&
ord_item->full_name() && !strcmp(ord_item->full_name(), "Not_used"))
{
continue;
}
else if (ord_item->type() == Item::CONST_ITEM && ord_item->cmp_type() == INT_RESULT)
{
// DRRTUY This section looks useless b/c there is no
// way to put constant INT into an ORDER BY list
rc = gwi.returnedCols[((Item_int*)ord_item)->val_int() - 1]->clone();
}
else if (ord_item->type() == Item::SUBSELECT_ITEM)
{
gwi.fatalParseError = true;
}
else if ((ord_item->type() == Item::FUNC_ITEM) &&
(((Item_func*)ord_item)->functype() == Item_func::COLLATE_FUNC))
{
push_warning(gwi.thd, Sql_condition::WARN_LEVEL_NOTE, WARN_OPTION_IGNORED,
"COLLATE is ignored in ColumnStore");
continue;
}
else
{
rc = buildReturnedColumn(ord_item, gwi, gwi.fatalParseError);
rc = wrapIntoAggregate(rc, gwi, ord_item);
}
// @bug5501 try item_ptr if item can not be fixed. For some
// weird dml statement state, item can not be fixed but the
// infomation is available in item_ptr.
if (!rc || gwi.fatalParseError)
{
Item* item_ptr = ordercol->item_ptr;
while (item_ptr->type() == Item::REF_ITEM)
item_ptr = *(((Item_ref*)item_ptr)->ref);
rc = buildReturnedColumn(item_ptr, gwi, gwi.fatalParseError);
}
if (!rc)
{
string emsg = IDBErrorInfo::instance()->errorMsg(ERR_NON_SUPPORT_ORDER_BY);
gwi.parseErrorText = emsg;
setError(gwi.thd, ER_CHECK_NOT_IMPLEMENTED, emsg, gwi);
return ER_CHECK_NOT_IMPLEMENTED;
}
}
if (ordercol->direction == ORDER::ORDER_ASC)
rc->asc(true);
else
rc->asc(false);
gwi.orderByCols.push_back(SRCP(rc));
}
}
// make sure columnmap, returnedcols and count(*) arg_list are not empty
TableMap::iterator tb_iter = gwi.tableMap.begin();
try
{
for (; tb_iter != gwi.tableMap.end(); tb_iter++)
{
if ((*tb_iter).second.first == 1)
continue;
CalpontSystemCatalog::TableAliasName tan = (*tb_iter).first;
CalpontSystemCatalog::TableName tn = make_table((*tb_iter).first.schema, (*tb_iter).first.table);
SimpleColumn* sc = getSmallestColumn(csc, tn, tan, (*tb_iter).second.second->table, gwi);
SRCP srcp(sc);
gwi.columnMap.insert(CalpontSelectExecutionPlan::ColumnMap::value_type(sc->columnName(), srcp));
(*tb_iter).second.first = 1;
}
}
catch (runtime_error& e)
{
setError(gwi.thd, ER_INTERNAL_ERROR, e.what(), gwi);
CalpontSystemCatalog::removeCalpontSystemCatalog(sessionID);
return ER_INTERNAL_ERROR;
}
catch (...)
{
string emsg = IDBErrorInfo::instance()->errorMsg(ERR_LOST_CONN_EXEMGR);
setError(gwi.thd, ER_INTERNAL_ERROR, emsg, gwi);
CalpontSystemCatalog::removeCalpontSystemCatalog(sessionID);
return ER_INTERNAL_ERROR;
}
if (!gwi.count_asterisk_list.empty() || !gwi.no_parm_func_list.empty() || gwi.returnedCols.empty())
{
// get the smallest column from colmap
CalpontSelectExecutionPlan::ColumnMap::const_iterator iter;
int minColWidth = 0;
CalpontSystemCatalog::ColType ct;
try
{
for (iter = gwi.columnMap.begin(); iter != gwi.columnMap.end(); ++iter)
{
// should always not null
SimpleColumn* sc = dynamic_cast<SimpleColumn*>(iter->second.get());
if (sc && !(sc->joinInfo() & JOIN_CORRELATED))
{
ct = csc->colType(sc->oid());
if (minColWidth == 0)
{
minColWidth = ct.colWidth;
minSc = iter->second;
}
else if (ct.colWidth < minColWidth)
{
minColWidth = ct.colWidth;
minSc = iter->second;
}
}
}
}
catch (...)
{
string emsg = IDBErrorInfo::instance()->errorMsg(ERR_LOST_CONN_EXEMGR);
setError(gwi.thd, ER_INTERNAL_ERROR, emsg, gwi);
CalpontSystemCatalog::removeCalpontSystemCatalog(sessionID);
return ER_INTERNAL_ERROR;
}
if (gwi.returnedCols.empty() && gwi.additionalRetCols.empty() && minSc)
gwi.returnedCols.push_back(minSc);
}
// ORDER BY translation part
if (!isUnion && !gwi.hasWindowFunc && gwi.subSelectType == CalpontSelectExecutionPlan::MAIN_SELECT)
{
{
if (unionSel)
order_list = select_lex.master_unit()->global_parameters()->order_list;
ordercol = static_cast<ORDER*>(order_list.first);
for (; ordercol; ordercol = ordercol->next)
{
Item* ord_item = *(ordercol->item);
if (ord_item->name.length)
{
// for union order by 1 case. For unknown reason, it doesn't show in_field_list
if (ord_item->type() == Item::CONST_ITEM && ord_item->cmp_type() == INT_RESULT)
{
}
else if (ord_item->type() == Item::SUBSELECT_ITEM)
{
}
else
{
}
}
}
}
if (gwi.orderByCols.size()) // has order by
{
csep->hasOrderBy(true);
// To activate LimitedOrderBy
csep->orderByThreads(get_orderby_threads(gwi.thd));
csep->specHandlerProcessed(true);
}
}
// json dictionary for debug and testing options
csep->pron(get_pron(gwi.thd));
// We don't currently support limit with correlated subquery
if ((rc = processLimitAndOffset(select_lex, gwi, csep, unionSel, isUnion, isSelectHandlerTop)))
{
return rc;
}
} // ORDER BY end
if (select_lex.options & SELECT_DISTINCT)
csep->distinct(true);
// add the smallest column to count(*) parm.
// select constant in subquery case
std::vector<AggregateColumn*>::iterator coliter;
if (!minSc)
{
if (!gwi.returnedCols.empty())
minSc = gwi.returnedCols[0];
else if (!gwi.additionalRetCols.empty())
minSc = gwi.additionalRetCols[0];
}
// @bug3523, count(*) on subquery always pick column[0].
SimpleColumn* sc = dynamic_cast<SimpleColumn*>(minSc.get());
if (sc && sc->schemaName().empty())
{
if (gwi.derivedTbList.size() >= 1)
{
SimpleColumn* sc1 = new SimpleColumn();
sc1->columnName(sc->columnName());
sc1->tableName(sc->tableName());
sc1->tableAlias(sc->tableAlias());
sc1->viewName(sc->viewName());
sc1->colPosition(0);
sc1->timeZone(gwi.timeZone);
minSc.reset(sc1);
}
}
for (coliter = gwi.count_asterisk_list.begin(); coliter != gwi.count_asterisk_list.end(); ++coliter)
{
// @bug5977 @note should never throw this, but checking just in case.
// When ExeMgr fix is ready, this should not error out...
if (dynamic_cast<AggregateColumn*>(minSc.get()))
{
gwi.fatalParseError = true;
gwi.parseErrorText = "No project column found for aggregate function";
setError(gwi.thd, ER_INTERNAL_ERROR, gwi.parseErrorText, gwi);
return ER_CHECK_NOT_IMPLEMENTED;
}
// Replace the last (presumably constant) object with minSc
if ((*coliter)->aggParms().empty())
{
(*coliter)->aggParms().push_back(minSc);
}
else
{
(*coliter)->aggParms()[0] = minSc;
}
}
std::vector<FunctionColumn*>::iterator funciter;
SPTP sptp(new ParseTree(minSc.get()->clone()));
for (funciter = gwi.no_parm_func_list.begin(); funciter != gwi.no_parm_func_list.end(); ++funciter)
{
FunctionParm funcParms = (*funciter)->functionParms();
funcParms.push_back(sptp);
(*funciter)->functionParms(funcParms);
}
// set sequence# for subquery localCols
for (uint32_t i = 0; i < gwi.localCols.size(); i++)
gwi.localCols[i]->sequence(i);
gwi.select_lex = oldSelectLex;
// append additionalRetCols to returnedCols
gwi.returnedCols.insert(gwi.returnedCols.begin(), gwi.additionalRetCols.begin(),
gwi.additionalRetCols.end());
csep->groupByCols(gwi.groupByCols);
csep->withRollup(withRollup);
csep->orderByCols(gwi.orderByCols);
csep->returnedCols(gwi.returnedCols);
csep->columnMap(gwi.columnMap);
csep->having(havingFilter.release());
csep->derivedTableList(gwi.derivedTbList);
csep->selectSubList(selectSubList);
csep->subSelectList(gwi.subselectList);
return 0;
}
int cp_get_table_plan(THD* thd, SCSEP& csep, cal_table_info& ti, long timeZone)
{
SubQueryChainHolder chainHolder;
bool allocated = false;
gp_walk_info* gwi;
if (ti.condInfo)
{
gwi = &ti.condInfo->gwi;
}
else
{
allocated = true;
gwi = new gp_walk_info(timeZone, &chainHolder.chain);
}
gwi->thd = thd;
LEX* lex = thd->lex;
idbassert(lex != 0);
uint32_t sessionID = csep->sessionID();
gwi->sessionid = sessionID;
TABLE* table = ti.msTablePtr;
boost::shared_ptr<CalpontSystemCatalog> csc = CalpontSystemCatalog::makeCalpontSystemCatalog(sessionID);
csc->identity(CalpontSystemCatalog::FE);
// get all columns that mysql needs to fetch
MY_BITMAP* read_set = table->read_set;
Field **f_ptr, *field;
gwi->columnMap.clear();
for (f_ptr = table->field; (field = *f_ptr); f_ptr++)
{
if (bitmap_is_set(read_set, field->field_index))
{
SimpleColumn* sc =
new SimpleColumn(table->s->db.str, table->s->table_name.str, field->field_name.str, sessionID);
string alias(table->alias.c_ptr());
if (lower_case_table_names)
{
boost::algorithm::to_lower(alias);
}
sc->tableAlias(alias);
sc->timeZone(gwi->timeZone);
assert(sc);
boost::shared_ptr<SimpleColumn> spsc(sc);
gwi->returnedCols.push_back(spsc);
gwi->columnMap.insert(
CalpontSelectExecutionPlan::ColumnMap::value_type(string(field->field_name.str), spsc));
}
}
if (gwi->columnMap.empty())
{
CalpontSystemCatalog::TableName tn = make_table(table->s->db.str, table->s->table_name.str);
CalpontSystemCatalog::TableAliasName tan =
make_aliastable(table->s->db.str, table->s->table_name.str, table->alias.c_ptr());
SimpleColumn* sc = getSmallestColumn(csc, tn, tan, table, *gwi);
SRCP srcp(sc);
gwi->columnMap.insert(CalpontSelectExecutionPlan::ColumnMap::value_type(sc->columnName(), srcp));
gwi->returnedCols.push_back(srcp);
}
// get filter
if (ti.condInfo)
{
gp_walk_info* gwi = &ti.condInfo->gwi;
ParseTree* filters = 0;
ParseTree* ptp = 0;
ParseTree* rhs = 0;
while (!gwi->ptWorkStack.empty())
{
filters = gwi->ptWorkStack.top();
gwi->ptWorkStack.pop();
SimpleFilter* sf = dynamic_cast<SimpleFilter*>(filters->data());
if (sf && sf->op()->data() == "noop")
{
delete filters;
filters = 0;
if (gwi->ptWorkStack.empty())
break;
continue;
}
if (gwi->ptWorkStack.empty())
break;
ptp = new ParseTree(new LogicOperator("and"));
ptp->left(filters);
rhs = gwi->ptWorkStack.top();
gwi->ptWorkStack.pop();
ptp->right(rhs);
gwi->ptWorkStack.push(ptp);
}
csep->filters(filters);
}
csep->returnedCols(gwi->returnedCols);
csep->columnMap(gwi->columnMap);
CalpontSelectExecutionPlan::TableList tblist;
tblist.push_back(make_aliastable(table->s->db.str, table->s->table_name.str, table->alias.c_ptr(), true,
lower_case_table_names));
csep->tableList(tblist);
// @bug 3321. Set max number of blocks in a dictionary file to be scanned for filtering
csep->stringScanThreshold(get_string_scan_threshold(gwi->thd));
if (allocated)
{
delete gwi;
}
return 0;
}
int cp_get_group_plan(THD* thd, SCSEP& csep, cal_impl_if::cal_group_info& gi)
{
SELECT_LEX* select_lex = gi.groupByTables->select_lex;
const char* timeZone = thd->variables.time_zone->get_name()->ptr();
long timeZoneOffset;
dataconvert::timeZoneToOffset(timeZone, strlen(timeZone), &timeZoneOffset);
SubQuery* chain = nullptr;
gp_walk_info gwi(timeZoneOffset, &chain);
gwi.thd = thd;
gwi.isGroupByHandler = true;
idbassert(0);
int status = getGroupPlan(gwi, *select_lex, csep, gi);
#ifdef DEBUG_WALK_COND
cerr << "---------------- cp_get_group_plan EXECUTION PLAN ----------------" << endl;
cerr << *csep << endl;
cerr << "-------------- EXECUTION PLAN END --------------\n" << endl;
#endif
if (status > 0)
return ER_INTERNAL_ERROR;
else if (status < 0)
return status;
// Derived table projection and filter optimization.
derivedTableOptimization(&gwi, csep);
return 0;
}
int cs_get_derived_plan(ha_columnstore_derived_handler* handler, THD* thd, SCSEP& csep, gp_walk_info& gwi)
{
SELECT_LEX& select_lex = *handler->select;
int status = getSelectPlan(gwi, select_lex, csep, false);
if (status > 0)
return ER_INTERNAL_ERROR;
else if (status < 0)
return status;
#ifdef DEBUG_WALK_COND
cerr << "---------------- cs_get_derived_plan EXECUTION PLAN ----------------" << endl;
cerr << *csep << endl;
cerr << "-------------- EXECUTION PLAN END --------------\n" << endl;
#endif
// Derived table projection and filter optimization.
derivedTableOptimization(&gwi, csep);
return 0;
}
int cs_get_select_plan(ha_columnstore_select_handler* handler, THD* thd, SCSEP& csep, gp_walk_info& gwi,
bool isSelectLexUnit)
{
SELECT_LEX& select_lex = handler->select_lex ? *handler->select_lex : *handler->lex_unit->first_select();
if (select_lex.where)
{
gwi.condList.push_back(select_lex.where);
}
buildTableOnExprList(&select_lex.top_join_list, gwi.tableOnExprList);
convertOuterJoinToInnerJoin(&select_lex.top_join_list, gwi.tableOnExprList, gwi.condList,
handler->tableOuterJoinMap);
int status = getSelectPlan(gwi, select_lex, csep, false, true, isSelectLexUnit);
if (status > 0)
return ER_INTERNAL_ERROR;
else if (status < 0)
return status;
#ifdef DEBUG_WALK_COND
cerr << "---------------- cs_get_select_plan EXECUTION PLAN ----------------" << endl;
cerr << *csep << endl;
cerr << "-------------- EXECUTION PLAN END --------------\n" << endl;
#endif
// Derived table projection and filter optimization.
derivedTableOptimization(&gwi, csep);
return 0;
}
/*@brief buildConstColFromFilter- change SimpleColumn into ConstColumn*/
/***********************************************************
* DESCRIPTION:
* Server could optimize out fields from GROUP BY list, when certain
* filter predicate is used, e.g.
* field = 'AIR', field IN ('AIR'). This utility function tries to
* replace such fields with ConstantColumns using cond_pushed filters.
* TBD Take into account that originalSC SimpleColumn could be:
* SimpleColumn, ArithmeticColumn, FunctionColumn.
* PARAMETERS:
* originalSC SimpleColumn* removed field
* gwi main strucutre
* gi auxilary group_by handler structure
* RETURNS
* ConstantColumn* if originalSC equals with cond_pushed columns.
* NULL otherwise
***********************************************************/
ConstantColumn* buildConstColFromFilter(SimpleColumn* originalSC, gp_walk_info& gwi, cal_group_info& gi)
{
execplan::SimpleColumn* simpleCol;
execplan::ConstantColumn* constCol;
execplan::SOP op;
execplan::SimpleFilter* simpFilter;
execplan::ConstantColumn* result = NULL;
std::vector<ParseTree*>::iterator ptIt = gi.pushedPts.begin();
for (; ptIt != gi.pushedPts.end(); ptIt++)
{
simpFilter = dynamic_cast<execplan::SimpleFilter*>((*ptIt)->data());
if (simpFilter == NULL)
continue;
simpleCol = dynamic_cast<execplan::SimpleColumn*>(simpFilter->lhs());
constCol = dynamic_cast<execplan::ConstantColumn*>(simpFilter->rhs());
if (simpleCol == NULL || constCol == NULL)
continue;
op = simpFilter->op();
execplan::ReturnedColumn* rc = dynamic_cast<execplan::ReturnedColumn*>(simpleCol);
// The filter could have any kind of op
if (originalSC->sameColumn(rc))
{
#ifdef DEBUG_WALK_COND
cerr << "buildConstColFromFilter() replaced " << endl;
cerr << simpleCol->toString() << endl;
cerr << " with " << endl;
cerr << constCol << endl;
#endif
result = constCol;
}
}
return result;
}
// XXX: need to trigger that somehow.
int getGroupPlan(gp_walk_info& gwi, SELECT_LEX& select_lex, SCSEP& csep, cal_group_info& gi, bool isUnion)
{
#ifdef DEBUG_WALK_COND
cerr << "getGroupPlan()" << endl;
#endif
idbassert_s(false, "getGroupPlan is utterly out of date");
// XXX: rollup is currently not supported (not tested) in this part.
// but this is not triggered in any of tests.
if (select_lex.olap == ROLLUP_TYPE)
{
gwi.fatalParseError = true;
gwi.parseErrorText = IDBErrorInfo::instance()->errorMsg(ERR_ROLLUP_NOT_SUPPORT);
setError(gwi.thd, ER_CHECK_NOT_IMPLEMENTED, gwi.parseErrorText, gwi);
return ER_CHECK_NOT_IMPLEMENTED;
}
gwi.internalDecimalScale = (get_use_decimal_scale(gwi.thd) ? get_decimal_scale(gwi.thd) : -1);
gwi.subSelectType = csep->subType();
JOIN* join = select_lex.join;
Item_cond* icp = 0;
if (gi.groupByWhere)
icp = static_cast<Item_cond*>(gi.groupByWhere);
uint32_t sessionID = csep->sessionID();
gwi.sessionid = sessionID;
boost::shared_ptr<CalpontSystemCatalog> csc = CalpontSystemCatalog::makeCalpontSystemCatalog(sessionID);
csc->identity(CalpontSystemCatalog::FE);
gwi.csc = csc;
// @bug 2123. Override large table estimate if infinidb_ordered hint was used.
// @bug 2404. Always override if the infinidb_ordered_only variable is turned on.
if (get_ordered_only(gwi.thd))
csep->overrideLargeSideEstimate(true);
// @bug 5741. Set a flag when in Local PM only query mode
csep->localQuery(get_local_query(gwi.thd));
// @bug 3321. Set max number of blocks in a dictionary file to be scanned for filtering
csep->stringScanThreshold(get_string_scan_threshold(gwi.thd));
csep->stringTableThreshold(get_stringtable_threshold(gwi.thd));
csep->djsSmallSideLimit(get_diskjoin_smallsidelimit(gwi.thd) * 1024ULL * 1024);
csep->djsLargeSideLimit(get_diskjoin_largesidelimit(gwi.thd) * 1024ULL * 1024);
csep->djsPartitionSize(get_diskjoin_bucketsize(gwi.thd) * 1024ULL * 1024);
if (get_um_mem_limit(gwi.thd) == 0)
csep->umMemLimit(numeric_limits<int64_t>::max());
else
csep->umMemLimit(get_um_mem_limit(gwi.thd) * 1024ULL * 1024);
// populate table map and trigger syscolumn cache for all the tables (@bug 1637).
// all tables on FROM list must have at least one col in colmap
TABLE_LIST* table_ptr = gi.groupByTables;
CalpontSelectExecutionPlan::SelectList derivedTbList;
// DEBUG
#ifdef DEBUG_WALK_COND
List_iterator<TABLE_LIST> sj_list_it(select_lex.sj_nests);
TABLE_LIST* sj_nest;
while ((sj_nest = sj_list_it++))
{
cerr << sj_nest->db.str << "." << sj_nest->table_name.str << endl;
}
#endif
// @bug 1796. Remember table order on the FROM list.
gwi.clauseType = FROM;
try
{
for (; table_ptr; table_ptr = table_ptr->next_local)
{
// mysql put vtable here for from sub. we ignore it
// if (string(table_ptr->table_name).find("$vtable") != string::npos)
// continue;
// Until we handle recursive cte:
// Checking here ensures we catch all with clauses in the query.
if (table_ptr->is_recursive_with_table())
{
gwi.fatalParseError = true;
gwi.parseErrorText = "Recursive CTE";
setError(gwi.thd, ER_CHECK_NOT_IMPLEMENTED, gwi.parseErrorText, gwi);
return ER_CHECK_NOT_IMPLEMENTED;
}
string viewName = getViewName(table_ptr);
if (lower_case_table_names)
{
boost::algorithm::to_lower(viewName);
}
// @todo process from subquery
if (table_ptr->derived)
{
String str;
(table_ptr->derived->first_select())->print(gwi.thd, &str, QT_ORDINARY);
SELECT_LEX* select_cursor = table_ptr->derived->first_select();
// Use Pushdown handler for subquery processing
FromSubQuery* fromSub = new FromSubQuery(gwi, select_cursor);
string alias(table_ptr->alias.str);
if (lower_case_table_names)
{
boost::algorithm::to_lower(alias);
}
fromSub->alias(alias);
CalpontSystemCatalog::TableAliasName tn = make_aliasview("", "", alias, viewName);
// @bug 3852. check return execplan
SCSEP plan = fromSub->transform();
if (!plan)
{
setError(gwi.thd, ER_INTERNAL_ERROR, fromSub->gwip().parseErrorText, gwi);
CalpontSystemCatalog::removeCalpontSystemCatalog(sessionID);
return ER_INTERNAL_ERROR;
}
gwi.derivedTbList.push_back(plan);
gwi.tbList.push_back(tn);
CalpontSystemCatalog::TableAliasName tan = make_aliastable("", alias, alias);
gwi.tableMap[tan] = make_pair(0, table_ptr);
// MCOL-2178 isUnion member only assigned, never used
// MIGR::infinidb_vtable.isUnion = true; //by-pass the 2nd pass of rnd_init
}
else if (table_ptr->view)
{
View* view = new View(*table_ptr->view->first_select_lex(), &gwi);
CalpontSystemCatalog::TableAliasName tn = make_aliastable(
table_ptr->db.str, table_ptr->table_name.str, table_ptr->alias.str, true, lower_case_table_names);
view->viewName(tn);
gwi.viewList.push_back(view);
view->transform();
}
else
{
// check foreign engine tables
bool columnStore = (table_ptr->table ? isMCSTable(table_ptr->table) : true);
// trigger system catalog cache
if (columnStore)
csc->columnRIDs(make_table(table_ptr->db.str, table_ptr->table_name.str, lower_case_table_names),
true);
string table_name = table_ptr->table_name.str;
// @bug5523
if (table_ptr->db.length && strcmp(table_ptr->db.str, "information_schema") == 0)
table_name =
(table_ptr->schema_table_name.length ? table_ptr->schema_table_name.str : table_ptr->alias.str);
CalpontSystemCatalog::TableAliasName tn =
make_aliasview(table_ptr->db.str, table_name, table_ptr->alias.str, viewName, columnStore,
lower_case_table_names);
gwi.tbList.push_back(tn);
CalpontSystemCatalog::TableAliasName tan = make_aliastable(
table_ptr->db.str, table_name, table_ptr->alias.str, columnStore, lower_case_table_names);
gwi.tableMap[tan] = make_pair(0, table_ptr);
#ifdef DEBUG_WALK_COND
cerr << tn << endl;
#endif
}
}
if (gwi.fatalParseError)
{
setError(gwi.thd, ER_INTERNAL_ERROR, gwi.parseErrorText, gwi);
return ER_INTERNAL_ERROR;
}
}
catch (IDBExcept& ie)
{
setError(gwi.thd, ER_INTERNAL_ERROR, ie.what(), gwi);
CalpontSystemCatalog::removeCalpontSystemCatalog(sessionID);
// @bug 3852. set error status for gwi.
gwi.fatalParseError = true;
gwi.parseErrorText = ie.what();
return ER_INTERNAL_ERROR;
}
catch (...)
{
string emsg = IDBErrorInfo::instance()->errorMsg(ERR_LOST_CONN_EXEMGR);
// @bug3852 set error status for gwi.
gwi.fatalParseError = true;
gwi.parseErrorText = emsg;
setError(gwi.thd, ER_INTERNAL_ERROR, emsg, gwi);
CalpontSystemCatalog::removeCalpontSystemCatalog(sessionID);
return ER_INTERNAL_ERROR;
}
csep->tableList(gwi.tbList);
bool unionSel = false;
gwi.clauseType = WHERE;
if (icp)
{
// MCOL-1052 The condition could be useless.
// MariaDB bug 624 - without the fix_fields call, delete with join may error with "No query step".
// #if MYSQL_VERSION_ID < 50172
//@bug 3039. fix fields for constants
if (!icp->fixed())
{
icp->fix_fields(gwi.thd, (Item**)&icp);
}
// #endif
gwi.fatalParseError = false;
#ifdef DEBUG_WALK_COND
cerr << "------------------ WHERE -----------------------" << endl;
icp->traverse_cond(debug_walk, &gwi, Item::POSTFIX);
cerr << "------------------------------------------------\n" << endl;
#endif
icp->traverse_cond(gp_walk, &gwi, Item::POSTFIX);
if (gwi.fatalParseError)
{
// if this is dervied table process phase, mysql may have not developed the plan
// completely. Do not error and eventually mysql will call JOIN::exec() again.
// related to bug 2922. Need to find a way to skip calling rnd_init for derived table
// processing.
if (gwi.thd->derived_tables_processing)
{
// MCOL-2178 isUnion member only assigned, never used
// MIGR::infinidb_vtable.isUnion = false;
return -1;
}
setError(gwi.thd, ER_INTERNAL_ERROR, gwi.parseErrorText, gwi);
return ER_INTERNAL_ERROR;
}
}
else if (join && join->zero_result_cause)
{
gwi.rcWorkStack.push(new ConstantColumn((int64_t)0, ConstantColumn::NUM));
(dynamic_cast<ConstantColumn*>(gwi.rcWorkStack.top()))->timeZone(gwi.timeZone);
}
SELECT_LEX tmp_select_lex;
tmp_select_lex.table_list.first = gi.groupByTables;
// InfiniDB bug5764 requires outer joins to be appended to the
// end of the filter list. This causes outer join filters to
// have a higher join id than inner join filters.
// TODO MCOL-4680 Figure out why this is the case, and possibly
// eliminate this requirement.
std::stack<execplan::ParseTree*> outerJoinStack;
uint32_t failed = buildJoin(gwi, tmp_select_lex.top_join_list, outerJoinStack);
if (failed)
return failed;
if (gwi.subQuery)
{
for (uint i = 0; i < gwi.viewList.size(); i++)
{
failed = gwi.viewList[i]->processJoin(gwi, outerJoinStack);
if (failed)
break;
}
}
if (failed != 0)
return failed;
ParseTree* filters = NULL;
ParseTree* outerJoinFilters = NULL;
ParseTree* ptp = NULL;
ParseTree* rhs = NULL;
// @bug 2932. for "select * from region where r_name" case. if icp not null and
// ptWorkStack empty, the item is in rcWorkStack.
// MySQL 5.6 (MariaDB?). when icp is null and zero_result_cause is set, a constant 0
// is pushed to rcWorkStack.
if (/*icp && */ gwi.ptWorkStack.empty() && !gwi.rcWorkStack.empty())
{
filters = new ParseTree(gwi.rcWorkStack.top());
gwi.rcWorkStack.pop();
}
while (!gwi.ptWorkStack.empty())
{
filters = gwi.ptWorkStack.top();
gwi.ptWorkStack.pop();
if (gwi.ptWorkStack.empty())
break;
ptp = new ParseTree(new LogicOperator("and"));
ptp->left(filters);
rhs = gwi.ptWorkStack.top();
gwi.ptWorkStack.pop();
ptp->right(rhs);
gwi.ptWorkStack.push(ptp);
}
while (!outerJoinStack.empty())
{
outerJoinFilters = outerJoinStack.top();
outerJoinStack.pop();
if (outerJoinStack.empty())
break;
ptp = new ParseTree(new LogicOperator("and"));
ptp->left(outerJoinFilters);
rhs = outerJoinStack.top();
outerJoinStack.pop();
ptp->right(rhs);
outerJoinStack.push(ptp);
}
// Append outer join filters at the end of inner join filters.
// JLF_ExecPlanToJobList::walkTree processes ParseTree::left
// before ParseTree::right which is what we intend to do in the
// below.
if (filters && outerJoinFilters)
{
ptp = new ParseTree(new LogicOperator("and"));
ptp->left(filters);
ptp->right(outerJoinFilters);
filters = ptp;
}
else if (outerJoinFilters)
{
filters = outerJoinFilters;
}
if (filters)
{
csep->filters(filters);
#ifdef DEBUG_WALK_COND
std::string aTmpDir(startup::StartUp::tmpDir());
aTmpDir = aTmpDir + "/filter1.dot";
filters->drawTree(aTmpDir);
#endif
}
gwi.clauseType = SELECT;
#ifdef DEBUG_WALK_COND
{
cerr << "------------------- SELECT --------------------" << endl;
List_iterator_fast<Item> it(*gi.groupByFields);
Item* item;
while ((item = it++))
{
debug_walk(item, 0);
}
cerr << "-----------------------------------------------\n" << endl;
}
#endif
// populate returnedcolumnlist and columnmap
List_iterator_fast<Item> it(*gi.groupByFields);
Item* item;
vector<Item_field*> funcFieldVec;
bool redo = false;
// empty rcWorkStack and ptWorkStack. They should all be empty by now.
clearStacks(gwi, false);
// indicate the starting pos of scalar returned column, because some join column
// has been inserted to the returned column list.
if (gwi.subQuery)
{
ScalarSub* scalar = dynamic_cast<ScalarSub*>(gwi.subQuery);
if (scalar)
scalar->returnedColPos(gwi.additionalRetCols.size());
}
CalpontSelectExecutionPlan::SelectList selectSubList;
while ((item = it++))
{
string itemAlias;
if (item->name.length)
itemAlias = (item->name.str);
else
{
itemAlias = "<NULL>";
}
// @bug 5916. Need to keep checking until getting concret item in case
// of nested view.
while (item->type() == Item::REF_ITEM)
{
Item_ref* ref = (Item_ref*)item;
item = (*(ref->ref));
}
Item::Type itype = item->type();
switch (itype)
{
case Item::FIELD_ITEM:
{
Item_field* ifp = (Item_field*)item;
SimpleColumn* sc = NULL;
ConstantColumn* constCol = NULL;
if (ifp->field_name.length && string(ifp->field_name.str) == "*")
{
collectAllCols(gwi, ifp);
break;
}
sc = buildSimpleColumn(ifp, gwi);
if (sc)
{
constCol = buildConstColFromFilter(sc, gwi, gi);
boost::shared_ptr<ConstantColumn> spcc(constCol);
boost::shared_ptr<SimpleColumn> spsc(sc);
string fullname;
String str;
ifp->print(&str, QT_ORDINARY);
fullname = str.c_ptr();
if (!ifp->is_explicit_name()) // no alias
{
sc->alias(fullname);
}
else // alias
{
if (!itemAlias.empty())
sc->alias(itemAlias);
}
// MCOL-1052 Replace SimpleColumn with ConstantColumn,
// since it must have a single value only.
if (constCol)
{
gwi.returnedCols.push_back(spcc);
gwi.columnMap.insert(
CalpontSelectExecutionPlan::ColumnMap::value_type(string(ifp->field_name.str), spcc));
}
else
{
gwi.returnedCols.push_back(spsc);
gwi.columnMap.insert(
CalpontSelectExecutionPlan::ColumnMap::value_type(string(ifp->field_name.str), spsc));
}
TABLE_LIST* tmp = 0;
if (ifp->cached_table)
tmp = ifp->cached_table;
gwi.tableMap[make_aliastable(sc->schemaName(), sc->tableName(), sc->tableAlias(),
sc->isColumnStore())] = make_pair(1, tmp);
}
else
{
setError(gwi.thd, ER_INTERNAL_ERROR, gwi.parseErrorText, gwi);
delete sc;
return ER_INTERNAL_ERROR;
}
break;
}
// aggregate column
case Item::SUM_FUNC_ITEM:
{
ReturnedColumn* ac = buildAggregateColumn(item, gwi);
if (gwi.fatalParseError)
{
// e.g., non-support ref column
setError(gwi.thd, ER_CHECK_NOT_IMPLEMENTED, gwi.parseErrorText, gwi);
delete ac;
return ER_CHECK_NOT_IMPLEMENTED;
}
// add this agg col to returnedColumnList
boost::shared_ptr<ReturnedColumn> spac(ac);
gwi.returnedCols.push_back(spac);
// This item could be used in projection or HAVING later.
gwi.extSelAggColsItems.push_back(item);
break;
}
case Item::FUNC_ITEM:
{
Item_func* ifp = static_cast<Item_func*>(item);
// @bug4383. error out non-support stored function
if (ifp->functype() == Item_func::FUNC_SP)
{
gwi.fatalParseError = true;
gwi.parseErrorText = IDBErrorInfo::instance()->errorMsg(ERR_SP_FUNCTION_NOT_SUPPORT);
setError(gwi.thd, ER_CHECK_NOT_IMPLEMENTED, gwi.parseErrorText, gwi);
return ER_CHECK_NOT_IMPLEMENTED;
}
if (string(ifp->func_name()) == "xor")
{
gwi.fatalParseError = true;
gwi.parseErrorText = IDBErrorInfo::instance()->errorMsg(ERR_FILTER_COND_EXP);
setError(gwi.thd, ER_CHECK_NOT_IMPLEMENTED, gwi.parseErrorText, gwi);
return ER_CHECK_NOT_IMPLEMENTED;
}
uint16_t parseInfo = 0;
vector<Item_field*> tmpVec;
bool hasNonSupportItem = false;
parse_item(ifp, tmpVec, hasNonSupportItem, parseInfo, &gwi);
if (ifp->with_subquery() || string(ifp->func_name()) == string("<in_optimizer>") ||
ifp->functype() == Item_func::NOT_ALL_FUNC || parseInfo & SUB_BIT)
{
gwi.fatalParseError = true;
gwi.parseErrorText = IDBErrorInfo::instance()->errorMsg(ERR_NON_SUPPORT_SELECT_SUB);
setError(gwi.thd, ER_CHECK_NOT_IMPLEMENTED, gwi.parseErrorText, gwi);
return ER_CHECK_NOT_IMPLEMENTED;
}
ReturnedColumn* rc = buildFunctionColumn(ifp, gwi, hasNonSupportItem, true);
SRCP srcp(rc);
if (rc)
{
if (!hasNonSupportItem && !nonConstFunc(ifp) && !(parseInfo & AF_BIT) && tmpVec.size() == 0)
{
if (isUnion || unionSel || gwi.subSelectType != CalpontSelectExecutionPlan::MAIN_SELECT ||
parseInfo & SUB_BIT) //|| select_lex.group_list.elements != 0)
{
srcp.reset(buildReturnedColumn(item, gwi, gwi.fatalParseError));
gwi.returnedCols.push_back(srcp);
if (ifp->name.length)
srcp->alias(ifp->name.str);
continue;
}
break;
}
gwi.returnedCols.push_back(srcp);
}
else // InfiniDB Non support functions still go through post process for now
{
hasNonSupportItem = false;
uint32_t before_size = funcFieldVec.size();
// MCOL-1510 Use gwi pointer here to catch funcs with
// not supported aggregate args in projections,
// e.g. NOT(SUM(i)).
parse_item(ifp, funcFieldVec, hasNonSupportItem, parseInfo, &gwi);
uint32_t after_size = funcFieldVec.size();
// group by func and func in subquery can not be post processed
// @bug3881. set_user_var can not be treated as constant function
// @bug5716. Try to avoid post process function for union query.
if ((gwi.subQuery /*|| select_lex.group_list.elements != 0 */ || !csep->unionVec().empty() ||
isUnion) &&
!hasNonSupportItem && (after_size - before_size) == 0 && !(parseInfo & AGG_BIT) &&
!(parseInfo & SUB_BIT))
{
ConstantColumn* cc = buildConstantColumnMaybeNullUsingValStr(ifp, gwi);
SRCP srcp(cc);
if (ifp->name.length)
cc->alias(ifp->name.str);
gwi.returnedCols.push_back(srcp);
// clear the error set by buildFunctionColumn
gwi.fatalParseError = false;
gwi.parseErrorText = "";
break;
}
else if (hasNonSupportItem || parseInfo & AGG_BIT || parseInfo & SUB_BIT ||
(gwi.fatalParseError && gwi.subQuery))
{
if (gwi.parseErrorText.empty())
{
Message::Args args;
args.add(ifp->func_name());
gwi.parseErrorText = IDBErrorInfo::instance()->errorMsg(ERR_NON_SUPPORTED_FUNCTION, args);
}
setError(gwi.thd, ER_CHECK_NOT_IMPLEMENTED, gwi.parseErrorText, gwi);
return ER_CHECK_NOT_IMPLEMENTED;
}
else if (gwi.subQuery && (isPredicateFunction(ifp, &gwi) || ifp->type() == Item::COND_ITEM))
{
gwi.fatalParseError = true;
gwi.parseErrorText = IDBErrorInfo::instance()->errorMsg(ERR_FILTER_COND_EXP);
setError(gwi.thd, ER_CHECK_NOT_IMPLEMENTED, gwi.parseErrorText, gwi);
return ER_CHECK_NOT_IMPLEMENTED;
}
//@Bug 3030 Add error check for dml statement
if (isUpdateOrDeleteStatement(gwi.thd->lex->sql_command))
{
if (after_size - before_size != 0)
{
gwi.parseErrorText = ifp->func_name();
return -1;
}
}
else
{
// clear the error set by buildFunctionColumn
gwi.fatalParseError = false;
gwi.parseErrorText = "";
}
}
break;
}
// DRRTUY Replace the whole section with typeid() checks or use
// static_cast here
case Item::CONST_ITEM:
{
switch (item->cmp_type())
{
case INT_RESULT:
case STRING_RESULT:
case DECIMAL_RESULT:
case REAL_RESULT:
case TIME_RESULT:
{
if (isUpdateOrDeleteStatement(gwi.thd->lex->sql_command))
{
}
else
{
// do not push the dummy column (mysql added) to returnedCol
if (item->name.length && string(item->name.str) == "Not_used")
continue;
// @bug3509. Constant column is sent to ExeMgr now.
SRCP srcp(buildReturnedColumn(item, gwi, gwi.fatalParseError));
if (item->name.length)
srcp->alias(item->name.str);
gwi.returnedCols.push_back(srcp);
}
break;
}
// MCOL-2178 This switch doesn't handl
// ROW_
default:
{
IDEBUG(cerr << "Warning unsupported cmp_type() in projection" << endl);
}
}
break;
} // CONST_ITEM ends here
case Item::NULL_ITEM:
{
if (isUpdateOrDeleteStatement(gwi.thd->lex->sql_command))
{
}
else
{
SRCP srcp(buildReturnedColumn(item, gwi, gwi.fatalParseError));
gwi.returnedCols.push_back(srcp);
if (item->name.length)
srcp->alias(item->name.str);
}
break;
}
case Item::SUBSELECT_ITEM:
{
Item_subselect* sub = (Item_subselect*)item;
if (sub->substype() != Item_subselect::SINGLEROW_SUBS)
{
gwi.fatalParseError = true;
gwi.parseErrorText = IDBErrorInfo::instance()->errorMsg(ERR_NON_SUPPORT_SELECT_SUB);
setError(gwi.thd, ER_CHECK_NOT_IMPLEMENTED, gwi.parseErrorText, gwi);
return ER_CHECK_NOT_IMPLEMENTED;
}
#ifdef DEBUG_WALK_COND
cerr << "SELECT clause SUBSELECT Item: " << sub->substype() << endl;
JOIN* join = sub->get_select_lex()->join;
if (join)
{
Item_cond* cond = static_cast<Item_cond*>(join->conds);
if (cond)
cond->traverse_cond(debug_walk, &gwi, Item::POSTFIX);
}
cerr << "Finish SELECT clause subselect item traversing" << endl;
#endif
SelectSubQuery* selectSub = new SelectSubQuery(gwi, sub);
// selectSub->gwip(&gwi);
SCSEP ssub = selectSub->transform();
if (!ssub || gwi.fatalParseError)
{
if (gwi.parseErrorText.empty())
gwi.parseErrorText = "Unsupported Item in SELECT subquery.";
setError(gwi.thd, ER_CHECK_NOT_IMPLEMENTED, gwi.parseErrorText, gwi);
return ER_CHECK_NOT_IMPLEMENTED;
}
selectSubList.push_back(ssub);
SimpleColumn* rc = new SimpleColumn();
rc->colSource(rc->colSource() | SELECT_SUB);
rc->timeZone(gwi.timeZone);
if (sub->get_select_lex()->get_table_list())
{
rc->viewName(getViewName(sub->get_select_lex()->get_table_list()), lower_case_table_names);
}
if (sub->name.length)
rc->alias(sub->name.str);
gwi.returnedCols.push_back(SRCP(rc));
break;
}
case Item::COND_ITEM:
{
gwi.fatalParseError = true;
gwi.parseErrorText = IDBErrorInfo::instance()->errorMsg(ERR_FILTER_COND_EXP);
setError(gwi.thd, ER_CHECK_NOT_IMPLEMENTED, gwi.parseErrorText, gwi);
return ER_CHECK_NOT_IMPLEMENTED;
}
case Item::EXPR_CACHE_ITEM:
{
printf("EXPR_CACHE_ITEM in getSelectPlan\n");
gwi.fatalParseError = true;
gwi.parseErrorText = IDBErrorInfo::instance()->errorMsg(ERR_UNKNOWN_COL);
setError(gwi.thd, ER_CHECK_NOT_IMPLEMENTED, gwi.parseErrorText, gwi);
return ER_CHECK_NOT_IMPLEMENTED;
}
case Item::WINDOW_FUNC_ITEM:
{
SRCP srcp(buildWindowFunctionColumn(item, gwi, gwi.fatalParseError));
if (!srcp || gwi.fatalParseError)
{
if (gwi.parseErrorText.empty())
gwi.parseErrorText = "Unsupported Item in SELECT subquery.";
setError(gwi.thd, ER_CHECK_NOT_IMPLEMENTED, gwi.parseErrorText, gwi);
return ER_CHECK_NOT_IMPLEMENTED;
}
gwi.returnedCols.push_back(srcp);
break;
}
default:
{
break;
}
}
}
// @bug4388 normalize the project coltypes for union main select list
if (!csep->unionVec().empty())
{
unsigned int unionedTypeRc = 0;
for (uint32_t i = 0; i < gwi.returnedCols.size(); i++)
{
vector<CalpontSystemCatalog::ColType> coltypes;
for (uint32_t j = 0; j < csep->unionVec().size(); j++)
{
CalpontSelectExecutionPlan* unionCsep =
dynamic_cast<CalpontSelectExecutionPlan*>(csep->unionVec()[j].get());
coltypes.push_back(unionCsep->returnedCols()[i]->resultType());
// @bug5976. set hasAggregate true for the main column if
// one corresponding union column has aggregate
if (unionCsep->returnedCols()[i]->hasAggregate())
gwi.returnedCols[i]->hasAggregate(true);
}
gwi.returnedCols[i]->resultType(
CalpontSystemCatalog::ColType::convertUnionColType(coltypes, unionedTypeRc));
if (unionedTypeRc != 0)
{
gwi.parseErrorText = IDBErrorInfo::instance()->errorMsg(unionedTypeRc);
setError(gwi.thd, ER_CHECK_NOT_IMPLEMENTED, gwi.parseErrorText, gwi);
return ER_CHECK_NOT_IMPLEMENTED;
}
}
}
// Having clause handling
gwi.clauseType = HAVING;
clearStacks(gwi, false);
ParseTree* havingFilter = 0;
// clear fatalParseError that may be left from post process functions
gwi.fatalParseError = false;
gwi.parseErrorText = "";
if (gi.groupByHaving != 0)
{
Item_cond* having = static_cast<Item_cond*>(gi.groupByHaving);
#ifdef DEBUG_WALK_COND
cerr << "------------------- HAVING ---------------------" << endl;
having->traverse_cond(debug_walk, &gwi, Item::POSTFIX);
cerr << "------------------------------------------------\n" << endl;
#endif
having->traverse_cond(gp_walk, &gwi, Item::POSTFIX);
if (gwi.fatalParseError)
{
setError(gwi.thd, ER_INTERNAL_ERROR, gwi.parseErrorText, gwi);
return ER_INTERNAL_ERROR;
}
ParseTree* ptp = 0;
ParseTree* rhs = 0;
// @bug 4215. some function filter will be in the rcWorkStack.
if (gwi.ptWorkStack.empty() && !gwi.rcWorkStack.empty())
{
havingFilter = new ParseTree(gwi.rcWorkStack.top());
gwi.rcWorkStack.pop();
}
while (!gwi.ptWorkStack.empty())
{
havingFilter = gwi.ptWorkStack.top();
gwi.ptWorkStack.pop();
if (gwi.ptWorkStack.empty())
break;
ptp = new ParseTree(new LogicOperator("and"));
ptp->left(havingFilter);
rhs = gwi.ptWorkStack.top();
gwi.ptWorkStack.pop();
ptp->right(rhs);
gwi.ptWorkStack.push(ptp);
}
}
// for post process expressions on the select list
// error out post process for union and sub select unit
if (isUnion || gwi.subSelectType != CalpontSelectExecutionPlan::MAIN_SELECT)
{
if (funcFieldVec.size() != 0 && !gwi.fatalParseError)
{
string emsg("Fatal parse error in vtable mode: Unsupported Items in union or sub select unit");
setError(gwi.thd, ER_CHECK_NOT_IMPLEMENTED, emsg);
return ER_CHECK_NOT_IMPLEMENTED;
}
}
for (uint32_t i = 0; i < funcFieldVec.size(); i++)
{
SimpleColumn* sc = buildSimpleColumn(funcFieldVec[i], gwi);
if (!sc || gwi.fatalParseError)
{
string emsg;
if (gwi.parseErrorText.empty())
{
emsg = "un-recognized column";
if (funcFieldVec[i]->name.length)
emsg += string(funcFieldVec[i]->name.str);
}
else
{
emsg = gwi.parseErrorText;
}
setError(gwi.thd, ER_INTERNAL_ERROR, emsg, gwi);
return ER_INTERNAL_ERROR;
}
String str;
funcFieldVec[i]->print(&str, QT_ORDINARY);
sc->alias(string(str.c_ptr()));
// sc->tableAlias(funcFieldVec[i]->table_name);
sc->tableAlias(sc->alias());
SRCP srcp(sc);
uint32_t j = 0;
for (; j < gwi.returnedCols.size(); j++)
{
if (sc->sameColumn(gwi.returnedCols[j].get()))
{
SimpleColumn* field = dynamic_cast<SimpleColumn*>(gwi.returnedCols[j].get());
if (field && field->alias() == sc->alias())
break;
}
}
if (j == gwi.returnedCols.size())
{
gwi.returnedCols.push_back(srcp);
gwi.columnMap.insert(
CalpontSelectExecutionPlan::ColumnMap::value_type(string(funcFieldVec[i]->field_name.str), srcp));
string fullname;
fullname = str.c_ptr();
TABLE_LIST* tmp = (funcFieldVec[i]->cached_table ? funcFieldVec[i]->cached_table : 0);
gwi.tableMap[make_aliastable(sc->schemaName(), sc->tableName(), sc->tableAlias(),
sc->isColumnStore())] = make_pair(1, tmp);
}
}
// post-process Order by list and expressions on select by redo phase1. only for vtable
// ignore ORDER BY clause for union select unit
string ord_cols = ""; // for normal select phase
SRCP minSc; // min width projected column. for count(*) use
// Group by list. not valid for union main query
if (!unionSel)
{
gwi.clauseType = GROUP_BY;
Item* nonSupportItem = NULL;
ORDER* groupcol = static_cast<ORDER*>(gi.groupByGroup);
// check if window functions are in order by. InfiniDB process order by list if
// window functions are involved, either in order by or projection.
bool hasWindowFunc = gwi.hasWindowFunc;
gwi.hasWindowFunc = false;
for (; groupcol; groupcol = groupcol->next)
{
if ((*(groupcol->item))->type() == Item::WINDOW_FUNC_ITEM)
gwi.hasWindowFunc = true;
}
if (gwi.hasWindowFunc)
{
gwi.fatalParseError = true;
gwi.parseErrorText = IDBErrorInfo::instance()->errorMsg(ERR_WF_NOT_ALLOWED, "GROUP BY clause");
setError(gwi.thd, ER_CHECK_NOT_IMPLEMENTED, gwi.parseErrorText, gwi);
return ER_CHECK_NOT_IMPLEMENTED;
}
gwi.hasWindowFunc = hasWindowFunc;
groupcol = static_cast<ORDER*>(gi.groupByGroup);
for (; groupcol; groupcol = groupcol->next)
{
Item* groupItem = *(groupcol->item);
// @bug5993. Could be nested ref.
while (groupItem->type() == Item::REF_ITEM)
groupItem = (*((Item_ref*)groupItem)->ref);
if (groupItem->type() == Item::FUNC_ITEM)
{
Item_func* ifp = (Item_func*)groupItem;
// call buildFunctionColumn here mostly for finding out
// non-support column on GB list. Should be simplified.
ReturnedColumn* fc = buildFunctionColumn(ifp, gwi, gwi.fatalParseError);
if (!fc || gwi.fatalParseError)
{
nonSupportItem = ifp;
break;
}
if (groupcol->in_field_list && groupcol->counter_used)
{
delete fc;
fc = gwi.returnedCols[groupcol->counter - 1].get();
SRCP srcp(fc->clone());
// check if no column parm
for (uint32_t i = 0; i < gwi.no_parm_func_list.size(); i++)
{
if (gwi.no_parm_func_list[i]->expressionId() == fc->expressionId())
{
gwi.no_parm_func_list.push_back(dynamic_cast<FunctionColumn*>(srcp.get()));
break;
}
}
srcp->orderPos(groupcol->counter - 1);
gwi.groupByCols.push_back(srcp);
continue;
}
else if (groupItem->is_explicit_name()) // alias
{
uint32_t i = 0;
for (; i < gwi.returnedCols.size(); i++)
{
if (string(groupItem->name.str) == gwi.returnedCols[i]->alias())
{
ReturnedColumn* rc = gwi.returnedCols[i]->clone();
rc->orderPos(i);
gwi.groupByCols.push_back(SRCP(rc));
delete fc;
break;
}
}
if (i == gwi.returnedCols.size())
{
nonSupportItem = groupItem;
break;
}
}
else
{
uint32_t i = 0;
for (; i < gwi.returnedCols.size(); i++)
{
if (fc->operator==(gwi.returnedCols[i].get()))
{
ReturnedColumn* rc = gwi.returnedCols[i]->clone();
rc->orderPos(i);
gwi.groupByCols.push_back(SRCP(rc));
delete fc;
break;
}
}
if (i == gwi.returnedCols.size())
{
gwi.groupByCols.push_back(SRCP(fc));
break;
}
}
}
else if (groupItem->type() == Item::FIELD_ITEM)
{
Item_field* ifp = (Item_field*)groupItem;
// this GB col could be an alias of F&E on the SELECT clause, not necessarily a field.
ReturnedColumn* rc = buildSimpleColumn(ifp, gwi);
SimpleColumn* sc = dynamic_cast<SimpleColumn*>(rc);
for (uint32_t j = 0; j < gwi.returnedCols.size(); j++)
{
if (sc)
{
if (sc->sameColumn(gwi.returnedCols[j].get()))
{
sc->orderPos(j);
break;
}
else if (strcasecmp(sc->alias().c_str(), gwi.returnedCols[j]->alias().c_str()) == 0)
{
rc = gwi.returnedCols[j].get()->clone();
rc->orderPos(j);
break;
}
}
else
{
if (ifp->name.length && string(ifp->name.str) == gwi.returnedCols[j].get()->alias())
{
rc = gwi.returnedCols[j].get()->clone();
rc->orderPos(j);
break;
}
}
}
if (!rc)
{
nonSupportItem = ifp;
break;
}
SRCP srcp(rc);
// bug 3151
AggregateColumn* ac = dynamic_cast<AggregateColumn*>(rc);
if (ac)
{
nonSupportItem = ifp;
break;
}
gwi.groupByCols.push_back(srcp);
gwi.columnMap.insert(
CalpontSelectExecutionPlan::ColumnMap::value_type(string(ifp->field_name.str), srcp));
}
// @bug5638. The group by column is constant but not counter, alias has to match a column
// on the select list
else if (!groupcol->counter_used &&
(groupItem->type() == Item::CONST_ITEM &&
(groupItem->cmp_type() == INT_RESULT || groupItem->cmp_type() == STRING_RESULT ||
groupItem->cmp_type() == REAL_RESULT || groupItem->cmp_type() == DECIMAL_RESULT)))
{
ReturnedColumn* rc = 0;
for (uint32_t j = 0; j < gwi.returnedCols.size(); j++)
{
if (groupItem->name.length && string(groupItem->name.str) == gwi.returnedCols[j].get()->alias())
{
rc = gwi.returnedCols[j].get()->clone();
rc->orderPos(j);
break;
}
}
if (!rc)
{
nonSupportItem = groupItem;
break;
}
gwi.groupByCols.push_back(SRCP(rc));
}
else if ((*(groupcol->item))->type() == Item::SUBSELECT_ITEM)
{
if (!groupcol->in_field_list || !groupItem->name.length)
{
nonSupportItem = groupItem;
}
else
{
uint32_t i = 0;
for (; i < gwi.returnedCols.size(); i++)
{
if (string(groupItem->name.str) == gwi.returnedCols[i]->alias())
{
ReturnedColumn* rc = gwi.returnedCols[i]->clone();
rc->orderPos(i);
gwi.groupByCols.push_back(SRCP(rc));
break;
}
}
if (i == gwi.returnedCols.size())
{
nonSupportItem = groupItem;
}
}
}
// @bug 3761.
else if (groupcol->counter_used)
{
if (gwi.returnedCols.size() <= (uint32_t)(groupcol->counter - 1))
{
nonSupportItem = groupItem;
}
else
{
gwi.groupByCols.push_back(SRCP(gwi.returnedCols[groupcol->counter - 1]->clone()));
}
}
else
{
nonSupportItem = groupItem;
}
}
// @bug 4756. Add internal groupby column for correlated join to the groupby list
if (gwi.aggOnSelect && !gwi.subGroupByCols.empty())
gwi.groupByCols.insert(gwi.groupByCols.end(), gwi.subGroupByCols.begin(), gwi.subGroupByCols.end());
// this is window func on SELECT becuase ORDER BY has not been processed
if (!gwi.windowFuncList.empty() && !gwi.subGroupByCols.empty())
{
for (uint32_t i = 0; i < gwi.windowFuncList.size(); i++)
{
if (gwi.windowFuncList[i]->hasWindowFunc())
{
vector<WindowFunctionColumn*> windowFunctions = gwi.windowFuncList[i]->windowfunctionColumnList();
for (uint32_t j = 0; j < windowFunctions.size(); j++)
windowFunctions[j]->addToPartition(gwi.subGroupByCols);
}
}
}
if (nonSupportItem)
{
Message::Args args;
if (nonSupportItem->name.length)
args.add("'" + string(nonSupportItem->name.str) + "'");
else
args.add("");
gwi.parseErrorText = IDBErrorInfo::instance()->errorMsg(ERR_NON_SUPPORT_GROUP_BY, args);
setError(gwi.thd, ER_CHECK_NOT_IMPLEMENTED, gwi.parseErrorText, gwi);
return ER_CHECK_NOT_IMPLEMENTED;
}
} // GROUP processing ends here
// ORDER BY processing starts here
{
ORDER* ordercol = static_cast<ORDER*>(gi.groupByOrder);
// check if window functions are in order by. InfiniDB process order by list if
// window functions are involved, either in order by or projection.
for (; ordercol; ordercol = ordercol->next)
{
if ((*(ordercol->item))->type() == Item::WINDOW_FUNC_ITEM)
gwi.hasWindowFunc = true;
}
// re-visit the first of ordercol list
ordercol = static_cast<ORDER*>(gi.groupByOrder);
// for subquery, order+limit by will be supported in infinidb. build order by columns
// @todo union order by and limit support
// if (gwi.hasWindowFunc || gwi.subSelectType != CalpontSelectExecutionPlan::MAIN_SELECT)
for (; ordercol; ordercol = ordercol->next)
{
ReturnedColumn* rc = NULL;
if (ordercol->in_field_list && ordercol->counter_used)
{
rc = gwi.returnedCols[ordercol->counter - 1]->clone();
rc->orderPos(ordercol->counter - 1);
// can not be optimized off if used in order by with counter.
// set with self derived table alias if it's derived table
gwi.returnedCols[ordercol->counter - 1]->incRefCount();
}
else
{
Item* ord_item = *(ordercol->item);
bool nonAggField = true;
// ignore not_used column on order by.
if ((ord_item->type() == Item::CONST_ITEM && ord_item->cmp_type() == INT_RESULT) &&
ord_item->full_name() && !strcmp(ord_item->full_name(), "Not_used"))
{
continue;
}
else if (ord_item->type() == Item::CONST_ITEM && ord_item->cmp_type() == INT_RESULT)
{
rc = gwi.returnedCols[((Item_int*)ord_item)->val_int() - 1]->clone();
}
else if (ord_item->type() == Item::SUBSELECT_ITEM)
{
gwi.fatalParseError = true;
}
else if (ordercol->in_field_list && ord_item->type() == Item::FIELD_ITEM)
{
rc = buildReturnedColumn(ord_item, gwi, gwi.fatalParseError);
Item_field* ifp = static_cast<Item_field*>(ord_item);
// The item must be an alias for a projected column
// and extended SELECT list must contain a proper rc
// either aggregation or a field.
if (!rc && ifp->name.length)
{
gwi.fatalParseError = false;
execplan::CalpontSelectExecutionPlan::ReturnedColumnList::iterator iter =
gwi.returnedCols.begin();
for (; iter != gwi.returnedCols.end(); iter++)
{
if ((*iter).get()->alias() == ord_item->name.str)
{
rc = (*iter).get()->clone();
nonAggField = rc->hasAggregate() ? false : true;
break;
}
}
}
}
else
rc = buildReturnedColumn(ord_item, gwi, gwi.fatalParseError);
// Looking for a match for this item in GROUP BY list.
if (rc && ord_item->type() == Item::FIELD_ITEM && nonAggField)
{
execplan::CalpontSelectExecutionPlan::ReturnedColumnList::iterator iter = gwi.groupByCols.begin();
for (; iter != gwi.groupByCols.end(); iter++)
{
if (rc->sameColumn((*iter).get()))
break;
}
// MCOL-1052 Find and remove the optimized field
// from ORDER using cond_pushed filters.
if (buildConstColFromFilter(dynamic_cast<SimpleColumn*>(rc), gwi, gi))
{
break;
}
// MCOL-1052 GROUP BY items list doesn't contain
// this ORDER BY item.
if (iter == gwi.groupByCols.end())
{
std::ostringstream ostream;
std::ostringstream& osr = ostream;
getColNameFromItem(osr, *ordercol->item);
Message::Args args;
args.add(ostream.str());
string emsg = IDBErrorInfo::instance()->errorMsg(ERR_NOT_GROUPBY_EXPRESSION, args);
gwi.parseErrorText = emsg;
setError(gwi.thd, ER_INTERNAL_ERROR, emsg, gwi);
return ERR_NOT_GROUPBY_EXPRESSION;
}
}
// @bug5501 try item_ptr if item can not be fixed. For some
// weird dml statement state, item can not be fixed but the
// infomation is available in item_ptr.
if (!rc || gwi.fatalParseError)
{
gwi.fatalParseError = false;
Item* item_ptr = ordercol->item_ptr;
while (item_ptr->type() == Item::REF_ITEM)
item_ptr = *(((Item_ref*)item_ptr)->ref);
rc = buildReturnedColumn(item_ptr, gwi, gwi.fatalParseError);
}
// This ORDER BY item must be an agg function -
// the ordercol->item_ptr and exteded SELECT list
// must contain the corresponding item.
if (!rc)
{
Item* item_ptr = ordercol->item_ptr;
if (item_ptr)
rc = buildReturnedColumn(item_ptr, gwi, gwi.fatalParseError);
}
if (!rc)
{
string emsg = IDBErrorInfo::instance()->errorMsg(ERR_NON_SUPPORT_ORDER_BY);
gwi.parseErrorText = emsg;
setError(gwi.thd, ER_CHECK_NOT_IMPLEMENTED, emsg, gwi);
return ER_CHECK_NOT_IMPLEMENTED;
}
}
if (ordercol->direction == ORDER::ORDER_ASC)
rc->asc(true);
else
rc->asc(false);
gwi.orderByCols.push_back(SRCP(rc));
}
// make sure columnmap, returnedcols and count(*) arg_list are not empty
TableMap::iterator tb_iter = gwi.tableMap.begin();
try
{
for (; tb_iter != gwi.tableMap.end(); tb_iter++)
{
if ((*tb_iter).second.first == 1)
continue;
CalpontSystemCatalog::TableAliasName tan = (*tb_iter).first;
CalpontSystemCatalog::TableName tn = make_table((*tb_iter).first.schema, (*tb_iter).first.table);
SimpleColumn* sc = getSmallestColumn(csc, tn, tan, (*tb_iter).second.second->table, gwi);
SRCP srcp(sc);
gwi.columnMap.insert(CalpontSelectExecutionPlan::ColumnMap::value_type(sc->columnName(), srcp));
(*tb_iter).second.first = 1;
}
}
catch (runtime_error& e)
{
setError(gwi.thd, ER_INTERNAL_ERROR, e.what(), gwi);
CalpontSystemCatalog::removeCalpontSystemCatalog(sessionID);
return ER_INTERNAL_ERROR;
}
catch (...)
{
string emsg = IDBErrorInfo::instance()->errorMsg(ERR_LOST_CONN_EXEMGR);
setError(gwi.thd, ER_INTERNAL_ERROR, emsg, gwi);
CalpontSystemCatalog::removeCalpontSystemCatalog(sessionID);
return ER_INTERNAL_ERROR;
}
if (!gwi.count_asterisk_list.empty() || !gwi.no_parm_func_list.empty() || gwi.returnedCols.empty())
{
// get the smallest column from colmap
CalpontSelectExecutionPlan::ColumnMap::const_iterator iter;
int minColWidth = 0;
CalpontSystemCatalog::ColType ct;
try
{
for (iter = gwi.columnMap.begin(); iter != gwi.columnMap.end(); ++iter)
{
// should always not null
SimpleColumn* sc = dynamic_cast<SimpleColumn*>(iter->second.get());
if (sc && !(sc->joinInfo() & JOIN_CORRELATED))
{
ct = csc->colType(sc->oid());
if (minColWidth == 0)
{
minColWidth = ct.colWidth;
minSc = iter->second;
}
else if (ct.colWidth < minColWidth)
{
minColWidth = ct.colWidth;
minSc = iter->second;
}
}
}
}
catch (...)
{
string emsg = IDBErrorInfo::instance()->errorMsg(ERR_LOST_CONN_EXEMGR);
setError(gwi.thd, ER_INTERNAL_ERROR, emsg, gwi);
CalpontSystemCatalog::removeCalpontSystemCatalog(sessionID);
return ER_INTERNAL_ERROR;
}
if (gwi.returnedCols.empty() && gwi.additionalRetCols.empty())
gwi.returnedCols.push_back(minSc);
}
if (!isUnion && !gwi.hasWindowFunc && gwi.subSelectType == CalpontSelectExecutionPlan::MAIN_SELECT)
{
// re-construct the select query and redo phase 1
if (redo)
{
TABLE_LIST* table_ptr = gi.groupByTables;
// put all tables, derived tables and views on the list
// TABLE_LIST* table_ptr = select_lex.get_table_list();
set<string> aliasSet; // to avoid duplicate table alias
for (; table_ptr; table_ptr = table_ptr->next_local)
{
if (string(table_ptr->table_name.str).find("$vtable") != string::npos)
continue;
if (table_ptr->derived)
{
if (aliasSet.find(table_ptr->alias.str) != aliasSet.end())
continue;
aliasSet.insert(table_ptr->alias.str);
}
else if (table_ptr->view)
{
if (aliasSet.find(table_ptr->alias.str) != aliasSet.end())
continue;
aliasSet.insert(table_ptr->alias.str);
}
else
{
// table referenced by view is represented by viewAlias_tableAlias.
// consistent with item.cc field print.
if (table_ptr->referencing_view)
{
if (aliasSet.find(string(table_ptr->referencing_view->alias.str) + "_" +
string(table_ptr->alias.str)) != aliasSet.end())
continue;
aliasSet.insert(string(table_ptr->referencing_view->alias.str) + "_" +
string(table_ptr->alias.str));
}
else
{
if (aliasSet.find(table_ptr->alias.str) != aliasSet.end())
continue;
aliasSet.insert(table_ptr->alias.str);
}
}
}
}
else
{
// remove order by clause in case this phase has been executed before.
// need a better fix later, like skip all the other non-optimized phase.
// MCOL-1052
if (unionSel)
{
ordercol = static_cast<ORDER*>(gi.groupByOrder);
}
else
ordercol = 0;
for (; ordercol; ordercol = ordercol->next)
{
Item* ord_item = *(ordercol->item);
if (ord_item->type() == Item::NULL_ITEM)
{
// MCOL-793 Do nothing for an ORDER BY NULL
}
else if (ord_item->type() == Item::SUM_FUNC_ITEM)
{
Item_sum* ifp = (Item_sum*)(*(ordercol->item));
ReturnedColumn* fc = buildAggregateColumn(ifp, gwi);
for (uint32_t i = 0; i < gwi.returnedCols.size(); i++)
{
if (fc->operator==(gwi.returnedCols[i].get()))
{
ostringstream oss;
oss << i + 1;
ord_cols += oss.str();
break;
}
}
// continue;
}
// @bug 3518. if order by clause = selected column, use position.
else if (ord_item->name.length && ord_item->type() == Item::FIELD_ITEM)
{
Item_field* field = static_cast<Item_field*>(ord_item);
string fullname;
if (field->db_name.str)
fullname += string(field->db_name.str) + ".";
if (field->table_name.str)
fullname += string(field->table_name.str) + ".";
if (field->field_name.length)
fullname += string(field->field_name.str);
uint32_t i = 0;
for (i = 0; i < gwi.returnedCols.size(); i++)
{
SimpleColumn* sc = dynamic_cast<SimpleColumn*>(gwi.returnedCols[i].get());
if (sc && ((Item_field*)ord_item)->cached_table &&
(strcasecmp(getViewName(((Item_field*)ord_item)->cached_table).c_str(),
sc->viewName().c_str()) != 0))
continue;
if (strcasecmp(fullname.c_str(), gwi.returnedCols[i]->alias().c_str()) == 0 ||
strcasecmp(ord_item->name.str, gwi.returnedCols[i]->alias().c_str()) == 0)
{
ostringstream oss;
oss << i + 1;
ord_cols += oss.str();
break;
}
}
if (i == gwi.returnedCols.size())
ord_cols += string(" `") + escapeBackTick(ord_item->name.str) + '`';
}
else if (ord_item->name.length)
{
// for union order by 1 case. For unknown reason, it doesn't show in_field_list
if (ord_item->type() == Item::CONST_ITEM && ord_item->cmp_type() == INT_RESULT)
{
ord_cols += ord_item->name.str;
}
else if (ord_item->type() == Item::SUBSELECT_ITEM)
{
string emsg = IDBErrorInfo::instance()->errorMsg(ERR_NON_SUPPORT_ORDER_BY);
setError(gwi.thd, ER_CHECK_NOT_IMPLEMENTED, emsg, gwi);
return ER_CHECK_NOT_IMPLEMENTED;
}
else
{
ord_cols += string(" `") + escapeBackTick(ord_item->name.str) + '`';
}
}
else if (ord_item->type() == Item::FUNC_ITEM)
{
// @bug5636. check if this order by column is on the select list
ReturnedColumn* rc = buildFunctionColumn((Item_func*)(ord_item), gwi, gwi.fatalParseError);
for (uint32_t i = 0; i < gwi.returnedCols.size(); i++)
{
if (rc && rc->operator==(gwi.returnedCols[i].get()))
{
ostringstream oss;
oss << i + 1;
ord_cols += oss.str();
break;
}
}
}
else
{
String str;
ord_item->print(&str, QT_ORDINARY);
ord_cols += string(str.c_ptr());
}
if (ordercol->direction != ORDER::ORDER_ASC)
ord_cols += " desc";
}
}
if (gwi.orderByCols.size()) // has order by
{
csep->hasOrderBy(true);
csep->specHandlerProcessed(true);
csep->orderByThreads(get_orderby_threads(gwi.thd));
}
}
// LIMIT and OFFSET are extracted from TABLE_LIST elements.
// All of JOIN-ed tables contain relevant limit and offset.
uint64_t limit = (uint64_t)-1;
if (gi.groupByTables->select_lex->limit_params.select_limit &&
(limit =
static_cast<Item_int*>(gi.groupByTables->select_lex->limit_params.select_limit)->val_int()) &&
limit != (uint64_t)-1)
{
csep->limitNum(limit);
}
else if (csep->hasOrderBy())
{
// We use LimitedOrderBy so set the limit to
// go through the check in addOrderByAndLimit
csep->limitNum((uint64_t)-2);
}
if (gi.groupByTables->select_lex->limit_params.offset_limit)
{
csep->limitStart(((Item_int*)gi.groupByTables->select_lex->limit_params.offset_limit)->val_int());
}
// We don't currently support limit with correlated subquery
if (csep->limitNum() != (uint64_t)-1 && gwi.subQuery && !gwi.correlatedTbNameVec.empty())
{
gwi.fatalParseError = true;
gwi.parseErrorText = IDBErrorInfo::instance()->errorMsg(ERR_NON_SUPPORT_LIMIT_SUB);
setError(gwi.thd, ER_INTERNAL_ERROR, gwi.parseErrorText, gwi);
return ER_CHECK_NOT_IMPLEMENTED;
}
} // ORDER BY processing ends here
if (gi.groupByDistinct)
csep->distinct(true);
// add the smallest column to count(*) parm.
// select constant in subquery case
std::vector<AggregateColumn*>::iterator coliter;
if (!minSc)
{
if (!gwi.returnedCols.empty())
minSc = gwi.returnedCols[0];
else if (!gwi.additionalRetCols.empty())
minSc = gwi.additionalRetCols[0];
}
// @bug3523, count(*) on subquery always pick column[0].
SimpleColumn* sc = dynamic_cast<SimpleColumn*>(minSc.get());
if (sc && sc->schemaName().empty())
{
if (gwi.derivedTbList.size() >= 1)
{
SimpleColumn* sc1 = new SimpleColumn();
sc1->columnName(sc->columnName());
sc1->tableName(sc->tableName());
sc1->tableAlias(sc->tableAlias());
sc1->viewName(sc->viewName());
sc1->timeZone(gwi.timeZone);
sc1->colPosition(0);
minSc.reset(sc1);
}
}
for (coliter = gwi.count_asterisk_list.begin(); coliter != gwi.count_asterisk_list.end(); ++coliter)
{
// @bug5977 @note should never throw this, but checking just in case.
// When ExeMgr fix is ready, this should not error out...
if (dynamic_cast<AggregateColumn*>(minSc.get()))
{
gwi.fatalParseError = true;
gwi.parseErrorText = "No project column found for aggregate function";
setError(gwi.thd, ER_INTERNAL_ERROR, gwi.parseErrorText, gwi);
return ER_CHECK_NOT_IMPLEMENTED;
}
// Replace the last (presumably constant) object with minSc
if ((*coliter)->aggParms().empty())
{
(*coliter)->aggParms().push_back(minSc);
}
else
{
(*coliter)->aggParms()[0] = minSc;
}
}
std::vector<FunctionColumn*>::iterator funciter;
SPTP sptp(new ParseTree(minSc.get()->clone()));
for (funciter = gwi.no_parm_func_list.begin(); funciter != gwi.no_parm_func_list.end(); ++funciter)
{
FunctionParm funcParms = (*funciter)->functionParms();
funcParms.push_back(sptp);
(*funciter)->functionParms(funcParms);
}
// set sequence# for subquery localCols
for (uint32_t i = 0; i < gwi.localCols.size(); i++)
gwi.localCols[i]->sequence(i);
// append additionalRetCols to returnedCols
gwi.returnedCols.insert(gwi.returnedCols.begin(), gwi.additionalRetCols.begin(),
gwi.additionalRetCols.end());
csep->groupByCols(gwi.groupByCols);
csep->orderByCols(gwi.orderByCols);
csep->returnedCols(gwi.returnedCols);
csep->columnMap(gwi.columnMap);
csep->having(havingFilter);
csep->derivedTableList(gwi.derivedTbList);
csep->selectSubList(selectSubList);
csep->subSelectList(gwi.subselectList);
clearStacks(gwi);
return 0;
}
} // namespace cal_impl_if