From d1ada753957adaef2d12889957d799966facea39 Mon Sep 17 00:00:00 2001 From: Gagan Goel Date: Sun, 30 Dec 2018 19:07:20 -0500 Subject: [PATCH 01/38] MCOL-270 Add support for MEDIUMINT data type --- dbcon/ddlpackage/ddl.l | 1 + dbcon/ddlpackage/ddl.y | 12 ++++++- dbcon/ddlpackage/ddlpkg.cpp | 16 +++++++-- dbcon/ddlpackageproc/altertableprocessor.cpp | 24 +++++++++++-- dbcon/execplan/calpontsystemcatalog.h | 11 ++++-- dbcon/joblist/tupleunion.cpp | 1 + dbcon/mysql/ha_calpont_ddl.cpp | 20 +++++++++-- dbcon/mysql/ha_calpont_dml.cpp | 2 ++ dbcon/mysql/ha_calpont_impl.cpp | 10 ++++++ primitives/linux-port/column.cpp | 4 +++ utils/dataconvert/dataconvert.cpp | 21 +++++++++++ utils/rowgroup/rowaggregation.cpp | 8 ++--- writeengine/bulk/we_bulkloadbuffer.cpp | 6 ++-- writeengine/bulk/we_columninfo.cpp | 2 ++ writeengine/index/we_indextree.cpp | 1 + writeengine/server/we_ddlcommon.h | 4 +++ writeengine/server/we_dmlcommandproc.cpp | 4 +++ writeengine/shared/we_convertor.cpp | 38 ++++++++++++++++---- writeengine/shared/we_type.h | 4 ++- writeengine/wrapper/we_colop.cpp | 6 ++++ writeengine/wrapper/writeengine.cpp | 22 ++++++++++++ writeengine/xml/we_xmljob.cpp | 12 +++++++ 22 files changed, 206 insertions(+), 23 deletions(-) diff --git a/dbcon/ddlpackage/ddl.l b/dbcon/ddlpackage/ddl.l index bb65715da..edc962a10 100644 --- a/dbcon/ddlpackage/ddl.l +++ b/dbcon/ddlpackage/ddl.l @@ -187,6 +187,7 @@ MEDIUMTEXT {return MEDIUMTEXT;} LONGTEXT {return LONGTEXT;} BOOL {return BOOL;} BOOLEAN {return BOOLEAN;} +MEDIUMINT {return MEDIUMINT;} \n { lineno++;} diff --git a/dbcon/ddlpackage/ddl.y b/dbcon/ddlpackage/ddl.y index 2b35c8392..cdd439927 100644 --- a/dbcon/ddlpackage/ddl.y +++ b/dbcon/ddlpackage/ddl.y @@ -112,7 +112,7 @@ MIN_ROWS MODIFY NO NOT NULL_TOK NUMBER NUMERIC ON PARTIAL PRECISION PRIMARY REFERENCES RENAME RESTRICT SET SMALLINT TABLE TEXT TINYBLOB TINYTEXT TINYINT TO UNIQUE UNSIGNED UPDATE USER SESSION_USER SYSTEM_USER VARCHAR VARBINARY VARYING WITH ZONE DOUBLE IDB_FLOAT REAL CHARSET IDB_IF EXISTS CHANGE TRUNCATE -BOOL BOOLEAN +BOOL BOOLEAN MEDIUMINT %token DQ_IDENT IDENT FCONST SCONST CP_SEARCH_CONDITION_TEXT ICONST DATE TIME @@ -1054,6 +1054,16 @@ exact_numeric_type: $$->fLength = DDLDatatypeLength[DDL_TINYINT]; $$->fPrecision = 1; } + | MEDIUMINT opt_display_width + { + $$ = new ColumnType(DDL_MEDINT); + $$->fLength = DDLDatatypeLength[DDL_MEDINT]; + } + | MEDIUMINT opt_display_width UNSIGNED + { + $$ = new ColumnType(DDL_UNSIGNED_MEDINT); + $$->fLength = DDLDatatypeLength[DDL_UNSIGNED_MEDINT]; + } ; /* Bug 1570, change default scale to 0 from -1 */ opt_precision_scale: diff --git a/dbcon/ddlpackage/ddlpkg.cpp b/dbcon/ddlpackage/ddlpkg.cpp index 1edb909ad..03794a304 100644 --- a/dbcon/ddlpackage/ddlpkg.cpp +++ b/dbcon/ddlpackage/ddlpkg.cpp @@ -115,9 +115,16 @@ ColumnType::ColumnType(int type) : fPrecision = 5; break; + case DDL_MEDINT: + fPrecision = 7; + break; + + case DDL_UNSIGNED_MEDINT: + fPrecision = 8; + break; + case DDL_INT: case DDL_UNSIGNED_INT: - case DDL_MEDINT: fPrecision = 10; break; @@ -202,7 +209,12 @@ void ColumnDef::convertDecimal() fType->fType = DDL_SMALLINT; fType->fLength = 2; } - else if (fType->fPrecision > 4 && fType->fPrecision < 10) + else if (fType->fPrecision > 4 && fType->fPrecision < 7) + { + fType->fType = DDL_MEDINT; + fType->fLength = 4; + } + else if (fType->fPrecision > 6 && fType->fPrecision < 10) { //dataType = CalpontSystemCatalog::INT; fType->fType = DDL_INT; diff --git a/dbcon/ddlpackageproc/altertableprocessor.cpp b/dbcon/ddlpackageproc/altertableprocessor.cpp index a398e656f..cc14424e2 100644 --- a/dbcon/ddlpackageproc/altertableprocessor.cpp +++ b/dbcon/ddlpackageproc/altertableprocessor.cpp @@ -139,7 +139,6 @@ bool typesAreSame(const CalpontSystemCatalog::ColType& colType, const ColumnType break; - // Don't think there can be such a type in syscat right now... case (CalpontSystemCatalog::MEDINT): if (newType.fType == DDL_MEDINT && colType.precision == newType.fPrecision && colType.scale == newType.fScale) return true; @@ -149,6 +148,12 @@ bool typesAreSame(const CalpontSystemCatalog::ColType& colType, const ColumnType // colType.scale == newType.fScale) return true; break; + case (CalpontSystemCatalog::UMEDINT): + if (newType.fType == DDL_UNSIGNED_MEDINT && colType.precision == newType.fPrecision && + colType.scale == newType.fScale) return true; + + break; + case (CalpontSystemCatalog::INT): if (newType.fType == DDL_INT && colType.precision == newType.fPrecision && colType.scale == newType.fScale) return true; @@ -2103,7 +2108,6 @@ void AlterTableProcessor::tableComment(uint32_t sessionID, execplan::CalpontSyst break; case CalpontSystemCatalog::INT: - case CalpontSystemCatalog::MEDINT: if (static_cast(nextVal) > MAX_INT) validated = false; @@ -2113,12 +2117,26 @@ void AlterTableProcessor::tableComment(uint32_t sessionID, execplan::CalpontSyst break; case CalpontSystemCatalog::UINT: - case CalpontSystemCatalog::UMEDINT: if (nextVal > MAX_UINT) validated = false; break; + case CalpontSystemCatalog::MEDINT: + if (static_cast(nextVal) > MAX_MEDINT) + validated = false; + + if (static_cast(nextVal) < 1) + negative = true; + + break; + + case CalpontSystemCatalog::UMEDINT: + if (nextVal > MAX_UMEDINT) + validated = false; + + break; + case CalpontSystemCatalog::SMALLINT: if (static_cast(nextVal) > MAX_SMALLINT) validated = false; diff --git a/dbcon/execplan/calpontsystemcatalog.h b/dbcon/execplan/calpontsystemcatalog.h index 7b828a297..debf7dd07 100644 --- a/dbcon/execplan/calpontsystemcatalog.h +++ b/dbcon/execplan/calpontsystemcatalog.h @@ -58,6 +58,8 @@ const int64_t MIN_TINYINT __attribute__ ((unused)) = std::numeric_limits const int64_t MAX_TINYINT __attribute__ ((unused)) = std::numeric_limits::max(); //127; const int64_t MIN_SMALLINT __attribute__ ((unused)) = std::numeric_limits::min() + 2; //-32766; const int64_t MAX_SMALLINT __attribute__ ((unused)) = std::numeric_limits::max(); //32767; +const int64_t MIN_MEDINT __attribute__ ((unused)) = -(1ULL << 23); //-8388608; +const int64_t MAX_MEDINT __attribute__ ((unused)) = (1ULL << 23) - 1; //8388607; const int64_t MIN_INT __attribute__ ((unused)) = std::numeric_limits::min() + 2; //-2147483646; const int64_t MAX_INT __attribute__ ((unused)) = std::numeric_limits::max(); //2147483647; const int64_t MIN_BIGINT __attribute__ ((unused)) = std::numeric_limits::min() + 2; //-9223372036854775806LL; @@ -66,10 +68,12 @@ const int64_t MAX_BIGINT __attribute__ ((unused)) = std::numeric_limits::max() - 2; //4294967293 const uint64_t MAX_UTINYINT __attribute__ ((unused)) = std::numeric_limits::max() - 2; //253; const uint64_t MAX_USMALLINT __attribute__ ((unused)) = std::numeric_limits::max() - 2; //65533; +const uint64_t MAX_UMEDINT __attribute__ ((unused)) = (1ULL << 24) - 1; //16777215 const uint64_t MAX_UBIGINT __attribute__ ((unused)) = std::numeric_limits::max() - 2; //18446744073709551613 const float MAX_FLOAT __attribute__ ((unused)) = std::numeric_limits::max(); //3.402823466385289e+38 @@ -969,8 +973,8 @@ inline bool isCharType(const execplan::CalpontSystemCatalog::ColDataType type) execplan::CalpontSystemCatalog::TEXT == type); } -/** convenience function to determine if column type is an - * unsigned type +/** convenience function to determine if column type is a + * numeric type */ inline bool isNumeric(const execplan::CalpontSystemCatalog::ColDataType type) { @@ -999,6 +1003,9 @@ inline bool isNumeric(const execplan::CalpontSystemCatalog::ColDataType type) } } +/** convenience function to determine if column type is an + * unsigned type + */ inline bool isUnsigned(const execplan::CalpontSystemCatalog::ColDataType type) { switch (type) diff --git a/dbcon/joblist/tupleunion.cpp b/dbcon/joblist/tupleunion.cpp index d0892e45f..3b7232c62 100644 --- a/dbcon/joblist/tupleunion.cpp +++ b/dbcon/joblist/tupleunion.cpp @@ -1068,6 +1068,7 @@ void TupleUnion::writeNull(Row* out, uint32_t col) out->setUintField<4>(joblist::INTNULL, col); break; + case CalpontSystemCatalog::UMEDINT: case CalpontSystemCatalog::UINT: out->setUintField<4>(joblist::UINTNULL, col); break; diff --git a/dbcon/mysql/ha_calpont_ddl.cpp b/dbcon/mysql/ha_calpont_ddl.cpp index 487e0d4f8..dd6206869 100644 --- a/dbcon/mysql/ha_calpont_ddl.cpp +++ b/dbcon/mysql/ha_calpont_ddl.cpp @@ -198,6 +198,10 @@ uint32_t convertDataType(int dataType) calpontDataType = CalpontSystemCatalog::USMALLINT; break; + case ddlpackage::DDL_UNSIGNED_MEDINT: + calpontDataType = CalpontSystemCatalog::UMEDINT; + break; + case ddlpackage::DDL_UNSIGNED_INT: calpontDataType = CalpontSystemCatalog::UINT; break; @@ -323,21 +327,33 @@ bool validateNextValue( int type, int64_t value ) case ddlpackage::DDL_INT: case ddlpackage::DDL_INTEGER: - case ddlpackage::DDL_MEDINT: { if (value > MAX_INT) validValue = false; } break; + case ddlpackage::DDL_MEDINT: + { + if (value > MAX_MEDINT) + validValue = false; + } + break; + case ddlpackage::DDL_UNSIGNED_INT: - case ddlpackage::DDL_UNSIGNED_MEDINT: { if (static_cast(value) > MAX_UINT) validValue = false; } break; + case ddlpackage::DDL_UNSIGNED_MEDINT: + { + if (static_cast(value) > MAX_UMEDINT) + validValue = false; + } + break; + case ddlpackage::DDL_SMALLINT: { if (value > MAX_SMALLINT) diff --git a/dbcon/mysql/ha_calpont_dml.cpp b/dbcon/mysql/ha_calpont_dml.cpp index 43bdc8cd6..b5bbe57aa 100644 --- a/dbcon/mysql/ha_calpont_dml.cpp +++ b/dbcon/mysql/ha_calpont_dml.cpp @@ -1062,6 +1062,7 @@ int ha_calpont_impl_write_batch_row_(uchar* buf, TABLE* table, cal_impl_if::cal_ } case CalpontSystemCatalog::INT: + case CalpontSystemCatalog::MEDINT: { if (nullVal && (ci.columnTypes[colpos].constraintType != CalpontSystemCatalog::NOTNULL_CONSTRAINT)) fprintf(ci.filePtr, "%c", ci.delimiter); @@ -1073,6 +1074,7 @@ int ha_calpont_impl_write_batch_row_(uchar* buf, TABLE* table, cal_impl_if::cal_ } case CalpontSystemCatalog::UINT: + case CalpontSystemCatalog::UMEDINT: { if (nullVal && (ci.columnTypes[colpos].constraintType != CalpontSystemCatalog::NOTNULL_CONSTRAINT)) fprintf(ci.filePtr, "%c", ci.delimiter); diff --git a/dbcon/mysql/ha_calpont_impl.cpp b/dbcon/mysql/ha_calpont_impl.cpp index 3be44d2b8..c3a4cfd02 100644 --- a/dbcon/mysql/ha_calpont_impl.cpp +++ b/dbcon/mysql/ha_calpont_impl.cpp @@ -220,6 +220,14 @@ void storeNumericField(Field** f, int64_t value, CalpontSystemCatalog::ColType& break; } + case MYSQL_TYPE_INT24: //MEDINT type + { + Field_medium* f2 = (Field_medium*)*f; + longlong int_val = (longlong)value; + f2->store(int_val, f2->unsigned_flag); + break; + } + case MYSQL_TYPE_LONG: //INT type { Field_long* f2 = (Field_long*)*f; @@ -592,6 +600,7 @@ int fetchNextRow(uchar* buf, cal_table_info& ti, cal_connection_info* ci, bool h } case CalpontSystemCatalog::INT: + case CalpontSystemCatalog::MEDINT: { intColVal = row.getIntField<4>(s); storeNumericField(f, intColVal, colType); @@ -599,6 +608,7 @@ int fetchNextRow(uchar* buf, cal_table_info& ti, cal_connection_info* ci, bool h } case CalpontSystemCatalog::UINT: + case CalpontSystemCatalog::UMEDINT: { uintColVal = row.getUintField<4>(s); storeNumericField(f, uintColVal, colType); diff --git a/primitives/linux-port/column.cpp b/primitives/linux-port/column.cpp index 2b4450d2c..8832a6448 100644 --- a/primitives/linux-port/column.cpp +++ b/primitives/linux-port/column.cpp @@ -325,6 +325,7 @@ inline bool isEmptyVal<4>(uint8_t type, const uint8_t* ival) return (joblist::CHAR4EMPTYROW == *val); case CalpontSystemCatalog::UINT: + case CalpontSystemCatalog::UMEDINT: return (joblist::UINTEMPTYROW == *val); default: @@ -445,6 +446,7 @@ inline bool isNullVal<4>(uint8_t type, const uint8_t* ival) return (joblist::DATENULL == *val); case CalpontSystemCatalog::UINT: + case CalpontSystemCatalog::UMEDINT: return (joblist::UINTNULL == *val); default: @@ -550,6 +552,7 @@ inline bool isMinMaxValid(const NewColRequestHeader* in) case CalpontSystemCatalog::TINYINT: case CalpontSystemCatalog::SMALLINT: + case CalpontSystemCatalog::MEDINT: case CalpontSystemCatalog::INT: case CalpontSystemCatalog::DATE: case CalpontSystemCatalog::BIGINT: @@ -557,6 +560,7 @@ inline bool isMinMaxValid(const NewColRequestHeader* in) case CalpontSystemCatalog::TIME: case CalpontSystemCatalog::UTINYINT: case CalpontSystemCatalog::USMALLINT: + case CalpontSystemCatalog::UMEDINT: case CalpontSystemCatalog::UINT: case CalpontSystemCatalog::UBIGINT: return true; diff --git a/utils/dataconvert/dataconvert.cpp b/utils/dataconvert/dataconvert.cpp index 1d196a436..6566646da 100644 --- a/utils/dataconvert/dataconvert.cpp +++ b/utils/dataconvert/dataconvert.cpp @@ -330,6 +330,19 @@ int64_t number_int_value(const string& data, break; case CalpontSystemCatalog::MEDINT: + if (intVal < MIN_MEDINT) + { + intVal = MIN_MEDINT; + pushwarning = true; + } + else if (intVal > MAX_MEDINT) + { + intVal = MAX_MEDINT; + pushwarning = true; + } + + break; + case CalpontSystemCatalog::INT: if (intVal < MIN_INT) { @@ -559,6 +572,14 @@ uint64_t number_uint_value(const string& data, break; case CalpontSystemCatalog::UMEDINT: + if (uintVal > MAX_UMEDINT) + { + uintVal = MAX_UMEDINT; + pushwarning = true; + } + + break; + case CalpontSystemCatalog::UINT: if (uintVal > MAX_UINT) { diff --git a/utils/rowgroup/rowaggregation.cpp b/utils/rowgroup/rowaggregation.cpp index 8a80ca683..d6aa253a6 100644 --- a/utils/rowgroup/rowaggregation.cpp +++ b/utils/rowgroup/rowaggregation.cpp @@ -2757,7 +2757,6 @@ void RowAggregationUM::SetUDAFValue(static_any::any& valOut, int64_t colOut) break; case execplan::CalpontSystemCatalog::SMALLINT: - case execplan::CalpontSystemCatalog::MEDINT: if (valOut.compatible(shortTypeId)) { intOut = valOut.cast(); @@ -2767,6 +2766,7 @@ void RowAggregationUM::SetUDAFValue(static_any::any& valOut, int64_t colOut) break; + case execplan::CalpontSystemCatalog::MEDINT: case execplan::CalpontSystemCatalog::INT: if (valOut.compatible(uintTypeId)) { @@ -2809,7 +2809,6 @@ void RowAggregationUM::SetUDAFValue(static_any::any& valOut, int64_t colOut) break; case execplan::CalpontSystemCatalog::USMALLINT: - case execplan::CalpontSystemCatalog::UMEDINT: if (valOut.compatible(ushortTypeId)) { uintOut = valOut.cast(); @@ -2819,6 +2818,7 @@ void RowAggregationUM::SetUDAFValue(static_any::any& valOut, int64_t colOut) break; + case execplan::CalpontSystemCatalog::UMEDINT: case execplan::CalpontSystemCatalog::UINT: if (valOut.compatible(uintTypeId)) { @@ -3030,10 +3030,10 @@ void RowAggregationUM::SetUDAFAnyValue(static_any::any& valOut, int64_t colOut) break; case execplan::CalpontSystemCatalog::SMALLINT: - case execplan::CalpontSystemCatalog::MEDINT: fRow.setIntField<2>(intOut, colOut); break; + case execplan::CalpontSystemCatalog::MEDINT: case execplan::CalpontSystemCatalog::INT: fRow.setIntField<4>(intOut, colOut); break; @@ -3049,10 +3049,10 @@ void RowAggregationUM::SetUDAFAnyValue(static_any::any& valOut, int64_t colOut) break; case execplan::CalpontSystemCatalog::USMALLINT: - case execplan::CalpontSystemCatalog::UMEDINT: fRow.setUintField<2>(uintOut, colOut); break; + case execplan::CalpontSystemCatalog::UMEDINT: case execplan::CalpontSystemCatalog::UINT: fRow.setUintField<4>(uintOut, colOut); break; diff --git a/writeengine/bulk/we_bulkloadbuffer.cpp b/writeengine/bulk/we_bulkloadbuffer.cpp index da568f9a6..f0336d79d 100644 --- a/writeengine/bulk/we_bulkloadbuffer.cpp +++ b/writeengine/bulk/we_bulkloadbuffer.cpp @@ -1224,8 +1224,9 @@ void BulkLoadBuffer::convert(char* field, int fieldLength, } //---------------------------------------------------------------------- - // UNSIGNED INTEGER + // UNSIGNED MEDIUM INTEGER AND UNSIGNED INTEGER //---------------------------------------------------------------------- + case WriteEngine::WR_UMEDINT : case WriteEngine::WR_UINT : { int64_t origVal; @@ -1306,8 +1307,9 @@ void BulkLoadBuffer::convert(char* field, int fieldLength, } //---------------------------------------------------------------------- - // INTEGER + // MEDIUM INTEGER AND INTEGER //---------------------------------------------------------------------- + case WriteEngine::WR_MEDINT : case WriteEngine::WR_INT : default : { diff --git a/writeengine/bulk/we_columninfo.cpp b/writeengine/bulk/we_columninfo.cpp index 9c1a1a9e2..4c66e35ff 100644 --- a/writeengine/bulk/we_columninfo.cpp +++ b/writeengine/bulk/we_columninfo.cpp @@ -197,10 +197,12 @@ ColumnInfo::ColumnInfo(Log* logger, case WriteEngine::WR_SHORT: case WriteEngine::WR_BYTE: case WriteEngine::WR_LONGLONG: + case WriteEngine::WR_MEDINT: case WriteEngine::WR_INT: case WriteEngine::WR_USHORT: case WriteEngine::WR_UBYTE: case WriteEngine::WR_ULONGLONG: + case WriteEngine::WR_UMEDINT: case WriteEngine::WR_UINT: default: { diff --git a/writeengine/index/we_indextree.cpp b/writeengine/index/we_indextree.cpp index a2dd97789..0b927c9e6 100644 --- a/writeengine/index/we_indextree.cpp +++ b/writeengine/index/we_indextree.cpp @@ -1182,6 +1182,7 @@ const int IndexTree::setBitsetColumn( void* val, const int pos, const int width, break; case WriteEngine::WR_INT : + case WriteEngine::WR_MEDINT : default : memcpy( m_multiColKey.keyBuf + m_multiColKey.totalBit / 8, (int*) val, copyLen ); m_multiColKey.curBitset = *((int*) val); diff --git a/writeengine/server/we_ddlcommon.h b/writeengine/server/we_ddlcommon.h index 1d482b442..b1e4e48f1 100644 --- a/writeengine/server/we_ddlcommon.h +++ b/writeengine/server/we_ddlcommon.h @@ -464,6 +464,10 @@ inline int convertDataType(int dataType) calpontDataType = CalpontSystemCatalog::USMALLINT; break; + case ddlpackage::DDL_UNSIGNED_MEDINT: + calpontDataType = CalpontSystemCatalog::UMEDINT; + break; + case ddlpackage::DDL_UNSIGNED_INT: calpontDataType = CalpontSystemCatalog::UINT; break; diff --git a/writeengine/server/we_dmlcommandproc.cpp b/writeengine/server/we_dmlcommandproc.cpp index 057ef2504..63cf70092 100644 --- a/writeengine/server/we_dmlcommandproc.cpp +++ b/writeengine/server/we_dmlcommandproc.cpp @@ -3027,6 +3027,8 @@ uint8_t WE_DMLCommandProc::processUpdate(messageqcpp::ByteStream& bs, case CalpontSystemCatalog::UBIGINT: case CalpontSystemCatalog::INT: case CalpontSystemCatalog::UINT: + case CalpontSystemCatalog::MEDINT: + case CalpontSystemCatalog::UMEDINT: case CalpontSystemCatalog::SMALLINT: case CalpontSystemCatalog::USMALLINT: case CalpontSystemCatalog::TINYINT: @@ -3362,6 +3364,8 @@ uint8_t WE_DMLCommandProc::processUpdate(messageqcpp::ByteStream& bs, case CalpontSystemCatalog::UBIGINT: case CalpontSystemCatalog::INT: case CalpontSystemCatalog::UINT: + case CalpontSystemCatalog::MEDINT: + case CalpontSystemCatalog::UMEDINT: case CalpontSystemCatalog::SMALLINT: case CalpontSystemCatalog::USMALLINT: case CalpontSystemCatalog::TINYINT: diff --git a/writeengine/shared/we_convertor.cpp b/writeengine/shared/we_convertor.cpp index 8050426f3..73340ff13 100644 --- a/writeengine/shared/we_convertor.cpp +++ b/writeengine/shared/we_convertor.cpp @@ -404,8 +404,12 @@ void Convertor::convertColType(CalpontSystemCatalog::ColDataType dataType, internalType = WriteEngine::WR_SHORT; break; - // Map MEDINT, INT, and DATE to WR_INT + // Map MEDINT to WR_MEDINT case CalpontSystemCatalog::MEDINT : + internalType = WriteEngine::WR_MEDINT; + break; + + // Map INT, and DATE to WR_INT case CalpontSystemCatalog::INT : case CalpontSystemCatalog::DATE : internalType = WriteEngine::WR_INT; @@ -469,8 +473,12 @@ void Convertor::convertColType(CalpontSystemCatalog::ColDataType dataType, internalType = WriteEngine::WR_USHORT; break; - // Map UMEDINT and UINT to WR_UINT + // Map UMEDINT to WR_UMEDINT case CalpontSystemCatalog::UMEDINT: + internalType = WriteEngine::WR_UMEDINT; + break; + + // Map UINT to WR_UINT case CalpontSystemCatalog::UINT: internalType = WriteEngine::WR_UINT; break; @@ -512,7 +520,12 @@ void Convertor::convertWEColType(ColType internalType, dataType = CalpontSystemCatalog::SMALLINT; break; - // Map MEDINT, INT, and DATE to WR_INT + // Map MEDINT to WR_MEDINT + case WriteEngine::WR_MEDINT : + dataType = CalpontSystemCatalog::MEDINT; + break; + + // Map INT, and DATE to WR_INT case WriteEngine::WR_INT : dataType = CalpontSystemCatalog::INT; break; @@ -562,7 +575,12 @@ void Convertor::convertWEColType(ColType internalType, dataType = CalpontSystemCatalog::USMALLINT; break; - // Map UMEDINT and UINT to WR_UINT + // Map UMEDINT to WR_UMEDINT + case WriteEngine::WR_UMEDINT: + dataType = CalpontSystemCatalog::UMEDINT; + break; + + // Map UINT to WR_UINT case WriteEngine::WR_UINT: dataType = CalpontSystemCatalog::UINT; break; @@ -614,8 +632,12 @@ void Convertor::convertColType(ColStruct* curStruct) *internalType = WriteEngine::WR_SHORT; break; - // Map MEDINT, INT, and DATE to WR_INT + // Map MEDINT to WR_MEDINT case CalpontSystemCatalog::MEDINT : + *internalType = WriteEngine::WR_MEDINT; + break; + + // Map INT, and DATE to WR_INT case CalpontSystemCatalog::INT : case CalpontSystemCatalog::DATE : *internalType = WriteEngine::WR_INT; @@ -698,8 +720,12 @@ void Convertor::convertColType(ColStruct* curStruct) *internalType = WriteEngine::WR_USHORT; break; - // Map UMEDINT and UINT to WR_UINT + // Map UMEDINT to WR_UMEDINT case CalpontSystemCatalog::UMEDINT: + *internalType = WriteEngine::WR_UMEDINT; + break; + + // Map UINT to WR_UINT case CalpontSystemCatalog::UINT: *internalType = WriteEngine::WR_UINT; break; diff --git a/writeengine/shared/we_type.h b/writeengine/shared/we_type.h index 06a367ecb..8a8cae5a9 100644 --- a/writeengine/shared/we_type.h +++ b/writeengine/shared/we_type.h @@ -108,7 +108,9 @@ enum ColType /** @brief Column type enumeration*/ WR_USHORT = 14, /** @brief Unsigned Short */ WR_UINT = 15, /** @brief Unsigned Int */ WR_ULONGLONG = 16, /** @brief Unsigned Long long*/ - WR_TEXT = 17 /** @brief TEXT */ + WR_TEXT = 17, /** @brief TEXT */ + WR_MEDINT = 18, /** @brief Medium Int */ + WR_UMEDINT = 19 /** @brief Unsigned Medium Int */ }; // Describes relation of field to column for a bulk load diff --git a/writeengine/wrapper/we_colop.cpp b/writeengine/wrapper/we_colop.cpp index ffd01df2e..4f0e6a0d2 100644 --- a/writeengine/wrapper/we_colop.cpp +++ b/writeengine/wrapper/we_colop.cpp @@ -1618,6 +1618,7 @@ int ColumnOp::writeRow(Column& curCol, uint64_t totalRow, const RID* rowIdArray, break; case WriteEngine::WR_INT : + case WriteEngine::WR_MEDINT : if (!bDelete) pVal = &((int*) valArray)[i]; //pOldVal = &((int *) oldValArray)[i]; @@ -1636,6 +1637,7 @@ int ColumnOp::writeRow(Column& curCol, uint64_t totalRow, const RID* rowIdArray, break; case WriteEngine::WR_UINT : + case WriteEngine::WR_UMEDINT : if (!bDelete) pVal = &((uint32_t*) valArray)[i]; //pOldVal = &((uint8_t *) oldValArray)[i]; @@ -1789,6 +1791,7 @@ int ColumnOp::writeRows(Column& curCol, uint64_t totalRow, const RIDList& ridLis break; case WriteEngine::WR_INT : + case WriteEngine::WR_MEDINT : if (!bDelete) pVal = &((int*) valArray)[0]; //pOldVal = &((int *) oldValArray)[i]; @@ -1813,6 +1816,7 @@ int ColumnOp::writeRows(Column& curCol, uint64_t totalRow, const RIDList& ridLis break; case WriteEngine::WR_UINT : + case WriteEngine::WR_UMEDINT : if (!bDelete) pVal = &((uint32_t*) valArray)[0]; //pOldVal = &((uint32_t *) oldValArray)[i]; @@ -1944,6 +1948,7 @@ int ColumnOp::writeRowsValues(Column& curCol, uint64_t totalRow, const RIDList& break; case WriteEngine::WR_INT : + case WriteEngine::WR_MEDINT : pVal = &((int*) valArray)[i]; break; @@ -1960,6 +1965,7 @@ int ColumnOp::writeRowsValues(Column& curCol, uint64_t totalRow, const RIDList& break; case WriteEngine::WR_UINT : + case WriteEngine::WR_UMEDINT : pVal = &((uint32_t*) valArray)[i]; break; diff --git a/writeengine/wrapper/writeengine.cpp b/writeengine/wrapper/writeengine.cpp index 6386d70c4..62842da0d 100644 --- a/writeengine/wrapper/writeengine.cpp +++ b/writeengine/wrapper/writeengine.cpp @@ -261,6 +261,7 @@ void WriteEngineWrapper::convertValue(const ColType colType, void* value, boost: switch (colType) { case WriteEngine::WR_INT : + case WriteEngine::WR_MEDINT : if (data.type() == typeid(int)) { int val = boost::any_cast(data); @@ -277,6 +278,7 @@ void WriteEngineWrapper::convertValue(const ColType colType, void* value, boost: break; case WriteEngine::WR_UINT : + case WriteEngine::WR_UMEDINT : { uint32_t val = boost::any_cast(data); size = sizeof(uint32_t); @@ -410,6 +412,7 @@ void WriteEngineWrapper::convertValue(const ColType colType, void* valArray, con switch (colType) { case WriteEngine::WR_INT : + case WriteEngine::WR_MEDINT : if (data.type() == typeid(long)) ((int*)valArray)[pos] = static_cast(boost::any_cast(data)); else if (data.type() == typeid(int)) @@ -420,6 +423,7 @@ void WriteEngineWrapper::convertValue(const ColType colType, void* valArray, con break; case WriteEngine::WR_UINT : + case WriteEngine::WR_UMEDINT : ((uint32_t*)valArray)[pos] = boost::any_cast(data); break; @@ -495,10 +499,12 @@ void WriteEngineWrapper::convertValue(const ColType colType, void* valArray, con switch (colType) { case WriteEngine::WR_INT : + case WriteEngine::WR_MEDINT : data = ((int*)valArray)[pos]; break; case WriteEngine::WR_UINT : + case WriteEngine::WR_UMEDINT : data = ((uint64_t*)valArray)[pos]; break; @@ -814,10 +820,12 @@ int WriteEngineWrapper::deleteBadRows(const TxnID& txnid, ColStructList& colStru switch (colStructs[i].colType) { case WriteEngine::WR_INT: + case WriteEngine::WR_MEDINT: valArray = (int*) calloc(sizeof(int), 1); break; case WriteEngine::WR_UINT: + case WriteEngine::WR_UMEDINT: valArray = (uint32_t*) calloc(sizeof(uint32_t), 1); break; @@ -4484,10 +4492,12 @@ int WriteEngineWrapper::writeColumnRecords(const TxnID& txnid, switch (curColStruct.colType) { case WriteEngine::WR_INT: + case WriteEngine::WR_MEDINT: valArray = (int*) calloc(sizeof(int), totalRow); break; case WriteEngine::WR_UINT: + case WriteEngine::WR_UMEDINT: valArray = (uint32_t*) calloc(sizeof(uint32_t), totalRow); break; @@ -4721,10 +4731,12 @@ int WriteEngineWrapper::writeColumnRec(const TxnID& txnid, switch (colStructList[i].colType) { case WriteEngine::WR_INT: + case WriteEngine::WR_MEDINT: valArray = (int*) calloc(sizeof(int), totalRow1); break; case WriteEngine::WR_UINT: + case WriteEngine::WR_UMEDINT: valArray = (uint32_t*) calloc(sizeof(uint32_t), totalRow1); break; @@ -4894,10 +4906,12 @@ int WriteEngineWrapper::writeColumnRec(const TxnID& txnid, switch (newColStructList[i].colType) { case WriteEngine::WR_INT: + case WriteEngine::WR_MEDINT: valArray = (int*) calloc(sizeof(int), totalRow2); break; case WriteEngine::WR_UINT: + case WriteEngine::WR_UMEDINT: valArray = (uint32_t*) calloc(sizeof(uint32_t), totalRow2); break; @@ -5065,10 +5079,12 @@ int WriteEngineWrapper::writeColumnRec(const TxnID& txnid, switch (colStructList[i].colType) { case WriteEngine::WR_INT: + case WriteEngine::WR_MEDINT: valArray = (int*) calloc(sizeof(int), totalRow1); break; case WriteEngine::WR_UINT: + case WriteEngine::WR_UMEDINT: valArray = (uint32_t*) calloc(sizeof(uint32_t), totalRow1); break; @@ -5308,6 +5324,8 @@ int WriteEngineWrapper::writeColumnRecBinary(const TxnID& txnid, case WriteEngine::WR_INT: case WriteEngine::WR_UINT: + case WriteEngine::WR_MEDINT: + case WriteEngine::WR_UMEDINT: case WriteEngine::WR_FLOAT: tmp32 = curValue; ((uint32_t*)valArray)[j] = tmp32; @@ -5449,6 +5467,8 @@ int WriteEngineWrapper::writeColumnRecBinary(const TxnID& txnid, case WriteEngine::WR_INT: case WriteEngine::WR_UINT: + case WriteEngine::WR_MEDINT: + case WriteEngine::WR_UMEDINT: case WriteEngine::WR_FLOAT: tmp32 = curValue; ((uint32_t*)valArray)[j] = tmp32; @@ -5704,10 +5724,12 @@ int WriteEngineWrapper::writeColumnRec(const TxnID& txnid, switch (curColStruct.colType) { case WriteEngine::WR_INT: + case WriteEngine::WR_MEDINT: valArray = (int*) calloc(sizeof(int), 1); break; case WriteEngine::WR_UINT: + case WriteEngine::WR_UMEDINT: valArray = (uint32_t*) calloc(sizeof(uint32_t), 1); break; diff --git a/writeengine/xml/we_xmljob.cpp b/writeengine/xml/we_xmljob.cpp index 8d755b824..4dd923172 100644 --- a/writeengine/xml/we_xmljob.cpp +++ b/writeengine/xml/we_xmljob.cpp @@ -719,6 +719,18 @@ void XMLJob::initSatLimits( JobColumn& curColumn ) const curColumn.fMinIntSat = MIN_UBIGINT; curColumn.fMaxIntSat = MAX_UBIGINT; } + else if ( curColumn.typeName == + ColDataTypeStr[CalpontSystemCatalog::MEDINT] ) + { + curColumn.fMinIntSat = MIN_MEDINT; + curColumn.fMaxIntSat = MAX_MEDINT; + } + else if ( curColumn.typeName == + ColDataTypeStr[CalpontSystemCatalog::UMEDINT] ) + { + curColumn.fMinIntSat = MIN_UMEDINT; + curColumn.fMaxIntSat = MAX_UMEDINT; + } else if ( curColumn.typeName == ColDataTypeStr[CalpontSystemCatalog::SMALLINT] ) { From 098e49bd01bfc0ae07da480bb092c716f337bd1d Mon Sep 17 00:00:00 2001 From: David Hall Date: Wed, 23 Jan 2019 10:29:12 -0600 Subject: [PATCH 02/38] MCOL-2001 Make the parameters for redistribute case insensitive --- oamapps/mcsadmin/mcsadmin.cpp | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/oamapps/mcsadmin/mcsadmin.cpp b/oamapps/mcsadmin/mcsadmin.cpp index f2ea97596..8b31353ac 100644 --- a/oamapps/mcsadmin/mcsadmin.cpp +++ b/oamapps/mcsadmin/mcsadmin.cpp @@ -32,6 +32,7 @@ extern int h_errno; #include "boost/filesystem/path.hpp" #include "boost/scoped_ptr.hpp" #include "boost/tokenizer.hpp" +#include "boost/algorithm/string/predicate.hpp" #include "sessionmanager.h" #include "dbrm.h" #include "messagequeue.h" @@ -677,7 +678,7 @@ int processCommand(string* arguments) vector srcDbroots; // all of the currently configured dbroots vector destDbroots; // srcDbroots - removeDbroots set::iterator dbiter; - if (arguments[1] == "start") + if (boost::iequals(arguments[1], "start")) { // Get a list of all the configured dbroots in the xml file. DBRootConfigList dbRootConfigList; @@ -688,7 +689,7 @@ int processCommand(string* arguments) // The user may choose to redistribute in such a way as to // leave certain dbroots empty, presumably for later removal. - if (arguments[2] == "remove") + if (boost::iequals(arguments[2], "remove")) { int dbroot; bool error = false; @@ -792,7 +793,7 @@ int processCommand(string* arguments) SendToWES(oam, bs); } - else if (arguments[1] == "stop") + else if (boost::iequals(arguments[1], "stop")) { ByteStream bs; // message WES ID, sequence #, action id @@ -802,7 +803,7 @@ int processCommand(string* arguments) bs.append((const ByteStream::byte*) &header, sizeof(header)); SendToWES(oam, bs); } - else if (arguments[1] == "status") + else if (boost::iequals(arguments[1], "status")) { ByteStream bs; // message WES ID, sequence #, action id From c5b9ae11e54453491f2b9e37e299d2069dbee043 Mon Sep 17 00:00:00 2001 From: David Hall Date: Tue, 29 Jan 2019 09:55:43 -0600 Subject: [PATCH 03/38] MCOL-1822 add LONG DOUBLE support --- dbcon/execplan/aggregatecolumn.cpp | 8 ++ dbcon/execplan/calpontsystemcatalog.cpp | 4 + dbcon/execplan/calpontsystemcatalog.h | 2 + dbcon/execplan/constantcolumn.cpp | 40 ++++++- dbcon/execplan/constantcolumn.h | 4 + dbcon/execplan/simplecolumn.cpp | 6 + dbcon/execplan/treenode.h | 54 +++++++++ dbcon/execplan/windowfunctioncolumn.cpp | 10 ++ dbcon/joblist/groupconcat.cpp | 7 ++ dbcon/joblist/jlf_common.cpp | 11 ++ dbcon/joblist/joblisttypes.h | 2 + dbcon/joblist/tupleaggregatestep.cpp | 3 + dbcon/joblist/tupleconstantstep.cpp | 6 + dbcon/joblist/tupleunion.cpp | 121 ++++++++++++++++++++ dbcon/mysql/ha_calpont_dml.cpp | 14 +++ dbcon/mysql/ha_calpont_execplan.cpp | 17 +-- dbcon/mysql/ha_calpont_impl.cpp | 35 ++++++ dbcon/mysql/ha_calpont_partition.cpp | 3 + utils/dataconvert/dataconvert.cpp | 63 +++++++++- utils/funcexp/func_between.cpp | 18 +++ utils/funcexp/func_ceil.cpp | 6 + utils/funcexp/func_hex.cpp | 15 +++ utils/funcexp/func_substring_index.cpp | 10 +- utils/funcexp/funcexp.cpp | 12 ++ utils/funcexp/functor_str.h | 26 +++-- utils/joiner/joinpartition.cpp | 1 - utils/messageqcpp/bytestream.cpp | 26 +++++ utils/messageqcpp/bytestream.h | 15 +++ utils/rowgroup/rowgroup.cpp | 12 +- utils/rowgroup/rowgroup.h | 6 + utils/windowfunction/idborderby.cpp | 45 +++++++- utils/windowfunction/idborderby.h | 8 ++ utils/windowfunction/wf_lead_lag.cpp | 6 + utils/windowfunction/wf_min_max.cpp | 6 + utils/windowfunction/wf_percentile.cpp | 7 ++ utils/windowfunction/wf_stats.cpp | 6 + utils/windowfunction/wf_sum_avg.cpp | 31 ++++- utils/windowfunction/wf_udaf.cpp | 74 ++++++++++++ utils/windowfunction/windowfunctiontype.cpp | 35 ++++++ utils/windowfunction/windowfunctiontype.h | 9 +- 40 files changed, 746 insertions(+), 38 deletions(-) diff --git a/dbcon/execplan/aggregatecolumn.cpp b/dbcon/execplan/aggregatecolumn.cpp index c996dad17..bf46fafd5 100644 --- a/dbcon/execplan/aggregatecolumn.cpp +++ b/dbcon/execplan/aggregatecolumn.cpp @@ -498,6 +498,14 @@ void AggregateColumn::evaluate(Row& row, bool& isNull) break; + case CalpontSystemCatalog::LONGDOUBLE: + if (row.equals(LONGDOUBLENULL, fInputIndex)) + isNull = true; + else + fResult.longDoubleVal = row.getLongDoubleField(fInputIndex); + + break; + case CalpontSystemCatalog::DECIMAL: case CalpontSystemCatalog::UDECIMAL: switch (fResultType.colWidth) diff --git a/dbcon/execplan/calpontsystemcatalog.cpp b/dbcon/execplan/calpontsystemcatalog.cpp index 7b75c15a9..0b4fe4fce 100644 --- a/dbcon/execplan/calpontsystemcatalog.cpp +++ b/dbcon/execplan/calpontsystemcatalog.cpp @@ -155,6 +155,10 @@ const string colDataTypeToString(CalpontSystemCatalog::ColDataType cdt) return "double"; break; + case CalpontSystemCatalog::LONGDOUBLE: + return "long double"; + break; + case CalpontSystemCatalog::DATETIME: return "datetime"; break; diff --git a/dbcon/execplan/calpontsystemcatalog.h b/dbcon/execplan/calpontsystemcatalog.h index 7b828a297..e56872b84 100644 --- a/dbcon/execplan/calpontsystemcatalog.h +++ b/dbcon/execplan/calpontsystemcatalog.h @@ -76,6 +76,8 @@ const float MAX_FLOAT __attribute__ ((unused)) = std::numeric_limits::ma const float MIN_FLOAT __attribute__ ((unused)) = -std::numeric_limits::max(); const double MAX_DOUBLE __attribute__ ((unused)) = std::numeric_limits::max(); //1.7976931348623157e+308 const double MIN_DOUBLE __attribute__ ((unused)) = -std::numeric_limits::max(); +const long double MAX_LONGDOUBLE __attribute__ ((unused)) = std::numeric_limits::max(); //1.7976931348623157e+308 +const long double MIN_LONGDOUBLE __attribute__ ((unused)) = -std::numeric_limits::max(); const uint64_t AUTOINCR_SATURATED __attribute__ ((unused)) = std::numeric_limits::max(); diff --git a/dbcon/execplan/constantcolumn.cpp b/dbcon/execplan/constantcolumn.cpp index dd112600e..60d99580d 100644 --- a/dbcon/execplan/constantcolumn.cpp +++ b/dbcon/execplan/constantcolumn.cpp @@ -51,7 +51,7 @@ ConstantColumn::ConstantColumn(const string& sql, TYPE type) : fData(sql) { fResult.strVal = sql; - + if (type == LITERAL && sql.length() < 9) { memcpy(tmp, sql.c_str(), sql.length()); @@ -67,6 +67,7 @@ ConstantColumn::ConstantColumn(const string& sql, TYPE type) : fResult.floatVal = atof(sql.c_str()); fResult.doubleVal = atof(sql.c_str()); + fResult.longDoubleVal = strtold(sql.c_str(), NULL); // decimal for constant should be constructed by the caller and call the decimal constructor fResult.decimalVal.value = fResult.intVal; @@ -105,6 +106,27 @@ ConstantColumn::ConstantColumn(const string& sql, const double val) : fResult.intVal = (int64_t)val; fResult.uintVal = (uint64_t)val; fResult.floatVal = (float)val; + fResult.longDoubleVal = val; + // decimal for constant should be constructed by the caller and call the decimal constructor + fResult.decimalVal.value = fResult.intVal; + fResult.decimalVal.scale = 0; + fResult.decimalVal.precision = 18; + fResultType.colDataType = CalpontSystemCatalog::DOUBLE; + fResultType.colWidth = 8; +} + +ConstantColumn::ConstantColumn(const string& sql, const long double val) : + ReturnedColumn(), + fConstval(sql), + fType(NUM), + fData(sql) +{ + fResult.strVal = sql; + fResult.doubleVal = (double)val; + fResult.intVal = (int64_t)val; + fResult.uintVal = (uint64_t)val; + fResult.floatVal = (float)val; + fResult.longDoubleVal = val; // decimal for constant should be constructed by the caller and call the decimal constructor fResult.decimalVal.value = fResult.intVal; fResult.decimalVal.scale = 0; @@ -124,6 +146,7 @@ ConstantColumn::ConstantColumn(const string& sql, const int64_t val, TYPE type) fResult.uintVal = (uint64_t)fResult.intVal; fResult.floatVal = (float)fResult.intVal; fResult.doubleVal = (double)fResult.intVal; + fResult.longDoubleVal = (long double)fResult.intVal; fResult.decimalVal.value = fResult.intVal; fResult.decimalVal.scale = 0; fResultType.colDataType = CalpontSystemCatalog::BIGINT; @@ -141,6 +164,7 @@ ConstantColumn::ConstantColumn(const string& sql, const uint64_t val, TYPE type) fResult.intVal = (int64_t)fResult.uintVal; fResult.floatVal = (float)fResult.uintVal; fResult.doubleVal = (double)fResult.uintVal; + fResult.longDoubleVal = (long double)fResult.uintVal; fResult.decimalVal.value = fResult.uintVal; fResult.decimalVal.scale = 0; fResultType.colDataType = CalpontSystemCatalog::UBIGINT; @@ -158,6 +182,7 @@ ConstantColumn::ConstantColumn(const string& sql, const IDB_Decimal& val) : fResult.uintVal = strtoull(sql.c_str(), NULL, 0); fResult.floatVal = atof(sql.c_str()); fResult.doubleVal = atof(sql.c_str()); + fResult.longDoubleVal = strtold(sql.c_str(), NULL); fResult.decimalVal = val; fResultType.colDataType = CalpontSystemCatalog::DECIMAL; fResultType.colWidth = 8; @@ -201,6 +226,7 @@ ConstantColumn::ConstantColumn(const int64_t val, TYPE type) : fResult.uintVal = (uint64_t)fResult.intVal; fResult.floatVal = (float)fResult.intVal; fResult.doubleVal = (double)fResult.intVal; + fResult.longDoubleVal = (long double)fResult.intVal; fResult.decimalVal.value = fResult.intVal; fResult.decimalVal.scale = 0; fResultType.colDataType = CalpontSystemCatalog::BIGINT; @@ -220,6 +246,7 @@ ConstantColumn::ConstantColumn(const uint64_t val, TYPE type) : fResult.uintVal = val; fResult.floatVal = (float)fResult.uintVal; fResult.doubleVal = (double)fResult.uintVal; + fResult.longDoubleVal = (long double)fResult.uintVal; fResult.decimalVal.value = fResult.uintVal; fResult.decimalVal.scale = 0; fResultType.colDataType = CalpontSystemCatalog::UBIGINT; @@ -281,13 +308,15 @@ void ConstantColumn::serialize(messageqcpp::ByteStream& b) const b << static_cast(fReturnAll); b << (uint64_t)fResult.intVal; b << fResult.uintVal; - b << (*(uint64_t*)(&fResult.doubleVal)); - b << (*(uint32_t*)(&fResult.floatVal)); + b << fResult.doubleVal; + b << fResult.longDoubleVal; + b << fResult.floatVal; b << (uint8_t)fResult.boolVal; b << fResult.strVal; b << (uint64_t)fResult.decimalVal.value; b << (uint8_t)fResult.decimalVal.scale; b << (uint8_t)fResult.decimalVal.precision; + b << fResult.longDoubleVal; } void ConstantColumn::unserialize(messageqcpp::ByteStream& b) @@ -303,8 +332,9 @@ void ConstantColumn::unserialize(messageqcpp::ByteStream& b) b >> reinterpret_cast< ByteStream::doublebyte&>(fReturnAll); b >> (uint64_t&)fResult.intVal; b >> fResult.uintVal; - b >> (uint64_t&)fResult.doubleVal; - b >> (uint32_t&)fResult.floatVal; + b >> fResult.doubleVal; + b >> fResult.longDoubleVal; + b >> fResult.floatVal; b >> (uint8_t&)fResult.boolVal; b >> fResult.strVal; b >> (uint64_t&)fResult.decimalVal.value; diff --git a/dbcon/execplan/constantcolumn.h b/dbcon/execplan/constantcolumn.h index be0731044..e19abb364 100644 --- a/dbcon/execplan/constantcolumn.h +++ b/dbcon/execplan/constantcolumn.h @@ -200,6 +200,10 @@ public: * ctor */ ConstantColumn(const std::string& sql, const double val); + /** + * ctor + */ + ConstantColumn(const std::string& sql, const long double val); /** * ctor */ diff --git a/dbcon/execplan/simplecolumn.cpp b/dbcon/execplan/simplecolumn.cpp index 1d7780e33..c95f7ac1e 100644 --- a/dbcon/execplan/simplecolumn.cpp +++ b/dbcon/execplan/simplecolumn.cpp @@ -592,6 +592,12 @@ void SimpleColumn::evaluate(Row& row, bool& isNull) break; } + case CalpontSystemCatalog::LONGDOUBLE: + { + fResult.longDoubleVal = row.getLongDoubleField(fInputIndex); + break; + } + case CalpontSystemCatalog::DECIMAL: case CalpontSystemCatalog::UDECIMAL: { diff --git a/dbcon/execplan/treenode.h b/dbcon/execplan/treenode.h index d43239563..126d7ee6c 100644 --- a/dbcon/execplan/treenode.h +++ b/dbcon/execplan/treenode.h @@ -249,6 +249,7 @@ struct Result // when converting origIntVal uint64_t dummy; double doubleVal; + long double longDoubleVal; float floatVal; bool boolVal; std::string strVal; @@ -376,6 +377,10 @@ public: { return fResult.doubleVal; } + virtual long double getLongDoubleVal(rowgroup::Row& row, bool& isNull) + { + return fResult.longDoubleVal; + } virtual IDB_Decimal getDecimalVal(rowgroup::Row& row, bool& isNull) { return fResult.decimalVal; @@ -517,6 +522,9 @@ inline bool TreeNode::getBoolVal() case CalpontSystemCatalog::UDOUBLE: return (fResult.doubleVal != 0); + case CalpontSystemCatalog::LONGDOUBLE: + return (fResult.longDoubleVal != 0); + case CalpontSystemCatalog::DECIMAL: case CalpontSystemCatalog::UDECIMAL: return (fResult.decimalVal.value != 0); @@ -646,6 +654,40 @@ inline const std::string& TreeNode::getStrVal() fResult.strVal += tmp; } +// snprintf(tmp, 312, "%e", fResult.doubleVal); +// fResult.strVal = tmp; + } + + break; + } + + case CalpontSystemCatalog::LONGDOUBLE: + { + if ((fabsl(fResult.longDoubleVal) > (1.0 / IDB_pow[13])) && + (fabsl(fResult.longDoubleVal) < (float) IDB_pow[15])) + { + snprintf(tmp, 312, "%Lf", fResult.longDoubleVal); + fResult.strVal = removeTrailing0(tmp, 312); + } + else + { + // MCOL-299 Print scientific with 9 mantissa and no + sign for exponent + int exponent = (int)floorl(log10( fabsl(fResult.longDoubleVal))); // This will round down the exponent + long double base = fResult.longDoubleVal * pow(10, -1.0 * exponent); + + if (isnan(exponent) || isnan(base)) + { + snprintf(tmp, 312, "%Lf", fResult.longDoubleVal); + fResult.strVal = removeTrailing0(tmp, 312); + } + else + { + snprintf(tmp, 312, "%.14Lf", base); + fResult.strVal = removeTrailing0(tmp, 312); + snprintf(tmp, 312, "e%02d", exponent); + fResult.strVal += tmp; + } + // snprintf(tmp, 312, "%e", fResult.doubleVal); // fResult.strVal = tmp; } @@ -736,6 +778,9 @@ inline int64_t TreeNode::getIntVal() case CalpontSystemCatalog::UDOUBLE: return (int64_t)fResult.doubleVal; + case CalpontSystemCatalog::LONGDOUBLE: + return (int64_t)fResult.longDoubleVal; + case CalpontSystemCatalog::DECIMAL: case CalpontSystemCatalog::UDECIMAL: { @@ -779,6 +824,9 @@ inline uint64_t TreeNode::getUintVal() case CalpontSystemCatalog::UDOUBLE: return (uint64_t)fResult.doubleVal; + case CalpontSystemCatalog::LONGDOUBLE: + return (uint64_t)fResult.longDoubleVal; + case CalpontSystemCatalog::DECIMAL: case CalpontSystemCatalog::UDECIMAL: { @@ -843,6 +891,9 @@ inline float TreeNode::getFloatVal() case CalpontSystemCatalog::UDOUBLE: return (float)fResult.doubleVal; + case CalpontSystemCatalog::LONGDOUBLE: + return (float)fResult.doubleVal; + case CalpontSystemCatalog::DECIMAL: { return (fResult.decimalVal.value / pow((double)10, fResult.decimalVal.scale)); @@ -978,6 +1029,9 @@ inline IDB_Decimal TreeNode::getDecimalVal() case CalpontSystemCatalog::UDOUBLE: throw logging::InvalidConversionExcept("TreeNode::getDecimalVal: non-support conversion from double unsigned"); + case CalpontSystemCatalog::LONGDOUBLE: + throw logging::InvalidConversionExcept("TreeNode::getDecimalVal: non-support conversion from long double"); + case CalpontSystemCatalog::DECIMAL: case CalpontSystemCatalog::UDECIMAL: return fResult.decimalVal; diff --git a/dbcon/execplan/windowfunctioncolumn.cpp b/dbcon/execplan/windowfunctioncolumn.cpp index b1377880f..cad99215a 100644 --- a/dbcon/execplan/windowfunctioncolumn.cpp +++ b/dbcon/execplan/windowfunctioncolumn.cpp @@ -583,6 +583,16 @@ void WindowFunctionColumn::evaluate(Row& row, bool& isNull) break; } + case CalpontSystemCatalog::LONGDOUBLE: + { + if (row.equals(LONGDOUBLENULL, fInputIndex)) + isNull = true; + else + fResult.longDoubleVal = row.getLongDoubleField(fInputIndex); + + break; + } + case CalpontSystemCatalog::DECIMAL: case CalpontSystemCatalog::UDECIMAL: { diff --git a/dbcon/joblist/groupconcat.cpp b/dbcon/joblist/groupconcat.cpp index afc91a2ec..f74782d49 100644 --- a/dbcon/joblist/groupconcat.cpp +++ b/dbcon/joblist/groupconcat.cpp @@ -488,6 +488,12 @@ void GroupConcator::outputRow(std::ostringstream& oss, const rowgroup::Row& row) break; } + case CalpontSystemCatalog::LONGDOUBLE: + { + oss << setprecision(15) << row.getLongDoubleField(*i); + break; + } + case CalpontSystemCatalog::FLOAT: case CalpontSystemCatalog::UFLOAT: { @@ -632,6 +638,7 @@ int64_t GroupConcator::lengthEstimate(const rowgroup::Row& row) case CalpontSystemCatalog::UDOUBLE: case CalpontSystemCatalog::FLOAT: case CalpontSystemCatalog::UFLOAT: + case CalpontSystemCatalog::LONGDOUBLE: { fieldLen = 1; // minimum length break; diff --git a/dbcon/joblist/jlf_common.cpp b/dbcon/joblist/jlf_common.cpp index 4b1980d49..9579aad60 100644 --- a/dbcon/joblist/jlf_common.cpp +++ b/dbcon/joblist/jlf_common.cpp @@ -838,6 +838,17 @@ bool compatibleColumnTypes(const CalpontSystemCatalog::ColDataType& dt1, uint32_ break; + case CalpontSystemCatalog::LONGDOUBLE: + if (forJoin && (dt2 != CalpontSystemCatalog::LONGDOUBLE)) + return false; + else if (dt2 != CalpontSystemCatalog::FLOAT && + dt2 != CalpontSystemCatalog::DOUBLE && + dt2 != CalpontSystemCatalog::UFLOAT && + dt2 != CalpontSystemCatalog::UDOUBLE && + dt2 != CalpontSystemCatalog::LONGDOUBLE) return false; + + break; + default: return false; break; diff --git a/dbcon/joblist/joblisttypes.h b/dbcon/joblist/joblisttypes.h index 37d52f9f4..2174f3002 100644 --- a/dbcon/joblist/joblisttypes.h +++ b/dbcon/joblist/joblisttypes.h @@ -25,6 +25,7 @@ #include #include +#include namespace joblist { @@ -51,6 +52,7 @@ const uint32_t FLOATNULL = 0xFFAAAAAA; const uint32_t FLOATEMPTYROW = 0xFFAAAAAB; const uint64_t DOUBLENULL = 0xFFFAAAAAAAAAAAAAULL; const uint64_t DOUBLEEMPTYROW = 0xFFFAAAAAAAAAAAABULL; +const long double LONGDOUBLENULL = nanl(""); const uint32_t DATENULL = 0xFFFFFFFE; const uint32_t DATEEMPTYROW = 0xFFFFFFFF; diff --git a/dbcon/joblist/tupleaggregatestep.cpp b/dbcon/joblist/tupleaggregatestep.cpp index 4c1893115..61768dfd3 100644 --- a/dbcon/joblist/tupleaggregatestep.cpp +++ b/dbcon/joblist/tupleaggregatestep.cpp @@ -266,6 +266,9 @@ inline string colTypeIdString(CalpontSystemCatalog::ColDataType type) case CalpontSystemCatalog::DOUBLE: return string("DOUBLE"); + case CalpontSystemCatalog::LONGDOUBLE: + return string("LONGDOUBLE"); + case CalpontSystemCatalog::DATETIME: return string("DATETIME"); diff --git a/dbcon/joblist/tupleconstantstep.cpp b/dbcon/joblist/tupleconstantstep.cpp index 309bb0058..4e3eb670d 100644 --- a/dbcon/joblist/tupleconstantstep.cpp +++ b/dbcon/joblist/tupleconstantstep.cpp @@ -250,6 +250,12 @@ void TupleConstantStep::constructContanstRow(const JobInfo& jobInfo) break; } + case CalpontSystemCatalog::LONGDOUBLE: + { + fRowConst.setLongDoubleField(c.longDoubleVal, *i); + break; + } + case CalpontSystemCatalog::CHAR: case CalpontSystemCatalog::VARCHAR: case CalpontSystemCatalog::TEXT: diff --git a/dbcon/joblist/tupleunion.cpp b/dbcon/joblist/tupleunion.cpp index d0892e45f..d23a9d4d9 100644 --- a/dbcon/joblist/tupleunion.cpp +++ b/dbcon/joblist/tupleunion.cpp @@ -510,6 +510,22 @@ void TupleUnion::normalize(const Row& in, Row* out) break; } + case CalpontSystemCatalog::LONGDOUBLE: + { + int scale = in.getScale(i); + + if (scale != 0) + { + long double d = in.getIntField(i); + d /= (uint64_t) pow(10.0, scale); + out->setLongDoubleField(d, i); + } + else + out->setLongDoubleField(in.getIntField(i), i); + + break; + } + case CalpontSystemCatalog::DECIMAL: case CalpontSystemCatalog::UDECIMAL: { @@ -620,6 +636,22 @@ dec1: break; } + case CalpontSystemCatalog::LONGDOUBLE: + { + int scale = in.getScale(i); + + if (scale != 0) + { + long double d = in.getUintField(i); + d /= (uint64_t) pow(10.0, scale); + out->setLongDoubleField(d, i); + } + else + out->setLongDoubleField(in.getUintField(i), i); + + break; + } + case CalpontSystemCatalog::DECIMAL: case CalpontSystemCatalog::UDECIMAL: { @@ -804,6 +836,10 @@ dec2: out->setDoubleField(val, i); break; + case CalpontSystemCatalog::LONGDOUBLE: + out->setLongDoubleField(val, i); + break; + case CalpontSystemCatalog::CHAR: case CalpontSystemCatalog::TEXT: case CalpontSystemCatalog::VARCHAR: @@ -842,6 +878,83 @@ dec3: /* have to pick a scale to use for the double. using 5... */ break; } + case CalpontSystemCatalog::LONGDOUBLE: + { + long double val = in.getLongDoubleField(i); + + switch (out->getColTypes()[i]) + { + case CalpontSystemCatalog::TINYINT: + case CalpontSystemCatalog::SMALLINT: + case CalpontSystemCatalog::MEDINT: + case CalpontSystemCatalog::INT: + case CalpontSystemCatalog::BIGINT: + if (out->getScale(i)) + goto dec4; + + out->setIntField((int64_t) val, i); + break; + + case CalpontSystemCatalog::UTINYINT: + case CalpontSystemCatalog::USMALLINT: + case CalpontSystemCatalog::UMEDINT: + case CalpontSystemCatalog::UINT: + case CalpontSystemCatalog::UBIGINT: + out->setUintField((uint64_t) val, i); + break; + + case CalpontSystemCatalog::FLOAT: + case CalpontSystemCatalog::UFLOAT: + out->setFloatField(val, i); + break; + + case CalpontSystemCatalog::DOUBLE: + case CalpontSystemCatalog::UDOUBLE: + out->setDoubleField(val, i); + break; + + case CalpontSystemCatalog::LONGDOUBLE: + out->setLongDoubleField(val, i); + break; + + case CalpontSystemCatalog::CHAR: + case CalpontSystemCatalog::TEXT: + case CalpontSystemCatalog::VARCHAR: + { + ostringstream os; + os.precision(15); // to match mysql's output + os << val; + out->setStringField(os.str(), i); + break; + } + + case CalpontSystemCatalog::DECIMAL: + case CalpontSystemCatalog::UDECIMAL: + { +dec4: /* have to pick a scale to use for the double. using 5... */ + uint32_t scale = 5; + uint64_t ival = (uint64_t) (double) (val * pow((double) 10, (double) scale)); + int diff = out->getScale(i) - scale; + + if (diff < 0) + ival /= (uint64_t) pow((double) 10, (double) - diff); + else + ival *= (uint64_t) pow((double) 10, (double) diff); + + out->setIntField((int64_t) val, i); + break; + } + + default: + ostringstream os; + os << "TupleUnion::normalize(): tried an illegal conversion: floating point to " + << out->getColTypes()[i]; + throw logic_error(os.str()); + } + + break; + } + case CalpontSystemCatalog::DECIMAL: case CalpontSystemCatalog::UDECIMAL: { @@ -882,12 +995,20 @@ dec3: /* have to pick a scale to use for the double. using 5... */ } case CalpontSystemCatalog::DOUBLE: + case CalpontSystemCatalog::UDOUBLE: { double dval = ((double) val) / IDB_pow[scale]; out->setDoubleField(dval, i); break; } + case CalpontSystemCatalog::LONGDOUBLE: + { + long double dval = ((long double) val) / IDB_pow[scale]; + out->setLongDoubleField(dval, i); + break; + } + case CalpontSystemCatalog::CHAR: case CalpontSystemCatalog::TEXT: case CalpontSystemCatalog::VARCHAR: diff --git a/dbcon/mysql/ha_calpont_dml.cpp b/dbcon/mysql/ha_calpont_dml.cpp index 43bdc8cd6..3860f6d5e 100644 --- a/dbcon/mysql/ha_calpont_dml.cpp +++ b/dbcon/mysql/ha_calpont_dml.cpp @@ -1173,6 +1173,20 @@ int ha_calpont_impl_write_batch_row_(uchar* buf, TABLE* table, cal_impl_if::cal_ break; } + case CalpontSystemCatalog::LONGDOUBLE: + { + if (nullVal && (ci.columnTypes[colpos].constraintType != CalpontSystemCatalog::NOTNULL_CONSTRAINT)) + fprintf(ci.filePtr, "%c", ci.delimiter); + else + { + fprintf(ci.filePtr, "%.15Lg%c", *((long double*)buf), ci.delimiter); + //printf("%.15g|", *((double*)buf)); + } + + buf += 8; + break; + } + case CalpontSystemCatalog::DECIMAL: case CalpontSystemCatalog::UDECIMAL: { diff --git a/dbcon/mysql/ha_calpont_execplan.cpp b/dbcon/mysql/ha_calpont_execplan.cpp index 914743a25..83be3c650 100644 --- a/dbcon/mysql/ha_calpont_execplan.cpp +++ b/dbcon/mysql/ha_calpont_execplan.cpp @@ -4499,7 +4499,11 @@ ReturnedColumn* buildAggregateColumn(Item* item, gp_walk_info& gwi) isp->sum_func() == Item_sum::AVG_DISTINCT_FUNC) { CalpontSystemCatalog::ColType ct = parm->resultType(); + ct.colDataType = CalpontSystemCatalog::LONGDOUBLE; + ct.scale += 4; +// ct.colWidth = 8; +#if 0 switch (ct.colDataType) { case CalpontSystemCatalog::TINYINT: @@ -4514,13 +4518,9 @@ ReturnedColumn* buildAggregateColumn(Item* item, gp_walk_info& gwi) case CalpontSystemCatalog::UMEDINT: case CalpontSystemCatalog::UINT: case CalpontSystemCatalog::UBIGINT: - ct.colDataType = CalpontSystemCatalog::DECIMAL; - ct.colWidth = 8; ct.scale += 4; break; -#if PROMOTE_FLOAT_TO_DOUBLE_ON_SUM - case CalpontSystemCatalog::FLOAT: case CalpontSystemCatalog::UFLOAT: case CalpontSystemCatalog::DOUBLE: @@ -4528,12 +4528,11 @@ ReturnedColumn* buildAggregateColumn(Item* item, gp_walk_info& gwi) ct.colDataType = CalpontSystemCatalog::DOUBLE; ct.colWidth = 8; break; -#endif default: break; } - +#endif ac->resultType(ct); } else if (isp->sum_func() == Item_sum::COUNT_FUNC || @@ -4549,7 +4548,9 @@ ReturnedColumn* buildAggregateColumn(Item* item, gp_walk_info& gwi) isp->sum_func() == Item_sum::SUM_DISTINCT_FUNC) { CalpontSystemCatalog::ColType ct = parm->resultType(); - + ct.colDataType = CalpontSystemCatalog::LONGDOUBLE; + ct.scale += 4; +#if 0 switch (ct.colDataType) { case CalpontSystemCatalog::TINYINT: @@ -4589,7 +4590,7 @@ ReturnedColumn* buildAggregateColumn(Item* item, gp_walk_info& gwi) default: break; } - +#endif ac->resultType(ct); } else if (isp->sum_func() == Item_sum::STD_FUNC || diff --git a/dbcon/mysql/ha_calpont_impl.cpp b/dbcon/mysql/ha_calpont_impl.cpp index 3be44d2b8..0a633fe1c 100644 --- a/dbcon/mysql/ha_calpont_impl.cpp +++ b/dbcon/mysql/ha_calpont_impl.cpp @@ -703,6 +703,41 @@ int fetchNextRow(uchar* buf, cal_table_info& ti, cal_connection_info* ci, bool h //break; } + case CalpontSystemCatalog::LONGDOUBLE: + { + double dl = row.getLongDoubleField(s); + + if (dl == std::numeric_limits::infinity()) + continue; + + Field_double* f2 = (Field_double*)*f; + // bug 3483, reserve enough space for the longest double value + // -1.7976931348623157E+308 to -2.2250738585072014E-308, 0, and + // 2.2250738585072014E-308 to 1.7976931348623157E+308. + (*f)->field_length = 310; + + //double double_val = *(double*)(&value); + //f2->store(double_val); + if ((f2->decimals() == DECIMAL_NOT_SPECIFIED && row.getScale(s) > 0) + || f2->decimals() < row.getScale(s)) + { + f2->dec = row.getScale(s); + } + + f2->store(dl); + + if ((*f)->null_ptr) + *(*f)->null_ptr &= ~(*f)->null_bit; + + break; + + + //int64_t* icvp = (int64_t*)&dl; + //intColVal = *icvp; + //storeNumericField(f, intColVal, colType); + //break; + } + case CalpontSystemCatalog::DECIMAL: case CalpontSystemCatalog::UDECIMAL: { diff --git a/dbcon/mysql/ha_calpont_partition.cpp b/dbcon/mysql/ha_calpont_partition.cpp index 038c668c9..a4eca3212 100644 --- a/dbcon/mysql/ha_calpont_partition.cpp +++ b/dbcon/mysql/ha_calpont_partition.cpp @@ -190,6 +190,9 @@ string name(CalpontSystemCatalog::ColType& ct) case CalpontSystemCatalog::UDOUBLE: return "UDOUBLE"; + case CalpontSystemCatalog::LONGDOUBLE: + return "LONGDOUBLE"; + default: return "Unknown Type"; } diff --git a/utils/dataconvert/dataconvert.cpp b/utils/dataconvert/dataconvert.cpp index 1d196a436..7614385b5 100644 --- a/utils/dataconvert/dataconvert.cpp +++ b/utils/dataconvert/dataconvert.cpp @@ -871,7 +871,7 @@ bool mysql_str_to_datetime( const string& input, DateTime& output, bool& isDate bool mysql_str_to_time( const string& input, Time& output, long decimals ) { - int32_t datesepct = 0; +// int32_t datesepct = 0; uint32_t dtend = 0; bool isNeg = false; @@ -2995,6 +2995,7 @@ CalpontSystemCatalog::ColType DataConvert::convertUnionColType(vector 20) ? types[i].colWidth : 20; break; diff --git a/utils/funcexp/func_between.cpp b/utils/funcexp/func_between.cpp index f23d67826..05ea8be3a 100644 --- a/utils/funcexp/func_between.cpp +++ b/utils/funcexp/func_between.cpp @@ -196,6 +196,24 @@ inline bool getBool(rowgroup::Row& row, numericLE(val, pm[2]->data()->getDoubleVal(row, isNull)); } + case execplan::CalpontSystemCatalog::LONGDOUBLE: + { + long double val = pm[0]->data()->getLongDoubleVal(row, isNull); + + if (notBetween) + { + if (!numericGE(val, pm[1]->data()->getLongDoubleVal(row, isNull)) && !isNull) + return true; + + isNull = false; + return (!numericLE(val, pm[2]->data()->getLongDoubleVal(row, isNull)) && !isNull); + } + + return !isNull && + numericGE(val, pm[1]->data()->getLongDoubleVal(row, isNull)) && + numericLE(val, pm[2]->data()->getLongDoubleVal(row, isNull)); + } + case execplan::CalpontSystemCatalog::DECIMAL: case execplan::CalpontSystemCatalog::UDECIMAL: { diff --git a/utils/funcexp/func_ceil.cpp b/utils/funcexp/func_ceil.cpp index 7c2f7f571..f270ed641 100644 --- a/utils/funcexp/func_ceil.cpp +++ b/utils/funcexp/func_ceil.cpp @@ -123,6 +123,12 @@ int64_t Func_ceil::getIntVal(Row& row, } break; + case CalpontSystemCatalog::LONGDOUBLE: + { + ret = (int64_t) ceill(parm[0]->data()->getLongDoubleVal(row, isNull)); + } + break; + case CalpontSystemCatalog::VARCHAR: case CalpontSystemCatalog::CHAR: case CalpontSystemCatalog::TEXT: diff --git a/utils/funcexp/func_hex.cpp b/utils/funcexp/func_hex.cpp index 83106a9be..9bf35b125 100644 --- a/utils/funcexp/func_hex.cpp +++ b/utils/funcexp/func_hex.cpp @@ -103,6 +103,21 @@ string Func_hex::getStrVal(rowgroup::Row& row, break; } + case CalpontSystemCatalog::LONGDOUBLE: + { + char buf[256]; + long double val = parm[0]->data()->getLongDoubleVal(row, isNull); + +#ifdef _MSC_VER + sprintf(buf, "%llA", val); + +#else + sprintf(buf, "%LA", val); +#endif + retval = buf; + break; + } + case CalpontSystemCatalog::VARBINARY: case CalpontSystemCatalog::BLOB: { diff --git a/utils/funcexp/func_substring_index.cpp b/utils/funcexp/func_substring_index.cpp index cf1395759..7266960d6 100644 --- a/utils/funcexp/func_substring_index.cpp +++ b/utils/funcexp/func_substring_index.cpp @@ -61,7 +61,7 @@ std::string Func_substring_index::getStrVal(rowgroup::Row& row, if (isNull) return ""; - int64_t count = fp[2]->data()->getIntVal(row, isNull); + size_t count = fp[2]->data()->getIntVal(row, isNull); if (isNull) return ""; @@ -71,7 +71,7 @@ std::string Func_substring_index::getStrVal(rowgroup::Row& row, size_t end = strlen(str.c_str()); - if ( count > (int64_t) end ) + if ( count > end ) return str; if (( count < 0 ) && ((count * -1) > end)) @@ -83,7 +83,7 @@ std::string Func_substring_index::getStrVal(rowgroup::Row& row, { int pointer = 0; - for ( int i = 0 ; i < count ; i ++ ) + for ( size_t i = 0 ; i < count ; i ++ ) { string::size_type pos = str.find(delim, pointer); @@ -102,13 +102,13 @@ std::string Func_substring_index::getStrVal(rowgroup::Row& row, int pointer = end; int start = 0; - for ( int i = 0 ; i < count ; i ++ ) + for ( size_t i = 0 ; i < count ; i ++ ) { string::size_type pos = str.rfind(delim, pointer); if (pos != string::npos) { - if ( count > (int64_t) end ) + if ( count > end ) return ""; pointer = pos - 1; diff --git a/utils/funcexp/funcexp.cpp b/utils/funcexp/funcexp.cpp index 53f7da595..9751eabd5 100644 --- a/utils/funcexp/funcexp.cpp +++ b/utils/funcexp/funcexp.cpp @@ -439,6 +439,18 @@ void FuncExp::evaluate(rowgroup::Row& row, std::vector& expressi break; } + case CalpontSystemCatalog::LONGDOUBLE: + { + long double val = expression[i]->getLongDoubleVal(row, isNull); + + if (isNull) + row.setLongDoubleField(LONGDOUBLENULL, expression[i]->outputIndex()); + else + row.setLongDoubleField(val, expression[i]->outputIndex()); + + break; + } + case CalpontSystemCatalog::DECIMAL: case CalpontSystemCatalog::UDECIMAL: { diff --git a/utils/funcexp/functor_str.h b/utils/funcexp/functor_str.h index 8b6c10404..820228848 100644 --- a/utils/funcexp/functor_str.h +++ b/utils/funcexp/functor_str.h @@ -57,6 +57,14 @@ public: return strtod(getStrVal(row, fp, isNull, op_ct).c_str(), NULL); } + double getLongDoubleVal(rowgroup::Row& row, + FunctionParm& fp, + bool& isNull, + execplan::CalpontSystemCatalog::ColType& op_ct) + { + return strtold(getStrVal(row, fp, isNull, op_ct).c_str(), NULL); + } + #if 0 std::string getStrVal(rowgroup::Row& row, FunctionParm& fp, @@ -105,12 +113,16 @@ protected: // Bug3788, use the shorter of fixed or scientific notation for floating point values. // [ the default format in treenode.h is fixed-point notation ] char buf[20]; - double floatVal; - int exponent; - double base; + long double floatVal; + int64_t exponent; + long double base; switch (fp->data()->resultType().colDataType) { + case execplan::CalpontSystemCatalog::LONGDOUBLE: + floatVal = fp->data()->getLongDoubleVal(row, isNull); + break; + case execplan::CalpontSystemCatalog::DOUBLE: floatVal = fp->data()->getDoubleVal(row, isNull); break; @@ -125,19 +137,19 @@ protected: break; } - exponent = (int)floor(log10( fabs(floatVal))); + exponent = (int)floor(log10( fabsl(floatVal))); base = floatVal * pow(10, -1.0 * exponent); if (isnan(exponent) || isnan(base)) { - snprintf(buf, 20, "%f", floatVal); + snprintf(buf, 20, "%Lf", floatVal); fFloatStr = execplan::removeTrailing0(buf, 20); } else { - snprintf(buf, 20, "%.5f", base); + snprintf(buf, 20, "%.5Lf", base); fFloatStr = execplan::removeTrailing0(buf, 20); - snprintf(buf, 20, "e%02d", exponent); + snprintf(buf, 20, "e%02ld", exponent); fFloatStr += buf; } diff --git a/utils/joiner/joinpartition.cpp b/utils/joiner/joinpartition.cpp index 962143aac..7d0af1d26 100644 --- a/utils/joiner/joinpartition.cpp +++ b/utils/joiner/joinpartition.cpp @@ -118,7 +118,6 @@ JoinPartition::JoinPartition(const JoinPartition& jp, bool splitMode) : totalBytesWritten(0), maxLargeSize(0), maxSmallSize(0), nextSmallOffset(0), nextLargeOffset(0) { - config::Config* config = config::Config::makeConfig(); boost::posix_time::ptime t; ostringstream os; diff --git a/utils/messageqcpp/bytestream.cpp b/utils/messageqcpp/bytestream.cpp index ec471be8f..edd184ddb 100644 --- a/utils/messageqcpp/bytestream.cpp +++ b/utils/messageqcpp/bytestream.cpp @@ -636,6 +636,18 @@ ByteStream& ByteStream::operator<<(const double d) return *this; } +ByteStream& ByteStream::operator<<(const long double d) +{ + int sz = sizeof(long double); + + if (fBuf == 0 || (fCurInPtr - fBuf + sz > fMaxLen + ISSOverhead)) + growBuf(fMaxLen + BlockSize); + + *((long double*) fCurInPtr) = d; + fCurInPtr += sz; + + return *this; +} ByteStream& ByteStream::operator>>(float& f) { peek(f); @@ -648,6 +660,12 @@ ByteStream& ByteStream::operator>>(double& d) fCurOutPtr += sizeof(double); return *this; } +ByteStream& ByteStream::operator>>(long double& d) +{ + peek(d); + fCurOutPtr += sizeof(long double); + return *this; +} void ByteStream::peek(float& f) const { if (length() < sizeof(float)) @@ -663,6 +681,14 @@ void ByteStream::peek(double& d) const d = *((double*) fCurOutPtr); } +void ByteStream::peek(long double& d) const +{ + if (length() < sizeof(long double)) + throw underflow_error("ByteStream>int64_t: not enough data in stream to fill datatype"); + + d = *((long double*) fCurOutPtr); +} + }//namespace messageqcpp diff --git a/utils/messageqcpp/bytestream.h b/utils/messageqcpp/bytestream.h index f8453843e..4b47c8593 100644 --- a/utils/messageqcpp/bytestream.h +++ b/utils/messageqcpp/bytestream.h @@ -153,6 +153,11 @@ public: * whatever the native byte order is. */ EXPORT ByteStream& operator<<(const double d); + /** + * push a long double onto the end of the stream. The byte + * order is whatever the native byte order is. + */ + EXPORT ByteStream& operator<<(const long double d); /** * push a std::string onto the end of the stream. */ @@ -212,6 +217,11 @@ public: * order is whatever the native byte order is. */ EXPORT ByteStream& operator>>(double& d); + /** + * extract a long double from the front of the stream. The byte + * order is whatever the native byte order is. + */ + EXPORT ByteStream& operator>>(long double& d); /** * extract a std::string from the front of the stream. */ @@ -277,6 +287,11 @@ public: * order is whatever the native byte order is. */ EXPORT void peek(double& f) const; + /** + * Peek at a long double from the front of the stream. The byte + * order is whatever the native byte order is. + */ + EXPORT void peek(long double& f) const; /** * Peek at a std::string from the front of the stream. */ diff --git a/utils/rowgroup/rowgroup.cpp b/utils/rowgroup/rowgroup.cpp index 3411f6fa6..5d5308ba7 100644 --- a/utils/rowgroup/rowgroup.cpp +++ b/utils/rowgroup/rowgroup.cpp @@ -702,6 +702,9 @@ void Row::initToNull() *((uint64_t*) &data[offsets[i]]) = joblist::DOUBLENULL; break; + case CalpontSystemCatalog::LONGDOUBLE: + *((long double*) &data[offsets[i]]) = joblist::LONGDOUBLENULL; + case CalpontSystemCatalog::DATETIME: *((uint64_t*) &data[offsets[i]]) = joblist::DATETIMENULL; break; @@ -804,13 +807,6 @@ void Row::initToNull() *((uint64_t*) &data[offsets[i]]) = joblist::UBIGINTNULL; break; - case CalpontSystemCatalog::LONGDOUBLE: - { - // no NULL value for long double yet, this is a nan. - memset(&data[offsets[i]], 0xFF, getColumnWidth(i)); - break; - } - default: ostringstream os; os << "Row::initToNull(): got bad column type (" << types[i] << @@ -956,7 +952,7 @@ bool Row::isNullValue(uint32_t colIndex) const return (*((uint64_t*) &data[offsets[colIndex]]) == joblist::UBIGINTNULL); case CalpontSystemCatalog::LONGDOUBLE: - // return false; // no NULL value for long double yet + return (*((long double*) &data[offsets[colIndex]]) == joblist::LONGDOUBLENULL); break; default: diff --git a/utils/rowgroup/rowgroup.h b/utils/rowgroup/rowgroup.h index ca05360c2..1c3618735 100644 --- a/utils/rowgroup/rowgroup.h +++ b/utils/rowgroup/rowgroup.h @@ -330,6 +330,7 @@ public: template inline int64_t getIntField(uint32_t colIndex) const; inline int64_t getIntField(uint32_t colIndex) const; template inline bool equals(uint64_t val, uint32_t colIndex) const; + inline bool equals(long double val, uint32_t colIndex) const; inline bool equals(const std::string& val, uint32_t colIndex) const; inline double getDoubleField(uint32_t colIndex) const; @@ -616,6 +617,11 @@ inline bool Row::equals(uint64_t val, uint32_t colIndex) const } } +inline bool Row::equals(long double val, uint32_t colIndex) const +{ + return *((long double*) &data[offsets[colIndex]]) == val; +} + inline bool Row::equals(const std::string& val, uint32_t colIndex) const { if (inStringTable(colIndex)) diff --git a/utils/windowfunction/idborderby.cpp b/utils/windowfunction/idborderby.cpp index db5cdf1c1..f839bc506 100644 --- a/utils/windowfunction/idborderby.cpp +++ b/utils/windowfunction/idborderby.cpp @@ -174,7 +174,6 @@ int DoubleCompare::operator()(IdbCompare* l, Row::Pointer r1, Row::Pointer r2) return ret; } - int FloatCompare::operator()(IdbCompare* l, Row::Pointer r1, Row::Pointer r2) { l->row1().setData(r1); @@ -206,6 +205,37 @@ int FloatCompare::operator()(IdbCompare* l, Row::Pointer r1, Row::Pointer r2) return ret; } +int LongDoubleCompare::operator()(IdbCompare* l, Row::Pointer r1, Row::Pointer r2) +{ + l->row1().setData(r1); + l->row2().setData(r2); + + bool b1 = l->row1().isNullValue(fSpec.fIndex); + bool b2 = l->row2().isNullValue(fSpec.fIndex); + + int ret = 0; + + if (b1 == true || b2 == true) + { + if (b1 == false && b2 == true) + ret = fSpec.fNf; + else if (b1 == true && b2 == false) + ret = -fSpec.fNf; + } + else + { + long double v1 = l->row1().getLongDoubleField(fSpec.fIndex); + long double v2 = l->row2().getLongDoubleField(fSpec.fIndex); + + if (v1 > v2) + ret = fSpec.fAsc; + else if (v1 < v2) + ret = -fSpec.fAsc; + } + + return ret; +} + bool CompareRule::less(Row::Pointer r1, Row::Pointer r2) { @@ -279,6 +309,13 @@ void CompareRule::compileRules(const std::vector& spec, const rowgr break; } + case CalpontSystemCatalog::LONGDOUBLE: + { + Compare* c = new LongDoubleCompare(*i); + fCompares.push_back(c); + break; + } + case CalpontSystemCatalog::DATE: case CalpontSystemCatalog::DATETIME: case CalpontSystemCatalog::TIME: @@ -442,6 +479,12 @@ bool EqualCompData::operator()(Row::Pointer a, Row::Pointer b) break; } + case CalpontSystemCatalog::LONGDOUBLE: + { + eq = (fRow1.getLongDoubleField(*i) == fRow2.getLongDoubleField(*i)); + break; + } + default: { eq = false; diff --git a/utils/windowfunction/idborderby.h b/utils/windowfunction/idborderby.h index a432fdc31..cd31eff30 100644 --- a/utils/windowfunction/idborderby.h +++ b/utils/windowfunction/idborderby.h @@ -119,6 +119,14 @@ public: int operator()(IdbCompare*, rowgroup::Row::Pointer, rowgroup::Row::Pointer); }; +class LongDoubleCompare : public Compare +{ +public: + LongDoubleCompare(const IdbSortSpec& spec) : Compare(spec) {} + + int operator()(IdbCompare*, rowgroup::Row::Pointer, rowgroup::Row::Pointer); +}; + class FloatCompare : public Compare { diff --git a/utils/windowfunction/wf_lead_lag.cpp b/utils/windowfunction/wf_lead_lag.cpp index 5160b75bf..961032f2f 100644 --- a/utils/windowfunction/wf_lead_lag.cpp +++ b/utils/windowfunction/wf_lead_lag.cpp @@ -90,6 +90,12 @@ boost::shared_ptr WF_lead_lag::makeFunction(int id, const break; } + case CalpontSystemCatalog::LONGDOUBLE: + { + func.reset(new WF_lead_lag(id, name)); + break; + } + case CalpontSystemCatalog::FLOAT: case CalpontSystemCatalog::UFLOAT: { diff --git a/utils/windowfunction/wf_min_max.cpp b/utils/windowfunction/wf_min_max.cpp index b0c1fe033..8679c743b 100644 --- a/utils/windowfunction/wf_min_max.cpp +++ b/utils/windowfunction/wf_min_max.cpp @@ -96,6 +96,12 @@ boost::shared_ptr WF_min_max::makeFunction(int id, const break; } + case CalpontSystemCatalog::LONGDOUBLE: + { + func.reset(new WF_min_max(id, name)); + break; + } + default: { func.reset(new WF_min_max(id, name)); diff --git a/utils/windowfunction/wf_percentile.cpp b/utils/windowfunction/wf_percentile.cpp index 83d6f57d8..b3e56c17a 100644 --- a/utils/windowfunction/wf_percentile.cpp +++ b/utils/windowfunction/wf_percentile.cpp @@ -102,6 +102,12 @@ boost::shared_ptr WF_percentile::makeFunction(int id, con break; } + case CalpontSystemCatalog::LONGDOUBLE: + { + func.reset(new WF_percentile(id, name)); + break; + } + default: { if (id == WF__PERCENTILE_DISC) @@ -140,6 +146,7 @@ boost::shared_ptr WF_percentile::makeFunction(int id, con case CalpontSystemCatalog::UDOUBLE: case CalpontSystemCatalog::FLOAT: case CalpontSystemCatalog::UFLOAT: + case CalpontSystemCatalog::LONGDOUBLE: { func.reset(new WF_percentile(id, name)); break; diff --git a/utils/windowfunction/wf_stats.cpp b/utils/windowfunction/wf_stats.cpp index 3599271d4..b5af19883 100644 --- a/utils/windowfunction/wf_stats.cpp +++ b/utils/windowfunction/wf_stats.cpp @@ -96,6 +96,12 @@ boost::shared_ptr WF_stats::makeFunction(int id, const st break; } + case CalpontSystemCatalog::LONGDOUBLE: + { + func.reset(new WF_stats(id, name)); + break; + } + default: { string errStr = name + "(" + colType2String[ct] + ")"; diff --git a/utils/windowfunction/wf_sum_avg.cpp b/utils/windowfunction/wf_sum_avg.cpp index 15586da3b..07d671c66 100644 --- a/utils/windowfunction/wf_sum_avg.cpp +++ b/utils/windowfunction/wf_sum_avg.cpp @@ -159,7 +159,7 @@ template boost::shared_ptr WF_sum_avg::makeFunction(int id, const string& name, int ct) { boost::shared_ptr func; - +#if 0 switch (ct) { case CalpontSystemCatalog::TINYINT: @@ -198,6 +198,35 @@ boost::shared_ptr WF_sum_avg::makeFunction(int id, const break; } + case CalpontSystemCatalog::LONGDOUBLE: + { + func.reset(new WF_sum_avg(id, name)); + break; + } +#endif + switch (ct) + { + case CalpontSystemCatalog::TINYINT: + case CalpontSystemCatalog::SMALLINT: + case CalpontSystemCatalog::MEDINT: + case CalpontSystemCatalog::INT: + case CalpontSystemCatalog::BIGINT: + case CalpontSystemCatalog::DECIMAL: + case CalpontSystemCatalog::UTINYINT: + case CalpontSystemCatalog::USMALLINT: + case CalpontSystemCatalog::UMEDINT: + case CalpontSystemCatalog::UINT: + case CalpontSystemCatalog::UBIGINT: + case CalpontSystemCatalog::UDECIMAL: + case CalpontSystemCatalog::DOUBLE: + case CalpontSystemCatalog::UDOUBLE: + case CalpontSystemCatalog::FLOAT: + case CalpontSystemCatalog::UFLOAT: + case CalpontSystemCatalog::LONGDOUBLE: + { + func.reset(new WF_sum_avg(id, name)); + break; + } default: { string errStr = name + "(" + colType2String[ct] + ")"; diff --git a/utils/windowfunction/wf_udaf.cpp b/utils/windowfunction/wf_udaf.cpp index 91ae5f9b2..250f3fc42 100644 --- a/utils/windowfunction/wf_udaf.cpp +++ b/utils/windowfunction/wf_udaf.cpp @@ -398,6 +398,48 @@ bool WF_udaf::dropValues(int64_t b, int64_t e) break; } + case CalpontSystemCatalog::LONGDOUBLE: + { + double valIn; + + if (cc) + { + valIn = cc->getLongDoubleVal(fRow, isNull); + } + else + { + getValue(colIn, valIn); + } + + // Check for distinct, if turned on. + // Currently, distinct only works on the first parameter. + if (k == 0) + { + if (fDistinct) + { + DistinctMap::iterator distinct; + distinct = fDistinctMap.find(valIn); + if (distinct != fDistinctMap.end()) + { + // This is a duplicate: decrement the count + --(*distinct).second; + if ((*distinct).second > 0) // still more of these + { + bSkipIt = true; + continue; + } + else + { + fDistinctMap.erase(distinct); + } + } + } + } + + datum.columnData = valIn; + break; + } + case CalpontSystemCatalog::CHAR: case CalpontSystemCatalog::VARCHAR: case CalpontSystemCatalog::VARBINARY: @@ -971,6 +1013,38 @@ void WF_udaf::operator()(int64_t b, int64_t e, int64_t c) break; } + case CalpontSystemCatalog::LONGDOUBLE: + { + long double valIn; + + if (cc) + { + valIn = cc->getLongDoubleVal(fRow, isNull); + } + else + { + getValue(colIn, valIn); + } + + // Check for distinct, if turned on. + // Currently, distinct only works on the first parameter. + if (k == 0 && fDistinct) + { + std::pair val = make_pair(valIn, 1); + std::pair distinct; + distinct = fDistinctMap.insert(val); + if (distinct.second == false) + { + ++(*distinct.first).second; + bSkipIt = true; + continue; + } + } + + datum.columnData = valIn; + break; + } + case CalpontSystemCatalog::CHAR: case CalpontSystemCatalog::VARCHAR: case CalpontSystemCatalog::VARBINARY: diff --git a/utils/windowfunction/windowfunctiontype.cpp b/utils/windowfunction/windowfunctiontype.cpp index 68015d120..471cdc1ac 100644 --- a/utils/windowfunction/windowfunctiontype.cpp +++ b/utils/windowfunction/windowfunctiontype.cpp @@ -290,6 +290,16 @@ template<> void WindowFunctionType::getValue(uint64_t i, float& t, CDT* c } } +template<> void WindowFunctionType::getValue(uint64_t i, long double& t, CDT* cdt) +{ + t = fRow.getLongDoubleField(i); + + if (cdt) + { + *cdt = execplan::CalpontSystemCatalog::LONGDOUBLE; + } +} + template<> void WindowFunctionType::getValue(uint64_t i, string& t, CDT* cdt) { t = fRow.getStringField(i); @@ -320,6 +330,11 @@ template<> void WindowFunctionType::setValue(uint64_t i, float& t) fRow.setFloatField(t, i); } +template<> void WindowFunctionType::setValue(uint64_t i, long double& t) +{ + fRow.setLongDoubleField(t, i); +} + template<> void WindowFunctionType::setValue(uint64_t i, string& t) { fRow.setStringField(t, i); @@ -406,6 +421,14 @@ void WindowFunctionType::setValue(int ct, int64_t b, int64_t e, int64_t c, T* v) setValue(i, fv); break; } + + case CalpontSystemCatalog::LONGDOUBLE: + { + long double dv = *v; + setValue(i, dv); + break; + } + default: { setValue(i, *v); @@ -523,6 +546,12 @@ void WindowFunctionType::getConstValue(ConstantColumn* cc, double& t, bo t = cc->getDoubleVal(fRow, b); } +template<> +void WindowFunctionType::getConstValue(ConstantColumn* cc, long double& t, bool& b) +{ + t = cc->getLongDoubleVal(fRow, b); +} + template<> void WindowFunctionType::getConstValue(ConstantColumn* cc, float& t, bool& b) { @@ -539,11 +568,13 @@ template void WindowFunctionType::implicit2T(uint64_t, int64_t&, int); template void WindowFunctionType::implicit2T(uint64_t, uint64_t&, int); template void WindowFunctionType::implicit2T(uint64_t, float&, int); template void WindowFunctionType::implicit2T(uint64_t, double&, int); +template void WindowFunctionType::implicit2T(uint64_t, long double&, int); template void WindowFunctionType::setValue(int, int64_t, int64_t, int64_t, int64_t*); template void WindowFunctionType::setValue(int, int64_t, int64_t, int64_t, uint64_t*); template void WindowFunctionType::setValue(int, int64_t, int64_t, int64_t, float*); template void WindowFunctionType::setValue(int, int64_t, int64_t, int64_t, double*); +template void WindowFunctionType::setValue(int, int64_t, int64_t, int64_t, long double*); void* WindowFunctionType::getNullValueByType(int ct, int pos) { @@ -557,6 +588,7 @@ void* WindowFunctionType::getNullValueByType(int ct, int pos) static uint64_t utinyIntNull = joblist::UTINYINTNULL; static uint64_t floatNull = joblist::FLOATNULL; static uint64_t doubleNull = joblist::DOUBLENULL; + static long double longDoubleNull= joblist::LONGDOUBLENULL; static uint64_t dateNull = joblist::DATENULL; static uint64_t datetimeNull = joblist::DATETIMENULL; static uint64_t timeNull = joblist::TIMENULL; @@ -691,6 +723,9 @@ void* WindowFunctionType::getNullValueByType(int ct, int pos) break; case CalpontSystemCatalog::LONGDOUBLE: + v = &longDoubleNull; + break; + case CalpontSystemCatalog::VARBINARY: default: std::ostringstream oss; diff --git a/utils/windowfunction/windowfunctiontype.h b/utils/windowfunction/windowfunctiontype.h index 5c2f43db0..5388898a0 100644 --- a/utils/windowfunction/windowfunctiontype.h +++ b/utils/windowfunction/windowfunctiontype.h @@ -223,6 +223,10 @@ protected: { return fRow.getDoubleField(i); } + long double getLongDoubleValue(uint64_t i) + { + return fRow.getLongDoubleField(i); + } void setIntValue(int64_t i, int64_t v) { fRow.setIntField(v, i); @@ -231,7 +235,10 @@ protected: { fRow.setDoubleField(v, i); } - + void setLongDoubleValue(int64_t i, long double v) + { + fRow.setLongDoubleField(v, i); + } // for string table rowgroup::Row::Pointer getPointer(joblist::RowPosition& r) From bf4a215cc8d721be09cd2c311ab95c951ae8cef9 Mon Sep 17 00:00:00 2001 From: David Hall Date: Mon, 4 Feb 2019 12:32:04 -0600 Subject: [PATCH 04/38] MCOL-2001 make redistribute parameters case insensitive. --- oamapps/mcsadmin/mcsadmin.cpp | 25 ++++++++++++++++++++----- 1 file changed, 20 insertions(+), 5 deletions(-) diff --git a/oamapps/mcsadmin/mcsadmin.cpp b/oamapps/mcsadmin/mcsadmin.cpp index 8b31353ac..dd7262d1e 100644 --- a/oamapps/mcsadmin/mcsadmin.cpp +++ b/oamapps/mcsadmin/mcsadmin.cpp @@ -32,7 +32,6 @@ extern int h_errno; #include "boost/filesystem/path.hpp" #include "boost/scoped_ptr.hpp" #include "boost/tokenizer.hpp" -#include "boost/algorithm/string/predicate.hpp" #include "sessionmanager.h" #include "dbrm.h" #include "messagequeue.h" @@ -678,7 +677,11 @@ int processCommand(string* arguments) vector srcDbroots; // all of the currently configured dbroots vector destDbroots; // srcDbroots - removeDbroots set::iterator dbiter; - if (boost::iequals(arguments[1], "start")) +#if _MSC_VER + if (_strnicmp(arguments[1].c_str(), "start", 5) == 0)) +#else + if (strncasecmp(arguments[1].c_str(), "start", 5) == 0) +#endif { // Get a list of all the configured dbroots in the xml file. DBRootConfigList dbRootConfigList; @@ -689,7 +692,11 @@ int processCommand(string* arguments) // The user may choose to redistribute in such a way as to // leave certain dbroots empty, presumably for later removal. - if (boost::iequals(arguments[2], "remove")) +#if _MSC_VER + if (_strnicmp(arguments[1].c_str(), "remove", 6) == 0)) +#else + if (strncasecmp(arguments[1].c_str(), "remove", 6) == 0) +#endif { int dbroot; bool error = false; @@ -793,7 +800,11 @@ int processCommand(string* arguments) SendToWES(oam, bs); } - else if (boost::iequals(arguments[1], "stop")) +#if _MSC_VER + if (_strnicmp(arguments[1].c_str(), "stop", 4) == 0)) +#else + if (strncasecmp(arguments[1].c_str(), "stop", 4) == 0) +#endif { ByteStream bs; // message WES ID, sequence #, action id @@ -803,7 +814,11 @@ int processCommand(string* arguments) bs.append((const ByteStream::byte*) &header, sizeof(header)); SendToWES(oam, bs); } - else if (boost::iequals(arguments[1], "status")) +#if _MSC_VER + if (_strnicmp(arguments[1].c_str(), "status", 6) == 0)) +#else + if (strncasecmp(arguments[1].c_str(), "status", 6) == 0) +#endif { ByteStream bs; // message WES ID, sequence #, action id From b81455744d938c442d248b09b10ebbee65e4e2ee Mon Sep 17 00:00:00 2001 From: Ben Thompson Date: Tue, 5 Feb 2019 13:43:21 -0600 Subject: [PATCH 05/38] Revert "Kill Jemalloc" This reverts commit e03f7dc88503c7044ebc5befdd432238e31d3f16. --- CMakeLists.txt | 8 ++++++- FindJeMalloc.cmake | 44 ++++++++++++++++++++++++++++++++++++++ build/infinidb.spec.in | 1 + build/infinidb.spec.in.4.5 | 1 + 4 files changed, 53 insertions(+), 1 deletion(-) create mode 100644 FindJeMalloc.cmake diff --git a/CMakeLists.txt b/CMakeLists.txt index 0932307ed..2645adc5c 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -92,6 +92,12 @@ if (NOT LIBXML2_FOUND) MESSAGE(FATAL_ERROR "Could not find a usable libxml2 development environment!") endif() +INCLUDE (FindJeMalloc.cmake) +if (NOT JEMALLOC_FOUND) + message(NOTICE "jemalloc not found! Compiling with standard malloc") + SET(JEMALLOC_LIBRARIES "") +endif() + FIND_PROGRAM(AWK_EXECUTABLE awk DOC "path to the awk executable") if(NOT AWK_EXECUTABLE) message(FATAL_ERROR "awk not found!") @@ -123,7 +129,7 @@ SET (ENGINE_LOCALDIR "${INSTALL_ENGINE}/local") SET (ENGINE_MYSQLDIR "${INSTALL_ENGINE}/mysql") SET (ENGINE_TOOLSDIR "${INSTALL_ENGINE}/tools") -SET (ENGINE_COMMON_LIBS messageqcpp loggingcpp configcpp idbboot ${Boost_LIBRARIES} xml2 pthread rt) +SET (ENGINE_COMMON_LIBS messageqcpp loggingcpp configcpp idbboot ${Boost_LIBRARIES} xml2 pthread rt ${JEMALLOC_LIBRARIES}) SET (ENGINE_OAM_LIBS oamcpp alarmmanager) SET (ENGINE_BRM_LIBS brm idbdatafile cacheutils rwlock ${ENGINE_OAM_LIBS} ${ENGINE_COMMON_LIBS}) SET (ENGINE_EXEC_LIBS joblist execplan windowfunction joiner rowgroup funcexp udfsdk dataconvert common compress mysqlcl_idb querystats querytele thrift threadpool ${ENGINE_BRM_LIBS}) diff --git a/FindJeMalloc.cmake b/FindJeMalloc.cmake new file mode 100644 index 000000000..c97bb1b9f --- /dev/null +++ b/FindJeMalloc.cmake @@ -0,0 +1,44 @@ +# - Try to find jemalloc headers and libraries. +# +# Usage of this module as follows: +# +# find_package(JeMalloc) +# +# Variables used by this module, they can change the default behaviour and need +# to be set before calling find_package: +# +# JEMALLOC_ROOT_DIR Set this variable to the root installation of +# jemalloc if the module has problems finding +# the proper installation path. +# +# Variables defined by this module: +# +# JEMALLOC_FOUND System has jemalloc libs/headers +# JEMALLOC_LIBRARIES The jemalloc library/libraries +# JEMALLOC_INCLUDE_DIR The location of jemalloc headers + +find_path(JEMALLOC_ROOT_DIR + NAMES include/jemalloc/jemalloc.h +) + +find_library(JEMALLOC_LIBRARIES + NAMES jemalloc + HINTS ${JEMALLOC_ROOT_DIR}/lib +) + +find_path(JEMALLOC_INCLUDE_DIR + NAMES jemalloc/jemalloc.h + HINTS ${JEMALLOC_ROOT_DIR}/include +) + +include(FindPackageHandleStandardArgs) +find_package_handle_standard_args(JeMalloc DEFAULT_MSG + JEMALLOC_LIBRARIES + JEMALLOC_INCLUDE_DIR +) + +mark_as_advanced( + JEMALLOC_ROOT_DIR + JEMALLOC_LIBRARIES + JEMALLOC_INCLUDE_DIR +) diff --git a/build/infinidb.spec.in b/build/infinidb.spec.in index 20a44e834..2531044a0 100644 --- a/build/infinidb.spec.in +++ b/build/infinidb.spec.in @@ -336,6 +336,7 @@ rm -rf $RPM_BUILD_ROOT $RPM_BUILD_DIR/%{name}-%{version}-%{release} /usr/local/mariadb/columnstore/lib/libmysqlcl_idb.so.1.0.0 /usr/local/mariadb/columnstore/lib/libquerystats.so.1.0.0 /usr/local/mariadb/columnstore/lib/libwriteengineredistribute.so.1.0.0 +/usr/local/mariadb/columnstore/lib/libjemalloc.so.3.3.0 /usr/local/mariadb/columnstore/lib/libidbdatafile.so.1.0.0 /usr/local/mariadb/columnstore/lib/hdfs-20.so /usr/local/mariadb/columnstore/lib/hdfs-12.so diff --git a/build/infinidb.spec.in.4.5 b/build/infinidb.spec.in.4.5 index 966f2a73a..b8145d580 100755 --- a/build/infinidb.spec.in.4.5 +++ b/build/infinidb.spec.in.4.5 @@ -338,6 +338,7 @@ rm -rf $RPM_BUILD_ROOT $RPM_BUILD_DIR/%{name}-%{version}-%{release} /usr/local/Calpont/lib/libmysqlcl_idb.so.1.0.0 /usr/local/Calpont/lib/libquerystats.so.1.0.0 /usr/local/Calpont/lib/libwriteengineredistribute.so.1.0.0 +/usr/local/Calpont/lib/libjemalloc.so.3.3.0 /usr/local/Calpont/lib/libidbdatafile.so.1.0.0 /usr/local/Calpont/lib/hdfs-20.so /usr/local/Calpont/lib/hdfs-12.so From 52a741e6efdd7d22b29701ab1cce0ae86278886e Mon Sep 17 00:00:00 2001 From: Ben Thompson Date: Tue, 5 Feb 2019 17:18:02 -0600 Subject: [PATCH 06/38] MCOL-2136: Force use of jemalloc for build and install. --- CMakeLists.txt | 2 +- build/infinidb.spec.in | 1 - build/infinidb.spec.in.4.5 | 1 - cpackEngineDEB.cmake | 2 +- cpackEngineRPM.cmake | 4 ++-- 5 files changed, 4 insertions(+), 6 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 2645adc5c..c72a74d61 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -94,7 +94,7 @@ endif() INCLUDE (FindJeMalloc.cmake) if (NOT JEMALLOC_FOUND) - message(NOTICE "jemalloc not found! Compiling with standard malloc") + message(FATAL_ERROR "jemalloc not found!") SET(JEMALLOC_LIBRARIES "") endif() diff --git a/build/infinidb.spec.in b/build/infinidb.spec.in index 2531044a0..20a44e834 100644 --- a/build/infinidb.spec.in +++ b/build/infinidb.spec.in @@ -336,7 +336,6 @@ rm -rf $RPM_BUILD_ROOT $RPM_BUILD_DIR/%{name}-%{version}-%{release} /usr/local/mariadb/columnstore/lib/libmysqlcl_idb.so.1.0.0 /usr/local/mariadb/columnstore/lib/libquerystats.so.1.0.0 /usr/local/mariadb/columnstore/lib/libwriteengineredistribute.so.1.0.0 -/usr/local/mariadb/columnstore/lib/libjemalloc.so.3.3.0 /usr/local/mariadb/columnstore/lib/libidbdatafile.so.1.0.0 /usr/local/mariadb/columnstore/lib/hdfs-20.so /usr/local/mariadb/columnstore/lib/hdfs-12.so diff --git a/build/infinidb.spec.in.4.5 b/build/infinidb.spec.in.4.5 index b8145d580..966f2a73a 100755 --- a/build/infinidb.spec.in.4.5 +++ b/build/infinidb.spec.in.4.5 @@ -338,7 +338,6 @@ rm -rf $RPM_BUILD_ROOT $RPM_BUILD_DIR/%{name}-%{version}-%{release} /usr/local/Calpont/lib/libmysqlcl_idb.so.1.0.0 /usr/local/Calpont/lib/libquerystats.so.1.0.0 /usr/local/Calpont/lib/libwriteengineredistribute.so.1.0.0 -/usr/local/Calpont/lib/libjemalloc.so.3.3.0 /usr/local/Calpont/lib/libidbdatafile.so.1.0.0 /usr/local/Calpont/lib/hdfs-20.so /usr/local/Calpont/lib/hdfs-12.so diff --git a/cpackEngineDEB.cmake b/cpackEngineDEB.cmake index 30d3d0ab9..13865c05b 100644 --- a/cpackEngineDEB.cmake +++ b/cpackEngineDEB.cmake @@ -60,7 +60,7 @@ SET(CPACK_DEBIAN_LIBS_PACKAGE_PROVIDES "mariadb-columnstore-libs") SET(CPACK_DEBIAN_PLATFORM_PACKAGE_PROVIDES "mariadb-columnstore-platform") SET(CPACK_DEBIAN_STORAGE-ENGINE_PACKAGE_PROVIDES "mariadb-columnstore-storage-engine") -SET(CPACK_DEBIAN_PLATFORM_PACKAGE_DEPENDS "expect, libboost-all-dev, mariadb-columnstore-libs") +SET(CPACK_DEBIAN_PLATFORM_PACKAGE_DEPENDS "expect, libboost-all-dev, mariadb-columnstore-libs, libjemalloc1") SET(CPACK_DEBIAN_STORAGE-ENGINE_PACKAGE_DEPENDS "mariadb-columnstore-libs") diff --git a/cpackEngineRPM.cmake b/cpackEngineRPM.cmake index 98974325d..12da14498 100644 --- a/cpackEngineRPM.cmake +++ b/cpackEngineRPM.cmake @@ -90,9 +90,9 @@ if (${REDHAT_VERSION_NUMBER} EQUAL 6) # Disable auto require as this will also try to pull Boost via RPM SET(CPACK_RPM_PACKAGE_AUTOREQPROV " no") elseif (${SUSE_VERSION_NUMBER} EQUAL 12) - SETA(CPACK_RPM_platform_PACKAGE_REQUIRES "expect" "boost-devel >= 1.54.0" "mariadb-columnstore-libs") + SETA(CPACK_RPM_platform_PACKAGE_REQUIRES "expect" "boost-devel >= 1.54.0" "mariadb-columnstore-libs" "jemalloc") else () - SETA(CPACK_RPM_platform_PACKAGE_REQUIRES "expect" "boost >= 1.53.0" "mariadb-columnstore-libs") + SETA(CPACK_RPM_platform_PACKAGE_REQUIRES "expect" "boost >= 1.53.0" "mariadb-columnstore-libs" "jemalloc") endif() SETA(CPACK_RPM_storage-engine_PACKAGE_REQUIRES "mariadb-columnstore-libs") From f805478eb91173676012c481e8dc81511707963e Mon Sep 17 00:00:00 2001 From: Andrew Hutchings Date: Fri, 8 Feb 2019 17:55:50 +0000 Subject: [PATCH 07/38] MCOL-2149 Fix cpimport decimal saturation If we saturate int64_t during string -> decimal conversion then end processing there instead of continuing. This preserves a good saturation value. --- writeengine/shared/we_convertor.cpp | 21 ++++++++++++++------- 1 file changed, 14 insertions(+), 7 deletions(-) diff --git a/writeengine/shared/we_convertor.cpp b/writeengine/shared/we_convertor.cpp index 8ad344ce3..7dcad3986 100644 --- a/writeengine/shared/we_convertor.cpp +++ b/writeengine/shared/we_convertor.cpp @@ -182,19 +182,26 @@ long long Convertor::convertDecimalString( { long double dval = strtold(field, NULL); long long ret = 0; - + // move scale digits to the left of the decimal point for (int i = 0; i < scale; i++) dval *= 10; - + // range check against int64 - if (dval > LLONG_MAX || dval < LLONG_MIN) + if (dval > LLONG_MAX) + { errno = ERANGE; - else - errno = 0; - + return LLONG_MAX; + } + if (dval < LLONG_MIN) + { + errno = ERANGE; + return LLONG_MIN; + } + errno = 0; + ret = dval; - + // get the fractional part of what's left & round ret up or down. dval -= ret; if (dval >= 0.5 && ret < LLONG_MAX) From d2f86b3f8db4c6e521e2edd40230da4a89edc72e Mon Sep 17 00:00:00 2001 From: zdrav1 Date: Tue, 12 Feb 2019 14:09:02 +0200 Subject: [PATCH 08/38] MCOL-2151, added jemalloc dependency check in columnstoreClusterTester --- utils/clusterTester/columnstoreClusterTester.sh | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/utils/clusterTester/columnstoreClusterTester.sh b/utils/clusterTester/columnstoreClusterTester.sh index 78fa3f889..a50387411 100755 --- a/utils/clusterTester/columnstoreClusterTester.sh +++ b/utils/clusterTester/columnstoreClusterTester.sh @@ -721,7 +721,7 @@ checkPackages() echo "** Run MariaDB ColumnStore Dependent Package Check" echo "" - declare -a CENTOS_PKG=("expect" "perl" "perl-DBI" "openssl" "zlib" "file" "sudo" "libaio" "rsync" "snappy" "net-tools" "perl-DBD-MySQL") + declare -a CENTOS_PKG=("expect" "perl" "perl-DBI" "openssl" "zlib" "file" "sudo" "libaio" "rsync" "jemalloc" "snappy" "net-tools" "perl-DBD-MySQL") if [ "$OS" == "centos6" ] || [ "$OS" == "centos7" ]; then if [ ! `which yum 2>/dev/null` ] ; then @@ -796,7 +796,7 @@ checkPackages() fi fi - declare -a SUSE_PKG=("boost-devel" "expect" "perl" "perl-DBI" "openssl" "file" "sudo" "libaio1" "rsync" "libsnappy1" "net-tools" "perl-DBD-mysql") + declare -a SUSE_PKG=("boost-devel" "expect" "perl" "perl-DBI" "openssl" "file" "sudo" "libaio1" "rsync" "jemalloc" "libsnappy1" "net-tools" "perl-DBD-mysql") if [ "$OS" == "suse12" ]; then if [ ! `which rpm 2>/dev/null` ] ; then @@ -848,7 +848,7 @@ checkPackages() fi fi - declare -a UBUNTU_PKG=("libboost-all-dev" "expect" "libdbi-perl" "perl" "openssl" "file" "sudo" "libreadline-dev" "rsync" "libsnappy1V5" "net-tools" "libdbd-mysql-perl") + declare -a UBUNTU_PKG=("libboost-all-dev" "expect" "libdbi-perl" "perl" "openssl" "file" "sudo" "libreadline-dev" "rsync" "jemalloc" "libsnappy1V5" "net-tools" "libdbd-mysql-perl") if [ "$OS" == "ubuntu16" ] ; then if [ ! `which dpkg 2>/dev/null` ] ; then @@ -913,7 +913,7 @@ checkPackages() fi fi - declare -a DEBIAN_PKG=("libboost-all-dev" "expect" "libdbi-perl" "perl" "openssl" "file" "sudo" "libreadline-dev" "rsync" "libsnappy1" "net-tools" "libdbd-mysql-perl") + declare -a DEBIAN_PKG=("libboost-all-dev" "expect" "libdbi-perl" "perl" "openssl" "file" "sudo" "libreadline-dev" "rsync" "jemalloc" "libsnappy1" "net-tools" "libdbd-mysql-perl") if [ "$OS" == "debian8" ]; then if [ ! `which dpkg 2>/dev/null` ] ; then @@ -978,7 +978,7 @@ checkPackages() fi fi - declare -a DEBIAN9_PKG=("libboost-all-dev" "expect" "libdbi-perl" "perl" "openssl" "file" "sudo" "libreadline5" "rsync" "libsnappy1V5" "net-tools" "libaio1") + declare -a DEBIAN9_PKG=("libboost-all-dev" "expect" "libdbi-perl" "perl" "openssl" "file" "sudo" "libreadline5" "rsync" "jemalloc" "libsnappy1V5" "net-tools" "libaio1") if [ "$OS" == "debian9" ]; then if [ ! `which dpkg 2>/dev/null` ] ; then From 971055a4739e96fceefba93cac7f3f7d580323e6 Mon Sep 17 00:00:00 2001 From: Roman Nozdrin Date: Thu, 27 Dec 2018 11:38:56 +0300 Subject: [PATCH 09/38] MCOL-1101. PoC for INFINIDB_VTABLE and thd variables migration to plugin sys variables. --- dbcon/mysql/CMakeLists.txt | 1 + dbcon/mysql/ha_calpont.cpp | 111 ++++++++++++++++--------------- dbcon/mysql/ha_calpont.h | 24 +------ dbcon/mysql/ha_calpont_ddl.cpp | 9 +-- dbcon/mysql/ha_calpont_impl.cpp | 14 +++- dbcon/mysql/ha_calpont_impl.h | 8 +-- dbcon/mysql/ha_calpont_impl_if.h | 2 - 7 files changed, 80 insertions(+), 89 deletions(-) diff --git a/dbcon/mysql/CMakeLists.txt b/dbcon/mysql/CMakeLists.txt index ae8f30622..b9af67200 100644 --- a/dbcon/mysql/CMakeLists.txt +++ b/dbcon/mysql/CMakeLists.txt @@ -4,6 +4,7 @@ include_directories( ${ENGINE_COMMON_INCLUDES} SET ( libcalmysql_SRCS + mcs_sysvars.cpp ha_calpont.cpp ha_calpont_impl.cpp ha_calpont_dml.cpp diff --git a/dbcon/mysql/ha_calpont.cpp b/dbcon/mysql/ha_calpont.cpp index 6ea5f85a6..b709a923d 100644 --- a/dbcon/mysql/ha_calpont.cpp +++ b/dbcon/mysql/ha_calpont.cpp @@ -380,6 +380,7 @@ static int calpont_close_connection ( handlerton* hton, THD* thd ) ha_calpont::ha_calpont(handlerton* hton, TABLE_SHARE* table_arg) : handler(hton, table_arg), + fe_conn_info(NULL), int_table_flags(HA_BINLOG_STMT_CAPABLE | HA_BINLOG_ROW_CAPABLE | HA_TABLE_SCAN_ON_INDEX | HA_CAN_TABLE_CONDITION_PUSHDOWN) @@ -683,7 +684,11 @@ int ha_calpont::rnd_init(bool scan) { DBUG_ENTER("ha_calpont::rnd_init"); - int rc = ha_calpont_impl_rnd_init(table); + // Use global THD* + set_original_query(current_thd, current_thd->query_string.str()); + mcs_handler_info mhi(static_cast(this), LEGACY); + + int rc = ha_calpont_impl_rnd_init(table, mhi); DBUG_RETURN(rc); } @@ -1101,44 +1106,6 @@ struct st_mysql_storage_engine columnstore_storage_engine = struct st_mysql_storage_engine infinidb_storage_engine = { MYSQL_HANDLERTON_INTERFACE_VERSION }; -#if 0 -static ulong srv_enum_var = 0; -static ulong srv_ulong_var = 0; - -const char* enum_var_names[] = -{ - "e1", "e2", NullS -}; - -TYPELIB enum_var_typelib = -{ - array_elements(enum_var_names) - 1, "enum_var_typelib", - enum_var_names, NULL -}; - -static MYSQL_SYSVAR_ENUM( - enum_var, // name - srv_enum_var, // varname - PLUGIN_VAR_RQCMDARG, // opt - "Sample ENUM system variable.", // comment - NULL, // check - NULL, // update - 0, // def - &enum_var_typelib); // typelib - -static MYSQL_SYSVAR_ULONG( - ulong_var, - srv_ulong_var, - PLUGIN_VAR_RQCMDARG, - "0..1000", - NULL, - NULL, - 8, - 0, - 1000, - 0); -#endif - /*@brief check_walk - It traverses filter conditions*/ /************************************************************ * DESCRIPTION: @@ -1367,15 +1334,55 @@ int ha_calpont_group_by_handler::end_scan() DBUG_RETURN(rc); } +/* +// compression_type +enum mcs_compression_type_t { + NO_COMPRESSION = 0, + SNAPPY = 2 +}; +const char* mcs_compression_type_names[] = { + "NO_COMPRESSION", + "SNAPPY", + NullS +}; -static struct st_mysql_sys_var* calpont_system_variables[] = -{ -// MYSQL_SYSVAR(enum_var), -// MYSQL_SYSVAR(ulong_var), +static TYPELIB mcs_compression_type_names_lib = { + array_elements(mcs_compression_type_names) - 1, + "mcs_compression_type_names", + mcs_compression_type_names, NULL }; +static MYSQL_THDVAR_ENUM( + compression_type, + PLUGIN_VAR_RQCMDARG, + "Controls compression type for create tables. Possible values are: " + "NO_COMPRESSION segment files aren't compressed; " + "SNAPPY segment files are Snappy compressed (default);", + NULL, + NULL, + SNAPPY, + &mcs_compression_type_names_lib); + +// original query +static MYSQL_THDVAR_STR( + original_query, + PLUGIN_VAR_MEMALLOC | + PLUGIN_VAR_RQCMDARG, + "Original query text", + NULL, + NULL, + NULL +); + +static struct st_mysql_sys_var* columnstore_system_variables[] = +{ + MYSQL_SYSVAR(compression_type), + MYSQL_SYSVAR(original_query), + NULL +};*/ + mysql_declare_plugin(columnstore) { MYSQL_STORAGE_ENGINE_PLUGIN, @@ -1388,7 +1395,7 @@ mysql_declare_plugin(columnstore) columnstore_done_func, /* Plugin Deinit */ 0x0100 /* 1.0 */, NULL, /* status variables */ - calpont_system_variables, /* system variables */ + mcs_system_variables, /* system variables */ NULL, /* reserved */ 0 /* config flags */ }, @@ -1403,7 +1410,7 @@ mysql_declare_plugin(columnstore) infinidb_done_func, /* Plugin Deinit */ 0x0100 /* 1.0 */, NULL, /* status variables */ - calpont_system_variables, /* system variables */ + mcs_system_variables, /* system variables */ NULL, /* reserved */ 0 /* config flags */ } @@ -1419,9 +1426,9 @@ maria_declare_plugin(columnstore) columnstore_init_func, columnstore_done_func, 0x0100, /* 1.0 */ - NULL, /* status variables */ - calpont_system_variables, /* system variables */ - "1.0", /* string version */ + NULL, /* status variables */ + mcs_system_variables, /* system variables */ + "1.0", /* string version */ MariaDB_PLUGIN_MATURITY_STABLE /* maturity */ }, { @@ -1434,10 +1441,10 @@ maria_declare_plugin(columnstore) infinidb_init_func, infinidb_done_func, 0x0100, /* 1.0 */ - NULL, /* status variables */ - calpont_system_variables, /* system variables */ - "1.0", /* string version */ - MariaDB_PLUGIN_MATURITY_STABLE /* maturity */ + NULL, /* status variables */ + mcs_system_variables, /* system variables */ + "1.0", /* string version */ + MariaDB_PLUGIN_MATURITY_STABLE /* maturity */ } maria_declare_plugin_end; diff --git a/dbcon/mysql/ha_calpont.h b/dbcon/mysql/ha_calpont.h index e618ed4f0..4ce29b5a6 100644 --- a/dbcon/mysql/ha_calpont.h +++ b/dbcon/mysql/ha_calpont.h @@ -15,35 +15,16 @@ along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ -/** @file ha_example.h - - @brief - The ha_example engine is a stubbed storage engine for example purposes only; - it does nothing at this point. Its purpose is to provide a source - code illustration of how to begin writing new storage engines; see also - /storage/example/ha_example.cc. - - @note - Please read ha_example.cc before reading this file. - Reminder: The example storage engine implements all methods that are *required* - to be implemented. For a full list of all methods that you can implement, see - handler.h. - - @see - /sql/handler.h and /storage/example/ha_example.cc -*/ - -// $Id: ha_calpont.h 9210 2013-01-21 14:10:42Z rdempsey $ - #ifndef HA_CALPONT_H__ #define HA_CALPONT_H__ #include #include "idb_mysql.h" +#include "mcs_sysvars.h" extern handlerton* calpont_hton; /** @brief - EXAMPLE_SHARE is a structure that will be shared among all open handlers. + This structure will be shared among all open handlers. This example implements the minimum of what you will probably need. */ typedef struct st_calpont_share @@ -62,6 +43,7 @@ class ha_calpont: public handler THR_LOCK_DATA lock; ///< MySQL lock INFINIDB_SHARE* share; ///< Shared lock info ulonglong int_table_flags; + void* fe_conn_info; public: ha_calpont(handlerton* hton, TABLE_SHARE* table_arg); diff --git a/dbcon/mysql/ha_calpont_ddl.cpp b/dbcon/mysql/ha_calpont_ddl.cpp index 487e0d4f8..cc77c0f89 100644 --- a/dbcon/mysql/ha_calpont_ddl.cpp +++ b/dbcon/mysql/ha_calpont_ddl.cpp @@ -49,6 +49,7 @@ using namespace std; #include using namespace boost; +#include "mcs_sysvars.h" #include "idb_mysql.h" #include "ha_calpont_impl_if.h" @@ -2128,7 +2129,7 @@ int ha_calpont_impl_create_(const char* name, TABLE* table_arg, HA_CREATE_INFO* return 1; } - int compressiontype = thd->variables.infinidb_compression_type; + int compressiontype = get_compression_type(thd); if (compressiontype == 1) compressiontype = 2; @@ -2140,7 +2141,7 @@ int ha_calpont_impl_create_(const char* name, TABLE* table_arg, HA_CREATE_INFO* } if ( compressiontype == MAX_INT ) - compressiontype = thd->variables.infinidb_compression_type; + compressiontype = get_compression_type(thd); else if ( compressiontype < 0 ) { string emsg = IDBErrorInfo::instance()->errorMsg(ERR_INVALID_COMPRESSION_TYPE); @@ -2473,12 +2474,12 @@ extern "C" if ( thd->db.length ) db = thd->db.str; - int compressiontype = thd->variables.infinidb_compression_type; + int compressiontype = get_compression_type(thd); if (compressiontype == 1) compressiontype = 2; if ( compressiontype == MAX_INT ) - compressiontype = thd->variables.infinidb_compression_type; + compressiontype = get_compression_type(thd); //hdfs if ((compressiontype == 0) && (useHdfs)) diff --git a/dbcon/mysql/ha_calpont_impl.cpp b/dbcon/mysql/ha_calpont_impl.cpp index 3be44d2b8..4bbdac344 100644 --- a/dbcon/mysql/ha_calpont_impl.cpp +++ b/dbcon/mysql/ha_calpont_impl.cpp @@ -142,6 +142,7 @@ using namespace funcexp; #include "installdir.h" #include "columnstoreversion.h" +#include "mcs_sysvars.h" namespace cal_impl_if { @@ -2767,8 +2768,14 @@ int ha_calpont_impl_discover_existence(const char* schema, const char* name) return 0; } -int ha_calpont_impl_rnd_init(TABLE* table) +int ha_calpont_impl_rnd_init(TABLE* table, mcs_handler_info hndtl_ptr) { + ha_calpont* handler; + if ( hndtl_ptr.hndl_type == LEGACY ) + { + handler = reinterpret_cast(hndtl_ptr.hndl_ptr); + } + #ifdef DEBUG_SETENV string home(getenv("HOME")); @@ -3092,8 +3099,9 @@ int ha_calpont_impl_rnd_init(TABLE* table) return 0; string query; - query.assign(thd->infinidb_vtable.original_query.ptr(), - thd->infinidb_vtable.original_query.length()); + const char *original_query = get_original_query(current_thd); + query.assign(original_query, + strlen(original_query)); csep->data(query); try diff --git a/dbcon/mysql/ha_calpont_impl.h b/dbcon/mysql/ha_calpont_impl.h index 7f7c43aae..db465ea83 100644 --- a/dbcon/mysql/ha_calpont_impl.h +++ b/dbcon/mysql/ha_calpont_impl.h @@ -16,12 +16,6 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ -/* - * $Id: ha_calpont_impl.h 9413 2013-04-22 22:03:42Z zzhu $ - */ - -/** @file */ - #ifndef HA_CALPONT_IMPL_H__ #define HA_CALPONT_IMPL_H__ @@ -33,7 +27,7 @@ extern int ha_calpont_impl_create(const char* name, TABLE* table_arg, HA_CREATE_ extern int ha_calpont_impl_delete_table(const char* name); extern int ha_calpont_impl_open(const char* name, int mode, uint32_t test_if_locked); extern int ha_calpont_impl_close(void); -extern int ha_calpont_impl_rnd_init(TABLE* table); +extern int ha_calpont_impl_rnd_init(TABLE* table, mcs_handler_info hndtl_ptr); extern int ha_calpont_impl_rnd_next(uchar* buf, TABLE* table); extern int ha_calpont_impl_rnd_end(TABLE* table); extern int ha_calpont_impl_write_row(uchar* buf, TABLE* table); diff --git a/dbcon/mysql/ha_calpont_impl_if.h b/dbcon/mysql/ha_calpont_impl_if.h index 72579111b..23d9461c2 100644 --- a/dbcon/mysql/ha_calpont_impl_if.h +++ b/dbcon/mysql/ha_calpont_impl_if.h @@ -321,8 +321,6 @@ struct cal_connection_info std::vector columnTypes; }; -typedef std::tr1::unordered_map CalConnMap; - const std::string infinidb_err_msg = "\nThe query includes syntax that is not supported by MariaDB Columnstore. Use 'show warnings;' to get more information. Review the MariaDB Columnstore Syntax guide for additional information on supported distributed syntax or consider changing the MariaDB Columnstore Operating Mode (infinidb_vtable_mode)."; int cp_get_plan(THD* thd, execplan::SCSEP& csep); From ca0240037ad8eaaa3a062c368a11cc777ce049b1 Mon Sep 17 00:00:00 2001 From: Roman Nozdrin Date: Sun, 30 Dec 2018 07:38:39 +0300 Subject: [PATCH 10/38] FE connection info structure migration. Replaced INFINIDB_VTABLE.cal_conn_info with a plugin thread variable. Fixed query_string memory deallocation crash at plugin_thdvar_cleanup. --- dbcon/mysql/ha_calpont.cpp | 8 +- dbcon/mysql/ha_calpont.h | 1 - dbcon/mysql/ha_calpont_ddl.cpp | 6 +- dbcon/mysql/ha_calpont_execplan.cpp | 37 +++--- dbcon/mysql/ha_calpont_impl.cpp | 168 ++++++++++++++-------------- dbcon/mysql/ha_calpont_impl.h | 2 +- dbcon/mysql/ha_pseudocolumn.cpp | 13 ++- dbcon/mysql/ha_window_function.cpp | 13 ++- dbcon/mysql/mcs_sysvars.cpp | 124 ++++++++++++++++++++ dbcon/mysql/mcs_sysvars.h | 58 ++++++++++ 10 files changed, 303 insertions(+), 127 deletions(-) create mode 100644 dbcon/mysql/mcs_sysvars.cpp create mode 100644 dbcon/mysql/mcs_sysvars.h diff --git a/dbcon/mysql/ha_calpont.cpp b/dbcon/mysql/ha_calpont.cpp index b709a923d..5caf10997 100644 --- a/dbcon/mysql/ha_calpont.cpp +++ b/dbcon/mysql/ha_calpont.cpp @@ -380,7 +380,6 @@ static int calpont_close_connection ( handlerton* hton, THD* thd ) ha_calpont::ha_calpont(handlerton* hton, TABLE_SHARE* table_arg) : handler(hton, table_arg), - fe_conn_info(NULL), int_table_flags(HA_BINLOG_STMT_CAPABLE | HA_BINLOG_ROW_CAPABLE | HA_TABLE_SCAN_ON_INDEX | HA_CAN_TABLE_CONDITION_PUSHDOWN) @@ -684,11 +683,10 @@ int ha_calpont::rnd_init(bool scan) { DBUG_ENTER("ha_calpont::rnd_init"); - // Use global THD* - set_original_query(current_thd, current_thd->query_string.str()); - mcs_handler_info mhi(static_cast(this), LEGACY); + String query_string_cpy; query_string_cpy.append(current_thd->query_string.str()); + set_original_query(current_thd, query_string_cpy.c_ptr_safe()); - int rc = ha_calpont_impl_rnd_init(table, mhi); + int rc = ha_calpont_impl_rnd_init(table); DBUG_RETURN(rc); } diff --git a/dbcon/mysql/ha_calpont.h b/dbcon/mysql/ha_calpont.h index 4ce29b5a6..2323d2d13 100644 --- a/dbcon/mysql/ha_calpont.h +++ b/dbcon/mysql/ha_calpont.h @@ -43,7 +43,6 @@ class ha_calpont: public handler THR_LOCK_DATA lock; ///< MySQL lock INFINIDB_SHARE* share; ///< Shared lock info ulonglong int_table_flags; - void* fe_conn_info; public: ha_calpont(handlerton* hton, TABLE_SHARE* table_arg); diff --git a/dbcon/mysql/ha_calpont_ddl.cpp b/dbcon/mysql/ha_calpont_ddl.cpp index cc77c0f89..e7d7153bc 100644 --- a/dbcon/mysql/ha_calpont_ddl.cpp +++ b/dbcon/mysql/ha_calpont_ddl.cpp @@ -664,10 +664,10 @@ int ProcessDDLStatement(string& ddlStatement, string& schema, const string& tabl IDBCompressInterface idbCompress; parser.Parse(ddlStatement.c_str()); - if (!thd->infinidb_vtable.cal_conn_info) - thd->infinidb_vtable.cal_conn_info = (void*)(new cal_connection_info()); + if (get_fe_conn_info_ptr() == NULL) + set_fe_conn_info_ptr((void*)new cal_connection_info()); - cal_connection_info* ci = reinterpret_cast(thd->infinidb_vtable.cal_conn_info); + cal_connection_info* ci = reinterpret_cast(get_fe_conn_info_ptr()); if (parser.Good()) { diff --git a/dbcon/mysql/ha_calpont_execplan.cpp b/dbcon/mysql/ha_calpont_execplan.cpp index 914743a25..99ad80605 100644 --- a/dbcon/mysql/ha_calpont_execplan.cpp +++ b/dbcon/mysql/ha_calpont_execplan.cpp @@ -58,6 +58,7 @@ using namespace logging; #include "idb_mysql.h" #include "ha_calpont_impl_if.h" +#include "mcs_sysvars.h" #include "ha_subquery.h" //#include "ha_view.h" using namespace cal_impl_if; @@ -1442,10 +1443,10 @@ bool buildPredicateItem(Item_func* ifp, gp_walk_info* gwip) } } - if (!(gwip->thd->infinidb_vtable.cal_conn_info)) - gwip->thd->infinidb_vtable.cal_conn_info = (void*)(new cal_connection_info()); + if (get_fe_conn_info_ptr() == NULL) + set_fe_conn_info_ptr((void*)new cal_connection_info()); - cal_connection_info* ci = reinterpret_cast(gwip->thd->infinidb_vtable.cal_conn_info); + cal_connection_info* ci = reinterpret_cast(get_fe_conn_info_ptr()); if (ifp->functype() == Item_func::BETWEEN) @@ -2466,10 +2467,10 @@ void setError(THD* thd, uint32_t errcode, string errmsg) thd->infinidb_vtable.override_largeside_estimate = false; // reset expressionID - if (!(thd->infinidb_vtable.cal_conn_info)) - thd->infinidb_vtable.cal_conn_info = (void*)(new cal_connection_info()); + if (get_fe_conn_info_ptr() == NULL) + set_fe_conn_info_ptr((void*)new cal_connection_info()); - cal_connection_info* ci = reinterpret_cast(thd->infinidb_vtable.cal_conn_info); + cal_connection_info* ci = reinterpret_cast(get_fe_conn_info_ptr()); ci->expressionId = 0; } @@ -3104,10 +3105,10 @@ ArithmeticColumn* buildArithmeticColumn( bool& nonSupport, bool pushdownHand) { - if (!(gwi.thd->infinidb_vtable.cal_conn_info)) - gwi.thd->infinidb_vtable.cal_conn_info = (void*)(new cal_connection_info()); + if (get_fe_conn_info_ptr() == NULL) + set_fe_conn_info_ptr((void*)new cal_connection_info()); - cal_connection_info* ci = reinterpret_cast(gwi.thd->infinidb_vtable.cal_conn_info); + cal_connection_info* ci = reinterpret_cast(get_fe_conn_info_ptr()); ArithmeticColumn* ac = new ArithmeticColumn(); Item** sfitempp = item->arguments(); @@ -3329,10 +3330,10 @@ ReturnedColumn* buildFunctionColumn( bool& nonSupport, bool pushdownHand) { - if (!(gwi.thd->infinidb_vtable.cal_conn_info)) - gwi.thd->infinidb_vtable.cal_conn_info = (void*)(new cal_connection_info()); + if (get_fe_conn_info_ptr() == NULL) + set_fe_conn_info_ptr((void*)new cal_connection_info()); - cal_connection_info* ci = reinterpret_cast(gwi.thd->infinidb_vtable.cal_conn_info); + cal_connection_info* ci = reinterpret_cast(get_fe_conn_info_ptr()); string funcName = ifp->func_name(); FuncExp* funcExp = FuncExp::instance(); @@ -3801,10 +3802,10 @@ ReturnedColumn* buildFunctionColumn( FunctionColumn* buildCaseFunction(Item_func* item, gp_walk_info& gwi, bool& nonSupport) { - if (!(gwi.thd->infinidb_vtable.cal_conn_info)) - gwi.thd->infinidb_vtable.cal_conn_info = (void*)(new cal_connection_info()); + if (get_fe_conn_info_ptr() == NULL) + set_fe_conn_info_ptr((void*)new cal_connection_info()); - cal_connection_info* ci = reinterpret_cast(gwi.thd->infinidb_vtable.cal_conn_info); + cal_connection_info* ci = reinterpret_cast(get_fe_conn_info_ptr()); FunctionColumn* fc = new FunctionColumn(); FunctionParm funcParms; @@ -4194,10 +4195,10 @@ ReturnedColumn* buildAggregateColumn(Item* item, gp_walk_info& gwi) vector selCols; vector orderCols; bool bIsConst = false; - if (!(gwi.thd->infinidb_vtable.cal_conn_info)) - gwi.thd->infinidb_vtable.cal_conn_info = (void*)(new cal_connection_info()); + if (get_fe_conn_info_ptr() == NULL) + set_fe_conn_info_ptr((void*)new cal_connection_info()); - cal_connection_info* ci = reinterpret_cast(gwi.thd->infinidb_vtable.cal_conn_info); + cal_connection_info* ci = reinterpret_cast(get_fe_conn_info_ptr()); Item_sum* isp = reinterpret_cast(item); Item** sfitempp = isp->get_orig_args(); diff --git a/dbcon/mysql/ha_calpont_impl.cpp b/dbcon/mysql/ha_calpont_impl.cpp index 4bbdac344..c15b0f98b 100644 --- a/dbcon/mysql/ha_calpont_impl.cpp +++ b/dbcon/mysql/ha_calpont_impl.cpp @@ -866,10 +866,10 @@ void makeUpdateSemiJoin(const ParseTree* n, void* obj) uint32_t doUpdateDelete(THD* thd) { - if (!thd->infinidb_vtable.cal_conn_info) - thd->infinidb_vtable.cal_conn_info = (void*)(new cal_connection_info()); + if (get_fe_conn_info_ptr() == NULL) + set_fe_conn_info_ptr((void*)new cal_connection_info()); - cal_connection_info* ci = reinterpret_cast(thd->infinidb_vtable.cal_conn_info); + cal_connection_info* ci = reinterpret_cast(get_fe_conn_info_ptr()); //@bug 5660. Error out DDL/DML on slave node, or on local query node if (ci->isSlaveNode && !thd->slave_thread) @@ -1919,10 +1919,10 @@ extern "C" { THD* thd = current_thd; - if (!thd->infinidb_vtable.cal_conn_info) - thd->infinidb_vtable.cal_conn_info = (void*)(new cal_connection_info()); + if (get_fe_conn_info_ptr() == NULL) + set_fe_conn_info_ptr((void*)new cal_connection_info()); - cal_connection_info* ci = reinterpret_cast(thd->infinidb_vtable.cal_conn_info); + cal_connection_info* ci = reinterpret_cast(get_fe_conn_info_ptr()); unsigned long l = ci->queryStats.size(); @@ -1971,10 +1971,10 @@ extern "C" { THD* thd = current_thd; - if (!thd->infinidb_vtable.cal_conn_info) - thd->infinidb_vtable.cal_conn_info = (void*)(new cal_connection_info()); + if (get_fe_conn_info_ptr() == NULL) + set_fe_conn_info_ptr((void*)new cal_connection_info()); - cal_connection_info* ci = reinterpret_cast(thd->infinidb_vtable.cal_conn_info); + cal_connection_info* ci = reinterpret_cast(get_fe_conn_info_ptr()); long long oldTrace = ci->traceFlags; ci->traceFlags = (uint32_t)(*((long long*)args->args[0])); @@ -2329,10 +2329,10 @@ extern "C" { THD* thd = current_thd; - if (!thd->infinidb_vtable.cal_conn_info) - thd->infinidb_vtable.cal_conn_info = (void*)(new cal_connection_info()); + if (get_fe_conn_info_ptr() == NULL) + set_fe_conn_info_ptr((void*)new cal_connection_info()); - cal_connection_info* ci = reinterpret_cast(thd->infinidb_vtable.cal_conn_info); + cal_connection_info* ci = reinterpret_cast(get_fe_conn_info_ptr()); CalpontSystemCatalog::TableName tableName; if ( args->arg_count == 2 ) @@ -2402,11 +2402,11 @@ extern "C" { THD* thd = current_thd; - if (!thd->infinidb_vtable.cal_conn_info) - thd->infinidb_vtable.cal_conn_info = (void*)(new cal_connection_info()); + if (get_fe_conn_info_ptr() == NULL) + set_fe_conn_info_ptr((void*)new cal_connection_info()); cal_connection_info* ci = reinterpret_cast( - thd->infinidb_vtable.cal_conn_info); + get_fe_conn_info_ptr()); long long lockID = *reinterpret_cast(args->args[0]); if ( !ci->dmlProc ) @@ -2581,10 +2581,10 @@ extern "C" } } - if (!thd->infinidb_vtable.cal_conn_info) - thd->infinidb_vtable.cal_conn_info = (void*)(new cal_connection_info()); + if (get_fe_conn_info_ptr() == NULL) + set_fe_conn_info_ptr((void*)new cal_connection_info()); - cal_connection_info* ci = reinterpret_cast(thd->infinidb_vtable.cal_conn_info); + cal_connection_info* ci = reinterpret_cast(get_fe_conn_info_ptr()); if (flags > 0) //msgp = &connMap[sessionID].extendedStats; @@ -2677,10 +2677,10 @@ extern "C" { THD* thd = current_thd; - if (!thd->infinidb_vtable.cal_conn_info) - thd->infinidb_vtable.cal_conn_info = (void*)(new cal_connection_info()); + if (get_fe_conn_info_ptr() == NULL) + set_fe_conn_info_ptr((void*)new cal_connection_info()); - cal_connection_info* ci = reinterpret_cast(thd->infinidb_vtable.cal_conn_info); + cal_connection_info* ci = reinterpret_cast(get_fe_conn_info_ptr()); idbassert(ci != 0); MessageQueueClient* mqc = 0; @@ -2768,14 +2768,8 @@ int ha_calpont_impl_discover_existence(const char* schema, const char* name) return 0; } -int ha_calpont_impl_rnd_init(TABLE* table, mcs_handler_info hndtl_ptr) +int ha_calpont_impl_rnd_init(TABLE* table) { - ha_calpont* handler; - if ( hndtl_ptr.hndl_type == LEGACY ) - { - handler = reinterpret_cast(hndtl_ptr.hndl_ptr); - } - #ifdef DEBUG_SETENV string home(getenv("HOME")); @@ -2874,10 +2868,10 @@ int ha_calpont_impl_rnd_init(TABLE* table, mcs_handler_info hndtl_ptr) boost::shared_ptr csc = CalpontSystemCatalog::makeCalpontSystemCatalog(sessionID); csc->identity(CalpontSystemCatalog::FE); - if (!thd->infinidb_vtable.cal_conn_info) - thd->infinidb_vtable.cal_conn_info = (void*)(new cal_connection_info()); + if (get_fe_conn_info_ptr() == NULL) + set_fe_conn_info_ptr((void*)new cal_connection_info()); - cal_connection_info* ci = reinterpret_cast(thd->infinidb_vtable.cal_conn_info); + cal_connection_info* ci = reinterpret_cast(get_fe_conn_info_ptr()); idbassert(ci != 0); @@ -3413,10 +3407,10 @@ int ha_calpont_impl_rnd_next(uchar* buf, TABLE* table) return ER_INTERNAL_ERROR; } - if (!thd->infinidb_vtable.cal_conn_info) - thd->infinidb_vtable.cal_conn_info = (void*)(new cal_connection_info()); + if (get_fe_conn_info_ptr() == NULL) + set_fe_conn_info_ptr((void*)new cal_connection_info()); - cal_connection_info* ci = reinterpret_cast(thd->infinidb_vtable.cal_conn_info); + cal_connection_info* ci = reinterpret_cast(get_fe_conn_info_ptr()); // @bug 3078 if (thd->killed == KILL_QUERY || thd->killed == KILL_QUERY_HARD) @@ -3514,8 +3508,8 @@ int ha_calpont_impl_rnd_end(TABLE* table) thd->infinidb_vtable.isNewQuery = true; - if (thd->infinidb_vtable.cal_conn_info) - ci = reinterpret_cast(thd->infinidb_vtable.cal_conn_info); + if (get_fe_conn_info_ptr() != NULL) + ci = reinterpret_cast(get_fe_conn_info_ptr()); if (thd->infinidb_vtable.vtable_state == THD::INFINIDB_ORDER_BY ) { @@ -3563,8 +3557,8 @@ int ha_calpont_impl_rnd_end(TABLE* table) if (!ci) { - thd->infinidb_vtable.cal_conn_info = (void*)(new cal_connection_info()); - ci = reinterpret_cast(thd->infinidb_vtable.cal_conn_info); + set_fe_conn_info_ptr((void*)new cal_connection_info()); + ci = reinterpret_cast(get_fe_conn_info_ptr()); } // @bug 3078. Also session limit variable works the same as ctrl+c @@ -3684,10 +3678,10 @@ int ha_calpont_impl_create(const char* name, TABLE* table_arg, HA_CREATE_INFO* c { THD* thd = current_thd; - if (!thd->infinidb_vtable.cal_conn_info) - thd->infinidb_vtable.cal_conn_info = (void*)(new cal_connection_info()); + if (get_fe_conn_info_ptr() == NULL) + set_fe_conn_info_ptr((void*)new cal_connection_info()); - cal_connection_info* ci = reinterpret_cast(thd->infinidb_vtable.cal_conn_info); + cal_connection_info* ci = reinterpret_cast(get_fe_conn_info_ptr()); // @bug1940 Do nothing for select query. Support of set default engine to IDB. if (thd->infinidb_vtable.vtable_state == THD::INFINIDB_CREATE_VTABLE || @@ -3733,10 +3727,10 @@ int ha_calpont_impl_delete_table(const char* name) if (string(name).find("@0024vtable") != string::npos) return 0; - if (!thd->infinidb_vtable.cal_conn_info) - thd->infinidb_vtable.cal_conn_info = (void*)(new cal_connection_info()); + if (get_fe_conn_info_ptr() == NULL) + set_fe_conn_info_ptr((void*)new cal_connection_info()); - cal_connection_info* ci = reinterpret_cast(thd->infinidb_vtable.cal_conn_info); + cal_connection_info* ci = reinterpret_cast(get_fe_conn_info_ptr()); if (!thd) return 0; @@ -3809,10 +3803,10 @@ int ha_calpont_impl_write_row(uchar* buf, TABLE* table) } } - if (!thd->infinidb_vtable.cal_conn_info) - thd->infinidb_vtable.cal_conn_info = (void*)(new cal_connection_info()); + if (get_fe_conn_info_ptr() == NULL) + set_fe_conn_info_ptr((void*)new cal_connection_info()); - cal_connection_info* ci = reinterpret_cast(thd->infinidb_vtable.cal_conn_info); + cal_connection_info* ci = reinterpret_cast(get_fe_conn_info_ptr()); if (thd->slave_thread) return 0; @@ -3855,10 +3849,10 @@ int ha_calpont_impl_update_row() //@Bug 2540. Return the correct error code. THD* thd = current_thd; - if (!thd->infinidb_vtable.cal_conn_info) - thd->infinidb_vtable.cal_conn_info = (void*)(new cal_connection_info()); + if (get_fe_conn_info_ptr() == NULL) + set_fe_conn_info_ptr((void*)new cal_connection_info()); - cal_connection_info* ci = reinterpret_cast(thd->infinidb_vtable.cal_conn_info); + cal_connection_info* ci = reinterpret_cast(get_fe_conn_info_ptr()); int rc = ci->rc; if ( rc != 0 ) @@ -3872,10 +3866,10 @@ int ha_calpont_impl_delete_row() //@Bug 2540. Return the correct error code. THD* thd = current_thd; - if (!thd->infinidb_vtable.cal_conn_info) - thd->infinidb_vtable.cal_conn_info = (void*)(new cal_connection_info()); + if (get_fe_conn_info_ptr() == NULL) + set_fe_conn_info_ptr((void*)new cal_connection_info()); - cal_connection_info* ci = reinterpret_cast(thd->infinidb_vtable.cal_conn_info); + cal_connection_info* ci = reinterpret_cast(get_fe_conn_info_ptr()); int rc = ci->rc; if ( rc != 0 ) @@ -3888,10 +3882,10 @@ void ha_calpont_impl_start_bulk_insert(ha_rows rows, TABLE* table) { THD* thd = current_thd; - if (!thd->infinidb_vtable.cal_conn_info) - thd->infinidb_vtable.cal_conn_info = (void*)(new cal_connection_info()); + if (get_fe_conn_info_ptr() == NULL) + set_fe_conn_info_ptr((void*)new cal_connection_info()); - cal_connection_info* ci = reinterpret_cast(thd->infinidb_vtable.cal_conn_info); + cal_connection_info* ci = reinterpret_cast(get_fe_conn_info_ptr()); // clear rows variable ci->rowsHaveInserted = 0; @@ -4456,10 +4450,10 @@ int ha_calpont_impl_end_bulk_insert(bool abort, TABLE* table) std::string aTmpDir(startup::StartUp::tmpDir()); - if (!thd->infinidb_vtable.cal_conn_info) - thd->infinidb_vtable.cal_conn_info = (void*)(new cal_connection_info()); + if (get_fe_conn_info_ptr() == NULL) + set_fe_conn_info_ptr((void*)new cal_connection_info()); - cal_connection_info* ci = reinterpret_cast(thd->infinidb_vtable.cal_conn_info); + cal_connection_info* ci = reinterpret_cast(get_fe_conn_info_ptr()); if (thd->slave_thread) return 0; @@ -4714,10 +4708,10 @@ int ha_calpont_impl_commit (handlerton* hton, THD* thd, bool all) thd->infinidb_vtable.vtable_state == THD::INFINIDB_REDO_PHASE1) return 0; - if (!thd->infinidb_vtable.cal_conn_info) - thd->infinidb_vtable.cal_conn_info = (void*)(new cal_connection_info()); + if (get_fe_conn_info_ptr() == NULL) + set_fe_conn_info_ptr((void*)new cal_connection_info()); - cal_connection_info* ci = reinterpret_cast(thd->infinidb_vtable.cal_conn_info); + cal_connection_info* ci = reinterpret_cast(get_fe_conn_info_ptr()); if (ci->isAlter) return 0; @@ -4755,10 +4749,10 @@ int ha_calpont_impl_rollback (handlerton* hton, THD* thd, bool all) // return 0; //} - if (!thd->infinidb_vtable.cal_conn_info) - thd->infinidb_vtable.cal_conn_info = (void*)(new cal_connection_info()); + if (get_fe_conn_info_ptr() == NULL) + set_fe_conn_info_ptr((void*)new cal_connection_info()); - cal_connection_info* ci = reinterpret_cast(thd->infinidb_vtable.cal_conn_info); + cal_connection_info* ci = reinterpret_cast(get_fe_conn_info_ptr()); if ( !ci->dmlProc ) { @@ -4785,10 +4779,10 @@ int ha_calpont_impl_close_connection (handlerton* hton, THD* thd) execplan::CalpontSystemCatalog::removeCalpontSystemCatalog(tid2sid(thd->thread_id)); - if (!thd->infinidb_vtable.cal_conn_info) - thd->infinidb_vtable.cal_conn_info = (void*)(new cal_connection_info()); + if (get_fe_conn_info_ptr() == NULL) + set_fe_conn_info_ptr((void*)new cal_connection_info()); - cal_connection_info* ci = reinterpret_cast(thd->infinidb_vtable.cal_conn_info); + cal_connection_info* ci = reinterpret_cast(get_fe_conn_info_ptr()); if (!ci) return 0; @@ -4815,10 +4809,10 @@ int ha_calpont_impl_rename_table(const char* from, const char* to) IDEBUG( cout << "ha_calpont_impl_rename_table: " << from << " => " << to << endl ); THD* thd = current_thd; - if (!thd->infinidb_vtable.cal_conn_info) - thd->infinidb_vtable.cal_conn_info = (void*)(new cal_connection_info()); + if (get_fe_conn_info_ptr() == NULL) + set_fe_conn_info_ptr((void*)new cal_connection_info()); - cal_connection_info* ci = reinterpret_cast(thd->infinidb_vtable.cal_conn_info); + cal_connection_info* ci = reinterpret_cast(get_fe_conn_info_ptr()); //@Bug 1948. Alter table call rename table twice if ( ci->alterTableState == cal_connection_info::ALTER_FIRST_RENAME ) @@ -4866,10 +4860,10 @@ COND* ha_calpont_impl_cond_push(COND* cond, TABLE* table) alias.assign(table->alias.ptr(), table->alias.length()); IDEBUG( cout << "ha_calpont_impl_cond_push: " << alias << endl ); - if (!thd->infinidb_vtable.cal_conn_info) - thd->infinidb_vtable.cal_conn_info = (void*)(new cal_connection_info()); + if (get_fe_conn_info_ptr() == NULL) + set_fe_conn_info_ptr((void*)new cal_connection_info()); - cal_connection_info* ci = reinterpret_cast(thd->infinidb_vtable.cal_conn_info); + cal_connection_info* ci = reinterpret_cast(get_fe_conn_info_ptr()); cal_table_info ti = ci->tableMap[table]; @@ -4947,10 +4941,10 @@ int ha_calpont_impl_external_lock(THD* thd, TABLE* table, int lock_type) if ( thd->infinidb_vtable.vtable_state == THD::INFINIDB_INIT ) return 0; - if (!thd->infinidb_vtable.cal_conn_info) - thd->infinidb_vtable.cal_conn_info = (void*)(new cal_connection_info()); + if (get_fe_conn_info_ptr() == NULL) + set_fe_conn_info_ptr((void*)new cal_connection_info()); - cal_connection_info* ci = reinterpret_cast(thd->infinidb_vtable.cal_conn_info); + cal_connection_info* ci = reinterpret_cast(get_fe_conn_info_ptr()); if (thd->killed == KILL_QUERY || thd->killed == KILL_QUERY_HARD) { @@ -5112,10 +5106,10 @@ int ha_calpont_impl_group_by_init(ha_calpont_group_by_handler* group_hand, TABLE boost::shared_ptr csc = CalpontSystemCatalog::makeCalpontSystemCatalog(sessionID); csc->identity(CalpontSystemCatalog::FE); - if (!thd->infinidb_vtable.cal_conn_info) - thd->infinidb_vtable.cal_conn_info = (void*)(new cal_connection_info()); + if (get_fe_conn_info_ptr() == NULL) + set_fe_conn_info_ptr((void*)new cal_connection_info()); - cal_connection_info* ci = reinterpret_cast(thd->infinidb_vtable.cal_conn_info); + cal_connection_info* ci = reinterpret_cast(get_fe_conn_info_ptr()); idbassert(ci != 0); @@ -5614,10 +5608,10 @@ int ha_calpont_impl_group_by_next(ha_calpont_group_by_handler* group_hand, TABLE return ER_INTERNAL_ERROR; } */ - if (!thd->infinidb_vtable.cal_conn_info) - thd->infinidb_vtable.cal_conn_info = (void*)(new cal_connection_info()); + if (get_fe_conn_info_ptr() == NULL) + set_fe_conn_info_ptr((void*)new cal_connection_info()); - cal_connection_info* ci = reinterpret_cast(thd->infinidb_vtable.cal_conn_info); + cal_connection_info* ci = reinterpret_cast(get_fe_conn_info_ptr()); // @bug 3078 if (thd->killed == KILL_QUERY || thd->killed == KILL_QUERY_HARD) @@ -5718,8 +5712,8 @@ int ha_calpont_impl_group_by_end(ha_calpont_group_by_handler* group_hand, TABLE* thd->infinidb_vtable.isNewQuery = true; thd->infinidb_vtable.isUnion = false; - if (thd->infinidb_vtable.cal_conn_info) - ci = reinterpret_cast(thd->infinidb_vtable.cal_conn_info); + if (get_fe_conn_info_ptr() != NULL) + ci = reinterpret_cast(get_fe_conn_info_ptr()); // MCOL-1052 //if (thd->infinidb_vtable.vtable_state == THD::INFINIDB_ORDER_BY ) @@ -5756,8 +5750,8 @@ int ha_calpont_impl_group_by_end(ha_calpont_group_by_handler* group_hand, TABLE* if (!ci) { - thd->infinidb_vtable.cal_conn_info = (void*)(new cal_connection_info()); - ci = reinterpret_cast(thd->infinidb_vtable.cal_conn_info); + set_fe_conn_info_ptr((void*)new cal_connection_info()); + ci = reinterpret_cast(get_fe_conn_info_ptr()); } // @bug 3078. Also session limit variable works the same as ctrl+c diff --git a/dbcon/mysql/ha_calpont_impl.h b/dbcon/mysql/ha_calpont_impl.h index db465ea83..bdc0e0eef 100644 --- a/dbcon/mysql/ha_calpont_impl.h +++ b/dbcon/mysql/ha_calpont_impl.h @@ -27,7 +27,7 @@ extern int ha_calpont_impl_create(const char* name, TABLE* table_arg, HA_CREATE_ extern int ha_calpont_impl_delete_table(const char* name); extern int ha_calpont_impl_open(const char* name, int mode, uint32_t test_if_locked); extern int ha_calpont_impl_close(void); -extern int ha_calpont_impl_rnd_init(TABLE* table, mcs_handler_info hndtl_ptr); +extern int ha_calpont_impl_rnd_init(TABLE* table); extern int ha_calpont_impl_rnd_next(uchar* buf, TABLE* table); extern int ha_calpont_impl_rnd_end(TABLE* table); extern int ha_calpont_impl_write_row(uchar* buf, TABLE* table); diff --git a/dbcon/mysql/ha_pseudocolumn.cpp b/dbcon/mysql/ha_pseudocolumn.cpp index 284130033..410b0615c 100644 --- a/dbcon/mysql/ha_pseudocolumn.cpp +++ b/dbcon/mysql/ha_pseudocolumn.cpp @@ -20,6 +20,7 @@ using namespace execplan; #include "functor_str.h" #include "ha_calpont_impl_if.h" +#include "mcs_sysvars.h" using namespace cal_impl_if; namespace @@ -54,10 +55,10 @@ int64_t idblocalpm() { THD* thd = current_thd; - if (!thd->infinidb_vtable.cal_conn_info) - thd->infinidb_vtable.cal_conn_info = (void*)(new cal_connection_info()); + if (get_fe_conn_info_ptr() == NULL) + set_fe_conn_info_ptr((void*)new cal_connection_info()); - cal_connection_info* ci = reinterpret_cast(thd->infinidb_vtable.cal_conn_info); + cal_connection_info* ci = reinterpret_cast(get_fe_conn_info_ptr()); if (ci->localPm == -1) { @@ -485,10 +486,10 @@ execplan::ReturnedColumn* buildPseudoColumn(Item* item, bool& nonSupport, uint32_t pseudoType) { - if (!(gwi.thd->infinidb_vtable.cal_conn_info)) - gwi.thd->infinidb_vtable.cal_conn_info = (void*)(new cal_connection_info()); + if (get_fe_conn_info_ptr() == NULL) + set_fe_conn_info_ptr((void*)new cal_connection_info()); - cal_connection_info* ci = reinterpret_cast(gwi.thd->infinidb_vtable.cal_conn_info); + cal_connection_info* ci = reinterpret_cast(get_fe_conn_info_ptr()); Item_func* ifp = (Item_func*)item; diff --git a/dbcon/mysql/ha_window_function.cpp b/dbcon/mysql/ha_window_function.cpp index f4a95bbc3..4033d022d 100644 --- a/dbcon/mysql/ha_window_function.cpp +++ b/dbcon/mysql/ha_window_function.cpp @@ -28,6 +28,7 @@ using namespace std; #include "idb_mysql.h" #include "ha_calpont_impl_if.h" +#include "mcs_sysvars.h" #include "arithmeticcolumn.h" #include "arithmeticoperator.h" @@ -93,10 +94,10 @@ WF_FRAME frame(Window_frame_bound::Bound_precedence_type bound, Item* offset) } 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()); + if (get_fe_conn_info_ptr() == NULL) + set_fe_conn_info_ptr((void*)new cal_connection_info()); - cal_connection_info* ci = reinterpret_cast(gwi.thd->infinidb_vtable.cal_conn_info); + cal_connection_info* ci = reinterpret_cast(get_fe_conn_info_ptr()); bool addOp = true; ReturnedColumn* rc = NULL; @@ -337,10 +338,10 @@ ReturnedColumn* buildWindowFunctionColumn(Item* item, gp_walk_info& gwi, bool& n //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()); + if (get_fe_conn_info_ptr() == NULL) + set_fe_conn_info_ptr((void*)new cal_connection_info()); - cal_connection_info* ci = reinterpret_cast(gwi.thd->infinidb_vtable.cal_conn_info); + cal_connection_info* ci = reinterpret_cast(get_fe_conn_info_ptr()); gwi.hasWindowFunc = true; Item_window_func* wf = (Item_window_func*)item; diff --git a/dbcon/mysql/mcs_sysvars.cpp b/dbcon/mysql/mcs_sysvars.cpp new file mode 100644 index 000000000..97069eb8e --- /dev/null +++ b/dbcon/mysql/mcs_sysvars.cpp @@ -0,0 +1,124 @@ +/* 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. */ + +#include +#include "idb_mysql.h" +#include "mcs_sysvars.h" + +const char* mcs_compression_type_names[] = { + "NO_COMPRESSION", + "SNAPPY", + NullS +}; + +static TYPELIB mcs_compression_type_names_lib = { + array_elements(mcs_compression_type_names) - 1, + "mcs_compression_type_names", + mcs_compression_type_names, + NULL +}; + +// compression type +static MYSQL_THDVAR_ENUM( + compression_type, + PLUGIN_VAR_RQCMDARG, + "Controls compression type for create tables. Possible values are: " + "NO_COMPRESSION segment files aren't compressed; " + "SNAPPY segment files are Snappy compressed (default);", + NULL, + NULL, + SNAPPY, + &mcs_compression_type_names_lib); + +// original query +static MYSQL_THDVAR_STR( + original_query, /* name */ + PLUGIN_VAR_MEMALLOC | + PLUGIN_VAR_RQCMDARG, + "Original query text", /* comment */ + NULL, /* check */ + NULL, /* update */ + NULL /* def */ +); + +// fe_conn_info pointer +static MYSQL_THDVAR_ULONGLONG( + fe_conn_info_ptr, + PLUGIN_VAR_NOSYSVAR | PLUGIN_VAR_NOCMDOPT, + "FrontEnd connection structure pointer. For internal usage.", + NULL, + NULL, + 0, + 0, + ~0U, + 1 +); + +st_mysql_sys_var* mcs_system_variables[] = +{ + MYSQL_SYSVAR(compression_type), + MYSQL_SYSVAR(original_query), + MYSQL_SYSVAR(fe_conn_info_ptr), + NULL +}; + +const char* get_original_query(THD* thd) { + return THDVAR(thd, original_query); +} + +void set_original_query(THD* thd, char* query) { + THDVAR(thd, original_query) = query; +} + +void* get_fe_conn_info_ptr() +{ + return ( current_thd == NULL ) ? NULL : + (void*)THDVAR(current_thd, fe_conn_info_ptr); +} + +void set_fe_conn_info_ptr(void* ptr) +{ + if ( current_thd == NULL ) + { + return; + } + + THDVAR(current_thd, fe_conn_info_ptr) = (uint64_t)(ptr); +} + +/*ha_calpont* get_legacy_handler(mcs_handler_info mcs_hndl_ptr) +{ + //MCOL-1101 Add handler type check + //hndl_ptr.hndl_type == LEGACY ) + ha_calpont* hndl; + if ( mcs_hndl_ptr.hndl_ptr != NULL ) + { + hndl = (ha_calpont*)(mcs_hndl_ptr.hndl_ptr); + } + else + { + hndl = new ha_calpont(); + hndl->fe_conn_info = (void*)THDVAR(current_thd, fe_conn_info_ptr); + } + + return hndl; +}*/ + +mcs_compression_type_t get_compression_type(THD* thd) { + return (mcs_compression_type_t) THDVAR(thd, compression_type); +} diff --git a/dbcon/mysql/mcs_sysvars.h b/dbcon/mysql/mcs_sysvars.h new file mode 100644 index 000000000..c1506615b --- /dev/null +++ b/dbcon/mysql/mcs_sysvars.h @@ -0,0 +1,58 @@ +/* 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. */ + +#ifndef MCS_SYSVARS_H__ +#define MCS_SYSVARS_H__ + +#include +#include "idb_mysql.h" + +extern st_mysql_sys_var* mcs_system_variables[]; + +enum mcs_handler_types_t +{ + SELECT, + GROUP_BY, + LEGACY +}; + +struct mcs_handler_info +{ + mcs_handler_info() : hndl_ptr(NULL), hndl_type(LEGACY) { }; + mcs_handler_info(mcs_handler_types_t type) : hndl_ptr(NULL), hndl_type(type) { }; + mcs_handler_info(void* ptr, mcs_handler_types_t type) : hndl_ptr(ptr), hndl_type(type) { }; + ~mcs_handler_info() { }; + void* hndl_ptr; + mcs_handler_types_t hndl_type; +}; + +// compression_type +enum mcs_compression_type_t { + NO_COMPRESSION = 0, + SNAPPY = 2 +}; + +// simple setters/getters +const char* get_original_query(THD* thd); +void set_original_query(THD* thd, char* query); +mcs_compression_type_t get_compression_type(THD* thd); + +void* get_fe_conn_info_ptr(); +void set_fe_conn_info_ptr(void* ptr); + +#endif From d22183e195f9043b7f1944f398bc25e09f46db97 Mon Sep 17 00:00:00 2001 From: Roman Nozdrin Date: Tue, 1 Jan 2019 12:33:47 +0300 Subject: [PATCH 11/38] MCOL-1101 Move client UDFs into a separate file. Remove rmParms from ha_calpont_impl.cpp --- dbcon/mysql/CMakeLists.txt | 1 + dbcon/mysql/ha_calpont.cpp | 91 ---- dbcon/mysql/ha_calpont_impl.cpp | 841 +---------------------------- dbcon/mysql/ha_calpont_impl_if.h | 2 + dbcon/mysql/mcs_client_udfs.cpp | 871 +++++++++++++++++++++++++++++++ dbcon/mysql/mcs_sysvars.cpp | 18 - dbcon/mysql/mcs_sysvars.h | 3 +- 7 files changed, 882 insertions(+), 945 deletions(-) create mode 100644 dbcon/mysql/mcs_client_udfs.cpp diff --git a/dbcon/mysql/CMakeLists.txt b/dbcon/mysql/CMakeLists.txt index b9af67200..7744e50b4 100644 --- a/dbcon/mysql/CMakeLists.txt +++ b/dbcon/mysql/CMakeLists.txt @@ -5,6 +5,7 @@ include_directories( ${ENGINE_COMMON_INCLUDES} SET ( libcalmysql_SRCS mcs_sysvars.cpp + mcs_client_udfs.cpp ha_calpont.cpp ha_calpont_impl.cpp ha_calpont_dml.cpp diff --git a/dbcon/mysql/ha_calpont.cpp b/dbcon/mysql/ha_calpont.cpp index 5caf10997..3c15dd146 100644 --- a/dbcon/mysql/ha_calpont.cpp +++ b/dbcon/mysql/ha_calpont.cpp @@ -16,97 +16,6 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ -// $Id: ha_calpont.cpp 9642 2013-06-24 14:57:42Z rdempsey $ - -/* Copyright (C) 2003 MySQL AB - - 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ - -/** - @file ha_example.cc - - @brief - The ha_example engine is a stubbed storage engine for example purposes only; - it does nothing at this point. Its purpose is to provide a source - code illustration of how to begin writing new storage engines; see also - /storage/example/ha_example.h. - - @details - ha_example will let you create/open/delete tables, but - nothing further (for example, indexes are not supported nor can data - be stored in the table). Use this example as a template for - implementing the same functionality in your own storage engine. You - can enable the example storage engine in your build by doing the - following during your build process:
./configure - --with-example-storage-engine - - Once this is done, MySQL will let you create tables with:
- CREATE TABLE (...) ENGINE=EXAMPLE; - - The example storage engine is set up to use table locks. It - implements an example "SHARE" that is inserted into a hash by table - name. You can use this to store information of state that any - example handler object will be able to see when it is using that - table. - - Please read the object definition in ha_example.h before reading the rest - of this file. - - @note - When you create an EXAMPLE table, the MySQL Server creates a table .frm - (format) file in the database directory, using the table name as the file - name as is customary with MySQL. No other files are created. To get an idea - of what occurs, here is an example select that would do a scan of an entire - table: - - @code - ha_example::store_lock - ha_example::external_lock - ha_example::info - ha_example::rnd_init - ha_example::extra - ENUM HA_EXTRA_CACHE Cache record in HA_rrnd() - ha_example::rnd_next - ha_example::rnd_next - ha_example::rnd_next - ha_example::rnd_next - ha_example::rnd_next - ha_example::rnd_next - ha_example::rnd_next - ha_example::rnd_next - ha_example::rnd_next - ha_example::extra - ENUM HA_EXTRA_NO_CACHE End caching of records (def) - ha_example::external_lock - ha_example::extra - ENUM HA_EXTRA_RESET Reset database to after open - @endcode - - Here you see that the example storage engine has 9 rows called before - rnd_next signals that it has reached the end of its data. Also note that - the table in question was already opened; had it not been open, a call to - ha_example::open() would also have been necessary. Calls to - ha_example::extra() are hints as to what will be occuring to the request. - - A Longer Example can be found called the "Skeleton Engine" which can be - found on TangentOrg. It has both an engine and a full build environment - for building a pluggable storage engine. - - Happy coding!
- -Brian -*/ - #include "ha_calpont.h" #include "columnstoreversion.h" diff --git a/dbcon/mysql/ha_calpont_impl.cpp b/dbcon/mysql/ha_calpont_impl.cpp index c15b0f98b..43a2fd628 100644 --- a/dbcon/mysql/ha_calpont_impl.cpp +++ b/dbcon/mysql/ha_calpont_impl.cpp @@ -169,7 +169,7 @@ static const string interval_names[] = const unsigned NONSUPPORTED_ERR_THRESH = 2000; //TODO: make this session-safe (put in connMap?) -vector rmParms; +//vector rmParms; ResourceManager* rm = ResourceManager::instance(); bool useHdfs = rm->useHdfs(); @@ -1593,7 +1593,7 @@ uint32_t doUpdateDelete(THD* thd) ByteStream bytestream, bytestream1; bytestream << sessionID; boost::shared_ptr plan = pDMLPackage->get_ExecutionPlan(); - updateCP->rmParms(rmParms); + updateCP->rmParms(ci->rmParms); updateCP->serialize(*plan); // recover original vtable state thd->infinidb_vtable.vtable_state = origState; @@ -1908,835 +1908,6 @@ uint32_t doUpdateDelete(THD* thd) } //anon namespace -extern "C" -{ -#ifdef _MSC_VER - __declspec(dllexport) -#endif - const char* calgetstats(UDF_INIT* initid, UDF_ARGS* args, - char* result, unsigned long* length, - char* is_null, char* error) - { - THD* thd = current_thd; - - if (get_fe_conn_info_ptr() == NULL) - set_fe_conn_info_ptr((void*)new cal_connection_info()); - - cal_connection_info* ci = reinterpret_cast(get_fe_conn_info_ptr()); - - unsigned long l = ci->queryStats.size(); - - if (l == 0) - { - *is_null = 1; - return 0; - } - - if (l > 255) l = 255; - - memcpy(result, ci->queryStats.c_str(), l); - *length = l; - return result; - } - -#ifdef _MSC_VER - __declspec(dllexport) -#endif - my_bool calgetstats_init(UDF_INIT* initid, UDF_ARGS* args, char* message) - { - if (args->arg_count != 0) - { - strcpy(message, "CALGETSTATS() takes no arguments"); - return 1; - } - - initid->maybe_null = 1; - initid->max_length = 255; - - return 0; - } - -#ifdef _MSC_VER - __declspec(dllexport) -#endif - void calgetstats_deinit(UDF_INIT* initid) - { - } - -#ifdef _MSC_VER - __declspec(dllexport) -#endif - long long calsettrace(UDF_INIT* initid, UDF_ARGS* args, - char* is_null, char* error) - { - THD* thd = current_thd; - - if (get_fe_conn_info_ptr() == NULL) - set_fe_conn_info_ptr((void*)new cal_connection_info()); - - cal_connection_info* ci = reinterpret_cast(get_fe_conn_info_ptr()); - - long long oldTrace = ci->traceFlags; - ci->traceFlags = (uint32_t)(*((long long*)args->args[0])); - // keep the vtablemode bit - ci->traceFlags |= (oldTrace & CalpontSelectExecutionPlan::TRACE_TUPLE_OFF); - ci->traceFlags |= (oldTrace & CalpontSelectExecutionPlan::TRACE_TUPLE_AUTOSWITCH); - return oldTrace; - } - -#ifdef _MSC_VER - __declspec(dllexport) -#endif - my_bool calsettrace_init(UDF_INIT* initid, UDF_ARGS* args, char* message) - { - if (args->arg_count != 1 || args->arg_type[0] != INT_RESULT) - { - strcpy(message, "CALSETTRACE() requires one INTEGER argument"); - return 1; - } - - return 0; - } - -#ifdef _MSC_VER - __declspec(dllexport) -#endif - void calsettrace_deinit(UDF_INIT* initid) - { - } - -#ifdef _MSC_VER - __declspec(dllexport) -#endif -// Return 1 if system is ready for reads or 0 if not. - long long mcssystemready(UDF_INIT* initid, UDF_ARGS* args, - char* is_null, char* error) - { - long long rtn = 0; - Oam oam; - DBRM dbrm(true); - SystemStatus systemstatus; - - try - { - oam.getSystemStatus(systemstatus); - - if (systemstatus.SystemOpState == ACTIVE - && dbrm.getSystemReady() - && dbrm.getSystemQueryReady()) - { - return 1; - } - } - catch (...) - { - *error = 1; - } - - return rtn; - } - -#ifdef _MSC_VER - __declspec(dllexport) -#endif - my_bool mcssystemready_init(UDF_INIT* initid, UDF_ARGS* args, char* message) - { - return 0; - } - -#ifdef _MSC_VER - __declspec(dllexport) -#endif - void mcssystemready_deinit(UDF_INIT* initid) - { - } - -#ifdef _MSC_VER - __declspec(dllexport) -#endif -// Return non-zero if system is read only; 0 if writeable - long long mcssystemreadonly(UDF_INIT* initid, UDF_ARGS* args, - char* is_null, char* error) - { - long long rtn = 0; - DBRM dbrm(true); - - try - { - if (dbrm.getSystemSuspended()) - { - rtn = 1; - } - - if (dbrm.isReadWrite() > 0) // Returns 0 for writable, 5 for read only - { - rtn = 2; - } - } - catch (...) - { - *error = 1; - rtn = 1; - } - - return rtn; - } - -#ifdef _MSC_VER - __declspec(dllexport) -#endif - my_bool mcssystemreadonly_init(UDF_INIT* initid, UDF_ARGS* args, char* message) - { - return 0; - } - -#ifdef _MSC_VER - __declspec(dllexport) -#endif - void mcssystemreadonly_deinit(UDF_INIT* initid) - { - } - -#ifdef _MSC_VER - __declspec(dllexport) -#endif -// Return non-zero if this is the primary UM; 0 if not primary - long long mcssystemprimary(UDF_INIT* initid, UDF_ARGS* args, - char* is_null, char* error) - { - long long rtn = 0; - Oam oam; - string PrimaryUMModuleName; - string localModule; - oamModuleInfo_t st; - - try - { - st = oam.getModuleInfo(); - localModule = boost::get<0>(st); - PrimaryUMModuleName = config::Config::makeConfig()->getConfig("SystemConfig", "PrimaryUMModuleName"); - - if (boost::iequals(localModule, PrimaryUMModuleName)) - rtn = 1; - if (PrimaryUMModuleName == "unassigned") - rtn = 1; - } - catch (runtime_error& e) - { - // It's difficult to return an error message from a numerical UDF - //string msg = string("ERROR: Problem getting Primary UM Module Name. ") + e.what(); - *error = 1; - } - catch (...) - { - *error = 1; - } - return rtn; - } - -#ifdef _MSC_VER - __declspec(dllexport) -#endif - my_bool mcssystemprimary_init(UDF_INIT* initid, UDF_ARGS* args, char* message) - { - return 0; - } - -#ifdef _MSC_VER - __declspec(dllexport) -#endif - void mcssystemprimary_deinit(UDF_INIT* initid) - { - } - -#define MAXSTRINGLENGTH 50 - - const char* PmSmallSideMaxMemory = "pmmaxmemorysmallside"; - - const char* SetParmsPrelude = "Updated "; - const char* SetParmsError = "Invalid parameter: "; - const char* InvalidParmSize = "Invalid parameter size: Input value cannot be larger than "; - - const size_t Plen = strlen(SetParmsPrelude); - const size_t Elen = strlen(SetParmsError); - - const char* invalidParmSizeMessage(uint64_t size, size_t& len) - { - static char str[sizeof(InvalidParmSize) + 12] = {0}; - ostringstream os; - os << InvalidParmSize << size; - len = os.str().length(); - strcpy(str, os.str().c_str()); - return str; - } - -#ifdef _MSC_VER - __declspec(dllexport) -#endif - const char* calsetparms(UDF_INIT* initid, UDF_ARGS* args, - char* result, unsigned long* length, - char* is_null, char* error) - { - char parameter[MAXSTRINGLENGTH]; - char valuestr[MAXSTRINGLENGTH]; - size_t plen = args->lengths[0]; - size_t vlen = args->lengths[1]; - - memcpy(parameter, args->args[0], plen); - memcpy(valuestr, args->args[1], vlen); - - parameter[plen] = '\0'; - valuestr[vlen] = '\0'; - - uint64_t value = Config::uFromText(valuestr); - - THD* thd = current_thd; - uint32_t sessionID = tid2sid(thd->thread_id); - - const char* msg = SetParmsError; - size_t mlen = Elen; - bool includeInput = true; - - string pstr(parameter); - boost::algorithm::to_lower(pstr); - - if (pstr == PmSmallSideMaxMemory) - { - joblist::ResourceManager* rm = joblist::ResourceManager::instance(); - - if (rm->getHjTotalUmMaxMemorySmallSide() >= value) - { - rmParms.push_back(RMParam(sessionID, execplan::PMSMALLSIDEMEMORY, value)); - - msg = SetParmsPrelude; - mlen = Plen; - } - else - { - msg = invalidParmSizeMessage(rm->getHjTotalUmMaxMemorySmallSide(), mlen); - includeInput = false; - } - } - - memcpy(result, msg, mlen); - - if (includeInput) - { - memcpy(result + mlen, parameter, plen); - mlen += plen; - memcpy(result + mlen++, " ", 1); - memcpy(result + mlen, valuestr, vlen); - *length = mlen + vlen; - } - else - *length = mlen; - - return result; - } - -#ifdef _MSC_VER - __declspec(dllexport) -#endif - my_bool calsetparms_init(UDF_INIT* initid, UDF_ARGS* args, char* message) - { - if (args->arg_count != 2 || args->arg_type[0] != STRING_RESULT || args->arg_type[1] != STRING_RESULT) - { - strcpy(message, "CALSETPARMS() requires two string arguments"); - return 1; - } - - initid->max_length = MAXSTRINGLENGTH; - - char valuestr[MAXSTRINGLENGTH]; - size_t vlen = args->lengths[1]; - - memcpy(valuestr, args->args[1], vlen--); - - for (size_t i = 0; i < vlen; ++i) - if (!isdigit(valuestr[i])) - { - strcpy(message, "CALSETPARMS() second argument must be numeric or end in G, M or K"); - return 1; - } - - if (!isdigit(valuestr[vlen])) - { - switch (valuestr[vlen]) - { - case 'G': - case 'g': - case 'M': - case 'm': - case 'K': - case 'k': - case '\0': - break; - - default: - strcpy(message, "CALSETPARMS() second argument must be numeric or end in G, M or K"); - return 1; - } - } - - return 0; - } - -#ifdef _MSC_VER - __declspec(dllexport) -#endif - void calsetparms_deinit(UDF_INIT* initid) - { - } - -#ifdef _MSC_VER - __declspec(dllexport) -#endif - my_bool calviewtablelock_init(UDF_INIT* initid, UDF_ARGS* args, char* message) - { - if (args->arg_count == 2 && (args->arg_type[0] != STRING_RESULT || args->arg_type[1] != STRING_RESULT)) - { - strcpy(message, "CALVIEWTABLELOCK() requires two string arguments"); - return 1; - } - else if ((args->arg_count == 1) && (args->arg_type[0] != STRING_RESULT ) ) - { - strcpy(message, "CALVIEWTABLELOCK() requires one string argument"); - return 1; - } - else if (args->arg_count > 2 ) - { - strcpy(message, "CALVIEWTABLELOCK() takes one or two arguments only"); - return 1; - } - else if (args->arg_count == 0 ) - { - strcpy(message, "CALVIEWTABLELOCK() requires at least one argument"); - return 1; - } - - initid->maybe_null = 1; - initid->max_length = 255; - - return 0; - } - -#ifdef _MSC_VER - __declspec(dllexport) -#endif - const char* calviewtablelock(UDF_INIT* initid, UDF_ARGS* args, - char* result, unsigned long* length, - char* is_null, char* error) - { - THD* thd = current_thd; - - if (get_fe_conn_info_ptr() == NULL) - set_fe_conn_info_ptr((void*)new cal_connection_info()); - - cal_connection_info* ci = reinterpret_cast(get_fe_conn_info_ptr()); - CalpontSystemCatalog::TableName tableName; - - if ( args->arg_count == 2 ) - { - tableName.schema = args->args[0]; - tableName.table = args->args[1]; - } - else if ( args->arg_count == 1 ) - { - tableName.table = args->args[0]; - - if (thd->db.length) - tableName.schema = thd->db.str; - else - { - string msg("No schema information provided"); - memcpy(result, msg.c_str(), msg.length()); - *length = msg.length(); - return result; - } - } - - if ( !ci->dmlProc ) - { - ci->dmlProc = new MessageQueueClient("DMLProc"); - //cout << "viewtablelock starts a new client " << ci->dmlProc << " for session " << thd->thread_id << endl; - } - - string lockinfo = ha_calpont_impl_viewtablelock(*ci, tableName); - - memcpy(result, lockinfo.c_str(), lockinfo.length()); - *length = lockinfo.length(); - return result; - } - -#ifdef _MSC_VER - __declspec(dllexport) -#endif - void calviewtablelock_deinit(UDF_INIT* initid) - { - } - -#ifdef _MSC_VER - __declspec(dllexport) -#endif - my_bool calcleartablelock_init(UDF_INIT* initid, UDF_ARGS* args, char* message) - { - if ((args->arg_count != 1) || (args->arg_type[0] != INT_RESULT)) - { - strcpy(message, - "CALCLEARTABLELOCK() requires one integer argument (the lockID)"); - return 1; - } - - initid->maybe_null = 1; - initid->max_length = 255; - - return 0; - } - -#ifdef _MSC_VER - __declspec(dllexport) -#endif - const char* calcleartablelock(UDF_INIT* initid, UDF_ARGS* args, - char* result, unsigned long* length, - char* is_null, char* error) - { - THD* thd = current_thd; - - if (get_fe_conn_info_ptr() == NULL) - set_fe_conn_info_ptr((void*)new cal_connection_info()); - - cal_connection_info* ci = reinterpret_cast( - get_fe_conn_info_ptr()); - long long lockID = *reinterpret_cast(args->args[0]); - - if ( !ci->dmlProc ) - { - ci->dmlProc = new MessageQueueClient("DMLProc"); - //cout << "cleartablelock starts a new client " << ci->dmlProc << " for session " << thd->thread_id << endl; - } - - unsigned long long uLockID = lockID; - string lockinfo = ha_calpont_impl_cleartablelock(*ci, uLockID); - - memcpy(result, lockinfo.c_str(), lockinfo.length()); - *length = lockinfo.length(); - return result; - } - -#ifdef _MSC_VER - __declspec(dllexport) -#endif - void calcleartablelock_deinit(UDF_INIT* initid) - { - } - -#ifdef _MSC_VER - __declspec(dllexport) -#endif - my_bool callastinsertid_init(UDF_INIT* initid, UDF_ARGS* args, char* message) - { - if (args->arg_count == 2 && (args->arg_type[0] != STRING_RESULT || args->arg_type[1] != STRING_RESULT)) - { - strcpy(message, "CALLASTINSRTID() requires two string arguments"); - return 1; - } - else if ((args->arg_count == 1) && (args->arg_type[0] != STRING_RESULT ) ) - { - strcpy(message, "CALLASTINSERTID() requires one string argument"); - return 1; - } - else if (args->arg_count > 2 ) - { - strcpy(message, "CALLASTINSERTID() takes one or two arguments only"); - return 1; - } - else if (args->arg_count == 0 ) - { - strcpy(message, "CALLASTINSERTID() requires at least one argument"); - return 1; - } - - initid->maybe_null = 1; - initid->max_length = 255; - - return 0; - } - -#ifdef _MSC_VER - __declspec(dllexport) -#endif - long long callastinsertid(UDF_INIT* initid, UDF_ARGS* args, - char* is_null, char* error) - { - THD* thd = current_thd; - - CalpontSystemCatalog::TableName tableName; - uint64_t nextVal = 0; - - if ( args->arg_count == 2 ) - { - tableName.schema = args->args[0]; - tableName.table = args->args[1]; - } - else if ( args->arg_count == 1 ) - { - tableName.table = args->args[0]; - - if (thd->db.length) - tableName.schema = thd->db.str; - else - { - return -1; - } - } - - boost::shared_ptr csc = CalpontSystemCatalog::makeCalpontSystemCatalog(tid2sid(thd->thread_id)); - csc->identity(execplan::CalpontSystemCatalog::FE); - - try - { - nextVal = csc->nextAutoIncrValue(tableName); - } - catch (std::exception&) - { - string msg("No such table found during autincrement"); - setError(thd, ER_INTERNAL_ERROR, msg); - return nextVal; - } - - if (nextVal == AUTOINCR_SATURATED) - { - setError(thd, ER_INTERNAL_ERROR, IDBErrorInfo::instance()->errorMsg(ERR_EXCEED_LIMIT)); - return nextVal; - } - - //@Bug 3559. Return a message for table without autoincrement column. - if (nextVal == 0) - { - string msg("Autoincrement does not exist for this table."); - setError(thd, ER_INTERNAL_ERROR, msg); - return nextVal; - } - - return (nextVal - 1); - } - -#ifdef _MSC_VER - __declspec(dllexport) -#endif - void callastinsertid_deinit(UDF_INIT* initid) - { - } - -#ifdef _MSC_VER - __declspec(dllexport) -#endif - void calflushcache_deinit(UDF_INIT* initid) - { - } - -#ifdef _MSC_VER - __declspec(dllexport) -#endif - long long calflushcache(UDF_INIT* initid, UDF_ARGS* args, - char* is_null, char* error) - { - return static_cast(cacheutils::flushPrimProcCache()); - } - -#ifdef _MSC_VER - __declspec(dllexport) -#endif - my_bool calflushcache_init(UDF_INIT* initid, UDF_ARGS* args, char* message) - { - if (args->arg_count != 0) - { - strcpy(message, "CALFLUSHCACHE() takes no arguments"); - return 1; - } - - return 0; - } - - static const unsigned long TraceSize = 16 * 1024; - -//mysqld will call this with only 766 bytes available in result no matter what we asked for in calgettrace_init() -// if we return a pointer that is not result, mysqld will take our pointer and use it, freeing up result -#ifdef _MSC_VER - __declspec(dllexport) -#endif - const char* calgettrace(UDF_INIT* initid, UDF_ARGS* args, - char* result, unsigned long* length, - char* is_null, char* error) - { - THD* thd = current_thd; - const string* msgp; - int flags = 0; - - if (args->arg_count > 0) - { - if (args->arg_type[0] == INT_RESULT) - { - flags = *reinterpret_cast(args->args[0]); - } - } - - if (get_fe_conn_info_ptr() == NULL) - set_fe_conn_info_ptr((void*)new cal_connection_info()); - - cal_connection_info* ci = reinterpret_cast(get_fe_conn_info_ptr()); - - if (flags > 0) - //msgp = &connMap[sessionID].extendedStats; - msgp = &ci->extendedStats; - else - //msgp = &connMap[sessionID].miniStats; - msgp = &ci->miniStats; - - unsigned long l = msgp->size(); - - if (l == 0) - { - *is_null = 1; - return 0; - } - - if (l > TraceSize) l = TraceSize; - - *length = l; - return msgp->c_str(); - } - -#ifdef _MSC_VER - __declspec(dllexport) -#endif - my_bool calgettrace_init(UDF_INIT* initid, UDF_ARGS* args, char* message) - { -#if 0 - - if (args->arg_count != 0) - { - strcpy(message, "CALGETTRACE() takes no arguments"); - return 1; - } - -#endif - initid->maybe_null = 1; - initid->max_length = TraceSize; - - return 0; - } - -#ifdef _MSC_VER - __declspec(dllexport) -#endif - void calgettrace_deinit(UDF_INIT* initid) - { - } - -#ifdef _MSC_VER - __declspec(dllexport) -#endif - const char* calgetversion(UDF_INIT* initid, UDF_ARGS* args, - char* result, unsigned long* length, - char* is_null, char* error) - { - string version(columnstore_version); - *length = version.size(); - memcpy(result, version.c_str(), *length); - return result; - } - -#ifdef _MSC_VER - __declspec(dllexport) -#endif - my_bool calgetversion_init(UDF_INIT* initid, UDF_ARGS* args, char* message) - { - if (args->arg_count != 0) - { - strcpy(message, "CALGETVERSION() takes no arguments"); - return 1; - } - - return 0; - } - -#ifdef _MSC_VER - __declspec(dllexport) -#endif - void calgetversion_deinit(UDF_INIT* initid) - { - } - -#ifdef _MSC_VER - __declspec(dllexport) -#endif - const char* calgetsqlcount(UDF_INIT* initid, UDF_ARGS* args, - char* result, unsigned long* length, - char* is_null, char* error) - { - THD* thd = current_thd; - - if (get_fe_conn_info_ptr() == NULL) - set_fe_conn_info_ptr((void*)new cal_connection_info()); - - cal_connection_info* ci = reinterpret_cast(get_fe_conn_info_ptr()); - idbassert(ci != 0); - - MessageQueueClient* mqc = 0; - mqc = new MessageQueueClient("ExeMgr1"); - - ByteStream msg; - ByteStream::quadbyte runningSql, waitingSql; - ByteStream::quadbyte qb = 5; - msg << qb; - mqc->write(msg); - - //get ExeMgr response - msg.restart(); - msg = mqc->read(); - - if (msg.length() == 0) - { - memcpy(result, "Lost connection to ExeMgr", *length); - return result; - } - - msg >> runningSql; - msg >> waitingSql; - delete mqc; - - char ans[128]; - sprintf(ans, "Running SQL statements %d, Waiting SQL statments %d", runningSql, waitingSql); - *length = strlen(ans); - memcpy(result, ans, *length); - return result; - } - -#ifdef _MSC_VER - __declspec(dllexport) -#endif - my_bool calgetsqlcount_init(UDF_INIT* initid, UDF_ARGS* args, char* message) - { - if (args->arg_count != 0) - { - strcpy(message, "CALGETSQLCOUNT() takes no arguments"); - return 1; - } - - return 0; - } - -#ifdef _MSC_VER - __declspec(dllexport) -#endif - void calgetsqlcount_deinit(UDF_INIT* initid) - { - } - - -} //extern "C" - int ha_calpont_impl_open(const char* name, int mode, uint32_t test_if_locked) { IDEBUG ( cout << "ha_calpont_impl_open: " << name << ", " << mode << ", " << test_if_locked << endl ); @@ -3147,7 +2318,7 @@ int ha_calpont_impl_rnd_init(TABLE* table) msg << qb; hndl->exeMgr->write(msg); msg.restart(); - csep->rmParms(rmParms); + csep->rmParms(ci->rmParms); //send plan csep->serialize(msg); @@ -3207,7 +2378,7 @@ int ha_calpont_impl_rnd_init(TABLE* table) return ER_INTERNAL_ERROR; } - rmParms.clear(); + ci->rmParms.clear(); if (thd->infinidb_vtable.vtable_state == THD::INFINIDB_DISABLE_VTABLE) { @@ -5330,7 +4501,7 @@ int ha_calpont_impl_group_by_init(ha_calpont_group_by_handler* group_hand, TABLE msg << qb; hndl->exeMgr->write(msg); msg.restart(); - csep->rmParms(rmParms); + csep->rmParms(ci->rmParms); //send plan csep->serialize(msg); @@ -5390,7 +4561,7 @@ int ha_calpont_impl_group_by_init(ha_calpont_group_by_handler* group_hand, TABLE return ER_INTERNAL_ERROR; } - rmParms.clear(); + ci->rmParms.clear(); if (thd->infinidb_vtable.vtable_state == THD::INFINIDB_DISABLE_VTABLE) { diff --git a/dbcon/mysql/ha_calpont_impl_if.h b/dbcon/mysql/ha_calpont_impl_if.h index 23d9461c2..4f0046745 100644 --- a/dbcon/mysql/ha_calpont_impl_if.h +++ b/dbcon/mysql/ha_calpont_impl_if.h @@ -319,6 +319,8 @@ struct cal_connection_info char delimiter; char enclosed_by; std::vector columnTypes; + // MCOL-1101 remove compilation unit variable rmParms + std::vector rmParms; }; const std::string infinidb_err_msg = "\nThe query includes syntax that is not supported by MariaDB Columnstore. Use 'show warnings;' to get more information. Review the MariaDB Columnstore Syntax guide for additional information on supported distributed syntax or consider changing the MariaDB Columnstore Operating Mode (infinidb_vtable_mode)."; diff --git a/dbcon/mysql/mcs_client_udfs.cpp b/dbcon/mysql/mcs_client_udfs.cpp new file mode 100644 index 000000000..605e62392 --- /dev/null +++ b/dbcon/mysql/mcs_client_udfs.cpp @@ -0,0 +1,871 @@ +/* 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. */ + +#define NEED_CALPONT_INTERFACE +#include "ha_calpont_impl.h" + +#include "ha_calpont_impl_if.h" +using namespace cal_impl_if; + +#include "configcpp.h" +using namespace config; +#include "brmtypes.h" +using namespace BRM; +#include "bytestream.h" +using namespace messageqcpp; +#include "liboamcpp.h" +using namespace oam; +#include "cacheutils.h" + +#include "errorcodes.h" +#include "idberrorinfo.h" +#include "errorids.h" +using namespace logging; + +//#include "resourcemanager.h" + +#include "columnstoreversion.h" +#include "mcs_sysvars.h" + +extern "C" +{ +#define MAXSTRINGLENGTH 50 + + const char* PmSmallSideMaxMemory = "pmmaxmemorysmallside"; + + const char* SetParmsPrelude = "Updated "; + const char* SetParmsError = "Invalid parameter: "; + const char* InvalidParmSize = "Invalid parameter size: Input value cannot be larger than "; + + const size_t Plen = strlen(SetParmsPrelude); + const size_t Elen = strlen(SetParmsError); + + const char* invalidParmSizeMessage(uint64_t size, size_t& len) + { + static char str[sizeof(InvalidParmSize) + 12] = {0}; + ostringstream os; + os << InvalidParmSize << size; + len = os.str().length(); + strcpy(str, os.str().c_str()); + return str; + } + +#ifdef _MSC_VER + __declspec(dllexport) +#endif + const char* calsetparms(UDF_INIT* initid, UDF_ARGS* args, + char* result, unsigned long* length, + char* is_null, char* error) + { + char parameter[MAXSTRINGLENGTH]; + char valuestr[MAXSTRINGLENGTH]; + size_t plen = args->lengths[0]; + size_t vlen = args->lengths[1]; + + memcpy(parameter, args->args[0], plen); + memcpy(valuestr, args->args[1], vlen); + + parameter[plen] = '\0'; + valuestr[vlen] = '\0'; + + uint64_t value = Config::uFromText(valuestr); + + THD* thd = current_thd; + uint32_t sessionID = CalpontSystemCatalog::idb_tid2sid(thd->thread_id); + + const char* msg = SetParmsError; + size_t mlen = Elen; + bool includeInput = true; + + string pstr(parameter); + boost::algorithm::to_lower(pstr); + + if (get_fe_conn_info_ptr() == NULL) + set_fe_conn_info_ptr((void*)new cal_connection_info()); + + cal_connection_info* ci = reinterpret_cast(get_fe_conn_info_ptr()); + idbassert(ci != 0); + + if (pstr == PmSmallSideMaxMemory) + { + joblist::ResourceManager* rm = joblist::ResourceManager::instance(); + + if (rm->getHjTotalUmMaxMemorySmallSide() >= value) + { + ci->rmParms.push_back(RMParam(sessionID, execplan::PMSMALLSIDEMEMORY, value)); + + msg = SetParmsPrelude; + mlen = Plen; + } + else + { + msg = invalidParmSizeMessage(rm->getHjTotalUmMaxMemorySmallSide(), mlen); + includeInput = false; + } + } + + memcpy(result, msg, mlen); + + if (includeInput) + { + memcpy(result + mlen, parameter, plen); + mlen += plen; + memcpy(result + mlen++, " ", 1); + memcpy(result + mlen, valuestr, vlen); + *length = mlen + vlen; + } + else + *length = mlen; + + return result; + } + +#ifdef _MSC_VER + __declspec(dllexport) +#endif + my_bool calsetparms_init(UDF_INIT* initid, UDF_ARGS* args, char* message) + { + if (args->arg_count != 2 || args->arg_type[0] != STRING_RESULT || args->arg_type[1] != STRING_RESULT) + { + strcpy(message, "CALSETPARMS() requires two string arguments"); + return 1; + } + + initid->max_length = MAXSTRINGLENGTH; + + char valuestr[MAXSTRINGLENGTH]; + size_t vlen = args->lengths[1]; + + memcpy(valuestr, args->args[1], vlen--); + + for (size_t i = 0; i < vlen; ++i) + if (!isdigit(valuestr[i])) + { + strcpy(message, "CALSETPARMS() second argument must be numeric or end in G, M or K"); + return 1; + } + + if (!isdigit(valuestr[vlen])) + { + switch (valuestr[vlen]) + { + case 'G': + case 'g': + case 'M': + case 'm': + case 'K': + case 'k': + case '\0': + break; + + default: + strcpy(message, "CALSETPARMS() second argument must be numeric or end in G, M or K"); + return 1; + } + } + + return 0; + } + +#ifdef _MSC_VER + __declspec(dllexport) +#endif + void calsetparms_deinit(UDF_INIT* initid) + { + } + +#ifdef _MSC_VER + __declspec(dllexport) +#endif + const char* calgetstats(UDF_INIT* initid, UDF_ARGS* args, + char* result, unsigned long* length, + char* is_null, char* error) + { + if (get_fe_conn_info_ptr() == NULL) + set_fe_conn_info_ptr((void*)new cal_connection_info()); + + cal_connection_info* ci = reinterpret_cast(get_fe_conn_info_ptr()); + + unsigned long l = ci->queryStats.size(); + + if (l == 0) + { + *is_null = 1; + return 0; + } + + if (l > 255) l = 255; + + memcpy(result, ci->queryStats.c_str(), l); + *length = l; + return result; + } + +#ifdef _MSC_VER + __declspec(dllexport) +#endif + my_bool calgetstats_init(UDF_INIT* initid, UDF_ARGS* args, char* message) + { + if (args->arg_count != 0) + { + strcpy(message, "CALGETSTATS() takes no arguments"); + return 1; + } + + initid->maybe_null = 1; + initid->max_length = 255; + + return 0; + } + +#ifdef _MSC_VER + __declspec(dllexport) +#endif + void calgetstats_deinit(UDF_INIT* initid) + { + } + +#ifdef _MSC_VER + __declspec(dllexport) +#endif + long long calsettrace(UDF_INIT* initid, UDF_ARGS* args, + char* is_null, char* error) + { + if (get_fe_conn_info_ptr() == NULL) + set_fe_conn_info_ptr((void*)new cal_connection_info()); + + cal_connection_info* ci = reinterpret_cast(get_fe_conn_info_ptr()); + + long long oldTrace = ci->traceFlags; + ci->traceFlags = (uint32_t)(*((long long*)args->args[0])); + // keep the vtablemode bit + ci->traceFlags |= (oldTrace & CalpontSelectExecutionPlan::TRACE_TUPLE_OFF); + ci->traceFlags |= (oldTrace & CalpontSelectExecutionPlan::TRACE_TUPLE_AUTOSWITCH); + return oldTrace; + } + +#ifdef _MSC_VER + __declspec(dllexport) +#endif + my_bool calsettrace_init(UDF_INIT* initid, UDF_ARGS* args, char* message) + { + if (args->arg_count != 1 || args->arg_type[0] != INT_RESULT) + { + strcpy(message, "CALSETTRACE() requires one INTEGER argument"); + return 1; + } + + return 0; + } + +#ifdef _MSC_VER + __declspec(dllexport) +#endif + void calsettrace_deinit(UDF_INIT* initid) + { + } + +#ifdef _MSC_VER + __declspec(dllexport) +#endif +// Return 1 if system is ready for reads or 0 if not. + long long mcssystemready(UDF_INIT* initid, UDF_ARGS* args, + char* is_null, char* error) + { + long long rtn = 0; + Oam oam; + DBRM dbrm(true); + SystemStatus systemstatus; + + try + { + oam.getSystemStatus(systemstatus); + + if (systemstatus.SystemOpState == ACTIVE + && dbrm.getSystemReady() + && dbrm.getSystemQueryReady()) + { + return 1; + } + } + catch (...) + { + *error = 1; + } + + return rtn; + } + +#ifdef _MSC_VER + __declspec(dllexport) +#endif + my_bool mcssystemready_init(UDF_INIT* initid, UDF_ARGS* args, char* message) + { + return 0; + } + +#ifdef _MSC_VER + __declspec(dllexport) +#endif + void mcssystemready_deinit(UDF_INIT* initid) + { + } + +#ifdef _MSC_VER + __declspec(dllexport) +#endif +// Return non-zero if system is read only; 0 if writeable + long long mcssystemreadonly(UDF_INIT* initid, UDF_ARGS* args, + char* is_null, char* error) + { + long long rtn = 0; + DBRM dbrm(true); + + try + { + if (dbrm.getSystemSuspended()) + { + rtn = 1; + } + + if (dbrm.isReadWrite() > 0) // Returns 0 for writable, 5 for read only + { + rtn = 2; + } + } + catch (...) + { + *error = 1; + rtn = 1; + } + + return rtn; + } + +#ifdef _MSC_VER + __declspec(dllexport) +#endif + my_bool mcssystemreadonly_init(UDF_INIT* initid, UDF_ARGS* args, char* message) + { + return 0; + } + +#ifdef _MSC_VER + __declspec(dllexport) +#endif + void mcssystemreadonly_deinit(UDF_INIT* initid) + { + } + +#ifdef _MSC_VER + __declspec(dllexport) +#endif +// Return non-zero if this is the primary UM; 0 if not primary + long long mcssystemprimary(UDF_INIT* initid, UDF_ARGS* args, + char* is_null, char* error) + { + long long rtn = 0; + Oam oam; + string PrimaryUMModuleName; + string localModule; + oamModuleInfo_t st; + + try + { + st = oam.getModuleInfo(); + localModule = boost::get<0>(st); + PrimaryUMModuleName = config::Config::makeConfig()->getConfig("SystemConfig", "PrimaryUMModuleName"); + + if (boost::iequals(localModule, PrimaryUMModuleName)) + rtn = 1; + if (PrimaryUMModuleName == "unassigned") + rtn = 1; + } + catch (runtime_error& e) + { + // It's difficult to return an error message from a numerical UDF + //string msg = string("ERROR: Problem getting Primary UM Module Name. ") + e.what(); + *error = 1; + } + catch (...) + { + *error = 1; + } + return rtn; + } + +#ifdef _MSC_VER + __declspec(dllexport) +#endif + my_bool mcssystemprimary_init(UDF_INIT* initid, UDF_ARGS* args, char* message) + { + return 0; + } + +#ifdef _MSC_VER + __declspec(dllexport) +#endif + void mcssystemprimary_deinit(UDF_INIT* initid) + { + } + +#ifdef _MSC_VER + __declspec(dllexport) +#endif + my_bool calviewtablelock_init(UDF_INIT* initid, UDF_ARGS* args, char* message) + { + if (args->arg_count == 2 && (args->arg_type[0] != STRING_RESULT || args->arg_type[1] != STRING_RESULT)) + { + strcpy(message, "CALVIEWTABLELOCK() requires two string arguments"); + return 1; + } + else if ((args->arg_count == 1) && (args->arg_type[0] != STRING_RESULT ) ) + { + strcpy(message, "CALVIEWTABLELOCK() requires one string argument"); + return 1; + } + else if (args->arg_count > 2 ) + { + strcpy(message, "CALVIEWTABLELOCK() takes one or two arguments only"); + return 1; + } + else if (args->arg_count == 0 ) + { + strcpy(message, "CALVIEWTABLELOCK() requires at least one argument"); + return 1; + } + + initid->maybe_null = 1; + initid->max_length = 255; + + return 0; + } + +#ifdef _MSC_VER + __declspec(dllexport) +#endif + const char* calviewtablelock(UDF_INIT* initid, UDF_ARGS* args, + char* result, unsigned long* length, + char* is_null, char* error) + { + THD* thd = current_thd; + + if (get_fe_conn_info_ptr() == NULL) + set_fe_conn_info_ptr((void*)new cal_connection_info()); + + cal_connection_info* ci = reinterpret_cast(get_fe_conn_info_ptr()); + CalpontSystemCatalog::TableName tableName; + + if ( args->arg_count == 2 ) + { + tableName.schema = args->args[0]; + tableName.table = args->args[1]; + } + else if ( args->arg_count == 1 ) + { + tableName.table = args->args[0]; + + if (thd->db.length) + tableName.schema = thd->db.str; + else + { + string msg("No schema information provided"); + memcpy(result, msg.c_str(), msg.length()); + *length = msg.length(); + return result; + } + } + + if ( !ci->dmlProc ) + { + ci->dmlProc = new MessageQueueClient("DMLProc"); + //cout << "viewtablelock starts a new client " << ci->dmlProc << " for session " << thd->thread_id << endl; + } + + string lockinfo = ha_calpont_impl_viewtablelock(*ci, tableName); + + memcpy(result, lockinfo.c_str(), lockinfo.length()); + *length = lockinfo.length(); + return result; + } + +#ifdef _MSC_VER + __declspec(dllexport) +#endif + void calviewtablelock_deinit(UDF_INIT* initid) + { + } + +#ifdef _MSC_VER + __declspec(dllexport) +#endif + my_bool calcleartablelock_init(UDF_INIT* initid, UDF_ARGS* args, char* message) + { + if ((args->arg_count != 1) || (args->arg_type[0] != INT_RESULT)) + { + strcpy(message, + "CALCLEARTABLELOCK() requires one integer argument (the lockID)"); + return 1; + } + + initid->maybe_null = 1; + initid->max_length = 255; + + return 0; + } + +#ifdef _MSC_VER + __declspec(dllexport) +#endif + const char* calcleartablelock(UDF_INIT* initid, UDF_ARGS* args, + char* result, unsigned long* length, + char* is_null, char* error) + { + if (get_fe_conn_info_ptr() == NULL) + set_fe_conn_info_ptr((void*)new cal_connection_info()); + + cal_connection_info* ci = reinterpret_cast( + get_fe_conn_info_ptr()); + long long lockID = *reinterpret_cast(args->args[0]); + + if ( !ci->dmlProc ) + { + ci->dmlProc = new MessageQueueClient("DMLProc"); + //cout << "cleartablelock starts a new client " << ci->dmlProc << " for session " << thd->thread_id << endl; + } + + unsigned long long uLockID = lockID; + string lockinfo = ha_calpont_impl_cleartablelock(*ci, uLockID); + + memcpy(result, lockinfo.c_str(), lockinfo.length()); + *length = lockinfo.length(); + return result; + } + +#ifdef _MSC_VER + __declspec(dllexport) +#endif + void calcleartablelock_deinit(UDF_INIT* initid) + { + } + +#ifdef _MSC_VER + __declspec(dllexport) +#endif + my_bool callastinsertid_init(UDF_INIT* initid, UDF_ARGS* args, char* message) + { + if (args->arg_count == 2 && (args->arg_type[0] != STRING_RESULT || args->arg_type[1] != STRING_RESULT)) + { + strcpy(message, "CALLASTINSRTID() requires two string arguments"); + return 1; + } + else if ((args->arg_count == 1) && (args->arg_type[0] != STRING_RESULT ) ) + { + strcpy(message, "CALLASTINSERTID() requires one string argument"); + return 1; + } + else if (args->arg_count > 2 ) + { + strcpy(message, "CALLASTINSERTID() takes one or two arguments only"); + return 1; + } + else if (args->arg_count == 0 ) + { + strcpy(message, "CALLASTINSERTID() requires at least one argument"); + return 1; + } + + initid->maybe_null = 1; + initid->max_length = 255; + + return 0; + } + +#ifdef _MSC_VER + __declspec(dllexport) +#endif + long long callastinsertid(UDF_INIT* initid, UDF_ARGS* args, + char* is_null, char* error) + { + THD* thd = current_thd; + + CalpontSystemCatalog::TableName tableName; + uint64_t nextVal = 0; + + if ( args->arg_count == 2 ) + { + tableName.schema = args->args[0]; + tableName.table = args->args[1]; + } + else if ( args->arg_count == 1 ) + { + tableName.table = args->args[0]; + + if (thd->db.length) + tableName.schema = thd->db.str; + else + { + return -1; + } + } + + boost::shared_ptr csc = + CalpontSystemCatalog::makeCalpontSystemCatalog( + CalpontSystemCatalog::idb_tid2sid(thd->thread_id)); + csc->identity(execplan::CalpontSystemCatalog::FE); + + try + { + nextVal = csc->nextAutoIncrValue(tableName); + } + catch (std::exception&) + { + string msg("No such table found during autincrement"); + setError(thd, ER_INTERNAL_ERROR, msg); + return nextVal; + } + + if (nextVal == AUTOINCR_SATURATED) + { + setError(thd, ER_INTERNAL_ERROR, IDBErrorInfo::instance()->errorMsg(ERR_EXCEED_LIMIT)); + return nextVal; + } + + //@Bug 3559. Return a message for table without autoincrement column. + if (nextVal == 0) + { + string msg("Autoincrement does not exist for this table."); + setError(thd, ER_INTERNAL_ERROR, msg); + return nextVal; + } + + return (nextVal - 1); + } + +#ifdef _MSC_VER + __declspec(dllexport) +#endif + void callastinsertid_deinit(UDF_INIT* initid) + { + } + +#ifdef _MSC_VER + __declspec(dllexport) +#endif + void calflushcache_deinit(UDF_INIT* initid) + { + } + +#ifdef _MSC_VER + __declspec(dllexport) +#endif + long long calflushcache(UDF_INIT* initid, UDF_ARGS* args, + char* is_null, char* error) + { + return static_cast(cacheutils::flushPrimProcCache()); + } + +#ifdef _MSC_VER + __declspec(dllexport) +#endif + my_bool calflushcache_init(UDF_INIT* initid, UDF_ARGS* args, char* message) + { + if (args->arg_count != 0) + { + strcpy(message, "CALFLUSHCACHE() takes no arguments"); + return 1; + } + + return 0; + } + + static const unsigned long TraceSize = 16 * 1024; + +//mysqld will call this with only 766 bytes available in result no matter what we asked for in calgettrace_init() +// if we return a pointer that is not result, mysqld will take our pointer and use it, freeing up result +#ifdef _MSC_VER + __declspec(dllexport) +#endif + const char* calgettrace(UDF_INIT* initid, UDF_ARGS* args, + char* result, unsigned long* length, + char* is_null, char* error) + { + const string* msgp; + int flags = 0; + + if (args->arg_count > 0) + { + if (args->arg_type[0] == INT_RESULT) + { + flags = *reinterpret_cast(args->args[0]); + } + } + + if (get_fe_conn_info_ptr() == NULL) + set_fe_conn_info_ptr((void*)new cal_connection_info()); + + cal_connection_info* ci = reinterpret_cast(get_fe_conn_info_ptr()); + + if (flags > 0) + //msgp = &connMap[sessionID].extendedStats; + msgp = &ci->extendedStats; + else + //msgp = &connMap[sessionID].miniStats; + msgp = &ci->miniStats; + + unsigned long l = msgp->size(); + + if (l == 0) + { + *is_null = 1; + return 0; + } + + if (l > TraceSize) l = TraceSize; + + *length = l; + return msgp->c_str(); + } + +#ifdef _MSC_VER + __declspec(dllexport) +#endif + my_bool calgettrace_init(UDF_INIT* initid, UDF_ARGS* args, char* message) + { +#if 0 + + if (args->arg_count != 0) + { + strcpy(message, "CALGETTRACE() takes no arguments"); + return 1; + } + +#endif + initid->maybe_null = 1; + initid->max_length = TraceSize; + + return 0; + } + +#ifdef _MSC_VER + __declspec(dllexport) +#endif + void calgettrace_deinit(UDF_INIT* initid) + { + } + +#ifdef _MSC_VER + __declspec(dllexport) +#endif + const char* calgetversion(UDF_INIT* initid, UDF_ARGS* args, + char* result, unsigned long* length, + char* is_null, char* error) + { + string version(columnstore_version); + *length = version.size(); + memcpy(result, version.c_str(), *length); + return result; + } + +#ifdef _MSC_VER + __declspec(dllexport) +#endif + my_bool calgetversion_init(UDF_INIT* initid, UDF_ARGS* args, char* message) + { + if (args->arg_count != 0) + { + strcpy(message, "CALGETVERSION() takes no arguments"); + return 1; + } + + return 0; + } + +#ifdef _MSC_VER + __declspec(dllexport) +#endif + void calgetversion_deinit(UDF_INIT* initid) + { + } + +#ifdef _MSC_VER + __declspec(dllexport) +#endif + const char* calgetsqlcount(UDF_INIT* initid, UDF_ARGS* args, + char* result, unsigned long* length, + char* is_null, char* error) + { + if (get_fe_conn_info_ptr() == NULL) + set_fe_conn_info_ptr((void*)new cal_connection_info()); + + cal_connection_info* ci = reinterpret_cast(get_fe_conn_info_ptr()); + idbassert(ci != 0); + + MessageQueueClient* mqc = 0; + mqc = new MessageQueueClient("ExeMgr1"); + + ByteStream msg; + ByteStream::quadbyte runningSql, waitingSql; + ByteStream::quadbyte qb = 5; + msg << qb; + mqc->write(msg); + + //get ExeMgr response + msg.restart(); + msg = mqc->read(); + + if (msg.length() == 0) + { + memcpy(result, "Lost connection to ExeMgr", *length); + return result; + } + + msg >> runningSql; + msg >> waitingSql; + delete mqc; + + char ans[128]; + sprintf(ans, "Running SQL statements %d, Waiting SQL statments %d", runningSql, waitingSql); + *length = strlen(ans); + memcpy(result, ans, *length); + return result; + } + +#ifdef _MSC_VER + __declspec(dllexport) +#endif + my_bool calgetsqlcount_init(UDF_INIT* initid, UDF_ARGS* args, char* message) + { + if (args->arg_count != 0) + { + strcpy(message, "CALGETSQLCOUNT() takes no arguments"); + return 1; + } + + return 0; + } + +#ifdef _MSC_VER + __declspec(dllexport) +#endif + void calgetsqlcount_deinit(UDF_INIT* initid) + { + } + + +} //extern "C" diff --git a/dbcon/mysql/mcs_sysvars.cpp b/dbcon/mysql/mcs_sysvars.cpp index 97069eb8e..106559a0a 100644 --- a/dbcon/mysql/mcs_sysvars.cpp +++ b/dbcon/mysql/mcs_sysvars.cpp @@ -101,24 +101,6 @@ void set_fe_conn_info_ptr(void* ptr) THDVAR(current_thd, fe_conn_info_ptr) = (uint64_t)(ptr); } -/*ha_calpont* get_legacy_handler(mcs_handler_info mcs_hndl_ptr) -{ - //MCOL-1101 Add handler type check - //hndl_ptr.hndl_type == LEGACY ) - ha_calpont* hndl; - if ( mcs_hndl_ptr.hndl_ptr != NULL ) - { - hndl = (ha_calpont*)(mcs_hndl_ptr.hndl_ptr); - } - else - { - hndl = new ha_calpont(); - hndl->fe_conn_info = (void*)THDVAR(current_thd, fe_conn_info_ptr); - } - - return hndl; -}*/ - mcs_compression_type_t get_compression_type(THD* thd) { return (mcs_compression_type_t) THDVAR(thd, compression_type); } diff --git a/dbcon/mysql/mcs_sysvars.h b/dbcon/mysql/mcs_sysvars.h index c1506615b..8e2436db6 100644 --- a/dbcon/mysql/mcs_sysvars.h +++ b/dbcon/mysql/mcs_sysvars.h @@ -24,6 +24,7 @@ extern st_mysql_sys_var* mcs_system_variables[]; +/* MCOL-1101 Remove before release enum mcs_handler_types_t { SELECT, @@ -40,7 +41,7 @@ struct mcs_handler_info void* hndl_ptr; mcs_handler_types_t hndl_type; }; - +*/ // compression_type enum mcs_compression_type_t { NO_COMPRESSION = 0, From 9c71f3d48086b5963a020cc9bd9d2316ca22a0cd Mon Sep 17 00:00:00 2001 From: Andrew Hutchings Date: Wed, 13 Feb 2019 21:23:08 +0000 Subject: [PATCH 12/38] Bump version to 1.0.17 --- README.md | 8 ++++---- VERSION | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index 3ad645174..bac34bdd2 100644 --- a/README.md +++ b/README.md @@ -1,9 +1,9 @@ -#MariaDB ColumnStore Storage/Execution engine 1.0.16 -MariaDB ColumnStore 1.0.16 is the development version of MariaDB ColumnStore. -It is built by porting InfiniDB 4.6.7 on MariaDB 10.1.35 and adding entirely +#MariaDB ColumnStore Storage/Execution engine 1.0.17 +MariaDB ColumnStore 1.0.17 is the development version of MariaDB ColumnStore. +It is built by porting InfiniDB 4.6.7 on MariaDB 10.1 and adding entirely new features not found anywhere else. -#MariaDB ColumnStore 1.0.16 is an GA release. +#MariaDB ColumnStore 1.0.17 is an GA release. #Building This repository is not meant to be built independently outside of the server. This repository is integrated into http://mariadb-corporation/mariadb-columnstore-server (ie, the *server*) as a git submodule. As such, you can find complete build instructions on *the server* page. diff --git a/VERSION b/VERSION index b13a94d3f..7e6391a76 100644 --- a/VERSION +++ b/VERSION @@ -1,4 +1,4 @@ COLUMNSTORE_VERSION_MAJOR=1 COLUMNSTORE_VERSION_MINOR=0 -COLUMNSTORE_VERSION_PATCH=16 +COLUMNSTORE_VERSION_PATCH=17 COLUMNSTORE_VERSION_RELEASE=1 From 620519384593c0b2ada9dcb24ce52b9a6e1463c8 Mon Sep 17 00:00:00 2001 From: Andrew Hutchings Date: Wed, 13 Feb 2019 21:50:44 +0000 Subject: [PATCH 13/38] Disable jemalloc on SLES --- CMakeLists.txt | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 387d34f3a..2053bbad7 100755 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -112,11 +112,16 @@ if (NOT SNAPPY_FOUND) MESSAGE(FATAL_ERROR "Snappy not found please install snappy-devel for CentOS/RedHat or libsnappy-dev for Ubuntu/Debian") endif() -INCLUDE (FindJeMalloc.cmake) -if (NOT JEMALLOC_FOUND) - message(FATAL_ERROR "jemalloc not found!") +# Jemalloc has issues with SLES 12, so disable for now +IF (EXISTS "/etc/SuSE-release") SET(JEMALLOC_LIBRARIES "") -endif() +ELSE () + INCLUDE (FindJeMalloc.cmake) + if (NOT JEMALLOC_FOUND) + message(FATAL_ERROR "jemalloc not found!") + SET(JEMALLOC_LIBRARIES "") + endif() +ENDIF () FIND_PROGRAM(AWK_EXECUTABLE awk DOC "path to the awk executable") if(NOT AWK_EXECUTABLE) From 7dfcc3c69ca8dd641c35864275f1cfb0cd0dbf23 Mon Sep 17 00:00:00 2001 From: Patrick LeBlanc Date: Thu, 14 Feb 2019 10:01:27 -0600 Subject: [PATCH 14/38] MCOL-1607. Let postconfigure store hostnames in the configuration. Made postconfig resolve hostname -> IP for its 'am I PM1' check. Made the text of one of the prompts more consistent. Made one of the paths it goes down to get ip addresses do hostname -> IP. Unrelated to ticket, just something Daniel noticed. --- oamapps/postConfigure/postConfigure.cpp | 25 ++++++++++++++++++++++--- 1 file changed, 22 insertions(+), 3 deletions(-) diff --git a/oamapps/postConfigure/postConfigure.cpp b/oamapps/postConfigure/postConfigure.cpp index c4e9b2126..fdfc1482c 100644 --- a/oamapps/postConfigure/postConfigure.cpp +++ b/oamapps/postConfigure/postConfigure.cpp @@ -662,9 +662,14 @@ int main(int argc, char* argv[]) if (moduleconfig.hostConfigList.size() > 0 ) { HostConfigList::iterator pt1 = moduleconfig.hostConfigList.begin(); - string PM1ipAdd = (*pt1).IPAddr; + + // MCOL-1607. The 'am I pm1?' check below requires an ipaddr. + string PM1ipAdd = oam.getIPAddress((*pt1).IPAddr.c_str()); + if (PM1ipAdd.empty()) + PM1ipAdd = (*pt1).IPAddr; // this is what it was doing before + //cout << PM1ipAdd << endl; - + if ( PM1ipAdd != "127.0.0.1" ) { if ( PM1ipAdd != "0.0.0.0") @@ -2602,6 +2607,13 @@ int main(int argc, char* argv[]) callFree(pcommand); } + + if (!doNotResolveHostNames) + { + string ugh = oam.getIPAddress(newModuleIPAddr); + if (ugh.length() > 0) + newModuleIPAddr = ugh; + } if (newModuleIPAddr == "127.0.0.1" || newModuleIPAddr == "0.0.0.0" || newModuleIPAddr == "128.0.0.1") { @@ -6580,7 +6592,7 @@ bool glusterSetup(string password, bool doNotResolveHostNames) //prompt for IP address while (true) { - prompt = "Enter PM #" + oam.itoa(DataRedundancyConfigs[pm].pmID) + " IP Address of " + moduleHostName + " (" + moduleIPAddr + ") > "; + prompt = "Enter PM #" + oam.itoa(DataRedundancyConfigs[pm].pmID) + " IP Address or hostname of " + moduleHostName + " (" + moduleIPAddr + ") > "; pcommand = callReadline(prompt.c_str()); if (pcommand) @@ -6589,6 +6601,13 @@ bool glusterSetup(string password, bool doNotResolveHostNames) callFree(pcommand); } + + if (!doNotResolveHostNames) + { + string ugh = oam.getIPAddress(moduleIPAddr); + if (ugh.length() > 0) + moduleIPAddr = ugh; + } if (moduleIPAddr == "127.0.0.1" || moduleIPAddr == "0.0.0.0" || moduleIPAddr == "128.0.0.1") { From 06696f596a797780f51caa0513319f25ac91a7e7 Mon Sep 17 00:00:00 2001 From: Roman Nozdrin Date: Fri, 15 Feb 2019 10:14:10 +0300 Subject: [PATCH 15/38] MCOL-1101 Add plugin variables to replace the legacy system vars. Legacy system vars with names infinidb_* was preserved for backward compatibility and they will be used if columnstore_use_legacy_vars variable is set. Remove unused structure and plugin variable. --- dbcon/mysql/CMakeLists.txt | 4 +- dbcon/mysql/ha_calpont.cpp | 51 -- dbcon/mysql/ha_calpont.h | 2 +- dbcon/mysql/ha_calpont_ddl.cpp | 2 +- dbcon/mysql/ha_calpont_execplan.cpp | 47 +- dbcon/mysql/ha_calpont_impl.cpp | 27 +- ...client_udfs.cpp => ha_mcs_client_udfs.cpp} | 2 +- dbcon/mysql/ha_mcs_sysvars.cpp | 520 ++++++++++++++++++ dbcon/mysql/ha_mcs_sysvars.h | 91 +++ dbcon/mysql/ha_pseudocolumn.cpp | 2 +- dbcon/mysql/ha_window_function.cpp | 4 +- dbcon/mysql/mcs_sysvars.cpp | 106 ---- dbcon/mysql/mcs_sysvars.h | 59 -- 13 files changed, 656 insertions(+), 261 deletions(-) rename dbcon/mysql/{mcs_client_udfs.cpp => ha_mcs_client_udfs.cpp} (99%) create mode 100644 dbcon/mysql/ha_mcs_sysvars.cpp create mode 100644 dbcon/mysql/ha_mcs_sysvars.h delete mode 100644 dbcon/mysql/mcs_sysvars.cpp delete mode 100644 dbcon/mysql/mcs_sysvars.h diff --git a/dbcon/mysql/CMakeLists.txt b/dbcon/mysql/CMakeLists.txt index 7744e50b4..fa2df6eaa 100644 --- a/dbcon/mysql/CMakeLists.txt +++ b/dbcon/mysql/CMakeLists.txt @@ -4,8 +4,8 @@ include_directories( ${ENGINE_COMMON_INCLUDES} SET ( libcalmysql_SRCS - mcs_sysvars.cpp - mcs_client_udfs.cpp + ha_mcs_sysvars.cpp + ha_mcs_client_udfs.cpp ha_calpont.cpp ha_calpont_impl.cpp ha_calpont_dml.cpp diff --git a/dbcon/mysql/ha_calpont.cpp b/dbcon/mysql/ha_calpont.cpp index 3c15dd146..8dc96942f 100644 --- a/dbcon/mysql/ha_calpont.cpp +++ b/dbcon/mysql/ha_calpont.cpp @@ -592,9 +592,6 @@ int ha_calpont::rnd_init(bool scan) { DBUG_ENTER("ha_calpont::rnd_init"); - String query_string_cpy; query_string_cpy.append(current_thd->query_string.str()); - set_original_query(current_thd, query_string_cpy.c_ptr_safe()); - int rc = ha_calpont_impl_rnd_init(table); DBUG_RETURN(rc); @@ -1241,54 +1238,6 @@ int ha_calpont_group_by_handler::end_scan() DBUG_RETURN(rc); } -/* -// compression_type -enum mcs_compression_type_t { - NO_COMPRESSION = 0, - SNAPPY = 2 -}; - -const char* mcs_compression_type_names[] = { - "NO_COMPRESSION", - "SNAPPY", - NullS -}; - -static TYPELIB mcs_compression_type_names_lib = { - array_elements(mcs_compression_type_names) - 1, - "mcs_compression_type_names", - mcs_compression_type_names, - NULL -}; - -static MYSQL_THDVAR_ENUM( - compression_type, - PLUGIN_VAR_RQCMDARG, - "Controls compression type for create tables. Possible values are: " - "NO_COMPRESSION segment files aren't compressed; " - "SNAPPY segment files are Snappy compressed (default);", - NULL, - NULL, - SNAPPY, - &mcs_compression_type_names_lib); - -// original query -static MYSQL_THDVAR_STR( - original_query, - PLUGIN_VAR_MEMALLOC | - PLUGIN_VAR_RQCMDARG, - "Original query text", - NULL, - NULL, - NULL -); - -static struct st_mysql_sys_var* columnstore_system_variables[] = -{ - MYSQL_SYSVAR(compression_type), - MYSQL_SYSVAR(original_query), - NULL -};*/ mysql_declare_plugin(columnstore) { diff --git a/dbcon/mysql/ha_calpont.h b/dbcon/mysql/ha_calpont.h index 2323d2d13..bab756851 100644 --- a/dbcon/mysql/ha_calpont.h +++ b/dbcon/mysql/ha_calpont.h @@ -19,7 +19,7 @@ #define HA_CALPONT_H__ #include #include "idb_mysql.h" -#include "mcs_sysvars.h" +#include "ha_mcs_sysvars.h" extern handlerton* calpont_hton; diff --git a/dbcon/mysql/ha_calpont_ddl.cpp b/dbcon/mysql/ha_calpont_ddl.cpp index e7d7153bc..8b766b512 100644 --- a/dbcon/mysql/ha_calpont_ddl.cpp +++ b/dbcon/mysql/ha_calpont_ddl.cpp @@ -49,7 +49,7 @@ using namespace std; #include using namespace boost; -#include "mcs_sysvars.h" +#include "ha_mcs_sysvars.h" #include "idb_mysql.h" #include "ha_calpont_impl_if.h" diff --git a/dbcon/mysql/ha_calpont_execplan.cpp b/dbcon/mysql/ha_calpont_execplan.cpp index 99ad80605..6e9755707 100644 --- a/dbcon/mysql/ha_calpont_execplan.cpp +++ b/dbcon/mysql/ha_calpont_execplan.cpp @@ -58,7 +58,7 @@ using namespace logging; #include "idb_mysql.h" #include "ha_calpont_impl_if.h" -#include "mcs_sysvars.h" +#include "ha_mcs_sysvars.h" #include "ha_subquery.h" //#include "ha_view.h" using namespace cal_impl_if; @@ -3276,7 +3276,7 @@ ArithmeticColumn* buildArithmeticColumn( //idbassert(pt->left() && pt->right() && pt->left()->data() && pt->right()->data()); CalpontSystemCatalog::ColType mysql_type = colType_MysqlToIDB(item); - if (current_thd->variables.infinidb_double_for_decimal_math == 1) + if (get_double_for_decimal_math(current_thd) == true) aop->adjustResultType(mysql_type); else aop->resultType(mysql_type); @@ -5776,7 +5776,8 @@ int getSelectPlan(gp_walk_info& gwi, SELECT_LEX& select_lex, SCSEP& csep, bool i return ER_CHECK_NOT_IMPLEMENTED; } - gwi.internalDecimalScale = (gwi.thd->variables.infinidb_use_decimal_scale ? gwi.thd->variables.infinidb_decimal_scale : -1); + gwi.internalDecimalScale = (get_use_decimal_scale(gwi.thd) ? get_decimal_scale(gwi.thd) : -1); + gwi.subSelectType = csep->subType(); JOIN* join = select_lex.join; @@ -5809,25 +5810,25 @@ int getSelectPlan(gp_walk_info& gwi, SELECT_LEX& select_lex, SCSEP& csep, bool i // @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 (gwi.thd->infinidb_vtable.override_largeside_estimate || gwi.thd->variables.infinidb_ordered_only) + if (gwi.thd->infinidb_vtable.override_largeside_estimate || get_ordered_only(gwi.thd)) csep->overrideLargeSideEstimate(true); // @bug 5741. Set a flag when in Local PM only query mode - csep->localQuery(gwi.thd->variables.infinidb_local_query); + 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(gwi.thd->variables.infinidb_string_scan_threshold); + csep->stringScanThreshold(get_string_scan_threshold(gwi.thd)); - csep->stringTableThreshold(gwi.thd->variables.infinidb_stringtable_threshold); + csep->stringTableThreshold(get_stringtable_threshold(gwi.thd)); - csep->djsSmallSideLimit(gwi.thd->variables.infinidb_diskjoin_smallsidelimit * 1024ULL * 1024); - csep->djsLargeSideLimit(gwi.thd->variables.infinidb_diskjoin_largesidelimit * 1024ULL * 1024); - csep->djsPartitionSize(gwi.thd->variables.infinidb_diskjoin_bucketsize * 1024ULL * 1024); + 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 (gwi.thd->variables.infinidb_um_mem_limit == 0) + if (get_um_mem_limit(gwi.thd) == 0) csep->umMemLimit(numeric_limits::max()); else - csep->umMemLimit(gwi.thd->variables.infinidb_um_mem_limit * 1024ULL * 1024); + 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 @@ -8242,7 +8243,7 @@ int cp_get_table_plan(THD* thd, SCSEP& csep, cal_table_info& ti) csep->tableList(tblist); // @bug 3321. Set max number of blocks in a dictionary file to be scanned for filtering - csep->stringScanThreshold(gwi->thd->variables.infinidb_string_scan_threshold); + csep->stringScanThreshold(get_string_scan_threshold(gwi->thd)); return 0; } @@ -8340,7 +8341,7 @@ int getGroupPlan(gp_walk_info& gwi, SELECT_LEX& select_lex, SCSEP& csep, cal_gro return ER_CHECK_NOT_IMPLEMENTED; } - gwi.internalDecimalScale = (gwi.thd->variables.infinidb_use_decimal_scale ? gwi.thd->variables.infinidb_decimal_scale : -1); + gwi.internalDecimalScale = (get_use_decimal_scale(gwi.thd) ? get_decimal_scale(gwi.thd) : -1); gwi.subSelectType = csep->subType(); JOIN* join = select_lex.join; @@ -8357,25 +8358,25 @@ int getGroupPlan(gp_walk_info& gwi, SELECT_LEX& select_lex, SCSEP& csep, cal_gro // @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 (gwi.thd->infinidb_vtable.override_largeside_estimate || gwi.thd->variables.infinidb_ordered_only) + if (gwi.thd->infinidb_vtable.override_largeside_estimate || get_ordered_only(gwi.thd)) csep->overrideLargeSideEstimate(true); // @bug 5741. Set a flag when in Local PM only query mode - csep->localQuery(gwi.thd->variables.infinidb_local_query); + 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(gwi.thd->variables.infinidb_string_scan_threshold); + csep->stringScanThreshold(get_string_scan_threshold(gwi.thd)); - csep->stringTableThreshold(gwi.thd->variables.infinidb_stringtable_threshold); + csep->stringTableThreshold(get_stringtable_threshold(gwi.thd)); - csep->djsSmallSideLimit(gwi.thd->variables.infinidb_diskjoin_smallsidelimit * 1024ULL * 1024); - csep->djsLargeSideLimit(gwi.thd->variables.infinidb_diskjoin_largesidelimit * 1024ULL * 1024); - csep->djsPartitionSize(gwi.thd->variables.infinidb_diskjoin_bucketsize * 1024ULL * 1024); + 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 (gwi.thd->variables.infinidb_um_mem_limit == 0) + if (get_um_mem_limit(gwi.thd) == 0) csep->umMemLimit(numeric_limits::max()); else - csep->umMemLimit(gwi.thd->variables.infinidb_um_mem_limit * 1024ULL * 1024); + 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 diff --git a/dbcon/mysql/ha_calpont_impl.cpp b/dbcon/mysql/ha_calpont_impl.cpp index 43a2fd628..24bf0687e 100644 --- a/dbcon/mysql/ha_calpont_impl.cpp +++ b/dbcon/mysql/ha_calpont_impl.cpp @@ -142,7 +142,7 @@ using namespace funcexp; #include "installdir.h" #include "columnstoreversion.h" -#include "mcs_sysvars.h" +#include "ha_mcs_sysvars.h" namespace cal_impl_if { @@ -560,7 +560,7 @@ int fetchNextRow(uchar* buf, cal_table_info& ti, cal_connection_info* ci, bool h { Field_varstring* f2 = (Field_varstring*)*f; - if (current_thd->variables.infinidb_varbin_always_hex) + if (get_varbin_always_hex(current_thd)) { uint32_t l; const uint8_t* p = row.getVarBinaryField(l, s); @@ -762,7 +762,7 @@ int fetchNextRow(uchar* buf, cal_table_info& ti, cal_connection_info* ci, bool h ti.moreRows = false; rc = logging::ERR_LOST_CONN_EXEMGR; sm::sm_init(tid2sid(current_thd->thread_id), &ci->cal_conn_hndl, - current_thd->variables.infinidb_local_query); + get_local_query(current_thd)); idbassert(ci->cal_conn_hndl != 0); ci->rc = rc; } @@ -2096,7 +2096,7 @@ int ha_calpont_impl_rnd_init(TABLE* table) CalpontSelectExecutionPlan::TRACE_TUPLE_OFF; } - bool localQuery = (thd->variables.infinidb_local_query > 0 ? true : false); + bool localQuery = get_local_query(thd); // table mode if (thd->infinidb_vtable.vtable_state == THD::INFINIDB_DISABLE_VTABLE) @@ -2264,9 +2264,8 @@ int ha_calpont_impl_rnd_init(TABLE* table) return 0; string query; - const char *original_query = get_original_query(current_thd); - query.assign(original_query, - strlen(original_query)); + query.assign(thd->infinidb_vtable.original_query.ptr(), + thd->infinidb_vtable.original_query.length()); csep->data(query); try @@ -3121,7 +3120,7 @@ void ha_calpont_impl_start_bulk_insert(ha_rows rows, TABLE* table) ((thd->lex)->sql_command == SQLCOM_LOAD) || (thd->lex)->sql_command == SQLCOM_INSERT_SELECT) && !ci->singleInsert ) { - ci->useCpimport = thd->variables.infinidb_use_import_for_batchinsert; + ci->useCpimport = get_use_import_for_batchinsert(thd); if (((thd->lex)->sql_command == SQLCOM_INSERT) && (rows > 0)) ci->useCpimport = 0; @@ -3194,14 +3193,14 @@ void ha_calpont_impl_start_bulk_insert(ha_rows rows, TABLE* table) ci->mysqld_pid = getpid(); //get delimiter - if (char(thd->variables.infinidb_import_for_batchinsert_delimiter) != '\007') - ci->delimiter = char(thd->variables.infinidb_import_for_batchinsert_delimiter); + if (char(get_import_for_batchinsert_delimiter(thd)) != '\007') + ci->delimiter = char(get_import_for_batchinsert_delimiter(thd)); else ci->delimiter = '\007'; //get enclosed by - if (char(thd->variables.infinidb_import_for_batchinsert_enclosed_by) != 8) - ci->enclosed_by = char(thd->variables.infinidb_import_for_batchinsert_enclosed_by); + if (char(get_import_for_batchinsert_enclosed_by(thd)) != 8) + ci->enclosed_by = char(get_import_for_batchinsert_enclosed_by(thd)); else ci->enclosed_by = 8; @@ -3217,7 +3216,7 @@ void ha_calpont_impl_start_bulk_insert(ha_rows rows, TABLE* table) if (ci->enclosed_by == 34) // Double quotes strcat(escapechar, "\\"); - if (thd->variables.infinidb_local_query > 0 ) + if (get_local_query(thd)) { OamCache* oamcache = OamCache::makeOamCache(); int localModuleId = oamcache->getLocalPMId(); @@ -4322,7 +4321,7 @@ int ha_calpont_impl_group_by_init(ha_calpont_group_by_handler* group_hand, TABLE sm::cpsm_conhdl_t* hndl; SCSEP csep; - bool localQuery = (thd->variables.infinidb_local_query > 0 ? true : false); + bool localQuery = get_local_query(thd); { ci->stats.reset(); // reset query stats diff --git a/dbcon/mysql/mcs_client_udfs.cpp b/dbcon/mysql/ha_mcs_client_udfs.cpp similarity index 99% rename from dbcon/mysql/mcs_client_udfs.cpp rename to dbcon/mysql/ha_mcs_client_udfs.cpp index 605e62392..1766bdf1c 100644 --- a/dbcon/mysql/mcs_client_udfs.cpp +++ b/dbcon/mysql/ha_mcs_client_udfs.cpp @@ -40,7 +40,7 @@ using namespace logging; //#include "resourcemanager.h" #include "columnstoreversion.h" -#include "mcs_sysvars.h" +#include "ha_mcs_sysvars.h" extern "C" { diff --git a/dbcon/mysql/ha_mcs_sysvars.cpp b/dbcon/mysql/ha_mcs_sysvars.cpp new file mode 100644 index 000000000..ff6943fa4 --- /dev/null +++ b/dbcon/mysql/ha_mcs_sysvars.cpp @@ -0,0 +1,520 @@ +/* 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. */ + +#include +#include "idb_mysql.h" +#include "ha_mcs_sysvars.h" + +const char* mcs_compression_type_names[] = { + "NO_COMPRESSION", + "SNAPPY", + NullS +}; + +static TYPELIB mcs_compression_type_names_lib = { + array_elements(mcs_compression_type_names) - 1, + "mcs_compression_type_names", + mcs_compression_type_names, + NULL +}; + +// compression type +static MYSQL_THDVAR_ENUM( + compression_type, + PLUGIN_VAR_RQCMDARG, + "Controls compression algorithm for create tables. Possible values are: " + "NO_COMPRESSION segment files aren't compressed; " + "SNAPPY segment files are Snappy compressed (default);", + NULL, // check + NULL, // update + 1, //default + &mcs_compression_type_names_lib); // values lib + +// fe_conn_info pointer +static MYSQL_THDVAR_ULONGLONG( + fe_conn_info_ptr, + PLUGIN_VAR_NOSYSVAR | PLUGIN_VAR_NOCMDOPT, + "FrontEnd connection structure pointer. For internal usage.", + NULL, + NULL, + 0, + 0, + ~0U, + 1 +); + +// legacy system variables +static MYSQL_THDVAR_ULONG( + decimal_scale, + PLUGIN_VAR_RQCMDARG, + "The default decimal precision for calculated column sub-operations ", + NULL, + NULL, + 8, + 0, + 18, + 1 +); + +static MYSQL_THDVAR_BOOL( + varbin_always_hex, + PLUGIN_VAR_NOCMDARG, + "Always display/process varbinary columns as if they have been hexified.", + NULL, + NULL, + 0 +); + +static MYSQL_THDVAR_BOOL( + use_decimal_scale, + PLUGIN_VAR_NOCMDARG, + "Enable/disable the MCS decimal scale to be used internally", + NULL, + NULL, + 0 +); + +static MYSQL_THDVAR_BOOL( + double_for_decimal_math, + PLUGIN_VAR_NOCMDARG, + "Enable/disable the InfiniDB to replace DECIMAL with DOUBLE in arithmetic operation.", + NULL, + NULL, + 0 +); + +static MYSQL_THDVAR_BOOL( + ordered_only, + PLUGIN_VAR_NOCMDARG, + "Always use the first table in the from clause as the large side " + "table for joins", + NULL, + NULL, + 0 +); + +static MYSQL_THDVAR_ULONG( + string_scan_threshold, + PLUGIN_VAR_RQCMDARG, + "Max number of blocks in a dictionary file to be scanned for filtering", + NULL, + NULL, + 10, + 1, + ~0U, + 1 +); + +static MYSQL_THDVAR_ULONG( + stringtable_threshold, + PLUGIN_VAR_RQCMDARG, + "The minimum width of a string column to be stored in a string table", + NULL, + NULL, + 20, + 9, + ~0U, + 1 +); + +static MYSQL_THDVAR_ULONG( + diskjoin_smallsidelimit, + PLUGIN_VAR_RQCMDARG, + "The maximum amount of disk space in MB to use per query for storing " + "'small side' tables for a disk-based join. (0 = unlimited)", + NULL, + NULL, + 0, + 0, + ~0U, + 1 +); + +static MYSQL_THDVAR_ULONG( + diskjoin_largesidelimit, + PLUGIN_VAR_RQCMDARG, + "The maximum amount of disk space in MB to use per join for storing " + "'large side' table data for a disk-based join. (0 = unlimited)", + NULL, + NULL, + 0, + 0, + ~0U, + 1 +); + +static MYSQL_THDVAR_ULONG( + diskjoin_bucketsize, + PLUGIN_VAR_RQCMDARG, + "The maximum size in MB of each 'small side' table in memory.", + NULL, + NULL, + 100, + 1, + ~0U, + 1 +); + +static MYSQL_THDVAR_ULONG( + um_mem_limit, + PLUGIN_VAR_RQCMDARG, + "Per user Memory limit(MB). Switch to disk-based JOIN when limit is reached", + NULL, + NULL, + 0, + 0, + ~0U, + 1 +); + +static MYSQL_THDVAR_ULONG( + local_query, + PLUGIN_VAR_RQCMDARG, + "Enable/disable the Infinidb local PM query only feature.", + NULL, + NULL, + 0, + 0, + 2, + 1 +); + +static MYSQL_THDVAR_ULONG( + import_for_batchinsert_delimiter, + PLUGIN_VAR_RQCMDARG, + "ASCII value of the delimiter used by LDI and INSERT..SELECT", + NULL, // check + NULL, // update + 7, // default + 0, // min + 127, // max + 1 // block size +); + +static MYSQL_THDVAR_ULONG( + import_for_batchinsert_enclosed_by, + PLUGIN_VAR_RQCMDARG, + "ASCII value of the quote symbol used by batch data ingestion", + NULL, // check + NULL, // update + 17, // default + 17, // min + 127, // max + 1 // block size +); + +static MYSQL_THDVAR_BOOL( + use_import_for_batchinsert, + PLUGIN_VAR_NOCMDARG, + "LOAD DATA INFILE and INSERT..SELECT will use cpimport internally", + NULL, // check + NULL, // update + 1 // default +); + +static MYSQL_THDVAR_BOOL( + use_legacy_sysvars, + PLUGIN_VAR_NOCMDARG, + "Control CS behavior using legacy * sysvars", + NULL, // check + NULL, // update + 0 // default +); + +st_mysql_sys_var* mcs_system_variables[] = +{ + MYSQL_SYSVAR(compression_type), + MYSQL_SYSVAR(fe_conn_info_ptr), + MYSQL_SYSVAR(decimal_scale), + MYSQL_SYSVAR(use_decimal_scale), + MYSQL_SYSVAR(ordered_only), + MYSQL_SYSVAR(string_scan_threshold), + MYSQL_SYSVAR(stringtable_threshold), + MYSQL_SYSVAR(diskjoin_smallsidelimit), + MYSQL_SYSVAR(diskjoin_largesidelimit), + MYSQL_SYSVAR(diskjoin_bucketsize), + MYSQL_SYSVAR(um_mem_limit), + MYSQL_SYSVAR(double_for_decimal_math), + MYSQL_SYSVAR(local_query), + MYSQL_SYSVAR(use_import_for_batchinsert), + MYSQL_SYSVAR(import_for_batchinsert_delimiter), + MYSQL_SYSVAR(import_for_batchinsert_enclosed_by), + MYSQL_SYSVAR(use_legacy_sysvars), + MYSQL_SYSVAR(varbin_always_hex), + NULL +}; + +void* get_fe_conn_info_ptr(THD* thd) +{ + return ( current_thd == NULL && thd == NULL ) ? NULL : + (void*)THDVAR(current_thd, fe_conn_info_ptr); +} + +void set_fe_conn_info_ptr(void* ptr, THD* thd) +{ + if ( current_thd == NULL && thd == NULL) + { + return; + } + + THDVAR(current_thd, fe_conn_info_ptr) = (uint64_t)(ptr); +} + +bool get_use_legacy_sysvars(THD* thd) +{ + return ( thd == NULL ) ? false : THDVAR(thd, use_legacy_sysvars); +} + +void set_use_legacy_sysvars(THD* thd, bool value) +{ + THDVAR(thd, use_legacy_sysvars) = value; +} + +void set_compression_type(THD* thd, ulong value) +{ + THDVAR(thd, compression_type) = value; +} + +mcs_compression_type_t get_compression_type(THD* thd) { + return (mcs_compression_type_t) THDVAR(thd, compression_type); +} + +bool get_use_decimal_scale(THD* thd) +{ + if(get_use_legacy_sysvars(thd)) + return ( thd == NULL ) ? false : thd->variables.infinidb_use_decimal_scale; + else + return ( thd == NULL ) ? false : THDVAR(thd, use_decimal_scale); +} +void set_use_decimal_scale(THD* thd, bool value) +{ + if(get_use_legacy_sysvars(thd)) + thd->variables.infinidb_use_decimal_scale = value; + else + THDVAR(thd, use_decimal_scale) = value; +} + +ulong get_decimal_scale(THD* thd) +{ + if(get_use_legacy_sysvars(thd)) + return ( thd == NULL ) ? 0 : thd->variables.infinidb_decimal_scale; + else + return ( thd == NULL ) ? 0 : THDVAR(thd, decimal_scale); +} +void set_decimal_scale(THD* thd, ulong value) +{ + if(get_use_legacy_sysvars(thd)) + thd->variables.infinidb_decimal_scale = value; + else + THDVAR(thd, decimal_scale) = value; +} + +bool get_ordered_only(THD* thd) +{ + if(get_use_legacy_sysvars(thd)) + return ( thd == NULL ) ? false : thd->variables.infinidb_ordered_only; + else + return ( thd == NULL ) ? false : THDVAR(thd, ordered_only); +} +void set_ordered_only(THD* thd, bool value) +{ + if(get_use_legacy_sysvars(thd)) + thd->variables.infinidb_ordered_only = value; + else + THDVAR(thd, ordered_only) = value; +} + +ulong get_string_scan_threshold(THD* thd) +{ + if(get_use_legacy_sysvars(thd)) + return ( thd == NULL ) ? 0 : thd->variables.infinidb_string_scan_threshold; + else + return ( thd == NULL ) ? 0 : THDVAR(thd, string_scan_threshold); +} +void set_string_scan_threshold(THD* thd, ulong value) +{ + if(get_use_legacy_sysvars(thd)) + thd->variables.infinidb_string_scan_threshold = value; + else + THDVAR(thd, string_scan_threshold) = value; +} + +ulong get_stringtable_threshold(THD* thd) +{ + if(get_use_legacy_sysvars(thd)) + return ( thd == NULL ) ? 0 : thd->variables.infinidb_stringtable_threshold; + else + return ( thd == NULL ) ? 0 : THDVAR(thd, stringtable_threshold); +} +void set_stringtable_threshold(THD* thd, ulong value) +{ + if(get_use_legacy_sysvars(thd)) + thd->variables.infinidb_stringtable_threshold = value; + else + THDVAR(thd, stringtable_threshold) = value; +} + +ulong get_diskjoin_smallsidelimit(THD* thd) +{ + if(get_use_legacy_sysvars(thd)) + return ( thd == NULL ) ? 0 : thd->variables.infinidb_diskjoin_smallsidelimit; + else + return ( thd == NULL ) ? 0 : THDVAR(thd, diskjoin_smallsidelimit); +} +void set_diskjoin_smallsidelimit(THD* thd, ulong value) +{ + if(get_use_legacy_sysvars(thd)) + thd->variables.infinidb_diskjoin_smallsidelimit = value; + else + THDVAR(thd, diskjoin_smallsidelimit) = value; +} + +ulong get_diskjoin_largesidelimit(THD* thd) +{ + if(get_use_legacy_sysvars(thd)) + return ( thd == NULL ) ? 0 : thd->variables.infinidb_diskjoin_largesidelimit; + else + return ( thd == NULL ) ? 0 : THDVAR(thd, diskjoin_largesidelimit); +} +void set_diskjoin_largesidelimit(THD* thd, ulong value) +{ + if(get_use_legacy_sysvars(thd)) + thd->variables.infinidb_diskjoin_largesidelimit = value; + else + THDVAR(thd, diskjoin_largesidelimit) = value; +} + +ulong get_diskjoin_bucketsize(THD* thd) +{ + if(get_use_legacy_sysvars(thd)) + return ( thd == NULL ) ? 0 : thd->variables.infinidb_diskjoin_bucketsize; + else + return ( thd == NULL ) ? 0 : THDVAR(thd, diskjoin_bucketsize); +} +void set_diskjoin_bucketsize(THD* thd, ulong value) +{ + if(get_use_legacy_sysvars(thd)) + thd->variables.infinidb_diskjoin_bucketsize = value; + else + THDVAR(thd, diskjoin_bucketsize) = value; +} + +ulong get_um_mem_limit(THD* thd) +{ + if(get_use_legacy_sysvars(thd)) + return ( thd == NULL ) ? 0 : thd->variables.infinidb_um_mem_limit; + else + return ( thd == NULL ) ? 0 : THDVAR(thd, um_mem_limit); +} +void set_um_mem_limit(THD* thd, ulong value) +{ + if(get_use_legacy_sysvars(thd)) + thd->variables.infinidb_um_mem_limit = value; + else + THDVAR(thd, um_mem_limit) = value; +} + +bool get_varbin_always_hex(THD* thd) +{ + if(get_use_legacy_sysvars(thd)) + return ( thd == NULL ) ? false : thd->variables.infinidb_varbin_always_hex; + else + return ( thd == NULL ) ? false : THDVAR(thd, varbin_always_hex); +} +void set_varbin_always_hex(THD* thd, bool value) +{ + if(get_use_legacy_sysvars(thd)) + thd->variables.infinidb_varbin_always_hex = value; + else + THDVAR(thd, varbin_always_hex) = value; +} + +bool get_double_for_decimal_math(THD* thd) +{ + if(get_use_legacy_sysvars(thd)) + return ( thd == NULL ) ? false : thd->variables.infinidb_double_for_decimal_math; + else + return ( thd == NULL ) ? false : THDVAR(thd, double_for_decimal_math); +} +void set_double_for_decimal_math(THD* thd, bool value) +{ + if(get_use_legacy_sysvars(thd)) + thd->variables.infinidb_double_for_decimal_math = value; + else + THDVAR(thd, double_for_decimal_math) = value; +} + +ulong get_local_query(THD* thd) +{ + if(get_use_legacy_sysvars(thd)) + return ( thd == NULL ) ? 0 : thd->variables.infinidb_local_query; + else + return ( thd == NULL ) ? 0 : THDVAR(thd, local_query); +} +void set_local_query(THD* thd, ulong value) +{ + if(get_use_legacy_sysvars(thd)) + thd->variables.infinidb_local_query = value; + else + THDVAR(thd, local_query) = value; +} + +bool get_use_import_for_batchinsert(THD* thd) +{ + if(get_use_legacy_sysvars(thd)) + return ( thd == NULL ) ? false : thd->variables.infinidb_use_import_for_batchinsert; + else + return ( thd == NULL ) ? false : THDVAR(thd, use_import_for_batchinsert); +} +void set_use_import_for_batchinsert(THD* thd, bool value) +{ + if(get_use_legacy_sysvars(thd)) + thd->variables.infinidb_use_import_for_batchinsert = value; + else + THDVAR(thd, use_import_for_batchinsert) = value; +} + +ulong get_import_for_batchinsert_delimiter(THD* thd) +{ + if(get_use_legacy_sysvars(thd)) + return ( thd == NULL ) ? 0 : thd->variables.infinidb_import_for_batchinsert_delimiter; + else + return ( thd == NULL ) ? 0 : THDVAR(thd, import_for_batchinsert_delimiter); +} +void set_import_for_batchinsert_delimiter(THD* thd, ulong value) +{ + if(get_use_legacy_sysvars(thd)) + thd->variables.infinidb_import_for_batchinsert_delimiter = value; + else + THDVAR(thd, import_for_batchinsert_delimiter) = value; +} + +ulong get_import_for_batchinsert_enclosed_by(THD* thd) +{ + if(get_use_legacy_sysvars(thd)) + return ( thd == NULL ) ? 0 : thd->variables.infinidb_import_for_batchinsert_enclosed_by; + else + return ( thd == NULL ) ? 0 : THDVAR(thd, import_for_batchinsert_enclosed_by); +} +void set_import_for_batchinsert_enclosed_by(THD* thd, ulong value) +{ + if(get_use_legacy_sysvars(thd)) + thd->variables.infinidb_import_for_batchinsert_enclosed_by = value; + else + THDVAR(thd, import_for_batchinsert_enclosed_by) = value; +} diff --git a/dbcon/mysql/ha_mcs_sysvars.h b/dbcon/mysql/ha_mcs_sysvars.h new file mode 100644 index 000000000..eae31b0ea --- /dev/null +++ b/dbcon/mysql/ha_mcs_sysvars.h @@ -0,0 +1,91 @@ +/* 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. */ + +#ifndef MCS_SYSVARS_H__ +#define MCS_SYSVARS_H__ + +#include +#include "idb_mysql.h" + +extern st_mysql_sys_var* mcs_system_variables[]; + +// compression_type +enum mcs_compression_type_t { + NO_COMPRESSION = 0, + SNAPPY = 2 +}; + +// simple setters/getters +const char* get_original_query(THD* thd); +void set_original_query(THD* thd, char* query); + +mcs_compression_type_t get_compression_type(THD* thd); +void set_compression_type(THD* thd, ulong value); + +void* get_fe_conn_info_ptr(THD* thd = NULL); +void set_fe_conn_info_ptr(void* ptr, THD* thd = NULL); + +bool get_use_legacy_sysvars(THD* thd); +void set_use_legacy_sysvars(THD* thd, bool value); + +bool get_use_decimal_scale(THD* thd); +void set_use_decimal_scale(THD* thd, bool value); + +ulong get_decimal_scale(THD* thd); +void set_decimal_scale(THD* thd, ulong value); + +bool get_ordered_only(THD* thd); +void set_ordered_only(THD* thd, bool value); + +ulong get_string_scan_threshold(THD* thd); +void set_string_scan_threshold(THD* thd, ulong value); + +ulong get_stringtable_threshold(THD* thd); +void set_stringtable_threshold(THD* thd, ulong value); + +ulong get_diskjoin_smallsidelimit(THD* thd); +void set_diskjoin_smallsidelimit(THD* thd, ulong value); + +ulong get_diskjoin_largesidelimit(THD* thd); +void set_diskjoin_largesidelimit(THD* thd, ulong value); + +ulong get_diskjoin_bucketsize(THD* thd); +void set_diskjoin_bucketsize(THD* thd, ulong value); + +ulong get_um_mem_limit(THD* thd); +void set_um_mem_limit(THD* thd, ulong value); + +bool get_varbin_always_hex(THD* thd); +void set_varbin_always_hex(THD* thd, bool value); + +bool get_double_for_decimal_math(THD* thd); +void set_double_for_decimal_math(THD* thd, bool value); + +ulong get_local_query(THD* thd); +void set_local_query(THD* thd, ulong value); + +bool get_use_import_for_batchinsert(THD* thd); +void set_use_import_for_batchinsert(THD* thd, bool value); + +ulong get_import_for_batchinsert_delimiter(THD* thd); +void set_import_for_batchinsert_delimiter(THD* thd, ulong value); + +ulong get_import_for_batchinsert_enclosed_by(THD* thd); +void set_import_for_batchinsert_enclosed_by(THD* thd, ulong value); + +#endif diff --git a/dbcon/mysql/ha_pseudocolumn.cpp b/dbcon/mysql/ha_pseudocolumn.cpp index 410b0615c..c87196e00 100644 --- a/dbcon/mysql/ha_pseudocolumn.cpp +++ b/dbcon/mysql/ha_pseudocolumn.cpp @@ -20,7 +20,7 @@ using namespace execplan; #include "functor_str.h" #include "ha_calpont_impl_if.h" -#include "mcs_sysvars.h" +#include "ha_mcs_sysvars.h" using namespace cal_impl_if; namespace diff --git a/dbcon/mysql/ha_window_function.cpp b/dbcon/mysql/ha_window_function.cpp index 4033d022d..84c082bee 100644 --- a/dbcon/mysql/ha_window_function.cpp +++ b/dbcon/mysql/ha_window_function.cpp @@ -28,7 +28,7 @@ using namespace std; #include "idb_mysql.h" #include "ha_calpont_impl_if.h" -#include "mcs_sysvars.h" +#include "ha_mcs_sysvars.h" #include "arithmeticcolumn.h" #include "arithmeticoperator.h" @@ -903,7 +903,7 @@ ReturnedColumn* buildWindowFunctionColumn(Item* item, gp_walk_info& gwi, bool& n { ac->resultType(colType_MysqlToIDB(item_sum)); // bug5736. Make the result type double for some window functions when - // infinidb_double_for_decimal_math is set. + // plugin variable double_for_decimal_math is set. ac->adjustResultType(); } diff --git a/dbcon/mysql/mcs_sysvars.cpp b/dbcon/mysql/mcs_sysvars.cpp deleted file mode 100644 index 106559a0a..000000000 --- a/dbcon/mysql/mcs_sysvars.cpp +++ /dev/null @@ -1,106 +0,0 @@ -/* 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. */ - -#include -#include "idb_mysql.h" -#include "mcs_sysvars.h" - -const char* mcs_compression_type_names[] = { - "NO_COMPRESSION", - "SNAPPY", - NullS -}; - -static TYPELIB mcs_compression_type_names_lib = { - array_elements(mcs_compression_type_names) - 1, - "mcs_compression_type_names", - mcs_compression_type_names, - NULL -}; - -// compression type -static MYSQL_THDVAR_ENUM( - compression_type, - PLUGIN_VAR_RQCMDARG, - "Controls compression type for create tables. Possible values are: " - "NO_COMPRESSION segment files aren't compressed; " - "SNAPPY segment files are Snappy compressed (default);", - NULL, - NULL, - SNAPPY, - &mcs_compression_type_names_lib); - -// original query -static MYSQL_THDVAR_STR( - original_query, /* name */ - PLUGIN_VAR_MEMALLOC | - PLUGIN_VAR_RQCMDARG, - "Original query text", /* comment */ - NULL, /* check */ - NULL, /* update */ - NULL /* def */ -); - -// fe_conn_info pointer -static MYSQL_THDVAR_ULONGLONG( - fe_conn_info_ptr, - PLUGIN_VAR_NOSYSVAR | PLUGIN_VAR_NOCMDOPT, - "FrontEnd connection structure pointer. For internal usage.", - NULL, - NULL, - 0, - 0, - ~0U, - 1 -); - -st_mysql_sys_var* mcs_system_variables[] = -{ - MYSQL_SYSVAR(compression_type), - MYSQL_SYSVAR(original_query), - MYSQL_SYSVAR(fe_conn_info_ptr), - NULL -}; - -const char* get_original_query(THD* thd) { - return THDVAR(thd, original_query); -} - -void set_original_query(THD* thd, char* query) { - THDVAR(thd, original_query) = query; -} - -void* get_fe_conn_info_ptr() -{ - return ( current_thd == NULL ) ? NULL : - (void*)THDVAR(current_thd, fe_conn_info_ptr); -} - -void set_fe_conn_info_ptr(void* ptr) -{ - if ( current_thd == NULL ) - { - return; - } - - THDVAR(current_thd, fe_conn_info_ptr) = (uint64_t)(ptr); -} - -mcs_compression_type_t get_compression_type(THD* thd) { - return (mcs_compression_type_t) THDVAR(thd, compression_type); -} diff --git a/dbcon/mysql/mcs_sysvars.h b/dbcon/mysql/mcs_sysvars.h deleted file mode 100644 index 8e2436db6..000000000 --- a/dbcon/mysql/mcs_sysvars.h +++ /dev/null @@ -1,59 +0,0 @@ -/* 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. */ - -#ifndef MCS_SYSVARS_H__ -#define MCS_SYSVARS_H__ - -#include -#include "idb_mysql.h" - -extern st_mysql_sys_var* mcs_system_variables[]; - -/* MCOL-1101 Remove before release -enum mcs_handler_types_t -{ - SELECT, - GROUP_BY, - LEGACY -}; - -struct mcs_handler_info -{ - mcs_handler_info() : hndl_ptr(NULL), hndl_type(LEGACY) { }; - mcs_handler_info(mcs_handler_types_t type) : hndl_ptr(NULL), hndl_type(type) { }; - mcs_handler_info(void* ptr, mcs_handler_types_t type) : hndl_ptr(ptr), hndl_type(type) { }; - ~mcs_handler_info() { }; - void* hndl_ptr; - mcs_handler_types_t hndl_type; -}; -*/ -// compression_type -enum mcs_compression_type_t { - NO_COMPRESSION = 0, - SNAPPY = 2 -}; - -// simple setters/getters -const char* get_original_query(THD* thd); -void set_original_query(THD* thd, char* query); -mcs_compression_type_t get_compression_type(THD* thd); - -void* get_fe_conn_info_ptr(); -void set_fe_conn_info_ptr(void* ptr); - -#endif From a95e7bf162e2ec03d5415428e9aa4c9472dff1d7 Mon Sep 17 00:00:00 2001 From: Andrew Hutchings Date: Mon, 18 Feb 2019 16:01:32 +0000 Subject: [PATCH 16/38] Fix VERSION file --- VERSION | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/VERSION b/VERSION index ebe283cd3..f5d54e457 100644 --- a/VERSION +++ b/VERSION @@ -1,4 +1,4 @@ COLUMNSTORE_VERSION_MAJOR=1 -COLUMNSTORE_VERSION_MINOR=2 -COLUMNSTORE_VERSION_PATCH=3 +COLUMNSTORE_VERSION_MINOR=3 +COLUMNSTORE_VERSION_PATCH=0 COLUMNSTORE_VERSION_RELEASE=1 From eef813982c9097c67918cfca75c708f7a2ccc5e1 Mon Sep 17 00:00:00 2001 From: Roman Nozdrin Date: Thu, 14 Feb 2019 13:58:48 +0300 Subject: [PATCH 17/38] MCOL-2165 This disables correlated subqueries processing in group_by_nahdler handing the query back to the server. --- dbcon/mysql/ha_calpont.cpp | 68 +++++++++++++++++++++++++------------- 1 file changed, 45 insertions(+), 23 deletions(-) diff --git a/dbcon/mysql/ha_calpont.cpp b/dbcon/mysql/ha_calpont.cpp index 6ea5f85a6..da7bd8085 100644 --- a/dbcon/mysql/ha_calpont.cpp +++ b/dbcon/mysql/ha_calpont.cpp @@ -1190,7 +1190,7 @@ void check_walk(const Item* item, void* arg) break; } - case Item::EXPR_CACHE_ITEM: // IN + correlated subquery + case Item::EXPR_CACHE_ITEM: // IN + correlated subquery { const Item_cache_wrapper* icw = static_cast(item); if ( icw->get_orig_item()->type() == Item::FUNC_ITEM ) @@ -1231,11 +1231,14 @@ void check_walk(const Item* item, void* arg) * logical OR in the filter predicates * Impossible WHERE * Impossible HAVING - * Valid queries with the last two crashes the server if processed. + * and there is either GROUP BY or aggregation function + * exists at the top level. + * Valid queries with the last two crashes the server if + * processed. * Details are in server/sql/group_by_handler.h * PARAMETERS: - * thd - THD pointer. - * query - Query structure, that describes the pushdowned query. + * thd - THD pointer + * query - Query structure LFM in group_by_handler.h * RETURN: * group_by_handler if success * NULL in other case @@ -1254,29 +1257,48 @@ create_calpont_group_by_handler(THD* thd, Query* query) && ( query->group_by || select_lex->with_sum_func ) ) { bool unsupported_feature = false; - // Impossible HAVING or WHERE - if ( ( query->having && select_lex->having_value == Item::COND_FALSE ) - || ( select_lex->cond_count > 0 - && select_lex->cond_value == Item::COND_FALSE ) ) + // revisit SELECT_LEX for all units + for(TABLE_LIST* tl = query->from; !unsupported_feature && tl; tl = tl->next_global) { - unsupported_feature = true; - } + select_lex = tl->select_lex; + // Correlation subquery. Comming soon so fail on this yet. + unsupported_feature = select_lex->is_correlated; - // Unsupported conditions check. - if ( !unsupported_feature ) - { - JOIN *join = select_lex->join; - Item_cond *icp = 0; - - if (join != 0) - icp = reinterpret_cast(join->conds); - - if ( unsupported_feature == false - && icp ) + // Impossible HAVING or WHERE + if ( ( !unsupported_feature && query->having && select_lex->having_value == Item::COND_FALSE ) + || ( select_lex->cond_count > 0 + && select_lex->cond_value == Item::COND_FALSE ) ) { - icp->traverse_cond(check_walk, &unsupported_feature, Item::POSTFIX); + unsupported_feature = true; } - } + + // Unsupported JOIN conditions + if ( !unsupported_feature ) + { + JOIN *join = select_lex->join; + Item_cond *icp = 0; + + if (join != 0) + icp = reinterpret_cast(join->conds); + + if ( unsupported_feature == false + && icp ) + { + icp->traverse_cond(check_walk, &unsupported_feature, Item::POSTFIX); + } + + // Optimizer could move some join conditions into where + if (select_lex->where != 0) + icp = reinterpret_cast(select_lex->where); + + if ( unsupported_feature == false + && icp ) + { + icp->traverse_cond(check_walk, &unsupported_feature, Item::POSTFIX); + } + + } + } // unsupported features check ends here if ( !unsupported_feature ) { From a2aa4b84796c7f1cb327f3ad469066e3cb42127b Mon Sep 17 00:00:00 2001 From: David Hall Date: Mon, 25 Feb 2019 14:54:46 -0600 Subject: [PATCH 18/38] MCOL-1822 Intermediate checkin. DISTINCT not working. --- dbcon/execplan/aggregatecolumn.h | 9 + dbcon/execplan/arithmeticcolumn.h | 5 + dbcon/execplan/constantcolumn.cpp | 1 - dbcon/execplan/functioncolumn.h | 4 + dbcon/execplan/operator.h | 4 + dbcon/execplan/parsetree.h | 8 + dbcon/execplan/predicateoperator.cpp | 10 + dbcon/execplan/predicateoperator.h | 29 + dbcon/execplan/treenode.h | 98 ++- dbcon/execplan/windowfunctioncolumn.h | 6 + dbcon/joblist/batchprimitiveprocessor-jl.cpp | 56 +- dbcon/joblist/jlf_common.h | 4 - dbcon/joblist/jlf_subquery.cpp | 5 + dbcon/joblist/jlf_tuplejoblist.cpp | 19 +- dbcon/joblist/joblistfactory.cpp | 49 +- dbcon/joblist/joblisttypes.h | 2 +- dbcon/joblist/primitivestep.h | 4 +- dbcon/joblist/rowestimator.cpp | 2 +- dbcon/joblist/rowestimator.h | 2 +- dbcon/joblist/subquerytransformer.cpp | 11 +- dbcon/joblist/tupleaggregatestep.cpp | 389 +++------ dbcon/mysql/ha_calpont_execplan.cpp | 79 +- dbcon/mysql/ha_calpont_impl.cpp | 69 +- exemgr/main.cpp | 3 +- utils/common/nullvaluemanip.cpp | 5 +- utils/dataconvert/dataconvert.cpp | 3 +- utils/funcexp/func_abs.cpp | 8 + utils/funcexp/func_case.cpp | 47 ++ utils/funcexp/func_cast.cpp | 94 +++ utils/funcexp/func_ceil.cpp | 64 ++ utils/funcexp/func_coalesce.cpp | 25 + utils/funcexp/func_exp.cpp | 32 + utils/funcexp/func_floor.cpp | 60 ++ utils/funcexp/func_from_unixtime.cpp | 8 + utils/funcexp/func_greatest.cpp | 20 + utils/funcexp/func_if.cpp | 18 + utils/funcexp/func_ifnull.cpp | 19 + utils/funcexp/func_in.cpp | 21 + utils/funcexp/func_isnull.cpp | 4 + utils/funcexp/func_least.cpp | 20 + utils/funcexp/func_math.cpp | 20 + utils/funcexp/func_mod.cpp | 132 +++ utils/funcexp/func_nullif.cpp | 103 +++ utils/funcexp/func_pow.cpp | 38 + utils/funcexp/func_round.cpp | 97 ++- utils/funcexp/func_truncate.cpp | 82 +- utils/funcexp/funchelpers.h | 10 + utils/funcexp/functor.cpp | 6 + utils/funcexp/functor.h | 12 + utils/funcexp/functor_all.h | 40 + utils/funcexp/functor_bool.h | 8 + utils/funcexp/functor_dtm.h | 23 + utils/funcexp/functor_export.h | 8 + utils/funcexp/functor_int.h | 8 + utils/funcexp/functor_real.h | 54 +- utils/funcexp/functor_str.h | 2 +- utils/joiner/tuplejoiner.cpp | 478 +++++++++-- utils/joiner/tuplejoiner.h | 49 +- utils/rowgroup/rowaggregation.cpp | 827 ++++++++----------- utils/rowgroup/rowaggregation.h | 12 +- utils/udfsdk/udfsdk.cpp | 16 + utils/udfsdk/udfsdk.h | 16 + utils/windowfunction/frameboundrange.cpp | 14 + utils/windowfunction/wf_nth_value.cpp | 6 + utils/windowfunction/wf_percentile.cpp | 7 +- utils/windowfunction/wf_udaf.cpp | 15 + utils/windowfunction/windowfunctiontype.cpp | 10 + 67 files changed, 2391 insertions(+), 1018 deletions(-) diff --git a/dbcon/execplan/aggregatecolumn.h b/dbcon/execplan/aggregatecolumn.h index 07bbab0b6..443be7eff 100644 --- a/dbcon/execplan/aggregatecolumn.h +++ b/dbcon/execplan/aggregatecolumn.h @@ -384,6 +384,15 @@ public: return TreeNode::getDoubleVal(); } + /** + * F&E + */ + virtual long double getLongDoubleVal(rowgroup::Row& row, bool& isNull) + { + evaluate(row, isNull); + return TreeNode::getLongDoubleVal(); + } + /** * F&E */ diff --git a/dbcon/execplan/arithmeticcolumn.h b/dbcon/execplan/arithmeticcolumn.h index 191416fbf..b4e6ab3a9 100644 --- a/dbcon/execplan/arithmeticcolumn.h +++ b/dbcon/execplan/arithmeticcolumn.h @@ -233,6 +233,11 @@ public: return fExpression->getDoubleVal(row, isNull); } + virtual long double getLongDoubleVal(rowgroup::Row& row, bool& isNull) + { + return fExpression->getLongDoubleVal(row, isNull); + } + virtual IDB_Decimal getDecimalVal(rowgroup::Row& row, bool& isNull) { return fExpression->getDecimalVal(row, isNull); diff --git a/dbcon/execplan/constantcolumn.cpp b/dbcon/execplan/constantcolumn.cpp index 60d99580d..ea8bb913c 100644 --- a/dbcon/execplan/constantcolumn.cpp +++ b/dbcon/execplan/constantcolumn.cpp @@ -316,7 +316,6 @@ void ConstantColumn::serialize(messageqcpp::ByteStream& b) const b << (uint64_t)fResult.decimalVal.value; b << (uint8_t)fResult.decimalVal.scale; b << (uint8_t)fResult.decimalVal.precision; - b << fResult.longDoubleVal; } void ConstantColumn::unserialize(messageqcpp::ByteStream& b) diff --git a/dbcon/execplan/functioncolumn.h b/dbcon/execplan/functioncolumn.h index 5a099d1d2..59ed92146 100644 --- a/dbcon/execplan/functioncolumn.h +++ b/dbcon/execplan/functioncolumn.h @@ -225,6 +225,10 @@ public: { return fFunctor->getDoubleVal(row, fFunctionParms, isNull, fOperationType); } + virtual long double getLongDoubleVal(rowgroup::Row& row, bool& isNull) + { + return fFunctor->getLongDoubleVal(row, fFunctionParms, isNull, fOperationType); + } virtual IDB_Decimal getDecimalVal(rowgroup::Row& row, bool& isNull) { IDB_Decimal decimal = fFunctor->getDecimalVal(row, fFunctionParms, isNull, fOperationType); diff --git a/dbcon/execplan/operator.h b/dbcon/execplan/operator.h index 1a33dcb94..a253c6593 100644 --- a/dbcon/execplan/operator.h +++ b/dbcon/execplan/operator.h @@ -178,6 +178,10 @@ public: { return fResult.doubleVal; } + virtual long double getLongDoubleVal(rowgroup::Row& row, bool& isNull, ParseTree* lop, ParseTree* rop) + { + return fResult.longDoubleVal; + } virtual IDB_Decimal getDecimalVal(rowgroup::Row& row, bool& isNull, ParseTree* lop, ParseTree* rop) { return fResult.decimalVal; diff --git a/dbcon/execplan/parsetree.h b/dbcon/execplan/parsetree.h index 2ad41b6bb..112649f81 100644 --- a/dbcon/execplan/parsetree.h +++ b/dbcon/execplan/parsetree.h @@ -250,6 +250,14 @@ public: return fData->getDoubleVal(row, isNull); } + inline long double getLongDoubleVal(rowgroup::Row& row, bool& isNull) + { + if (fLeft && fRight) + return (reinterpret_cast(fData))->getLongDoubleVal(row, isNull, fLeft, fRight); + else + return fData->getLongDoubleVal(row, isNull); + } + inline IDB_Decimal getDecimalVal(rowgroup::Row& row, bool& isNull) { if (fLeft && fRight) diff --git a/dbcon/execplan/predicateoperator.cpp b/dbcon/execplan/predicateoperator.cpp index 166a8edc0..6f84d2353 100644 --- a/dbcon/execplan/predicateoperator.cpp +++ b/dbcon/execplan/predicateoperator.cpp @@ -317,6 +317,10 @@ void PredicateOperator::setOpType(Type& l, Type& r) fOperationType.colWidth = 8; break; + case execplan::CalpontSystemCatalog::LONGDOUBLE: + fOperationType.colDataType = execplan::CalpontSystemCatalog::LONGDOUBLE; + fOperationType.colWidth = 16; + break; default: fOperationType.colDataType = execplan::CalpontSystemCatalog::DOUBLE; fOperationType.colWidth = 8; @@ -370,6 +374,12 @@ void PredicateOperator::setOpType(Type& l, Type& r) fOperationType.colWidth = 255; } } + else if (l.colDataType == execplan::CalpontSystemCatalog::LONGDOUBLE || + r.colDataType == execplan::CalpontSystemCatalog::LONGDOUBLE) + { + fOperationType.colDataType = execplan::CalpontSystemCatalog::LONGDOUBLE; + fOperationType.colWidth = 16; + } else { fOperationType.colDataType = execplan::CalpontSystemCatalog::DOUBLE; diff --git a/dbcon/execplan/predicateoperator.h b/dbcon/execplan/predicateoperator.h index 6253d9389..30251c510 100644 --- a/dbcon/execplan/predicateoperator.h +++ b/dbcon/execplan/predicateoperator.h @@ -256,6 +256,35 @@ inline bool PredicateOperator::getBoolVal(rowgroup::Row& row, bool& isNull, Retu return numericCompare(val1, rop->getDoubleVal(row, isNull)) && !isNull; } + case execplan::CalpontSystemCatalog::LONGDOUBLE: + { + if (fOp == OP_ISNULL) + { + lop->getLongDoubleVal(row, isNull); + bool ret = isNull; + isNull = false; + return ret; + } + + if (fOp == OP_ISNOTNULL) + { + lop->getLongDoubleVal(row, isNull); + bool ret = isNull; + isNull = false; + return !ret; + } + + if (isNull) + return false; + + long double val1 = lop->getLongDoubleVal(row, isNull); + + if (isNull) + return false; + + return numericCompare(val1, rop->getLongDoubleVal(row, isNull)) && !isNull; + } + case execplan::CalpontSystemCatalog::DECIMAL: case execplan::CalpontSystemCatalog::UDECIMAL: { diff --git a/dbcon/execplan/treenode.h b/dbcon/execplan/treenode.h index 126d7ee6c..0fc836dad 100644 --- a/dbcon/execplan/treenode.h +++ b/dbcon/execplan/treenode.h @@ -239,7 +239,7 @@ inline std::string removeTrailing0(char* val, uint32_t length) struct Result { Result(): intVal(0), uintVal(0), origIntVal(0), dummy(0), - doubleVal(0), floatVal(0), boolVal(false), + doubleVal(0), longDoubleVal(0), floatVal(0), boolVal(false), strVal(""), decimalVal(IDB_Decimal(0, 0, 0)), valueConverted(false) {} int64_t intVal; @@ -409,6 +409,7 @@ public: inline uint64_t getUintVal(); inline float getFloatVal(); inline double getDoubleVal(); + inline long double getLongDoubleVal(); inline IDB_Decimal getDecimalVal(); inline int32_t getDateIntVal(); inline int64_t getDatetimeIntVal(); @@ -957,6 +958,9 @@ inline double TreeNode::getDoubleVal() case CalpontSystemCatalog::UDOUBLE: return fResult.doubleVal; + case CalpontSystemCatalog::LONGDOUBLE: + return (double)fResult.longDoubleVal; + case CalpontSystemCatalog::DECIMAL: case CalpontSystemCatalog::UDECIMAL: { @@ -975,6 +979,74 @@ inline double TreeNode::getDoubleVal() return fResult.doubleVal; } +inline long double TreeNode::getLongDoubleVal() +{ + switch (fResultType.colDataType) + { + case CalpontSystemCatalog::CHAR: + if (fResultType.colWidth <= 8) + return strtold((char*)(&fResult.origIntVal), NULL); + + return strtold(fResult.strVal.c_str(), NULL); + + case CalpontSystemCatalog::VARCHAR: + if (fResultType.colWidth <= 7) + return strtold((char*)(&fResult.origIntVal), NULL); + + return strtold(fResult.strVal.c_str(), NULL); + + //FIXME: ??? + case CalpontSystemCatalog::VARBINARY: + case CalpontSystemCatalog::BLOB: + case CalpontSystemCatalog::TEXT: + if (fResultType.colWidth <= 7) + return strtold((char*)(&fResult.origIntVal), NULL); + + return strtold(fResult.strVal.c_str(), NULL); + + case CalpontSystemCatalog::BIGINT: + case CalpontSystemCatalog::TINYINT: + case CalpontSystemCatalog::SMALLINT: + case CalpontSystemCatalog::MEDINT: + case CalpontSystemCatalog::INT: + return (double)fResult.intVal; + + case CalpontSystemCatalog::UBIGINT: + case CalpontSystemCatalog::UTINYINT: + case CalpontSystemCatalog::USMALLINT: + case CalpontSystemCatalog::UMEDINT: + case CalpontSystemCatalog::UINT: + return (long double)fResult.uintVal; + + case CalpontSystemCatalog::FLOAT: + case CalpontSystemCatalog::UFLOAT: + return (long double)fResult.floatVal; + + case CalpontSystemCatalog::DOUBLE: + case CalpontSystemCatalog::UDOUBLE: + return (long double)fResult.doubleVal; + + case CalpontSystemCatalog::LONGDOUBLE: + return (long double)fResult.longDoubleVal; + + case CalpontSystemCatalog::DECIMAL: + case CalpontSystemCatalog::UDECIMAL: + { + // this may not be accurate. if this is problematic, change to pre-calculated power array. + return (long double)(fResult.decimalVal.value / pow((long double)10, fResult.decimalVal.scale)); + } + + case CalpontSystemCatalog::DATE: + case CalpontSystemCatalog::DATETIME: + case CalpontSystemCatalog::TIME: + return (long double)fResult.intVal; + + default: + throw logging::InvalidConversionExcept("TreeNode::getDoubleVal: Invalid conversion."); + } + + return fResult.doubleVal; +} inline IDB_Decimal TreeNode::getDecimalVal() { switch (fResultType.colDataType) @@ -993,7 +1065,7 @@ inline IDB_Decimal TreeNode::getDecimalVal() case CalpontSystemCatalog::INT: case CalpontSystemCatalog::SMALLINT: case CalpontSystemCatalog::TINYINT: - fResult.decimalVal.value = (int64_t)(fResult.intVal * pow((double)10, fResultType.scale)); + fResult.decimalVal.value = (int64_t)(fResult.intVal * pow((double)10.0, fResultType.scale)); fResult.decimalVal.scale = fResultType.scale; fResult.decimalVal.precision = fResultType.precision; break; @@ -1003,11 +1075,28 @@ inline IDB_Decimal TreeNode::getDecimalVal() case CalpontSystemCatalog::UINT: case CalpontSystemCatalog::USMALLINT: case CalpontSystemCatalog::UTINYINT: - fResult.decimalVal.value = (int64_t)(fResult.uintVal * pow((double)10, fResultType.scale)); + fResult.decimalVal.value = (int64_t)(fResult.uintVal * pow((double)10.0, fResultType.scale)); fResult.decimalVal.scale = fResultType.scale; fResult.decimalVal.precision = fResultType.precision; break; + case CalpontSystemCatalog::LONGDOUBLE: + { + long double dlScaled = fResult.longDoubleVal; + if (fResultType.scale > 0) + { + dlScaled= fResult.longDoubleVal * pow((double)10.0, fResultType.scale); + } + if (dlScaled > (double)MAX_BIGINT) + { + throw logging::InvalidConversionExcept("TreeNode::getDecimalVal: decimal overflow."); + } + fResult.decimalVal.value = (int64_t)roundl((fResult.longDoubleVal * pow((double)10.0, fResultType.scale))); + fResult.decimalVal.scale = fResultType.scale; + fResult.decimalVal.precision = fResultType.precision; + } + break; + case CalpontSystemCatalog::DATE: throw logging::InvalidConversionExcept("TreeNode::getDecimalVal: Invalid conversion from date."); @@ -1029,9 +1118,6 @@ inline IDB_Decimal TreeNode::getDecimalVal() case CalpontSystemCatalog::UDOUBLE: throw logging::InvalidConversionExcept("TreeNode::getDecimalVal: non-support conversion from double unsigned"); - case CalpontSystemCatalog::LONGDOUBLE: - throw logging::InvalidConversionExcept("TreeNode::getDecimalVal: non-support conversion from long double"); - case CalpontSystemCatalog::DECIMAL: case CalpontSystemCatalog::UDECIMAL: return fResult.decimalVal; diff --git a/dbcon/execplan/windowfunctioncolumn.h b/dbcon/execplan/windowfunctioncolumn.h index 47c82b805..732d956cd 100644 --- a/dbcon/execplan/windowfunctioncolumn.h +++ b/dbcon/execplan/windowfunctioncolumn.h @@ -202,6 +202,12 @@ public: return TreeNode::getDoubleVal(); } + virtual long double getLongDoubleVal(rowgroup::Row& row, bool& isNull) + { + evaluate(row, isNull); + return TreeNode::getLongDoubleVal(); + } + virtual IDB_Decimal getDecimalVal(rowgroup::Row& row, bool& isNull) { evaluate(row, isNull); diff --git a/dbcon/joblist/batchprimitiveprocessor-jl.cpp b/dbcon/joblist/batchprimitiveprocessor-jl.cpp index 8adf80e12..e21f7cca4 100644 --- a/dbcon/joblist/batchprimitiveprocessor-jl.cpp +++ b/dbcon/joblist/batchprimitiveprocessor-jl.cpp @@ -1411,12 +1411,19 @@ bool BatchPrimitiveProcessorJL::nextTupleJoinerMsg(ByteStream& bs) } } - bs << (uint8_t) isNull; - if (!isNull) { tlData = makeTypelessKey(r, smallSideKeys[joinerNum], - tlKeyLens[joinerNum], &fa); + tlKeyLens[joinerNum], &fa, + largeSideRG, tJoiners[joinerNum]->getLargeKeyColumns()); + if (tlData.len == 0) + { + isNull = true; + } + } + bs << (uint8_t) isNull; + if (!isNull) + { tlData.serialize(bs); bs << i; } @@ -1442,7 +1449,48 @@ bool BatchPrimitiveProcessorJL::nextTupleJoinerMsg(ByteStream& bs) { r.setPointer((*tSmallSide)[i]); - if (r.isUnsigned(smallKeyCol)) + if ( r.getColType(smallKeyCol)== CalpontSystemCatalog::LONGDOUBLE) + { + // Small side is a long double. Since CS can't store larger than DOUBLE, + // we need to convert to whatever type large side is -- double or int64 + long double smallkeyld = r.getLongDoubleField(smallKeyCol); + switch (largeSideRG.getColType(tJoiners[joinerNum]->getLargeKeyColumns()[0])) + { + case CalpontSystemCatalog::DOUBLE: + case CalpontSystemCatalog::UDOUBLE: + case CalpontSystemCatalog::FLOAT: + case CalpontSystemCatalog::UFLOAT: + { + if (smallkeyld > MAX_DOUBLE || smallkeyld < MIN_DOUBLE) + { + smallkey = joblist::UBIGINTEMPTYROW; + } + else + { + double d = (double)smallkeyld; + smallkey = *(int64_t*)&d; + } + break; + } + default: + { + if (r.isUnsigned(smallKeyCol) && smallkeyld > MAX_UBIGINT) + { + smallkey = joblist::UBIGINTEMPTYROW; + } + else if (smallkeyld > MAX_BIGINT || smallkeyld < MIN_BIGINT) + { + smallkey = joblist::UBIGINTEMPTYROW; + } + else + { + smallkey = (int64_t)smallkeyld; + } + break; + } + } + } + else if (r.isUnsigned(smallKeyCol)) smallkey = r.getUintField(smallKeyCol); else smallkey = r.getIntField(smallKeyCol); diff --git a/dbcon/joblist/jlf_common.h b/dbcon/joblist/jlf_common.h index 669a26b4b..2ae8018c8 100644 --- a/dbcon/joblist/jlf_common.h +++ b/dbcon/joblist/jlf_common.h @@ -277,10 +277,6 @@ struct JobInfo std::multimap cloneAggregateColMap; std::vector > aggEidIndexList; - // for AVG to support CNX_USE_DECIMAL_SCALE - // map - std::map scaleOfAvg; - // table pairs with incompatible join which is treated as expression std::map incompatibleJoinMap; diff --git a/dbcon/joblist/jlf_subquery.cpp b/dbcon/joblist/jlf_subquery.cpp index 1e4eaeeec..08592d314 100644 --- a/dbcon/joblist/jlf_subquery.cpp +++ b/dbcon/joblist/jlf_subquery.cpp @@ -125,6 +125,11 @@ void getColumnValue(ConstantColumn** cc, uint64_t i, const Row& row) *cc = new ConstantColumn(oss.str(), row.getDoubleField(i)); break; + case CalpontSystemCatalog::LONGDOUBLE: + oss << fixed << row.getLongDoubleField(i); + *cc = new ConstantColumn(oss.str(), row.getLongDoubleField(i)); + break; + case CalpontSystemCatalog::DATE: oss << dataconvert::DataConvert::dateToString(row.getUintField<4>(i)); *cc = new ConstantColumn(oss.str()); diff --git a/dbcon/joblist/jlf_tuplejoblist.cpp b/dbcon/joblist/jlf_tuplejoblist.cpp index b006eb90c..996f1369d 100644 --- a/dbcon/joblist/jlf_tuplejoblist.cpp +++ b/dbcon/joblist/jlf_tuplejoblist.cpp @@ -1454,7 +1454,10 @@ bool addFunctionJoin(vector& joinedTables, JobStepVector& joinSteps, TupleInfo ti1 = getTupleInfo(key1, jobInfo); TupleInfo ti2 = getTupleInfo(key2, jobInfo); - if (ti1.dtype == CalpontSystemCatalog::CHAR || ti1.dtype == CalpontSystemCatalog::VARCHAR || ti1.dtype == CalpontSystemCatalog::TEXT) + if (ti1.dtype == CalpontSystemCatalog::CHAR + || ti1.dtype == CalpontSystemCatalog::VARCHAR + || ti1.dtype == CalpontSystemCatalog::TEXT) +// || ti1.dtype == CalpontSystemCatalog::LONGDOUBLE) m1->second.fTypeless = m2->second.fTypeless = true; // ti2 is compatible else m1->second.fTypeless = m2->second.fTypeless = false; @@ -3623,9 +3626,21 @@ void associateTupleJobSteps(JobStepVector& querySteps, JobStepVector& projectSte TupleInfo ti2(getTupleInfo(key2, jobInfo)); if (ti1.width > 8 || ti2.width > 8) - m1->second.fTypeless = m2->second.fTypeless = true; + { + if (ti1.dtype == execplan::CalpontSystemCatalog::LONGDOUBLE + || ti2.dtype == execplan::CalpontSystemCatalog::LONGDOUBLE) + { + m1->second.fTypeless = m2->second.fTypeless = false; + } + else + { + m1->second.fTypeless = m2->second.fTypeless = true; + } + } else + { m1->second.fTypeless = m2->second.fTypeless = false; + } } else { diff --git a/dbcon/joblist/joblistfactory.cpp b/dbcon/joblist/joblistfactory.cpp index 04989e7b7..97bd6a53c 100644 --- a/dbcon/joblist/joblistfactory.cpp +++ b/dbcon/joblist/joblistfactory.cpp @@ -695,44 +695,13 @@ void updateAggregateColType(AggregateColumn* ac, const SRCP& srcp, int op, JobIn else if ((fc = dynamic_cast(srcp.get())) != NULL) ct = fc->resultType(); - if (op == AggregateColumn::SUM || op == AggregateColumn::DISTINCT_SUM) - { - if (ct.colDataType == CalpontSystemCatalog::TINYINT || - ct.colDataType == CalpontSystemCatalog::SMALLINT || - ct.colDataType == CalpontSystemCatalog::MEDINT || - ct.colDataType == CalpontSystemCatalog::INT || - ct.colDataType == CalpontSystemCatalog::BIGINT || - ct.colDataType == CalpontSystemCatalog::DECIMAL || - ct.colDataType == CalpontSystemCatalog::UDECIMAL) - { - ct.colWidth = sizeof(int64_t); - - if (ct.scale != 0) - ct.colDataType = CalpontSystemCatalog::DECIMAL; - else - ct.colDataType = CalpontSystemCatalog::BIGINT; - - ct.precision = 19; - } - - if (ct.colDataType == CalpontSystemCatalog::UTINYINT || - ct.colDataType == CalpontSystemCatalog::USMALLINT || - ct.colDataType == CalpontSystemCatalog::UMEDINT || - ct.colDataType == CalpontSystemCatalog::UINT || - ct.colDataType == CalpontSystemCatalog::UBIGINT) - { - ct.colWidth = sizeof(uint64_t); - ct.colDataType = CalpontSystemCatalog::UBIGINT; - ct.precision = 20; - } - } - else if (op == AggregateColumn::STDDEV_POP || op == AggregateColumn::STDDEV_SAMP || - op == AggregateColumn::VAR_POP || op == AggregateColumn::VAR_SAMP) + if (op == AggregateColumn::STDDEV_POP || op == AggregateColumn::STDDEV_SAMP || + op == AggregateColumn::VAR_POP || op == AggregateColumn::VAR_SAMP) { ct.colWidth = sizeof(double); ct.colDataType = CalpontSystemCatalog::DOUBLE; ct.scale = 0; - ct.precision = 0; + ct.precision = -1; } else if (op == AggregateColumn::UDAF) { @@ -1059,7 +1028,11 @@ const JobStepVector doAggProject(const CalpontSelectExecutionPlan* csep, JobInfo // Set the col type based on the single parm. // Changing col type based on a parm if multiple parms // doesn't really make sense. - updateAggregateColType(aggc, srcp, op, jobInfo); + if (op != AggregateColumn::SUM && op != AggregateColumn::DISTINCT_SUM && + op != AggregateColumn::AVG && op != AggregateColumn::DISTINCT_AVG) + { + updateAggregateColType(aggc, srcp, op, jobInfo); + } } aggCt = aggc->resultType(); @@ -1203,9 +1176,6 @@ const JobStepVector doAggProject(const CalpontSelectExecutionPlan* csep, JobInfo // remember the columns to be returned jobInfo.returnedColVec.push_back(make_pair(tupleKey, op)); - if (op == AggregateColumn::AVG || op == AggregateColumn::DISTINCT_AVG) - jobInfo.scaleOfAvg[tupleKey] = (ct.scale << 8) + aggCt.scale; - // bug 1499 distinct processing, save unique distinct columns if (doDistinct && (jobInfo.distinctColVec.end() == @@ -1354,9 +1324,6 @@ const JobStepVector doAggProject(const CalpontSelectExecutionPlan* csep, JobInfo // remember the columns to be returned jobInfo.returnedColVec.push_back(make_pair(tupleKey, op)); - if (op == AggregateColumn::AVG || op == AggregateColumn::DISTINCT_AVG) - jobInfo.scaleOfAvg[tupleKey] = (ct.scale << 8) + aggCt.scale; - // bug 1499 distinct processing, save unique distinct columns if (doDistinct && (jobInfo.distinctColVec.end() == diff --git a/dbcon/joblist/joblisttypes.h b/dbcon/joblist/joblisttypes.h index 2174f3002..82c8fe7ca 100644 --- a/dbcon/joblist/joblisttypes.h +++ b/dbcon/joblist/joblisttypes.h @@ -52,7 +52,7 @@ const uint32_t FLOATNULL = 0xFFAAAAAA; const uint32_t FLOATEMPTYROW = 0xFFAAAAAB; const uint64_t DOUBLENULL = 0xFFFAAAAAAAAAAAAAULL; const uint64_t DOUBLEEMPTYROW = 0xFFFAAAAAAAAAAAABULL; -const long double LONGDOUBLENULL = nanl(""); +const long double LONGDOUBLENULL = INFINITY; const uint32_t DATENULL = 0xFFFFFFFE; const uint32_t DATEEMPTYROW = 0xFFFFFFFF; diff --git a/dbcon/joblist/primitivestep.h b/dbcon/joblist/primitivestep.h index 8dd02e0e1..cc18cb43d 100644 --- a/dbcon/joblist/primitivestep.h +++ b/dbcon/joblist/primitivestep.h @@ -1410,7 +1410,7 @@ private: boost::mutex serializeJoinerMutex; boost::condition condvarWakeupProducer, condvar; - std::vector scanFlags; // use to keep track of which extents to eliminate from this step + std::vector scanFlags; // use to keep track of which extents to eliminate from this step bool BPPIsAllocated; uint32_t uniqueID; ResourceManager* fRm; @@ -1476,7 +1476,7 @@ private: * component and this new array as the runtime component. The final CP decision * is scanFlags & runtimeCP. */ - std::vector runtimeCPFlags; + std::vector runtimeCPFlags; /* semijoin vars */ rowgroup::RowGroup joinFERG; diff --git a/dbcon/joblist/rowestimator.cpp b/dbcon/joblist/rowestimator.cpp index e69c66d79..8ec09488c 100644 --- a/dbcon/joblist/rowestimator.cpp +++ b/dbcon/joblist/rowestimator.cpp @@ -455,7 +455,7 @@ float RowEstimator::estimateRowReturnFactor(const BRM::EMEntry& emEntry, // This function returns the estimated row count for the entire TupleBPS. It samples the last 20 (configurable) extents to // calculate the estimate. uint64_t RowEstimator::estimateRows(const vector& cpColVec, - const std::vector& scanFlags, + const std::vector& scanFlags, BRM::DBRM& dbrm, const execplan::CalpontSystemCatalog::OID oid) diff --git a/dbcon/joblist/rowestimator.h b/dbcon/joblist/rowestimator.h index 477bc2b30..01547c2d0 100644 --- a/dbcon/joblist/rowestimator.h +++ b/dbcon/joblist/rowestimator.h @@ -62,7 +62,7 @@ public: * */ uint64_t estimateRows(const std::vector& cpColVec, - const std::vector & scanFlags, + const std::vector & scanFlags, BRM::DBRM& dbrm, const execplan::CalpontSystemCatalog::OID oid); diff --git a/dbcon/joblist/subquerytransformer.cpp b/dbcon/joblist/subquerytransformer.cpp index c212c701d..fb8d0db55 100644 --- a/dbcon/joblist/subquerytransformer.cpp +++ b/dbcon/joblist/subquerytransformer.cpp @@ -230,8 +230,15 @@ SJSTEP& SubQueryTransformer::makeSubQueryStep(execplan::CalpontSelectExecutionPl ct.colDataType = row.getColTypes()[i]; ct.scale = row.getScale(i); - if (ct.scale != 0 && ct.precision != -1) - ct.colDataType = CalpontSystemCatalog::DECIMAL; + if (colDataTypeInRg != CalpontSystemCatalog::FLOAT && + colDataTypeInRg != CalpontSystemCatalog::UFLOAT && + colDataTypeInRg != CalpontSystemCatalog::DOUBLE && + colDataTypeInRg != CalpontSystemCatalog::UDOUBLE && + colDataTypeInRg != CalpontSystemCatalog::LONGDOUBLE) + { + if (ct.scale != 0 && ct.precision != -1) + ct.colDataType = CalpontSystemCatalog::DECIMAL; + } ct.precision = row.getPrecision(i); fVtable.columnType(ct, i); diff --git a/dbcon/joblist/tupleaggregatestep.cpp b/dbcon/joblist/tupleaggregatestep.cpp index 61768dfd3..689be0306 100644 --- a/dbcon/joblist/tupleaggregatestep.cpp +++ b/dbcon/joblist/tupleaggregatestep.cpp @@ -738,13 +738,13 @@ void TupleAggregateStep::configDeliveredRowGroup(const JobInfo& jobInfo) // correct the scale vector scale = fRowGroupOut.getScale(); - for (uint64_t i = 0; i < scale.size(); i++) - { +// for (uint64_t i = 0; i < scale.size(); i++) +// { // to support CNX_DECIMAL_SCALE the avg column's scale is coded with two scales: // fe's avg column scale << 8 + original column scale //if ((scale[i] & 0x0000FF00) > 0) - scale[i] = scale[i] & 0x000000FF; - } +// scale[i] = scale[i] & 0x000000FF; +// } size_t retColCount = jobInfo.nonConstDelCols.size(); @@ -1402,43 +1402,10 @@ void TupleAggregateStep::prep1PhaseAggregate( oidsAgg.push_back(oidsProj[colProj]); keysAgg.push_back(key); - - if (typeProj[colProj] == CalpontSystemCatalog::DOUBLE || - typeProj[colProj] == CalpontSystemCatalog::UDOUBLE || - typeProj[colProj] == CalpontSystemCatalog::FLOAT || - typeProj[colProj] == CalpontSystemCatalog::UFLOAT) - { - typeAgg.push_back(typeProj[colProj]); - scaleAgg.push_back(scaleProj[colProj]); - precisionAgg.push_back(precisionProj[colProj]); - widthAgg.push_back(width[colProj]); - } - else if (isUnsigned(typeProj[colProj])) - { - typeAgg.push_back(CalpontSystemCatalog::UBIGINT); - uint32_t scale = scaleProj[colProj]; - - // for int average, FE expects a decimal - if (aggOp == ROWAGG_AVG) - scale = jobInfo.scaleOfAvg[key]; // scale += 4; - - scaleAgg.push_back(scale); - precisionAgg.push_back(20); - widthAgg.push_back(bigUintWidth); - } - else - { - typeAgg.push_back(CalpontSystemCatalog::BIGINT); - uint32_t scale = scaleProj[colProj]; - - // for int average, FE expects a decimal - if (aggOp == ROWAGG_AVG) - scale = jobInfo.scaleOfAvg[key]; // scale += 4; - - scaleAgg.push_back(scale); - precisionAgg.push_back(19); - widthAgg.push_back(bigIntWidth); - } + typeAgg.push_back(CalpontSystemCatalog::LONGDOUBLE); + precisionAgg.push_back(-1); + widthAgg.push_back(sizeof(long double)); + scaleAgg.push_back(scaleProj[colProj]); } break; @@ -1450,16 +1417,7 @@ void TupleAggregateStep::prep1PhaseAggregate( scaleAgg.push_back(0); // work around count() in select subquery precisionAgg.push_back(9999); - - if (isUnsigned(typeProj[colProj])) - { - typeAgg.push_back(CalpontSystemCatalog::UBIGINT); - } - else - { - typeAgg.push_back(CalpontSystemCatalog::BIGINT); - } - + typeAgg.push_back(CalpontSystemCatalog::UBIGINT); widthAgg.push_back(bigIntWidth); } break; @@ -1603,7 +1561,7 @@ void TupleAggregateStep::prep1PhaseAggregate( keysAgg.push_back(k->first); scaleAgg.push_back(0); precisionAgg.push_back(19); - typeAgg.push_back(CalpontSystemCatalog::BIGINT); + typeAgg.push_back(CalpontSystemCatalog::UBIGINT); widthAgg.push_back(bigIntWidth); } } @@ -1643,7 +1601,7 @@ void TupleAggregateStep::prep1PhaseAggregate( oidsAgg.push_back(oidsAgg[j]); keysAgg.push_back(keysAgg[j]); scaleAgg.push_back(0); - precisionAgg.push_back(0); + precisionAgg.push_back(-1); typeAgg.push_back(CalpontSystemCatalog::LONGDOUBLE); widthAgg.push_back(sizeof(long double)); ++lastCol; @@ -1652,7 +1610,7 @@ void TupleAggregateStep::prep1PhaseAggregate( oidsAgg.push_back(oidsAgg[j]); keysAgg.push_back(keysAgg[j]); scaleAgg.push_back(0); - precisionAgg.push_back(0); + precisionAgg.push_back(-1); typeAgg.push_back(CalpontSystemCatalog::LONGDOUBLE); widthAgg.push_back(sizeof(long double)); ++lastCol; @@ -1973,47 +1931,19 @@ void TupleAggregateStep::prep1PhaseDistinctAggregate( oidsAgg.push_back(oidsProj[colProj]); keysAgg.push_back(aggKey); - - if (typeProj[colProj] != CalpontSystemCatalog::DOUBLE && - typeProj[colProj] != CalpontSystemCatalog::FLOAT) - { - if (isUnsigned(typeProj[colProj])) - { - typeAgg.push_back(CalpontSystemCatalog::UBIGINT); - precisionAgg.push_back(20); - } - else - { - typeAgg.push_back(CalpontSystemCatalog::BIGINT); - precisionAgg.push_back(19); - } - - uint32_t scale = scaleProj[colProj]; - - // for int average, FE expects a decimal - if (aggOp == ROWAGG_AVG) - scale = jobInfo.scaleOfAvg[aggKey]; // scale += 4; - - scaleAgg.push_back(scale); - widthAgg.push_back(bigIntWidth); - } - else - { - typeAgg.push_back(typeProj[colProj]); - scaleAgg.push_back(scaleProj[colProj]); - precisionAgg.push_back(precisionProj[colProj]); - widthAgg.push_back(widthProj[colProj]); - } - + typeAgg.push_back(CalpontSystemCatalog::LONGDOUBLE); + precisionAgg.push_back(-1); + widthAgg.push_back(sizeof(long double)); + scaleAgg.push_back(scaleProj[colProj]); colAgg++; - } - // has distinct step, put the count column for avg next to the sum - // let fall through to add a count column for average function + // has distinct step, put the count column for avg next to the sum + // let fall through to add a count column for average function if (aggOp == ROWAGG_AVG) funct->fAuxColumnIndex = colAgg; else break; + } case ROWAGG_COUNT_ASTERISK: case ROWAGG_COUNT_COL_NAME: @@ -2070,7 +2000,7 @@ void TupleAggregateStep::prep1PhaseDistinctAggregate( oidsAgg.push_back(oidsProj[colProj]); keysAgg.push_back(aggKey); scaleAgg.push_back(0); - precisionAgg.push_back(0); + precisionAgg.push_back(-1); typeAgg.push_back(CalpontSystemCatalog::LONGDOUBLE); widthAgg.push_back(sizeof(long double)); ++colAgg; @@ -2079,7 +2009,7 @@ void TupleAggregateStep::prep1PhaseDistinctAggregate( oidsAgg.push_back(oidsProj[colProj]); keysAgg.push_back(aggKey); scaleAgg.push_back(0); - precisionAgg.push_back(0); + precisionAgg.push_back(-1); typeAgg.push_back(CalpontSystemCatalog::LONGDOUBLE); widthAgg.push_back(sizeof(long double)); ++colAgg; @@ -2321,37 +2251,10 @@ void TupleAggregateStep::prep1PhaseDistinctAggregate( oidsAggDist.push_back(oidsAgg[colAgg]); keysAggDist.push_back(retKey); - - if (typeAgg[colAgg] != CalpontSystemCatalog::DOUBLE && - typeAgg[colAgg] != CalpontSystemCatalog::FLOAT) - { - if (isUnsigned(typeAgg[colAgg])) - { - typeAggDist.push_back(CalpontSystemCatalog::UBIGINT); - precisionAggDist.push_back(20); - } - else - { - typeAggDist.push_back(CalpontSystemCatalog::BIGINT); - precisionAggDist.push_back(19); - } - - uint32_t scale = scaleProj[colAgg]; - - // for int average, FE expects a decimal - if (aggOp == ROWAGG_DISTINCT_AVG) - scale = jobInfo.scaleOfAvg[retKey]; // scale += 4; - - scaleAggDist.push_back(scale); - widthAggDist.push_back(bigIntWidth); - } - else - { - typeAggDist.push_back(typeAgg[colAgg]); - scaleAggDist.push_back(scaleAgg[colAgg]); - precisionAggDist.push_back(precisionAgg[colAgg]); - widthAggDist.push_back(widthAgg[colAgg]); - } + typeAggDist.push_back(CalpontSystemCatalog::LONGDOUBLE); + precisionAggDist.push_back(-1); + widthAggDist.push_back(sizeof(long double)); + scaleAggDist.push_back(scaleProj[colAgg]); } break; @@ -2362,16 +2265,7 @@ void TupleAggregateStep::prep1PhaseDistinctAggregate( scaleAggDist.push_back(0); // work around count() in select subquery precisionAggDist.push_back(9999); - - if (isUnsigned(typeAgg[colAgg])) - { - typeAggDist.push_back(CalpontSystemCatalog::UBIGINT); - } - else - { - typeAggDist.push_back(CalpontSystemCatalog::BIGINT); - } - + typeAggDist.push_back(CalpontSystemCatalog::UBIGINT); widthAggDist.push_back(bigIntWidth); } break; @@ -2659,7 +2553,7 @@ void TupleAggregateStep::prep1PhaseDistinctAggregate( keysAggDist.push_back(k->first); scaleAggDist.push_back(0); precisionAggDist.push_back(19); - typeAggDist.push_back(CalpontSystemCatalog::BIGINT); + typeAggDist.push_back(CalpontSystemCatalog::UBIGINT); widthAggDist.push_back(bigIntWidth); } } @@ -2739,7 +2633,7 @@ void TupleAggregateStep::prep1PhaseDistinctAggregate( oidsAggDist.push_back(oidsAggDist[j]); keysAggDist.push_back(keysAggDist[j]); scaleAggDist.push_back(0); - precisionAggDist.push_back(0); + precisionAggDist.push_back(-1); typeAggDist.push_back(CalpontSystemCatalog::LONGDOUBLE); widthAggDist.push_back(sizeof(long double)); ++lastCol; @@ -3151,13 +3045,32 @@ void TupleAggregateStep::prep2PhasesAggregate( SP_ROWAGG_GRPBY_t groupby(new RowAggGroupByCol(colProj, colAggPm)); groupByPm.push_back(groupby); - // PM: just copy down to aggregation rowgroup + // PM: Except for SUM/AVG, just copy down to aggregation rowgroup + RowAggFunctionType aggOp = rowgroup::ROWAGG_COUNT_NO_OP; + for (size_t agg = 0; agg < aggColVec.size(); ++agg) + { + if (aggColVec[agg].first == key) + { + aggOp = functionIdMap(aggColVec[agg].second); + break; + } + } oidsAggPm.push_back(oidsProj[colProj]); keysAggPm.push_back(key); scaleAggPm.push_back(scaleProj[colProj]); - precisionAggPm.push_back(precisionProj[colProj]); - typeAggPm.push_back(typeProj[colProj]); - widthAggPm.push_back(width[colProj]); + if (aggOp == ROWAGG_DISTINCT_SUM || + aggOp == ROWAGG_DISTINCT_AVG) + { + typeAggPm.push_back(CalpontSystemCatalog::LONGDOUBLE); + precisionAggPm.push_back(-1); + widthAggPm.push_back(sizeof(long double)); + } + else + { + typeAggPm.push_back(typeProj[colProj]); + widthAggPm.push_back(width[colProj]); + precisionAggPm.push_back(precisionProj[colProj]); + } aggFuncMap.insert(make_pair(boost::make_tuple(keysAggPm[colAggPm], 0, pUDAFFunc, udafc ? udafc->getContext().getParamKeys() : NULL), colAggPm)); colAggPm++; @@ -3277,49 +3190,15 @@ void TupleAggregateStep::prep2PhasesAggregate( oidsAggPm.push_back(oidsProj[colProj]); keysAggPm.push_back(aggKey); - - if (typeProj[colProj] == CalpontSystemCatalog::DOUBLE || - typeProj[colProj] == CalpontSystemCatalog::UDOUBLE || - typeProj[colProj] == CalpontSystemCatalog::FLOAT || - typeProj[colProj] == CalpontSystemCatalog::UFLOAT) - { - typeAggPm.push_back(typeProj[colProj]); - scaleAggPm.push_back(scaleProj[colProj]); - precisionAggPm.push_back(precisionProj[colProj]); - widthAggPm.push_back(width[colProj]); - } - else if (isUnsigned(typeProj[colProj])) - { - typeAggPm.push_back(CalpontSystemCatalog::UBIGINT); - uint32_t scale = scaleProj[colProj]; - - // for int average, FE expects a decimal - if (aggOp == ROWAGG_AVG) - scale = jobInfo.scaleOfAvg[aggKey]; // scale += 4; - - scaleAggPm.push_back(scale); - precisionAggPm.push_back(20); - widthAggPm.push_back(bigUintWidth); - } - else - { - typeAggPm.push_back(CalpontSystemCatalog::BIGINT); - uint32_t scale = scaleProj[colProj]; - - // for int average, FE expects a decimal - if (aggOp == ROWAGG_AVG) - scale = jobInfo.scaleOfAvg[aggKey]; // scale += 4; - - scaleAggPm.push_back(scale); - precisionAggPm.push_back(19); - widthAggPm.push_back(bigIntWidth); - } - + typeAggPm.push_back(CalpontSystemCatalog::LONGDOUBLE); + scaleAggPm.push_back(scaleProj[colProj]); + precisionAggPm.push_back(-1); + widthAggPm.push_back(sizeof(long double)); colAggPm++; } - // PM: put the count column for avg next to the sum - // let fall through to add a count column for average function + // PM: put the count column for avg next to the sum + // let fall through to add a count column for average function if (aggOp != ROWAGG_AVG) break; @@ -3331,16 +3210,7 @@ void TupleAggregateStep::prep2PhasesAggregate( scaleAggPm.push_back(0); // work around count() in select subquery precisionAggPm.push_back(9999); - - if (isUnsigned(typeProj[colProj])) - { - typeAggPm.push_back(CalpontSystemCatalog::UBIGINT); - } - else - { - typeAggPm.push_back(CalpontSystemCatalog::BIGINT); - } - + typeAggPm.push_back(CalpontSystemCatalog::UBIGINT); widthAggPm.push_back(bigIntWidth); colAggPm++; } @@ -3378,7 +3248,7 @@ void TupleAggregateStep::prep2PhasesAggregate( oidsAggPm.push_back(oidsProj[colProj]); keysAggPm.push_back(aggKey); scaleAggPm.push_back(0); - precisionAggPm.push_back(0); + precisionAggPm.push_back(-1); typeAggPm.push_back(CalpontSystemCatalog::LONGDOUBLE); widthAggPm.push_back(sizeof(long double)); ++colAggPm; @@ -3387,7 +3257,7 @@ void TupleAggregateStep::prep2PhasesAggregate( oidsAggPm.push_back(oidsProj[colProj]); keysAggPm.push_back(aggKey); scaleAggPm.push_back(0); - precisionAggPm.push_back(0); + precisionAggPm.push_back(-1); typeAggPm.push_back(CalpontSystemCatalog::LONGDOUBLE); widthAggPm.push_back(sizeof(long double)); ++colAggPm; @@ -3612,7 +3482,7 @@ void TupleAggregateStep::prep2PhasesAggregate( keysAggUm.push_back(retKey); scaleAggUm.push_back(0); precisionAggUm.push_back(19); - typeAggUm.push_back(CalpontSystemCatalog::BIGINT); + typeAggUm.push_back(CalpontSystemCatalog::UBIGINT); widthAggUm.push_back(bigIntWidth); } } @@ -3779,7 +3649,7 @@ void TupleAggregateStep::prep2PhasesAggregate( keysAggUm.push_back(k->first); scaleAggUm.push_back(0); precisionAggUm.push_back(19); - typeAggUm.push_back(CalpontSystemCatalog::BIGINT); + typeAggUm.push_back(CalpontSystemCatalog::UBIGINT); widthAggUm.push_back(bigIntWidth); } } @@ -3818,7 +3688,7 @@ void TupleAggregateStep::prep2PhasesAggregate( oidsAggUm.push_back(oidsAggUm[j]); keysAggUm.push_back(keysAggUm[j]); scaleAggUm.push_back(0); - precisionAggUm.push_back(0); + precisionAggUm.push_back(-1); typeAggUm.push_back(CalpontSystemCatalog::LONGDOUBLE); widthAggUm.push_back(sizeof(long double)); ++lastCol; @@ -3827,7 +3697,7 @@ void TupleAggregateStep::prep2PhasesAggregate( oidsAggUm.push_back(oidsAggUm[j]); keysAggUm.push_back(keysAggUm[j]); scaleAggUm.push_back(0); - precisionAggUm.push_back(0); + precisionAggUm.push_back(-1); typeAggUm.push_back(CalpontSystemCatalog::LONGDOUBLE); widthAggUm.push_back(sizeof(long double)); ++lastCol; @@ -4035,16 +3905,37 @@ void TupleAggregateStep::prep2PhasesDistinctAggregate( uint64_t colProj = projColPosMap[key]; - SP_ROWAGG_GRPBY_t groupby(new RowAggGroupByCol(colProj, colAggPm)); +// SP_ROWAGG_GRPBY_t groupby(new RowAggGroupByCol(colProj, colAggPm)); + SP_ROWAGG_GRPBY_t groupby(new RowAggGroupByCol(colProj)); groupByPm.push_back(groupby); - // PM: just copy down to aggregation rowgroup + // PM: Except for SUM/AVG, just copy down to aggregation rowgroup + RowAggFunctionType aggOp = rowgroup::ROWAGG_COUNT_NO_OP; + for (size_t agg = 0; agg < aggColVec.size(); ++agg) + { + if (aggColVec[agg].first == key) + { + aggOp = functionIdMap(aggColVec[agg].second); + break; + } + } oidsAggPm.push_back(oidsProj[colProj]); keysAggPm.push_back(key); scaleAggPm.push_back(scaleProj[colProj]); precisionAggPm.push_back(precisionProj[colProj]); - typeAggPm.push_back(typeProj[colProj]); - widthAggPm.push_back(width[colProj]); + if (aggOp == ROWAGG_DISTINCT_SUM || + aggOp == ROWAGG_DISTINCT_AVG) + { + typeAggPm.push_back(CalpontSystemCatalog::LONGDOUBLE); + precisionAggPm.push_back(-1); + widthAggPm.push_back(sizeof(long double)); + } + else + { + typeAggPm.push_back(typeProj[colProj]); + widthAggPm.push_back(width[colProj]); + precisionAggPm.push_back(precisionProj[colProj]); + } aggFuncMap.insert(make_pair(boost::make_tuple(keysAggPm[colAggPm], 0, pUDAFFunc, udafc ? udafc->getContext().getParamKeys() : NULL), colAggPm)); colAggPm++; @@ -4171,43 +4062,15 @@ void TupleAggregateStep::prep2PhasesDistinctAggregate( oidsAggPm.push_back(oidsProj[colProj]); keysAggPm.push_back(aggKey); - - if (typeProj[colProj] != CalpontSystemCatalog::DOUBLE && - typeProj[colProj] != CalpontSystemCatalog::FLOAT) - { - if (isUnsigned(typeProj[colProj])) - { - typeAggPm.push_back(CalpontSystemCatalog::UBIGINT); - precisionAggPm.push_back(20); - } - else - { - typeAggPm.push_back(CalpontSystemCatalog::BIGINT); - precisionAggPm.push_back(19); - } - - uint32_t scale = scaleProj[colProj]; - - // for int average, FE expects a decimal - if (aggOp == ROWAGG_AVG) - scale = jobInfo.scaleOfAvg[aggKey]; // scale += 4; - - scaleAggPm.push_back(scale); - widthAggPm.push_back(bigIntWidth); - } - else - { - typeAggPm.push_back(typeProj[colProj]); - scaleAggPm.push_back(scaleProj[colProj]); - precisionAggPm.push_back(precisionProj[colProj]); - widthAggPm.push_back(width[colProj]); - } - + typeAggPm.push_back(CalpontSystemCatalog::LONGDOUBLE); + precisionAggPm.push_back(-1); + widthAggPm.push_back(sizeof(long double)); + scaleAggPm.push_back(scaleProj[colProj]); colAggPm++; } - // PM: put the count column for avg next to the sum - // let fall through to add a count column for average function + // PM: put the count column for avg next to the sum + // let fall through to add a count column for average function if (aggOp == ROWAGG_AVG) funct->fAuxColumnIndex = colAggPm; else @@ -4268,7 +4131,7 @@ void TupleAggregateStep::prep2PhasesDistinctAggregate( oidsAggPm.push_back(oidsProj[colProj]); keysAggPm.push_back(aggKey); scaleAggPm.push_back(0); - precisionAggPm.push_back(0); + precisionAggPm.push_back(-1); typeAggPm.push_back(CalpontSystemCatalog::LONGDOUBLE); widthAggPm.push_back(sizeof(long double)); ++colAggPm; @@ -4277,7 +4140,7 @@ void TupleAggregateStep::prep2PhasesDistinctAggregate( oidsAggPm.push_back(oidsProj[colProj]); keysAggPm.push_back(aggKey); scaleAggPm.push_back(0); - precisionAggPm.push_back(0); + precisionAggPm.push_back(-1); typeAggPm.push_back(CalpontSystemCatalog::LONGDOUBLE); widthAggPm.push_back(sizeof(long double)); ++colAggPm; @@ -4566,41 +4429,14 @@ void TupleAggregateStep::prep2PhasesDistinctAggregate( oidsAggDist.push_back(oidsAggUm[colUm]); keysAggDist.push_back(retKey); - - if (typeAggUm[colUm] != CalpontSystemCatalog::DOUBLE && - typeAggUm[colUm] != CalpontSystemCatalog::FLOAT) - { - if (isUnsigned(typeAggUm[colUm])) - { - typeAggDist.push_back(CalpontSystemCatalog::UBIGINT); - precisionAggDist.push_back(20); - } - else - { - typeAggDist.push_back(CalpontSystemCatalog::BIGINT); - precisionAggDist.push_back(19); - } - - uint32_t scale = scaleAggUm[colUm]; - - // for int average, FE expects a decimal - if (aggOp == ROWAGG_DISTINCT_AVG) - scale = jobInfo.scaleOfAvg[retKey]; // scale += 4; - - scaleAggDist.push_back(scale); - widthAggDist.push_back(bigIntWidth); - } - else - { - typeAggDist.push_back(typeAggUm[colUm]); - scaleAggDist.push_back(scaleAggUm[colUm]); - precisionAggDist.push_back(precisionAggUm[colUm]); - widthAggDist.push_back(widthAggUm[colUm]); - } + typeAggDist.push_back(typeAggUm[colUm]); + scaleAggDist.push_back(scaleAggUm[colUm]); + precisionAggDist.push_back(precisionAggUm[colUm]); + widthAggDist.push_back(widthAggUm[colUm]); } - // PM: put the count column for avg next to the sum - // let fall through to add a count column for average function - //if (aggOp != ROWAGG_DISTINCT_AVG) + // PM: put the count column for avg next to the sum + // let fall through to add a count column for average function + //if (aggOp != ROWAGG_DISTINCT_AVG) break; case ROWAGG_COUNT_DISTINCT_COL_NAME: @@ -4610,16 +4446,7 @@ void TupleAggregateStep::prep2PhasesDistinctAggregate( scaleAggDist.push_back(0); // work around count() in select subquery precisionAggDist.push_back(9999); - - if (isUnsigned(typeAggUm[colUm])) - { - typeAggDist.push_back(CalpontSystemCatalog::UBIGINT); - } - else - { - typeAggDist.push_back(CalpontSystemCatalog::BIGINT); - } - + typeAggDist.push_back(CalpontSystemCatalog::UBIGINT); widthAggDist.push_back(bigIntWidth); } break; @@ -4847,7 +4674,7 @@ void TupleAggregateStep::prep2PhasesDistinctAggregate( keysAggDist.push_back(k->first); scaleAggDist.push_back(0); precisionAggDist.push_back(19); - typeAggDist.push_back(CalpontSystemCatalog::BIGINT); + typeAggDist.push_back(CalpontSystemCatalog::UBIGINT); widthAggDist.push_back(bigIntWidth); } } @@ -4918,7 +4745,7 @@ void TupleAggregateStep::prep2PhasesDistinctAggregate( oidsAggDist.push_back(oidsAggDist[j]); keysAggDist.push_back(keysAggDist[j]); scaleAggDist.push_back(0); - precisionAggDist.push_back(0); + precisionAggDist.push_back(-1); typeAggDist.push_back(CalpontSystemCatalog::LONGDOUBLE); widthAggDist.push_back(sizeof(long double)); ++lastCol; @@ -4927,7 +4754,7 @@ void TupleAggregateStep::prep2PhasesDistinctAggregate( oidsAggDist.push_back(oidsAggDist[j]); keysAggDist.push_back(keysAggDist[j]); scaleAggDist.push_back(0); - precisionAggDist.push_back(0); + precisionAggDist.push_back(-1); typeAggDist.push_back(CalpontSystemCatalog::LONGDOUBLE); widthAggDist.push_back(sizeof(long double)); ++lastCol; diff --git a/dbcon/mysql/ha_calpont_execplan.cpp b/dbcon/mysql/ha_calpont_execplan.cpp index 83be3c650..9e92162a0 100644 --- a/dbcon/mysql/ha_calpont_execplan.cpp +++ b/dbcon/mysql/ha_calpont_execplan.cpp @@ -3372,7 +3372,6 @@ ReturnedColumn* buildFunctionColumn( return ac; } - // comment out for now until case function is fully tested. else if (funcName == "case") { fc = buildCaseFunction(ifp, gwi, nonSupport); @@ -4500,39 +4499,9 @@ ReturnedColumn* buildAggregateColumn(Item* item, gp_walk_info& gwi) { CalpontSystemCatalog::ColType ct = parm->resultType(); ct.colDataType = CalpontSystemCatalog::LONGDOUBLE; + ct.colWidth = 16; ct.scale += 4; -// ct.colWidth = 8; - -#if 0 - switch (ct.colDataType) - { - case CalpontSystemCatalog::TINYINT: - case CalpontSystemCatalog::SMALLINT: - case CalpontSystemCatalog::MEDINT: - case CalpontSystemCatalog::INT: - case CalpontSystemCatalog::BIGINT: - case CalpontSystemCatalog::DECIMAL: - case CalpontSystemCatalog::UDECIMAL: - case CalpontSystemCatalog::UTINYINT: - case CalpontSystemCatalog::USMALLINT: - case CalpontSystemCatalog::UMEDINT: - case CalpontSystemCatalog::UINT: - case CalpontSystemCatalog::UBIGINT: - ct.scale += 4; - break; - - case CalpontSystemCatalog::FLOAT: - case CalpontSystemCatalog::UFLOAT: - case CalpontSystemCatalog::DOUBLE: - case CalpontSystemCatalog::UDOUBLE: - ct.colDataType = CalpontSystemCatalog::DOUBLE; - ct.colWidth = 8; - break; - - default: - break; - } -#endif + ct.precision = -1; ac->resultType(ct); } else if (isp->sum_func() == Item_sum::COUNT_FUNC || @@ -4549,48 +4518,8 @@ ReturnedColumn* buildAggregateColumn(Item* item, gp_walk_info& gwi) { CalpontSystemCatalog::ColType ct = parm->resultType(); ct.colDataType = CalpontSystemCatalog::LONGDOUBLE; - ct.scale += 4; -#if 0 - switch (ct.colDataType) - { - case CalpontSystemCatalog::TINYINT: - case CalpontSystemCatalog::SMALLINT: - case CalpontSystemCatalog::MEDINT: - case CalpontSystemCatalog::INT: - case CalpontSystemCatalog::BIGINT: - ct.colDataType = CalpontSystemCatalog::BIGINT; - - // no break, let fall through - - case CalpontSystemCatalog::DECIMAL: - case CalpontSystemCatalog::UDECIMAL: - ct.colWidth = 8; - break; - - case CalpontSystemCatalog::UTINYINT: - case CalpontSystemCatalog::USMALLINT: - case CalpontSystemCatalog::UMEDINT: - case CalpontSystemCatalog::UINT: - case CalpontSystemCatalog::UBIGINT: - ct.colDataType = CalpontSystemCatalog::UBIGINT; - ct.colWidth = 8; - break; - -#if PROMOTE_FLOAT_TO_DOUBLE_ON_SUM - - case CalpontSystemCatalog::FLOAT: - case CalpontSystemCatalog::UFLOAT: - case CalpontSystemCatalog::DOUBLE: - case CalpontSystemCatalog::UDOUBLE: - ct.colDataType = CalpontSystemCatalog::DOUBLE; - ct.colWidth = 8; - break; -#endif - - default: - break; - } -#endif + ct.colWidth = 16; + ct.precision = -1; ac->resultType(ct); } else if (isp->sum_func() == Item_sum::STD_FUNC || diff --git a/dbcon/mysql/ha_calpont_impl.cpp b/dbcon/mysql/ha_calpont_impl.cpp index 0a633fe1c..58170f5aa 100644 --- a/dbcon/mysql/ha_calpont_impl.cpp +++ b/dbcon/mysql/ha_calpont_impl.cpp @@ -705,37 +705,58 @@ int fetchNextRow(uchar* buf, cal_table_info& ti, cal_connection_info* ci, bool h case CalpontSystemCatalog::LONGDOUBLE: { - double dl = row.getLongDoubleField(s); - + long double dl = row.getLongDoubleField(s); if (dl == std::numeric_limits::infinity()) - continue; - - Field_double* f2 = (Field_double*)*f; - // bug 3483, reserve enough space for the longest double value - // -1.7976931348623157E+308 to -2.2250738585072014E-308, 0, and - // 2.2250738585072014E-308 to 1.7976931348623157E+308. - (*f)->field_length = 310; - - //double double_val = *(double*)(&value); - //f2->store(double_val); - if ((f2->decimals() == DECIMAL_NOT_SPECIFIED && row.getScale(s) > 0) - || f2->decimals() < row.getScale(s)) { - f2->dec = row.getScale(s); + continue; } - f2->store(dl); + switch((*f)->type()) + { + case MYSQL_TYPE_NEWDECIMAL: + { + char buf[310]; + Field_new_decimal* f2 = (Field_new_decimal*)*f; + if ((f2->decimals() == DECIMAL_NOT_SPECIFIED && row.getScale(s) > 0) + || f2->decimals() < row.getScale(s)) + { + f2->dec = row.getScale(s); + } +// dl /= pow(10.0, (double)f2->dec); + snprintf(buf, 310, "%.20Lg", dl); + f2->store(buf, strlen(buf), f2->charset()); + if ((*f)->null_ptr) + *(*f)->null_ptr &= ~(*f)->null_bit; + } + break; + case MYSQL_TYPE_DOUBLE: + { + Field_double* f2 = (Field_double*)*f; - if ((*f)->null_ptr) - *(*f)->null_ptr &= ~(*f)->null_bit; + // bug 3483, reserve enough space for the longest double value + // -1.7976931348623157E+308 to -2.2250738585072014E-308, 0, and + // 2.2250738585072014E-308 to 1.7976931348623157E+308. + (*f)->field_length = 310; + + if ((f2->decimals() == DECIMAL_NOT_SPECIFIED && row.getScale(s) > 0) + || f2->decimals() < row.getScale(s)) + { + f2->dec = row.getScale(s); + } + dl /= pow(10.0, (double)f2->dec); + + f2->store(static_cast(dl)); + if ((*f)->null_ptr) + *(*f)->null_ptr &= ~(*f)->null_bit; + } + break; + default: + { + continue; // Shouldn't happen. Functions should not return long double to other than double or decimal return type. + } + } break; - - - //int64_t* icvp = (int64_t*)&dl; - //intColVal = *icvp; - //storeNumericField(f, intColVal, colType); - //break; } case CalpontSystemCatalog::DECIMAL: diff --git a/exemgr/main.cpp b/exemgr/main.cpp index a403360ac..e34431e9d 100644 --- a/exemgr/main.cpp +++ b/exemgr/main.cpp @@ -718,8 +718,7 @@ new_plan: string emsg("NOERROR"); ByteStream emsgBs; ByteStream::quadbyte tflg = 0; - jl = JobListFactory::makeJobList( - &csep, fRm, true, true); + jl = JobListFactory::makeJobList(&csep, fRm, true, true); // assign query stats jl->queryStats(fStats); diff --git a/utils/common/nullvaluemanip.cpp b/utils/common/nullvaluemanip.cpp index 475f495cc..dc0777eef 100644 --- a/utils/common/nullvaluemanip.cpp +++ b/utils/common/nullvaluemanip.cpp @@ -122,9 +122,6 @@ uint64_t getNullValue(CalpontSystemCatalog::ColDataType t, uint32_t colWidth) case CalpontSystemCatalog::UBIGINT: return joblist::UBIGINTNULL; - case CalpontSystemCatalog::LONGDOUBLE: - return -1; // no NULL value for long double yet, this is a nan. - case CalpontSystemCatalog::VARBINARY: default: ostringstream os; @@ -233,7 +230,7 @@ int64_t getSignedNullValue(CalpontSystemCatalog::ColDataType t, uint32_t colWidt return (int64_t)joblist::UBIGINTNULL; case CalpontSystemCatalog::LONGDOUBLE: - return -1; // no NULL value for long double yet, this is a nan. + return (int64_t)joblist::LONGDOUBLENULL; case CalpontSystemCatalog::VARBINARY: default: diff --git a/utils/dataconvert/dataconvert.cpp b/utils/dataconvert/dataconvert.cpp index 7614385b5..08a833cd0 100644 --- a/utils/dataconvert/dataconvert.cpp +++ b/utils/dataconvert/dataconvert.cpp @@ -3208,8 +3208,9 @@ CalpontSystemCatalog::ColType DataConvert::convertUnionColType(vector unionedType.scale) ? types[i].scale : unionedType.scale; unionedType.colWidth = sizeof(long double); + unionedType.precision = -1; break; default: diff --git a/utils/funcexp/func_abs.cpp b/utils/funcexp/func_abs.cpp index 14cb5531a..2cb25c001 100644 --- a/utils/funcexp/func_abs.cpp +++ b/utils/funcexp/func_abs.cpp @@ -78,6 +78,14 @@ double Func_abs::getDoubleVal(Row& row, return fabs(parm[0]->data()->getDoubleVal(row, isNull)); } +long double Func_abs::getLongDoubleVal(Row& row, + FunctionParm& parm, + bool& isNull, + CalpontSystemCatalog::ColType&) +{ + return fabsl(parm[0]->data()->getLongDoubleVal(row, isNull)); +} + } // namespace funcexp // vim:ts=4 sw=4: diff --git a/utils/funcexp/func_case.cpp b/utils/funcexp/func_case.cpp index 03ca241f8..46371026a 100644 --- a/utils/funcexp/func_case.cpp +++ b/utils/funcexp/func_case.cpp @@ -243,6 +243,27 @@ inline uint64_t simple_case_cmp(Row& row, break; } + case execplan::CalpontSystemCatalog::LONGDOUBLE: + { + long double ev = parm[n]->data()->getLongDoubleVal(row, isNull); + + if (isNull) + break; + + for (i = 1; i <= whereCount; i++) + { + if (ev == parm[i]->data()->getLongDoubleVal(row, isNull) && !isNull) + { + foundIt = true; + break; + } + else + isNull = false; + } + + break; + } + default: { std::ostringstream oss; @@ -510,6 +531,19 @@ double Func_simple_case::getDoubleVal(Row& row, return parm[i]->data()->getDoubleVal(row, isNull); } +long double Func_simple_case::getLongDoubleVal(Row& row, + FunctionParm& parm, + bool& isNull, + CalpontSystemCatalog::ColType& operationColType) +{ + uint64_t i = simple_case_cmp(row, parm, isNull, operationColType); + + if (isNull) + return doubleNullVal(); + + return parm[i]->data()->getLongDoubleVal(row, isNull); +} + int32_t Func_simple_case::getDateIntVal(rowgroup::Row& row, FunctionParm& parm, @@ -649,6 +683,19 @@ double Func_searched_case::getDoubleVal(Row& row, return parm[i]->data()->getDoubleVal(row, isNull); } +long double Func_searched_case::getLongDoubleVal(Row& row, + FunctionParm& parm, + bool& isNull, + CalpontSystemCatalog::ColType&) +{ + uint64_t i = searched_case_cmp(row, parm, isNull); + + if (isNull) + return longDoubleNullVal(); + + return parm[i]->data()->getLongDoubleVal(row, isNull); +} + int32_t Func_searched_case::getDateIntVal(rowgroup::Row& row, FunctionParm& parm, diff --git a/utils/funcexp/func_cast.cpp b/utils/funcexp/func_cast.cpp index b396c45ea..75262f13a 100644 --- a/utils/funcexp/func_cast.cpp +++ b/utils/funcexp/func_cast.cpp @@ -139,6 +139,26 @@ int64_t Func_cast_signed::getIntVal(Row& row, } break; + case execplan::CalpontSystemCatalog::LONGDOUBLE: + { + long double value = parm[0]->data()->getLongDoubleVal(row, isNull); + + if (value > 0) + value += 0.5; + else if (value < 0) + value -= 0.5; + + int64_t ret = (int64_t) value; + + if (value > (long double) numeric_limits::max()) + ret = numeric_limits::max(); + else if (value < (long double) (numeric_limits::min() + 2)) + ret = numeric_limits::min() + 2; // IDB min for bigint + + return ret; + } + break; + case execplan::CalpontSystemCatalog::VARCHAR: case execplan::CalpontSystemCatalog::CHAR: case execplan::CalpontSystemCatalog::TEXT: @@ -266,6 +286,26 @@ uint64_t Func_cast_unsigned::getUintVal(Row& row, } break; + case execplan::CalpontSystemCatalog::LONGDOUBLE: + { + long double value = parm[0]->data()->getLongDoubleVal(row, isNull); + + if (value > 0) + value += 0.5; + else if (value < 0) + value -= 0.5; + + uint64_t ret = (uint64_t) value; + + if (value > (long double) numeric_limits::max() - 2) + ret = numeric_limits::max(); + else if (value < 0) + ret = 0; + + return ret; + } + break; + case execplan::CalpontSystemCatalog::VARCHAR: case execplan::CalpontSystemCatalog::CHAR: case execplan::CalpontSystemCatalog::TEXT: @@ -391,6 +431,12 @@ string Func_cast_char::getStrVal(Row& row, } break; + case execplan::CalpontSystemCatalog::LONGDOUBLE: + { + return helpers::longDoubleToString(parm[0]->data()->getLongDoubleVal(row, isNull)).substr(0, length); + } + break; + case execplan::CalpontSystemCatalog::FLOAT: case execplan::CalpontSystemCatalog::UFLOAT: { @@ -526,6 +572,17 @@ double Func_cast_date::getDoubleVal(Row& row, operationColType); } +long double Func_cast_date::getLongDoubleVal(Row& row, + FunctionParm& parm, + bool& isNull, + CalpontSystemCatalog::ColType& operationColType) +{ + return (long double) Func_cast_date::getDatetimeIntVal(row, + parm, + isNull, + operationColType); +} + int32_t Func_cast_date::getDateIntVal(rowgroup::Row& row, FunctionParm& parm, @@ -795,6 +852,17 @@ double Func_cast_datetime::getDoubleVal(Row& row, operationColType); } +long double Func_cast_datetime::getLongDoubleVal(Row& row, + FunctionParm& parm, + bool& isNull, + CalpontSystemCatalog::ColType& operationColType) +{ + return (long double) Func_cast_datetime::getDatetimeIntVal(row, + parm, + isNull, + operationColType); +} + int64_t Func_cast_datetime::getDatetimeIntVal(rowgroup::Row& row, FunctionParm& parm, bool& isNull, @@ -1099,6 +1167,26 @@ IDB_Decimal Func_cast_decimal::getDecimalVal(Row& row, } break; + case execplan::CalpontSystemCatalog::LONGDOUBLE: + { + long double value = parm[0]->data()->getLongDoubleVal(row, isNull); + + if (value > 0) + decimal.value = (int64_t) (value * helpers::powerOf10_c[decimals] + 0.5); + else if (value < 0) + decimal.value = (int64_t) (value * helpers::powerOf10_c[decimals] - 0.5); + else + decimal.value = 0; + + decimal.scale = decimals; + + if ( value > max_number_decimal ) + decimal.value = max_number_decimal; + else if ( value < -max_number_decimal ) + decimal.value = -max_number_decimal; + } + break; + case execplan::CalpontSystemCatalog::DECIMAL: case execplan::CalpontSystemCatalog::UDECIMAL: { @@ -1426,6 +1514,12 @@ double Func_cast_double::getDoubleVal(Row& row, } break; + case execplan::CalpontSystemCatalog::LONGDOUBLE: + { + dblval = static_cast(parm[0]->data()->getLongDoubleVal(row, isNull)); + } + break; + case execplan::CalpontSystemCatalog::DECIMAL: case execplan::CalpontSystemCatalog::UDECIMAL: { diff --git a/utils/funcexp/func_ceil.cpp b/utils/funcexp/func_ceil.cpp index f270ed641..8fda7c6de 100644 --- a/utils/funcexp/func_ceil.cpp +++ b/utils/funcexp/func_ceil.cpp @@ -219,6 +219,12 @@ uint64_t Func_ceil::getUintVal(Row& row, } break; + case CalpontSystemCatalog::LONGDOUBLE: + { + ret = (uint64_t) ceill(parm[0]->data()->getLongDoubleVal(row, isNull)); + } + break; + case CalpontSystemCatalog::VARCHAR: case CalpontSystemCatalog::CHAR: case CalpontSystemCatalog::TEXT: @@ -293,6 +299,52 @@ double Func_ceil::getDoubleVal(Row& row, if (!isNull) ret = ceil(strtod(str.c_str(), 0)); } + else if (op_ct.colDataType == CalpontSystemCatalog::LONGDOUBLE) + { + ret = (double)ceill(parm[0]->data()->getLongDoubleVal(row, isNull)); + } + else + { + if (isUnsigned(op_ct.colDataType)) + { + ret = (double) getUintVal(row, parm, isNull, op_ct); + } + else + { + ret = (double) getIntVal(row, parm, isNull, op_ct); + } + } + + return ret; +} + +long double Func_ceil::getLongDoubleVal(Row& row, + FunctionParm& parm, + bool& isNull, + CalpontSystemCatalog::ColType& op_ct) +{ + long double ret = 0.0; + + if (op_ct.colDataType == CalpontSystemCatalog::LONGDOUBLE) + { + ret = ceill(parm[0]->data()->getLongDoubleVal(row, isNull)); + } + else if (op_ct.colDataType == CalpontSystemCatalog::DOUBLE || + op_ct.colDataType == CalpontSystemCatalog::UDOUBLE || + op_ct.colDataType == CalpontSystemCatalog::FLOAT || + op_ct.colDataType == CalpontSystemCatalog::UFLOAT) + { + ret = ceil(parm[0]->data()->getDoubleVal(row, isNull)); + } + else if (op_ct.colDataType == CalpontSystemCatalog::VARCHAR || + op_ct.colDataType == CalpontSystemCatalog::CHAR || + op_ct.colDataType == CalpontSystemCatalog::TEXT) + { + const string& str = parm[0]->data()->getStrVal(row, isNull); + + if (!isNull) + ret = ceil(strtod(str.c_str(), 0)); + } else { if (isUnsigned(op_ct.colDataType)) @@ -334,6 +386,18 @@ string Func_ceil::getStrVal(Row& row, *d = '\0'; } + else if (op_ct.colDataType == CalpontSystemCatalog::LONGDOUBLE) + { + snprintf(tmp, 511, "%Lf", getLongDoubleVal(row, parm, isNull, op_ct)); + + // remove the decimals in the oss string. + char* d = tmp; + + while ((*d != '.') && (*d != '\0')) + d++; + + *d = '\0'; + } else if (isUnsigned(op_ct.colDataType)) { #ifndef __LP64__ diff --git a/utils/funcexp/func_coalesce.cpp b/utils/funcexp/func_coalesce.cpp index e03040350..c79119bb7 100644 --- a/utils/funcexp/func_coalesce.cpp +++ b/utils/funcexp/func_coalesce.cpp @@ -188,6 +188,31 @@ double Func_coalesce::getDoubleVal(rowgroup::Row& row, } +long double Func_coalesce::getLongDoubleVal(rowgroup::Row& row, + FunctionParm& parm, + bool& isNull, + execplan::CalpontSystemCatalog::ColType& ct) +{ + long double d = 0.0; + + for (uint32_t i = 0; i < parm.size(); i++) + { + d = parm[i]->data()->getLongDoubleVal(row, isNull); + + if (isNull) + { + isNull = false; + continue; + } + + return d; + } + + isNull = true; + return d; +} + + execplan::IDB_Decimal Func_coalesce::getDecimalVal(rowgroup::Row& row, FunctionParm& parm, bool& isNull, diff --git a/utils/funcexp/func_exp.cpp b/utils/funcexp/func_exp.cpp index 8f1a5bbec..a1913064d 100644 --- a/utils/funcexp/func_exp.cpp +++ b/utils/funcexp/func_exp.cpp @@ -81,6 +81,38 @@ double Func_exp::getDoubleVal(Row& row, return ret; } +long double Func_exp::getLongDoubleVal(Row& row, + FunctionParm& parm, + bool& isNull, + CalpontSystemCatalog::ColType&) +{ + // null value is indicated by isNull + long double x = parm[0]->data()->getLongDoubleVal(row, isNull); + long double ret = 0.0; + + if (!isNull) + { + errno = 0; + ret = expl(x); + + if (errno == ERANGE) // display NULL for out range value + { + if (x > 0) + { + isNull = true; + Message::Args args; + args.add("exp"); + args.add((double)x); + unsigned errcode = ERR_FUNC_OUT_OF_RANGE_RESULT; + throw IDBExcept(IDBErrorInfo::instance()->errorMsg(errcode, args), errcode); + } + else + ret = 0.0; + } + } + + return ret; +} } // namespace funcexp // vim:ts=4 sw=4: diff --git a/utils/funcexp/func_floor.cpp b/utils/funcexp/func_floor.cpp index eb05b405f..8e632c971 100644 --- a/utils/funcexp/func_floor.cpp +++ b/utils/funcexp/func_floor.cpp @@ -118,6 +118,12 @@ int64_t Func_floor::getIntVal(Row& row, } break; + case execplan::CalpontSystemCatalog::LONGDOUBLE: + { + ret = (int64_t) floorl(parm[0]->data()->getLongDoubleVal(row, isNull)); + } + break; + case execplan::CalpontSystemCatalog::VARCHAR: case execplan::CalpontSystemCatalog::CHAR: case execplan::CalpontSystemCatalog::TEXT: @@ -216,6 +222,12 @@ uint64_t Func_floor::getUintVal(Row& row, } break; + case execplan::CalpontSystemCatalog::LONGDOUBLE: + { + ret = (uint64_t) floorl(parm[0]->data()->getLongDoubleVal(row, isNull)); + } + break; + case execplan::CalpontSystemCatalog::VARCHAR: case execplan::CalpontSystemCatalog::CHAR: case execplan::CalpontSystemCatalog::TEXT: @@ -286,6 +298,10 @@ double Func_floor::getDoubleVal(Row& row, { ret = floor(parm[0]->data()->getDoubleVal(row, isNull)); } + else if (op_ct.colDataType == CalpontSystemCatalog::LONGDOUBLE) + { + ret = floorl(parm[0]->data()->getLongDoubleVal(row, isNull)); + } else if (op_ct.colDataType == CalpontSystemCatalog::VARCHAR || op_ct.colDataType == CalpontSystemCatalog::CHAR || op_ct.colDataType == CalpontSystemCatalog::TEXT) @@ -303,6 +319,38 @@ double Func_floor::getDoubleVal(Row& row, return ret; } +long double Func_floor::getLongDoubleVal(Row& row, + FunctionParm& parm, + bool& isNull, + CalpontSystemCatalog::ColType& op_ct) +{ + long double ret = 0.0; + + if (op_ct.colDataType == CalpontSystemCatalog::LONGDOUBLE || + op_ct.colDataType == CalpontSystemCatalog::FLOAT) + { + ret = floor(parm[0]->data()->getDoubleVal(row, isNull)); + } + else if (op_ct.colDataType == CalpontSystemCatalog::LONGDOUBLE) + { + ret = floorl(parm[0]->data()->getLongDoubleVal(row, isNull)); + } + else if (op_ct.colDataType == CalpontSystemCatalog::VARCHAR || + op_ct.colDataType == CalpontSystemCatalog::CHAR || + op_ct.colDataType == CalpontSystemCatalog::TEXT) + { + const string& str = parm[0]->data()->getStrVal(row, isNull); + + if (!isNull) + ret = floor(strtod(str.c_str(), 0)); + } + else + { + ret = (long double) getIntVal(row, parm, isNull, op_ct); + } + + return ret; +} string Func_floor::getStrVal(Row& row, FunctionParm& parm, @@ -329,6 +377,18 @@ string Func_floor::getStrVal(Row& row, *d = '\0'; } + if (op_ct.colDataType == CalpontSystemCatalog::LONGDOUBLE) + { + snprintf(tmp, 511, "%Lf", getLongDoubleVal(row, parm, isNull, op_ct)); + + // remove the decimals in the oss string. + char* d = tmp; + + while ((*d != '.') && (*d != '\0')) + d++; + + *d = '\0'; + } else if (isUnsigned(op_ct.colDataType)) { #ifndef __LP64__ diff --git a/utils/funcexp/func_from_unixtime.cpp b/utils/funcexp/func_from_unixtime.cpp index 9968ba1f0..9f116c751 100644 --- a/utils/funcexp/func_from_unixtime.cpp +++ b/utils/funcexp/func_from_unixtime.cpp @@ -209,6 +209,14 @@ double Func_from_unixtime::getDoubleVal(rowgroup::Row& row, return (double) atoi(getStrVal(row, parm, isNull, ct).c_str()); } +long double Func_from_unixtime::getLongDoubleVal(rowgroup::Row& row, + FunctionParm& parm, + bool& isNull, + CalpontSystemCatalog::ColType& ct) +{ + return (long double) getDoubleVal(row, parm, isNull, ct); +} + } // namespace funcexp // vim:ts=4 sw=4: diff --git a/utils/funcexp/func_greatest.cpp b/utils/funcexp/func_greatest.cpp index de63c283e..4b55d2250 100644 --- a/utils/funcexp/func_greatest.cpp +++ b/utils/funcexp/func_greatest.cpp @@ -121,6 +121,26 @@ double Func_greatest::getDoubleVal(rowgroup::Row& row, return (double) greatestStr; } +long double Func_greatest::getLongDoubleVal(rowgroup::Row& row, + FunctionParm& fp, + bool& isNull, + execplan::CalpontSystemCatalog::ColType& op_ct) +{ + long double str = fp[0]->data()->getLongDoubleVal(row, isNull); + + long double greatestStr = str; + + for (uint32_t i = 1; i < fp.size(); i++) + { + long double str1 = fp[i]->data()->getLongDoubleVal(row, isNull); + + if ( greatestStr < str1 ) + greatestStr = str1; + } + + return greatestStr; +} + std::string Func_greatest::getStrVal(rowgroup::Row& row, FunctionParm& fp, bool& isNull, diff --git a/utils/funcexp/func_if.cpp b/utils/funcexp/func_if.cpp index ad65d10c9..7141037da 100644 --- a/utils/funcexp/func_if.cpp +++ b/utils/funcexp/func_if.cpp @@ -61,6 +61,9 @@ bool boolVal(SPTP& parm, Row& row) case CalpontSystemCatalog::UDOUBLE: ret = (parm->data()->getDoubleVal(row, isNull) != 0); + case CalpontSystemCatalog::LONGDOUBLE: + ret = (parm->data()->getLongDoubleVal(row, isNull) != 0); + case CalpontSystemCatalog::DECIMAL: case CalpontSystemCatalog::UDECIMAL: ret = (parm->data()->getDecimalVal(row, isNull).value != 0); @@ -188,6 +191,21 @@ double Func_if::getDoubleVal(Row& row, } } +long double Func_if::getLongDoubleVal(Row& row, + FunctionParm& parm, + bool& isNull, + CalpontSystemCatalog::ColType&) +{ + if (boolVal(parm[0], row)) + { + return parm[1]->data()->getLongDoubleVal(row, isNull); + } + else + { + return parm[2]->data()->getLongDoubleVal(row, isNull); + } +} + int32_t Func_if::getDateIntVal(Row& row, FunctionParm& parm, diff --git a/utils/funcexp/func_ifnull.cpp b/utils/funcexp/func_ifnull.cpp index 4b8606d4e..8879a98dc 100644 --- a/utils/funcexp/func_ifnull.cpp +++ b/utils/funcexp/func_ifnull.cpp @@ -129,6 +129,25 @@ double Func_ifnull::getDoubleVal(Row& row, return r; } +long double Func_ifnull::getLongDoubleVal(Row& row, + FunctionParm& parm, + bool& isNull, + CalpontSystemCatalog::ColType&) +{ + if (isNull) + return 0.0; + + long double r = parm[0]->data()->getLongDoubleVal(row, isNull); + + if (isNull) + { + isNull = false; + return parm[1]->data()->getLongDoubleVal(row, isNull); + } + + return r; +} + int32_t Func_ifnull::getDateIntVal(Row& row, FunctionParm& parm, diff --git a/utils/funcexp/func_in.cpp b/utils/funcexp/func_in.cpp index e271fa0f7..f34d9f8b6 100644 --- a/utils/funcexp/func_in.cpp +++ b/utils/funcexp/func_in.cpp @@ -203,6 +203,27 @@ inline bool getBoolForIn(rowgroup::Row& row, return false; } + case execplan::CalpontSystemCatalog::LONGDOUBLE: + { + long double val = pm[0]->data()->getLongDoubleVal(row, isNull); + + if (isNull) + return false; + + for (uint32_t i = 1; i < pm.size(); i++) + { + isNull = false; + + if ( val == pm[i]->data()->getLongDoubleVal(row, isNull) && !isNull ) + return true; + + if (isNull && isNotIn) + return true; // will be reversed to false by the caller + } + + return false; + } + case execplan::CalpontSystemCatalog::DECIMAL: case execplan::CalpontSystemCatalog::UDECIMAL: { diff --git a/utils/funcexp/func_isnull.cpp b/utils/funcexp/func_isnull.cpp index 3558f6e30..709abf814 100644 --- a/utils/funcexp/func_isnull.cpp +++ b/utils/funcexp/func_isnull.cpp @@ -74,6 +74,10 @@ bool Func_isnull::getBoolVal(Row& row, parm[0]->data()->getStrVal(row, isNull); break; + case CalpontSystemCatalog::LONGDOUBLE: + parm[0]->data()->getLongDoubleVal(row, isNull); + break; + default: parm[0]->data()->getIntVal(row, isNull); } diff --git a/utils/funcexp/func_least.cpp b/utils/funcexp/func_least.cpp index 9ba5c98de..874548d07 100644 --- a/utils/funcexp/func_least.cpp +++ b/utils/funcexp/func_least.cpp @@ -100,6 +100,26 @@ double Func_least::getDoubleVal(rowgroup::Row& row, return (double) leastStr; } +long double Func_least::getLongDoubleVal(rowgroup::Row& row, + FunctionParm& fp, + bool& isNull, + execplan::CalpontSystemCatalog::ColType& op_ct) +{ + long double str = fp[0]->data()->getLongDoubleVal(row, isNull); + + long double leastStr = str; + + for (uint32_t i = 1; i < fp.size(); i++) + { + long double str1 = fp[i]->data()->getLongDoubleVal(row, isNull); + + if ( leastStr > str1 ) + leastStr = str1; + } + + return leastStr; +} + std::string Func_least::getStrVal(rowgroup::Row& row, FunctionParm& fp, bool& isNull, diff --git a/utils/funcexp/func_math.cpp b/utils/funcexp/func_math.cpp index 8592ec8b0..6863517b8 100644 --- a/utils/funcexp/func_math.cpp +++ b/utils/funcexp/func_math.cpp @@ -1523,6 +1523,26 @@ string Func_format::getStrVal(Row& row, } break; + case execplan::CalpontSystemCatalog::LONGDOUBLE: + { + long double rawValue = parm[0]->data()->getLongDoubleVal(row, isNull); + + // roundup + if (scale < 0) scale = 0; + + if (rawValue >= 0) + rawValue += 0.5 / pow(10.0, scale); + else + rawValue -= 0.5 / pow(10.0, scale); + + // double's can be *really* long to print out. Max mysql + // is e308 so allow for 308 + 36 decimal places minimum. + char buf[384]; + snprintf(buf, 384, "%0.36Lf", rawValue); + value = buf; + } + break; + default: { std::ostringstream oss; diff --git a/utils/funcexp/func_mod.cpp b/utils/funcexp/func_mod.cpp index c5eb05112..a13780d17 100644 --- a/utils/funcexp/func_mod.cpp +++ b/utils/funcexp/func_mod.cpp @@ -142,6 +142,14 @@ double Func_mod::getDoubleVal(Row& row, } break; + case execplan::CalpontSystemCatalog::LONGDOUBLE: + { + long double value = parm[0]->data()->getLongDoubleVal(row, isNull); + + mod = (double)fmodl(value, div); + } + break; + case execplan::CalpontSystemCatalog::FLOAT: case execplan::CalpontSystemCatalog::UFLOAT: { @@ -181,6 +189,110 @@ double Func_mod::getDoubleVal(Row& row, return mod; } +long double Func_mod::getLongDoubleVal(Row& row, + FunctionParm& parm, + bool& isNull, + CalpontSystemCatalog::ColType& operationColType) +{ + if ( parm.size() < 2 ) + { + isNull = true; + return 0; + } + + int64_t div = parm[1]->data()->getIntVal(row, isNull); + + if ( div == 0 ) + { + isNull = true; + return 0; + } + + long double mod = 0; + + switch (parm[0]->data()->resultType().colDataType) + { + case execplan::CalpontSystemCatalog::BIGINT: + case execplan::CalpontSystemCatalog::INT: + case execplan::CalpontSystemCatalog::MEDINT: + case execplan::CalpontSystemCatalog::TINYINT: + case execplan::CalpontSystemCatalog::SMALLINT: + { + int64_t value = parm[0]->data()->getIntVal(row, isNull); + + mod = value % div; + } + break; + + case execplan::CalpontSystemCatalog::UBIGINT: + case execplan::CalpontSystemCatalog::UINT: + case execplan::CalpontSystemCatalog::UMEDINT: + case execplan::CalpontSystemCatalog::UTINYINT: + case execplan::CalpontSystemCatalog::USMALLINT: + { + uint64_t udiv = parm[1]->data()->getIntVal(row, isNull); + uint64_t uvalue = parm[0]->data()->getUintVal(row, isNull); + + mod = uvalue % udiv; + } + break; + + case execplan::CalpontSystemCatalog::DOUBLE: + case execplan::CalpontSystemCatalog::UDOUBLE: + { + double value = parm[0]->data()->getDoubleVal(row, isNull); + + mod = fmod(value, div); + } + break; + + case execplan::CalpontSystemCatalog::FLOAT: + case execplan::CalpontSystemCatalog::UFLOAT: + { + float value = parm[0]->data()->getFloatVal(row, isNull); + + mod = fmod(value, div); + } + break; + + case execplan::CalpontSystemCatalog::LONGDOUBLE: + { + long double value = parm[0]->data()->getLongDoubleVal(row, isNull); + + mod = fmodl(value, div); + } + break; + + case execplan::CalpontSystemCatalog::DECIMAL: + case execplan::CalpontSystemCatalog::UDECIMAL: + { + IDB_Decimal d = parm[0]->data()->getDecimalVal(row, isNull); + int64_t value = d.value / helpers::power(d.scale); + + mod = value % div; + } + break; + + case execplan::CalpontSystemCatalog::CHAR: + case execplan::CalpontSystemCatalog::VARCHAR: + case execplan::CalpontSystemCatalog::TEXT: + { + double value = parm[0]->data()->getDoubleVal(row, isNull); + mod = fmod(value, div); + break; + } + + default: + { + std::ostringstream oss; + oss << "mod: datatype of " << execplan::colDataTypeToString(parm[0]->data()->resultType().colDataType); + throw logging::IDBExcept(oss.str(), ERR_DATATYPE_NOT_SUPPORT); + } + } + + return mod; +} + int64_t Func_mod::getIntVal(Row& row, FunctionParm& parm, bool& isNull, @@ -250,6 +362,14 @@ int64_t Func_mod::getIntVal(Row& row, } break; + case execplan::CalpontSystemCatalog::LONGDOUBLE: + { + long double value = parm[0]->data()->getLongDoubleVal(row, isNull); + + mod = fmodl(value, div); + } + break; + case execplan::CalpontSystemCatalog::DECIMAL: case execplan::CalpontSystemCatalog::UDECIMAL: { @@ -340,6 +460,14 @@ uint64_t Func_mod::getUIntVal(Row& row, } break; + case execplan::CalpontSystemCatalog::LONGDOUBLE: + { + long double value = parm[0]->data()->getLongDoubleVal(row, isNull); + + mod = fmodl(value, div); + } + break; + case execplan::CalpontSystemCatalog::DECIMAL: case execplan::CalpontSystemCatalog::UDECIMAL: { @@ -387,6 +515,10 @@ std::string Func_mod::getStrVal(Row& row, return intToString(getIntVal(row, fp, isNull, op_ct)); break; + case execplan::CalpontSystemCatalog::LONGDOUBLE: + return longDoubleToString(getLongDoubleVal(row, fp, isNull, op_ct)); + break; + default: return doubleToString(getDoubleVal(row, fp, isNull, op_ct)); break; diff --git a/utils/funcexp/func_nullif.cpp b/utils/funcexp/func_nullif.cpp index 04a45534a..3a40fe134 100644 --- a/utils/funcexp/func_nullif.cpp +++ b/utils/funcexp/func_nullif.cpp @@ -579,6 +579,109 @@ double Func_nullif::getDoubleVal(rowgroup::Row& row, break; } + case execplan::CalpontSystemCatalog::LONGDOUBLE: + { + exp2 = (double)parm[1]->data()->getLongDoubleVal(row, isNull); + + if (isNull) + { + isNull = false; + return exp1; + } + + break; + } + + case execplan::CalpontSystemCatalog::DATE: + { + exp2 = parm[1]->data()->getDateIntVal(row, isNull); + + if (isNull) + { + isNull = false; + return exp1; + } + + break; + } + break; + + case execplan::CalpontSystemCatalog::TIME: + case execplan::CalpontSystemCatalog::DATETIME: + { + exp2 = parm[1]->data()->getDatetimeIntVal(row, isNull); + + if (isNull) + { + isNull = false; + return exp1; + } + + break; + } + + default: + { + isNull = true; + } + } + + if ( exp1 == exp2 ) + { + isNull = true; + return 0; + } + + return exp1; +} + + +long double Func_nullif::getLongDoubleVal(rowgroup::Row& row, + FunctionParm& parm, + bool& isNull, + execplan::CalpontSystemCatalog::ColType& op_ct) +{ + long double exp1 = parm[0]->data()->getLongDoubleVal(row, isNull); + long double exp2 = 0; + + switch (parm[1]->data()->resultType().colDataType) + { + case execplan::CalpontSystemCatalog::BIGINT: + case execplan::CalpontSystemCatalog::INT: + case execplan::CalpontSystemCatalog::MEDINT: + case execplan::CalpontSystemCatalog::TINYINT: + case execplan::CalpontSystemCatalog::SMALLINT: + case execplan::CalpontSystemCatalog::DOUBLE: + case execplan::CalpontSystemCatalog::FLOAT: + case execplan::CalpontSystemCatalog::DECIMAL: + case execplan::CalpontSystemCatalog::VARCHAR: + case execplan::CalpontSystemCatalog::CHAR: + case execplan::CalpontSystemCatalog::TEXT: + { + exp2 = parm[1]->data()->getDoubleVal(row, isNull); + + if (isNull) + { + isNull = false; + return exp1; + } + + break; + } + + case execplan::CalpontSystemCatalog::LONGDOUBLE: + { + exp2 = parm[1]->data()->getLongDoubleVal(row, isNull); + + if (isNull) + { + isNull = false; + return exp1; + } + + break; + } + case execplan::CalpontSystemCatalog::DATE: { exp2 = parm[1]->data()->getDateIntVal(row, isNull); diff --git a/utils/funcexp/func_pow.cpp b/utils/funcexp/func_pow.cpp index c838d174e..cbe1a9905 100644 --- a/utils/funcexp/func_pow.cpp +++ b/utils/funcexp/func_pow.cpp @@ -89,6 +89,44 @@ double Func_pow::getDoubleVal(Row& row, return 0.0; } +long double Func_pow::getLongDoubleVal(Row& row, + FunctionParm& parm, + bool& isNull, + CalpontSystemCatalog::ColType&) +{ + // null value is indicated by isNull + long double base = parm[0]->data()->getLongDoubleVal(row, isNull); + + if (!isNull) + { + // Should this be long double? Not sure on usage. + double exponent = parm[1]->data()->getDoubleVal(row, isNull); + + if (!isNull) + { + errno = 0; + long double x = powl(base, (long double)exponent); + + // @bug3490, 4461, rule out domain error, pole error and overflow range error. + if (!isfinite(x)) + { + isNull = true; + Message::Args args; + args.add("pow"); + args.add((double)base); + args.add(exponent); + unsigned errcode = ERR_FUNC_OUT_OF_RANGE_RESULT; + throw IDBExcept(IDBErrorInfo::instance()->errorMsg(errcode, args), errcode); + } + + + return x; + } + } + + return 0.0; +} + } // namespace funcexp // vim:ts=4 sw=4: diff --git a/utils/funcexp/func_round.cpp b/utils/funcexp/func_round.cpp index c107fc0fc..eab18abc2 100644 --- a/utils/funcexp/func_round.cpp +++ b/utils/funcexp/func_round.cpp @@ -45,7 +45,9 @@ namespace using namespace funcexp; -inline void decimalPlaceDouble(FunctionParm& fp, int64_t& s, double& p, Row& row, bool& isNull) +// P should either be double or long double +template +inline void decimalPlaceDouble(FunctionParm& fp, int64_t& s, P& p, Row& row, bool& isNull) { s = fp[1]->data()->getIntVal(row, isNull); int64_t d = s; @@ -60,9 +62,9 @@ inline void decimalPlaceDouble(FunctionParm& fp, int64_t& s, double& p, Row& row r *= 10; if (d >= 0) - p = (double) r; + p = (P) r; else - p = 1.0 / ((double) r); + p = 1.0 / ((P) r); } } @@ -200,6 +202,67 @@ double Func_round::getDoubleVal(Row& row, return d; } +long double Func_round::getLongDoubleVal(Row& row, + FunctionParm& parm, + bool& isNull, + CalpontSystemCatalog::ColType& op_ct) +{ + if (execplan::CalpontSystemCatalog::LONGDOUBLE == op_ct.colDataType) + { + int64_t d = 0; + long double p = 1; + + if (parm.size() > 1) // round(X, D) + decimalPlaceDouble(parm, d, p, row, isNull); + + if (isNull) + return 0.0; + + long double x = parm[0]->data()->getLongDoubleVal(row, isNull); + + if (!isNull) + { + x *= p; + + if (x >= 0) + x = floor(x + 0.5); + else + x = ceil(x - 0.5); + + if (p != 0.0) + x /= p; + else + x = 0.0; + } + + return x; + } + + if (isUnsigned(op_ct.colDataType)) + { + return getUintVal(row, parm, isNull, op_ct); + } + + IDB_Decimal x = getDecimalVal(row, parm, isNull, op_ct); + + if (isNull) + return 0.0; + + double d = x.value; + + if (x.scale > 0) + { + while (x.scale-- > 0) + d /= 10.0; + } + else + { + while (x.scale++ < 0) + d *= 10.0; + } + + return d; +} IDB_Decimal Func_round::getDecimalVal(Row& row, FunctionParm& parm, @@ -330,6 +393,34 @@ IDB_Decimal Func_round::getDecimalVal(Row& row, } break; + case execplan::CalpontSystemCatalog::LONGDOUBLE: + { + int64_t s = 0; + long double p = 1; + + if (parm.size() > 1) // round(X, D) + decimalPlaceDouble(parm, s, p, row, isNull); + + if (isNull) + break; + + long double x = parm[0]->data()->getDoubleVal(row, isNull); + + if (!isNull) + { + x *= p; + + if (x >= 0) + x = floor(x + 0.5); + else + x = ceil(x - 0.5); + + decimal.value = (int64_t) x; + decimal.scale = s; + } + } + break; + case execplan::CalpontSystemCatalog::DATE: { int32_t s = 0; diff --git a/utils/funcexp/func_truncate.cpp b/utils/funcexp/func_truncate.cpp index 2da482ab8..9976e9c38 100644 --- a/utils/funcexp/func_truncate.cpp +++ b/utils/funcexp/func_truncate.cpp @@ -42,7 +42,9 @@ namespace using namespace funcexp; -inline void decimalPlaceDouble(FunctionParm& fp, int64_t& s, double& p, Row& row, bool& isNull) +// P should be double or long double +template +inline void decimalPlaceDouble(FunctionParm& fp, int64_t& s, P& p, Row& row, bool& isNull) { s = fp[1]->data()->getIntVal(row, isNull); int64_t d = s; @@ -57,9 +59,9 @@ inline void decimalPlaceDouble(FunctionParm& fp, int64_t& s, double& p, Row& row r *= 10; if (d >= 0) - p = (double) r; + p = (P) r; else - p = 1.0 / ((double) r); + p = 1.0 / ((P) r); } } @@ -223,6 +225,60 @@ double Func_truncate::getDoubleVal(Row& row, return d; } +long double Func_truncate::getLongDoubleVal(Row& row, + FunctionParm& parm, + bool& isNull, + CalpontSystemCatalog::ColType& op_ct) +{ + if (execplan::CalpontSystemCatalog::LONGDOUBLE == op_ct.colDataType) + { + int64_t d = 0; + long double p = 1; + decimalPlaceDouble(parm, d, p, row, isNull); + + if (isNull) + return 0.0; + + long double x = parm[0]->data()->getLongDoubleVal(row, isNull); + + if (!isNull) + { + x *= p; + + if (x > 0) + x = floor(x); + else + x = ceil(x); + + if (p != 0.0) + x /= p; + else + x = 0.0; + } + + return x; + } + + IDB_Decimal x = getDecimalVal(row, parm, isNull, op_ct); + + if (isNull) + return 0.0; + + double d = x.value; + + if (x.scale > 0) + { + while (x.scale-- > 0) + d /= 10.0; + } + else + { + while (x.scale++ < 0) + d *= 10.0; + } + + return d; +} IDB_Decimal Func_truncate::getDecimalVal(Row& row, FunctionParm& parm, @@ -325,6 +381,26 @@ IDB_Decimal Func_truncate::getDecimalVal(Row& row, } break; + case execplan::CalpontSystemCatalog::LONGDOUBLE: + { + int64_t s = 0; + long double p = 1; + decimalPlaceDouble(parm, s, p, row, isNull); + + if (isNull) + break; + + long double x = parm[0]->data()->getLongDoubleVal(row, isNull); + + if (!isNull) + { + x *= p; + decimal.value = (int64_t) x; + decimal.scale = s; + } + } + break; + case execplan::CalpontSystemCatalog::DATE: { int32_t s = 0; diff --git a/utils/funcexp/funchelpers.h b/utils/funcexp/funchelpers.h index ebb78f8cc..b6942bc68 100644 --- a/utils/funcexp/funchelpers.h +++ b/utils/funcexp/funchelpers.h @@ -751,6 +751,16 @@ string doubleToString(double d) return buf; } +inline +string longDoubleToString(long double ld) +{ + // long double's can be *really* long to print out. Max mysql + // is e308 so allow for 308 + 36 decimal places minimum. + char buf[384]; + snprintf(buf, 384, "%Lf", ld); + return buf; +} + //@bug6146, remove duplicate function with incorrect impl. Use the DataConvert::decimalToString() //string decimalToString( execplan::IDB_Decimal x, int p ) diff --git a/utils/funcexp/functor.cpp b/utils/funcexp/functor.cpp index 1e50ea1fc..4350d2dc0 100644 --- a/utils/funcexp/functor.cpp +++ b/utils/funcexp/functor.cpp @@ -60,6 +60,7 @@ void Func::init() double* dp = reinterpret_cast(&dni); fDoubleNullVal = *dp; + fDoubleNullVal = joblist::LONGDOUBLENULL; } @@ -341,6 +342,11 @@ string Func::doubleToString(double d) return helpers::doubleToString(d); } +string Func::longDoubleToString(long double ld) +{ + return helpers::longDoubleToString(ld); +} + } // namespace funcexp // vim:ts=4 sw=4: diff --git a/utils/funcexp/functor.h b/utils/funcexp/functor.h index 20914e99e..ff3d68976 100644 --- a/utils/funcexp/functor.h +++ b/utils/funcexp/functor.h @@ -90,6 +90,11 @@ public: bool& isNull, execplan::CalpontSystemCatalog::ColType& op_ct) = 0; + virtual long double getLongDoubleVal(rowgroup::Row& row, + FunctionParm& fp, + bool& isNull, + execplan::CalpontSystemCatalog::ColType& op_ct) = 0; + virtual std::string getStrVal(rowgroup::Row& row, FunctionParm& fp, bool& isNull, @@ -152,6 +157,11 @@ public: { return fDoubleNullVal; } + const long double longDoubleNullVal() const + { + return fLongDoubleNullVal; + } + protected: virtual uint32_t stringToDate(std::string); @@ -164,6 +174,7 @@ protected: virtual std::string intToString(int64_t); virtual std::string doubleToString(double); + virtual std::string longDoubleToString(long double); virtual int64_t nowDatetime(); virtual int64_t addTime(DateTime& dt1, dataconvert::Time& dt2); @@ -180,6 +191,7 @@ private: float fFloatNullVal; double fDoubleNullVal; + long double fLongDoubleNullVal; }; diff --git a/utils/funcexp/functor_all.h b/utils/funcexp/functor_all.h index 553abc40f..24dbebfaf 100644 --- a/utils/funcexp/functor_all.h +++ b/utils/funcexp/functor_all.h @@ -85,6 +85,11 @@ public: bool& isNull, execplan::CalpontSystemCatalog::ColType& op_ct); + long double getLongDoubleVal(rowgroup::Row& row, + FunctionParm& fp, + bool& isNull, + execplan::CalpontSystemCatalog::ColType& op_ct); + std::string getStrVal(rowgroup::Row& row, FunctionParm& fp, bool& isNull, @@ -137,6 +142,11 @@ public: bool& isNull, execplan::CalpontSystemCatalog::ColType& op_ct); + long double getLongDoubleVal(rowgroup::Row& row, + FunctionParm& fp, + bool& isNull, + execplan::CalpontSystemCatalog::ColType& op_ct); + std::string getStrVal(rowgroup::Row& row, FunctionParm& fp, bool& isNull, @@ -185,6 +195,11 @@ public: bool& isNull, execplan::CalpontSystemCatalog::ColType& op_ct); + long double getLongDoubleVal(rowgroup::Row& row, + FunctionParm& fp, + bool& isNull, + execplan::CalpontSystemCatalog::ColType& op_ct); + std::string getStrVal(rowgroup::Row& row, FunctionParm& fp, bool& isNull, @@ -232,6 +247,11 @@ public: bool& isNull, execplan::CalpontSystemCatalog::ColType& op_ct); + long double getLongDoubleVal(rowgroup::Row& row, + FunctionParm& fp, + bool& isNull, + execplan::CalpontSystemCatalog::ColType& op_ct); + std::string getStrVal(rowgroup::Row& row, FunctionParm& fp, bool& isNull, @@ -289,6 +309,11 @@ public: bool& isNull, execplan::CalpontSystemCatalog::ColType& op_ct); + long double getLongDoubleVal(rowgroup::Row& row, + FunctionParm& fp, + bool& isNull, + execplan::CalpontSystemCatalog::ColType& op_ct); + std::string getStrVal(rowgroup::Row& row, FunctionParm& fp, bool& isNull, @@ -337,6 +362,11 @@ public: bool& isNull, execplan::CalpontSystemCatalog::ColType& op_ct); + long double getLongDoubleVal(rowgroup::Row& row, + FunctionParm& fp, + bool& isNull, + execplan::CalpontSystemCatalog::ColType& op_ct); + std::string getStrVal(rowgroup::Row& row, FunctionParm& fp, bool& isNull, @@ -384,6 +414,11 @@ public: bool& isNull, execplan::CalpontSystemCatalog::ColType& op_ct); + long double getLongDoubleVal(rowgroup::Row& row, + FunctionParm& fp, + bool& isNull, + execplan::CalpontSystemCatalog::ColType& op_ct); + std::string getStrVal(rowgroup::Row& row, FunctionParm& fp, bool& isNull, @@ -456,6 +491,11 @@ public: bool& isNull, execplan::CalpontSystemCatalog::ColType& op_ct); + long double getLongDoubleVal(rowgroup::Row& row, + FunctionParm& fp, + bool& isNull, + execplan::CalpontSystemCatalog::ColType& op_ct); + execplan::IDB_Decimal getDecimalVal(rowgroup::Row& row, FunctionParm& fp, bool& isNull, diff --git a/utils/funcexp/functor_bool.h b/utils/funcexp/functor_bool.h index b7b85106b..27e32fbbc 100644 --- a/utils/funcexp/functor_bool.h +++ b/utils/funcexp/functor_bool.h @@ -64,6 +64,14 @@ public: return (getBoolVal(row, fp, isNull, op_ct) ? 1.0 : 0.0); } + long double getLongDoubleVal(rowgroup::Row& row, + FunctionParm& fp, + bool& isNull, + execplan::CalpontSystemCatalog::ColType& op_ct) + { + return (getBoolVal(row, fp, isNull, op_ct) ? 1.0 : 0.0); + } + std::string getStrVal(rowgroup::Row& row, FunctionParm& fp, bool& isNull, diff --git a/utils/funcexp/functor_dtm.h b/utils/funcexp/functor_dtm.h index bcff47854..79c4eabd1 100644 --- a/utils/funcexp/functor_dtm.h +++ b/utils/funcexp/functor_dtm.h @@ -57,6 +57,14 @@ public: return (double) getIntVal(row, fp, isNull, op_ct); } + long double getLongDoubleVal(rowgroup::Row& row, + FunctionParm& fp, + bool& isNull, + execplan::CalpontSystemCatalog::ColType& op_ct) + { + return (long double) getIntVal(row, fp, isNull, op_ct); + } + /* std::string getStrVal(rowgroup::Row& row, FunctionParm& fp, @@ -196,6 +204,11 @@ public: bool& isNull, execplan::CalpontSystemCatalog::ColType& op_ct); + long double getLongDoubleVal(rowgroup::Row& row, + FunctionParm& fp, + bool& isNull, + execplan::CalpontSystemCatalog::ColType& op_ct); + std::string getStrVal(rowgroup::Row& row, FunctionParm& fp, bool& isNull, @@ -238,6 +251,11 @@ public: bool& isNull, execplan::CalpontSystemCatalog::ColType& op_ct); + long double getLongDoubleVal(rowgroup::Row& row, + FunctionParm& fp, + bool& isNull, + execplan::CalpontSystemCatalog::ColType& op_ct); + std::string getStrVal(rowgroup::Row& row, FunctionParm& fp, bool& isNull, @@ -423,6 +441,11 @@ public: bool& isNull, execplan::CalpontSystemCatalog::ColType& op_ct); + long double getLongDoubleVal(rowgroup::Row& row, + FunctionParm& fp, + bool& isNull, + execplan::CalpontSystemCatalog::ColType& op_ct); + std::string getStrVal(rowgroup::Row& row, FunctionParm& fp, bool& isNull, diff --git a/utils/funcexp/functor_export.h b/utils/funcexp/functor_export.h index a58293f98..8cec08373 100644 --- a/utils/funcexp/functor_export.h +++ b/utils/funcexp/functor_export.h @@ -58,6 +58,14 @@ public: bool& isNull, execplan::CalpontSystemCatalog::ColType& op_ct); + long double getLongDoubleVal(rowgroup::Row& row, + FunctionParm& fp, + bool& isNull, + execplan::CalpontSystemCatalog::ColType& op_ct) + { + return (long double)getDoubleVal(row, fp, isNull, op_ct); + } + std::string getStrVal(rowgroup::Row& row, FunctionParm& fp, bool& isNull, diff --git a/utils/funcexp/functor_int.h b/utils/funcexp/functor_int.h index 2eb7cc63e..15319ee3a 100644 --- a/utils/funcexp/functor_int.h +++ b/utils/funcexp/functor_int.h @@ -55,6 +55,14 @@ public: return ((double) getIntVal(row, fp, isNull, op_ct)); } + long double getLongDoubleVal(rowgroup::Row& row, + FunctionParm& fp, + bool& isNull, + execplan::CalpontSystemCatalog::ColType& op_ct) + { + return ((long double) getIntVal(row, fp, isNull, op_ct)); + } + std::string getStrVal(rowgroup::Row& row, FunctionParm& fp, bool& isNull, diff --git a/utils/funcexp/functor_real.h b/utils/funcexp/functor_real.h index 5d40ce589..341602ddf 100644 --- a/utils/funcexp/functor_real.h +++ b/utils/funcexp/functor_real.h @@ -57,12 +57,14 @@ public: { return ((uint64_t) getDoubleVal(row, fp, isNull, op_ct)); } - /* - double getDoubleVal(rowgroup::Row& row, - FunctionParm& fp, - bool& isNull, - execplan::CalpontSystemCatalog::ColType& op_ct) = 0; - */ + + long double getLongDoubleVal(rowgroup::Row& row, + FunctionParm& fp, + bool& isNull, + execplan::CalpontSystemCatalog::ColType& op_ct) + { + return ((long double) getDoubleVal(row, fp, isNull, op_ct)); + } std::string getStrVal(rowgroup::Row& row, FunctionParm& fp, @@ -99,6 +101,11 @@ public: bool& isNull, execplan::CalpontSystemCatalog::ColType& op_ct); + long double getLongDoubleVal(rowgroup::Row& row, + FunctionParm& fp, + bool& isNull, + execplan::CalpontSystemCatalog::ColType& op_ct); + execplan::IDB_Decimal getDecimalVal(rowgroup::Row& row, FunctionParm& fp, bool& isNull, @@ -120,6 +127,11 @@ public: FunctionParm& fp, bool& isNull, execplan::CalpontSystemCatalog::ColType& op_ct); + + long double getLongDoubleVal(rowgroup::Row& row, + FunctionParm& fp, + bool& isNull, + execplan::CalpontSystemCatalog::ColType& op_ct); }; @@ -137,6 +149,11 @@ public: FunctionParm& fp, bool& isNull, execplan::CalpontSystemCatalog::ColType& op_ct); + + long double getLongDoubleVal(rowgroup::Row& row, + FunctionParm& fp, + bool& isNull, + execplan::CalpontSystemCatalog::ColType& op_ct); }; @@ -165,6 +182,11 @@ public: bool& isNull, execplan::CalpontSystemCatalog::ColType& op_ct); + long double getLongDoubleVal(rowgroup::Row& row, + FunctionParm& fp, + bool& isNull, + execplan::CalpontSystemCatalog::ColType& op_ct); + execplan::IDB_Decimal getDecimalVal(rowgroup::Row& row, FunctionParm& fp, bool& isNull, @@ -202,6 +224,11 @@ public: bool& isNull, execplan::CalpontSystemCatalog::ColType& op_ct); + long double getLongDoubleVal(rowgroup::Row& row, + FunctionParm& fp, + bool& isNull, + execplan::CalpontSystemCatalog::ColType& op_ct); + std::string getStrVal(rowgroup::Row& row, FunctionParm& fp, bool& isNull, @@ -239,6 +266,11 @@ public: bool& isNull, execplan::CalpontSystemCatalog::ColType& op_ct); + long double getLongDoubleVal(rowgroup::Row& row, + FunctionParm& fp, + bool& isNull, + execplan::CalpontSystemCatalog::ColType& op_ct); + std::string getStrVal(rowgroup::Row& row, FunctionParm& fp, bool& isNull, @@ -271,6 +303,11 @@ public: bool& isNull, execplan::CalpontSystemCatalog::ColType& op_ct); + long double getLongDoubleVal(rowgroup::Row& row, + FunctionParm& fp, + bool& isNull, + execplan::CalpontSystemCatalog::ColType& op_ct); + std::string getStrVal(rowgroup::Row& row, FunctionParm& fp, bool& isNull, @@ -350,6 +387,11 @@ public: bool& isNull, execplan::CalpontSystemCatalog::ColType& op_ct); + long double getLongDoubleVal(rowgroup::Row& row, + FunctionParm& fp, + bool& isNull, + execplan::CalpontSystemCatalog::ColType& op_ct); + execplan::IDB_Decimal getDecimalVal(rowgroup::Row& row, FunctionParm& fp, bool& isNull, diff --git a/utils/funcexp/functor_str.h b/utils/funcexp/functor_str.h index 820228848..6624290ac 100644 --- a/utils/funcexp/functor_str.h +++ b/utils/funcexp/functor_str.h @@ -57,7 +57,7 @@ public: return strtod(getStrVal(row, fp, isNull, op_ct).c_str(), NULL); } - double getLongDoubleVal(rowgroup::Row& row, + long double getLongDoubleVal(rowgroup::Row& row, FunctionParm& fp, bool& isNull, execplan::CalpontSystemCatalog::ColType& op_ct) diff --git a/utils/joiner/tuplejoiner.cpp b/utils/joiner/tuplejoiner.cpp index edc94b067..c84e54ae0 100644 --- a/utils/joiner/tuplejoiner.cpp +++ b/utils/joiner/tuplejoiner.cpp @@ -45,7 +45,14 @@ TupleJoiner::TupleJoiner( smallRG(smallInput), largeRG(largeInput), joinAlg(INSERTING), joinType(jt), threadCount(1), typelessJoin(false), bSignedUnsignedJoin(false), uniqueLimit(100), finished(false) { - if (smallRG.usesStringTable()) + if (smallRG.getColTypes()[smallJoinColumn] == CalpontSystemCatalog::LONGDOUBLE) + { + STLPoolAllocator > alloc(64 * 1024 * 1024 + 1); + _pool = alloc.getPoolAllocator(); + + ld.reset(new ldhash_t(10, hasher(), ldhash_t::key_equal(), alloc)); + } + else if (smallRG.usesStringTable()) { STLPoolAllocator > alloc(64 * 1024 * 1024 + 1); _pool = alloc.getPoolAllocator(); @@ -131,8 +138,14 @@ TupleJoiner::TupleJoiner( if (keyLength > 65536) keyLength = 65536; } + else if (smallRG.getColTypes()[smallKeyColumns[i]] == CalpontSystemCatalog::LONGDOUBLE) + { + keyLength += sizeof(long double); + } else + { keyLength += 8; + } // Set bSignedUnsignedJoin if one or more join columns are signed to unsigned compares. if (smallRG.isUnsigned(smallKeyColumns[i]) != largeRG.isUnsigned(largeKeyColumns[i])) @@ -198,9 +211,20 @@ void TupleJoiner::insert(Row& r, bool zeroTheRid) { if (typelessJoin) { - ht->insert(pair - (makeTypelessKey(r, smallKeyColumns, keyLength, &storedKeyAlloc), - r.getPointer())); + TypelessData td = makeTypelessKey(r, smallKeyColumns, keyLength, &storedKeyAlloc, + largeRG, largeKeyColumns); + if (td.len > 0) + { + ht->insert(pair(td, r.getPointer())); + } + } + else if (r.getColType(smallKeyColumns[0]) == execplan::CalpontSystemCatalog::LONGDOUBLE) + { + long double smallKey = r.getLongDoubleField(smallKeyColumns[0]); + if (UNLIKELY(smallKey == joblist::LONGDOUBLENULL)) + ld->insert(pair(joblist::LONGDOUBLENULL, r.getPointer())); + else + ld->insert(pair(smallKey, r.getPointer())); } else if (!smallRG.usesStringTable()) { @@ -260,9 +284,16 @@ void TupleJoiner::match(rowgroup::Row& largeSideRow, uint32_t largeRowIndex, uin thIterator it; pair range; - largeKey = makeTypelessKey(largeSideRow, largeKeyColumns, keyLength, &tmpKeyAlloc[threadID]); - it = ht->find(largeKey); - + largeKey = makeTypelessKey(largeSideRow, largeKeyColumns, keyLength, &tmpKeyAlloc[threadID], smallRG, smallKeyColumns); + if (largeKey.len > 0) + { + it = ht->find(largeKey); + } + else + { + return; + } + if (it == ht->end() && !(joinType & (LARGEOUTER | MATCHNULLS))) return; @@ -271,6 +302,28 @@ void TupleJoiner::match(rowgroup::Row& largeSideRow, uint32_t largeRowIndex, uin for (; range.first != range.second; ++range.first) matches->push_back(range.first->second); } + else if (largeSideRow.getColType(largeKeyColumns[0]) == CalpontSystemCatalog::LONGDOUBLE + && ld) + { + // This is a compare of two long double + long double largeKey; + ldIterator it; + pair range; + Row r; + + largeKey = largeSideRow.getLongDoubleField(largeKeyColumns[0]); + it = ld->find(largeKey); + + if (it == ld->end() && !(joinType & (LARGEOUTER | MATCHNULLS))) + return; + + range = ld->equal_range(largeKey); + + for (; range.first != range.second; ++range.first) + { + matches->push_back(range.first->second); + } + } else if (!smallRG.usesStringTable()) { int64_t largeKey; @@ -278,7 +331,11 @@ void TupleJoiner::match(rowgroup::Row& largeSideRow, uint32_t largeRowIndex, uin pair range; Row r; - if (largeSideRow.isUnsigned(largeKeyColumns[0])) + if (largeSideRow.getColType(largeKeyColumns[0]) == CalpontSystemCatalog::LONGDOUBLE) + { + largeKey = (int64_t)largeSideRow.getLongDoubleField(largeKeyColumns[0]); + } + else if (largeSideRow.isUnsigned(largeKeyColumns[0])) { largeKey = (int64_t)largeSideRow.getUintField(largeKeyColumns[0]); } @@ -287,19 +344,41 @@ void TupleJoiner::match(rowgroup::Row& largeSideRow, uint32_t largeRowIndex, uin largeKey = largeSideRow.getIntField(largeKeyColumns[0]); } - it = h->find(largeKey); - - if (it == end() && !(joinType & (LARGEOUTER | MATCHNULLS))) - return; - - range = h->equal_range(largeKey); - - //smallRG.initRow(&r); - for (; range.first != range.second; ++range.first) + if (ld) { - //r.setData(range.first->second); - //cerr << "matched small side row: " << r.toString() << endl; - matches->push_back(range.first->second); + // Compare against long double + ldIterator it; + pair range; + long double ldKey = largeKey; +// ldKey = 6920; + it = ld->find(ldKey); + + if (it == ld->end() && !(joinType & (LARGEOUTER | MATCHNULLS))) + return; + + range = ld->equal_range(ldKey); + + for (; range.first != range.second; ++range.first) + { + matches->push_back(range.first->second); + } + } + else + { + it = h->find(largeKey); + + if (it == h->end() && !(joinType & (LARGEOUTER | MATCHNULLS))) + return; + + range = h->equal_range(largeKey); + + //smallRG.initRow(&r); + for (; range.first != range.second; ++range.first) + { + //r.setData(range.first->second); + //cerr << "matched small side row: " << r.toString() << endl; + matches->push_back(range.first->second); + } } } else @@ -335,7 +414,14 @@ void TupleJoiner::match(rowgroup::Row& largeSideRow, uint32_t largeRowIndex, uin if (UNLIKELY(inUM() && (joinType & MATCHNULLS) && !isNull && !typelessJoin)) { - if (!smallRG.usesStringTable()) + if (smallRG.getColType(largeKeyColumns[0]) == CalpontSystemCatalog::LONGDOUBLE) + { + pair range = ld->equal_range(joblist::LONGDOUBLENULL); + + for (; range.first != range.second; ++range.first) + matches->push_back(range.first->second); + } + else if (!smallRG.usesStringTable()) { pair range = h->equal_range(getJoinNullValue()); @@ -357,7 +443,14 @@ void TupleJoiner::match(rowgroup::Row& largeSideRow, uint32_t largeRowIndex, uin { if (!typelessJoin) { - if (!smallRG.usesStringTable()) + if (smallRG.getColType(smallKeyColumns[0]) == CalpontSystemCatalog::LONGDOUBLE) + { + ldIterator it; + + for (it = ld->begin(); it != ld->end(); ++it) + matches->push_back(it->second); + } + else if (!smallRG.usesStringTable()) { iterator it; @@ -410,6 +503,7 @@ void TupleJoiner::doneInserting() tr1::unordered_set::iterator uit; sthash_t::iterator sthit; hash_t::iterator hit; + ldhash_t::iterator ldit; typelesshash_t::iterator thit; uint32_t i, pmpos = 0, rowCount; Row smallRow; @@ -425,6 +519,8 @@ void TupleJoiner::doneInserting() pmpos = 0; else if (typelessJoin) thit = ht->begin(); + else if (smallRG.getColType(smallKeyColumns[0]) == CalpontSystemCatalog::LONGDOUBLE) + ldit = ld->begin(); else if (!smallRG.usesStringTable()) hit = h->begin(); else @@ -439,6 +535,11 @@ void TupleJoiner::doneInserting() smallRow.setPointer(thit->second); ++thit; } + else if (smallRG.getColType(smallKeyColumns[col]) == CalpontSystemCatalog::LONGDOUBLE) + { + smallRow.setPointer(ldit->second); + ++ldit; + } else if (!smallRG.usesStringTable()) { smallRow.setPointer(hit->second); @@ -450,6 +551,25 @@ void TupleJoiner::doneInserting() ++sthit; } + if (smallRow.getColType(smallKeyColumns[col]) == CalpontSystemCatalog::LONGDOUBLE) + { + double dval = (double)roundl(smallRow.getLongDoubleField(smallKeyColumns[col])); + switch (largeRG.getColType(largeKeyColumns[col])) + { + case CalpontSystemCatalog::DOUBLE: + case CalpontSystemCatalog::UDOUBLE: + case CalpontSystemCatalog::FLOAT: + case CalpontSystemCatalog::UFLOAT: + { + uniquer.insert(*(int64_t*)&dval); + } + default: + { + uniquer.insert((int64_t)dval); + } + } + } + else if (smallRow.isUnsigned(smallKeyColumns[col])) { uniquer.insert((int64_t)smallRow.getUintField(smallKeyColumns[col])); @@ -614,11 +734,23 @@ void TupleJoiner::getUnmarkedRows(vector* out) out->push_back(it->second); } } + else if (smallRG.getColType(smallKeyColumns[0]) == CalpontSystemCatalog::LONGDOUBLE) + { + ldIterator it; + + for (it = ld->begin(); it != ld->end(); ++it) + { + smallR.setPointer(it->second); + + if (!smallR.isMarked()) + out->push_back(it->second); + } + } else if (!smallRG.usesStringTable()) { iterator it; - for (it = begin(); it != end(); ++it) + for (it = h->begin(); it != h->end(); ++it) { smallR.setPointer(it->second); @@ -670,8 +802,9 @@ void TupleJoiner::updateCPData(const Row& r) for (col = 0; col < smallKeyColumns.size(); col++) { +// if (r.getColumnWidth(smallKeyColumns[col]) > 8) if (r.isLongString(smallKeyColumns[col])) - continue; + continue; int64_t& min = cpValues[col][0], &max = cpValues[col][1]; @@ -693,7 +826,29 @@ void TupleJoiner::updateCPData(const Row& r) } else if (r.isUnsigned(smallKeyColumns[col])) { - uint64_t uval = r.getUintField(smallKeyColumns[col]); + uint64_t uval; + if (r.getColType(smallKeyColumns[col]) == CalpontSystemCatalog::LONGDOUBLE) + { + double dval = (double)roundl(r.getLongDoubleField(smallKeyColumns[col])); + switch (largeRG.getColType(largeKeyColumns[col])) + { + case CalpontSystemCatalog::DOUBLE: + case CalpontSystemCatalog::UDOUBLE: + case CalpontSystemCatalog::FLOAT: + case CalpontSystemCatalog::UFLOAT: + { + uval = *(uint64_t*)&dval; + } + default: + { + uval = (uint64_t)dval; + } + } + } + else + { + uval = r.getUintField(smallKeyColumns[col]); + } if (uval > static_cast(max)) max = static_cast(uval); @@ -703,7 +858,29 @@ void TupleJoiner::updateCPData(const Row& r) } else { - int64_t val = r.getIntField(smallKeyColumns[col]); + int64_t val; + if (r.getColType(smallKeyColumns[col]) == CalpontSystemCatalog::LONGDOUBLE) + { + double dval = (double)roundl(r.getLongDoubleField(smallKeyColumns[col])); + switch (largeRG.getColType(largeKeyColumns[col])) + { + case CalpontSystemCatalog::DOUBLE: + case CalpontSystemCatalog::UDOUBLE: + case CalpontSystemCatalog::FLOAT: + case CalpontSystemCatalog::UFLOAT: + { + val = *(int64_t*)&dval; + } + default: + { + val = (int64_t)dval; + } + } + } + else + { + val = r.getIntField(smallKeyColumns[col]); + } if (val > max) max = val; @@ -720,6 +897,8 @@ size_t TupleJoiner::size() const { if (UNLIKELY(typelessJoin)) return ht->size(); + else if (smallRG.getColType(smallKeyColumns[0]) == CalpontSystemCatalog::LONGDOUBLE) + return ld->size(); else if (!smallRG.usesStringTable()) return h->size(); else @@ -768,20 +947,18 @@ TypelessData makeTypelessKey(const Row& r, const vector& keyCols, ret.data[off++] = 0; } + else if (r.isUnsigned(keyCols[i])) + { + if (off + 8 > keylen) + goto toolong; + *((uint64_t*) &ret.data[off]) = r.getUintField(keyCols[i]); + off += 8; + } else { if (off + 8 > keylen) goto toolong; - - if (r.isUnsigned(keyCols[i])) - { - *((uint64_t*) &ret.data[off]) = r.getUintField(keyCols[i]); - } - else - { - *((int64_t*) &ret.data[off]) = r.getIntField(keyCols[i]); - } - + *((int64_t*) &ret.data[off]) = r.getIntField(keyCols[i]); off += 8; } } @@ -795,7 +972,132 @@ toolong: return ret; } -TypelessData makeTypelessKey(const Row& r, const vector& keyCols, PoolAllocator* fa) +TypelessData makeTypelessKey(const Row& r, const vector& keyCols, + uint32_t keylen, FixedAllocator* fa, + const rowgroup::RowGroup& otherSideRG, const std::vector& otherKeyCols) +{ + TypelessData ret; + uint32_t off = 0, i, j; + execplan::CalpontSystemCatalog::ColDataType type; + + ret.data = (uint8_t*) fa->allocate(); + + for (i = 0; i < keyCols.size(); i++) + { + type = r.getColTypes()[keyCols[i]]; + + if (type == CalpontSystemCatalog::VARCHAR || + type == CalpontSystemCatalog::CHAR || + type == CalpontSystemCatalog::TEXT) + { + // this is a string, copy a normalized version + const uint8_t* str = r.getStringPointer(keyCols[i]); + uint32_t width = r.getStringLength(keyCols[i]); + + if (width > 65536) + { + throw runtime_error("Cannot join strings greater than 64KB"); + } + + for (j = 0; j < width && str[j] != 0; j++) + { + if (off >= keylen) + goto toolong; + + ret.data[off++] = str[j]; + } + + if (off >= keylen) + goto toolong; + + ret.data[off++] = 0; + } + else if (r.getColType(keyCols[i]) == CalpontSystemCatalog::LONGDOUBLE) + { + if (off + sizeof(long double) > keylen) + goto toolong; + // Small side is a long double. Since CS can't store larger than DOUBLE, + // we need to convert to whatever type large side is -- double or int64 + long double keyld = r.getLongDoubleField(keyCols[i]); + switch (otherSideRG.getColType(otherKeyCols[i])) + { + case CalpontSystemCatalog::DOUBLE: + case CalpontSystemCatalog::UDOUBLE: + case CalpontSystemCatalog::FLOAT: + case CalpontSystemCatalog::UFLOAT: + { + if (off + 8 > keylen) + goto toolong; + if (keyld > MAX_DOUBLE || keyld < MIN_DOUBLE) + { + ret.len = 0; + return ret; + } + else + { + double d = (double)keyld; + *((int64_t*) &ret.data[off]) = *(int64_t*)&d; + } + break; + } + case CalpontSystemCatalog::LONGDOUBLE: + { + if (off + sizeof(long double) > keylen) + goto toolong; + *((long double*) &ret.data[off]) = keyld; + off += sizeof(long double); + break; + } + default: + { + if (off + 8 > keylen) + goto toolong; + if (r.isUnsigned(keyCols[i]) && keyld > MAX_UBIGINT) + { + ret.len = 0; + return ret; + } + else if (keyld > MAX_BIGINT || keyld < MIN_BIGINT) + { + ret.len = 0; + return ret; + } + else + { + *((int64_t*) &ret.data[off]) = (int64_t)keyld; + off += 8; + } + break; + } + } + } + else if (r.isUnsigned(keyCols[i])) + { + if (off + 8 > keylen) + goto toolong; + *((uint64_t*) &ret.data[off]) = r.getUintField(keyCols[i]); + off += 8; + } + else + { + if (off + 8 > keylen) + goto toolong; + *((int64_t*) &ret.data[off]) = r.getIntField(keyCols[i]); + off += 8; + } + } + + ret.len = off; + fa->truncateBy(keylen - off); + return ret; +toolong: + fa->truncateBy(keylen); + ret.len = 0; + return ret; +} + +TypelessData makeTypelessKey(const Row& r, const vector& keyCols, PoolAllocator* fa, + const rowgroup::RowGroup& otherSideRG, const std::vector& otherKeyCols) { TypelessData ret; uint32_t off = 0, i, j; @@ -808,7 +1110,12 @@ TypelessData makeTypelessKey(const Row& r, const vector& keyCols, Pool { type = r.getColTypes()[keyCols[i]]; - if (r.isCharType(keyCols[i])) + if (r.getColType(keyCols[i]) == CalpontSystemCatalog::LONGDOUBLE + && otherSideRG.getColType(otherKeyCols[i]) == CalpontSystemCatalog::LONGDOUBLE) + { + keylen += sizeof(long double); + } + else if (r.isCharType(keyCols[i])) keylen += r.getStringLength(keyCols[i]) + 1; else keylen += 8; @@ -838,17 +1145,66 @@ TypelessData makeTypelessKey(const Row& r, const vector& keyCols, Pool ret.data[off++] = 0; } + else if (type == CalpontSystemCatalog::LONGDOUBLE) + { + // Small side is a long double. Since CS can't store larger than DOUBLE, + // we need to convert to whatever type large side is -- double or int64 + long double keyld = r.getLongDoubleField(keyCols[i]); + switch (otherSideRG.getColType(otherKeyCols[i])) + { + case CalpontSystemCatalog::DOUBLE: + case CalpontSystemCatalog::UDOUBLE: + case CalpontSystemCatalog::FLOAT: + case CalpontSystemCatalog::UFLOAT: + { + if (keyld > MAX_DOUBLE || keyld < MIN_DOUBLE) + { + ret.len = 0; + return ret; + } + else + { + double d = (double)keyld; + *((int64_t*) &ret.data[off]) = *(int64_t*)&d; + off += 8; + } + break; + } + case CalpontSystemCatalog::LONGDOUBLE: + { + *((long double*) &ret.data[off]) = keyld; + off += sizeof(long double); + break; + } + default: + { + if (r.isUnsigned(keyCols[i]) && keyld > MAX_UBIGINT) + { + ret.len = 0; + return ret; + } + else if (keyld > MAX_BIGINT || keyld < MIN_BIGINT) + { + ret.len = 0; + return ret; + } + else + { + *((int64_t*) &ret.data[off]) = (int64_t)keyld; + off += 8; + } + break; + } + } + } + else if (r.isUnsigned(keyCols[i])) + { + *((uint64_t*)&ret.data[off]) = r.getUintField(keyCols[i]); + off += 8; + } else { - if (r.isUnsigned(keyCols[i])) - { - *((uint64_t*)&ret.data[off]) = r.getUintField(keyCols[i]); - } - else - { - *((int64_t*)&ret.data[off]) = r.getIntField(keyCols[i]); - } - + *((int64_t*)&ret.data[off]) = r.getIntField(keyCols[i]); off += 8; } } @@ -881,25 +1237,29 @@ uint64_t getHashOfTypelessKey(const Row& r, const vector& keyCols, uin ret = hasher((const char*) str, len, ret); /* for (uint32_t j = 0; j < width && str[j] != 0; j++) - ret.data[off++] = str[j]; + ret.data[off++] = str[j]; */ ret = hasher(&nullChar, 1, ret); width += len + 1; } + else if (r.getColType(keyCols[i]) == CalpontSystemCatalog::LONGDOUBLE) + { + long double tmp = r.getLongDoubleField(keyCols[i]); + ret = hasher((char*) &tmp, sizeof(long double), ret); + width += sizeof(long double); + } + else + if (r.isUnsigned(keyCols[i])) + { + tmp = r.getUintField(keyCols[i]); + ret = hasher((char*) &tmp, 8, ret); + width += 8; + } else { + tmp = r.getIntField(keyCols[i]); + ret = hasher((char*) &tmp, 8, ret); width += 8; - - if (r.isUnsigned(keyCols[i])) - { - tmp = r.getUintField(keyCols[i]); - ret = hasher((char*) &tmp, 8, ret); - } - else - { - tmp = r.getIntField(keyCols[i]); - ret = hasher((char*) &tmp, 8, ret); - } } } diff --git a/utils/joiner/tuplejoiner.h b/utils/joiner/tuplejoiner.h index 0391e7cdf..211dd5cfe 100644 --- a/utils/joiner/tuplejoiner.h +++ b/utils/joiner/tuplejoiner.h @@ -78,14 +78,30 @@ inline bool TypelessData::operator==(const TypelessData& t) const return (memcmp(data, t.data, len) == 0); } +// Comparator for long double in the hash +class LongDoubleEq +{ +public: + LongDoubleEq(){}; + inline bool operator()(const long double& pos1, const long double& pos2) const + { + return pos1 == pos2; + } +}; + /* This function makes the keys for string & compound joins. The length of the * key is limited by keylen. Keys that are longer are assigned a length of 0 on return, * signifying that it shouldn't match anything. */ extern TypelessData makeTypelessKey(const rowgroup::Row&, const std::vector&, uint32_t keylen, utils::FixedAllocator* fa); +// MCOL-1822 SUM/AVG as long double: pass in RG and col so we can determine type conversion extern TypelessData makeTypelessKey(const rowgroup::Row&, - const std::vector&, utils::PoolAllocator* fa); + const std::vector&, uint32_t keylen, utils::FixedAllocator* fa, + const rowgroup::RowGroup&, const std::vector&); +extern TypelessData makeTypelessKey(const rowgroup::Row&, + const std::vector&, utils::PoolAllocator* fa, + const rowgroup::RowGroup&, const std::vector&); extern uint64_t getHashOfTypelessKey(const rowgroup::Row&, const std::vector&, uint32_t seed = 0); @@ -99,10 +115,26 @@ public: { return fHasher((char*) &val, 8); } + inline size_t operator()(uint64_t val) const + { + return fHasher((char*) &val, 8); + } inline size_t operator()(const TypelessData& e) const { return fHasher((char*) e.data, e.len); } + inline size_t operator()(long double val) const + { + if (sizeof(long double) == 8) // Probably just MSC, but you never know. + { + return fHasher((char*) &val, sizeof(long double)); + } + else + { + // For Linux x86_64, long double is stored in 128 bits, but only 80 are significant + return fHasher((char*) &val, 10); + } + } private: utils::Hasher fHasher; @@ -300,29 +332,24 @@ private: utils::STLPoolAllocator > > sthash_t; typedef std::tr1::unordered_multimap, utils::STLPoolAllocator > > typelesshash_t; + // MCOL-1822 Add support for Long Double AVG/SUM small side + typedef std::tr1::unordered_multimap > > ldhash_t; typedef hash_t::iterator iterator; typedef typelesshash_t::iterator thIterator; + typedef ldhash_t::iterator ldIterator; TupleJoiner(); TupleJoiner(const TupleJoiner&); TupleJoiner& operator=(const TupleJoiner&); - iterator begin() - { - return h->begin(); - } - iterator end() - { - return h->end(); - } - - rowgroup::RGData smallNullMemory; boost::scoped_ptr h; // used for UM joins on ints boost::scoped_ptr sth; // used for UM join on ints where the backing table uses a string table + boost::scoped_ptr ld; // used for UM join on long double std::vector rows; // used for PM join /* This struct is rough. The BPP-JL stores the parsed results for diff --git a/utils/rowgroup/rowaggregation.cpp b/utils/rowgroup/rowaggregation.cpp index 8a80ca683..ac3310326 100644 --- a/utils/rowgroup/rowaggregation.cpp +++ b/utils/rowgroup/rowaggregation.cpp @@ -203,6 +203,10 @@ inline float getFloatNullValue() return *y; } +inline long double getLongDoubleNullValue() +{ + return joblist::LONGDOUBLENULL; +} inline string getStringNullValue() { @@ -229,6 +233,7 @@ const static_any::any& RowAggregation::ulongTypeId((unsigned long)1); const static_any::any& RowAggregation::ullTypeId((unsigned long long)1); const static_any::any& RowAggregation::floatTypeId((float)1); const static_any::any& RowAggregation::doubleTypeId((double)1); +const static_any::any& RowAggregation::longdoubleTypeId((long double)1); const static_any::any& RowAggregation::strTypeId(typeStr); KeyStorage::KeyStorage(const RowGroup& keys, Row** tRow) : tmpRow(tRow), rg(keys) @@ -357,6 +362,15 @@ inline void RowAggregation::updateDoubleMinMax(double val1, double val2, int64_t } +inline void RowAggregation::updateLongDoubleMinMax(long double val1, long double val2, int64_t col, int func) +{ + if (isNull(fRowGroupOut, fRow, col)) + fRow.setLongDoubleField(val1, col); + else if (minMax(val1, val2, func)) + fRow.setLongDoubleField(val1, col); +} + + inline void RowAggregation::updateFloatMinMax(float val1, float val2, int64_t col, int func) { if (isNull(fRowGroupOut, fRow, col)) @@ -396,124 +410,6 @@ void RowAggregation::updateStringMinMax(string val1, string val2, int64_t col, i #endif } - -inline void RowAggregation::updateIntSum(int64_t val1, int64_t val2, int64_t col) -{ - if (isNull(fRowGroupOut, fRow, col)) - { - fRow.setIntField(val1, col); - } - else - { -#ifndef PROMOTE_AGGR_OVRFLW_TO_DBL - - if (((val2 >= 0) && ((numeric_limits::max() - val2) >= val1)) || - ((val2 < 0) && ((numeric_limits::min() - val2) <= val1))) - fRow.setIntField(val1 + val2, col); - -#else /* PROMOTE_AGGR_OVRFLW_TO_DBL */ - - if (fRow.getColTypes()[col] != execplan::CalpontSystemCatalog::DOUBLE && - fRow.getColTypes()[col] != execplan::CalpontSystemCatalog::UDOUBLE) - { - if (((val2 >= 0) && ((numeric_limits::max() - val2) >= val1)) || - ((val2 < 0) && ((numeric_limits::min() - val2) <= val1))) - { - fRow.setIntField(val1 + val2, col); - } - else - { - execplan::CalpontSystemCatalog::ColDataType* cdtp = fRow.getColTypes(); - cdtp += col; - *cdtp = execplan::CalpontSystemCatalog::DOUBLE; - updateDoubleSum((double)val1, (double)val2, col); - } - } - -#endif /* PROMOTE_AGGR_OVRFLW_TO_DBL */ - else - { -#ifndef PROMOTE_AGGR_OVRFLW_TO_DBL - ostringstream oss; - oss << overflowMsg << ": " << val2 << "+" << val1; - - if (val2 > 0) - oss << " > " << numeric_limits::max(); - else - oss << " < " << numeric_limits::min(); - - throw logging::QueryDataExcept(oss.str(), logging::aggregateDataErr); -#else /* PROMOTE_AGGR_OVRFLW_TO_DBL */ - double* dp2 = (double*)&val2; - updateDoubleSum((double)val1, *dp2, col); -#endif /* PROMOTE_AGGR_OVRFLW_TO_DBL */ - } - } -} - -inline void RowAggregation::updateUintSum(uint64_t val1, uint64_t val2, int64_t col) -{ - if (isNull(fRowGroupOut, fRow, col)) - { - fRow.setUintField(val1, col); - } - else - { -#ifndef PROMOTE_AGGR_OVRFLW_TO_DBL - - if ((numeric_limits::max() - val2) >= val1) - fRow.setUintField(val1 + val2, col); - else - { - ostringstream oss; - oss << overflowMsg << ": " << val2 << "+" << val1 << " > " << numeric_limits::max(); - throw logging::QueryDataExcept(oss.str(), logging::aggregateDataErr); - } - -#else /* PROMOTE_AGGR_OVRFLW_TO_DBL */ - - if (fRow.getColTypes()[col] != execplan::CalpontSystemCatalog::DOUBLE && - fRow.getColTypes()[col] != execplan::CalpontSystemCatalog::UDOUBLE) - { - if ((numeric_limits::max() - val2) >= val1) - { - fRow.setUintField(val1 + val2, col); - } - else - { - execplan::CalpontSystemCatalog::ColDataType* cdtp = fRow.getColTypes(); - cdtp += col; - *cdtp = execplan::CalpontSystemCatalog::DOUBLE; - updateDoubleSum((double)val1, (double)val2, col); - } - } - else - { - double* dp2 = (double*)&val2; - updateDoubleSum((double)val1, *dp2, col); - } - -#endif /* PROMOTE_AGGR_OVRFLW_TO_DBL */ - } -} - -inline void RowAggregation::updateDoubleSum(double val1, double val2, int64_t col) -{ - if (isNull(fRowGroupOut, fRow, col)) - fRow.setDoubleField(val1, col); - else - fRow.setDoubleField(val1 + val2, col); -} - - -inline void RowAggregation::updateFloatSum(float val1, float val2, int64_t col) -{ - if (isNull(fRowGroupOut, fRow, col)) - fRow.setFloatField(val1, col); - else - fRow.setFloatField(val1 + val2, col); -} - //------------------------------------------------------------------------------ // Verify if the column value is NULL // row(in) - Row to be included in aggregation. @@ -598,6 +494,12 @@ inline bool RowAggregation::isNull(const RowGroup* pRowGroup, const Row& row, in break; } + case execplan::CalpontSystemCatalog::LONGDOUBLE: + { + ret = (row.getLongDoubleField(col) == joblist::LONGDOUBLENULL); + break; + } + case execplan::CalpontSystemCatalog::MEDINT: case execplan::CalpontSystemCatalog::INT: { @@ -1134,6 +1036,12 @@ void RowAggregation::initMapData(const Row& rowIn) break; } + case execplan::CalpontSystemCatalog::LONGDOUBLE: + { + fRow.setLongDoubleField(rowIn.getLongDoubleField(colIn), colOut); + break; + } + case execplan::CalpontSystemCatalog::FLOAT: case execplan::CalpontSystemCatalog::UFLOAT: { @@ -1265,6 +1173,12 @@ void RowAggregation::makeAggFieldsNull(Row& row) break; } + case execplan::CalpontSystemCatalog::LONGDOUBLE: + { + row.setLongDoubleField(getLongDoubleNullValue(), colOut); + break; + } + case execplan::CalpontSystemCatalog::FLOAT: case execplan::CalpontSystemCatalog::UFLOAT: { @@ -1290,7 +1204,7 @@ void RowAggregation::makeAggFieldsNull(Row& row) //------------------------------------------------------------------------------ -// Update the min/max/sum fields if input is not null. +// Update the min/max fields if input is not null. // rowIn(in) - Row to be included in aggregation. // colIn(in) - column in the input row group // colOut(in) - column in the output row group @@ -1298,7 +1212,7 @@ void RowAggregation::makeAggFieldsNull(Row& row) // Note: NULL value check must be done on UM & PM // UM may receive NULL values, too. //------------------------------------------------------------------------------ -void RowAggregation::doMinMaxSum(const Row& rowIn, int64_t colIn, int64_t colOut, int funcType) +void RowAggregation::doMinMax(const Row& rowIn, int64_t colIn, int64_t colOut, int funcType) { int colDataType = (fRowGroupIn.getColTypes())[colIn]; @@ -1317,12 +1231,7 @@ void RowAggregation::doMinMaxSum(const Row& rowIn, int64_t colIn, int64_t colOut { int64_t valIn = rowIn.getIntField(colIn); int64_t valOut = fRow.getIntField(colOut); - - if (funcType == ROWAGG_SUM || funcType == ROWAGG_DISTINCT_SUM) - updateIntSum(valIn, valOut, colOut); - else - updateIntMinMax(valIn, valOut, colOut, funcType); - + updateIntMinMax(valIn, valOut, colOut, funcType); break; } @@ -1334,12 +1243,7 @@ void RowAggregation::doMinMaxSum(const Row& rowIn, int64_t colIn, int64_t colOut { uint64_t valIn = rowIn.getUintField(colIn); uint64_t valOut = fRow.getUintField(colOut); - - if (funcType == ROWAGG_SUM || funcType == ROWAGG_DISTINCT_SUM) - updateUintSum(valIn, valOut, colOut); - else - updateUintMinMax(valIn, valOut, colOut, funcType); - + updateUintMinMax(valIn, valOut, colOut, funcType); break; } @@ -1347,16 +1251,7 @@ void RowAggregation::doMinMaxSum(const Row& rowIn, int64_t colIn, int64_t colOut case execplan::CalpontSystemCatalog::VARCHAR: case execplan::CalpontSystemCatalog::TEXT: { - if (funcType == ROWAGG_SUM) - { - std::ostringstream errmsg; - errmsg << "RowAggregation: sum(CHAR[]) is not supported."; - cerr << errmsg.str() << endl; - throw logging::QueryDataExcept(errmsg.str(), logging::aggregateFuncErr); - } - int colWidth = fRowGroupIn.getColumnWidth(colIn); - if (colWidth <= 8) { uint64_t valIn = rowIn.getUintField(colIn); @@ -1369,7 +1264,6 @@ void RowAggregation::doMinMaxSum(const Row& rowIn, int64_t colIn, int64_t colOut string valOut = fRow.getStringField(colOut); updateStringMinMax(valIn, valOut, colOut, funcType); } - break; } @@ -1378,12 +1272,7 @@ void RowAggregation::doMinMaxSum(const Row& rowIn, int64_t colIn, int64_t colOut { double valIn = rowIn.getDoubleField(colIn); double valOut = fRow.getDoubleField(colOut); - - if (funcType == ROWAGG_SUM) - updateDoubleSum(valIn, valOut, colOut); - else - updateDoubleMinMax(valIn, valOut, colOut, funcType); - + updateDoubleMinMax(valIn, valOut, colOut, funcType); break; } @@ -1392,12 +1281,7 @@ void RowAggregation::doMinMaxSum(const Row& rowIn, int64_t colIn, int64_t colOut { float valIn = rowIn.getFloatField(colIn); float valOut = fRow.getFloatField(colOut); - - if (funcType == ROWAGG_SUM) - updateFloatSum(valIn, valOut, colOut); - else - updateFloatMinMax(valIn, valOut, colOut, funcType); - + updateFloatMinMax(valIn, valOut, colOut, funcType); break; } @@ -1405,20 +1289,20 @@ void RowAggregation::doMinMaxSum(const Row& rowIn, int64_t colIn, int64_t colOut case execplan::CalpontSystemCatalog::DATETIME: case execplan::CalpontSystemCatalog::TIME: { - if (funcType == ROWAGG_SUM) - { - std::ostringstream errmsg; - errmsg << "RowAggregation: sum(date|date time) is not supported."; - cerr << errmsg.str() << endl; - throw logging::QueryDataExcept(errmsg.str(), logging::aggregateFuncErr); - } - uint64_t valIn = rowIn.getUintField(colIn); uint64_t valOut = fRow.getUintField(colOut); updateUintMinMax(valIn, valOut, colOut, funcType); break; } + case execplan::CalpontSystemCatalog::LONGDOUBLE: + { + long double valIn = rowIn.getDoubleField(colIn); + long double valOut = fRow.getDoubleField(colOut); + updateLongDoubleMinMax(valIn, valOut, colOut, funcType); + break; + } + default: { break; @@ -1426,6 +1310,113 @@ void RowAggregation::doMinMaxSum(const Row& rowIn, int64_t colIn, int64_t colOut } } +//------------------------------------------------------------------------------ +// Update the sum fields if input is not null. +// rowIn(in) - Row to be included in aggregation. +// colIn(in) - column in the input row group +// colOut(in) - column in the output row group +// funcType(in) - aggregation function type +// Note: NULL value check must be done on UM & PM +// UM may receive NULL values, too. +//------------------------------------------------------------------------------ +void RowAggregation::doSum(const Row& rowIn, int64_t colIn, int64_t colOut, int funcType) +{ + int colDataType = (fRowGroupIn.getColTypes())[colIn]; + long double valIn = 0; + long double valOut = fRow.getLongDoubleField(colOut); + if (isNull(&fRowGroupIn, rowIn, colIn) == true) + return; + + switch (colDataType) + { + case execplan::CalpontSystemCatalog::TINYINT: + case execplan::CalpontSystemCatalog::SMALLINT: + case execplan::CalpontSystemCatalog::MEDINT: + case execplan::CalpontSystemCatalog::INT: + case execplan::CalpontSystemCatalog::BIGINT: + { + valIn = rowIn.getIntField(colIn); + break; + } + + case execplan::CalpontSystemCatalog::UTINYINT: + case execplan::CalpontSystemCatalog::USMALLINT: + case execplan::CalpontSystemCatalog::UMEDINT: + case execplan::CalpontSystemCatalog::UINT: + case execplan::CalpontSystemCatalog::UBIGINT: + { + valIn = rowIn.getUintField(colIn); + break; + } + + case execplan::CalpontSystemCatalog::DECIMAL: + case execplan::CalpontSystemCatalog::UDECIMAL: + { + valIn = rowIn.getIntField(colIn); + double scale = (double)(fRowGroupIn.getScale())[colIn]; + if (valIn != 0 && scale > 0) + { + valIn /= pow(10.0, scale); + } + break; + } + + case execplan::CalpontSystemCatalog::CHAR: + case execplan::CalpontSystemCatalog::VARCHAR: + case execplan::CalpontSystemCatalog::TEXT: + { + std::ostringstream errmsg; + errmsg << "RowAggregation: sum(CHAR[]) is not supported."; + cerr << errmsg.str() << endl; + throw logging::QueryDataExcept(errmsg.str(), logging::aggregateFuncErr); + break; + } + + case execplan::CalpontSystemCatalog::DOUBLE: + case execplan::CalpontSystemCatalog::UDOUBLE: + { + valIn = rowIn.getDoubleField(colIn); + break; + } + + case execplan::CalpontSystemCatalog::FLOAT: + case execplan::CalpontSystemCatalog::UFLOAT: + { + valIn = rowIn.getFloatField(colIn); + break; + } + + case execplan::CalpontSystemCatalog::DATE: + case execplan::CalpontSystemCatalog::DATETIME: + case execplan::CalpontSystemCatalog::TIME: + { + std::ostringstream errmsg; + errmsg << "RowAggregation: sum(date|date time) is not supported."; + cerr << errmsg.str() << endl; + throw logging::QueryDataExcept(errmsg.str(), logging::aggregateFuncErr); + break; + } + + case execplan::CalpontSystemCatalog::LONGDOUBLE: + { + valIn = rowIn.getLongDoubleField(colIn); + break; + } + + default: + { + break; + } + } + if (isNull(fRowGroupOut, fRow, colOut)) + { + fRow.setLongDoubleField(valIn, colOut); + } + else + { + fRow.setLongDoubleField(valIn+valOut, colOut); + } +} //------------------------------------------------------------------------------ // Update the and/or/xor fields if input is not null. @@ -1507,12 +1498,15 @@ void RowAggregation::doBitOp(const Row& rowIn, int64_t colIn, int64_t colOut, in case execplan::CalpontSystemCatalog::FLOAT: case execplan::CalpontSystemCatalog::UDOUBLE: case execplan::CalpontSystemCatalog::UFLOAT: + case execplan::CalpontSystemCatalog::LONGDOUBLE: { double dbl = 0.0; if (colDataType == execplan::CalpontSystemCatalog::DOUBLE || colDataType == execplan::CalpontSystemCatalog::UDOUBLE) dbl = rowIn.getDoubleField(colIn); + else if (colDataType == execplan::CalpontSystemCatalog::LONGDOUBLE) + dbl = (double)rowIn.getLongDoubleField(colIn); else dbl = rowIn.getFloatField(colIn); @@ -1684,13 +1678,16 @@ void RowAggregation::updateEntry(const Row& rowIn) if (isNull(&fRowGroupIn, rowIn, colIn) == true) break; case ROWAGG_COUNT_ASTERISK: - fRow.setIntField<8>(fRow.getIntField<8>(colOut) + 1, colOut); + fRow.setUintField<8>(fRow.getUintField<8>(colOut) + 1, colOut); break; case ROWAGG_MIN: case ROWAGG_MAX: + doMinMax(rowIn, colIn, colOut, fFunctionCols[i]->fAggFunction); + break; + case ROWAGG_SUM: - doMinMaxSum(rowIn, colIn, colOut, fFunctionCols[i]->fAggFunction); + doSum(rowIn, colIn, colOut, fFunctionCols[i]->fAggFunction); break; case ROWAGG_AVG: @@ -1749,11 +1746,13 @@ void RowAggregation::updateEntry(const Row& rowIn) //------------------------------------------------------------------------------ void RowAggregation::doAvg(const Row& rowIn, int64_t colIn, int64_t colOut, int64_t colAux) { - int colDataType = (fRowGroupIn.getColTypes())[colIn]; - if (isNull(&fRowGroupIn, rowIn, colIn) == true) return; + int colDataType = (fRowGroupIn.getColTypes())[colIn]; + long double valIn = 0; + long double valOut = fRow.getLongDoubleField(colOut); + switch (colDataType) { case execplan::CalpontSystemCatalog::TINYINT: @@ -1761,70 +1760,9 @@ void RowAggregation::doAvg(const Row& rowIn, int64_t colIn, int64_t colOut, int6 case execplan::CalpontSystemCatalog::MEDINT: case execplan::CalpontSystemCatalog::INT: case execplan::CalpontSystemCatalog::BIGINT: - case execplan::CalpontSystemCatalog::DECIMAL: - case execplan::CalpontSystemCatalog::UDECIMAL: { - int64_t valIn = rowIn.getIntField(colIn); - - if (fRow.getIntField(colAux) == 0) - { - fRow.setIntField(valIn, colOut); - fRow.setIntField(1, colAux); - } - else - { - int64_t valOut = fRow.getIntField(colOut); -#ifndef PROMOTE_AGGR_OVRFLW_TO_DBL - - if (((valOut >= 0) && ((numeric_limits::max() - valOut) >= valIn)) || - ((valOut < 0) && ((numeric_limits::min() - valOut) <= valIn))) - fRow.setIntField(valIn + valOut, colOut); - -#else /* PROMOTE_AGGR_OVRFLW_TO_DBL */ - - if (fRow.getColTypes()[colOut] != execplan::CalpontSystemCatalog::DOUBLE) - { - if (((valOut >= 0) && ((numeric_limits::max() - valOut) >= valIn)) || - ((valOut < 0) && ((numeric_limits::min() - valOut) <= valIn))) - { - fRow.setIntField(valIn + valOut, colOut); - fRow.setIntField(fRow.getIntField(colAux) + 1, colAux); - } - else - { - execplan::CalpontSystemCatalog::ColDataType* cdtp = fRow.getColTypes(); - cdtp += colOut; - *cdtp = execplan::CalpontSystemCatalog::DOUBLE; - fRow.setDoubleField((double)valIn + (double)valOut, colOut); - fRow.setIntField(fRow.getIntField(colAux) + 1, colAux); - } - } - -#endif /* PROMOTE_AGGR_OVRFLW_TO_DBL */ - else -#ifndef PROMOTE_AGGR_OVRFLW_TO_DBL - { - ostringstream oss; - oss << overflowMsg << ": " << valOut << "+" << valIn; - - if (valOut > 0) - oss << " > " << numeric_limits::max(); - else - oss << " < " << numeric_limits::min(); - - throw logging::QueryDataExcept(oss.str(), logging::aggregateDataErr); - } - - fRow.setIntField(fRow.getIntField(colAux) + 1, colAux); -#else /* PROMOTE_AGGR_OVRFLW_TO_DBL */ - { - double* dp = (double*)&valOut; - fRow.setDoubleField((double)valIn + *dp, colOut); - fRow.setIntField(fRow.getIntField(colAux) + 1, colAux); - } -#endif /* PROMOTE_AGGR_OVRFLW_TO_DBL */ - } - + valIn = rowIn.getIntField(colIn); + break; break; } @@ -1834,98 +1772,39 @@ void RowAggregation::doAvg(const Row& rowIn, int64_t colIn, int64_t colOut, int6 case execplan::CalpontSystemCatalog::UINT: case execplan::CalpontSystemCatalog::UBIGINT: { - uint64_t valIn = rowIn.getUintField(colIn); + valIn = rowIn.getUintField(colIn); + break; + } - if (fRow.getUintField(colAux) == 0) + case execplan::CalpontSystemCatalog::DECIMAL: + case execplan::CalpontSystemCatalog::UDECIMAL: + { + valIn = rowIn.getIntField(colIn); + double scale = (double)(fRowGroupIn.getScale())[colIn]; + if (valIn != 0 && scale > 0) { - fRow.setUintField(valIn, colOut); - fRow.setUintField(1, colAux); + valIn /= pow(10.0, scale); } - else - { - uint64_t valOut = fRow.getUintField(colOut); -#ifndef PROMOTE_AGGR_OVRFLW_TO_DBL - - if ((numeric_limits::max() - valOut) >= valIn) - { - fRow.setUintField(valIn + valOut, colOut); - fRow.setUintField(fRow.getUintField(colAux) + 1, colAux); - } - else - { - ostringstream oss; - oss << overflowMsg << ": " << valOut << "+" << valIn << " > " << numeric_limits::max(); - throw logging::QueryDataExcept(oss.str(), logging::aggregateDataErr); - } - -#else /* PROMOTE_AGGR_OVRFLW_TO_DBL */ - - if (fRow.getColTypes()[colOut] != execplan::CalpontSystemCatalog::DOUBLE) - { - if ((numeric_limits::max() - valOut) >= valIn) - { - fRow.setUintField(valIn + valOut, colOut); - fRow.setUintField(fRow.getUintField(colAux) + 1, colAux); - } - else - { - execplan::CalpontSystemCatalog::ColDataType* cdtp = fRow.getColTypes(); - cdtp += colOut; - *cdtp = execplan::CalpontSystemCatalog::DOUBLE; - fRow.setDoubleField((double)valIn + (double)valOut, colOut); - fRow.setUintField(fRow.getUintField(colAux) + 1, colAux); - } - } - else - { - double* dp = (double*)&valOut; - fRow.setDoubleField((double)valIn + *dp, colOut); - fRow.setUintField(fRow.getUintField(colAux) + 1, colAux); - } - -#endif /* PROMOTE_AGGR_OVRFLW_TO_DBL */ - } - break; } case execplan::CalpontSystemCatalog::DOUBLE: case execplan::CalpontSystemCatalog::UDOUBLE: { - double valIn = rowIn.getDoubleField(colIn); - - if (fRow.getIntField(colAux) == 0) - { - fRow.setDoubleField(valIn, colOut); - fRow.setIntField(1, colAux); - } - else - { - double valOut = fRow.getDoubleField(colOut); - fRow.setDoubleField(valIn + valOut, colOut); - fRow.setIntField(fRow.getIntField(colAux) + 1, colAux); - } - + valIn = rowIn.getDoubleField(colIn); break; } case execplan::CalpontSystemCatalog::FLOAT: case execplan::CalpontSystemCatalog::UFLOAT: { - float valIn = rowIn.getFloatField(colIn); - - if (fRow.getIntField(colAux) == 0) - { - fRow.setFloatField(valIn, colOut); - fRow.setIntField(1, colAux); - } - else - { - float valOut = fRow.getFloatField(colOut); - fRow.setFloatField(valIn + valOut, colOut); - fRow.setIntField(fRow.getIntField(colAux) + 1, colAux); - } + valIn = rowIn.getFloatField(colIn); + break; + } + case execplan::CalpontSystemCatalog::LONGDOUBLE: + { + valIn = rowIn.getLongDoubleField(colIn); break; } @@ -1938,6 +1817,18 @@ void RowAggregation::doAvg(const Row& rowIn, int64_t colIn, int64_t colOut, int6 break; } } + + if (fRow.getUintField(colAux) == 0) + { + // This is the first value + fRow.setLongDoubleField(valIn, colOut); + fRow.setUintField(1, colAux); + } + else + { + fRow.setLongDoubleField(valIn + valOut, colOut); + fRow.setUintField(fRow.getUintField(colAux) + 1, colAux); + } } @@ -1988,6 +1879,10 @@ void RowAggregation::doStatistics(const Row& rowIn, int64_t colIn, int64_t colOu valIn = (long double) rowIn.getFloatField(colIn); break; + case execplan::CalpontSystemCatalog::LONGDOUBLE: + valIn = rowIn.getLongDoubleField(colIn); + break; + default: std::ostringstream errmsg; errmsg << "RowAggregation: no average for data type: " << colDataType; @@ -2137,6 +2032,22 @@ void RowAggregation::doUDAF(const Row& rowIn, int64_t colIn, int64_t colOut, break; } + case execplan::CalpontSystemCatalog::LONGDOUBLE: + { + datum.dataType = execplan::CalpontSystemCatalog::LONGDOUBLE; + + if (cc) + { + datum.columnData = cc->getLongDoubleVal(const_cast(rowIn), bIsNull); + } + else + { + datum.columnData = rowIn.getLongDoubleField(colIn); + } + + break; + } + case execplan::CalpontSystemCatalog::FLOAT: case execplan::CalpontSystemCatalog::UFLOAT: { @@ -2509,13 +2420,16 @@ void RowAggregationUM::updateEntry(const Row& rowIn) if (isNull(&fRowGroupIn, rowIn, colIn) == true) break; case ROWAGG_COUNT_ASTERISK: - fRow.setIntField<8>(fRow.getIntField<8>(colOut) + 1, colOut); + fRow.setUintField<8>(fRow.getUintField<8>(colOut) + 1, colOut); break; case ROWAGG_MIN: case ROWAGG_MAX: + doMinMax(rowIn, colIn, colOut, fFunctionCols[i]->fAggFunction); + break; + case ROWAGG_SUM: - doMinMaxSum(rowIn, colIn, colOut, fFunctionCols[i]->fAggFunction); + doSum(rowIn, colIn, colOut, fFunctionCols[i]->fAggFunction); break; case ROWAGG_AVG: @@ -2600,12 +2514,11 @@ void RowAggregationUM::calculateAvgColumns() { int64_t colOut = fFunctionCols[i]->fOutputColumnIndex; int64_t colAux = fFunctionCols[i]->fAuxColumnIndex; - int colDataType = (fRowGroupOut->getColTypes())[colOut]; - int scale = fRowGroupOut->getScale()[colOut]; - int scale1 = scale >> 8; - int scale2 = scale & 0x000000FF; - long double factor = pow(10.0, scale2 - scale1); +// int scale = fRowGroupOut->getScale()[colOut]; +// int scale1 = scale >> 8; +// int scale2 = scale & 0x000000FF; +// long double factor = pow(10.0, scale2 - scale1); for (uint64_t j = 0; j < fRowGroupOut->getRowCount(); j++) { @@ -2618,97 +2531,11 @@ void RowAggregationUM::calculateAvgColumns() long double sum = 0.0; long double avg = 0.0; - switch (colDataType) - { - case execplan::CalpontSystemCatalog::DOUBLE: - case execplan::CalpontSystemCatalog::UDOUBLE: - sum = fRow.getDoubleField(colOut); - avg = sum / cnt; - fRow.setDoubleField(avg, colOut); - break; - - case execplan::CalpontSystemCatalog::FLOAT: - case execplan::CalpontSystemCatalog::UFLOAT: - sum = fRow.getFloatField(colOut); - avg = sum / cnt; - fRow.setFloatField(avg, colOut); - break; - - case execplan::CalpontSystemCatalog::TINYINT: - case execplan::CalpontSystemCatalog::SMALLINT: - case execplan::CalpontSystemCatalog::MEDINT: - case execplan::CalpontSystemCatalog::INT: - case execplan::CalpontSystemCatalog::BIGINT: - case execplan::CalpontSystemCatalog::DECIMAL: - case execplan::CalpontSystemCatalog::UDECIMAL: - sum = static_cast(fRow.getIntField(colOut)); - avg = sum / cnt; - avg *= factor; - avg += (avg < 0) ? (-0.5) : (0.5); - - if (avg > (long double) numeric_limits::max() || - avg < (long double) numeric_limits::min()) -#ifndef PROMOTE_AGGR_OVRFLW_TO_DBL - { - ostringstream oss; - oss << overflowMsg << ": " << avg << "(incl factor " << factor; - - if (avg > 0) - oss << ") > " << numeric_limits::max(); - else - oss << ") < " << numeric_limits::min(); - - throw logging::QueryDataExcept(oss.str(), logging::aggregateDataErr); - } - - fRow.setIntField((int64_t) avg, colOut); -#else /* PROMOTE_AGGR_OVRFLW_TO_DBL */ - { - sum = fRow.getDoubleField(colOut); - avg = sum / cnt; - avg += (avg < 0) ? (-0.5) : (0.5); - fRow.getColTypes()[colOut] = execplan::CalpontSystemCatalog::DOUBLE; - fRow.setDoubleField(avg, colOut); - } - else - fRow.setIntField((int64_t)avg, colOut); - -#endif /* PROMOTE_AGGR_OVRFLW_TO_DBL */ - break; - - case execplan::CalpontSystemCatalog::UTINYINT: - case execplan::CalpontSystemCatalog::USMALLINT: - case execplan::CalpontSystemCatalog::UMEDINT: - case execplan::CalpontSystemCatalog::UINT: - case execplan::CalpontSystemCatalog::UBIGINT: - sum = static_cast(fRow.getUintField(colOut)); - avg = sum / cnt; - avg *= factor; - avg += (avg < 0) ? (-0.5) : (0.5); - - if (avg > (long double) numeric_limits::max()) -#ifndef PROMOTE_AGGR_OVRFLW_TO_DBL - { - ostringstream oss; - oss << overflowMsg << ": " << avg << "(incl factor " << factor << ") > " << numeric_limits::max(); - throw logging::QueryDataExcept(oss.str(), logging::aggregateDataErr); - } - - fRow.setUintField((uint64_t) avg, colOut); -#else /* PROMOTE_AGGR_OVRFLW_TO_DBL */ - { - sum = fRow.getDoubleField(colOut); - avg = sum / cnt; - avg += 0.5; - fRow.getColTypes()[colOut] = execplan::CalpontSystemCatalog::DOUBLE; - fRow.setDoubleField(avg, colOut); - } - else - fRow.setUintField((uint64_t)avg, colOut); - -#endif /* PROMOTE_AGGR_OVRFLW_TO_DBL */ - break; - } + // MCOL-1822 Always long double + sum = fRow.getLongDoubleField(colOut); + avg = sum / cnt; +// avg *= factor; + fRow.setLongDoubleField(avg, colOut); } } } @@ -2729,6 +2556,7 @@ void RowAggregationUM::SetUDAFValue(static_any::any& valOut, int64_t colOut) uint64_t uintOut = 0; float floatOut = 0.0; double doubleOut = 0.0; + long double longdoubleOut = 0.0; ostringstream oss; std::string strOut; @@ -2896,6 +2724,16 @@ void RowAggregationUM::SetUDAFValue(static_any::any& valOut, int64_t colOut) break; + case execplan::CalpontSystemCatalog::LONGDOUBLE: + if (valOut.compatible(doubleTypeId)) + { + longdoubleOut = valOut.cast(); + fRow.setLongDoubleField(longdoubleOut, colOut); + bSetSuccess = true; + } + + break; + default: { std::ostringstream errmsg; @@ -2919,10 +2757,12 @@ void RowAggregationUM::SetUDAFAnyValue(static_any::any& valOut, int64_t colOut) // that they didn't set in mcsv1_UDAF::init(), but this // handles whatever return type is given and casts // it to whatever they said to return. + // TODO: Save cpu cycles here. int64_t intOut = 0; uint64_t uintOut = 0; float floatOut = 0.0; double doubleOut = 0.0; + long double longdoubleOut = 0.0; ostringstream oss; std::string strOut; @@ -2996,12 +2836,24 @@ void RowAggregationUM::SetUDAFAnyValue(static_any::any& valOut, int64_t colOut) { floatOut = valOut.cast(); doubleOut = floatOut; + longdoubleOut = doubleOut; intOut = uintOut = floatOut; oss << floatOut; } else if (valOut.compatible(doubleTypeId)) { doubleOut = valOut.cast(); + longdoubleOut = doubleOut; + floatOut = (float)doubleOut; + uintOut = (uint64_t)doubleOut; + intOut = (int64_t)doubleOut; + oss << doubleOut; + } + + else if (valOut.compatible(longdoubleTypeId)) + { + longdoubleOut = valOut.cast(); + doubleOut = (double)longdoubleOut; floatOut = (float)doubleOut; uintOut = (uint64_t)doubleOut; intOut = (int64_t)doubleOut; @@ -3015,6 +2867,7 @@ void RowAggregationUM::SetUDAFAnyValue(static_any::any& valOut, int64_t colOut) intOut = atol(strOut.c_str()); uintOut = strtoul(strOut.c_str(), NULL, 10); doubleOut = strtod(strOut.c_str(), NULL); + longdoubleOut = strtold(strOut.c_str(), NULL); floatOut = (float)doubleOut; } else @@ -3094,6 +2947,10 @@ void RowAggregationUM::SetUDAFAnyValue(static_any::any& valOut, int64_t colOut) fRow.setVarBinaryField(strOut, colOut); break; + case execplan::CalpontSystemCatalog::LONGDOUBLE: + fRow.setLongDoubleField(longdoubleOut, colOut); + break; + default: { std::ostringstream errmsg; @@ -3329,10 +3186,6 @@ void RowAggregationUM::doNullConstantAggregate(const ConstantAggData& aggData, u { case ROWAGG_MIN: case ROWAGG_MAX: - case ROWAGG_AVG: - case ROWAGG_SUM: - case ROWAGG_DISTINCT_AVG: - case ROWAGG_DISTINCT_SUM: case ROWAGG_STATS: { switch (colDataType) @@ -3394,14 +3247,30 @@ void RowAggregationUM::doNullConstantAggregate(const ConstantAggData& aggData, u fRow.setStringField("", colOut); } break; + + case execplan::CalpontSystemCatalog::LONGDOUBLE: + { + fRow.setLongDoubleField(getLongDoubleNullValue(), colOut); + } + break; + } } break; + case ROWAGG_AVG: + case ROWAGG_SUM: + case ROWAGG_DISTINCT_AVG: + case ROWAGG_DISTINCT_SUM: + { + fRow.setLongDoubleField(getLongDoubleNullValue(), colOut); + } + break; + case ROWAGG_COUNT_COL_NAME: case ROWAGG_COUNT_DISTINCT_COL_NAME: { - fRow.setIntField(0, colOut); + fRow.setUintField(0, colOut); } break; @@ -3548,6 +3417,12 @@ void RowAggregationUM::doNotNullConstantAggregate(const ConstantAggData& aggData } break; + case execplan::CalpontSystemCatalog::LONGDOUBLE: + { + fRow.setLongDoubleField(strtold(aggData.fConstValue.c_str(), 0), colOut); + } + break; + case execplan::CalpontSystemCatalog::FLOAT: case execplan::CalpontSystemCatalog::UFLOAT: { @@ -3650,6 +3525,13 @@ void RowAggregationUM::doNotNullConstantAggregate(const ConstantAggData& aggData } break; + case execplan::CalpontSystemCatalog::LONGDOUBLE: + { + long double dbl = strtold(aggData.fConstValue.c_str(), 0) * rowCnt; + fRow.setLongDoubleField(dbl, colOut); + } + break; + case execplan::CalpontSystemCatalog::FLOAT: case execplan::CalpontSystemCatalog::UFLOAT: { @@ -3712,6 +3594,12 @@ void RowAggregationUM::doNotNullConstantAggregate(const ConstantAggData& aggData } break; + case execplan::CalpontSystemCatalog::LONGDOUBLE: + { + fRow.setLongDoubleField(0.0, colOut); + } + break; + case execplan::CalpontSystemCatalog::FLOAT: case execplan::CalpontSystemCatalog::UFLOAT: { @@ -3746,13 +3634,13 @@ void RowAggregationUM::doNotNullConstantAggregate(const ConstantAggData& aggData case ROWAGG_COUNT_COL_NAME: { - fRow.setIntField(rowCnt, colOut); + fRow.setUintField(rowCnt, colOut); } break; case ROWAGG_COUNT_DISTINCT_COL_NAME: { - fRow.setIntField(1, colOut); + fRow.setUintField(1, colOut); } break; @@ -3839,6 +3727,12 @@ void RowAggregationUM::doNotNullConstantAggregate(const ConstantAggData& aggData } break; + case execplan::CalpontSystemCatalog::LONGDOUBLE: + { + datum.columnData = strtold(aggData.fConstValue.c_str(), 0); + } + break; + case execplan::CalpontSystemCatalog::FLOAT: case execplan::CalpontSystemCatalog::UFLOAT: { @@ -4065,15 +3959,18 @@ void RowAggregationUMP2::updateEntry(const Row& rowIn) case ROWAGG_COUNT_ASTERISK: case ROWAGG_COUNT_COL_NAME: { - int64_t count = fRow.getIntField<8>(colOut) + rowIn.getIntField<8>(colIn); - fRow.setIntField<8>(count, colOut); + uint64_t count = fRow.getUintField<8>(colOut) + rowIn.getUintField<8>(colIn); + fRow.setUintField<8>(count, colOut); break; } case ROWAGG_MIN: case ROWAGG_MAX: + doMinMax(rowIn, colIn, colOut, fFunctionCols[i]->fAggFunction); + break; + case ROWAGG_SUM: - doMinMaxSum(rowIn, colIn, colOut, fFunctionCols[i]->fAggFunction); + doSum(rowIn, colIn, colOut, fFunctionCols[i]->fAggFunction); break; case ROWAGG_AVG: @@ -4142,11 +4039,13 @@ void RowAggregationUMP2::updateEntry(const Row& rowIn) //------------------------------------------------------------------------------ void RowAggregationUMP2::doAvg(const Row& rowIn, int64_t colIn, int64_t colOut, int64_t colAux) { - int colDataType = (fRowGroupIn.getColTypes())[colIn]; - if (isNull(&fRowGroupIn, rowIn, colIn) == true) return; + int colDataType = (fRowGroupIn.getColTypes())[colIn]; + long double valIn = 0; + long double valOut = fRow.getLongDoubleField(colOut); + switch (colDataType) { case execplan::CalpontSystemCatalog::TINYINT: @@ -4154,23 +4053,8 @@ void RowAggregationUMP2::doAvg(const Row& rowIn, int64_t colIn, int64_t colOut, case execplan::CalpontSystemCatalog::MEDINT: case execplan::CalpontSystemCatalog::INT: case execplan::CalpontSystemCatalog::BIGINT: - case execplan::CalpontSystemCatalog::DECIMAL: - case execplan::CalpontSystemCatalog::UDECIMAL: { - int64_t valIn = rowIn.getIntField(colIn); - - if (fRow.getIntField(colAux) == 0) - { - fRow.setIntField(valIn, colOut); - fRow.setIntField(rowIn.getIntField(colIn + 1), colAux); - } - else - { - int64_t valOut = fRow.getIntField(colOut); - fRow.setIntField(valIn + valOut, colOut); - fRow.setIntField(rowIn.getIntField(colIn + 1) + fRow.getIntField(colAux), colAux); - } - + valIn = rowIn.getIntField(colIn); break; } @@ -4180,60 +4064,40 @@ void RowAggregationUMP2::doAvg(const Row& rowIn, int64_t colIn, int64_t colOut, case execplan::CalpontSystemCatalog::UINT: case execplan::CalpontSystemCatalog::UBIGINT: { - uint64_t valIn = rowIn.getUintField(colIn); + valIn = rowIn.getUintField(colIn); + break; + } - if (fRow.getUintField(colAux) == 0) + case execplan::CalpontSystemCatalog::DECIMAL: + case execplan::CalpontSystemCatalog::UDECIMAL: + { + valIn = rowIn.getIntField(colIn); + break; + double scale = (double)(fRowGroupIn.getScale())[colIn]; + if (valIn != 0 && scale > 0) { - fRow.setUintField(valIn, colOut); - fRow.setUintField(rowIn.getUintField(colIn + 1), colAux); + valIn /= pow(10.0, scale); } - else - { - uint64_t valOut = fRow.getUintField(colOut); - fRow.setUintField(valIn + valOut, colOut); - fRow.setUintField(rowIn.getUintField(colIn + 1) + fRow.getUintField(colAux), colAux); - } - break; } case execplan::CalpontSystemCatalog::DOUBLE: case execplan::CalpontSystemCatalog::UDOUBLE: { - double valIn = rowIn.getDoubleField(colIn); - - if (fRow.getIntField(colAux) == 0) - { - fRow.setDoubleField(valIn, colOut); - fRow.setIntField(rowIn.getIntField(colIn + 1), colAux); - } - else - { - double valOut = fRow.getDoubleField(colOut); - fRow.setDoubleField(valIn + valOut, colOut); - fRow.setIntField(rowIn.getIntField(colIn + 1) + fRow.getIntField(colAux), colAux); - } - + valIn = rowIn.getDoubleField(colIn); break; } case execplan::CalpontSystemCatalog::FLOAT: case execplan::CalpontSystemCatalog::UFLOAT: { - float valIn = rowIn.getFloatField(colIn); - - if (fRow.getIntField(colAux) == 0) - { - fRow.setFloatField(valIn, colOut); - fRow.setIntField(rowIn.getIntField(colIn + 1), colAux); - } - else - { - float valOut = fRow.getFloatField(colOut); - fRow.setFloatField(valIn + valOut, colOut); - fRow.setIntField(rowIn.getIntField(colIn + 1) + fRow.getIntField(colAux), colAux); - } + valIn = rowIn.getFloatField(colIn); + break; + } + case execplan::CalpontSystemCatalog::LONGDOUBLE: + { + valIn = rowIn.getLongDoubleField(colIn); break; } @@ -4246,8 +4110,19 @@ void RowAggregationUMP2::doAvg(const Row& rowIn, int64_t colIn, int64_t colOut, break; } } -} + int64_t cnt = fRow.getUintField(colAux); + if (cnt == 0) + { + fRow.setLongDoubleField(valIn, colOut); + fRow.setUintField(rowIn.getUintField(colIn + 1), colAux); + } + else + { + fRow.setLongDoubleField(valIn + valOut, colOut); + fRow.setUintField(rowIn.getUintField(colIn + 1) + cnt, colAux); + } +} //------------------------------------------------------------------------------ // Update the sum and count fields for stattistics if input is not null. @@ -4478,23 +4353,25 @@ void RowAggregationDistinct::updateEntry(const Row& rowIn) case ROWAGG_COUNT_ASTERISK: case ROWAGG_COUNT_COL_NAME: { - int64_t count = fRow.getIntField<8>(colOut) + rowIn.getIntField<8>(colIn); - fRow.setIntField<8>(count, colOut); + uint64_t count = fRow.getUintField<8>(colOut) + rowIn.getUintField<8>(colIn); + fRow.setUintField<8>(count, colOut); break; } case ROWAGG_COUNT_DISTINCT_COL_NAME: if (isNull(&fRowGroupIn, rowIn, colIn) != true) - fRow.setIntField<8>(fRow.getIntField<8>(colOut) + 1, colOut); + fRow.setUintField<8>(fRow.getUintField<8>(colOut) + 1, colOut); break; case ROWAGG_MIN: case ROWAGG_MAX: + doMinMax(rowIn, colIn, colOut, fFunctionCols[i]->fAggFunction); + break; + case ROWAGG_SUM: case ROWAGG_DISTINCT_SUM: - - doMinMaxSum(rowIn, colIn, colOut, fFunctionCols[i]->fAggFunction); + doSum(rowIn, colIn, colOut, fFunctionCols[i]->fAggFunction); break; case ROWAGG_AVG: diff --git a/utils/rowgroup/rowaggregation.h b/utils/rowgroup/rowaggregation.h index e039d5c2a..47d365085 100644 --- a/utils/rowgroup/rowaggregation.h +++ b/utils/rowgroup/rowaggregation.h @@ -612,14 +612,15 @@ protected: virtual void attachGroupConcatAg(); virtual void updateEntry(const Row& row); - virtual void doMinMaxSum(const Row&, int64_t, int64_t, int); + virtual void doMinMax(const Row&, int64_t, int64_t, int); + virtual void doSum(const Row&, int64_t, int64_t, int); virtual void doAvg(const Row&, int64_t, int64_t, int64_t); virtual void doStatistics(const Row&, int64_t, int64_t, int64_t); virtual void doBitOp(const Row&, int64_t, int64_t, int); virtual void doUDAF(const Row&, int64_t, int64_t, int64_t, uint64_t& funcColsIdx); virtual bool countSpecial(const RowGroup* pRG) { - fRow.setIntField<8>(fRow.getIntField<8>(0) + pRG->getRowCount(), 0); + fRow.setUintField<8>(fRow.getUintField<8>(0) + pRG->getRowCount(), 0); return true; } @@ -642,13 +643,9 @@ protected: inline void updateUintMinMax(uint64_t val1, uint64_t val2, int64_t col, int func); inline void updateCharMinMax(uint64_t val1, uint64_t val2, int64_t col, int func); inline void updateDoubleMinMax(double val1, double val2, int64_t col, int func); + inline void updateLongDoubleMinMax(long double val1, long double val2, int64_t col, int func); inline void updateFloatMinMax(float val1, float val2, int64_t col, int func); inline void updateStringMinMax(std::string val1, std::string val2, int64_t col, int func); - inline void updateIntSum(int64_t val1, int64_t val2, int64_t col); - inline void updateUintSum(uint64_t val1, uint64_t val2, int64_t col); - inline void updateDoubleSum(double val1, double val2, int64_t col); - inline void updateFloatSum(float val1, float val2, int64_t col); - std::vector fGroupByCols; std::vector fFunctionCols; RowAggMap_t* fAggMapPtr; @@ -711,6 +708,7 @@ protected: static const static_any::any& ullTypeId; static const static_any::any& floatTypeId; static const static_any::any& doubleTypeId; + static const static_any::any& longdoubleTypeId; static const static_any::any& strTypeId; }; diff --git a/utils/udfsdk/udfsdk.cpp b/utils/udfsdk/udfsdk.cpp index 9a8973232..f91473a81 100644 --- a/utils/udfsdk/udfsdk.cpp +++ b/utils/udfsdk/udfsdk.cpp @@ -201,6 +201,14 @@ double MCS_add::getDoubleVal(Row& row, return 0; } +long double MCS_add::getLongDoubleVal(Row& row, + FunctionParm& parm, + bool& isNull, + CalpontSystemCatalog::ColType& op_ct) +{ + return getDoubleVal(row, parm, isNull, op_ct); +} + float MCS_add::getFloatVal(Row& row, FunctionParm& parm, bool& isNull, @@ -349,6 +357,14 @@ double MCS_isnull::getDoubleVal(Row& row, return (getBoolVal(row, parm, isNull, op_ct) ? 1 : 0); } +long double MCS_isnull::getLongDoubleVal(Row& row, + FunctionParm& parm, + bool& isNull, + CalpontSystemCatalog::ColType& op_ct) +{ + return (getBoolVal(row, parm, isNull, op_ct) ? 1 : 0); +} + float MCS_isnull::getFloatVal(Row& row, FunctionParm& parm, bool& isNull, diff --git a/utils/udfsdk/udfsdk.h b/utils/udfsdk/udfsdk.h index 2160d552b..04e1c07b5 100644 --- a/utils/udfsdk/udfsdk.h +++ b/utils/udfsdk/udfsdk.h @@ -170,6 +170,14 @@ public: bool& isNull, execplan::CalpontSystemCatalog::ColType& op_ct); + /** + * Returns a long double result of this function. + */ + + virtual long double getLongDoubleVal(rowgroup::Row& row, + funcexp::FunctionParm& fp, + bool& isNull, + execplan::CalpontSystemCatalog::ColType& op_ct); /** * Returns a float result of this function. */ @@ -266,6 +274,14 @@ public: bool& isNull, execplan::CalpontSystemCatalog::ColType& op_ct); + /** + * Returns a double result of this function. + */ + virtual long double getLongDoubleVal(rowgroup::Row& row, + funcexp::FunctionParm& fp, + bool& isNull, + execplan::CalpontSystemCatalog::ColType& op_ct); + /** * Returns a float result of this function. */ diff --git a/utils/windowfunction/frameboundrange.cpp b/utils/windowfunction/frameboundrange.cpp index cdb65f5f9..8d73aace2 100644 --- a/utils/windowfunction/frameboundrange.cpp +++ b/utils/windowfunction/frameboundrange.cpp @@ -313,6 +313,20 @@ void FrameBoundExpressionRange::validate() break; } + case execplan::CalpontSystemCatalog::LONGDOUBLE: + { + long double tmp = this->fRow.getLongDoubleField(this->fIndex[1]); + this->fIsZero = (tmp == 0.0); + + if (tmp < 0) + { + invalid = true; + oss << tmp; + } + + break; + } + case execplan::CalpontSystemCatalog::FLOAT: case execplan::CalpontSystemCatalog::UFLOAT: { diff --git a/utils/windowfunction/wf_nth_value.cpp b/utils/windowfunction/wf_nth_value.cpp index eacd1202a..f5be1462f 100644 --- a/utils/windowfunction/wf_nth_value.cpp +++ b/utils/windowfunction/wf_nth_value.cpp @@ -97,6 +97,12 @@ boost::shared_ptr WF_nth_value::makeFunction(int id, cons break; } + case CalpontSystemCatalog::LONGDOUBLE: + { + func.reset(new WF_nth_value(id, name)); + break; + } + default: { func.reset(new WF_nth_value(id, name)); diff --git a/utils/windowfunction/wf_percentile.cpp b/utils/windowfunction/wf_percentile.cpp index b3e56c17a..d7b2ab9ca 100644 --- a/utils/windowfunction/wf_percentile.cpp +++ b/utils/windowfunction/wf_percentile.cpp @@ -146,12 +146,17 @@ boost::shared_ptr WF_percentile::makeFunction(int id, con case CalpontSystemCatalog::UDOUBLE: case CalpontSystemCatalog::FLOAT: case CalpontSystemCatalog::UFLOAT: - case CalpontSystemCatalog::LONGDOUBLE: { func.reset(new WF_percentile(id, name)); break; } + case CalpontSystemCatalog::LONGDOUBLE: + { + func.reset(new WF_percentile(id, name)); + break; + } + default: { string errStr = name + "(" + colType2String[ct] + ")"; diff --git a/utils/windowfunction/wf_udaf.cpp b/utils/windowfunction/wf_udaf.cpp index 250f3fc42..6b1359334 100644 --- a/utils/windowfunction/wf_udaf.cpp +++ b/utils/windowfunction/wf_udaf.cpp @@ -557,6 +557,7 @@ void WF_udaf::SetUDAFValue(static_any::any& valOut, int64_t colOut, uint64_t uintOut = 0; float floatOut = 0.0; double doubleOut = 0.0; + long double longdoubleOut = 0.0; ostringstream oss; std::string strOut; @@ -630,12 +631,14 @@ void WF_udaf::SetUDAFValue(static_any::any& valOut, int64_t colOut, { floatOut = valOut.cast(); doubleOut = floatOut; + longdoubleOut = floatOut; intOut = uintOut = floatOut; oss << floatOut; } else if (valOut.compatible(doubleTypeId)) { doubleOut = valOut.cast(); + longdoubleOut = doubleOut; floatOut = (float)doubleOut; uintOut = (uint64_t)doubleOut; intOut = (int64_t)doubleOut; @@ -649,6 +652,7 @@ void WF_udaf::SetUDAFValue(static_any::any& valOut, int64_t colOut, intOut = atol(strOut.c_str()); uintOut = strtoul(strOut.c_str(), NULL, 10); doubleOut = strtod(strOut.c_str(), NULL); + longdoubleOut = doubleOut; floatOut = (float)doubleOut; } else @@ -718,6 +722,17 @@ void WF_udaf::SetUDAFValue(static_any::any& valOut, int64_t colOut, } break; + case execplan::CalpontSystemCatalog::LONGDOUBLE: + if (valOut.empty()) + { + setValue(colDataType, b, e, c, (long double*)NULL); + } + else + { + setValue(colDataType, b, e, c, &longdoubleOut); + } + break; + case execplan::CalpontSystemCatalog::CHAR: case execplan::CalpontSystemCatalog::VARCHAR: case execplan::CalpontSystemCatalog::TEXT: diff --git a/utils/windowfunction/windowfunctiontype.cpp b/utils/windowfunction/windowfunctiontype.cpp index 471cdc1ac..930e6eecc 100644 --- a/utils/windowfunction/windowfunctiontype.cpp +++ b/utils/windowfunction/windowfunctiontype.cpp @@ -503,6 +503,16 @@ void WindowFunctionType::implicit2T(uint64_t i, T& t, int s) break; } + case CalpontSystemCatalog::LONGDOUBLE: + { + if (s == 0) + t = (T) fRow.getLongDoubleField(i); + else + t = (T) (fRow.getLongDoubleField(i) * IDB_pow[s]); // s is scale, [0, 18] + + break; + } + case CalpontSystemCatalog::CHAR: case CalpontSystemCatalog::VARCHAR: default: From 8298ced9e1e64ec2d3409ff0144dc00bcef5bdd6 Mon Sep 17 00:00:00 2001 From: David Hall Date: Mon, 25 Feb 2019 16:07:59 -0600 Subject: [PATCH 19/38] MCOL-1822 Fix for DISTINCT --- dbcon/joblist/tupleaggregatestep.cpp | 65 ++++++---------------------- 1 file changed, 13 insertions(+), 52 deletions(-) diff --git a/dbcon/joblist/tupleaggregatestep.cpp b/dbcon/joblist/tupleaggregatestep.cpp index 689be0306..a0fdbbc6a 100644 --- a/dbcon/joblist/tupleaggregatestep.cpp +++ b/dbcon/joblist/tupleaggregatestep.cpp @@ -3045,32 +3045,13 @@ void TupleAggregateStep::prep2PhasesAggregate( SP_ROWAGG_GRPBY_t groupby(new RowAggGroupByCol(colProj, colAggPm)); groupByPm.push_back(groupby); - // PM: Except for SUM/AVG, just copy down to aggregation rowgroup - RowAggFunctionType aggOp = rowgroup::ROWAGG_COUNT_NO_OP; - for (size_t agg = 0; agg < aggColVec.size(); ++agg) - { - if (aggColVec[agg].first == key) - { - aggOp = functionIdMap(aggColVec[agg].second); - break; - } - } + // PM: just copy down to aggregation rowgroup oidsAggPm.push_back(oidsProj[colProj]); keysAggPm.push_back(key); scaleAggPm.push_back(scaleProj[colProj]); - if (aggOp == ROWAGG_DISTINCT_SUM || - aggOp == ROWAGG_DISTINCT_AVG) - { - typeAggPm.push_back(CalpontSystemCatalog::LONGDOUBLE); - precisionAggPm.push_back(-1); - widthAggPm.push_back(sizeof(long double)); - } - else - { - typeAggPm.push_back(typeProj[colProj]); - widthAggPm.push_back(width[colProj]); - precisionAggPm.push_back(precisionProj[colProj]); - } + typeAggPm.push_back(typeProj[colProj]); + widthAggPm.push_back(width[colProj]); + precisionAggPm.push_back(precisionProj[colProj]); aggFuncMap.insert(make_pair(boost::make_tuple(keysAggPm[colAggPm], 0, pUDAFFunc, udafc ? udafc->getContext().getParamKeys() : NULL), colAggPm)); colAggPm++; @@ -3905,37 +3886,17 @@ void TupleAggregateStep::prep2PhasesDistinctAggregate( uint64_t colProj = projColPosMap[key]; -// SP_ROWAGG_GRPBY_t groupby(new RowAggGroupByCol(colProj, colAggPm)); - SP_ROWAGG_GRPBY_t groupby(new RowAggGroupByCol(colProj)); + SP_ROWAGG_GRPBY_t groupby(new RowAggGroupByCol(colProj, colAggPm)); groupByPm.push_back(groupby); - // PM: Except for SUM/AVG, just copy down to aggregation rowgroup - RowAggFunctionType aggOp = rowgroup::ROWAGG_COUNT_NO_OP; - for (size_t agg = 0; agg < aggColVec.size(); ++agg) - { - if (aggColVec[agg].first == key) - { - aggOp = functionIdMap(aggColVec[agg].second); - break; - } - } + // PM: just copy down to aggregation rowgroup oidsAggPm.push_back(oidsProj[colProj]); keysAggPm.push_back(key); scaleAggPm.push_back(scaleProj[colProj]); precisionAggPm.push_back(precisionProj[colProj]); - if (aggOp == ROWAGG_DISTINCT_SUM || - aggOp == ROWAGG_DISTINCT_AVG) - { - typeAggPm.push_back(CalpontSystemCatalog::LONGDOUBLE); - precisionAggPm.push_back(-1); - widthAggPm.push_back(sizeof(long double)); - } - else - { - typeAggPm.push_back(typeProj[colProj]); - widthAggPm.push_back(width[colProj]); - precisionAggPm.push_back(precisionProj[colProj]); - } + typeAggPm.push_back(typeProj[colProj]); + widthAggPm.push_back(width[colProj]); + precisionAggPm.push_back(precisionProj[colProj]); aggFuncMap.insert(make_pair(boost::make_tuple(keysAggPm[colAggPm], 0, pUDAFFunc, udafc ? udafc->getContext().getParamKeys() : NULL), colAggPm)); colAggPm++; @@ -4429,10 +4390,10 @@ void TupleAggregateStep::prep2PhasesDistinctAggregate( oidsAggDist.push_back(oidsAggUm[colUm]); keysAggDist.push_back(retKey); - typeAggDist.push_back(typeAggUm[colUm]); + typeAggDist.push_back(CalpontSystemCatalog::LONGDOUBLE); + precisionAggDist.push_back(-1); + widthAggDist.push_back(sizeof(long double)); scaleAggDist.push_back(scaleAggUm[colUm]); - precisionAggDist.push_back(precisionAggUm[colUm]); - widthAggDist.push_back(widthAggUm[colUm]); } // PM: put the count column for avg next to the sum // let fall through to add a count column for average function @@ -4452,7 +4413,7 @@ void TupleAggregateStep::prep2PhasesDistinctAggregate( break; default: - // cound happen if agg and agg distinct use same column. + // could happen if agg and agg distinct use same column. colUm = -1; break; } // switch From c654e8621eb8b633eda2b024f5a07f8a4d25c515 Mon Sep 17 00:00:00 2001 From: David Hall Date: Wed, 27 Feb 2019 13:09:37 -0600 Subject: [PATCH 20/38] MCOL-1822 interim checkin --- dbcon/execplan/arithmeticoperator.h | 11 +++++++++++ dbcon/execplan/simplecolumn.h | 6 ++++++ dbcon/execplan/simplecolumn_decimal.h | 10 ++++++++++ dbcon/execplan/simplecolumn_int.h | 10 ++++++++++ dbcon/execplan/simplecolumn_uint.h | 10 ++++++++++ dbcon/execplan/simplefilter.h | 6 ++++++ dbcon/joblist/tupleunion.cpp | 8 ++------ dbcon/mysql/ha_calpont_impl.cpp | 1 - utils/rowgroup/rowaggregation.cpp | 17 ++++++----------- utils/rowgroup/rowgroup.h | 5 +++++ 10 files changed, 66 insertions(+), 18 deletions(-) diff --git a/dbcon/execplan/arithmeticoperator.h b/dbcon/execplan/arithmeticoperator.h index ca254075b..74b8fe271 100644 --- a/dbcon/execplan/arithmeticoperator.h +++ b/dbcon/execplan/arithmeticoperator.h @@ -123,6 +123,11 @@ public: evaluate(row, isNull, lop, rop); return TreeNode::getDoubleVal(); } + virtual long double getLongDoubleVal(rowgroup::Row& row, bool& isNull, ParseTree* lop, ParseTree* rop) + { + evaluate(row, isNull, lop, rop); + return TreeNode::getLongDoubleVal(); + } virtual IDB_Decimal getDecimalVal(rowgroup::Row& row, bool& isNull, ParseTree* lop, ParseTree* rop) { evaluate(row, isNull, lop, rop); @@ -194,9 +199,15 @@ inline void ArithmeticOperator::evaluate(rowgroup::Row& row, bool& isNull, Parse case execplan::CalpontSystemCatalog::DOUBLE: case execplan::CalpontSystemCatalog::FLOAT: + case execplan::CalpontSystemCatalog::UDOUBLE: + case execplan::CalpontSystemCatalog::UFLOAT: fResult.doubleVal = execute(lop->getDoubleVal(row, isNull), rop->getDoubleVal(row, isNull), isNull); break; + case execplan::CalpontSystemCatalog::LONGDOUBLE: + fResult.longDoubleVal = execute(lop->getLongDoubleVal(row, isNull), rop->getLongDoubleVal(row, isNull), isNull); + break; + case execplan::CalpontSystemCatalog::DECIMAL: case execplan::CalpontSystemCatalog::UDECIMAL: execute (fResult.decimalVal, lop->getDecimalVal(row, isNull), rop->getDecimalVal(row, isNull), isNull); diff --git a/dbcon/execplan/simplecolumn.h b/dbcon/execplan/simplecolumn.h index 60eff939b..634399f3c 100644 --- a/dbcon/execplan/simplecolumn.h +++ b/dbcon/execplan/simplecolumn.h @@ -300,6 +300,12 @@ public: return TreeNode::getDoubleVal(); } + virtual long double getLongDoubleVal(rowgroup::Row& row, bool& isNull) + { + evaluate(row, isNull); + return TreeNode::getLongDoubleVal(); + } + virtual IDB_Decimal getDecimalVal(rowgroup::Row& row, bool& isNull) { evaluate(row, isNull); diff --git a/dbcon/execplan/simplecolumn_decimal.h b/dbcon/execplan/simplecolumn_decimal.h index 2b7b90cf6..dc2fdc268 100644 --- a/dbcon/execplan/simplecolumn_decimal.h +++ b/dbcon/execplan/simplecolumn_decimal.h @@ -76,6 +76,7 @@ public: virtual inline int64_t getIntVal(rowgroup::Row& row, bool& isNull); virtual inline float getFloatVal(rowgroup::Row& row, bool& isNull); virtual inline double getDoubleVal(rowgroup::Row& row, bool& isNull); + virtual inline long double getLongDoubleVal(rowgroup::Row& row, bool& isNull); virtual inline IDB_Decimal getDecimalVal(rowgroup::Row& row, bool& isNull); /** The serialize interface */ @@ -180,6 +181,15 @@ inline double SimpleColumn_Decimal::getDoubleVal(rowgroup::Row& row, bool& return (row.getIntField(fInputIndex) / pow((double)10, fResultType.scale)); } +template +inline long double SimpleColumn_Decimal::getLongDoubleVal(rowgroup::Row& row, bool& isNull) +{ + if (row.equals(fNullVal, fInputIndex)) + isNull = true; + + return (row.getIntField(fInputIndex) / pow((double)10, fResultType.scale)); +} + template inline IDB_Decimal SimpleColumn_Decimal::getDecimalVal(rowgroup::Row& row, bool& isNull) { diff --git a/dbcon/execplan/simplecolumn_int.h b/dbcon/execplan/simplecolumn_int.h index fd171909c..1fe58dd17 100644 --- a/dbcon/execplan/simplecolumn_int.h +++ b/dbcon/execplan/simplecolumn_int.h @@ -75,6 +75,7 @@ public: virtual inline uint64_t getUintVal(rowgroup::Row& row, bool& isNull); virtual inline float getFloatVal(rowgroup::Row& row, bool& isNull); virtual inline double getDoubleVal(rowgroup::Row& row, bool& isNull); + virtual inline long double getLongDoubleVal(rowgroup::Row& row, bool& isNull); virtual inline IDB_Decimal getDecimalVal(rowgroup::Row& row, bool& isNull); /** The serialize interface */ @@ -198,6 +199,15 @@ inline double SimpleColumn_INT::getDoubleVal(rowgroup::Row& row, bool& isNu return (double)row.getIntField(fInputIndex); } +template +inline long double SimpleColumn_INT::getLongDoubleVal(rowgroup::Row& row, bool& isNull) +{ + if (row.equals(fNullVal, fInputIndex)) + isNull = true; + + return (long double)row.getIntField(fInputIndex); +} + template inline IDB_Decimal SimpleColumn_INT::getDecimalVal(rowgroup::Row& row, bool& isNull) { diff --git a/dbcon/execplan/simplecolumn_uint.h b/dbcon/execplan/simplecolumn_uint.h index 2bb98cbd4..4027994df 100644 --- a/dbcon/execplan/simplecolumn_uint.h +++ b/dbcon/execplan/simplecolumn_uint.h @@ -75,6 +75,7 @@ public: virtual inline uint64_t getUintVal(rowgroup::Row& row, bool& isNull); virtual inline float getFloatVal(rowgroup::Row& row, bool& isNull); virtual inline double getDoubleVal(rowgroup::Row& row, bool& isNull); + virtual inline long double getLongDoubleVal(rowgroup::Row& row, bool& isNull); virtual inline IDB_Decimal getDecimalVal(rowgroup::Row& row, bool& isNull); /** The serialize interface */ @@ -198,6 +199,15 @@ inline double SimpleColumn_UINT::getDoubleVal(rowgroup::Row& row, bool& isN return (double)row.getUintField(fInputIndex); } +template +inline long double SimpleColumn_UINT::getLongDoubleVal(rowgroup::Row& row, bool& isNull) +{ + if (row.equals(fNullVal, fInputIndex)) + isNull = true; + + return (long double)row.getUintField(fInputIndex); +} + template inline IDB_Decimal SimpleColumn_UINT::getDecimalVal(rowgroup::Row& row, bool& isNull) { diff --git a/dbcon/execplan/simplefilter.h b/dbcon/execplan/simplefilter.h index d784aefcd..2081710aa 100644 --- a/dbcon/execplan/simplefilter.h +++ b/dbcon/execplan/simplefilter.h @@ -212,6 +212,7 @@ public: inline virtual bool getBoolVal(rowgroup::Row& row, bool& isNull); inline virtual int64_t getIntVal(rowgroup::Row& row, bool& isNull); inline virtual double getDoubleVal(rowgroup::Row& row, bool& isNull); + inline virtual long double getLongDoubleVal(rowgroup::Row& row, bool& isNull); // get all simple columns involved in this column const std::vector& simpleColumnList(); @@ -253,6 +254,11 @@ inline double SimpleFilter::getDoubleVal(rowgroup::Row& row, bool& isNull) return getIntVal(row, isNull); } +inline long double SimpleFilter::getLongDoubleVal(rowgroup::Row& row, bool& isNull) +{ + return getIntVal(row, isNull); +} + typedef boost::shared_ptr SSFP; std::ostream& operator<<(std::ostream& output, const SimpleFilter& rhs); diff --git a/dbcon/joblist/tupleunion.cpp b/dbcon/joblist/tupleunion.cpp index d23a9d4d9..792b95713 100644 --- a/dbcon/joblist/tupleunion.cpp +++ b/dbcon/joblist/tupleunion.cpp @@ -513,16 +513,12 @@ void TupleUnion::normalize(const Row& in, Row* out) case CalpontSystemCatalog::LONGDOUBLE: { int scale = in.getScale(i); - + long double d = in.getIntField(i); if (scale != 0) { - long double d = in.getIntField(i); d /= (uint64_t) pow(10.0, scale); - out->setLongDoubleField(d, i); } - else - out->setLongDoubleField(in.getIntField(i), i); - + out->setLongDoubleField(d, i); break; } diff --git a/dbcon/mysql/ha_calpont_impl.cpp b/dbcon/mysql/ha_calpont_impl.cpp index 58170f5aa..c9af2fdd0 100644 --- a/dbcon/mysql/ha_calpont_impl.cpp +++ b/dbcon/mysql/ha_calpont_impl.cpp @@ -743,7 +743,6 @@ int fetchNextRow(uchar* buf, cal_table_info& ti, cal_connection_info* ci, bool h { f2->dec = row.getScale(s); } - dl /= pow(10.0, (double)f2->dec); f2->store(static_cast(dl)); if ((*f)->null_ptr) diff --git a/utils/rowgroup/rowaggregation.cpp b/utils/rowgroup/rowaggregation.cpp index ac3310326..7df68392b 100644 --- a/utils/rowgroup/rowaggregation.cpp +++ b/utils/rowgroup/rowaggregation.cpp @@ -1297,8 +1297,8 @@ void RowAggregation::doMinMax(const Row& rowIn, int64_t colIn, int64_t colOut, i case execplan::CalpontSystemCatalog::LONGDOUBLE: { - long double valIn = rowIn.getDoubleField(colIn); - long double valOut = fRow.getDoubleField(colOut); + long double valIn = rowIn.getLongDoubleField(colIn); + long double valOut = fRow.getLongDoubleField(colOut); updateLongDoubleMinMax(valIn, valOut, colOut, funcType); break; } @@ -3186,6 +3186,10 @@ void RowAggregationUM::doNullConstantAggregate(const ConstantAggData& aggData, u { case ROWAGG_MIN: case ROWAGG_MAX: + case ROWAGG_AVG: + case ROWAGG_SUM: + case ROWAGG_DISTINCT_AVG: + case ROWAGG_DISTINCT_SUM: case ROWAGG_STATS: { switch (colDataType) @@ -3258,15 +3262,6 @@ void RowAggregationUM::doNullConstantAggregate(const ConstantAggData& aggData, u } break; - case ROWAGG_AVG: - case ROWAGG_SUM: - case ROWAGG_DISTINCT_AVG: - case ROWAGG_DISTINCT_SUM: - { - fRow.setLongDoubleField(getLongDoubleNullValue(), colOut); - } - break; - case ROWAGG_COUNT_COL_NAME: case ROWAGG_COUNT_DISTINCT_COL_NAME: { diff --git a/utils/rowgroup/rowgroup.h b/utils/rowgroup/rowgroup.h index 1c3618735..cbf74ef32 100644 --- a/utils/rowgroup/rowgroup.h +++ b/utils/rowgroup/rowgroup.h @@ -1036,6 +1036,11 @@ inline void Row::setFloatField(float val, uint32_t colIndex) inline void Row::setLongDoubleField(long double val, uint32_t colIndex) { + if (sizeof(long double) == 16) + { + // zero out the unused portion as there may be garbage there. + *((uint64_t*)&val+1) &= 0x000000000000FFFFULL; + } *((long double*) &data[offsets[colIndex]]) = val; } From 947eaaef26e8e7331f181d2fe22fb79dae48b42e Mon Sep 17 00:00:00 2001 From: Roman Nozdrin Date: Sat, 2 Mar 2019 02:01:41 +0300 Subject: [PATCH 21/38] MCOL-901 group_concat() uses only a subset of unique values b/c an incorrect number of key columns was used whilst initializing Eq() struct. The regression caused by changes made for MCOL-1829 so I revised the changes made. Now LimitedOrderBy::getKeyLength() returns an actual key columns count. --- dbcon/joblist/limitedorderby.cpp | 10 +++------- utils/windowfunction/idborderby.cpp | 10 +++------- 2 files changed, 6 insertions(+), 14 deletions(-) diff --git a/dbcon/joblist/limitedorderby.cpp b/dbcon/joblist/limitedorderby.cpp index de7f9bcb6..1b5c307cb 100644 --- a/dbcon/joblist/limitedorderby.cpp +++ b/dbcon/joblist/limitedorderby.cpp @@ -77,8 +77,6 @@ void LimitedOrderBy::initialize(const RowGroup& rg, const JobInfo& jobInfo) map::iterator j = keyToIndexMap.find(i->first); idbassert(j != keyToIndexMap.end()); - // TODO Ordering direction in CSEP differs from - // internal direction representation. This behavior should be fixed fOrderByCond.push_back(IdbSortSpec(j->second, i->second)); } @@ -86,16 +84,14 @@ void LimitedOrderBy::initialize(const RowGroup& rg, const JobInfo& jobInfo) fStart = jobInfo.limitStart; fCount = jobInfo.limitCount; -// fMemSize = (fStart + fCount) * rg.getRowSize(); - IdbOrderBy::initialize(rg); } - +// This must return a proper number of key columns and +// not just a column count. uint64_t LimitedOrderBy::getKeyLength() const { - //return (fRow0.getSize() - 2); - return fRow0.getColumnCount(); + return fOrderByCond.size(); } diff --git a/utils/windowfunction/idborderby.cpp b/utils/windowfunction/idborderby.cpp index db5cdf1c1..60f8cc525 100644 --- a/utils/windowfunction/idborderby.cpp +++ b/utils/windowfunction/idborderby.cpp @@ -1,4 +1,5 @@ /* 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 @@ -19,7 +20,6 @@ #include -//#define NDEBUG #include #include #include @@ -463,7 +463,6 @@ uint64_t IdbOrderBy::Hasher::operator()(const Row::Pointer& p) const row.setPointer(p); // MCOL-1829 Row::h uses colcount as an array idx down a callstack. uint64_t ret = row.hash(); - //cout << "hash(): returning " << ret << " for row: " << row.toString() << endl; return ret; } @@ -472,11 +471,8 @@ bool IdbOrderBy::Eq::operator()(const Row::Pointer& d1, const Row::Pointer& d2) Row& r1 = ts->row1, &r2 = ts->row2; r1.setPointer(d1); r2.setPointer(d2); - // MCOL-1829 Row::equals uses 2nd argument as container size boundary - // so it must be column count - 1. - bool ret = r1.equals(r2, colCount - 1); - //cout << "equals(): returning " << (int) ret << " for r1: " << r1.toString() << " r2: " << r2.toString() - // << endl; + // MCOL-1829 Row::equals uses 2nd argument as key columns container size boundary + bool ret = r1.equals(r2, colCount); return ret; } From 9e1cec56b1410dbb478d672f690fc0316c307c8a Mon Sep 17 00:00:00 2001 From: David Hall Date: Fri, 1 Mar 2019 17:34:57 -0600 Subject: [PATCH 22/38] MCOL-1822 finishing up use long double for SUM/AVG --- dbcon/execplan/predicateoperator.h | 35 +++++++++++++++- dbcon/execplan/windowfunctioncolumn.cpp | 8 ++++ dbcon/mysql/ha_window_function.cpp | 8 +++- utils/windowfunction/wf_sum_avg.cpp | 54 ++++++++----------------- utils/windowfunction/wf_sum_avg.h | 4 +- 5 files changed, 67 insertions(+), 42 deletions(-) diff --git a/dbcon/execplan/predicateoperator.h b/dbcon/execplan/predicateoperator.h index 30251c510..2c076e9cb 100644 --- a/dbcon/execplan/predicateoperator.h +++ b/dbcon/execplan/predicateoperator.h @@ -278,11 +278,42 @@ inline bool PredicateOperator::getBoolVal(rowgroup::Row& row, bool& isNull, Retu return false; long double val1 = lop->getLongDoubleVal(row, isNull); - if (isNull) return false; - return numericCompare(val1, rop->getLongDoubleVal(row, isNull)) && !isNull; + long double val2 = rop->getLongDoubleVal(row, isNull); + if (isNull) + return false; + + // In many case, rounding error will prevent an eq compare to work + // In these cases, use the largest scale of the two items. + if (fOp == execplan::OP_EQ) + { + // In case a val is a representation of a very large integer, + // we won't want to just multiply by scale, as it may move + // significant digits out of scope. So we break them apart + // and compare each separately + int64_t scale = max(lop->resultType().scale, rop->resultType().scale); + if (scale) + { + long double intpart1; + long double fract1 = modfl(val1, &intpart1); + long double intpart2; + long double fract2 = modfl(val2, &intpart2); + if (numericCompare(intpart1, intpart2)) + { + double factor = pow(10.0, (double)scale); + fract1 = roundl(fract1 * factor); + fract2 = roundl(fract2 * factor); + return numericCompare(fract1, fract2); + } + else + { + return false; + } + } + } + return numericCompare(val1, val2); } case execplan::CalpontSystemCatalog::DECIMAL: diff --git a/dbcon/execplan/windowfunctioncolumn.cpp b/dbcon/execplan/windowfunctioncolumn.cpp index cad99215a..680f0b524 100644 --- a/dbcon/execplan/windowfunctioncolumn.cpp +++ b/dbcon/execplan/windowfunctioncolumn.cpp @@ -378,6 +378,14 @@ void WindowFunctionColumn::adjustResultType() boost::iequals(fFunctionName, "NTH_VALUE")) && !fFunctionParms.empty()) fResultType = fFunctionParms[0]->resultType(); + + if (boost::iequals(fFunctionName, "SUM") || + boost::iequals(fFunctionName, "AVG")) + { + fResultType.colDataType = CalpontSystemCatalog::LONGDOUBLE; + fResultType.colWidth = sizeof(long double); + fResultType.precision = -1; + } } void WindowFunctionColumn::evaluate(Row& row, bool& isNull) diff --git a/dbcon/mysql/ha_window_function.cpp b/dbcon/mysql/ha_window_function.cpp index f4a95bbc3..ed10d4fda 100644 --- a/dbcon/mysql/ha_window_function.cpp +++ b/dbcon/mysql/ha_window_function.cpp @@ -897,7 +897,13 @@ ReturnedColumn* buildWindowFunctionColumn(Item* item, gp_walk_info& gwi, bool& n setError(gwi.thd, ER_CHECK_NOT_IMPLEMENTED, gwi.parseErrorText); return NULL; } - +#if 0 + if (item_sum->sum_func() != Item_sum::UDF_SUM_FUNC && + item_sum->sum_func() != Item_sum::SUM_FUNC && + item_sum->sum_func() != Item_sum::SUM_DISTINCT_FUNC && + item_sum->sum_func() != Item_sum::AVG_FUNC && + item_sum->sum_func() != Item_sum::AVG_DISTINCT_FUNC) +#endif if (item_sum->sum_func() != Item_sum::UDF_SUM_FUNC) { ac->resultType(colType_MysqlToIDB(item_sum)); diff --git a/utils/windowfunction/wf_sum_avg.cpp b/utils/windowfunction/wf_sum_avg.cpp index 07d671c66..341b55dd7 100644 --- a/utils/windowfunction/wf_sum_avg.cpp +++ b/utils/windowfunction/wf_sum_avg.cpp @@ -49,10 +49,10 @@ using namespace joblist; #include "wf_sum_avg.h" +#if 0 namespace { - template void checkSumLimit(T sum, T val) { @@ -102,14 +102,12 @@ void checkSumLimit(uint64_t sum, uint64_t val) } -template -T calculateAvg(T sum, uint64_t count, int s) +template<> +long double calculateAvg(long double sum, uint64_t count, int s) { - T avg = ((long double) sum) / count; - return avg; + return sum / count; } - long double avgWithLimit(long double sum, uint64_t count, int scale, long double u, long double l) { long double factor = pow(10.0, scale); @@ -149,8 +147,8 @@ uint64_t calculateAvg(uint64_t sum, uint64_t count, int scale) return t; } - } +#endif namespace windowfunction { @@ -159,7 +157,6 @@ template boost::shared_ptr WF_sum_avg::makeFunction(int id, const string& name, int ct) { boost::shared_ptr func; -#if 0 switch (ct) { case CalpontSystemCatalog::TINYINT: @@ -203,30 +200,6 @@ boost::shared_ptr WF_sum_avg::makeFunction(int id, const func.reset(new WF_sum_avg(id, name)); break; } -#endif - switch (ct) - { - case CalpontSystemCatalog::TINYINT: - case CalpontSystemCatalog::SMALLINT: - case CalpontSystemCatalog::MEDINT: - case CalpontSystemCatalog::INT: - case CalpontSystemCatalog::BIGINT: - case CalpontSystemCatalog::DECIMAL: - case CalpontSystemCatalog::UTINYINT: - case CalpontSystemCatalog::USMALLINT: - case CalpontSystemCatalog::UMEDINT: - case CalpontSystemCatalog::UINT: - case CalpontSystemCatalog::UBIGINT: - case CalpontSystemCatalog::UDECIMAL: - case CalpontSystemCatalog::DOUBLE: - case CalpontSystemCatalog::UDOUBLE: - case CalpontSystemCatalog::FLOAT: - case CalpontSystemCatalog::UFLOAT: - case CalpontSystemCatalog::LONGDOUBLE: - { - func.reset(new WF_sum_avg(id, name)); - break; - } default: { string errStr = name + "(" + colType2String[ct] + ")"; @@ -277,7 +250,7 @@ void WF_sum_avg::operator()(int64_t b, int64_t e, int64_t c) e = c; uint64_t colIn = fFieldIndex[1]; - int scale = fRow.getScale(colOut) - fRow.getScale(colIn); + double scale = fRow.getScale(colIn); for (int64_t i = b; i <= e; i++) { @@ -291,11 +264,16 @@ void WF_sum_avg::operator()(int64_t b, int64_t e, int64_t c) T valIn; getValue(colIn, valIn); - checkSumLimit(fSum, valIn); +// checkSumLimit(fSum, valIn); if ((!fDistinct) || (fSet.find(valIn) == fSet.end())) { - fSum += valIn; + long double val = valIn; + if (scale) + { + val /= pow(10.0, scale); + } + fSum += val; fCount++; if (fDistinct) @@ -304,10 +282,12 @@ void WF_sum_avg::operator()(int64_t b, int64_t e, int64_t c) } if ((fCount > 0) && (fFunctionId == WF__AVG || fFunctionId == WF__AVG_DISTINCT)) - fAvg = (T) calculateAvg(fSum, fCount, scale); + { + fAvg = fSum / fCount; + } } - T* v = NULL; + long double* v = NULL; if (fCount > 0) { diff --git a/utils/windowfunction/wf_sum_avg.h b/utils/windowfunction/wf_sum_avg.h index c96166fba..e21d7a86f 100644 --- a/utils/windowfunction/wf_sum_avg.h +++ b/utils/windowfunction/wf_sum_avg.h @@ -47,8 +47,8 @@ public: static boost::shared_ptr makeFunction(int, const string&, int); protected: - T fAvg; - T fSum; + long double fAvg; + long double fSum; uint64_t fCount; bool fDistinct; std::set fSet; From c716b2afbf181aa702d41946e5d105fd591fe13b Mon Sep 17 00:00:00 2001 From: Patrick LeBlanc Date: Mon, 4 Mar 2019 10:35:59 -0600 Subject: [PATCH 23/38] Tentative fix for the getsystemnetworkconfig cmd re mcol-1607. --- oamapps/mcsadmin/mcsadmin.cpp | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/oamapps/mcsadmin/mcsadmin.cpp b/oamapps/mcsadmin/mcsadmin.cpp index 362fb4b00..d07c67ffb 100644 --- a/oamapps/mcsadmin/mcsadmin.cpp +++ b/oamapps/mcsadmin/mcsadmin.cpp @@ -5404,7 +5404,14 @@ int processCommand(string* arguments) for ( ; pt1 != (*pt).hostConfigList.end() ; pt1++) { - string ipAddr = (*pt1).IPAddr; + /* MCOL-1607. IPAddr may be a host name here b/c it is read straight + from the config file. */ + string tmphost = oam.getIPAddress(pt1->IPAddr); + string ipAddr; + if (tmphost.empty()) + ipAddr = pt1->IPAddr; + else + ipAddr = tmphost; string hostname = (*pt1).HostName; string nicID = oam.itoa((*pt1).NicID); From 3f2c7539475750ff4f206e037c29d6d3c1749911 Mon Sep 17 00:00:00 2001 From: David Hall Date: Tue, 5 Mar 2019 09:33:39 -0600 Subject: [PATCH 24/38] MCOL-1822-c final checkin --- dbcon/execplan/aggregatecolumn.cpp | 1 + dbcon/execplan/aggregatecolumn.h | 1 + dbcon/execplan/arithmeticcolumn.h | 1 + dbcon/execplan/arithmeticoperator.cpp | 4 ++-- dbcon/execplan/arithmeticoperator.h | 1 + dbcon/execplan/calpontsystemcatalog.cpp | 2 +- dbcon/execplan/calpontsystemcatalog.h | 2 +- dbcon/execplan/constantcolumn.cpp | 1 + dbcon/execplan/constantcolumn.h | 1 + dbcon/execplan/functioncolumn.h | 1 + dbcon/execplan/operator.h | 1 + dbcon/execplan/parsetree.h | 1 + dbcon/execplan/predicateoperator.cpp | 1 + dbcon/execplan/predicateoperator.h | 1 + dbcon/execplan/simplecolumn.cpp | 1 + dbcon/execplan/simplecolumn.h | 23 +++++++++++++++----- dbcon/execplan/simplecolumn_decimal.h | 1 + dbcon/execplan/simplecolumn_int.h | 1 + dbcon/execplan/simplecolumn_uint.h | 1 + dbcon/execplan/simplefilter.h | 1 + dbcon/execplan/treenode.h | 1 + dbcon/execplan/windowfunctioncolumn.cpp | 1 + dbcon/execplan/windowfunctioncolumn.h | 1 + dbcon/joblist/batchprimitiveprocessor-jl.cpp | 1 + dbcon/joblist/groupconcat.cpp | 1 + dbcon/joblist/jlf_common.cpp | 2 +- dbcon/joblist/jlf_common.h | 1 + dbcon/joblist/jlf_subquery.cpp | 1 + dbcon/joblist/jlf_tuplejoblist.cpp | 1 + dbcon/joblist/joblistfactory.cpp | 2 +- dbcon/joblist/joblisttypes.h | 1 + dbcon/joblist/primitivestep.h | 1 + dbcon/joblist/rowestimator.cpp | 1 + dbcon/joblist/rowestimator.h | 1 + dbcon/joblist/subquerytransformer.cpp | 1 + dbcon/joblist/tupleaggregatestep.cpp | 1 + dbcon/joblist/tupleconstantstep.cpp | 1 + dbcon/joblist/tupleunion.cpp | 1 + dbcon/mysql/ha_calpont_dml.cpp | 2 +- dbcon/mysql/ha_calpont_execplan.cpp | 2 +- dbcon/mysql/ha_calpont_impl.cpp | 2 +- dbcon/mysql/ha_calpont_partition.cpp | 2 +- dbcon/mysql/ha_window_function.cpp | 2 +- exemgr/main.cpp | 2 +- utils/common/common.vpj | 2 ++ utils/common/nullvaluemanip.cpp | 1 + utils/dataconvert/dataconvert.cpp | 1 + utils/funcexp/func_abs.cpp | 1 + utils/funcexp/func_between.cpp | 1 + utils/funcexp/func_case.cpp | 1 + utils/funcexp/func_cast.cpp | 1 + utils/funcexp/func_ceil.cpp | 1 + utils/funcexp/func_coalesce.cpp | 1 + utils/funcexp/func_exp.cpp | 1 + utils/funcexp/func_floor.cpp | 1 + utils/funcexp/func_from_unixtime.cpp | 2 +- utils/funcexp/func_greatest.cpp | 1 + utils/funcexp/func_hex.cpp | 1 + utils/funcexp/func_if.cpp | 1 + utils/funcexp/func_ifnull.cpp | 1 + utils/funcexp/func_in.cpp | 1 + utils/funcexp/func_isnull.cpp | 1 + utils/funcexp/func_least.cpp | 1 + utils/funcexp/func_math.cpp | 1 + utils/funcexp/func_mod.cpp | 1 + utils/funcexp/func_nullif.cpp | 2 +- utils/funcexp/func_pow.cpp | 1 + utils/funcexp/func_round.cpp | 1 + utils/funcexp/func_substring_index.cpp | 1 + utils/funcexp/func_truncate.cpp | 1 + utils/funcexp/funcexp.cpp | 1 + utils/funcexp/funchelpers.h | 1 + utils/funcexp/functor.cpp | 1 + utils/funcexp/functor.h | 1 + utils/funcexp/functor_all.h | 1 + utils/funcexp/functor_dtm.h | 1 + utils/funcexp/functor_export.h | 1 + utils/funcexp/functor_int.h | 1 + utils/funcexp/functor_real.h | 1 + utils/funcexp/functor_str.h | 1 + utils/joiner/joinpartition.cpp | 2 +- utils/joiner/tuplejoiner.cpp | 1 + utils/joiner/tuplejoiner.h | 1 + utils/messageqcpp/bytestream.cpp | 2 +- utils/messageqcpp/bytestream.h | 2 +- utils/rowgroup/rowaggregation.cpp | 2 +- utils/rowgroup/rowaggregation.h | 2 +- utils/rowgroup/rowgroup.cpp | 2 +- utils/rowgroup/rowgroup.h | 2 +- utils/udfsdk/udfsdk.cpp | 2 +- utils/udfsdk/udfsdk.h | 2 +- utils/windowfunction/frameboundrange.cpp | 1 + utils/windowfunction/idborderby.cpp | 1 + utils/windowfunction/idborderby.h | 1 + utils/windowfunction/wf_lead_lag.cpp | 1 + utils/windowfunction/wf_min_max.cpp | 1 + utils/windowfunction/wf_nth_value.cpp | 1 + utils/windowfunction/wf_percentile.cpp | 1 + utils/windowfunction/wf_stats.cpp | 1 + utils/windowfunction/wf_sum_avg.cpp | 1 + utils/windowfunction/wf_sum_avg.h | 1 + utils/windowfunction/wf_udaf.cpp | 2 +- utils/windowfunction/windowfunctiontype.cpp | 2 +- utils/windowfunction/windowfunctiontype.h | 1 + 104 files changed, 122 insertions(+), 31 deletions(-) diff --git a/dbcon/execplan/aggregatecolumn.cpp b/dbcon/execplan/aggregatecolumn.cpp index bf46fafd5..982841e4e 100644 --- a/dbcon/execplan/aggregatecolumn.cpp +++ b/dbcon/execplan/aggregatecolumn.cpp @@ -1,4 +1,5 @@ /* Copyright (C) 2014 InfiniDB, Inc. + Copyright (C) 2019 MariaDB Corporaton This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License diff --git a/dbcon/execplan/aggregatecolumn.h b/dbcon/execplan/aggregatecolumn.h index 443be7eff..79df22e4d 100644 --- a/dbcon/execplan/aggregatecolumn.h +++ b/dbcon/execplan/aggregatecolumn.h @@ -1,4 +1,5 @@ /* Copyright (C) 2014 InfiniDB, Inc. + Copyright (C) 2019 MariaDB Corporaton This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License diff --git a/dbcon/execplan/arithmeticcolumn.h b/dbcon/execplan/arithmeticcolumn.h index b4e6ab3a9..14d838bad 100644 --- a/dbcon/execplan/arithmeticcolumn.h +++ b/dbcon/execplan/arithmeticcolumn.h @@ -1,4 +1,5 @@ /* Copyright (C) 2014 InfiniDB, Inc. + Copyright (C) 2019 MariaDB Corporaton This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License diff --git a/dbcon/execplan/arithmeticoperator.cpp b/dbcon/execplan/arithmeticoperator.cpp index 73e18b430..78dffe430 100644 --- a/dbcon/execplan/arithmeticoperator.cpp +++ b/dbcon/execplan/arithmeticoperator.cpp @@ -212,10 +212,10 @@ void ArithmeticOperator::adjustResultType(const CalpontSystemCatalog::ColType& m else { CalpontSystemCatalog::ColType n; - n.colDataType = CalpontSystemCatalog::DOUBLE; + n.colDataType = CalpontSystemCatalog::LONGDOUBLE; n.scale = m.scale; // @bug5736, save the original decimal scale n.precision = -1; // @bug5736, indicate this double is for decimal math - n.colWidth = 8; + n.colWidth = 16; fResultType = n; } } diff --git a/dbcon/execplan/arithmeticoperator.h b/dbcon/execplan/arithmeticoperator.h index 74b8fe271..0f54b1e82 100644 --- a/dbcon/execplan/arithmeticoperator.h +++ b/dbcon/execplan/arithmeticoperator.h @@ -1,4 +1,5 @@ /* Copyright (C) 2014 InfiniDB, Inc. + Copyright (C) 2019 MariaDB Corporaton This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License diff --git a/dbcon/execplan/calpontsystemcatalog.cpp b/dbcon/execplan/calpontsystemcatalog.cpp index 0b4fe4fce..119678cf6 100644 --- a/dbcon/execplan/calpontsystemcatalog.cpp +++ b/dbcon/execplan/calpontsystemcatalog.cpp @@ -1,5 +1,5 @@ /* Copyright (C) 2014 InfiniDB, Inc. - Copyright (C) 2016 MariaDB Corporaton + Copyright (C) 2019 MariaDB Corporaton This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License diff --git a/dbcon/execplan/calpontsystemcatalog.h b/dbcon/execplan/calpontsystemcatalog.h index e56872b84..8de670125 100644 --- a/dbcon/execplan/calpontsystemcatalog.h +++ b/dbcon/execplan/calpontsystemcatalog.h @@ -1,5 +1,5 @@ /* Copyright (C) 2014 InfiniDB, Inc. - Copyright (C) 2016 MariaDB Corporaton + Copyright (C) 2019 MariaDB Corporaton This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License diff --git a/dbcon/execplan/constantcolumn.cpp b/dbcon/execplan/constantcolumn.cpp index ea8bb913c..3a816b114 100644 --- a/dbcon/execplan/constantcolumn.cpp +++ b/dbcon/execplan/constantcolumn.cpp @@ -1,4 +1,5 @@ /* Copyright (C) 2014 InfiniDB, Inc. + Copyright (C) 2019 MariaDB Corporaton This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License diff --git a/dbcon/execplan/constantcolumn.h b/dbcon/execplan/constantcolumn.h index e19abb364..ed8315202 100644 --- a/dbcon/execplan/constantcolumn.h +++ b/dbcon/execplan/constantcolumn.h @@ -1,4 +1,5 @@ /* Copyright (C) 2014 InfiniDB, Inc. + Copyright (C) 2019 MariaDB Corporaton This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License diff --git a/dbcon/execplan/functioncolumn.h b/dbcon/execplan/functioncolumn.h index 59ed92146..cb4a397fb 100644 --- a/dbcon/execplan/functioncolumn.h +++ b/dbcon/execplan/functioncolumn.h @@ -1,4 +1,5 @@ /* Copyright (C) 2014 InfiniDB, Inc. + Copyright (C) 2019 MariaDB Corporaton This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License diff --git a/dbcon/execplan/operator.h b/dbcon/execplan/operator.h index a253c6593..13dea8d38 100644 --- a/dbcon/execplan/operator.h +++ b/dbcon/execplan/operator.h @@ -1,4 +1,5 @@ /* Copyright (C) 2014 InfiniDB, Inc. + Copyright (C) 2019 MariaDB Corporaton This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License diff --git a/dbcon/execplan/parsetree.h b/dbcon/execplan/parsetree.h index 112649f81..044d54fd6 100644 --- a/dbcon/execplan/parsetree.h +++ b/dbcon/execplan/parsetree.h @@ -1,4 +1,5 @@ /* Copyright (C) 2014 InfiniDB, Inc. + Copyright (C) 2019 MariaDB Corporaton This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License diff --git a/dbcon/execplan/predicateoperator.cpp b/dbcon/execplan/predicateoperator.cpp index 6f84d2353..119f49193 100644 --- a/dbcon/execplan/predicateoperator.cpp +++ b/dbcon/execplan/predicateoperator.cpp @@ -1,4 +1,5 @@ /* Copyright (C) 2014 InfiniDB, Inc. + Copyright (C) 2019 MariaDB Corporaton This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License diff --git a/dbcon/execplan/predicateoperator.h b/dbcon/execplan/predicateoperator.h index 2c076e9cb..b83931394 100644 --- a/dbcon/execplan/predicateoperator.h +++ b/dbcon/execplan/predicateoperator.h @@ -1,4 +1,5 @@ /* Copyright (C) 2014 InfiniDB, Inc. + Copyright (C) 2019 MariaDB Corporaton This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License diff --git a/dbcon/execplan/simplecolumn.cpp b/dbcon/execplan/simplecolumn.cpp index c95f7ac1e..206194954 100644 --- a/dbcon/execplan/simplecolumn.cpp +++ b/dbcon/execplan/simplecolumn.cpp @@ -1,4 +1,5 @@ /* Copyright (C) 2014 InfiniDB, Inc. + Copyright (C) 2019 MariaDB Corporaton This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License diff --git a/dbcon/execplan/simplecolumn.h b/dbcon/execplan/simplecolumn.h index 634399f3c..6694863d6 100644 --- a/dbcon/execplan/simplecolumn.h +++ b/dbcon/execplan/simplecolumn.h @@ -1,4 +1,5 @@ /* Copyright (C) 2014 InfiniDB, Inc. + Copyright (C) 2019 MariaDB Corporaton This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License @@ -312,14 +313,24 @@ public: // @bug5736, double type with precision -1 indicates that this type is for decimal math, // the original decimal scale is stored in scale field, which is no use for double. - if (fResultType.colDataType == CalpontSystemCatalog::DOUBLE && fResultType.precision == -1) + if (fResultType.precision == -1) { IDB_Decimal rv; - rv.scale = fResultType.scale; - rv.precision = 15; - rv.value = (int64_t)(TreeNode::getDoubleVal() * IDB_pow[rv.scale]); - - return rv; + if (fResultType.colDataType == CalpontSystemCatalog::DOUBLE) + { + rv.scale = fResultType.scale; + rv.precision = 15; + rv.value = (int64_t)(TreeNode::getDoubleVal() * IDB_pow[rv.scale]); + return rv; + } + else if (fResultType.colDataType == CalpontSystemCatalog::LONGDOUBLE) + { + IDB_Decimal rv; + rv.scale = fResultType.scale; + rv.precision = 19; + rv.value = (int64_t)(TreeNode::getLongDoubleVal() * IDB_pow[rv.scale]); + return rv; + } } return TreeNode::getDecimalVal(); diff --git a/dbcon/execplan/simplecolumn_decimal.h b/dbcon/execplan/simplecolumn_decimal.h index dc2fdc268..07d451e4b 100644 --- a/dbcon/execplan/simplecolumn_decimal.h +++ b/dbcon/execplan/simplecolumn_decimal.h @@ -1,4 +1,5 @@ /* Copyright (C) 2014 InfiniDB, Inc. + Copyright (C) 2019 MariaDB Corporaton This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License diff --git a/dbcon/execplan/simplecolumn_int.h b/dbcon/execplan/simplecolumn_int.h index 1fe58dd17..e39ab9953 100644 --- a/dbcon/execplan/simplecolumn_int.h +++ b/dbcon/execplan/simplecolumn_int.h @@ -1,4 +1,5 @@ /* Copyright (C) 2014 InfiniDB, Inc. + Copyright (C) 2019 MariaDB Corporaton This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License diff --git a/dbcon/execplan/simplecolumn_uint.h b/dbcon/execplan/simplecolumn_uint.h index 4027994df..7c9169f75 100644 --- a/dbcon/execplan/simplecolumn_uint.h +++ b/dbcon/execplan/simplecolumn_uint.h @@ -1,4 +1,5 @@ /* Copyright (C) 2014 InfiniDB, Inc. + Copyright (C) 2019 MariaDB Corporaton This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License diff --git a/dbcon/execplan/simplefilter.h b/dbcon/execplan/simplefilter.h index 2081710aa..04afb087a 100644 --- a/dbcon/execplan/simplefilter.h +++ b/dbcon/execplan/simplefilter.h @@ -1,4 +1,5 @@ /* Copyright (C) 2014 InfiniDB, Inc. + Copyright (C) 2019 MariaDB Corporaton This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License diff --git a/dbcon/execplan/treenode.h b/dbcon/execplan/treenode.h index 0fc836dad..3edf1e9db 100644 --- a/dbcon/execplan/treenode.h +++ b/dbcon/execplan/treenode.h @@ -1,4 +1,5 @@ /* Copyright (C) 2014 InfiniDB, Inc. + Copyright (C) 2019 MariaDB Corporaton This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License diff --git a/dbcon/execplan/windowfunctioncolumn.cpp b/dbcon/execplan/windowfunctioncolumn.cpp index 680f0b524..e01c5c010 100644 --- a/dbcon/execplan/windowfunctioncolumn.cpp +++ b/dbcon/execplan/windowfunctioncolumn.cpp @@ -1,4 +1,5 @@ /* Copyright (C) 2014 InfiniDB, Inc. + Copyright (C) 2019 MariaDB Corporaton This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License diff --git a/dbcon/execplan/windowfunctioncolumn.h b/dbcon/execplan/windowfunctioncolumn.h index 732d956cd..55f062990 100644 --- a/dbcon/execplan/windowfunctioncolumn.h +++ b/dbcon/execplan/windowfunctioncolumn.h @@ -1,4 +1,5 @@ /* Copyright (C) 2014 InfiniDB, Inc. + Copyright (C) 2019 MariaDB Corporaton This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License diff --git a/dbcon/joblist/batchprimitiveprocessor-jl.cpp b/dbcon/joblist/batchprimitiveprocessor-jl.cpp index e21f7cca4..d8aab62b4 100644 --- a/dbcon/joblist/batchprimitiveprocessor-jl.cpp +++ b/dbcon/joblist/batchprimitiveprocessor-jl.cpp @@ -1,4 +1,5 @@ /* Copyright (C) 2014 InfiniDB, Inc. + Copyright (C) 2019 MariaDB Corporaton This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License diff --git a/dbcon/joblist/groupconcat.cpp b/dbcon/joblist/groupconcat.cpp index e359b9c35..fe7dd8482 100644 --- a/dbcon/joblist/groupconcat.cpp +++ b/dbcon/joblist/groupconcat.cpp @@ -1,4 +1,5 @@ /* Copyright (C) 2014 InfiniDB, Inc. + Copyright (C) 2019 MariaDB Corporaton This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License diff --git a/dbcon/joblist/jlf_common.cpp b/dbcon/joblist/jlf_common.cpp index 9579aad60..7ce066427 100644 --- a/dbcon/joblist/jlf_common.cpp +++ b/dbcon/joblist/jlf_common.cpp @@ -1,5 +1,5 @@ /* Copyright (C) 2014 InfiniDB, Inc. - Copyright (C) 2016 MariaDB Corporaton + Copyright (C) 2019 MariaDB Corporaton This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License diff --git a/dbcon/joblist/jlf_common.h b/dbcon/joblist/jlf_common.h index 2ae8018c8..18c1ea396 100644 --- a/dbcon/joblist/jlf_common.h +++ b/dbcon/joblist/jlf_common.h @@ -1,4 +1,5 @@ /* Copyright (C) 2014 InfiniDB, Inc. + Copyright (C) 2019 MariaDB Corporaton This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License diff --git a/dbcon/joblist/jlf_subquery.cpp b/dbcon/joblist/jlf_subquery.cpp index 08592d314..7f2544be8 100644 --- a/dbcon/joblist/jlf_subquery.cpp +++ b/dbcon/joblist/jlf_subquery.cpp @@ -1,4 +1,5 @@ /* Copyright (C) 2014 InfiniDB, Inc. + Copyright (C) 2019 MariaDB Corporaton This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License diff --git a/dbcon/joblist/jlf_tuplejoblist.cpp b/dbcon/joblist/jlf_tuplejoblist.cpp index 996f1369d..5337215d2 100644 --- a/dbcon/joblist/jlf_tuplejoblist.cpp +++ b/dbcon/joblist/jlf_tuplejoblist.cpp @@ -1,4 +1,5 @@ /* Copyright (C) 2014 InfiniDB, Inc. + Copyright (C) 2019 MariaDB Corporaton This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License diff --git a/dbcon/joblist/joblistfactory.cpp b/dbcon/joblist/joblistfactory.cpp index 97bd6a53c..b0c314364 100644 --- a/dbcon/joblist/joblistfactory.cpp +++ b/dbcon/joblist/joblistfactory.cpp @@ -1,5 +1,5 @@ /* Copyright (C) 2014 InfiniDB, Inc. - Copyright (C) 2016 MariaDB Corporaton + Copyright (C) 2019 MariaDB Corporaton This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License diff --git a/dbcon/joblist/joblisttypes.h b/dbcon/joblist/joblisttypes.h index 82c8fe7ca..6fd099330 100644 --- a/dbcon/joblist/joblisttypes.h +++ b/dbcon/joblist/joblisttypes.h @@ -1,4 +1,5 @@ /* Copyright (C) 2014 InfiniDB, Inc. + Copyright (C) 2019 MariaDB Corporaton This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License diff --git a/dbcon/joblist/primitivestep.h b/dbcon/joblist/primitivestep.h index cc18cb43d..2a668ed06 100644 --- a/dbcon/joblist/primitivestep.h +++ b/dbcon/joblist/primitivestep.h @@ -1,4 +1,5 @@ /* Copyright (C) 2014 InfiniDB, Inc. + Copyright (C) 2019 MariaDB Corporaton This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License diff --git a/dbcon/joblist/rowestimator.cpp b/dbcon/joblist/rowestimator.cpp index 8ec09488c..0b250e2bb 100644 --- a/dbcon/joblist/rowestimator.cpp +++ b/dbcon/joblist/rowestimator.cpp @@ -1,4 +1,5 @@ /* Copyright (C) 2014 InfiniDB, Inc. + Copyright (C) 2019 MariaDB Corporaton This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License diff --git a/dbcon/joblist/rowestimator.h b/dbcon/joblist/rowestimator.h index 01547c2d0..3cbffde1d 100644 --- a/dbcon/joblist/rowestimator.h +++ b/dbcon/joblist/rowestimator.h @@ -1,4 +1,5 @@ /* Copyright (C) 2014 InfiniDB, Inc. + Copyright (C) 2019 MariaDB Corporaton This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License diff --git a/dbcon/joblist/subquerytransformer.cpp b/dbcon/joblist/subquerytransformer.cpp index fb8d0db55..753438387 100644 --- a/dbcon/joblist/subquerytransformer.cpp +++ b/dbcon/joblist/subquerytransformer.cpp @@ -1,4 +1,5 @@ /* Copyright (C) 2014 InfiniDB, Inc. + Copyright (C) 2019 MariaDB Corporaton This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License diff --git a/dbcon/joblist/tupleaggregatestep.cpp b/dbcon/joblist/tupleaggregatestep.cpp index a0fdbbc6a..b4aec4013 100644 --- a/dbcon/joblist/tupleaggregatestep.cpp +++ b/dbcon/joblist/tupleaggregatestep.cpp @@ -1,4 +1,5 @@ /* Copyright (C) 2014 InfiniDB, Inc. + Copyright (C) 2019 MariaDB Corporaton This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License diff --git a/dbcon/joblist/tupleconstantstep.cpp b/dbcon/joblist/tupleconstantstep.cpp index 4e3eb670d..d03996586 100644 --- a/dbcon/joblist/tupleconstantstep.cpp +++ b/dbcon/joblist/tupleconstantstep.cpp @@ -1,4 +1,5 @@ /* Copyright (C) 2014 InfiniDB, Inc. + Copyright (C) 2019 MariaDB Corporaton This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License diff --git a/dbcon/joblist/tupleunion.cpp b/dbcon/joblist/tupleunion.cpp index 792b95713..18c8b261f 100644 --- a/dbcon/joblist/tupleunion.cpp +++ b/dbcon/joblist/tupleunion.cpp @@ -1,4 +1,5 @@ /* Copyright (C) 2014 InfiniDB, Inc. + Copyright (C) 2019 MariaDB Corporaton This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License diff --git a/dbcon/mysql/ha_calpont_dml.cpp b/dbcon/mysql/ha_calpont_dml.cpp index 3860f6d5e..8f5a5f21c 100644 --- a/dbcon/mysql/ha_calpont_dml.cpp +++ b/dbcon/mysql/ha_calpont_dml.cpp @@ -1,5 +1,5 @@ /* Copyright (C) 2014 InfiniDB, Inc. - Copyright (C) 2016 MariaDB Corporaton + Copyright (C) 2019 MariaDB Corporaton This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License diff --git a/dbcon/mysql/ha_calpont_execplan.cpp b/dbcon/mysql/ha_calpont_execplan.cpp index 9e92162a0..f7425fce7 100644 --- a/dbcon/mysql/ha_calpont_execplan.cpp +++ b/dbcon/mysql/ha_calpont_execplan.cpp @@ -1,5 +1,5 @@ /* Copyright (C) 2014 InfiniDB, Inc. - Copyright (C) 2016 MariaDB Corporaton + Copyright (C) 2019 MariaDB Corporaton This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License diff --git a/dbcon/mysql/ha_calpont_impl.cpp b/dbcon/mysql/ha_calpont_impl.cpp index c9af2fdd0..52c270cf0 100644 --- a/dbcon/mysql/ha_calpont_impl.cpp +++ b/dbcon/mysql/ha_calpont_impl.cpp @@ -1,5 +1,5 @@ /* Copyright (C) 2014 InfiniDB, Inc. - Copyright (C) 2016 MariaDB Corporaton + Copyright (C) 2019 MariaDB Corporaton This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License diff --git a/dbcon/mysql/ha_calpont_partition.cpp b/dbcon/mysql/ha_calpont_partition.cpp index a4eca3212..61063f2f6 100644 --- a/dbcon/mysql/ha_calpont_partition.cpp +++ b/dbcon/mysql/ha_calpont_partition.cpp @@ -1,5 +1,5 @@ /* Copyright (C) 2014 InfiniDB, Inc. - Copyright (C) 2016 MariaDB Corporaton + Copyright (C) 2019 MariaDB Corporaton This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License diff --git a/dbcon/mysql/ha_window_function.cpp b/dbcon/mysql/ha_window_function.cpp index ed10d4fda..2543b53e0 100644 --- a/dbcon/mysql/ha_window_function.cpp +++ b/dbcon/mysql/ha_window_function.cpp @@ -1,5 +1,5 @@ /* Copyright (C) 2014 InfiniDB, Inc. - Copyright (C) 2016 MariaDB Corporaton + Copyright (C) 2019 MariaDB Corporaton This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License diff --git a/exemgr/main.cpp b/exemgr/main.cpp index e34431e9d..259d1443e 100644 --- a/exemgr/main.cpp +++ b/exemgr/main.cpp @@ -1,5 +1,5 @@ /* Copyright (C) 2014 InfiniDB, Inc. - Copyright (C) 2016 MariaDB Corporaton + Copyright (C) 2019 MariaDB Corporaton This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License diff --git a/utils/common/common.vpj b/utils/common/common.vpj index ea67e04ba..8b22a4a7a 100755 --- a/utils/common/common.vpj +++ b/utils/common/common.vpj @@ -203,6 +203,7 @@ + + diff --git a/utils/common/nullvaluemanip.cpp b/utils/common/nullvaluemanip.cpp index dc0777eef..a72e8ad00 100644 --- a/utils/common/nullvaluemanip.cpp +++ b/utils/common/nullvaluemanip.cpp @@ -1,4 +1,5 @@ /* Copyright (C) 2014 InfiniDB, Inc. + Copyright (C) 2019 MariaDB Corporaton This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License diff --git a/utils/dataconvert/dataconvert.cpp b/utils/dataconvert/dataconvert.cpp index 08a833cd0..9cd776130 100644 --- a/utils/dataconvert/dataconvert.cpp +++ b/utils/dataconvert/dataconvert.cpp @@ -1,4 +1,5 @@ /* Copyright (C) 2014 InfiniDB, Inc. + Copyright (C) 2019 MariaDB Corporaton This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License diff --git a/utils/funcexp/func_abs.cpp b/utils/funcexp/func_abs.cpp index 2cb25c001..4cef9a791 100644 --- a/utils/funcexp/func_abs.cpp +++ b/utils/funcexp/func_abs.cpp @@ -1,4 +1,5 @@ /* Copyright (C) 2014 InfiniDB, Inc. + Copyright (C) 2019 MariaDB Corporaton This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License diff --git a/utils/funcexp/func_between.cpp b/utils/funcexp/func_between.cpp index 05ea8be3a..43bde349d 100644 --- a/utils/funcexp/func_between.cpp +++ b/utils/funcexp/func_between.cpp @@ -1,4 +1,5 @@ /* Copyright (C) 2014 InfiniDB, Inc. + Copyright (C) 2019 MariaDB Corporaton This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License diff --git a/utils/funcexp/func_case.cpp b/utils/funcexp/func_case.cpp index 46371026a..571c66ec7 100644 --- a/utils/funcexp/func_case.cpp +++ b/utils/funcexp/func_case.cpp @@ -1,4 +1,5 @@ /* Copyright (C) 2014 InfiniDB, Inc. + Copyright (C) 2019 MariaDB Corporaton This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License diff --git a/utils/funcexp/func_cast.cpp b/utils/funcexp/func_cast.cpp index 75262f13a..97c5db075 100644 --- a/utils/funcexp/func_cast.cpp +++ b/utils/funcexp/func_cast.cpp @@ -1,4 +1,5 @@ /* Copyright (C) 2014 InfiniDB, Inc. + Copyright (C) 2019 MariaDB Corporaton This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License diff --git a/utils/funcexp/func_ceil.cpp b/utils/funcexp/func_ceil.cpp index 8fda7c6de..4cc916b42 100644 --- a/utils/funcexp/func_ceil.cpp +++ b/utils/funcexp/func_ceil.cpp @@ -1,4 +1,5 @@ /* Copyright (C) 2014 InfiniDB, Inc. + Copyright (C) 2019 MariaDB Corporaton This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License diff --git a/utils/funcexp/func_coalesce.cpp b/utils/funcexp/func_coalesce.cpp index c79119bb7..7df883757 100644 --- a/utils/funcexp/func_coalesce.cpp +++ b/utils/funcexp/func_coalesce.cpp @@ -1,4 +1,5 @@ /* Copyright (C) 2014 InfiniDB, Inc. + Copyright (C) 2019 MariaDB Corporaton This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License diff --git a/utils/funcexp/func_exp.cpp b/utils/funcexp/func_exp.cpp index a1913064d..6decbe652 100644 --- a/utils/funcexp/func_exp.cpp +++ b/utils/funcexp/func_exp.cpp @@ -1,4 +1,5 @@ /* Copyright (C) 2014 InfiniDB, Inc. + Copyright (C) 2019 MariaDB Corporaton This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License diff --git a/utils/funcexp/func_floor.cpp b/utils/funcexp/func_floor.cpp index 8e632c971..75954f128 100644 --- a/utils/funcexp/func_floor.cpp +++ b/utils/funcexp/func_floor.cpp @@ -1,4 +1,5 @@ /* Copyright (C) 2014 InfiniDB, Inc. + Copyright (C) 2019 MariaDB Corporaton This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License diff --git a/utils/funcexp/func_from_unixtime.cpp b/utils/funcexp/func_from_unixtime.cpp index 9f116c751..18dba41c8 100644 --- a/utils/funcexp/func_from_unixtime.cpp +++ b/utils/funcexp/func_from_unixtime.cpp @@ -1,5 +1,5 @@ /* Copyright (C) 2014 InfiniDB, Inc. - Copyright (C) 2016 MariaDB Corporaton + Copyright (C) 2019 MariaDB Corporaton This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License diff --git a/utils/funcexp/func_greatest.cpp b/utils/funcexp/func_greatest.cpp index 4b55d2250..57c3b82fb 100644 --- a/utils/funcexp/func_greatest.cpp +++ b/utils/funcexp/func_greatest.cpp @@ -1,4 +1,5 @@ /* Copyright (C) 2014 InfiniDB, Inc. + Copyright (C) 2019 MariaDB Corporaton This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License diff --git a/utils/funcexp/func_hex.cpp b/utils/funcexp/func_hex.cpp index 9bf35b125..462d8e779 100644 --- a/utils/funcexp/func_hex.cpp +++ b/utils/funcexp/func_hex.cpp @@ -1,4 +1,5 @@ /* Copyright (C) 2014 InfiniDB, Inc. + Copyright (C) 2019 MariaDB Corporaton This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License diff --git a/utils/funcexp/func_if.cpp b/utils/funcexp/func_if.cpp index 7141037da..5da8293e6 100644 --- a/utils/funcexp/func_if.cpp +++ b/utils/funcexp/func_if.cpp @@ -1,4 +1,5 @@ /* Copyright (C) 2014 InfiniDB, Inc. + Copyright (C) 2019 MariaDB Corporaton This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License diff --git a/utils/funcexp/func_ifnull.cpp b/utils/funcexp/func_ifnull.cpp index 8879a98dc..00aaa682b 100644 --- a/utils/funcexp/func_ifnull.cpp +++ b/utils/funcexp/func_ifnull.cpp @@ -1,4 +1,5 @@ /* Copyright (C) 2014 InfiniDB, Inc. + Copyright (C) 2019 MariaDB Corporaton This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License diff --git a/utils/funcexp/func_in.cpp b/utils/funcexp/func_in.cpp index f34d9f8b6..966b30ae5 100644 --- a/utils/funcexp/func_in.cpp +++ b/utils/funcexp/func_in.cpp @@ -1,4 +1,5 @@ /* Copyright (C) 2014 InfiniDB, Inc. + Copyright (C) 2019 MariaDB Corporaton This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License diff --git a/utils/funcexp/func_isnull.cpp b/utils/funcexp/func_isnull.cpp index 709abf814..ebe1c2d88 100644 --- a/utils/funcexp/func_isnull.cpp +++ b/utils/funcexp/func_isnull.cpp @@ -1,4 +1,5 @@ /* Copyright (C) 2014 InfiniDB, Inc. + Copyright (C) 2019 MariaDB Corporaton This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License diff --git a/utils/funcexp/func_least.cpp b/utils/funcexp/func_least.cpp index 874548d07..cfb590291 100644 --- a/utils/funcexp/func_least.cpp +++ b/utils/funcexp/func_least.cpp @@ -1,4 +1,5 @@ /* Copyright (C) 2014 InfiniDB, Inc. + Copyright (C) 2019 MariaDB Corporaton This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License diff --git a/utils/funcexp/func_math.cpp b/utils/funcexp/func_math.cpp index 6863517b8..7af31901a 100644 --- a/utils/funcexp/func_math.cpp +++ b/utils/funcexp/func_math.cpp @@ -1,4 +1,5 @@ /* Copyright (C) 2014 InfiniDB, Inc. + Copyright (C) 2019 MariaDB Corporaton This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License diff --git a/utils/funcexp/func_mod.cpp b/utils/funcexp/func_mod.cpp index a13780d17..4cbdf23af 100644 --- a/utils/funcexp/func_mod.cpp +++ b/utils/funcexp/func_mod.cpp @@ -1,4 +1,5 @@ /* Copyright (C) 2014 InfiniDB, Inc. + Copyright (C) 2019 MariaDB Corporaton This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License diff --git a/utils/funcexp/func_nullif.cpp b/utils/funcexp/func_nullif.cpp index 3a40fe134..4bac44c45 100644 --- a/utils/funcexp/func_nullif.cpp +++ b/utils/funcexp/func_nullif.cpp @@ -1,5 +1,5 @@ /* Copyright (C) 2014 InfiniDB, Inc. - Copyright (C) 2016 MariaDB Corporaton + Copyright (C) 2019 MariaDB Corporaton This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License diff --git a/utils/funcexp/func_pow.cpp b/utils/funcexp/func_pow.cpp index cbe1a9905..f94432b6d 100644 --- a/utils/funcexp/func_pow.cpp +++ b/utils/funcexp/func_pow.cpp @@ -1,4 +1,5 @@ /* Copyright (C) 2014 InfiniDB, Inc. + Copyright (C) 2019 MariaDB Corporaton This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License diff --git a/utils/funcexp/func_round.cpp b/utils/funcexp/func_round.cpp index eab18abc2..2a843f429 100644 --- a/utils/funcexp/func_round.cpp +++ b/utils/funcexp/func_round.cpp @@ -1,4 +1,5 @@ /* Copyright (C) 2014 InfiniDB, Inc. + Copyright (C) 2019 MariaDB Corporaton This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License diff --git a/utils/funcexp/func_substring_index.cpp b/utils/funcexp/func_substring_index.cpp index 7266960d6..3fb3643de 100644 --- a/utils/funcexp/func_substring_index.cpp +++ b/utils/funcexp/func_substring_index.cpp @@ -1,4 +1,5 @@ /* Copyright (C) 2014 InfiniDB, Inc. + Copyright (C) 2019 MariaDB Corporaton This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License diff --git a/utils/funcexp/func_truncate.cpp b/utils/funcexp/func_truncate.cpp index 9976e9c38..1b8383b28 100644 --- a/utils/funcexp/func_truncate.cpp +++ b/utils/funcexp/func_truncate.cpp @@ -1,4 +1,5 @@ /* Copyright (C) 2014 InfiniDB, Inc. + Copyright (C) 2019 MariaDB Corporaton This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License diff --git a/utils/funcexp/funcexp.cpp b/utils/funcexp/funcexp.cpp index 9751eabd5..d933474f7 100644 --- a/utils/funcexp/funcexp.cpp +++ b/utils/funcexp/funcexp.cpp @@ -1,4 +1,5 @@ /* Copyright (C) 2014 InfiniDB, Inc. + Copyright (C) 2019 MariaDB Corporaton This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License diff --git a/utils/funcexp/funchelpers.h b/utils/funcexp/funchelpers.h index b6942bc68..e1290d230 100644 --- a/utils/funcexp/funchelpers.h +++ b/utils/funcexp/funchelpers.h @@ -1,4 +1,5 @@ /* Copyright (C) 2014 InfiniDB, Inc. + Copyright (C) 2019 MariaDB Corporaton This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License diff --git a/utils/funcexp/functor.cpp b/utils/funcexp/functor.cpp index 4350d2dc0..54060d74b 100644 --- a/utils/funcexp/functor.cpp +++ b/utils/funcexp/functor.cpp @@ -1,4 +1,5 @@ /* Copyright (C) 2014 InfiniDB, Inc. + Copyright (C) 2019 MariaDB Corporaton This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License diff --git a/utils/funcexp/functor.h b/utils/funcexp/functor.h index ff3d68976..9904c6831 100644 --- a/utils/funcexp/functor.h +++ b/utils/funcexp/functor.h @@ -1,4 +1,5 @@ /* Copyright (C) 2014 InfiniDB, Inc. + Copyright (C) 2019 MariaDB Corporaton This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License diff --git a/utils/funcexp/functor_all.h b/utils/funcexp/functor_all.h index 24dbebfaf..71d857132 100644 --- a/utils/funcexp/functor_all.h +++ b/utils/funcexp/functor_all.h @@ -1,4 +1,5 @@ /* Copyright (C) 2014 InfiniDB, Inc. + Copyright (C) 2019 MariaDB Corporaton This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License diff --git a/utils/funcexp/functor_dtm.h b/utils/funcexp/functor_dtm.h index 79c4eabd1..bddd78202 100644 --- a/utils/funcexp/functor_dtm.h +++ b/utils/funcexp/functor_dtm.h @@ -1,4 +1,5 @@ /* Copyright (C) 2014 InfiniDB, Inc. + Copyright (C) 2019 MariaDB Corporaton This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License diff --git a/utils/funcexp/functor_export.h b/utils/funcexp/functor_export.h index 8cec08373..af7ca6cee 100644 --- a/utils/funcexp/functor_export.h +++ b/utils/funcexp/functor_export.h @@ -1,4 +1,5 @@ /* Copyright (C) 2014 InfiniDB, Inc. + Copyright (C) 2019 MariaDB Corporaton This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License diff --git a/utils/funcexp/functor_int.h b/utils/funcexp/functor_int.h index 15319ee3a..e5b0093a9 100644 --- a/utils/funcexp/functor_int.h +++ b/utils/funcexp/functor_int.h @@ -1,4 +1,5 @@ /* Copyright (C) 2014 InfiniDB, Inc. + Copyright (C) 2019 MariaDB Corporaton This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License diff --git a/utils/funcexp/functor_real.h b/utils/funcexp/functor_real.h index 341602ddf..a12759333 100644 --- a/utils/funcexp/functor_real.h +++ b/utils/funcexp/functor_real.h @@ -1,4 +1,5 @@ /* Copyright (C) 2014 InfiniDB, Inc. + Copyright (C) 2019 MariaDB Corporaton This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License diff --git a/utils/funcexp/functor_str.h b/utils/funcexp/functor_str.h index 6624290ac..efd4ae33c 100644 --- a/utils/funcexp/functor_str.h +++ b/utils/funcexp/functor_str.h @@ -1,4 +1,5 @@ /* Copyright (C) 2014 InfiniDB, Inc. + Copyright (C) 2019 MariaDB Corporaton This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License diff --git a/utils/joiner/joinpartition.cpp b/utils/joiner/joinpartition.cpp index 7d0af1d26..4982ed750 100644 --- a/utils/joiner/joinpartition.cpp +++ b/utils/joiner/joinpartition.cpp @@ -1,5 +1,5 @@ /* Copyright (C) 2014 InfiniDB, Inc. - Copyright (C) 2018 MariaDB Corporation + Copyright (C) 2019 MariaDB Corporaton This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License diff --git a/utils/joiner/tuplejoiner.cpp b/utils/joiner/tuplejoiner.cpp index c84e54ae0..ff64b8283 100644 --- a/utils/joiner/tuplejoiner.cpp +++ b/utils/joiner/tuplejoiner.cpp @@ -1,4 +1,5 @@ /* Copyright (C) 2014 InfiniDB, Inc. + Copyright (C) 2019 MariaDB Corporaton This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License diff --git a/utils/joiner/tuplejoiner.h b/utils/joiner/tuplejoiner.h index 211dd5cfe..7302876e0 100644 --- a/utils/joiner/tuplejoiner.h +++ b/utils/joiner/tuplejoiner.h @@ -1,4 +1,5 @@ /* Copyright (C) 2014 InfiniDB, Inc. + Copyright (C) 2019 MariaDB Corporaton This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License diff --git a/utils/messageqcpp/bytestream.cpp b/utils/messageqcpp/bytestream.cpp index edd184ddb..003c35fd4 100644 --- a/utils/messageqcpp/bytestream.cpp +++ b/utils/messageqcpp/bytestream.cpp @@ -1,6 +1,6 @@ /* - Copyright (c) 2017, MariaDB Copyright (C) 2014 InfiniDB, Inc. + Copyright (C) 2019 MariaDB Corporaton This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License diff --git a/utils/messageqcpp/bytestream.h b/utils/messageqcpp/bytestream.h index 4b47c8593..cf3eb2057 100644 --- a/utils/messageqcpp/bytestream.h +++ b/utils/messageqcpp/bytestream.h @@ -1,6 +1,6 @@ /* - Copyright (c) 2017, MariaDB Copyright (C) 2014 InfiniDB, Inc. + Copyright (C) 2019 MariaDB Corporaton This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License diff --git a/utils/rowgroup/rowaggregation.cpp b/utils/rowgroup/rowaggregation.cpp index 7df68392b..e550a3f04 100644 --- a/utils/rowgroup/rowaggregation.cpp +++ b/utils/rowgroup/rowaggregation.cpp @@ -1,6 +1,6 @@ /* - Copyright (c) 2017, MariaDB 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 diff --git a/utils/rowgroup/rowaggregation.h b/utils/rowgroup/rowaggregation.h index 47d365085..4817ed476 100644 --- a/utils/rowgroup/rowaggregation.h +++ b/utils/rowgroup/rowaggregation.h @@ -1,6 +1,6 @@ /* - Copyright (c) 2017, MariaDB 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 diff --git a/utils/rowgroup/rowgroup.cpp b/utils/rowgroup/rowgroup.cpp index 4303dbc27..b4ab56ff1 100644 --- a/utils/rowgroup/rowgroup.cpp +++ b/utils/rowgroup/rowgroup.cpp @@ -1,6 +1,6 @@ /* - Copyright (c) 2017, MariaDB 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 diff --git a/utils/rowgroup/rowgroup.h b/utils/rowgroup/rowgroup.h index cbf74ef32..27f0b98a4 100644 --- a/utils/rowgroup/rowgroup.h +++ b/utils/rowgroup/rowgroup.h @@ -1,6 +1,6 @@ /* - Copyright (c) 2017, MariaDB 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 diff --git a/utils/udfsdk/udfsdk.cpp b/utils/udfsdk/udfsdk.cpp index f91473a81..f8885a977 100644 --- a/utils/udfsdk/udfsdk.cpp +++ b/utils/udfsdk/udfsdk.cpp @@ -1,5 +1,5 @@ /* Copyright (C) 2014 InfiniDB, Inc. - Copyright (C) 2016 MariaDB Corporaton + 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 diff --git a/utils/udfsdk/udfsdk.h b/utils/udfsdk/udfsdk.h index 04e1c07b5..4587d067d 100644 --- a/utils/udfsdk/udfsdk.h +++ b/utils/udfsdk/udfsdk.h @@ -1,5 +1,5 @@ /* Copyright (C) 2014 InfiniDB, Inc. - Copyright (C) 2016 MariaDB Corporaton + 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 diff --git a/utils/windowfunction/frameboundrange.cpp b/utils/windowfunction/frameboundrange.cpp index 8d73aace2..21323f823 100644 --- a/utils/windowfunction/frameboundrange.cpp +++ b/utils/windowfunction/frameboundrange.cpp @@ -1,4 +1,5 @@ /* 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 diff --git a/utils/windowfunction/idborderby.cpp b/utils/windowfunction/idborderby.cpp index f839bc506..8d683fe5e 100644 --- a/utils/windowfunction/idborderby.cpp +++ b/utils/windowfunction/idborderby.cpp @@ -1,4 +1,5 @@ /* 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 diff --git a/utils/windowfunction/idborderby.h b/utils/windowfunction/idborderby.h index cd31eff30..4453828ba 100644 --- a/utils/windowfunction/idborderby.h +++ b/utils/windowfunction/idborderby.h @@ -1,4 +1,5 @@ /* 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 diff --git a/utils/windowfunction/wf_lead_lag.cpp b/utils/windowfunction/wf_lead_lag.cpp index 961032f2f..c720e9db2 100644 --- a/utils/windowfunction/wf_lead_lag.cpp +++ b/utils/windowfunction/wf_lead_lag.cpp @@ -1,4 +1,5 @@ /* 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 diff --git a/utils/windowfunction/wf_min_max.cpp b/utils/windowfunction/wf_min_max.cpp index 8679c743b..511fbb4eb 100644 --- a/utils/windowfunction/wf_min_max.cpp +++ b/utils/windowfunction/wf_min_max.cpp @@ -1,4 +1,5 @@ /* 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 diff --git a/utils/windowfunction/wf_nth_value.cpp b/utils/windowfunction/wf_nth_value.cpp index f5be1462f..c6eea0ace 100644 --- a/utils/windowfunction/wf_nth_value.cpp +++ b/utils/windowfunction/wf_nth_value.cpp @@ -1,4 +1,5 @@ /* 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 diff --git a/utils/windowfunction/wf_percentile.cpp b/utils/windowfunction/wf_percentile.cpp index d7b2ab9ca..acf137c2e 100644 --- a/utils/windowfunction/wf_percentile.cpp +++ b/utils/windowfunction/wf_percentile.cpp @@ -1,4 +1,5 @@ /* 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 diff --git a/utils/windowfunction/wf_stats.cpp b/utils/windowfunction/wf_stats.cpp index b5af19883..db4b107ee 100644 --- a/utils/windowfunction/wf_stats.cpp +++ b/utils/windowfunction/wf_stats.cpp @@ -1,4 +1,5 @@ /* 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 diff --git a/utils/windowfunction/wf_sum_avg.cpp b/utils/windowfunction/wf_sum_avg.cpp index 341b55dd7..4632496df 100644 --- a/utils/windowfunction/wf_sum_avg.cpp +++ b/utils/windowfunction/wf_sum_avg.cpp @@ -1,4 +1,5 @@ /* 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 diff --git a/utils/windowfunction/wf_sum_avg.h b/utils/windowfunction/wf_sum_avg.h index e21d7a86f..48d46453b 100644 --- a/utils/windowfunction/wf_sum_avg.h +++ b/utils/windowfunction/wf_sum_avg.h @@ -1,4 +1,5 @@ /* 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 diff --git a/utils/windowfunction/wf_udaf.cpp b/utils/windowfunction/wf_udaf.cpp index 6b1359334..903dda613 100644 --- a/utils/windowfunction/wf_udaf.cpp +++ b/utils/windowfunction/wf_udaf.cpp @@ -1,5 +1,5 @@ /************************************************************************************ - Copyright (C) 2017 MariaDB Corporation AB + Copyright (c) 2019 MariaDB Corporation This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public diff --git a/utils/windowfunction/windowfunctiontype.cpp b/utils/windowfunction/windowfunctiontype.cpp index 930e6eecc..776e63c73 100644 --- a/utils/windowfunction/windowfunctiontype.cpp +++ b/utils/windowfunction/windowfunctiontype.cpp @@ -1,6 +1,6 @@ /* - Copyright (c) 2017, MariaDB 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 diff --git a/utils/windowfunction/windowfunctiontype.h b/utils/windowfunction/windowfunctiontype.h index 5388898a0..e0a1aa832 100644 --- a/utils/windowfunction/windowfunctiontype.h +++ b/utils/windowfunction/windowfunctiontype.h @@ -1,4 +1,5 @@ /* 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 From b7606139d7310370f83983c977920ff2a3637f00 Mon Sep 17 00:00:00 2001 From: David Hall Date: Tue, 5 Mar 2019 09:45:34 -0600 Subject: [PATCH 25/38] MCOL-1822 Typo repair. Should be long double, not double --- dbcon/execplan/treenode.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dbcon/execplan/treenode.h b/dbcon/execplan/treenode.h index 3edf1e9db..91ceb2a11 100644 --- a/dbcon/execplan/treenode.h +++ b/dbcon/execplan/treenode.h @@ -1010,7 +1010,7 @@ inline long double TreeNode::getLongDoubleVal() case CalpontSystemCatalog::SMALLINT: case CalpontSystemCatalog::MEDINT: case CalpontSystemCatalog::INT: - return (double)fResult.intVal; + return (long double)fResult.intVal; case CalpontSystemCatalog::UBIGINT: case CalpontSystemCatalog::UTINYINT: From 0ea4ccfe063900516c58c2f88194062e47e46157 Mon Sep 17 00:00:00 2001 From: David Hall Date: Tue, 5 Mar 2019 09:49:11 -0600 Subject: [PATCH 26/38] MCOL-1822 colWidth should be sizeof(long double), not hardcoded 16 --- dbcon/execplan/arithmeticoperator.cpp | 2 +- dbcon/execplan/predicateoperator.cpp | 4 ++-- dbcon/mysql/ha_calpont_execplan.cpp | 4 ++-- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/dbcon/execplan/arithmeticoperator.cpp b/dbcon/execplan/arithmeticoperator.cpp index 78dffe430..71857a8fa 100644 --- a/dbcon/execplan/arithmeticoperator.cpp +++ b/dbcon/execplan/arithmeticoperator.cpp @@ -215,7 +215,7 @@ void ArithmeticOperator::adjustResultType(const CalpontSystemCatalog::ColType& m n.colDataType = CalpontSystemCatalog::LONGDOUBLE; n.scale = m.scale; // @bug5736, save the original decimal scale n.precision = -1; // @bug5736, indicate this double is for decimal math - n.colWidth = 16; + n.colWidth = sizeof(long double); fResultType = n; } } diff --git a/dbcon/execplan/predicateoperator.cpp b/dbcon/execplan/predicateoperator.cpp index 119f49193..704ab3a63 100644 --- a/dbcon/execplan/predicateoperator.cpp +++ b/dbcon/execplan/predicateoperator.cpp @@ -320,7 +320,7 @@ void PredicateOperator::setOpType(Type& l, Type& r) case execplan::CalpontSystemCatalog::LONGDOUBLE: fOperationType.colDataType = execplan::CalpontSystemCatalog::LONGDOUBLE; - fOperationType.colWidth = 16; + fOperationType.colWidth = sizeof(long double); break; default: fOperationType.colDataType = execplan::CalpontSystemCatalog::DOUBLE; @@ -379,7 +379,7 @@ void PredicateOperator::setOpType(Type& l, Type& r) r.colDataType == execplan::CalpontSystemCatalog::LONGDOUBLE) { fOperationType.colDataType = execplan::CalpontSystemCatalog::LONGDOUBLE; - fOperationType.colWidth = 16; + fOperationType.colWidth = sizeof(long double); } else { diff --git a/dbcon/mysql/ha_calpont_execplan.cpp b/dbcon/mysql/ha_calpont_execplan.cpp index f7425fce7..1094ffc37 100644 --- a/dbcon/mysql/ha_calpont_execplan.cpp +++ b/dbcon/mysql/ha_calpont_execplan.cpp @@ -4499,7 +4499,7 @@ ReturnedColumn* buildAggregateColumn(Item* item, gp_walk_info& gwi) { CalpontSystemCatalog::ColType ct = parm->resultType(); ct.colDataType = CalpontSystemCatalog::LONGDOUBLE; - ct.colWidth = 16; + ct.colWidth = sizeof(long double); ct.scale += 4; ct.precision = -1; ac->resultType(ct); @@ -4518,7 +4518,7 @@ ReturnedColumn* buildAggregateColumn(Item* item, gp_walk_info& gwi) { CalpontSystemCatalog::ColType ct = parm->resultType(); ct.colDataType = CalpontSystemCatalog::LONGDOUBLE; - ct.colWidth = 16; + ct.colWidth = sizeof(long double); ct.precision = -1; ac->resultType(ct); } From 38d077d72f47aaee79fae77084e3126fd7d0eebe Mon Sep 17 00:00:00 2001 From: David Hall Date: Tue, 5 Mar 2019 10:00:15 -0600 Subject: [PATCH 27/38] MCOL-1822 Change scanFlags back to bool (had changed to int for debugging) --- dbcon/joblist/primitivestep.h | 6 +++--- dbcon/joblist/rowestimator.cpp | 3 +-- dbcon/joblist/rowestimator.h | 3 +-- 3 files changed, 5 insertions(+), 7 deletions(-) diff --git a/dbcon/joblist/primitivestep.h b/dbcon/joblist/primitivestep.h index 2a668ed06..588dd49bb 100644 --- a/dbcon/joblist/primitivestep.h +++ b/dbcon/joblist/primitivestep.h @@ -392,7 +392,7 @@ private: boost::condition condvar; boost::condition flushed; SP_LBIDList lbidList; - std::vector scanFlags; // use to keep track of which extents to eliminate from this step + std::vector scanFlags; // use to keep track of which extents to eliminate from this step uint32_t uniqueID; //@bug 2634 @@ -1411,7 +1411,7 @@ private: boost::mutex serializeJoinerMutex; boost::condition condvarWakeupProducer, condvar; - std::vector scanFlags; // use to keep track of which extents to eliminate from this step + std::vector scanFlags; // use to keep track of which extents to eliminate from this step bool BPPIsAllocated; uint32_t uniqueID; ResourceManager* fRm; @@ -1477,7 +1477,7 @@ private: * component and this new array as the runtime component. The final CP decision * is scanFlags & runtimeCP. */ - std::vector runtimeCPFlags; + std::vector runtimeCPFlags; /* semijoin vars */ rowgroup::RowGroup joinFERG; diff --git a/dbcon/joblist/rowestimator.cpp b/dbcon/joblist/rowestimator.cpp index 0b250e2bb..e69c66d79 100644 --- a/dbcon/joblist/rowestimator.cpp +++ b/dbcon/joblist/rowestimator.cpp @@ -1,5 +1,4 @@ /* Copyright (C) 2014 InfiniDB, Inc. - Copyright (C) 2019 MariaDB Corporaton This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License @@ -456,7 +455,7 @@ float RowEstimator::estimateRowReturnFactor(const BRM::EMEntry& emEntry, // This function returns the estimated row count for the entire TupleBPS. It samples the last 20 (configurable) extents to // calculate the estimate. uint64_t RowEstimator::estimateRows(const vector& cpColVec, - const std::vector& scanFlags, + const std::vector& scanFlags, BRM::DBRM& dbrm, const execplan::CalpontSystemCatalog::OID oid) diff --git a/dbcon/joblist/rowestimator.h b/dbcon/joblist/rowestimator.h index 3cbffde1d..477bc2b30 100644 --- a/dbcon/joblist/rowestimator.h +++ b/dbcon/joblist/rowestimator.h @@ -1,5 +1,4 @@ /* Copyright (C) 2014 InfiniDB, Inc. - Copyright (C) 2019 MariaDB Corporaton This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License @@ -63,7 +62,7 @@ public: * */ uint64_t estimateRows(const std::vector& cpColVec, - const std::vector & scanFlags, + const std::vector & scanFlags, BRM::DBRM& dbrm, const execplan::CalpontSystemCatalog::OID oid); From 79aca9a28512af8cb3ee64b2de430273b8522c83 Mon Sep 17 00:00:00 2001 From: Roman Nozdrin Date: Tue, 5 Mar 2019 01:32:38 +0300 Subject: [PATCH 28/38] MCOL-2160 MCOL-1883 RENAME now supports both '/' symbols in table names and doesn't crash on multi-RENAME statements when non-alphanumeric chars are presented in table names. Replaced INFINIDB_DEBUG with MCS_DEBUG in ha_calpont_ddl.cpp Removed unused debug outputs. --- dbcon/mysql/ha_calpont_ddl.cpp | 96 +++++++++++++++++++--------------- 1 file changed, 54 insertions(+), 42 deletions(-) diff --git a/dbcon/mysql/ha_calpont_ddl.cpp b/dbcon/mysql/ha_calpont_ddl.cpp index 487e0d4f8..434e47441 100644 --- a/dbcon/mysql/ha_calpont_ddl.cpp +++ b/dbcon/mysql/ha_calpont_ddl.cpp @@ -39,7 +39,6 @@ #include #endif #include -//#define NDEBUG #include using namespace std; @@ -654,7 +653,7 @@ int ProcessDDLStatement(string& ddlStatement, string& schema, const string& tabl { SqlParser parser; THD* thd = current_thd; -#ifdef INFINIDB_DEBUG +#ifdef MCS_DEBUG cout << "ProcessDDLStatement: " << schema << "." << table << ":" << ddlStatement << endl; #endif @@ -1599,7 +1598,6 @@ int ProcessDDLStatement(string& ddlStatement, string& schema, const string& tabl } else if (ddlpackage::AtaRenameColumn* renameColumnsPtr = dynamic_cast(actionList[i])) { - //cout << "Rename a column" << endl; uint64_t startValue = 1; bool autoIncre = false; //@Bug 3746 Handle compression type @@ -1798,7 +1796,6 @@ int ProcessDDLStatement(string& ddlStatement, string& schema, const string& tabl stmt.fSql = ddlStatement; stmt.fOwner = schema; stmt.fTableWithAutoi = isAnyAutoincreCol; - //cout << "Sending to DDLProc" << endl; ByteStream bytestream; bytestream << stmt.fSessionID; stmt.serialize(bytestream); @@ -1887,30 +1884,6 @@ int ProcessDDLStatement(string& ddlStatement, string& schema, const string& tabl return rc; } -// Fails with `/` sign in table name -pair parseTableName(const string& tn) -{ - string db; - string tb; - typedef boost::tokenizer > tokenizer; -#ifdef _MSC_VER - boost::char_separator sep("\\"); -#else - boost::char_separator sep("/"); -#endif - tokenizer tokens(tn, sep); - tokenizer::iterator tok_iter = tokens.begin(); - ++tok_iter; - idbassert(tok_iter != tokens.end()); - db = *tok_iter; - ++tok_iter; - idbassert(tok_iter != tokens.end()); - tb = *tok_iter; - ++tok_iter; - idbassert(tok_iter == tokens.end()); - return make_pair(db, tb); -} - } // @@ -1988,7 +1961,7 @@ static bool get_field_default_value(THD *thd, Field *field, String *def_value, int ha_calpont_impl_create_(const char* name, TABLE* table_arg, HA_CREATE_INFO* create_info, cal_connection_info& ci) { -#ifdef INFINIDB_DEBUG +#ifdef MCS_DEBUG cout << "ha_calpont_impl_create_: " << name << endl; #endif THD* thd = current_thd; @@ -2060,7 +2033,7 @@ int ha_calpont_impl_create_(const char* name, TABLE* table_arg, HA_CREATE_INFO* if (isCreate) { -#ifdef INFINIDB_DEBUG +#ifdef MCS_DEBUG cout << "ha_calpont_impl_create_: SCHEMA SYNC ONLY found, returning" << endl; #endif return 0; @@ -2091,7 +2064,7 @@ int ha_calpont_impl_create_(const char* name, TABLE* table_arg, HA_CREATE_INFO* { ci.isAlter = true; ci.alterTableState = cal_connection_info::ALTER_FIRST_RENAME; -#ifdef INFINIDB_DEBUG +#ifdef MCS_DEBUG cout << "ha_calpont_impl_create_: now in state ALTER_FIRST_RENAME" << endl; #endif } @@ -2269,7 +2242,7 @@ int ha_calpont_impl_create_(const char* name, TABLE* table_arg, HA_CREATE_INFO* //Bug 1705 reset the flag if error occurs ci.alterTableState = cal_connection_info::NOT_ALTER; ci.isAlter = false; -#ifdef INFINIDB_DEBUG +#ifdef MCS_DEBUG cout << "ha_calpont_impl_create_: ProcessDDL error, now in state NOT_ALTER" << endl; #endif } @@ -2279,7 +2252,7 @@ int ha_calpont_impl_create_(const char* name, TABLE* table_arg, HA_CREATE_INFO* int ha_calpont_impl_delete_table_(const char* db, const char* name, cal_connection_info& ci) { -#ifdef INFINIDB_DEBUG +#ifdef MCS_DEBUG cout << "ha_calpont_impl_delete_table: " << db << name << endl; #endif THD* thd = current_thd; @@ -2295,7 +2268,6 @@ int ha_calpont_impl_delete_table_(const char* db, const char* name, cal_connecti std::string stmt(query); algorithm::to_upper(stmt); -// cout << "ha_calpont_impl_delete_table: " << schema.c_str() << "." << tbl.c_str() << " " << stmt.c_str() << endl; // @bug 4158 allow table name with 'restrict' in it (but not by itself) std::string::size_type fpos; fpos = stmt.rfind(" RESTRICT"); @@ -2334,7 +2306,6 @@ int ha_calpont_impl_delete_table_(const char* db, const char* name, cal_connecti stmt += ";"; int rc = ProcessDDLStatement(stmt, schema, tbl, tid2sid(thd->thread_id), emsg); -// cout << "ProcessDDLStatement rc=" << rc << endl; if (rc != 0) { push_warning(thd, Sql_condition::WARN_LEVEL_WARN, 9999, emsg.c_str()); @@ -2343,6 +2314,7 @@ int ha_calpont_impl_delete_table_(const char* db, const char* name, cal_connecti return rc; } + /** @brief Find and return a pointer to the last slash in the name. @@ -2395,16 +2367,54 @@ void decode_table_name(char *buf, const char *path, size_t pathLen) } } +/** + @brief + Parses the path to extract both database and table names. + + @details + Parses the path to extract both database + and table names, e.g path ./test/d$ produces + a pair of strings 'test' and 'd$'. This f() looks for a '/' + token only twice to allow '/' symbol in table names. + + Called from ha_calpont_ddl.cpp by ha_calpont_impl_rename_table_(). +*/ +pair parseTableName(const string& tn) +{ + string db; + string tb; + typedef boost::tokenizer > tokenizer; +#ifdef _MSC_VER + boost::char_separator sep("\\"); +#else + boost::char_separator sep("/"); +#endif + tokenizer tokens(tn, sep); + tokenizer::iterator tok_iter = tokens.begin(); + ++tok_iter; + idbassert(tok_iter != tokens.end()); + db = *tok_iter; + ++tok_iter; + idbassert(tok_iter != tokens.end()); + tb = *tok_iter; + return make_pair(db, tb); +} + int ha_calpont_impl_rename_table_(const char* from, const char* to, cal_connection_info& ci) { THD* thd = current_thd; string emsg; + // to and from are rewritten after decode_table_name() + // so use a copy of pntrs + const char* from_cpy = from; + const char* to_cpy = to; + pair fromPair; pair toPair; string stmt; - //this is replcated DDL, treat it just like SSO + //this is replicated DDL, treat it just like SSO if (thd->slave_thread) return 0; @@ -2418,16 +2428,18 @@ int ha_calpont_impl_rename_table_(const char* from, const char* to, cal_connecti } // MCOL-1855 Decode the table name if it contains encoded symbols. - // This approach fails when `/` is used in the paths. - size_t pathLen = strlen(from); - char pathCopy[pathLen]; - decode_table_name(pathCopy, from, pathLen); + size_t pathLen = strlen(from_cpy); + char pathCopy[FN_REFLEN]; + decode_table_name(pathCopy, from_cpy, pathLen); fromPair = parseTableName(const_cast(pathCopy)); - pathLen = strlen(to); - decode_table_name(pathCopy, to, pathLen); + pathLen = strlen(to_cpy); + decode_table_name(pathCopy, to_cpy, pathLen); + toPair = parseTableName(const_cast(pathCopy)); + // TBD The next two blocks must be removed to allow different dbnames + // in RENAME statement. if (fromPair.first != toPair.first) { thd->get_stmt_da()->set_overwrite_status(true); From 5dff25e0a041e31ffd79884dfeb1fa40169c3e23 Mon Sep 17 00:00:00 2001 From: David Hall Date: Wed, 6 Mar 2019 11:52:01 -0600 Subject: [PATCH 29/38] MCOL-2180 Update UDAF docs --- utils/udfsdk/docs/build/Makefile | 68 ++++++++++++++++++ utils/udfsdk/docs/build/UDAF.pdf | Bin 0 -> 225293 bytes utils/udfsdk/docs/source/changelog.rst | 1 + utils/udfsdk/docs/source/conf.py | 6 +- .../udfsdk/docs/source/reference/UDAFMap.rst | 21 ++++++ utils/udfsdk/docs/source/usage/sourcefile.rst | 0 6 files changed, 93 insertions(+), 3 deletions(-) create mode 100644 utils/udfsdk/docs/build/Makefile create mode 100644 utils/udfsdk/docs/build/UDAF.pdf mode change 100755 => 100644 utils/udfsdk/docs/source/usage/sourcefile.rst diff --git a/utils/udfsdk/docs/build/Makefile b/utils/udfsdk/docs/build/Makefile new file mode 100644 index 000000000..c561680ad --- /dev/null +++ b/utils/udfsdk/docs/build/Makefile @@ -0,0 +1,68 @@ +# Makefile for Sphinx LaTeX output + +ALLDOCS = $(basename $(wildcard *.tex)) +ALLPDF = $(addsuffix .pdf,$(ALLDOCS)) +ALLDVI = $(addsuffix .dvi,$(ALLDOCS)) +ALLXDV = +ALLPS = $(addsuffix .ps,$(ALLDOCS)) +ALLIMGS = $(wildcard *.png *.gif *.jpg *.jpeg) + +# Prefix for archive names +ARCHIVEPREFIX = +# Additional LaTeX options (passed via variables in latexmkrc/latexmkjarc file) +export LATEXOPTS = +# Additional latexmk options +LATEXMKOPTS = +# format: pdf or dvi (used only by archive targets) +FMT = pdf + +LATEX = latexmk -dvi +PDFLATEX = latexmk -pdf -dvi- -ps- + + +%.png %.gif %.jpg %.jpeg: FORCE_MAKE + extractbb '$@' + +%.dvi: %.tex FORCE_MAKE + $(LATEX) $(LATEXMKOPTS) '$<' + +%.ps: %.dvi + dvips '$<' + +%.pdf: %.tex FORCE_MAKE + $(PDFLATEX) $(LATEXMKOPTS) '$<' + +all: $(ALLPDF) + +all-dvi: $(ALLDVI) + +all-ps: $(ALLPS) + +all-pdf: $(ALLPDF) + +zip: all-$(FMT) + mkdir $(ARCHIVEPREFIX)docs-$(FMT) + cp $(ALLPDF) $(ARCHIVEPREFIX)docs-$(FMT) + zip -q -r -9 $(ARCHIVEPREFIX)docs-$(FMT).zip $(ARCHIVEPREFIX)docs-$(FMT) + rm -r $(ARCHIVEPREFIX)docs-$(FMT) + +tar: all-$(FMT) + mkdir $(ARCHIVEPREFIX)docs-$(FMT) + cp $(ALLPDF) $(ARCHIVEPREFIX)docs-$(FMT) + tar cf $(ARCHIVEPREFIX)docs-$(FMT).tar $(ARCHIVEPREFIX)docs-$(FMT) + rm -r $(ARCHIVEPREFIX)docs-$(FMT) + +gz: tar + gzip -9 < $(ARCHIVEPREFIX)docs-$(FMT).tar > $(ARCHIVEPREFIX)docs-$(FMT).tar.gz + +bz2: tar + bzip2 -9 -k $(ARCHIVEPREFIX)docs-$(FMT).tar + +xz: tar + xz -9 -k $(ARCHIVEPREFIX)docs-$(FMT).tar + +clean: + rm -f *.log *.ind *.aux *.toc *.syn *.idx *.out *.ilg *.pla *.ps *.tar *.tar.gz *.tar.bz2 *.tar.xz $(ALLPDF) $(ALLDVI) $(ALLXDV) *.fls *.fdb_latexmk + +.PHONY: all all-pdf all-dvi all-ps clean zip tar gz bz2 xz +.PHONY: FORCE_MAKE \ No newline at end of file diff --git a/utils/udfsdk/docs/build/UDAF.pdf b/utils/udfsdk/docs/build/UDAF.pdf new file mode 100644 index 0000000000000000000000000000000000000000..62b92cafd8694bff1774ef2074c2ace01ac4d893 GIT binary patch literal 225293 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ℑ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 V=d>UNfrLdNAW{wvWgQjL{{r}9A#(r# literal 0 HcmV?d00001 diff --git a/utils/udfsdk/docs/source/changelog.rst b/utils/udfsdk/docs/source/changelog.rst index 1a7c749f9..84688fad8 100644 --- a/utils/udfsdk/docs/source/changelog.rst +++ b/utils/udfsdk/docs/source/changelog.rst @@ -5,5 +5,6 @@ Version History | Version | Date | Changes | +=========+============+=============================+ | 1.1.0α | 2017-08-25 | - First alpha release | ++---------+------------+-----------------------------+ | 1.2.0α | 2016-05-18 | - Add multi parm support | +---------+------------+-----------------------------+ diff --git a/utils/udfsdk/docs/source/conf.py b/utils/udfsdk/docs/source/conf.py index 3bd11a000..9e86ebc84 100644 --- a/utils/udfsdk/docs/source/conf.py +++ b/utils/udfsdk/docs/source/conf.py @@ -49,7 +49,7 @@ master_doc = 'index' # General information about the project. project = u'UDAF' -copyright = u'2017, MariaDB Corporation' +copyright = u'2019, MariaDB Corporation' author = u'MariaDB Corporation' # The version info for the project you're documenting, acts as replacement for @@ -57,9 +57,9 @@ author = u'MariaDB Corporation' # built documents. # # The short X.Y version. -version = u'1.1' +version = u'1.2' # The full version, including alpha/beta/rc tags. -release = u'1.1' +release = u'1.2' # The language for content autogenerated by Sphinx. Refer to documentation # for a list of supported languages. diff --git a/utils/udfsdk/docs/source/reference/UDAFMap.rst b/utils/udfsdk/docs/source/reference/UDAFMap.rst index d3cda63f4..7c3b7c7c5 100644 --- a/utils/udfsdk/docs/source/reference/UDAFMap.rst +++ b/utils/udfsdk/docs/source/reference/UDAFMap.rst @@ -34,3 +34,24 @@ The UDAFMap is where we tell the system about our function. For Columnstore 1.2, return fm; } +An alternative method added for 1.2 is to put the following in your .cpp file. +replace "median" with the name of your function: + +:: + + class Add_median_ToUDAFMap + { + public: + Add_median_ToUDAFMap() + { + UDAFMap::getMap()["median"] = new median(); + } + }; + + static Add_median_ToUDAFMap addToMap; + +This defines an object whose constructor adds the entry to the UDAFMap. The +static declaration instatiates an object at runtime, thus adding the entry +at startup. + + diff --git a/utils/udfsdk/docs/source/usage/sourcefile.rst b/utils/udfsdk/docs/source/usage/sourcefile.rst old mode 100755 new mode 100644 From ea911a118f5cb465ae43a72222a544a3caeb25c6 Mon Sep 17 00:00:00 2001 From: David Hall Date: Wed, 6 Mar 2019 12:19:03 -0600 Subject: [PATCH 30/38] MCOL-2180 Update UDAF docs --- utils/udfsdk/docs/README | 8 ++++++++ utils/udfsdk/docs/build/UDAF.pdf | Bin 225293 -> 226430 bytes .../docs/source/usage/memoryallocation.rst | 4 ++-- 3 files changed, 10 insertions(+), 2 deletions(-) create mode 100644 utils/udfsdk/docs/README diff --git a/utils/udfsdk/docs/README b/utils/udfsdk/docs/README new file mode 100644 index 000000000..3fa3f0f3c --- /dev/null +++ b/utils/udfsdk/docs/README @@ -0,0 +1,8 @@ +To Build PDF: + + +[root@me docs]# sphinx-build -b latex source build +[root@me docs]# cd build +[root@me docs]# make + +The UDAF.pdf file will be in the build directory \ No newline at end of file diff --git a/utils/udfsdk/docs/build/UDAF.pdf b/utils/udfsdk/docs/build/UDAF.pdf index 62b92cafd8694bff1774ef2074c2ace01ac4d893..780d4024c53c6c631beeb998ad3897ac8c2e374a 100644 GIT binary patch delta 44377 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

6mr@y}#~%MN(GvU`Y9vo@xSEUnhty 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 IG~`kK53rM9n*aa+ delta 43516 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

  • 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 clas For MEDIAN, serialize() iterates over the set and streams the values to the :ref:`ByteStream ` and unserialze unstreams them back into the set: .. literalinclude:: ../../../median.cpp - :lines: 290-305 + :lines: 194-208 :language: cpp .. rubric:: createUserData() @@ -93,6 +93,6 @@ This function may be called many times from different modules. Do not expect you The implementation of the createUserData() method() in MEDIAN: .. literalinclude:: ../../../median.cpp - :lines: 283-288 + :lines: 187-192 :language: cpp From fd34320256e80a8e2135e8953c5d23ba3806be1d Mon Sep 17 00:00:00 2001 From: David Hall Date: Wed, 6 Mar 2019 12:22:35 -0600 Subject: [PATCH 31/38] MCOL-2180 PDF moved up one directory --- utils/udfsdk/docs/build/latex/UDAF.pdf | Bin 228021 -> 0 bytes 1 file changed, 0 insertions(+), 0 deletions(-) delete mode 100644 utils/udfsdk/docs/build/latex/UDAF.pdf diff --git a/utils/udfsdk/docs/build/latex/UDAF.pdf b/utils/udfsdk/docs/build/latex/UDAF.pdf deleted file mode 100644 index eb5011f335c55d94a31f7317a793ac2350b6347e..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 228021 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 z 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 From e9a920b4e87fe4340d3ba65a79cf488d21e4d32f Mon Sep 17 00:00:00 2001 From: David Hall Date: Wed, 6 Mar 2019 13:57:49 -0600 Subject: [PATCH 32/38] MCOL-1951 Don't crash when MariaDB UDAF not defined in Columnstore --- dbcon/mysql/ha_calpont_execplan.cpp | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/dbcon/mysql/ha_calpont_execplan.cpp b/dbcon/mysql/ha_calpont_execplan.cpp index 2de5dfa56..2383f7ff7 100755 --- a/dbcon/mysql/ha_calpont_execplan.cpp +++ b/dbcon/mysql/ha_calpont_execplan.cpp @@ -3984,10 +3984,19 @@ ReturnedColumn* buildAggregateColumn(Item* item, gp_walk_info& gwi) colTypes.push_back(make_pair(udafc->functionParms()->alias(), udafc->functionParms()->resultType().colDataType)); // Call the user supplied init() - if (context.getFunction()->init(&context, colTypes) == mcsv1_UDAF::ERROR) + try + { + if (context.getFunction()->init(&context, colTypes) == mcsv1_UDAF::ERROR) + { + gwi.fatalParseError = true; + gwi.parseErrorText = udafc->getContext().getErrorMessage(); + return NULL; + } + } + catch (std::exception& e) { gwi.fatalParseError = true; - gwi.parseErrorText = udafc->getContext().getErrorMessage(); + gwi.parseErrorText = e.what(); return NULL; } if (udafc->getContext().getRunFlag(UDAF_OVER_REQUIRED)) From c3f8148fff45a05127bf626f03449a742a18b235 Mon Sep 17 00:00:00 2001 From: David Hall Date: Fri, 8 Mar 2019 15:14:42 -0600 Subject: [PATCH 33/38] MCOL-2182 Change in MariaDB 10.3 allows for only 2 parameters to lpad and rpad --- utils/funcexp/func_lpad.cpp | 29 ++++++++++++++++++++++++----- utils/funcexp/func_rpad.cpp | 27 ++++++++++++++++++++++----- utils/funcexp/functor_str.h | 2 ++ 3 files changed, 48 insertions(+), 10 deletions(-) diff --git a/utils/funcexp/func_lpad.cpp b/utils/funcexp/func_lpad.cpp index dd25ee191..b5acfd362 100644 --- a/utils/funcexp/func_lpad.cpp +++ b/utils/funcexp/func_lpad.cpp @@ -20,7 +20,7 @@ * * ****************************************************************************/ - +#include "errorids.h" #include using namespace std; @@ -39,6 +39,9 @@ using namespace joblist; namespace funcexp { +const string Func_lpad::fPad = " "; + + CalpontSystemCatalog::ColType Func_lpad::operationType(FunctionParm& fp, CalpontSystemCatalog::ColType& resultType) { // operation type is not used by this functor @@ -114,17 +117,33 @@ std::string Func_lpad::getStrVal(rowgroup::Row& row, } break; + case execplan::CalpontSystemCatalog::CHAR: + case execplan::CalpontSystemCatalog::VARCHAR: + { + const string& strval = fp[1]->data()->getStrVal(row, isNull); + len = strtol(strval.c_str(), NULL, 10); + break; + } + default: { - len = fp[1]->data()->getIntVal(row, isNull); + std::ostringstream oss; + oss << "lpad parameter 2 must be numeric, not " << execplan::colDataTypeToString(fp[1]->data()->resultType().colDataType); + throw logging::IDBExcept(oss.str(), logging::ERR_DATATYPE_NOT_SUPPORT); + } } if (len < 1) return ""; + // MCOL-2182 As of MariaDB 10.3 the third parameter - pad characters - is optional // The pad characters. - const string& pad = fp[2]->data()->getStrVal(row, isNull); + const string* pad = &fPad; + if (fp.size() > 2) + { + pad = &fp[2]->data()->getStrVal(row, isNull); + } if (isNull) return ""; @@ -172,11 +191,11 @@ std::string Func_lpad::getStrVal(rowgroup::Row& row, // This is the case where there's room to pad. // Convert the pad string to wide - padwclen = pad.length(); // A guess to start. + padwclen = pad->length(); // A guess to start. size_t padbufsize = (padwclen + 1) * sizeof(wchar_t); wchar_t* wcpad = (wchar_t*)alloca(padbufsize); // padwclen+1 is for giving count for the terminating null - size_t padlen = utf8::idb_mbstowcs(wcpad, pad.c_str(), padwclen + 1); + size_t padlen = utf8::idb_mbstowcs(wcpad, pad->c_str(), padwclen + 1); // How many chars do we need? size_t padspace = len - strSize; diff --git a/utils/funcexp/func_rpad.cpp b/utils/funcexp/func_rpad.cpp index 7de0dfb78..7a6b94e0c 100644 --- a/utils/funcexp/func_rpad.cpp +++ b/utils/funcexp/func_rpad.cpp @@ -20,7 +20,7 @@ * * ****************************************************************************/ - +#include "errorids.h" #include using namespace std; @@ -39,6 +39,8 @@ using namespace joblist; namespace funcexp { +const string Func_rpad::fPad = " "; + CalpontSystemCatalog::ColType Func_rpad::operationType(FunctionParm& fp, CalpontSystemCatalog::ColType& resultType) { // operation type is not used by this functor @@ -114,9 +116,19 @@ std::string Func_rpad::getStrVal(rowgroup::Row& row, } break; + case execplan::CalpontSystemCatalog::CHAR: + case execplan::CalpontSystemCatalog::VARCHAR: + { + const string& strval = fp[1]->data()->getStrVal(row, isNull); + len = strtol(strval.c_str(), NULL, 10); + break; + } + default: { - len = fp[1]->data()->getIntVal(row, isNull); + std::ostringstream oss; + oss << "lpad parameter 2 must be numeric, not " << execplan::colDataTypeToString(fp[1]->data()->resultType().colDataType); + throw logging::IDBExcept(oss.str(), logging::ERR_DATATYPE_NOT_SUPPORT); } } @@ -124,7 +136,12 @@ std::string Func_rpad::getStrVal(rowgroup::Row& row, return ""; // The pad characters. - const string& pad = fp[2]->data()->getStrVal(row, isNull); + // MCOL-2182 As of MariaDB 10.3 the third parameter - pad characters - is optional + const string* pad = &fPad; + if (fp.size() > 2) + { + pad = &fp[2]->data()->getStrVal(row, isNull); + } if (isNull) return ""; @@ -172,10 +189,10 @@ std::string Func_rpad::getStrVal(rowgroup::Row& row, // This is the case where there's room to pad. // Convert the pad string to wide - padwclen = pad.length(); // A guess to start. + padwclen = pad->length(); // A guess to start. int padbufsize = (padwclen + 1) * sizeof(wchar_t); wchar_t* wcpad = (wchar_t*)alloca(padbufsize); - size_t padlen = utf8::idb_mbstowcs(wcpad, pad.c_str(), padwclen + 1); + size_t padlen = utf8::idb_mbstowcs(wcpad, pad->c_str(), padwclen + 1); // How many chars do we need? unsigned int padspace = len - strSize; diff --git a/utils/funcexp/functor_str.h b/utils/funcexp/functor_str.h index efd4ae33c..5402cc646 100644 --- a/utils/funcexp/functor_str.h +++ b/utils/funcexp/functor_str.h @@ -325,6 +325,7 @@ public: */ class Func_lpad : public Func_Str { + static const string fPad; public: Func_lpad() : Func_Str("lpad") {} virtual ~Func_lpad() {} @@ -342,6 +343,7 @@ public: */ class Func_rpad : public Func_Str { + static const string fPad; public: Func_rpad() : Func_Str("rpad") {} virtual ~Func_rpad() {} From 9ff348b97f83cb8864187b11f39bcb2c014872bf Mon Sep 17 00:00:00 2001 From: Roman Nozdrin Date: Wed, 13 Mar 2019 13:02:17 +0300 Subject: [PATCH 34/38] MCOL-2233 substring_index() now returns correct value when index value is negative. The problem caused by unsigned type used to store negative index value. --- utils/funcexp/func_substring_index.cpp | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/utils/funcexp/func_substring_index.cpp b/utils/funcexp/func_substring_index.cpp index 3fb3643de..f68679992 100644 --- a/utils/funcexp/func_substring_index.cpp +++ b/utils/funcexp/func_substring_index.cpp @@ -62,7 +62,7 @@ std::string Func_substring_index::getStrVal(rowgroup::Row& row, if (isNull) return ""; - size_t count = fp[2]->data()->getIntVal(row, isNull); + int64_t count = fp[2]->data()->getIntVal(row, isNull); if (isNull) return ""; @@ -70,7 +70,8 @@ std::string Func_substring_index::getStrVal(rowgroup::Row& row, if ( count == 0 ) return ""; - size_t end = strlen(str.c_str()); + // To avoid comparison b/w int64_t and size_t + int64_t end = strlen(str.c_str()) & 0x7fffffffffffffff; if ( count > end ) return str; @@ -84,7 +85,7 @@ std::string Func_substring_index::getStrVal(rowgroup::Row& row, { int pointer = 0; - for ( size_t i = 0 ; i < count ; i ++ ) + for ( int64_t i = 0 ; i < count ; i ++ ) { string::size_type pos = str.find(delim, pointer); @@ -103,7 +104,7 @@ std::string Func_substring_index::getStrVal(rowgroup::Row& row, int pointer = end; int start = 0; - for ( size_t i = 0 ; i < count ; i ++ ) + for ( int64_t i = 0 ; i < count ; i ++ ) { string::size_type pos = str.rfind(delim, pointer); From d4c4a10512697a98d5a845250be1d4844ba21663 Mon Sep 17 00:00:00 2001 From: Roman Nozdrin Date: Sat, 9 Mar 2019 12:06:36 +0300 Subject: [PATCH 35/38] MCOL-1883 This commit fixes RENAME behavior that doesn't work properly with table names that contains '/' sign in the middle. Given the name `some/table/` in to or from. RENAME uses a prefix to the left of the first '/', namely `some` and doesn't consider the trailer `/table/`. --- dbcon/mysql/ha_calpont_ddl.cpp | 45 ++++++++++++++++++---------------- 1 file changed, 24 insertions(+), 21 deletions(-) diff --git a/dbcon/mysql/ha_calpont_ddl.cpp b/dbcon/mysql/ha_calpont_ddl.cpp index 434e47441..20398fe3d 100644 --- a/dbcon/mysql/ha_calpont_ddl.cpp +++ b/dbcon/mysql/ha_calpont_ddl.cpp @@ -2373,33 +2373,36 @@ void decode_table_name(char *buf, const char *path, size_t pathLen) @details Parses the path to extract both database - and table names, e.g path ./test/d$ produces - a pair of strings 'test' and 'd$'. This f() looks for a '/' - token only twice to allow '/' symbol in table names. + and table names. This f() assumes the path format + ./test/d$ + and f() produces a pair of strings 'test' and 'd$'. + This f() looks for a '/' symbols only twice to allow '/' + symbol in table names. The f() supports international + glyphs in db or table names. Called from ha_calpont_ddl.cpp by ha_calpont_impl_rename_table_(). */ -pair parseTableName(const string& tn) +pair parseTableName(const char *path) { - string db; - string tb; - typedef boost::tokenizer > tokenizer; -#ifdef _MSC_VER - boost::char_separator sep("\\"); -#else - boost::char_separator sep("/"); -#endif - tokenizer tokens(tn, sep); - tokenizer::iterator tok_iter = tokens.begin(); - ++tok_iter; - idbassert(tok_iter != tokens.end()); - db = *tok_iter; - ++tok_iter; - idbassert(tok_iter != tokens.end()); - tb = *tok_iter; + const char *db_pnt = NULL, *tbl_pnt = NULL, *path_cursor = path; + string db, tb; + while (*path_cursor != '/') + { + path_cursor++; + } + path_cursor++; + db_pnt = path_cursor; + while (*path_cursor != '/') + { + path_cursor++; + } + path_cursor++; + tbl_pnt = path_cursor; + db.assign(db_pnt, tbl_pnt - 1 - db_pnt); + tb.assign(tbl_pnt, path + strlen(path) - tbl_pnt); + return make_pair(db, tb); } - int ha_calpont_impl_rename_table_(const char* from, const char* to, cal_connection_info& ci) { THD* thd = current_thd; From 5c8208c8bc6c3ebcd94db74c5a5ed76f7b494032 Mon Sep 17 00:00:00 2001 From: root Date: Wed, 13 Mar 2019 12:37:43 +0200 Subject: [PATCH 36/38] MCOL-2232 fixed dummy allerts on debian of jemalloc not installed --- utils/clusterTester/columnstoreClusterTester.sh | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/utils/clusterTester/columnstoreClusterTester.sh b/utils/clusterTester/columnstoreClusterTester.sh index f80f80737..3b48840b0 100755 --- a/utils/clusterTester/columnstoreClusterTester.sh +++ b/utils/clusterTester/columnstoreClusterTester.sh @@ -1010,7 +1010,7 @@ checkPackages() fi fi - declare -a UBUNTU_PKG=("libboost-all-dev" "expect" "libdbi-perl" "perl" "openssl" "file" "libreadline-dev" "rsync" "jemalloc" "libsnappy1V5" "net-tools" "libnuma1") + declare -a UBUNTU_PKG=("libboost-all-dev" "expect" "libdbi-perl" "perl" "openssl" "file" "libreadline-dev" "libjemalloc1" "libsnappy1V5" "net-tools" "libnuma1") declare -a UBUNTU_PKG_NOT=("mariadb-server" "libmariadb18") if [ "$OS" == "ubuntu16" ] || [ "$OS" == "ubuntu18" ]; then @@ -1127,7 +1127,7 @@ checkPackages() fi fi - declare -a DEBIAN_PKG=("libboost-all-dev" "expect" "libdbi-perl" "perl" "openssl" "file" "libreadline-dev" "rsync" "jemalloc" "libsnappy1" "net-tools" "libnuma1") + declare -a DEBIAN_PKG=("libboost-all-dev" "expect" "libdbi-perl" "perl" "openssl" "file" "libreadline-dev" "rsync" "libjemalloc1" "libsnappy1" "net-tools" "libnuma1") declare -a DEBIAN_PKG_NOT=("libmariadb18" "mariadb-server") if [ "$OS" == "debian8" ]; then @@ -1244,7 +1244,7 @@ checkPackages() fi fi - declare -a DEBIAN9_PKG=("libboost-all-dev" "expect" "libdbi-perl" "perl" "openssl" "file" "libreadline5" "rsync" "jemalloc" "libsnappy1V5" "net-tools" "libaio1") + declare -a DEBIAN9_PKG=("libboost-all-dev" "expect" "libdbi-perl" "perl" "openssl" "file" "libreadline5" "rsync" "libjemalloc1" "libsnappy1V5" "net-tools" "libaio1") declare -a DEBIAN9_PKG_NOT=("libmariadb18" "mariadb-server") if [ "$OS" == "debian9" ]; then From 00c0f22758fa0f81ecb5633dd6ab1cfb43fe468a Mon Sep 17 00:00:00 2001 From: root Date: Wed, 13 Mar 2019 13:10:23 +0200 Subject: [PATCH 37/38] MCOL-2232 fixed dummy allerts on debian of jemalloc not installed --- utils/clusterTester/columnstoreClusterTester.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/utils/clusterTester/columnstoreClusterTester.sh b/utils/clusterTester/columnstoreClusterTester.sh index 3b48840b0..de050bb70 100755 --- a/utils/clusterTester/columnstoreClusterTester.sh +++ b/utils/clusterTester/columnstoreClusterTester.sh @@ -1010,7 +1010,7 @@ checkPackages() fi fi - declare -a UBUNTU_PKG=("libboost-all-dev" "expect" "libdbi-perl" "perl" "openssl" "file" "libreadline-dev" "libjemalloc1" "libsnappy1V5" "net-tools" "libnuma1") + declare -a UBUNTU_PKG=("libboost-all-dev" "expect" "libdbi-perl" "perl" "openssl" "file" "libreadline-dev" "rsync" "libjemalloc1" "libsnappy1V5" "net-tools" "libnuma1") declare -a UBUNTU_PKG_NOT=("mariadb-server" "libmariadb18") if [ "$OS" == "ubuntu16" ] || [ "$OS" == "ubuntu18" ]; then From a0b3424603c653d32490a592c4b711edba2caea3 Mon Sep 17 00:00:00 2001 From: Roman Nozdrin Date: Wed, 13 Mar 2019 10:19:43 +0300 Subject: [PATCH 38/38] MCOL-2244 Columnstore execution threads now have names describe the threads operation. This should simplify CPU bottlenecks troubleshooting. --- dbcon/joblist/crossenginestep.h | 2 + dbcon/joblist/diskjoinstep.h | 6 ++ dbcon/joblist/pdictionaryscan.cpp | 4 + dbcon/joblist/subquerystep.h | 2 + dbcon/joblist/tuple-bps.cpp | 92 ++----------------- dbcon/joblist/tupleaggregatestep.h | 5 + dbcon/joblist/tupleannexstep.cpp | 4 + dbcon/joblist/tupleconstantstep.h | 2 + dbcon/joblist/tuplehashjoin.h | 4 + dbcon/joblist/tuplehavingstep.h | 2 + dbcon/joblist/tupleunion.h | 2 + dbcon/joblist/windowfunctionstep.h | 2 + .../primproc/batchprimitiveprocessor.cpp | 3 +- primitives/primproc/bppseeder.cpp | 1 + primitives/primproc/primitiveserver.cpp | 12 +++ utils/common/CMakeLists.txt | 3 +- utils/common/threadnaming.cpp | 26 ++++++ utils/common/threadnaming.h | 24 +++++ 18 files changed, 109 insertions(+), 87 deletions(-) create mode 100644 utils/common/threadnaming.cpp create mode 100644 utils/common/threadnaming.h diff --git a/dbcon/joblist/crossenginestep.h b/dbcon/joblist/crossenginestep.h index 242d00cdf..4ebf2ac9d 100644 --- a/dbcon/joblist/crossenginestep.h +++ b/dbcon/joblist/crossenginestep.h @@ -28,6 +28,7 @@ #include "jobstep.h" #include "primitivestep.h" +#include "threadnaming.h" using namespace std; @@ -192,6 +193,7 @@ protected: Runner(CrossEngineStep* step) : fStep(step) { } void operator()() { + utils::setThreadName("CESRunner"); fStep->execute(); } diff --git a/dbcon/joblist/diskjoinstep.h b/dbcon/joblist/diskjoinstep.h index 154571cdc..fbdda890a 100644 --- a/dbcon/joblist/diskjoinstep.h +++ b/dbcon/joblist/diskjoinstep.h @@ -1,4 +1,5 @@ /* 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 @@ -18,6 +19,7 @@ #include "jobstep.h" #include "tuplehashjoin.h" #include "joinpartition.h" +#include "threadnaming.h" #include "../../utils/threadpool/prioritythreadpool.h" #ifndef DISKJOINSTEP_H @@ -65,6 +67,7 @@ private: Runner(DiskJoinStep* d) : djs(d) { } void operator()() { + utils::setThreadName("DJSMainRunner"); djs->mainRunner(); } DiskJoinStep* djs; @@ -92,6 +95,7 @@ private: Loader(DiskJoinStep* d) : djs(d) { } void operator()() { + utils::setThreadName("DJSLoader"); djs->loadFcn(); } DiskJoinStep* djs; @@ -114,6 +118,7 @@ private: Builder(DiskJoinStep* d) : djs(d) { } void operator()() { + utils::setThreadName("DJSBuilder"); djs->buildFcn(); } DiskJoinStep* djs; @@ -126,6 +131,7 @@ private: Joiner(DiskJoinStep* d) : djs(d) { } void operator()() { + utils::setThreadName("DJSJoiner"); djs->joinFcn(); } DiskJoinStep* djs; diff --git a/dbcon/joblist/pdictionaryscan.cpp b/dbcon/joblist/pdictionaryscan.cpp index 47f469723..6c1b7cecd 100644 --- a/dbcon/joblist/pdictionaryscan.cpp +++ b/dbcon/joblist/pdictionaryscan.cpp @@ -63,6 +63,8 @@ using namespace rowgroup; #include "querytele.h" using namespace querytele; +#include "threadnaming.h" + namespace joblist { @@ -75,6 +77,7 @@ struct pDictionaryScanPrimitive { try { + utils::setThreadName("DSSScan"); fPDictScan->sendPrimitiveMessages(); } catch (runtime_error& re) @@ -99,6 +102,7 @@ struct pDictionaryScanAggregator { try { + utils::setThreadName("DSSAgg"); fPDictScan->receivePrimitiveMessages(); } catch (runtime_error& re) diff --git a/dbcon/joblist/subquerystep.h b/dbcon/joblist/subquerystep.h index 2ce6ec579..d69863df5 100644 --- a/dbcon/joblist/subquerystep.h +++ b/dbcon/joblist/subquerystep.h @@ -31,6 +31,7 @@ #include "jobstep.h" #include "joblist.h" #include "funcexpwrapper.h" +#include "threadnaming.h" namespace joblist { @@ -264,6 +265,7 @@ protected: Runner(SubAdapterStep* step) : fStep(step) { } void operator()() { + utils::setThreadName("SQSRunner"); fStep->execute(); } diff --git a/dbcon/joblist/tuple-bps.cpp b/dbcon/joblist/tuple-bps.cpp index b42533763..de46f85d0 100644 --- a/dbcon/joblist/tuple-bps.cpp +++ b/dbcon/joblist/tuple-bps.cpp @@ -70,6 +70,8 @@ using namespace BRM; #include "rowgroup.h" using namespace rowgroup; +#include "threadnaming.h" + #include "querytele.h" using namespace querytele; @@ -106,6 +108,7 @@ struct TupleBPSPrimitive { try { + utils::setThreadName("BPSPrimitive"); fBatchPrimitiveStep->sendPrimitiveMessages(); } catch (std::exception& re) @@ -135,6 +138,7 @@ struct TupleBPSAggregators { try { + utils::setThreadName("BPSAggregator"); fBatchPrimitiveStepCols->receiveMultiPrimitiveMessages(fThreadId); } catch (std::exception& re) @@ -276,7 +280,6 @@ TupleBPS::TupleBPS(const pColStep& rhs, const JobInfo& jobInfo) : hasPCFilter = hasPMFilter = hasRIDFilter = hasSegmentFilter = hasDBRootFilter = hasSegmentDirFilter = hasPartitionFilter = hasMaxFilter = hasMinFilter = hasLBIDFilter = hasExtentIDFilter = false; -// cout << "TBPSCount = " << ++TBPSCount << endl; } TupleBPS::TupleBPS(const pColScanStep& rhs, const JobInfo& jobInfo) : @@ -336,8 +339,6 @@ TupleBPS::TupleBPS(const pColScanStep& rhs, const JobInfo& jobInfo) : fBPP->setTxnID(fTxnId); fTraceFlags = rhs.fTraceFlags; fBPP->setTraceFlags(fTraceFlags); -// if (fOid>=3000) -// cout << "BPS:initalized from pColScanStep. fSessionId=" << fSessionId << endl; fBPP->setStepID(fStepId); fBPP->setOutputType(ROW_GROUP); fPhysicalIO = 0; @@ -352,9 +353,6 @@ TupleBPS::TupleBPS(const pColScanStep& rhs, const JobInfo& jobInfo) : hasUMJoin = false; fRunExecuted = false; smallOuterJoiner = -1; - // @1098 initialize scanFlags to be true - //scanFlags.assign(numExtents, true); - //runtimeCPFlags.assign(numExtents, true); bop = BOP_AND; runRan = joinRan = false; @@ -405,9 +403,6 @@ TupleBPS::TupleBPS(const PassThruStep& rhs, const JobInfo& jobInfo) : fTraceFlags = rhs.fTraceFlags; fBPP->setTraceFlags(fTraceFlags); fBPP->setOutputType(ROW_GROUP); -// if (fOid>=3000) -// cout << "BPS:initalized from PassThruStep. fSessionId=" << fSessionId << endl; - finishedSending = sendWaiting = false; fSwallowRows = false; fNumBlksSkipped = 0; @@ -437,7 +432,6 @@ TupleBPS::TupleBPS(const PassThruStep& rhs, const JobInfo& jobInfo) : hasPCFilter = hasPMFilter = hasRIDFilter = hasSegmentFilter = hasDBRootFilter = hasSegmentDirFilter = hasPartitionFilter = hasMaxFilter = hasMinFilter = hasLBIDFilter = hasExtentIDFilter = false; -// cout << "TBPSCount = " << ++TBPSCount << endl; } TupleBPS::TupleBPS(const pDictionaryStep& rhs, const JobInfo& jobInfo) : @@ -463,7 +457,6 @@ TupleBPS::TupleBPS(const pDictionaryStep& rhs, const JobInfo& jobInfo) : fStepCount = 1; fCPEvaluated = false; fEstimatedRows = 0; - //fColType = rhs.colType(); alias(rhs.alias()); view(rhs.view()); name(rhs.name()); @@ -472,8 +465,6 @@ TupleBPS::TupleBPS(const pDictionaryStep& rhs, const JobInfo& jobInfo) : fBPP.reset(new BatchPrimitiveProcessorJL(fRm)); initializeConfigParms(); fBPP->setSessionID(fSessionId); -// if (fOid>=3000) -// cout << "BPS:initalized from DictionaryStep. fSessionId=" << fSessionId << endl; fBPP->setStepID(fStepId); fBPP->setQueryContext(fVerId); fBPP->setTxnID(fTxnId); @@ -506,7 +497,6 @@ TupleBPS::TupleBPS(const pDictionaryStep& rhs, const JobInfo& jobInfo) : hasPCFilter = hasPMFilter = hasRIDFilter = hasSegmentFilter = hasDBRootFilter = hasSegmentDirFilter = hasPartitionFilter = hasMaxFilter = hasMinFilter = hasLBIDFilter = hasExtentIDFilter = false; -// cout << "TBPSCount = " << ++TBPSCount << endl; } TupleBPS::~TupleBPS() @@ -541,7 +531,6 @@ TupleBPS::~TupleBPS() fDec->removeQueue(uniqueID); } -// cout << "~TBPSCount = " << --TBPSCount << endl; } void TupleBPS::setBPP(JobStep* jobStep) @@ -558,7 +547,6 @@ void TupleBPS::setBPP(JobStep* jobStep) if (pseudo) { - //cout << "adding a pseudo col filter" << endl; fBPP->addFilterStep(*pseudo); if (pseudo->filterCount() > 0) @@ -690,8 +678,6 @@ void TupleBPS::setProjectBPP(JobStep* jobStep1, JobStep* jobStep2) colWidth = (pcsp->colType()).colWidth; projectOids.push_back(jobStep1->oid()); -// if (fOid>=3000) -// cout << "Adding project step pColStep and pDictionaryStep to BPS" << endl; } else { @@ -708,8 +694,6 @@ void TupleBPS::setProjectBPP(JobStep* jobStep1, JobStep* jobStep2) projectOids.push_back(jobStep1->oid()); colWidth = (psth->colType()).colWidth; -// if (fOid>=3000) -// cout << "Adding project step PassThruStep and pDictionaryStep to BPS" << endl; } } } @@ -723,7 +707,6 @@ void TupleBPS::setProjectBPP(JobStep* jobStep1, JobStep* jobStep2) if (pseudo) { - //cout << "adding a pseudo col projection" << endl; fBPP->addProjectStep(*pseudo); } else @@ -835,7 +818,6 @@ void TupleBPS::storeCasualPartitionInfo(const bool estimateRowCounts) } } - //cout << "cp column number=" << cpColVec.size() << " 1st col extents size= " << scanFlags.size() << endl; if (cpColVec.size() == 0) return; @@ -900,12 +882,8 @@ void TupleBPS::startAggregationThread() fProducerThreads.push_back(jobstepThreadPool.invoke(TupleBPSAggregators(this, fNumThreads - 1))); } -//#include "boost/date_time/posix_time/posix_time.hpp" - void TupleBPS::serializeJoiner() { -// boost::posix_time::ptime start, stop; -// start = boost::posix_time::microsec_clock::local_time(); ByteStream bs; bool more = true; @@ -925,8 +903,6 @@ void TupleBPS::serializeJoiner() bs.restart(); } -// stop = boost::posix_time::microsec_clock::local_time(); -// cout << "serializing took " << stop-start << endl; } void TupleBPS::serializeJoiner(uint32_t conn) @@ -957,8 +933,6 @@ void TupleBPS::prepCasualPartitioning() { if (fOid >= 3000) { - //if (scanFlags[i] && !runtimeCPFlags[i]) - // cout << "runtime flags eliminated an extent!\n"; scanFlags[i] = scanFlags[i] && runtimeCPFlags[i]; if (scanFlags[i] && lbidList->CasualPartitionDataType(fColType.colDataType, @@ -1209,22 +1183,10 @@ void TupleBPS::run() if (fe2) { - //if (fDelivery) { - // fe2Data.reinit(fe2Output); - // fe2Output.setData(&fe2Data); - //} primRowGroup.initRow(&fe2InRow); fe2Output.initRow(&fe2OutRow); } - /* - if (doJoin) { - for (uint32_t z = 0; z < smallSideCount; z++) - cout << "join #" << z << " " << "0x" << hex << tjoiners[z]->getJoinType() - << std::dec << " typeless: " << (uint32_t) tjoiners[z]->isTypelessJoin() << endl; - } - */ - try { fDec->addDECEventListener(this); @@ -1330,7 +1292,6 @@ void TupleBPS::sendError(uint16_t status) } fBPP->reset(); -// msgsSent++; // not expecting a response from this msg finishedSending = true; condvar.notify_all(); condvarWakeupProducer.notify_all(); @@ -1441,7 +1402,6 @@ void TupleBPS::sendJobs(const vector& jobs) for (i = 0; i < jobs.size() && !cancelled(); i++) { - //cout << "sending a job for dbroot " << jobs[i].dbroot << ", PM " << jobs[i].connectionNum << endl; fDec->write(uniqueID, *(jobs[i].msg)); tplLock.lock(); msgsSent += jobs[i].expectedResponses; @@ -1785,20 +1745,17 @@ void TupleBPS::makeJobs(vector* jobs) if (!inBounds) { - //cout << "out of bounds" << endl; continue; } if (!scanFlags[i]) { - //cout << "CP elimination" << endl; fNumBlksSkipped += lbidsToScan; continue; } if (!processPseudoColFilters(i, dbRootPMMap)) { - //cout << "Skipping an extent due to pseudo-column filter elimination" << endl; fNumBlksSkipped += lbidsToScan; continue; } @@ -1840,8 +1797,6 @@ void TupleBPS::makeJobs(vector* jobs) } } -// cout << " session " << fSessionId << " idx = " << i << " HWM = " << scannedExtents[i].HWM -// << " ... will scan " << lbidsToScan << " lbids\n"; // the # of logical blocks in this extent if (lbidsToScan % fColType.colWidth) @@ -1857,22 +1812,17 @@ void TupleBPS::makeJobs(vector* jobs) #else blocksPerJob = max(blocksToScan / fProcessorThreadsPerScan, 16U); #endif - //cout << "blocks to scan = " << blocksToScan << " blocks per job = " << blocksPerJob << - // " HWM == " << scannedExtents[i].HWM << endl; startingLBID = scannedExtents[i].range.start; while (blocksToScan > 0) { uint32_t blocksThisJob = min(blocksToScan, blocksPerJob); - //cout << "starting LBID = " << startingLBID << " count = " << blocksThisJob << - // " dbroot = " << scannedExtents[i].dbRoot << endl; fBPP->setLBID(startingLBID, scannedExtents[i]); fBPP->setCount(blocksThisJob); bs.reset(new ByteStream()); fBPP->runBPP(*bs, (*dbRootConnectionMap)[scannedExtents[i].dbRoot]); - //cout << "making job for connection # " << (*dbRootConnectionMap)[scannedExtents[i].dbRoot] << endl; jobs->push_back(Job(scannedExtents[i].dbRoot, (*dbRootConnectionMap)[scannedExtents[i].dbRoot], blocksThisJob, bs)); blocksToScan -= blocksThisJob; @@ -1881,7 +1831,6 @@ void TupleBPS::makeJobs(vector* jobs) } } -// cout << "session " << fSessionId << " sees " << extentCounter << " extents" << endl; } void TupleBPS::sendPrimitiveMessages() @@ -1901,19 +1850,16 @@ void TupleBPS::sendPrimitiveMessages() } catch (const IDBExcept& e) { - //cout << "Caught IDBExcept" << e.what() << e.errorCode() << endl; sendError(e.errorCode()); processError(e.what(), e.errorCode(), "TupleBPS::sendPrimitiveMessages()"); } catch (const std::exception& ex) { - //cout << "Caught exception" << endl; sendError(ERR_TUPLE_BPS); processError(ex.what(), ERR_TUPLE_BPS, "TupleBPS::sendPrimitiveMessages()"); } catch (...) { - //cout << "Caught ..." << endl; sendError(ERR_TUPLE_BPS); processError("unknown", ERR_TUPLE_BPS, "TupleBPS::sendPrimitiveMessages()"); } @@ -2189,7 +2135,6 @@ void TupleBPS::receiveMultiPrimitiveMessages(uint32_t threadID) tplLock.unlock(); -// cout << "thread " << threadID << " has " << size << " Bytestreams\n"; for (i = 0; i < size && !cancelled(); i++) { ByteStream* bs = bsv[i].get(); @@ -2244,18 +2189,16 @@ void TupleBPS::receiveMultiPrimitiveMessages(uint32_t threadID) local_outputRG.resetRowGroup(local_primRG.getBaseRid()); local_outputRG.setDBRoot(local_primRG.getDBRoot()); local_primRG.getRow(0, &largeSideRow); - //cout << "large-side raw data: " << local_primRG.toString() << endl; - //cout << "jointype = " << tjoiners[0]->getJoinType() << endl; for (k = 0; k < local_primRG.getRowCount() && !cancelled(); k++, largeSideRow.nextRow()) { - //cout << "TBPS: Large side row: " << largeSideRow.toString() << endl; matchCount = 0; for (j = 0; j < smallSideCount; j++) { tjoiners[j]->match(largeSideRow, k, threadID, &joinerOutput[j]); - /* Debugging code to print the matches +#ifdef JLF_DEBUG + // Debugging code to print the matches Row r; joinerMatchesRGs[j].initRow(&r); cout << joinerOutput[j].size() << " matches: \n"; @@ -2263,7 +2206,7 @@ void TupleBPS::receiveMultiPrimitiveMessages(uint32_t threadID) r.setPointer(joinerOutput[j][z]); cout << " " << r.toString() << endl; } - */ +#endif matchCount = joinerOutput[j].size(); if (tjoiners[j]->inUM()) @@ -2271,7 +2214,6 @@ void TupleBPS::receiveMultiPrimitiveMessages(uint32_t threadID) /* Count the # of rows that pass the join filter */ if (tjoiners[j]->hasFEFilter() && matchCount > 0) { - //cout << "doing FE filter" << endl; vector newJoinerOutput; applyMapping(fergMappings[smallSideCount], largeSideRow, &joinFERow); @@ -2311,10 +2253,6 @@ void TupleBPS::receiveMultiPrimitiveMessages(uint32_t threadID) if (tjoiners[j]->antiJoin()) { matchCount = (matchCount ? 0 : 1); - // if (matchCount) - // cout << "in the result\n"; - // else - // cout << "not in the result\n"; } if (matchCount == 0) @@ -2380,7 +2318,6 @@ void TupleBPS::receiveMultiPrimitiveMessages(uint32_t threadID) } else { -// cout << "TBPS: sending unjoined data\n"; rgDatav.push_back(rgData); } @@ -2488,7 +2425,6 @@ out: { smallSideRows[i].setPointer(unmatched[j]); -// cout << "small side Row: " << smallSideRows[i].toString() << endl; for (k = 0; k < smallSideCount; k++) { if (i == k) @@ -2499,8 +2435,6 @@ out: applyMapping(largeMapping, largeNull, &joinedBaseRow); joinedBaseRow.setRid(0); -// cout << "outer row is " << joinedBaseRow.toString() << endl; -// joinedBaseRow.setRid(largeSideRow.getRelRid()); joinedBaseRow.nextRow(); local_outputRG.incRowCount(); @@ -2785,8 +2719,6 @@ inline bool TupleBPS::scanit(uint64_t rid) fbo = rid >> rpbShift; extentIndex = fbo >> divShift; - //if (scanFlags[extentIndex] && !runtimeCPFlags[extentIndex]) - // cout << "HJ feedback eliminated an extent!\n"; return scanFlags[extentIndex] && runtimeCPFlags[extentIndex]; } @@ -2908,7 +2840,6 @@ void TupleBPS::generateJoinResultSet(const vector >& joiner { smallRow.setPointer(joinerOutput[depth][i]); applyMapping(mappings[depth], smallRow, &baseRow); -// cout << "depth " << depth << ", size " << joinerOutput[depth].size() << ", row " << i << ": " << smallRow.toString() << endl; generateJoinResultSet(joinerOutput, baseRow, mappings, depth + 1, outputRG, rgData, outputData, smallRows, joinedRow); } @@ -2926,7 +2857,6 @@ void TupleBPS::generateJoinResultSet(const vector >& joiner { uint32_t dbRoot = outputRG.getDBRoot(); uint64_t baseRid = outputRG.getBaseRid(); -// cout << "GJRS adding data\n"; outputData->push_back(rgData); rgData = RGData(outputRG); outputRG.setData(&rgData); @@ -2935,11 +2865,8 @@ void TupleBPS::generateJoinResultSet(const vector >& joiner outputRG.getRow(0, &joinedRow); } -// cout << "depth " << depth << ", size " << joinerOutput[depth].size() << ", row " << i << ": " << smallRow.toString() << endl; applyMapping(mappings[depth], smallRow, &baseRow); copyRow(baseRow, &joinedRow); - //memcpy(joinedRow.getData(), baseRow.getData(), joinedRow.getSize()); - //cout << "(step " << fStepId << ") fully joined row is: " << joinedRow.toString() << endl; } } } @@ -3104,7 +3031,6 @@ void TupleBPS::processFE2_oneRG(RowGroup& input, RowGroup& output, Row& inRow, if (ret) { applyMapping(fe2Mapping, inRow, &outRow); - //cout << "fe2 passed row: " << outRow.toString() << endl; outRow.setRid(inRow.getRelRid()); output.incRowCount(); outRow.nextRow(); @@ -3153,7 +3079,6 @@ void TupleBPS::processFE2(RowGroup& input, RowGroup& output, Row& inRow, Row& ou output.getBaseRid() != input.getBaseRid() ) { -// cout << "FE2 produced a full RG\n"; results.push_back(result); result = RGData(output); output.setData(&result); @@ -3167,12 +3092,9 @@ void TupleBPS::processFE2(RowGroup& input, RowGroup& output, Row& inRow, Row& ou if (output.getRowCount() > 0) { -// cout << "FE2 produced " << output.getRowCount() << " rows\n"; results.push_back(result); } -// else -// cout << "no rows from FE2\n"; rgData->swap(results); } diff --git a/dbcon/joblist/tupleaggregatestep.h b/dbcon/joblist/tupleaggregatestep.h index 13a12a8c8..ae356e156 100644 --- a/dbcon/joblist/tupleaggregatestep.h +++ b/dbcon/joblist/tupleaggregatestep.h @@ -1,4 +1,5 @@ /* 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 @@ -23,6 +24,7 @@ #include "jobstep.h" #include "rowaggregation.h" +#include "threadnaming.h" #include @@ -138,6 +140,7 @@ private: Aggregator(TupleAggregateStep* step) : fStep(step) { } void operator()() { + utils::setThreadName("TASAggr"); fStep->doAggregate(); } @@ -153,6 +156,7 @@ private: {} void operator()() { + utils::setThreadName("TASThrAggr"); fStep->threadedAggregateRowGroups(fThreadID); } @@ -171,6 +175,7 @@ private: } void operator()() { + utils::setThreadName("TASThr2ndPAggr"); for (uint32_t i = 0; i < bucketCount; i++) fStep->doThreadedSecondPhaseAggregate(fThreadID + i); } diff --git a/dbcon/joblist/tupleannexstep.cpp b/dbcon/joblist/tupleannexstep.cpp index 77a2670d9..c1ef06a4e 100644 --- a/dbcon/joblist/tupleannexstep.cpp +++ b/dbcon/joblist/tupleannexstep.cpp @@ -51,6 +51,7 @@ using namespace rowgroup; #include "hasher.h" #include "stlpoolallocator.h" +#include "threadnaming.h" using namespace utils; #include "querytele.h" @@ -314,6 +315,7 @@ void TupleAnnexStep::execute() void TupleAnnexStep::executeNoOrderBy() { + utils::setThreadName("TASwoOrd"); RGData rgDataIn; RGData rgDataOut; bool more = false; @@ -399,6 +401,7 @@ void TupleAnnexStep::executeNoOrderBy() void TupleAnnexStep::executeNoOrderByWithDistinct() { + utils::setThreadName("TASwoOrdDist"); scoped_ptr distinctMap(new DistinctMap_t(10, TAHasher(this), TAEq(this))); vector dataVec; RGData rgDataIn; @@ -500,6 +503,7 @@ void TupleAnnexStep::executeNoOrderByWithDistinct() void TupleAnnexStep::executeWithOrderBy() { + utils::setThreadName("TASwOrd"); RGData rgDataIn; RGData rgDataOut; bool more = false; diff --git a/dbcon/joblist/tupleconstantstep.h b/dbcon/joblist/tupleconstantstep.h index 6187f04a6..c98227805 100644 --- a/dbcon/joblist/tupleconstantstep.h +++ b/dbcon/joblist/tupleconstantstep.h @@ -22,6 +22,7 @@ #define JOBLIST_TUPLECONSTANTSTEP_H #include "jobstep.h" +#include "threadnaming.h" namespace joblist { @@ -98,6 +99,7 @@ protected: Runner(TupleConstantStep* step) : fStep(step) { } void operator()() { + utils::setThreadName("TCSRunner"); fStep->execute(); } diff --git a/dbcon/joblist/tuplehashjoin.h b/dbcon/joblist/tuplehashjoin.h index d89e76d2f..27154946f 100644 --- a/dbcon/joblist/tuplehashjoin.h +++ b/dbcon/joblist/tuplehashjoin.h @@ -1,4 +1,5 @@ /* 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 @@ -25,6 +26,7 @@ #include "calpontsystemcatalog.h" #include "hasher.h" #include "tuplejoiner.h" +#include "threadnaming.h" #include #include #include @@ -450,6 +452,7 @@ private: HJRunner(TupleHashJoinStep* hj) : HJ(hj) { } void operator()() { + utils::setThreadName("HJSBigSide"); HJ->hjRunner(); } TupleHashJoinStep* HJ; @@ -459,6 +462,7 @@ private: SmallRunner(TupleHashJoinStep* hj, uint32_t i) : HJ(hj), index(i) { } void operator()() { + utils::setThreadName("HJSSmallSide"); HJ->smallRunnerFcn(index); } TupleHashJoinStep* HJ; diff --git a/dbcon/joblist/tuplehavingstep.h b/dbcon/joblist/tuplehavingstep.h index 2624a7e4a..a7fc679ab 100644 --- a/dbcon/joblist/tuplehavingstep.h +++ b/dbcon/joblist/tuplehavingstep.h @@ -23,6 +23,7 @@ #include "jobstep.h" #include "expressionstep.h" +#include "threadnaming.h" // forward reference namespace fucexp @@ -97,6 +98,7 @@ protected: Runner(TupleHavingStep* step) : fStep(step) { } void operator()() { + utils::setThreadName("HVSRunner"); fStep->execute(); } diff --git a/dbcon/joblist/tupleunion.h b/dbcon/joblist/tupleunion.h index 2c54337d3..b6a445da9 100644 --- a/dbcon/joblist/tupleunion.h +++ b/dbcon/joblist/tupleunion.h @@ -35,6 +35,7 @@ #endif #include "stlpoolallocator.h" +#include "threadnaming.h" #ifndef TUPLEUNION2_H_ #define TUPLEUNION2_H_ @@ -155,6 +156,7 @@ private: Runner(TupleUnion* t, uint32_t in) : tu(t), index(in) { } void operator()() { + utils::setThreadName("TUSRunner"); tu->readInput(index); } }; diff --git a/dbcon/joblist/windowfunctionstep.h b/dbcon/joblist/windowfunctionstep.h index 302629c4c..f483c6027 100644 --- a/dbcon/joblist/windowfunctionstep.h +++ b/dbcon/joblist/windowfunctionstep.h @@ -25,6 +25,7 @@ #include "jobstep.h" #include "rowgroup.h" #include "windowfunctioncolumn.h" +#include "threadnaming.h" namespace execplan { @@ -153,6 +154,7 @@ private: Runner(WindowFunctionStep* step) : fStep(step) { } void operator()() { + utils::setThreadName("WFSRunner"); fStep->execute(); } diff --git a/primitives/primproc/batchprimitiveprocessor.cpp b/primitives/primproc/batchprimitiveprocessor.cpp index 019761d39..752c94d9e 100644 --- a/primitives/primproc/batchprimitiveprocessor.cpp +++ b/primitives/primproc/batchprimitiveprocessor.cpp @@ -52,6 +52,7 @@ using namespace boost; #include "fixedallocator.h" #include "blockcacheclient.h" #include "MonitorProcMem.h" +#include "threadnaming.h" #define MAX64 0x7fffffffffffffffLL #define MIN64 0x8000000000000000LL @@ -156,7 +157,6 @@ BatchPrimitiveProcessor::BatchPrimitiveProcessor(ByteStream& b, double prefetch, sendThread = bppst; pthread_mutex_init(&objLock, NULL); initBPP(b); -// cerr << "made a BPP\n"; } #if 0 @@ -1961,6 +1961,7 @@ void BatchPrimitiveProcessor::makeResponse() int BatchPrimitiveProcessor::operator()() { + utils::setThreadName("PPBatchPrimProc"); if (currentBlockOffset == 0) { #ifdef PRIMPROC_STOPWATCH // TODO: needs to be brought up-to-date diff --git a/primitives/primproc/bppseeder.cpp b/primitives/primproc/bppseeder.cpp index 1755135a3..4b94d0c5c 100644 --- a/primitives/primproc/bppseeder.cpp +++ b/primitives/primproc/bppseeder.cpp @@ -142,6 +142,7 @@ int BPPSeeder::operator()() pthread_t tid = 0; boost::mutex::scoped_lock scoped(bppLock, boost::defer_lock_t()); + try { if (firstRun) diff --git a/primitives/primproc/primitiveserver.cpp b/primitives/primproc/primitiveserver.cpp index 227b494de..76ff8ef3c 100644 --- a/primitives/primproc/primitiveserver.cpp +++ b/primitives/primproc/primitiveserver.cpp @@ -91,6 +91,8 @@ using namespace idbdatafile; using namespace threadpool; +#include "threadnaming.h" + #include "atomicops.h" #ifndef O_BINARY @@ -925,6 +927,7 @@ struct AsynchLoader void operator()() { + utils::setThreadName("PPAsyncLoader"); bool cached = false; uint32_t rCount = 0; char buf[BLOCK_SIZE]; @@ -1159,6 +1162,7 @@ void DictScanJob::write(const ByteStream& bs) int DictScanJob::operator()() { + utils::setThreadName("PPDictScanJob"); uint8_t data[DATA_BLOCK_SIZE]; uint32_t output_buf_size = MAX_BUFFER_SIZE; uint32_t session; @@ -1338,6 +1342,7 @@ struct BPPHandler LastJoiner(boost::shared_ptr r, SBS b) : BPPHandlerFunctor(r, b) { } int operator()() { + utils::setThreadName("PPHandLastJoiner"); return rt->lastJoinerMsg(*bs, dieTime); } }; @@ -1347,6 +1352,7 @@ struct BPPHandler Create(boost::shared_ptr r, SBS b) : BPPHandlerFunctor(r, b) { } int operator()() { + utils::setThreadName("PPHandCreate"); rt->createBPP(*bs); return 0; } @@ -1357,6 +1363,7 @@ struct BPPHandler Destroy(boost::shared_ptr r, SBS b) : BPPHandlerFunctor(r, b) { } int operator()() { + utils::setThreadName("PPHandDestroy"); return rt->destroyBPP(*bs, dieTime); } }; @@ -1366,6 +1373,7 @@ struct BPPHandler AddJoiner(boost::shared_ptr r, SBS b) : BPPHandlerFunctor(r, b) { } int operator()() { + utils::setThreadName("PPHandAddJoiner"); return rt->addJoinerToBPP(*bs, dieTime); } }; @@ -1375,6 +1383,7 @@ struct BPPHandler Abort(boost::shared_ptr r, SBS b) : BPPHandlerFunctor(r, b) { } int operator()() { + utils::setThreadName("PPHandAbort"); return rt->doAbort(*bs, dieTime); } }; @@ -1751,6 +1760,7 @@ public: virtual int execute() = 0; int operator()() { + utils::setThreadName("PPDictOp"); int ret; ret = execute(); @@ -1967,6 +1977,7 @@ struct ReadThread void operator()() { + utils::setThreadName("PPReadThread"); boost::shared_ptr procPoolPtr = fPrimitiveServerPtr->getProcessorThreadPool(); SBS bs; @@ -2376,6 +2387,7 @@ struct ServerThread void operator()() { + utils::setThreadName("PPServerThr"); IOSocket ios; try diff --git a/utils/common/CMakeLists.txt b/utils/common/CMakeLists.txt index 54900167e..eff8be12f 100644 --- a/utils/common/CMakeLists.txt +++ b/utils/common/CMakeLists.txt @@ -8,7 +8,8 @@ set(common_LIB_SRCS poolallocator.cpp cgroupconfigurator.cpp MonitorProcMem.cpp - nullvaluemanip.cpp) + nullvaluemanip.cpp + threadnaming.cpp) add_library(common SHARED ${common_LIB_SRCS}) diff --git a/utils/common/threadnaming.cpp b/utils/common/threadnaming.cpp new file mode 100644 index 000000000..2f4dda91f --- /dev/null +++ b/utils/common/threadnaming.cpp @@ -0,0 +1,26 @@ +/* Copyright (C) 2019 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. */ + +#include + +namespace utils +{ + void setThreadName(const char *threadName) + { + prctl(PR_SET_NAME, threadName, 0, 0, 0); + } +} // end of namespace diff --git a/utils/common/threadnaming.h b/utils/common/threadnaming.h new file mode 100644 index 000000000..1682b7045 --- /dev/null +++ b/utils/common/threadnaming.h @@ -0,0 +1,24 @@ +/* Copyright (C) 2019 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. */ +#ifndef H_SETTHREADNAME +#define H_SETTHREADNAME + +namespace utils +{ + void setThreadName(const char *threadName); +} // end of namespace +#endif