/* Copyright (C) 2014 InfiniDB, Inc. Copyright (C) 2016 MariaDB Corporaton This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; version 2 of the License. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ /*********************************************************************** * $Id: ha_window_function.cpp 9586 2013-05-31 22:02:06Z zzhu $ * * ***********************************************************************/ #include #include #include using namespace std; #include "idb_mysql.h" #include "ha_calpont_impl_if.h" #include "arithmeticcolumn.h" #include "arithmeticoperator.h" #include "constantcolumn.h" #include "functioncolumn.h" #include "intervalcolumn.h" #include "windowfunctioncolumn.h" using namespace execplan; #include "idberrorinfo.h" #include "errorids.h" using namespace logging; #include "functor.h" #include "funcexp.h" using namespace funcexp; #include "mcsv1_udaf.h" using namespace mcsv1sdk; namespace cal_impl_if { ReturnedColumn* nullOnError(gp_walk_info& gwi) { if (gwi.hasSubSelect) { gwi.parseErrorText = logging::IDBErrorInfo::instance()->errorMsg(logging::ERR_NON_SUPPORT_SELECT_SUB); setError(gwi.thd, ER_CHECK_NOT_IMPLEMENTED, gwi.parseErrorText); } if (gwi.parseErrorText.empty()) { gwi.parseErrorText = logging::IDBErrorInfo::instance()->errorMsg(logging::ERR_WF_NON_SUPPORT); setError(gwi.thd, ER_CHECK_NOT_IMPLEMENTED, gwi.parseErrorText); } return NULL; } WF_FRAME frame(Window_frame_bound::Bound_precedence_type bound, Item* offset) { switch (bound) { case Window_frame_bound::PRECEDING: if (offset) return WF_PRECEDING; else return WF_UNBOUNDED_PRECEDING; case Window_frame_bound::FOLLOWING: if (offset) return WF_FOLLOWING; else return WF_UNBOUNDED_FOLLOWING; case Window_frame_bound::CURRENT: // Offset is meaningless return WF_CURRENT_ROW; default: return WF_UNKNOWN; } } ReturnedColumn* buildBoundExp(WF_Boundary& bound, SRCP& order, gp_walk_info& gwi) { if (!(gwi.thd->infinidb_vtable.cal_conn_info)) gwi.thd->infinidb_vtable.cal_conn_info = (void*)(new cal_connection_info()); cal_connection_info* ci = reinterpret_cast(gwi.thd->infinidb_vtable.cal_conn_info); bool addOp = true; ReturnedColumn* rc = NULL; if (bound.fFrame == execplan::WF_PRECEDING) { if (order->asc()) addOp = false; } else if (!order->asc()) // must be WF_FOLLOWING addOp = false; funcexp::FunctionParm funcParms; SPTP sptp; IntervalColumn* intervalCol = dynamic_cast(bound.fVal.get()); // @todo error out non constant. only support literal interval for now. if (!intervalCol && order->resultType().colDataType == CalpontSystemCatalog::DATE) { intervalCol = new IntervalColumn(bound.fVal, (int)IntervalColumn::INTERVAL_DAY); bound.fVal.reset(intervalCol); } if (intervalCol) { // date_add rc = new FunctionColumn(); string funcName = "date_add_interval"; // @bug6061 . YEAR, QUARTER, MONTH, WEEK, DAY type CalpontSystemCatalog::ColType ct; if (order->resultType().colDataType == CalpontSystemCatalog::DATE && intervalCol->intervalType() <= IntervalColumn::INTERVAL_DAY) { ct.colDataType = CalpontSystemCatalog::DATE; ct.colWidth = 4; } else { ct.colDataType = CalpontSystemCatalog::DATETIME; ct.colWidth = 8; } // put interval val column to bound (dynamic_cast(rc))->functionName(funcName); sptp.reset(new ParseTree(order->clone())); funcParms.push_back(sptp); sptp.reset(new ParseTree(intervalCol->val()->clone())); funcParms.push_back(sptp); funcParms.push_back(getIntervalType(intervalCol->intervalType())); SRCP srcp(intervalCol->val()); bound.fVal = srcp; if (addOp) { sptp.reset(new ParseTree(new ConstantColumn("ADD"))); funcParms.push_back(sptp); } else { sptp.reset(new ParseTree(new ConstantColumn("SUB"))); funcParms.push_back(sptp); } (dynamic_cast(rc))->functionParms(funcParms); rc->resultType(ct); // @bug6061. Use result type as operation type for WF bound expression rc->operationType(ct); rc->expressionId(ci->expressionId++); return rc; } // arithmetic rc = new ArithmeticColumn(); ArithmeticOperator* aop; if (addOp) aop = new ArithmeticOperator("+"); else aop = new ArithmeticOperator("-"); ParseTree* pt = new ParseTree(aop); ParseTree* lhs = 0, *rhs = 0; lhs = new ParseTree(order->clone()); rhs = new ParseTree(bound.fVal->clone()); pt->left(lhs); pt->right(rhs); aop->resultType(order->resultType()); aop->operationType(aop->resultType()); (dynamic_cast(rc))->expression(pt); rc->resultType(aop->resultType()); rc->operationType(aop->operationType()); rc->expressionId(ci->expressionId++); return rc; } // Since columnstore implemented Windows Functions before MariaDB, we need // map from the enum MariaDB uses to the string that columnstore uses to // identify the function type. string ConvertFuncName(Item_sum* item) { switch (item->sum_func()) { case Item_sum::COUNT_FUNC: if (!item->arguments()[0]->name) return "COUNT(*)"; return "COUNT"; break; case Item_sum::COUNT_DISTINCT_FUNC: return "COUNT_DISTINCT"; break; case Item_sum::SUM_FUNC: return "SUM"; break; case Item_sum::SUM_DISTINCT_FUNC: return "SUM_DISTINCT"; break; case Item_sum::AVG_FUNC: return "AVG"; break; case Item_sum::AVG_DISTINCT_FUNC: return "AVG_DISTINCT"; break; case Item_sum::MIN_FUNC: return "MIN"; break; case Item_sum::MAX_FUNC: return "MAX"; break; case Item_sum::STD_FUNC: if (((Item_sum_variance*)item)->sample) return "STDDEV_SAMP"; else return "STDDEV_POP"; break; case Item_sum::VARIANCE_FUNC: if (((Item_sum_variance*)item)->sample) return "VAR_SAMP"; else return "VAR_POP"; break; case Item_sum::SUM_BIT_FUNC: if (strcmp(item->func_name(), "bit_or(") == 0) return "BIT_OR"; if (strcmp(item->func_name(), "bit_and(") == 0) return "BIT_AND"; if (strcmp(item->func_name(), "bit_xor(") == 0) return "BIT_XOR"; break; case Item_sum::UDF_SUM_FUNC: return "UDAF_FUNC"; break; case Item_sum::GROUP_CONCAT_FUNC: return "GROUP_CONCAT"; // Not supported break; case Item_sum::ROW_NUMBER_FUNC: return "ROW_NUMBER"; break; case Item_sum::RANK_FUNC: return "RANK"; break; case Item_sum::DENSE_RANK_FUNC: return "DENSE_RANK"; break; case Item_sum::PERCENT_RANK_FUNC: return "PERCENT_RANK"; break; case Item_sum::CUME_DIST_FUNC: return "CUME_DIST"; break; case Item_sum::NTILE_FUNC: return "NTILE"; break; case Item_sum::FIRST_VALUE_FUNC: return "FIRST_VALUE"; break; case Item_sum::LAST_VALUE_FUNC: return "LAST_VALUE"; break; case Item_sum::NTH_VALUE_FUNC: return "NTH_VALUE"; break; case Item_sum::LEAD_FUNC: return "LEAD"; break; case Item_sum::LAG_FUNC: return "LAG"; break; }; return ""; } ReturnedColumn* buildWindowFunctionColumn(Item* item, gp_walk_info& gwi, bool& nonSupport) { //@todo fix print for create view //String str; //item->print(&str, QT_INFINIDB_NO_QUOTE); //cout << str.c_ptr() << endl; if (!(gwi.thd->infinidb_vtable.cal_conn_info)) gwi.thd->infinidb_vtable.cal_conn_info = (void*)(new cal_connection_info()); cal_connection_info* ci = reinterpret_cast(gwi.thd->infinidb_vtable.cal_conn_info); gwi.hasWindowFunc = true; Item_window_func* wf = (Item_window_func*)item; Item_sum* item_sum = wf->window_func(); string funcName = ConvertFuncName(item_sum); WindowFunctionColumn* ac = new WindowFunctionColumn(funcName); ac->distinct(item_sum->has_with_distinct()); Window_spec* win_spec = wf->window_spec; SRCP srcp; // arguments vector funcParms; for (uint32_t i = 0; i < item_sum->argument_count(); i++) { srcp.reset(buildReturnedColumn((item_sum->arguments()[i]), gwi, nonSupport)); if (!srcp) return nullOnError(gwi); funcParms.push_back(srcp); if (gwi.clauseType == WHERE && !gwi.rcWorkStack.empty()) gwi.rcWorkStack.pop(); } // Setup UDAnF functions if (item_sum->sum_func() == Item_sum::UDF_SUM_FUNC) { Item_udf_sum* udfsum = (Item_udf_sum*)item_sum; mcsv1sdk::mcsv1Context& context = ac->getUDAFContext(); context.setName(udfsum->func_name()); // Set up the return type defaults for the call to init() execplan::CalpontSystemCatalog::ColType& rt = ac->resultType(); context.setResultType(rt.colDataType); context.setColWidth(rt.colWidth); context.setScale(rt.scale); context.setPrecision(rt.precision); // Turn on the Analytic flag so the function is aware it is being called // as a Window Function. context.setContextFlag(CONTEXT_IS_ANALYTIC); COL_TYPES colTypes; execplan::CalpontSelectExecutionPlan::ColumnMap::iterator cmIter; // Build the column type vector. for (size_t i = 0; i < funcParms.size(); ++i) { colTypes.push_back(make_pair(funcParms[i]->alias(), funcParms[i]->resultType().colDataType)); } // Call the user supplied init() if (context.getFunction()->init(&context, colTypes) == mcsv1_UDAF::ERROR) { gwi.fatalParseError = true; gwi.parseErrorText = context.getErrorMessage(); return NULL; } if (!context.getRunFlag(UDAF_OVER_REQUIRED) && !context.getRunFlag(UDAF_OVER_ALLOWED)) { gwi.parseErrorText = logging::IDBErrorInfo::instance()->errorMsg(logging::ERR_WF_UDANF_NOT_ALLOWED, context.getName()); return nullOnError(gwi); } // 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(); ac->resultType(ct); } // Some functions, such as LEAD/LAG don't have all parameters implemented in the // front end. Add dummies here to make the backend use defaults. // Some of these will be temporary until they are implemented in the front end. // Others need to stay because the back end expects them, but the front end // no longer sends them. // This case is kept in enum order in hopes the compiler can optimize switch (item_sum->sum_func()) { case Item_sum::UDF_SUM_FUNC: { uint64_t bIgnoreNulls = (ac->getUDAFContext().getRunFlag(mcsv1sdk::UDAF_IGNORE_NULLS)); char sIgnoreNulls[18]; sprintf(sIgnoreNulls, "%lu", bIgnoreNulls); srcp.reset(new ConstantColumn(sIgnoreNulls, (uint64_t)bIgnoreNulls, ConstantColumn::NUM)); // IGNORE/RESPECT NULLS. 1 => RESPECT funcParms.push_back(srcp); break; } case Item_sum::FIRST_VALUE_FUNC: srcp.reset(new ConstantColumn("1", (uint64_t)1, ConstantColumn::NUM)); // OFFSET (always one) funcParms.push_back(srcp); srcp.reset(new ConstantColumn("1", (uint64_t)1, ConstantColumn::NUM)); // FROM_FIRST funcParms.push_back(srcp); srcp.reset(new ConstantColumn("1", (uint64_t)1, ConstantColumn::NUM)); // IGNORE/RESPECT NULLS. 1 => RESPECT funcParms.push_back(srcp); break; case Item_sum::LAST_VALUE_FUNC: srcp.reset(new ConstantColumn("1", (uint64_t)1, ConstantColumn::NUM)); // OFFSET (always one) funcParms.push_back(srcp); srcp.reset(new ConstantColumn("0", (uint64_t)0, ConstantColumn::NUM)); // FROM_LAST funcParms.push_back(srcp); srcp.reset(new ConstantColumn("1", (uint64_t)1, ConstantColumn::NUM)); // IGNORE/RESPECT NULLS. 1 => RESPECT funcParms.push_back(srcp); break; case Item_sum::NTH_VALUE_FUNC: // When the front end supports these paramters, this needs modification srcp.reset(new ConstantColumn("1", (uint64_t)1, ConstantColumn::NUM)); // FROM FIRST/LAST 1 => FIRST funcParms.push_back(srcp); srcp.reset(new ConstantColumn("1", (uint64_t)1, ConstantColumn::NUM)); // IGNORE/RESPECT NULLS. 1 => RESPECT funcParms.push_back(srcp); break; case Item_sum::LEAD_FUNC: case Item_sum::LAG_FUNC: // When the front end supports these paramters, this needs modification srcp.reset(new ConstantColumn("", ConstantColumn::NULLDATA)); // Default to fill in for NULL values funcParms.push_back(srcp); srcp.reset(new ConstantColumn("1", (uint64_t)1, ConstantColumn::NUM)); // IGNORE/RESPECT NULLS. 1 => RESPECT funcParms.push_back(srcp); break; default: break; }; ac->functionParms(funcParms); // Partition by if (win_spec) { vector partitions; for (ORDER* ord = win_spec->partition_list->first; ord; ord = ord->next) { srcp.reset(buildReturnedColumn(*ord->item, gwi, nonSupport)); if (!srcp) return nullOnError(gwi); partitions.push_back(srcp); } ac->partitions(partitions); // Order by WF_OrderBy orderBy; // order columns if (win_spec->order_list) { // It is an error to have an order by clause if a UDAnF says it shouldn't if (item_sum->sum_func() == Item_sum::UDF_SUM_FUNC) { mcsv1sdk::mcsv1Context& context = ac->getUDAFContext(); if (!context.getRunFlag(UDAF_ORDER_ALLOWED)) { gwi.parseErrorText = logging::IDBErrorInfo::instance()->errorMsg(logging::ERR_WF_UDANF_ORDER_NOT_ALLOWED, context.getName()); return nullOnError(gwi); } } vector orders; ORDER* orderCol = reinterpret_cast(win_spec->order_list->first); for (; orderCol; orderCol = orderCol->next) { Item* orderItem = *(orderCol->item); srcp.reset(buildReturnedColumn(orderItem, gwi, nonSupport)); if (!srcp) return nullOnError(gwi); srcp->asc(orderCol->direction == ORDER::ORDER_ASC ? true : false); // srcp->nullsFirst(orderCol->nulls); // nulls 2-default, 1-nulls first, 0-nulls last srcp->nullsFirst(orderCol->direction == ORDER::ORDER_ASC ? 1 : 0); // WINDOWS TODO: implement NULLS FIRST/LAST in 10.2 front end orders.push_back(srcp); } orderBy.fOrders = orders; } else { if (item_sum->sum_func() == Item_sum::UDF_SUM_FUNC) { mcsv1sdk::mcsv1Context& context = ac->getUDAFContext(); if (context.getRunFlag(UDAF_ORDER_REQUIRED)) { gwi.parseErrorText = logging::IDBErrorInfo::instance()->errorMsg(logging::ERR_WF_UDANF_NOT_ALLOWED, context.getName()); return nullOnError(gwi); } } } // window frame WF_Frame frm; if (win_spec->window_frame) { // It is an error to have a frame clause if a UDAnF says it shouldn't if (item_sum->sum_func() == Item_sum::UDF_SUM_FUNC) { mcsv1sdk::mcsv1Context& context = ac->getUDAFContext(); if (!context.getRunFlag(UDAF_WINDOWFRAME_ALLOWED)) { gwi.parseErrorText = logging::IDBErrorInfo::instance()->errorMsg(logging::ERR_WF_UDANF_FRAME_NOT_ALLOWED, context.getName()); return nullOnError(gwi); } } frm.fIsRange = win_spec->window_frame->units == Window_frame::UNITS_RANGE; // start if (win_spec->window_frame->top_bound) { frm.fStart.fFrame = frame(win_spec->window_frame->top_bound->precedence_type, win_spec->window_frame->top_bound->offset); // offset NULL means UNBOUNDED if (win_spec->window_frame->top_bound->offset) { frm.fStart.fVal.reset(buildReturnedColumn(win_spec->window_frame->top_bound->offset, gwi, nonSupport)); if (!frm.fStart.fVal) return nullOnError(gwi); // 1. check expr is numeric type (rows) or interval (range) bool boundTypeErr = false; switch (frm.fStart.fVal->resultType().colDataType) { case CalpontSystemCatalog::CHAR: case CalpontSystemCatalog::VARCHAR: case CalpontSystemCatalog::VARBINARY: case CalpontSystemCatalog::BLOB: case CalpontSystemCatalog::TEXT: case CalpontSystemCatalog::CLOB: boundTypeErr = true; break; case CalpontSystemCatalog::DATE: case CalpontSystemCatalog::DATETIME: if (!frm.fIsRange) boundTypeErr = true; else if (dynamic_cast(frm.fStart.fVal.get()) == NULL) boundTypeErr = true; break; default: //okay break; } if (boundTypeErr) { gwi.fatalParseError = true; gwi.parseErrorText = logging::IDBErrorInfo::instance()->errorMsg(logging::ERR_WF_INVALID_BOUND_TYPE, colDataTypeToString(frm.fStart.fVal->resultType().colDataType)); return nullOnError(gwi); } } } // end if (win_spec->window_frame->bottom_bound) { frm.fEnd.fFrame = frame(win_spec->window_frame->bottom_bound->precedence_type, win_spec->window_frame->bottom_bound->offset); if (win_spec->window_frame->bottom_bound->offset) { frm.fEnd.fVal.reset(buildReturnedColumn(win_spec->window_frame->bottom_bound->offset, gwi, nonSupport)); if (!frm.fEnd.fVal) return nullOnError(gwi); // check expr is numeric type (rows) or interval (range) bool boundTypeErr = false; switch (frm.fEnd.fVal->resultType().colDataType) { case CalpontSystemCatalog::CHAR: case CalpontSystemCatalog::VARCHAR: case CalpontSystemCatalog::VARBINARY: case CalpontSystemCatalog::BLOB: case CalpontSystemCatalog::TEXT: case CalpontSystemCatalog::CLOB: boundTypeErr = true; break; case CalpontSystemCatalog::DATE: case CalpontSystemCatalog::DATETIME: if (!frm.fIsRange) boundTypeErr = true; else if (dynamic_cast(frm.fEnd.fVal.get()) == NULL) boundTypeErr = true; break; default: //okay break; } if (boundTypeErr) { gwi.fatalParseError = true; gwi.parseErrorText = logging::IDBErrorInfo::instance()->errorMsg(logging::ERR_WF_INVALID_BOUND_TYPE, colDataTypeToString(frm.fStart.fVal->resultType().colDataType)); return nullOnError(gwi); } } } else // no end specified. default end to current row { frm.fEnd.fFrame = WF_CURRENT_ROW; } if (frm.fStart.fVal || frm.fEnd.fVal) { // check order by key only 1 (should be error-ed out in parser. double check here) if (frm.fIsRange && orderBy.fOrders.size() > 1) { gwi.fatalParseError = true; gwi.parseErrorText = logging::IDBErrorInfo::instance()->errorMsg(logging::ERR_WF_INVALID_ORDER_KEY); return nullOnError(gwi); } // check order by key type is numeric or date/datetime bool orderTypeErr = false; if (frm.fIsRange && orderBy.fOrders.size() == 1) { switch (orderBy.fOrders[0]->resultType().colDataType) { case CalpontSystemCatalog::CHAR: case CalpontSystemCatalog::VARCHAR: case CalpontSystemCatalog::VARBINARY: case CalpontSystemCatalog::BLOB: case CalpontSystemCatalog::TEXT: case CalpontSystemCatalog::CLOB: orderTypeErr = true; break; default: //okay // interval bound has to have date/datetime order key if ((dynamic_cast(frm.fStart.fVal.get()) != NULL || dynamic_cast(frm.fEnd.fVal.get()) != NULL)) { if (orderBy.fOrders[0]->resultType().colDataType != CalpontSystemCatalog::DATE && orderBy.fOrders[0]->resultType().colDataType != CalpontSystemCatalog::DATETIME) orderTypeErr = true; } else { if (orderBy.fOrders[0]->resultType().colDataType == CalpontSystemCatalog::DATETIME) orderTypeErr = true; } break; } if (orderTypeErr) { gwi.fatalParseError = true; gwi.parseErrorText = logging::IDBErrorInfo::instance()->errorMsg(logging::ERR_WF_INVALID_ORDER_TYPE, colDataTypeToString(orderBy.fOrders[0]->resultType().colDataType)); return nullOnError(gwi); } } } // construct +,- or interval function for boundary if (frm.fIsRange && frm.fStart.fVal) { frm.fStart.fBound.reset(buildBoundExp(frm.fStart, orderBy.fOrders[0], gwi)); if (!frm.fStart.fBound) return nullOnError(gwi); } if (frm.fIsRange && frm.fEnd.fVal) { frm.fEnd.fBound.reset(buildBoundExp(frm.fEnd, orderBy.fOrders[0], gwi)); if (!frm.fEnd.fVal) return nullOnError(gwi); } } else { // Certain function types have different default boundaries // This case is kept in enum order in hopes the compiler can optimize switch (item_sum->sum_func()) { case Item_sum::COUNT_FUNC: case Item_sum::COUNT_DISTINCT_FUNC: case Item_sum::SUM_FUNC: case Item_sum::SUM_DISTINCT_FUNC: case Item_sum::AVG_FUNC: case Item_sum::AVG_DISTINCT_FUNC: frm.fStart.fFrame = WF_UNBOUNDED_PRECEDING; frm.fEnd.fFrame = WF_CURRENT_ROW; break; case Item_sum::MIN_FUNC: case Item_sum::MAX_FUNC: frm.fStart.fFrame = WF_UNBOUNDED_PRECEDING; // frm.fEnd.fFrame = WF_UNBOUNDED_FOLLOWING; frm.fEnd.fFrame = WF_CURRENT_ROW; break; case Item_sum::STD_FUNC: case Item_sum::VARIANCE_FUNC: case Item_sum::SUM_BIT_FUNC: frm.fStart.fFrame = WF_UNBOUNDED_PRECEDING; frm.fEnd.fFrame = WF_CURRENT_ROW; break; case Item_sum::UDF_SUM_FUNC: { // UDAnF functions each have their own default set in context. mcsv1sdk::mcsv1Context& context = ac->getUDAFContext(); if (context.getRunFlag(UDAF_WINDOWFRAME_REQUIRED)) { gwi.parseErrorText = logging::IDBErrorInfo::instance()->errorMsg(logging::ERR_WF_UDANF_FRAME_REQUIRED, context.getName()); return nullOnError(gwi); } int32_t bound; context.getStartFrame(frm.fStart.fFrame, bound); if (frm.fStart.fFrame == execplan::WF_PRECEDING) { if (bound == 0) bound = 1; srcp.reset(new ConstantColumn((int64_t)bound)); frm.fStart.fVal = srcp; frm.fStart.fBound.reset(buildBoundExp(frm.fStart, srcp, gwi)); if (!frm.fStart.fBound) return nullOnError(gwi); } context.getEndFrame(frm.fEnd.fFrame, bound); if (frm.fEnd.fFrame == execplan::WF_FOLLOWING) { if (bound == 0) bound = 1; srcp.reset(new ConstantColumn((int64_t)bound)); frm.fEnd.fVal = srcp; frm.fEnd.fBound.reset(buildBoundExp(frm.fEnd, srcp, gwi)); if (!frm.fEnd.fBound) return nullOnError(gwi); } break; } case Item_sum::GROUP_CONCAT_FUNC: frm.fStart.fFrame = WF_UNBOUNDED_PRECEDING; frm.fEnd.fFrame = WF_CURRENT_ROW; break; case Item_sum::ROW_NUMBER_FUNC: case Item_sum::RANK_FUNC: case Item_sum::DENSE_RANK_FUNC: case Item_sum::PERCENT_RANK_FUNC: case Item_sum::CUME_DIST_FUNC: case Item_sum::NTILE_FUNC: frm.fStart.fFrame = WF_UNBOUNDED_PRECEDING; frm.fEnd.fFrame = WF_UNBOUNDED_FOLLOWING; break; case Item_sum::FIRST_VALUE_FUNC: case Item_sum::LAST_VALUE_FUNC: case Item_sum::NTH_VALUE_FUNC: frm.fStart.fFrame = WF_UNBOUNDED_PRECEDING; frm.fEnd.fFrame = WF_CURRENT_ROW; break; case Item_sum::LEAD_FUNC: case Item_sum::LAG_FUNC: frm.fStart.fFrame = WF_UNBOUNDED_PRECEDING; frm.fEnd.fFrame = WF_UNBOUNDED_FOLLOWING; break; default: frm.fStart.fFrame = WF_UNBOUNDED_PRECEDING; frm.fEnd.fFrame = WF_CURRENT_ROW; break; }; } orderBy.fFrame = frm; ac->orderBy(orderBy); } if (gwi.fatalParseError || nonSupport) { if (gwi.parseErrorText.empty()) gwi.parseErrorText = logging::IDBErrorInfo::instance()->errorMsg(logging::ERR_WF_NON_SUPPORT); setError(gwi.thd, ER_CHECK_NOT_IMPLEMENTED, gwi.parseErrorText); return NULL; } ac->resultType(colType_MysqlToIDB(item_sum)); // bug5736. Make the result type double for some window functions when // infinidb_double_for_decimal_math is set. ac->adjustResultType(); ac->expressionId(ci->expressionId++); if (item->full_name()) ac->alias(item->full_name()); // put ac on windowFuncList gwi.windowFuncList.push_back(ac); return ac; } }