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
Reviewed-on: http://gerrit.cloudera.org:8080/9428
Reviewed-by: Tim Armstrong <tarmstr...@cloudera.com>
Tested-by: Tim Armstrong <tarmstr...@cloudera.com>


Project: http://git-wip-us.apache.org/repos/asf/impala/repo
Commit: http://git-wip-us.apache.org/repos/asf/impala/commit/ea3cefb8
Tree: http://git-wip-us.apache.org/repos/asf/impala/tree/ea3cefb8
Diff: http://git-wip-us.apache.org/repos/asf/impala/diff/ea3cefb8

Branch: refs/heads/2.x
Commit: ea3cefb8807aedd5f3d53b0d76f00c743f1adf0e
Parents: 8b772a0
Author: Greg Rahn <gregr...@users.noreply.github.com>
Authored: Tue Feb 20 18:31:57 2018 -0800
Committer: Tim Armstrong <tarmstr...@cloudera.com>
Committed: Sat Feb 24 01:58:46 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/ea3cefb8/be/src/exprs/expr-test.cc
----------------------------------------------------------------------
diff --git a/be/src/exprs/expr-test.cc b/be/src/exprs/expr-test.cc
index 5fc3429..acd37d7 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");
@@ -5932,11 +5934,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);
@@ -5948,11 +5952,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);
@@ -6010,22 +6016,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);
@@ -6045,6 +6055,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
@@ -6525,6 +6563,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))",
@@ -6546,6 +6586,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/ea3cefb8/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/ea3cefb8/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/ea3cefb8/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/ea3cefb8/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/ea3cefb8/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/ea3cefb8/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/ea3cefb8/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/ea3cefb8/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 49aa91c..f3e2294 100644
--- a/fe/src/test/java/org/apache/impala/analysis/AnalyzeExprsTest.java
+++ b/fe/src/test/java/org/apache/impala/analysis/AnalyzeExprsTest.java
@@ -1792,7 +1792,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/ea3cefb8/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",

Reply via email to