IMPALA-6537: Add missing ODBC scalar functions This patch contains the following builtin function changes:
New aliases for existing functions: - LEFT() same as STRLEFT() - RIGHT() same as STRRIGHT() - WEEK() same as WEEKOFYEAR() New functions: - QUARTER() - MONTHNAME() Refactors: - Remove TimestampFunctions::DayName and add LongDayName to match pattern of TimestampFunctions::ShortDayName Additionally, it adds the unit of QUARTER to EXTRACT() and DATE_PART() Testing: - manual testing comparing the translated ODBC functions to the non-translated ones - added at least one new expr-test for aliases - new expr-tests added for new functions Change-Id: Ia60af2b4de8c098be7ecb3e60840e459ae10d499 Reviewed-on: http://gerrit.cloudera.org:8080/9376 Reviewed-by: Alex Behm <alex.b...@cloudera.com> Tested-by: Impala Public Jenkins Project: http://git-wip-us.apache.org/repos/asf/impala/repo Commit: http://git-wip-us.apache.org/repos/asf/impala/commit/d91df9b6 Tree: http://git-wip-us.apache.org/repos/asf/impala/tree/d91df9b6 Diff: http://git-wip-us.apache.org/repos/asf/impala/diff/d91df9b6 Branch: refs/heads/master Commit: d91df9b63fa1fc8a907fc28e5db53b8eb275e8f3 Parents: 152cf8b Author: Greg Rahn <gregr...@users.noreply.github.com> Authored: Tue Feb 20 18:31:57 2018 -0800 Committer: Impala Public Jenkins <impala-public-jenk...@gerrit.cloudera.org> Committed: Fri Feb 23 07:19:07 2018 +0000 ---------------------------------------------------------------------- be/src/exprs/expr-test.cc | 42 +++++++++++++++++++ be/src/exprs/timestamp-functions-ir.cc | 43 ++++++++++++-------- be/src/exprs/timestamp-functions.cc | 14 +++---- be/src/exprs/timestamp-functions.h | 21 ++++------ be/src/exprs/udf-builtins-ir.cc | 6 +++ common/function-registry/impala_functions.py | 11 ++--- common/thrift/Exprs.thrift | 1 + fe/src/main/cup/sql-parser.cup | 6 ++- .../impala/analysis/AnalyzeExprsTest.java | 2 +- .../org/apache/impala/analysis/ParserTest.java | 17 ++++---- 10 files changed, 109 insertions(+), 54 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/impala/blob/d91df9b6/be/src/exprs/expr-test.cc ---------------------------------------------------------------------- diff --git a/be/src/exprs/expr-test.cc b/be/src/exprs/expr-test.cc index a78fcae..71f91c8 100644 --- a/be/src/exprs/expr-test.cc +++ b/be/src/exprs/expr-test.cc @@ -3759,6 +3759,7 @@ TEST_F(ExprTest, StringFunctions) { TestIsNull("strleft(NULL, 3)", TYPE_STRING); TestIsNull("strleft('abcdefg', NULL)", TYPE_STRING); TestIsNull("strleft(NULL, NULL)", TYPE_STRING); + TestStringValue("left('foobar', 3)", "foo"); TestStringValue("strright('abcdefg', 0)", ""); TestStringValue("strright('abcdefg', 3)", "efg"); TestStringValue("strright('abcdefg', cast(10 as bigint))", "abcdefg"); @@ -3767,6 +3768,7 @@ TEST_F(ExprTest, StringFunctions) { TestIsNull("strright(NULL, 3)", TYPE_STRING); TestIsNull("strright('abcdefg', NULL)", TYPE_STRING); TestIsNull("strright(NULL, NULL)", TYPE_STRING); + TestStringValue("right('foobar', 3)", "bar"); TestStringValue("translate('', '', '')", ""); TestStringValue("translate('abcd', '', '')", "abcd"); @@ -5933,11 +5935,13 @@ TEST_F(ExprTest, TimestampFunctions) { TestValue("cast('2011-12-22 09:10:11.000000' as timestamp) = \ cast('2011-12-22 09:10:11' as timestamp)", TYPE_BOOLEAN, true); TestValue("year(cast('2011-12-22 09:10:11.000000' as timestamp))", TYPE_INT, 2011); + TestValue("quarter(cast('2011-12-22 09:10:11.000000' as timestamp))", TYPE_INT, 4); TestValue("month(cast('2011-12-22 09:10:11.000000' as timestamp))", TYPE_INT, 12); TestValue("dayofmonth(cast('2011-12-22 09:10:11.000000' as timestamp))", TYPE_INT, 22); TestValue("day(cast('2011-12-22 09:10:11.000000' as timestamp))", TYPE_INT, 22); TestValue("dayofyear(cast('2011-12-22 09:10:11.000000' as timestamp))", TYPE_INT, 356); TestValue("weekofyear(cast('2011-12-22 09:10:11.000000' as timestamp))", TYPE_INT, 51); + TestValue("week(cast('2011-12-22 09:10:11.000000' as timestamp))", TYPE_INT, 51); TestValue("dayofweek(cast('2011-12-18 09:10:11.000000' as timestamp))", TYPE_INT, 1); TestValue("dayofweek(cast('2011-12-22 09:10:11.000000' as timestamp))", TYPE_INT, 5); TestValue("dayofweek(cast('2011-12-24 09:10:11.000000' as timestamp))", TYPE_INT, 7); @@ -5949,11 +5953,13 @@ TEST_F(ExprTest, TimestampFunctions) { TestValue("millisecond(cast('2011-12-22 09:10:11' as timestamp))", TYPE_INT, 0); TestValue("millisecond(cast('2011-12-22' as timestamp))", TYPE_INT, 0); TestValue("year(cast('2011-12-22' as timestamp))", TYPE_INT, 2011); + TestValue("quarter(cast('2011-12-22' as timestamp))", TYPE_INT, 4); TestValue("month(cast('2011-12-22' as timestamp))", TYPE_INT, 12); TestValue("dayofmonth(cast('2011-12-22' as timestamp))", TYPE_INT, 22); TestValue("day(cast('2011-12-22' as timestamp))", TYPE_INT, 22); TestValue("dayofyear(cast('2011-12-22' as timestamp))", TYPE_INT, 356); TestValue("weekofyear(cast('2011-12-22' as timestamp))", TYPE_INT, 51); + TestValue("week(cast('2011-12-22' as timestamp))", TYPE_INT, 51); TestValue("dayofweek(cast('2011-12-18' as timestamp))", TYPE_INT, 1); TestValue("dayofweek(cast('2011-12-22' as timestamp))", TYPE_INT, 5); TestValue("dayofweek(cast('2011-12-24' as timestamp))", TYPE_INT, 7); @@ -6011,22 +6017,26 @@ TEST_F(ExprTest, TimestampFunctions) { TestIsNull("cast('2000-12-31 24:59:59' as timestamp)", TYPE_TIMESTAMP); TestIsNull("year(cast('09:10:11.000000' as timestamp))", TYPE_INT); + TestIsNull("quarter(cast('09:10:11.000000' as timestamp))", TYPE_INT); TestIsNull("month(cast('09:10:11.000000' as timestamp))", TYPE_INT); TestIsNull("dayofmonth(cast('09:10:11.000000' as timestamp))", TYPE_INT); TestIsNull("day(cast('09:10:11.000000' as timestamp))", TYPE_INT); TestIsNull("dayofyear(cast('09:10:11.000000' as timestamp))", TYPE_INT); TestIsNull("dayofweek(cast('09:10:11.000000' as timestamp))", TYPE_INT); TestIsNull("weekofyear(cast('09:10:11.000000' as timestamp))", TYPE_INT); + TestIsNull("week(cast('09:10:11.000000' as timestamp))", TYPE_INT); TestIsNull("datediff(cast('09:10:11.12345678' as timestamp), " "cast('2012-12-22' as timestamp))", TYPE_INT); TestIsNull("year(NULL)", TYPE_INT); + TestIsNull("quarter(NULL)", TYPE_INT); TestIsNull("month(NULL)", TYPE_INT); TestIsNull("dayofmonth(NULL)", TYPE_INT); TestIsNull("day(NULL)", TYPE_INT); TestIsNull("dayofweek(NULL)", TYPE_INT); TestIsNull("dayofyear(NULL)", TYPE_INT); TestIsNull("weekofyear(NULL)", TYPE_INT); + TestIsNull("week(NULL)", TYPE_INT); TestIsNull("datediff(NULL, cast('2011-12-22 09:10:11.12345678' as timestamp))", TYPE_INT); TestIsNull("datediff(cast('2012-12-22' as timestamp), NULL)", TYPE_INT); @@ -6046,6 +6056,34 @@ TEST_F(ExprTest, TimestampFunctions) { TestStringValue("dayname(cast('2011-12-25 09:10:11.000000' as timestamp))", "Sunday"); TestIsNull("dayname(NULL)", TYPE_STRING); + TestStringValue("monthname(cast('2011-01-18 09:10:11.000000' as timestamp))", "January"); + TestStringValue("monthname(cast('2011-02-18 09:10:11.000000' as timestamp))", "February"); + TestStringValue("monthname(cast('2011-03-18 09:10:11.000000' as timestamp))", "March"); + TestStringValue("monthname(cast('2011-04-18 09:10:11.000000' as timestamp))", "April"); + TestStringValue("monthname(cast('2011-05-18 09:10:11.000000' as timestamp))", "May"); + TestStringValue("monthname(cast('2011-06-18 09:10:11.000000' as timestamp))", "June"); + TestStringValue("monthname(cast('2011-07-18 09:10:11.000000' as timestamp))", "July"); + TestStringValue("monthname(cast('2011-08-18 09:10:11.000000' as timestamp))", "August"); + TestStringValue("monthname(cast('2011-09-18 09:10:11.000000' as timestamp))", "September"); + TestStringValue("monthname(cast('2011-10-18 09:10:11.000000' as timestamp))", "October"); + TestStringValue("monthname(cast('2011-11-18 09:10:11.000000' as timestamp))", "November"); + TestStringValue("monthname(cast('2011-12-18 09:10:11.000000' as timestamp))", "December"); + TestIsNull("monthname(NULL)", TYPE_STRING); + + TestValue("quarter(cast('2011-01-18 09:10:11.000000' as timestamp))", TYPE_INT, 1); + TestValue("quarter(cast('2011-02-18 09:10:11.000000' as timestamp))", TYPE_INT, 1); + TestValue("quarter(cast('2011-03-18 09:10:11.000000' as timestamp))", TYPE_INT, 1); + TestValue("quarter(cast('2011-04-18 09:10:11.000000' as timestamp))", TYPE_INT, 2); + TestValue("quarter(cast('2011-05-18 09:10:11.000000' as timestamp))", TYPE_INT, 2); + TestValue("quarter(cast('2011-06-18 09:10:11.000000' as timestamp))", TYPE_INT, 2); + TestValue("quarter(cast('2011-07-18 09:10:11.000000' as timestamp))", TYPE_INT, 3); + TestValue("quarter(cast('2011-08-18 09:10:11.000000' as timestamp))", TYPE_INT, 3); + TestValue("quarter(cast('2011-09-18 09:10:11.000000' as timestamp))", TYPE_INT, 3); + TestValue("quarter(cast('2011-10-18 09:10:11.000000' as timestamp))", TYPE_INT, 4); + TestValue("quarter(cast('2011-11-18 09:10:11.000000' as timestamp))", TYPE_INT, 4); + TestValue("quarter(cast('2011-12-18 09:10:11.000000' as timestamp))", TYPE_INT, 4); + TestIsNull("quarter(NULL)", TYPE_INT); + // Tests from Hive // The hive documentation states that timestamps are timezoneless, but the tests // show that they treat them as being in the current timezone so these tests @@ -6526,6 +6564,8 @@ TEST_F(ExprTest, TimestampFunctions) { // Extract using FROM keyword TestValue("extract(YEAR from cast('2006-05-12 18:27:28.12345' as timestamp))", TYPE_INT, 2006); + TestValue("extract(QUARTER from cast('2006-05-12 18:27:28.12345' as timestamp))", + TYPE_INT, 2); TestValue("extract(MoNTH from cast('2006-05-12 18:27:28.12345' as timestamp))", TYPE_INT, 5); TestValue("extract(DaY from cast('2006-05-12 18:27:28.12345' as timestamp))", @@ -6547,6 +6587,8 @@ TEST_F(ExprTest, TimestampFunctions) { // Date_part, same as extract function but with arguments swapped TestValue("date_part('YEAR', cast('2006-05-12 18:27:28.12345' as timestamp))", TYPE_INT, 2006); + TestValue("date_part('QUARTER', cast('2006-05-12 18:27:28.12345' as timestamp))", + TYPE_INT, 2); TestValue("date_part('MoNTH', cast('2006-05-12 18:27:28.12345' as timestamp))", TYPE_INT, 5); TestValue("date_part('DaY', cast('2006-05-12 18:27:28.12345' as timestamp))", http://git-wip-us.apache.org/repos/asf/impala/blob/d91df9b6/be/src/exprs/timestamp-functions-ir.cc ---------------------------------------------------------------------- diff --git a/be/src/exprs/timestamp-functions-ir.cc b/be/src/exprs/timestamp-functions-ir.cc index 9104bcf..f2d741e 100644 --- a/be/src/exprs/timestamp-functions-ir.cc +++ b/be/src/exprs/timestamp-functions-ir.cc @@ -204,21 +204,6 @@ void TimestampFunctions::ReportBadFormat(FunctionContext* context, } } -StringVal TimestampFunctions::DayName(FunctionContext* context, const TimestampVal& ts) { - if (ts.is_null) return StringVal::null(); - IntVal dow = DayOfWeek(context, ts); - switch(dow.val) { - case 1: return StringVal(SUNDAY); - case 2: return StringVal(MONDAY); - case 3: return StringVal(TUESDAY); - case 4: return StringVal(WEDNESDAY); - case 5: return StringVal(THURSDAY); - case 6: return StringVal(FRIDAY); - case 7: return StringVal(SATURDAY); - default: return StringVal::null(); - } -} - IntVal TimestampFunctions::Year(FunctionContext* context, const TimestampVal& ts_val) { if (ts_val.is_null) return IntVal::null(); const TimestampValue& ts_value = TimestampValue::FromTimestampVal(ts_val); @@ -226,7 +211,6 @@ IntVal TimestampFunctions::Year(FunctionContext* context, const TimestampVal& ts return IntVal(ts_value.date().year()); } - IntVal TimestampFunctions::Month(FunctionContext* context, const TimestampVal& ts_val) { if (ts_val.is_null) return IntVal::null(); const TimestampValue& ts_value = TimestampValue::FromTimestampVal(ts_val); @@ -234,6 +218,13 @@ IntVal TimestampFunctions::Month(FunctionContext* context, const TimestampVal& t return IntVal(ts_value.date().month()); } +IntVal TimestampFunctions::Quarter(FunctionContext* context, const TimestampVal& ts_val) { + if (ts_val.is_null) return IntVal::null(); + const TimestampValue& ts_value = TimestampValue::FromTimestampVal(ts_val); + if (!ts_value.HasDate()) return IntVal::null(); + int m = ts_value.date().month(); + return IntVal((m - 1) / 3 + 1); +} IntVal TimestampFunctions::DayOfWeek(FunctionContext* context, const TimestampVal& ts_val) { @@ -473,6 +464,16 @@ string TimestampFunctions::ShortDayName(FunctionContext* context, return DAY_ARRAY[dow.val - 1]; } +StringVal TimestampFunctions::LongDayName(FunctionContext* context, + const TimestampVal& ts) { + if (ts.is_null) return StringVal::null(); + IntVal dow = DayOfWeek(context, ts); + DCHECK_GT(dow.val, 0); + DCHECK_LT(dow.val, 8); + const string& day_name = DAYNAME_ARRAY[dow.val - 1]; + return StringVal(reinterpret_cast<uint8_t*>(const_cast<char*>(day_name.data())), day_name.size()); +} + string TimestampFunctions::ShortMonthName(FunctionContext* context, const TimestampVal& ts) { if (ts.is_null) return NULL; @@ -482,6 +483,16 @@ string TimestampFunctions::ShortMonthName(FunctionContext* context, return MONTH_ARRAY[mth.val - 1]; } +StringVal TimestampFunctions::LongMonthName(FunctionContext* context, + const TimestampVal& ts) { + if (ts.is_null) return StringVal::null(); + IntVal mth = Month(context, ts); + DCHECK_GT(mth.val, 0); + DCHECK_LT(mth.val, 13); + const string& mn = MONTHNAME_ARRAY[mth.val - 1]; + return StringVal(reinterpret_cast<uint8_t*>(const_cast<char*>(mn.data())), mn.size()); +} + StringVal TimestampFunctions::TimeOfDay(FunctionContext* context) { const TimestampVal curr = Now(context); if (curr.is_null) return StringVal::null(); http://git-wip-us.apache.org/repos/asf/impala/blob/d91df9b6/be/src/exprs/timestamp-functions.cc ---------------------------------------------------------------------- diff --git a/be/src/exprs/timestamp-functions.cc b/be/src/exprs/timestamp-functions.cc index d9372fb..76edd9a 100644 --- a/be/src/exprs/timestamp-functions.cc +++ b/be/src/exprs/timestamp-functions.cc @@ -42,19 +42,15 @@ typedef boost::gregorian::date Date; namespace impala { -// Constant strings used for DayName function. -const char* TimestampFunctions::SUNDAY = "Sunday"; -const char* TimestampFunctions::MONDAY = "Monday"; -const char* TimestampFunctions::TUESDAY = "Tuesday"; -const char* TimestampFunctions::WEDNESDAY = "Wednesday"; -const char* TimestampFunctions::THURSDAY = "Thursday"; -const char* TimestampFunctions::FRIDAY = "Friday"; -const char* TimestampFunctions::SATURDAY = "Saturday"; - const string TimestampFunctions::DAY_ARRAY[7] = {"Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"}; +const string TimestampFunctions::DAYNAME_ARRAY[7] = {"Sunday", "Monday", "Tuesday", + "Wednesday", "Thursday", "Friday", "Saturday"}; const string TimestampFunctions::MONTH_ARRAY[12] = {"Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"}; +const string TimestampFunctions::MONTHNAME_ARRAY[12] = {"January", "February", "March", + "April", "May", "June", "July", "August", "September", "October", "November", + "December"}; namespace { /// Uses Boost's internal checking to throw an exception if 'date' is out of the http://git-wip-us.apache.org/repos/asf/impala/blob/d91df9b6/be/src/exprs/timestamp-functions.h ---------------------------------------------------------------------- diff --git a/be/src/exprs/timestamp-functions.h b/be/src/exprs/timestamp-functions.h index fdf33bd..ee0d54f 100644 --- a/be/src/exprs/timestamp-functions.h +++ b/be/src/exprs/timestamp-functions.h @@ -123,11 +123,9 @@ class TimestampFunctions { static TimestampVal ToUtc(FunctionContext* context, const TimestampVal& ts_val, const StringVal& tz_string_val); - /// Returns the day's name as a string (e.g. 'Saturday'). - static StringVal DayName(FunctionContext* context, const TimestampVal& dow); - /// Functions to extract parts of the timestamp, return integers. static IntVal Year(FunctionContext* context, const TimestampVal& ts_val); + static IntVal Quarter(FunctionContext* context, const TimestampVal& ts_val); static IntVal Month(FunctionContext* context, const TimestampVal& ts_val); static IntVal DayOfWeek(FunctionContext* context, const TimestampVal& ts_val); static IntVal DayOfMonth(FunctionContext* context, const TimestampVal& ts_val); @@ -145,7 +143,9 @@ class TimestampFunctions { static IntVal DateDiff(FunctionContext* context, const TimestampVal& ts_val1, const TimestampVal& ts_val2); static std::string ShortDayName(FunctionContext* context, const TimestampVal& ts); + static StringVal LongDayName(FunctionContext* context, const TimestampVal& ts); static std::string ShortMonthName(FunctionContext* context, const TimestampVal& ts); + static StringVal LongMonthName(FunctionContext* context, const TimestampVal& ts); /// Return verbose string version of current time of day /// e.g. Mon Dec 01 16:25:05 2003 EST. @@ -229,18 +229,13 @@ class TimestampFunctions { const StringVal& format, bool is_error); private: - /// Static result values for DayName() function. - static const char* MONDAY; - static const char* TUESDAY; - static const char* WEDNESDAY; - static const char* THURSDAY; - static const char* FRIDAY; - static const char* SATURDAY; - static const char* SUNDAY; - - /// Static result values for ShortDayName() and ShortMonthName() functions. + + /// Static result values for DayName(), ShortDayName(), ShortMonthName() and + /// LongMonthName functions. static const std::string DAY_ARRAY[7]; + static const std::string DAYNAME_ARRAY[7]; static const std::string MONTH_ARRAY[12]; + static const std::string MONTHNAME_ARRAY[12]; }; } // namespace impala http://git-wip-us.apache.org/repos/asf/impala/blob/d91df9b6/be/src/exprs/udf-builtins-ir.cc ---------------------------------------------------------------------- diff --git a/be/src/exprs/udf-builtins-ir.cc b/be/src/exprs/udf-builtins-ir.cc index 03b88ba..3c47e6b 100644 --- a/be/src/exprs/udf-builtins-ir.cc +++ b/be/src/exprs/udf-builtins-ir.cc @@ -119,6 +119,7 @@ TExtractField::type StrToExtractField(FunctionContext* ctx, const StringVal& uni StringVal unit = UdfBuiltins::Lower(ctx, unit_str); if (UNLIKELY(unit.is_null)) return TExtractField::INVALID_FIELD; if (unit == "year") return TExtractField::YEAR; + if (unit == "quarter") return TExtractField::QUARTER; if (unit == "month") return TExtractField::MONTH; if (unit == "day") return TExtractField::DAY; if (unit == "hour") return TExtractField::HOUR; @@ -153,6 +154,7 @@ IntVal UdfBuiltins::Extract(FunctionContext* context, const StringVal& unit_str, switch (field) { case TExtractField::YEAR: + case TExtractField::QUARTER: case TExtractField::MONTH: case TExtractField::DAY: if (orig_date.is_special()) return IntVal::null(); @@ -174,6 +176,10 @@ IntVal UdfBuiltins::Extract(FunctionContext* context, const StringVal& unit_str, case TExtractField::YEAR: { return IntVal(orig_date.year()); } + case TExtractField::QUARTER: { + int m = orig_date.month(); + return IntVal((m - 1) / 3 + 1); + } case TExtractField::MONTH: { return IntVal(orig_date.month()); } http://git-wip-us.apache.org/repos/asf/impala/blob/d91df9b6/common/function-registry/impala_functions.py ---------------------------------------------------------------------- diff --git a/common/function-registry/impala_functions.py b/common/function-registry/impala_functions.py index 8174abb..b3387c6 100644 --- a/common/function-registry/impala_functions.py +++ b/common/function-registry/impala_functions.py @@ -109,21 +109,23 @@ visible_functions = [ '_ZN6impala11UdfBuiltins9VectorGetEPN10impala_udf15FunctionContextERKNS1_9BigIntValERKNS1_9StringValE'], # Timestamp functions + [['monthname'], 'STRING', ['TIMESTAMP'], '_ZN6impala18TimestampFunctions13LongMonthNameEPN10impala_udf15FunctionContextERKNS1_12TimestampValE'], [['next_day'], 'TIMESTAMP', ['TIMESTAMP', 'STRING'], '_ZN6impala18TimestampFunctions7NextDayEPN10impala_udf15FunctionContextERKNS1_12TimestampValERKNS1_9StringValE'], [['last_day'], 'TIMESTAMP', ['TIMESTAMP'], '_ZN6impala18TimestampFunctions7LastDayEPN10impala_udf15FunctionContextERKNS1_12TimestampValE'], [['unix_timestamp'], 'BIGINT', ['STRING'], '_ZN6impala18TimestampFunctions14UnixFromStringEPN10impala_udf15FunctionContextERKNS1_9StringValE'], [['year'], 'INT', ['TIMESTAMP'], '_ZN6impala18TimestampFunctions4YearEPN10impala_udf15FunctionContextERKNS1_12TimestampValE'], + [['quarter'], 'INT', ['TIMESTAMP'], '_ZN6impala18TimestampFunctions7QuarterEPN10impala_udf15FunctionContextERKNS1_12TimestampValE'], [['month'], 'INT', ['TIMESTAMP'], '_ZN6impala18TimestampFunctions5MonthEPN10impala_udf15FunctionContextERKNS1_12TimestampValE'], [['dayofweek'], 'INT', ['TIMESTAMP'], '_ZN6impala18TimestampFunctions9DayOfWeekEPN10impala_udf15FunctionContextERKNS1_12TimestampValE'], [['day', 'dayofmonth'], 'INT', ['TIMESTAMP'], '_ZN6impala18TimestampFunctions10DayOfMonthEPN10impala_udf15FunctionContextERKNS1_12TimestampValE'], [['dayofyear'], 'INT', ['TIMESTAMP'], '_ZN6impala18TimestampFunctions9DayOfYearEPN10impala_udf15FunctionContextERKNS1_12TimestampValE'], - [['weekofyear'], 'INT', ['TIMESTAMP'], '_ZN6impala18TimestampFunctions10WeekOfYearEPN10impala_udf15FunctionContextERKNS1_12TimestampValE'], + [['week', 'weekofyear'], 'INT', ['TIMESTAMP'], '_ZN6impala18TimestampFunctions10WeekOfYearEPN10impala_udf15FunctionContextERKNS1_12TimestampValE'], [['hour'], 'INT', ['TIMESTAMP'], '_ZN6impala18TimestampFunctions4HourEPN10impala_udf15FunctionContextERKNS1_12TimestampValE'], [['minute'], 'INT', ['TIMESTAMP'], '_ZN6impala18TimestampFunctions6MinuteEPN10impala_udf15FunctionContextERKNS1_12TimestampValE'], [['second'], 'INT', ['TIMESTAMP'], '_ZN6impala18TimestampFunctions6SecondEPN10impala_udf15FunctionContextERKNS1_12TimestampValE'], [['millisecond'], 'INT', ['TIMESTAMP'], '_ZN6impala18TimestampFunctions11MillisecondEPN10impala_udf15FunctionContextERKNS1_12TimestampValE'], [['to_date'], 'STRING', ['TIMESTAMP'], '_ZN6impala18TimestampFunctions6ToDateEPN10impala_udf15FunctionContextERKNS1_12TimestampValE'], - [['dayname'], 'STRING', ['TIMESTAMP'], '_ZN6impala18TimestampFunctions7DayNameEPN10impala_udf15FunctionContextERKNS1_12TimestampValE'], + [['dayname'], 'STRING', ['TIMESTAMP'], '_ZN6impala18TimestampFunctions11LongDayNameEPN10impala_udf15FunctionContextERKNS1_12TimestampValE'], [['date_trunc'], 'TIMESTAMP', ['STRING', 'TIMESTAMP'], '_ZN6impala11UdfBuiltins9DateTruncEPN10impala_udf15FunctionContextERKNS1_9StringValERKNS1_12TimestampValE', '_ZN6impala11UdfBuiltins16DateTruncPrepareEPN10impala_udf15FunctionContextENS2_18FunctionStateScopeE', @@ -419,9 +421,8 @@ visible_functions = [ 'impala::StringFunctions::SplitPart'], [['base64encode'], 'STRING', ['STRING'], 'impala::StringFunctions::Base64Encode'], [['base64decode'], 'STRING', ['STRING'], 'impala::StringFunctions::Base64Decode'], - # left and right are key words, leave them out for now. - [['strleft'], 'STRING', ['STRING', 'BIGINT'], 'impala::StringFunctions::Left'], - [['strright'], 'STRING', ['STRING', 'BIGINT'], 'impala::StringFunctions::Right'], + [['left', 'strleft'], 'STRING', ['STRING', 'BIGINT'], 'impala::StringFunctions::Left'], + [['right', 'strright'], 'STRING', ['STRING', 'BIGINT'], 'impala::StringFunctions::Right'], [['space'], 'STRING', ['BIGINT'], 'impala::StringFunctions::Space'], [['repeat'], 'STRING', ['STRING', 'BIGINT'], 'impala::StringFunctions::Repeat'], [['lpad'], 'STRING', ['STRING', 'BIGINT', 'STRING'], 'impala::StringFunctions::Lpad'], http://git-wip-us.apache.org/repos/asf/impala/blob/d91df9b6/common/thrift/Exprs.thrift ---------------------------------------------------------------------- diff --git a/common/thrift/Exprs.thrift b/common/thrift/Exprs.thrift index 7c88f2b..ee9fe94 100644 --- a/common/thrift/Exprs.thrift +++ b/common/thrift/Exprs.thrift @@ -79,6 +79,7 @@ struct TTimestampLiteral { enum TExtractField { INVALID_FIELD, YEAR, + QUARTER, MONTH, DAY, HOUR, http://git-wip-us.apache.org/repos/asf/impala/blob/d91df9b6/fe/src/main/cup/sql-parser.cup ---------------------------------------------------------------------- diff --git a/fe/src/main/cup/sql-parser.cup b/fe/src/main/cup/sql-parser.cup index 9f55dda..9802778 100644 --- a/fe/src/main/cup/sql-parser.cup +++ b/fe/src/main/cup/sql-parser.cup @@ -2799,13 +2799,17 @@ non_pred_expr ::= {: RESULT = e; :} | analytic_expr:e {: RESULT = e; :} - /* Since "IF", "REPLACE", "TRUNCATE" are keywords, need to special case these functions */ + /* Additional rules for function names that are also keywords */ | KW_IF LPAREN expr_list:exprs RPAREN {: RESULT = new FunctionCallExpr("if", exprs); :} | KW_REPLACE LPAREN expr_list:exprs RPAREN {: RESULT = new FunctionCallExpr("replace", exprs); :} | KW_TRUNCATE LPAREN expr_list:exprs RPAREN {: RESULT = new FunctionCallExpr("truncate", exprs); :} + | KW_LEFT LPAREN expr_list:exprs RPAREN + {: RESULT = new FunctionCallExpr("left", exprs); :} + | KW_RIGHT LPAREN expr_list:exprs RPAREN + {: RESULT = new FunctionCallExpr("right", exprs); :} | cast_expr:c {: RESULT = c; :} | case_expr:c http://git-wip-us.apache.org/repos/asf/impala/blob/d91df9b6/fe/src/test/java/org/apache/impala/analysis/AnalyzeExprsTest.java ---------------------------------------------------------------------- diff --git a/fe/src/test/java/org/apache/impala/analysis/AnalyzeExprsTest.java b/fe/src/test/java/org/apache/impala/analysis/AnalyzeExprsTest.java index d3eeb19..8f2cc60 100644 --- a/fe/src/test/java/org/apache/impala/analysis/AnalyzeExprsTest.java +++ b/fe/src/test/java/org/apache/impala/analysis/AnalyzeExprsTest.java @@ -1786,7 +1786,7 @@ public class AnalyzeExprsTest extends AnalyzerTest { AnalyzesOk("select extract(year from now())"); AnalysisError("select extract(foo from now())", "Time unit 'foo' in expression 'EXTRACT(foo FROM now())' is invalid. Expected " + - "one of YEAR, MONTH, DAY, HOUR, MINUTE, SECOND, MILLISECOND, EPOCH."); + "one of YEAR, QUARTER, MONTH, DAY, HOUR, MINUTE, SECOND, MILLISECOND, EPOCH."); AnalysisError("select extract(year from 0)", "Expression '0' in 'EXTRACT(year FROM 0)' has a return type of TINYINT but a " + "TIMESTAMP is required."); http://git-wip-us.apache.org/repos/asf/impala/blob/d91df9b6/fe/src/test/java/org/apache/impala/analysis/ParserTest.java ---------------------------------------------------------------------- diff --git a/fe/src/test/java/org/apache/impala/analysis/ParserTest.java b/fe/src/test/java/org/apache/impala/analysis/ParserTest.java index 178539f..b51d148 100644 --- a/fe/src/test/java/org/apache/impala/analysis/ParserTest.java +++ b/fe/src/test/java/org/apache/impala/analysis/ParserTest.java @@ -3243,9 +3243,8 @@ public class ParserTest extends FrontendTestBase { "select from t\n" + " ^\n" + "Encountered: FROM\n" + - "Expected: ALL, CASE, CAST, DEFAULT, DISTINCT, EXISTS, " + - "FALSE, IF, INTERVAL, NOT, NULL, REPLACE, " + - "STRAIGHT_JOIN, TRUNCATE, TRUE, IDENTIFIER\n"); + "Expected: ALL, CASE, CAST, DEFAULT, DISTINCT, EXISTS, FALSE, IF, INTERVAL, " + + "LEFT, NOT, NULL, REPLACE, RIGHT, STRAIGHT_JOIN, TRUNCATE, TRUE, IDENTIFIER"); // missing from ParserError("select c, b, c where a = 5", @@ -3270,8 +3269,8 @@ public class ParserTest extends FrontendTestBase { "select c, b, c from t where\n" + " ^\n" + "Encountered: EOF\n" + - "Expected: CASE, CAST, DEFAULT, EXISTS, FALSE, " + - "IF, INTERVAL, NOT, NULL, REPLACE, TRUNCATE, TRUE, IDENTIFIER\n"); + "Expected: CASE, CAST, DEFAULT, EXISTS, FALSE, IF, INTERVAL, LEFT, NOT, NULL, " + + "REPLACE, RIGHT, TRUNCATE, TRUE, IDENTIFIER"); // missing predicate in where clause (group by) ParserError("select c, b, c from t where group by a, b", @@ -3279,8 +3278,8 @@ public class ParserTest extends FrontendTestBase { "select c, b, c from t where group by a, b\n" + " ^\n" + "Encountered: GROUP\n" + - "Expected: CASE, CAST, DEFAULT, EXISTS, FALSE, " + - "IF, INTERVAL, NOT, NULL, REPLACE, TRUNCATE, TRUE, IDENTIFIER\n"); + "Expected: CASE, CAST, DEFAULT, EXISTS, FALSE, IF, INTERVAL, LEFT, NOT, NULL, " + + "REPLACE, RIGHT, TRUNCATE, TRUE, IDENTIFIER"); // unmatched string literal starting with " ParserError("select c, \"b, c from t", @@ -3340,8 +3339,8 @@ public class ParserTest extends FrontendTestBase { "...c,c,c,c,c,c,c,c,cd,c,d,d, ,c, from t\n" + " ^\n" + "Encountered: COMMA\n" + - "Expected: CASE, CAST, DEFAULT, EXISTS, FALSE, " + - "IF, INTERVAL, NOT, NULL, REPLACE, TRUNCATE, TRUE, IDENTIFIER\n"); + "Expected: CASE, CAST, DEFAULT, EXISTS, FALSE, IF, INTERVAL, LEFT, NOT, NULL, " + + "REPLACE, RIGHT, TRUNCATE, TRUE, IDENTIFIER"); // Parsing identifiers that have different names printed as EXPECTED ParserError("DROP DATA SRC foo",