You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@impala.apache.org by jo...@apache.org on 2018/08/18 20:38:20 UTC

[1/7] impala git commit: Revert "IMPALA-7412: width_bucket() function overflows too easily"

Repository: impala
Updated Branches:
  refs/heads/master 1dd4403fd -> 0a901fcb2


Revert "IMPALA-7412: width_bucket() function overflows too easily"

IMPALA-7459 has been traced to this commit.

This reverts commit a8b32dbafa1f015c8316f205e32bbdce349f2474.

Change-Id: I61945c9199818ec98d7fb187e5931c76f7082c1a
Reviewed-on: http://gerrit.cloudera.org:8080/11265
Reviewed-by: Impala Public Jenkins <im...@cloudera.com>
Tested-by: Impala Public Jenkins <im...@cloudera.com>


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

Branch: refs/heads/master
Commit: 83bb4aed71db140ab13b2d727e7e09bb2077ccc6
Parents: 1dd4403
Author: Joe McDonnell <jo...@cloudera.com>
Authored: Fri Aug 17 16:19:14 2018 -0700
Committer: Impala Public Jenkins <im...@cloudera.com>
Committed: Sat Aug 18 03:05:46 2018 +0000

----------------------------------------------------------------------
 be/src/exprs/expr-test.cc             |  29 ++++--
 be/src/exprs/math-functions-ir.cc     | 156 ++++++++++++++++++-----------
 be/src/util/bit-util.h                |  53 +---------
 tests/query_test/test_decimal_fuzz.py |  63 +-----------
 4 files changed, 125 insertions(+), 176 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/impala/blob/83bb4aed/be/src/exprs/expr-test.cc
----------------------------------------------------------------------
diff --git a/be/src/exprs/expr-test.cc b/be/src/exprs/expr-test.cc
index 8cc0e9d..d0e3ff3 100644
--- a/be/src/exprs/expr-test.cc
+++ b/be/src/exprs/expr-test.cc
@@ -5348,11 +5348,16 @@ TEST_F(ExprTest, MathFunctions) {
   // Test when min > max
   TestErrorString("width_bucket(22, 50, 5, 4)",
       "UDF ERROR: Lower bound cannot be greater than or equal to the upper bound\n");
-  // IMPALA-7412: Test max - min should not overflow anymore
-  TestValue("width_bucket(11, -9, 99999999999999999999999999999999999999, 4000)",
-      TYPE_BIGINT, 1);
-  TestValue("width_bucket(1, -99999999999999999999999999999999999999, 9, 40)",
-      TYPE_BIGINT, 40);
+  // Test max - min will overflow during width_bucket evaluation
+  TestErrorString("width_bucket(11, -9, 99999999999999999999999999999999999999, 4000)",
+      "UDF ERROR: Overflow while evaluating the difference between min_range: -9 and "
+      "max_range: 99999999999999999999999999999999999999\n");
+  // If expr - min overflows during width_bucket evaluation, max - min will also
+  // overflow. Since we evaluate max - min before evaluating expr - min, we will never
+  // end up overflowing expr - min.
+  TestErrorString("width_bucket(1, -99999999999999999999999999999999999999, 9, 40)",
+      "UDF ERROR: Overflow while evaluating the difference between min_range: "
+      "-99999999999999999999999999999999999999 and max_range: 9\n");
   // Test when dist_from_min * buckets cannot be stored in a int128_t (overflows)
   // and needs to be stored in a int256_t
   TestValue("width_bucket(8000000000000000000000000000000000000,"
@@ -5375,12 +5380,16 @@ TEST_F(ExprTest, MathFunctions) {
   // max and min value that would require int256_t for evalation
   TestValue("width_bucket(10000000000000000000000000000000000000, 1,"
             "99999999999999999999999999999999999999, 15)", TYPE_BIGINT, 2);
-  // IMPALA-7412: These should not overflow anymore
-  TestValue("width_bucket(cast(-0.10 as decimal(37,30)), cast(-0.36028797018963968 "
+  // IMPALA-7242/IMPALA-7243: check for overflow when converting IntVal to DecimalValue
+  TestErrorString("width_bucket(cast(-0.10 as decimal(37,30)), cast(-0.36028797018963968 "
       "as decimal(25,25)), cast(9151517.4969773200562764155787276999832"
-      "as decimal(38,31)), 1328180220)", TYPE_BIGINT, 38);
-  TestValue("width_bucket(cast(9 as decimal(10,7)), cast(-60000 as decimal(11,6)), "
-      "cast(10 as decimal(7,5)), 249895273);", TYPE_BIGINT, 249891109);
+      "as decimal(38,31)), 1328180220)",
+      "UDF ERROR: Overflow while representing the num_buckets:1328180220 as a "
+      "DecimalVal\n");
+  TestErrorString("width_bucket(cast(9 as decimal(10,7)), cast(-60000 as decimal(11,6)), "
+      "cast(10 as decimal(7,5)), 249895273);",
+      "UDF ERROR: Overflow while representing the num_buckets:249895273 as a "
+      "DecimalVal\n");
   // Run twice to test deterministic behavior.
   for (uint32_t seed : {0, 1234}) {
     stringstream rand, random;

http://git-wip-us.apache.org/repos/asf/impala/blob/83bb4aed/be/src/exprs/math-functions-ir.cc
----------------------------------------------------------------------
diff --git a/be/src/exprs/math-functions-ir.cc b/be/src/exprs/math-functions-ir.cc
index 7103a10..28c9e1e 100644
--- a/be/src/exprs/math-functions-ir.cc
+++ b/be/src/exprs/math-functions-ir.cc
@@ -457,32 +457,57 @@ DoubleVal MathFunctions::FmodDouble(FunctionContext* ctx, const DoubleVal& a,
 
 // The bucket_number is evaluated using the following formula:
 //
-//   bucket_number = dist_from_min * num_buckets / range_size + 1
+//   bucket_number = dist_from_min * num_buckets / range_size
 //      where -
 //        dist_from_min = expr - min_range
 //        range_size = max_range - min_range
-//        num_buckets = number of buckets
+//        buckets = number of buckets
 //
-// Since expr, min_range, and max_range are all decimals with the same
-// byte size, precision, and scale we can interpret them as plain integers
-// and still calculate the proper bucket.
+// The results of the above subtractions are stored in Decimal16Value to avoid an overflow
+// in the following cases:
+//   case 1:
+//      T1 is decimal8Value
+//         When evaluating this particular expression
+//            dist_from_min = expr - min_range
+//         If expr is a max positive value which can be represented in decimal8Value and
+//         min_range < 0 the resulting dist_from_min can be represented in decimal16Val
+//         without overflowing
+//   case 2:
+//      T1 is decimal16Value
+//         Subtracting a negative min_range from expr can result in an overflow in which
+//         case the function errors out. There is no decimal32Val to handle this. So
+//         storing it in decimal16Value.
+//   case 3:
+//      T1 is decimal4Value
+//         We can store the results in a decimal8Value. But this change hard codes to
+//         store the result in decimal16Val for now to be compatible with the other
+//         decimal*Vals
 //
-// There are some possibilities of overflowing during the calculation:
-// range_size = max_range - min_range
-// dist_from_min = expr - min_range
-// dist_from_min * num_buckets
+// The result of this multiplication dist_from_min * buckets is stored as a int256_t
+// if storing it in a int128_t would overflow.
 //
-// For all the above cases we use a bigger integer type provided by the
-// BitUtil::DoubleWidth<> metafunction.
+// To perform the division, range_size is scaled up. The scale and precision of the
+// numerator and denominator are adjusted to be the same. This avoids the need to compute
+// the resulting scale and precision.
 template <class  T1>
 BigIntVal MathFunctions::WidthBucketImpl(FunctionContext* ctx,
     const T1& expr, const T1& min_range,
     const T1& max_range, const IntVal& num_buckets) {
-  auto expr_val = expr.value();
-  using ActualType = decltype(expr_val);
-  auto min_range_val = min_range.value();
-  auto max_range_val = max_range.value();
-  auto num_buckets_val = static_cast<ActualType>(num_buckets.val);
+  // FE casts expr, min_range and max_range to be of the scale and precision
+  int input_scale = ctx->impl()->GetConstFnAttr(FunctionContextImpl::ARG_TYPE_SCALE, 1);
+  int input_precision = ctx->impl()->GetConstFnAttr(
+      FunctionContextImpl::ARG_TYPE_PRECISION, 1);
+
+  bool overflow = false;
+  Decimal16Value range_size = max_range.template Subtract<int128_t>(input_scale,
+      min_range, input_scale, input_precision, input_scale, false, &overflow);
+  if (UNLIKELY(overflow)) {
+    ostringstream error_msg;
+    error_msg << "Overflow while evaluating the difference between min_range: " <<
+        min_range.value() << " and max_range: " << max_range.value();
+    ctx->SetError(error_msg.str().c_str());
+    return BigIntVal::null();
+  }
 
   if (UNLIKELY(num_buckets.val <= 0)) {
     ostringstream error_msg;
@@ -491,58 +516,73 @@ BigIntVal MathFunctions::WidthBucketImpl(FunctionContext* ctx,
     return BigIntVal::null();
   }
 
-  if (UNLIKELY(min_range_val >= max_range_val)) {
+  if (UNLIKELY(min_range >= max_range)) {
     ctx->SetError("Lower bound cannot be greater than or equal to the upper bound");
     return BigIntVal::null();
   }
 
-  if (expr_val < min_range_val) return 0;
-  if (expr_val >= max_range_val) {
-    return BigIntVal(static_cast<int64_t>(num_buckets.val) + 1);
+  if (UNLIKELY(expr < min_range)) return 0;
+
+  if (UNLIKELY(expr >= max_range)) {
+    BigIntVal result;
+    result.val = num_buckets.val;
+    ++result.val;
+    return result;
   }
 
-  bool bigger_type_needed = false;
-  // It is likely that this if stmt can be evaluated during codegen because
-  // 'max_range' and 'min_range' are almost certainly constant expressions:
-  if (max_range_val >= 0 && min_range_val < 0) {
-    if (static_cast<UnsignedType<ActualType>>(max_range_val) +
-        static_cast<UnsignedType<ActualType>>(abs(min_range_val)) >=
-        static_cast<UnsignedType<ActualType>>(BitUtil::Max<ActualType>())) {
-      bigger_type_needed = true;
-    }
+  Decimal16Value dist_from_min = expr.template Subtract<int128_t>(input_scale,
+      min_range, input_scale, input_precision, input_scale, false, &overflow);
+  DCHECK_EQ(overflow, false);
+
+  Decimal16Value buckets = Decimal16Value::FromInt(input_precision, input_scale,
+      num_buckets.val, &overflow);
+
+  if (UNLIKELY(overflow)) {
+    stringstream error_msg;
+    error_msg << "Overflow while representing the num_buckets:" << num_buckets.val
+        << " as a DecimalVal";
+    ctx->SetError(error_msg.str().c_str());
+    return BigIntVal::null();
+  }
+  bool needs_int256 = false;
+  // Check if dist_from_min * buckets would overflow and if there is a need to
+  // store the intermediate results in int256_t to avoid an overflows
+  // Check if scaling up range size overflows and if there is a need to store the
+  // intermediate results in int256_t to avoid the overflow
+  if (UNLIKELY(BitUtil::CountLeadingZeros(abs(buckets.value())) +
+      BitUtil::CountLeadingZeros(abs(dist_from_min.value())) <= 128 ||
+      BitUtil::CountLeadingZeros(range_size.value()) +
+      detail::MinLeadingZerosAfterScaling(BitUtil::CountLeadingZeros(
+      range_size.value()), input_scale) <= 128)) {
+    needs_int256 = true;
   }
 
-  auto MultiplicationOverflows = [](ActualType lhs, ActualType rhs) {
-    DCHECK(lhs > 0 && rhs > 0);
-    using ActualType = decltype(lhs);
-    return BitUtil::CountLeadingZeros(lhs) + BitUtil::CountLeadingZeros(rhs) <=
-        BitUtil::UnsignedWidth<ActualType>() + 1;
-  };
-
-  // It is likely that this can be evaluated during codegen:
-  bool multiplication_can_overflow = bigger_type_needed ||  MultiplicationOverflows(
-      max_range_val - min_range_val, num_buckets_val);
-  // 'expr_val' is not likely to be a constant expression, so this almost certainly
-  // needs runtime evaluation if bigger_type_needed is false:
-  bigger_type_needed = multiplication_can_overflow &&  MultiplicationOverflows(
-      expr_val - min_range_val, num_buckets_val);
-
-  auto BucketFunc = [](auto element, auto min_rng, auto max_rng, auto buckets) {
-    auto range_size = max_rng - min_rng;
-    auto dist_from_min = element - min_rng;
-    auto ret = dist_from_min * buckets / range_size;
-    return BigIntVal(static_cast<int64_t>(ret) + 1);
-  };
-
-  if (bigger_type_needed) {
-    using BiggerType = typename DoubleWidth<ActualType>::type;
-
-    return BucketFunc(static_cast<BiggerType>(expr_val),
-        static_cast<BiggerType>(min_range_val), static_cast<BiggerType>(max_range_val),
-        static_cast<BiggerType>(num_buckets.val));
+  int128_t result;
+  if (needs_int256) {
+    // resulting scale should be 2 * input_scale as per multiplication rules
+    int256_t x = ConvertToInt256(buckets.value()) * ConvertToInt256(
+        dist_from_min.value());
+
+    // Since "range_size" and "x" have different scales, the divison would require
+    // evaluating the resulting scale. To avoid this we scale up the denominator to
+    // match the scale of the numerator.
+    int256_t y = DecimalUtil::MultiplyByScale<int256_t>(ConvertToInt256(
+        range_size.value()), input_scale, false);
+    result = ConvertToInt128(x / y, DecimalUtil::MAX_UNSCALED_DECIMAL16,
+        &overflow);
+    DCHECK_EQ(overflow, false);
   } else {
-    return BucketFunc(expr_val, min_range_val, max_range_val, num_buckets.val);
+    // resulting scale should be 2 * input_scale as per multiplication rules
+    int128_t x = buckets.value() * dist_from_min.value();
+
+    // Since "range_size" and "x" have different scales, the divison would require
+    // evaluating the resulting scale. To avoid this we scale up the denominator to
+    // match the scale of the numerator.
+    int128_t y = DecimalUtil::MultiplyByScale<int128_t>(range_size.value(),
+        input_scale, false);
+    result = x / y; // NOLINT: clang-tidy thinks y may equal zero here.
   }
+  return (BigIntVal(abs(result) + 1));
 }
 
 BigIntVal MathFunctions::WidthBucket(FunctionContext* ctx, const DecimalVal& expr,

http://git-wip-us.apache.org/repos/asf/impala/blob/83bb4aed/be/src/util/bit-util.h
----------------------------------------------------------------------
diff --git a/be/src/util/bit-util.h b/be/src/util/bit-util.h
index d07dd9d..8a65509 100644
--- a/be/src/util/bit-util.h
+++ b/be/src/util/bit-util.h
@@ -28,7 +28,8 @@
 #include <climits>
 #include <limits>
 #include <typeinfo>
-#include <type_traits>
+
+#include <boost/type_traits/make_unsigned.hpp>
 
 #include "common/compiler-util.h"
 #include "gutil/bits.h"
@@ -38,40 +39,7 @@
 
 namespace impala {
 
-/// Nested 'type' corresponds to the unsigned version of T.
-template <typename T>
-struct MakeUnsigned {
-  using type = std::make_unsigned_t<T>;
-};
-
-template <>
-struct MakeUnsigned<int128_t> {
-  using type = __uint128_t;
-};
-
-template <typename T>
-using UnsignedType = typename MakeUnsigned<T>::type;
-
-// Doubles the width of integer types (e.g. int32_t -> int64_t).
-// Currently only works with a few signed types.
-// Feel free to extend it to other types as well.
-template <typename T>
-struct DoubleWidth {};
-
-template <>
-struct DoubleWidth<int32_t> {
-  using type = int64_t;
-};
-
-template <>
-struct DoubleWidth<int64_t> {
-  using type = int128_t;
-};
-
-template <>
-struct DoubleWidth<int128_t> {
-  using type = int256_t;
-};
+using boost::make_unsigned;
 
 /// Utility class to do standard bit tricks
 /// TODO: is this in boost or something else like that?
@@ -91,17 +59,6 @@ class BitUtil {
         std::is_same<CVR_REMOVED, __int128>::value ? 127 : -1;
   }
 
-  /// Returns the max value that can be represented in T.
-  template<typename T, typename CVR_REMOVED = typename std::decay<T>::type,
-      typename std::enable_if<std::is_integral<CVR_REMOVED> {}||
-                              std::is_same<CVR_REMOVED, __int128> {}, int>::type = 0>
-  constexpr static inline CVR_REMOVED Max() {
-    return std::is_integral<CVR_REMOVED>::value ?
-        std::numeric_limits<CVR_REMOVED>::max() :
-        std::is_same<CVR_REMOVED, __int128>::value ?
-            static_cast<UnsignedType<CVR_REMOVED>>(-1) / 2 : -1;
-  }
-
   /// Return an integer signifying the sign of the value, returning +1 for
   /// positive integers (and zero), -1 for negative integers.
   /// The extra shift is to silence GCC warnings about full width shift on
@@ -211,7 +168,7 @@ class BitUtil {
   template<typename T>
   static inline int PopcountSigned(T v) {
     // Converting to same-width unsigned then extending preserves the bit pattern.
-    return BitUtil::Popcount(static_cast<UnsignedType<T>>(v));
+    return BitUtil::Popcount(static_cast<typename make_unsigned<T>::type>(v));
   }
 
   /// Returns the 'num_bits' least-significant bits of 'v'.
@@ -292,7 +249,7 @@ class BitUtil {
   template <typename T>
   constexpr static T ShiftRightLogical(T v, int shift) {
     // Conversion to unsigned ensures most significant bits always filled with 0's
-    return static_cast<UnsignedType<T>>(v) >> shift;
+    return static_cast<typename make_unsigned<T>::type>(v) >> shift;
   }
 
   /// Get an specific bit of a numeric type

http://git-wip-us.apache.org/repos/asf/impala/blob/83bb4aed/tests/query_test/test_decimal_fuzz.py
----------------------------------------------------------------------
diff --git a/tests/query_test/test_decimal_fuzz.py b/tests/query_test/test_decimal_fuzz.py
index 1433ec3..a129e33 100644
--- a/tests/query_test/test_decimal_fuzz.py
+++ b/tests/query_test/test_decimal_fuzz.py
@@ -30,9 +30,6 @@ from tests.common.test_vector import ImpalaTestDimension, ImpalaTestMatrix
 
 class TestDecimalFuzz(ImpalaTestSuite):
 
-  # Impala's max precision for decimals is 38, so we should have the same in the tests
-  decimal.getcontext().prec = 38
-
   @classmethod
   def get_workload(cls):
     return 'functional-query'
@@ -203,7 +200,7 @@ class TestDecimalFuzz(ImpalaTestSuite):
         return True
     return False
 
-  def execute_one_decimal_op(self):
+  def execute_one(self):
     '''Executes a single query and compares the result to a result that we computed in
     Python.'''
     op = random.choice(['+', '-', '*', '/', '%'])
@@ -246,60 +243,6 @@ class TestDecimalFuzz(ImpalaTestSuite):
         expected_result = None
       assert self.result_equals(expected_result, result)
 
-  def test_decimal_ops(self, vector):
-    for _ in xrange(self.iterations):
-      self.execute_one_decimal_op()
-
-  def width_bucket(self, val, min_range, max_range, num_buckets):
-    # Multiplying the values by 10**40 guarantees that the numbers can be converted
-    # to int without losing information.
-    val_int = int(decimal.Decimal(val) * 10**40)
-    min_range_int = int(decimal.Decimal(min_range) * 10**40)
-    max_range_int = int(decimal.Decimal(max_range) * 10**40)
-
-    if min_range_int >= max_range_int:
-      return None
-    if val_int < min_range_int:
-      return 0
-    if val_int > max_range_int:
-      return num_buckets + 1
-
-    range_size = max_range_int - min_range_int
-    dist_from_min = val_int - min_range_int
-    return (num_buckets * dist_from_min) / range_size + 1
-
-  def execute_one_width_bucket(self):
-    val, val_prec, val_scale = self.get_decimal()
-    min_range, min_range_prec, min_range_scale = self.get_decimal()
-    max_range, max_range_prec, max_range_scale = self.get_decimal()
-    num_buckets = random.randint(1, 2147483647)
-
-    query = ('select width_bucket('
-        'cast({val} as decimal({val_prec},{val_scale})), '
-        'cast({min_range} as decimal({min_range_prec},{min_range_scale})), '
-        'cast({max_range} as decimal({max_range_prec},{max_range_scale})), '
-        '{num_buckets})')
-
-    query = query.format(val=val, val_prec=val_prec, val_scale=val_scale,
-        min_range=min_range, min_range_prec=min_range_prec,
-        min_range_scale=min_range_scale,
-        max_range=max_range, max_range_prec=max_range_prec,
-        max_range_scale=max_range_scale,
-        num_buckets=num_buckets)
-
-    expected_result = self.width_bucket(val, min_range, max_range, num_buckets)
-    if not expected_result:
-      return
-
-    try:
-      result = self.execute_scalar(query, query_options={'decimal_v2': 'true'})
-      assert int(result) == expected_result
-    except ImpalaBeeswaxException as e:
-      if "You need to wrap the arguments in a CAST" not in str(e):
-        # Sometimes the decimal inputs are incompatible with each other, so it's ok
-        # to ignore this error.
-        raise e
-
-  def test_width_bucket(self, vector):
+  def test_fuzz(self, vector):
     for _ in xrange(self.iterations):
-      self.execute_one_width_bucket()
+      self.execute_one()


[5/7] impala git commit: IMPALA-7345: Add the OWNER privilege

Posted by jo...@apache.org.
http://git-wip-us.apache.org/repos/asf/impala/blob/1d4df941/fe/src/test/java/org/apache/impala/analysis/AuthorizationStmtTest.java
----------------------------------------------------------------------
diff --git a/fe/src/test/java/org/apache/impala/analysis/AuthorizationStmtTest.java b/fe/src/test/java/org/apache/impala/analysis/AuthorizationStmtTest.java
index abfff91..7e10eee 100644
--- a/fe/src/test/java/org/apache/impala/analysis/AuthorizationStmtTest.java
+++ b/fe/src/test/java/org/apache/impala/analysis/AuthorizationStmtTest.java
@@ -347,57 +347,72 @@ public class AuthorizationStmtTest extends FrontendTestBase {
         authorize("select id from functional.alltypes"),
         // With clause with select.
         authorize("with t as (select id from functional.alltypes) select * from t")}) {
-      authzTest.ok(onServer(TPrivilegeLevel.ALL))
+      authzTest
           .ok(onServer(TPrivilegeLevel.ALL))
+          .ok(onServer(TPrivilegeLevel.OWNER))
           .ok(onServer(TPrivilegeLevel.SELECT))
           .ok(onDatabase("functional", TPrivilegeLevel.ALL))
+          .ok(onDatabase("functional", TPrivilegeLevel.OWNER))
           .ok(onDatabase("functional", TPrivilegeLevel.SELECT))
           .ok(onTable("functional", "alltypes", TPrivilegeLevel.ALL))
+          .ok(onTable("functional", "alltypes", TPrivilegeLevel.OWNER))
           .ok(onTable("functional", "alltypes", TPrivilegeLevel.SELECT))
           .ok(onColumn("functional", "alltypes", "id", TPrivilegeLevel.SELECT))
           .error(selectError("functional.alltypes"))
           .error(selectError("functional.alltypes"), onServer(allExcept(
-              TPrivilegeLevel.ALL, TPrivilegeLevel.SELECT)))
+              TPrivilegeLevel.ALL, TPrivilegeLevel.OWNER, TPrivilegeLevel.SELECT)))
           .error(selectError("functional.alltypes"), onDatabase("functional",
-              allExcept(TPrivilegeLevel.ALL, TPrivilegeLevel.SELECT)))
+              allExcept(TPrivilegeLevel.ALL, TPrivilegeLevel.OWNER,
+              TPrivilegeLevel.SELECT)))
           .error(selectError("functional.alltypes"), onTable("functional",
-              "alltypes", allExcept(TPrivilegeLevel.ALL, TPrivilegeLevel.SELECT)));
+              "alltypes", allExcept(TPrivilegeLevel.ALL, TPrivilegeLevel.OWNER,
+              TPrivilegeLevel.SELECT)));
     }
 
 
     // Select without referencing a column.
     authorize("select 1 from functional.alltypes")
         .ok(onServer(TPrivilegeLevel.ALL))
+        .ok(onServer(TPrivilegeLevel.OWNER))
         .ok(onServer(TPrivilegeLevel.SELECT))
         .ok(onDatabase("functional", TPrivilegeLevel.ALL))
+        .ok(onDatabase("functional", TPrivilegeLevel.OWNER))
         .ok(onDatabase("functional", TPrivilegeLevel.SELECT))
         .ok(onTable("functional", "alltypes", TPrivilegeLevel.ALL))
+        .ok(onTable("functional", "alltypes", TPrivilegeLevel.OWNER))
         .ok(onTable("functional", "alltypes", TPrivilegeLevel.SELECT))
         .error(selectError("functional.alltypes"))
         .error(selectError("functional.alltypes"), onServer(allExcept(
-            TPrivilegeLevel.ALL, TPrivilegeLevel.SELECT)))
+            TPrivilegeLevel.ALL, TPrivilegeLevel.OWNER, TPrivilegeLevel.SELECT)))
         .error(selectError("functional.alltypes"), onDatabase("functional",
-            allExcept(TPrivilegeLevel.ALL, TPrivilegeLevel.SELECT)))
+            allExcept(TPrivilegeLevel.ALL, TPrivilegeLevel.OWNER,
+            TPrivilegeLevel.SELECT)))
         .error(selectError("functional.alltypes"), onTable("functional", "alltypes",
-            allExcept(TPrivilegeLevel.ALL, TPrivilegeLevel.SELECT)));
+            allExcept(TPrivilegeLevel.ALL, TPrivilegeLevel.OWNER,
+            TPrivilegeLevel.SELECT)));
 
 
     // Select a specific column on a view.
     // Column-level privileges on views are not currently supported.
     authorize("select id from functional.alltypes_view")
         .ok(onServer(TPrivilegeLevel.ALL))
+        .ok(onServer(TPrivilegeLevel.OWNER))
         .ok(onServer(TPrivilegeLevel.SELECT))
         .ok(onDatabase("functional", TPrivilegeLevel.ALL))
+        .ok(onDatabase("functional", TPrivilegeLevel.OWNER))
         .ok(onDatabase("functional", TPrivilegeLevel.SELECT))
         .ok(onTable("functional", "alltypes_view", TPrivilegeLevel.ALL))
+        .ok(onTable("functional", "alltypes_view", TPrivilegeLevel.OWNER))
         .ok(onTable("functional", "alltypes_view", TPrivilegeLevel.SELECT))
         .error(selectError("functional.alltypes_view"))
         .error(selectError("functional.alltypes_view"), onServer(allExcept(
-            TPrivilegeLevel.ALL, TPrivilegeLevel.SELECT)))
+            TPrivilegeLevel.ALL, TPrivilegeLevel.OWNER, TPrivilegeLevel.SELECT)))
         .error(selectError("functional.alltypes_view"), onDatabase("functional",
-            allExcept(TPrivilegeLevel.ALL, TPrivilegeLevel.SELECT)))
+            allExcept(TPrivilegeLevel.ALL, TPrivilegeLevel.OWNER,
+            TPrivilegeLevel.SELECT)))
         .error(selectError("functional.alltypes_view"), onTable("functional",
-            "alltypes_view", allExcept(TPrivilegeLevel.ALL, TPrivilegeLevel.SELECT)));
+            "alltypes_view", allExcept(TPrivilegeLevel.ALL, TPrivilegeLevel.OWNER,
+            TPrivilegeLevel.SELECT)));
 
     // Constant select.
     authorize("select 1").ok();
@@ -406,51 +421,68 @@ public class AuthorizationStmtTest extends FrontendTestBase {
     authorize("select a.id from functional.view_view a " +
         "join functional.alltypesagg b ON (a.id = b.id)")
         .ok(onServer(TPrivilegeLevel.ALL))
+        .ok(onServer(TPrivilegeLevel.OWNER))
         .ok(onServer(TPrivilegeLevel.SELECT))
         .ok(onDatabase("functional", TPrivilegeLevel.ALL))
+        .ok(onDatabase("functional", TPrivilegeLevel.OWNER))
         .ok(onDatabase("functional", TPrivilegeLevel.SELECT))
         .ok(onTable("functional", "view_view", TPrivilegeLevel.ALL),
             onTable("functional", "alltypesagg", TPrivilegeLevel.ALL))
+        .ok(onTable("functional", "view_view", TPrivilegeLevel.OWNER),
+            onTable("functional", "alltypesagg", TPrivilegeLevel.OWNER))
         .ok(onTable("functional", "view_view", TPrivilegeLevel.ALL),
             onTable("functional", "alltypesagg", TPrivilegeLevel.SELECT))
+        .ok(onTable("functional", "view_view", TPrivilegeLevel.OWNER),
+            onTable("functional", "alltypesagg", TPrivilegeLevel.SELECT))
         .ok(onTable("functional", "view_view", TPrivilegeLevel.SELECT),
             onTable("functional", "alltypesagg", TPrivilegeLevel.ALL))
         .ok(onTable("functional", "view_view", TPrivilegeLevel.SELECT),
+            onTable("functional", "alltypesagg", TPrivilegeLevel.OWNER))
+        .ok(onTable("functional", "view_view", TPrivilegeLevel.SELECT),
             onTable("functional", "alltypesagg", TPrivilegeLevel.SELECT))
         .error(selectError("functional.view_view"))
         .error(selectError("functional.view_view"), onServer(allExcept(
-            TPrivilegeLevel.ALL, TPrivilegeLevel.SELECT)))
+            TPrivilegeLevel.ALL, TPrivilegeLevel.OWNER, TPrivilegeLevel.SELECT)))
         .error(selectError("functional.view_view"), onDatabase("functional",
-            allExcept(TPrivilegeLevel.ALL, TPrivilegeLevel.SELECT)))
+            allExcept(TPrivilegeLevel.ALL, TPrivilegeLevel.OWNER,
+            TPrivilegeLevel.SELECT)))
         .error(selectError("functional.view_view"), onTable("functional", "view_view",
-            allExcept(TPrivilegeLevel.ALL, TPrivilegeLevel.SELECT)), onTable("functional",
-            "alltypesagg", allExcept(TPrivilegeLevel.ALL, TPrivilegeLevel.SELECT)));
+            allExcept(TPrivilegeLevel.ALL, TPrivilegeLevel.OWNER,
+            TPrivilegeLevel.SELECT)), onTable("functional", "alltypesagg",
+            allExcept(TPrivilegeLevel.ALL, TPrivilegeLevel.OWNER,
+            TPrivilegeLevel.SELECT)));
 
     // Tests authorization after a statement has been rewritten (IMPALA-3915).
     authorize("select * from functional_seq_snap.subquery_view")
         .ok(onServer(TPrivilegeLevel.ALL))
+        .ok(onServer(TPrivilegeLevel.OWNER))
         .ok(onServer(TPrivilegeLevel.SELECT))
         .ok(onDatabase("functional_seq_snap", TPrivilegeLevel.ALL))
+        .ok(onDatabase("functional_seq_snap", TPrivilegeLevel.OWNER))
         .ok(onDatabase("functional_seq_snap", TPrivilegeLevel.SELECT))
         .ok(onTable("functional_seq_snap", "subquery_view", TPrivilegeLevel.ALL))
+        .ok(onTable("functional_seq_snap", "subquery_view", TPrivilegeLevel.OWNER))
         .ok(onTable("functional_seq_snap", "subquery_view", TPrivilegeLevel.SELECT))
         .error(selectError("functional_seq_snap.subquery_view"))
         .error(selectError("functional_seq_snap.subquery_view"), onServer(
-            allExcept(TPrivilegeLevel.ALL, TPrivilegeLevel.SELECT)))
+            allExcept(TPrivilegeLevel.ALL, TPrivilegeLevel.OWNER,
+            TPrivilegeLevel.SELECT)))
         .error(selectError("functional_seq_snap.subquery_view"),
             onDatabase("functional_seq_snap", allExcept(TPrivilegeLevel.ALL,
-            TPrivilegeLevel.SELECT)))
+            TPrivilegeLevel.OWNER, TPrivilegeLevel.SELECT)))
         .error(selectError("functional_seq_snap.subquery_view"),
             onTable("functional_seq_snap", "subquery_view", allExcept(
-            TPrivilegeLevel.ALL, TPrivilegeLevel.SELECT)));
+            TPrivilegeLevel.ALL, TPrivilegeLevel.OWNER, TPrivilegeLevel.SELECT)));
 
     // Select a UDF.
     ScalarFunction fn = addFunction("functional", "f");
     try {
       authorize("select functional.f()")
           .ok(onServer(TPrivilegeLevel.ALL))
+          .ok(onServer(TPrivilegeLevel.OWNER))
           .ok(onServer(viewMetadataPrivileges()))
           .ok(onDatabase("functional", TPrivilegeLevel.ALL))
+          .ok(onDatabase("functional", TPrivilegeLevel.OWNER))
           .ok(onDatabase("functional", viewMetadataPrivileges()))
           .error(accessError("functional"))
           .error(accessError("functional"), onServer(allExcept(
@@ -472,75 +504,96 @@ public class AuthorizationStmtTest extends FrontendTestBase {
     // Select with inline view.
     authorize("select a.* from (select * from functional.alltypes) a")
         .ok(onServer(TPrivilegeLevel.ALL))
+        .ok(onServer(TPrivilegeLevel.OWNER))
         .ok(onServer(TPrivilegeLevel.SELECT))
         .ok(onDatabase("functional", TPrivilegeLevel.ALL))
+        .ok(onDatabase("functional", TPrivilegeLevel.OWNER))
         .ok(onDatabase("functional", TPrivilegeLevel.SELECT))
         .ok(onTable("functional", "alltypes", TPrivilegeLevel.ALL))
+        .ok(onTable("functional", "alltypes", TPrivilegeLevel.OWNER))
         .ok(onTable("functional", "alltypes", TPrivilegeLevel.SELECT))
         .ok(onColumn("functional", "alltypes", ALLTYPES_COLUMNS, TPrivilegeLevel.SELECT))
         .error(selectError("functional.alltypes"))
         .error(selectError("functional.alltypes"), onServer(allExcept(
-            TPrivilegeLevel.ALL, TPrivilegeLevel.SELECT)))
+            TPrivilegeLevel.ALL, TPrivilegeLevel.OWNER, TPrivilegeLevel.SELECT)))
         .error(selectError("functional.alltypes"), onDatabase("functional",
-            allExcept(TPrivilegeLevel.ALL, TPrivilegeLevel.SELECT)))
+            allExcept(TPrivilegeLevel.ALL, TPrivilegeLevel.OWNER,
+            TPrivilegeLevel.SELECT)))
         .error(selectError("functional.alltypes"), onTable("functional", "alltypes",
-            allExcept(TPrivilegeLevel.ALL, TPrivilegeLevel.SELECT)));
+            allExcept(TPrivilegeLevel.ALL, TPrivilegeLevel.OWNER,
+            TPrivilegeLevel.SELECT)));
 
     // Select with columns referenced in function, where clause and group by.
     authorize("select count(id), int_col from functional.alltypes where id = 10 " +
         "group by id, int_col")
         .ok(onServer(TPrivilegeLevel.ALL))
+        .ok(onServer(TPrivilegeLevel.OWNER))
         .ok(onServer(TPrivilegeLevel.SELECT))
         .ok(onDatabase("functional", TPrivilegeLevel.ALL))
+        .ok(onDatabase("functional", TPrivilegeLevel.OWNER))
         .ok(onDatabase("functional", TPrivilegeLevel.SELECT))
         .ok(onTable("functional", "alltypes", TPrivilegeLevel.ALL))
+        .ok(onTable("functional", "alltypes", TPrivilegeLevel.OWNER))
         .ok(onTable("functional", "alltypes", TPrivilegeLevel.SELECT))
         .ok(onColumn("functional", "alltypes", new String[]{"id", "int_col"},
             TPrivilegeLevel.SELECT))
         .error(selectError("functional.alltypes"))
         .error(selectError("functional.alltypes"), onServer(allExcept(
-            TPrivilegeLevel.ALL, TPrivilegeLevel.SELECT)))
+            TPrivilegeLevel.ALL, TPrivilegeLevel.OWNER, TPrivilegeLevel.SELECT)))
         .error(selectError("functional.alltypes"), onDatabase("functional",
-            allExcept(TPrivilegeLevel.ALL, TPrivilegeLevel.SELECT)))
+            allExcept(TPrivilegeLevel.ALL, TPrivilegeLevel.OWNER,
+            TPrivilegeLevel.SELECT)))
         .error(selectError("functional.alltypes"), onTable("functional", "alltypes",
-            allExcept(TPrivilegeLevel.ALL, TPrivilegeLevel.SELECT)));
+            allExcept(TPrivilegeLevel.ALL, TPrivilegeLevel.OWNER,
+            TPrivilegeLevel.SELECT)));
 
     // Select on tables with complex types.
     authorize("select a.int_struct_col.f1 from functional.allcomplextypes a " +
         "where a.id = 1")
         .ok(onServer(TPrivilegeLevel.ALL))
+        .ok(onServer(TPrivilegeLevel.OWNER))
         .ok(onServer(TPrivilegeLevel.SELECT))
         .ok(onDatabase("functional", TPrivilegeLevel.ALL))
+        .ok(onDatabase("functional", TPrivilegeLevel.OWNER))
         .ok(onDatabase("functional", TPrivilegeLevel.SELECT))
         .ok(onTable("functional", "allcomplextypes", TPrivilegeLevel.ALL))
+        .ok(onTable("functional", "allcomplextypes", TPrivilegeLevel.OWNER))
         .ok(onTable("functional", "allcomplextypes", TPrivilegeLevel.SELECT))
         .ok(onColumn("functional", "allcomplextypes",
             new String[]{"id", "int_struct_col"}, TPrivilegeLevel.SELECT))
         .error(selectError("functional.allcomplextypes"))
         .error(selectError("functional.allcomplextypes"), onServer(
-            allExcept(TPrivilegeLevel.ALL, TPrivilegeLevel.SELECT)))
+            allExcept(TPrivilegeLevel.ALL, TPrivilegeLevel.OWNER,
+            TPrivilegeLevel.SELECT)))
         .error(selectError("functional.allcomplextypes"), onDatabase("functional",
-            allExcept(TPrivilegeLevel.ALL, TPrivilegeLevel.SELECT)))
+            allExcept(TPrivilegeLevel.ALL, TPrivilegeLevel.OWNER,
+            TPrivilegeLevel.SELECT)))
         .error(selectError("functional.allcomplextypes"), onTable("functional",
-            "allcomplextypes", allExcept(TPrivilegeLevel.ALL, TPrivilegeLevel.SELECT)));
+            "allcomplextypes", allExcept(TPrivilegeLevel.ALL, TPrivilegeLevel.OWNER,
+            TPrivilegeLevel.SELECT)));
 
     authorize("select key, pos, item.f1, f2 from functional.allcomplextypes t, " +
         "t.struct_array_col, functional.allcomplextypes.int_map_col")
         .ok(onServer(TPrivilegeLevel.ALL))
+        .ok(onServer(TPrivilegeLevel.OWNER))
         .ok(onServer(TPrivilegeLevel.SELECT))
         .ok(onDatabase("functional", TPrivilegeLevel.ALL))
+        .ok(onDatabase("functional", TPrivilegeLevel.OWNER))
         .ok(onDatabase("functional", TPrivilegeLevel.SELECT))
         .ok(onTable("functional", "allcomplextypes", TPrivilegeLevel.ALL))
+        .ok(onTable("functional", "allcomplextypes", TPrivilegeLevel.OWNER))
         .ok(onTable("functional", "allcomplextypes", TPrivilegeLevel.SELECT))
         .ok(onColumn("functional", "allcomplextypes",
             new String[]{"struct_array_col", "int_map_col"}, TPrivilegeLevel.SELECT))
         .error(selectError("functional.allcomplextypes"))
         .error(selectError("functional.allcomplextypes"), onServer(allExcept(
-            TPrivilegeLevel.ALL, TPrivilegeLevel.SELECT)))
+            TPrivilegeLevel.ALL, TPrivilegeLevel.OWNER, TPrivilegeLevel.SELECT)))
         .error(selectError("functional.allcomplextypes"), onDatabase("functional",
-            allExcept(TPrivilegeLevel.ALL, TPrivilegeLevel.SELECT)))
+            allExcept(TPrivilegeLevel.ALL, TPrivilegeLevel.OWNER,
+            TPrivilegeLevel.SELECT)))
         .error(selectError("functional.allcomplextypes"), onTable("functional",
-            "allcomplextypes", allExcept(TPrivilegeLevel.ALL, TPrivilegeLevel.SELECT)));
+            "allcomplextypes", allExcept(TPrivilegeLevel.ALL, TPrivilegeLevel.OWNER,
+            TPrivilegeLevel.SELECT)));
 
     for (AuthzTest authzTest: new AuthzTest[]{
         // Select with cross join.
@@ -550,15 +603,23 @@ public class AuthorizationStmtTest extends FrontendTestBase {
         authorize("select * from functional.alltypes a cross join " +
             "functional.alltypessmall b")}) {
       authzTest.ok(onServer(TPrivilegeLevel.ALL))
+          .ok(onServer(TPrivilegeLevel.OWNER))
           .ok(onServer(TPrivilegeLevel.SELECT))
           .ok(onDatabase("functional", TPrivilegeLevel.ALL))
+          .ok(onDatabase("functional", TPrivilegeLevel.OWNER))
           .ok(onDatabase("functional", TPrivilegeLevel.SELECT))
           .ok(onTable("functional", "alltypes", TPrivilegeLevel.ALL),
               onTable("functional", "alltypessmall", TPrivilegeLevel.ALL))
+          .ok(onTable("functional", "alltypes", TPrivilegeLevel.OWNER),
+              onTable("functional", "alltypessmall", TPrivilegeLevel.OWNER))
           .ok(onTable("functional", "alltypes", TPrivilegeLevel.SELECT),
               onTable("functional", "alltypessmall", TPrivilegeLevel.ALL))
+          .ok(onTable("functional", "alltypes", TPrivilegeLevel.SELECT),
+              onTable("functional", "alltypessmall", TPrivilegeLevel.OWNER))
           .ok(onTable("functional", "alltypes", TPrivilegeLevel.ALL),
               onTable("functional", "alltypessmall", TPrivilegeLevel.SELECT))
+          .ok(onTable("functional", "alltypes", TPrivilegeLevel.OWNER),
+              onTable("functional", "alltypessmall", TPrivilegeLevel.SELECT))
           .ok(onTable("functional", "alltypes", TPrivilegeLevel.SELECT),
               onTable("functional", "alltypessmall", TPrivilegeLevel.SELECT))
           .ok(onColumn("functional", "alltypes", ALLTYPES_COLUMNS,
@@ -566,12 +627,15 @@ public class AuthorizationStmtTest extends FrontendTestBase {
               ALLTYPES_COLUMNS, TPrivilegeLevel.SELECT))
           .error(selectError("functional.alltypes"))
           .error(selectError("functional.alltypes"), onServer(
-              allExcept(TPrivilegeLevel.ALL, TPrivilegeLevel.SELECT)))
+              allExcept(TPrivilegeLevel.ALL, TPrivilegeLevel.OWNER,
+              TPrivilegeLevel.SELECT)))
           .error(selectError("functional.alltypes"), onDatabase("functional",
-              allExcept(TPrivilegeLevel.ALL, TPrivilegeLevel.SELECT)))
+              allExcept(TPrivilegeLevel.ALL, TPrivilegeLevel.OWNER,
+              TPrivilegeLevel.SELECT)))
           .error(selectError("functional.alltypes"), onTable("functional", "alltypes",
-              allExcept(TPrivilegeLevel.ALL, TPrivilegeLevel.SELECT)),
-              onTable("functional", "alltypessmall", allExcept(TPrivilegeLevel.ALL,
+              allExcept(TPrivilegeLevel.ALL, TPrivilegeLevel.OWNER,
+              TPrivilegeLevel.SELECT)), onTable("functional", "alltypessmall",
+              allExcept(TPrivilegeLevel.ALL, TPrivilegeLevel.OWNER,
               TPrivilegeLevel.SELECT)))
           .error(selectError("functional.alltypessmall"), onColumn("functional",
               "alltypes", ALLTYPES_COLUMNS, TPrivilegeLevel.SELECT))
@@ -584,23 +648,30 @@ public class AuthorizationStmtTest extends FrontendTestBase {
     authorize("select id from functional.alltypes_view union all " +
         "select x from functional.alltypes_view_sub")
         .ok(onServer(TPrivilegeLevel.ALL))
+        .ok(onServer(TPrivilegeLevel.OWNER))
         .ok(onServer(TPrivilegeLevel.SELECT))
         .ok(onDatabase("functional", TPrivilegeLevel.ALL))
+        .ok(onDatabase("functional", TPrivilegeLevel.OWNER))
         .ok(onDatabase("functional", TPrivilegeLevel.SELECT))
         .ok(onTable("functional", "alltypes_view", TPrivilegeLevel.ALL),
             onTable("functional", "alltypes_view_sub", TPrivilegeLevel.ALL))
+        .ok(onTable("functional", "alltypes_view", TPrivilegeLevel.OWNER),
+            onTable("functional", "alltypes_view_sub", TPrivilegeLevel.OWNER))
         .ok(onTable("functional", "alltypes_view", TPrivilegeLevel.SELECT),
             onTable("functional", "alltypes_view_sub", TPrivilegeLevel.SELECT))
         .error(selectError("functional.alltypes_view"))
         .error(selectError("functional.alltypes_view"), onServer(allExcept(
-            TPrivilegeLevel.ALL, TPrivilegeLevel.SELECT)))
+            TPrivilegeLevel.ALL, TPrivilegeLevel.OWNER, TPrivilegeLevel.SELECT)))
         .error(selectError("functional.alltypes_view"), onDatabase("functional",
-            allExcept(TPrivilegeLevel.ALL, TPrivilegeLevel.SELECT)))
+            allExcept(TPrivilegeLevel.ALL, TPrivilegeLevel.OWNER,
+            TPrivilegeLevel.SELECT)))
         .error(selectError("functional.alltypes_view"), onTable("functional",
-            "alltypes_view", allExcept(TPrivilegeLevel.ALL, TPrivilegeLevel.SELECT)),
+            "alltypes_view", allExcept(TPrivilegeLevel.ALL, TPrivilegeLevel.OWNER,
+            TPrivilegeLevel.SELECT)),
             onTable("functional", "alltypes_view_sub", TPrivilegeLevel.SELECT))
         .error(selectError("functional.alltypes_view_sub"), onTable("functional",
-            "alltypes_view_sub", allExcept(TPrivilegeLevel.ALL, TPrivilegeLevel.SELECT)),
+            "alltypes_view_sub", allExcept(TPrivilegeLevel.ALL, TPrivilegeLevel.OWNER,
+            TPrivilegeLevel.SELECT)),
             onTable("functional", "alltypes_view", TPrivilegeLevel.SELECT));
 
     // Union from non-existent databases.
@@ -620,18 +691,23 @@ public class AuthorizationStmtTest extends FrontendTestBase {
         // Explain insert.
         authorize("explain insert into functional.zipcode_incomes(id) values('123')")}) {
       test.ok(onServer(TPrivilegeLevel.ALL))
+          .ok(onServer(TPrivilegeLevel.OWNER))
           .ok(onServer(TPrivilegeLevel.INSERT))
           .ok(onDatabase("functional", TPrivilegeLevel.ALL))
+          .ok(onDatabase("functional", TPrivilegeLevel.OWNER))
           .ok(onDatabase("functional", TPrivilegeLevel.INSERT))
           .ok(onTable("functional", "zipcode_incomes", TPrivilegeLevel.ALL))
+          .ok(onTable("functional", "zipcode_incomes", TPrivilegeLevel.OWNER))
           .ok(onTable("functional", "zipcode_incomes", TPrivilegeLevel.INSERT))
           .error(insertError("functional.zipcode_incomes"))
           .error(insertError("functional.zipcode_incomes"), onServer(allExcept(
-              TPrivilegeLevel.ALL, TPrivilegeLevel.INSERT)))
+              TPrivilegeLevel.ALL, TPrivilegeLevel.OWNER, TPrivilegeLevel.INSERT)))
           .error(insertError("functional.zipcode_incomes"), onDatabase("functional",
-              allExcept(TPrivilegeLevel.ALL, TPrivilegeLevel.INSERT)))
+              allExcept(TPrivilegeLevel.ALL, TPrivilegeLevel.OWNER,
+              TPrivilegeLevel.INSERT)))
           .error(insertError("functional.zipcode_incomes"), onTable("functional",
-              "zipcode_incomes", allExcept(TPrivilegeLevel.ALL, TPrivilegeLevel.INSERT)));
+              "zipcode_incomes", allExcept(TPrivilegeLevel.ALL, TPrivilegeLevel.OWNER,
+              TPrivilegeLevel.INSERT)));
     }
 
     for (AuthzTest test: new AuthzTest[]{
@@ -643,11 +719,15 @@ public class AuthorizationStmtTest extends FrontendTestBase {
         authorize("insert into functional.alltypes partition(month, year) " +
             "select * from functional.alltypestiny where id < 100")}) {
       test.ok(onServer(TPrivilegeLevel.ALL))
+          .ok(onServer(TPrivilegeLevel.OWNER))
           .ok(onServer(TPrivilegeLevel.INSERT, TPrivilegeLevel.SELECT))
           .ok(onDatabase("functional", TPrivilegeLevel.ALL))
+          .ok(onDatabase("functional", TPrivilegeLevel.OWNER))
           .ok(onDatabase("functional", TPrivilegeLevel.INSERT, TPrivilegeLevel.SELECT))
           .ok(onTable("functional", "alltypes", TPrivilegeLevel.ALL),
               onTable("functional", "alltypestiny", TPrivilegeLevel.ALL))
+          .ok(onTable("functional", "alltypes", TPrivilegeLevel.OWNER),
+              onTable("functional", "alltypestiny", TPrivilegeLevel.OWNER))
           .ok(onTable("functional", "alltypes", TPrivilegeLevel.INSERT),
               onTable("functional", "alltypestiny", TPrivilegeLevel.SELECT))
           .ok(onTable("functional", "alltypes", TPrivilegeLevel.INSERT),
@@ -655,16 +735,19 @@ public class AuthorizationStmtTest extends FrontendTestBase {
               TPrivilegeLevel.SELECT))
           .error(selectError("functional.alltypestiny"))
           .error(selectError("functional.alltypestiny"), onServer(allExcept(
-              TPrivilegeLevel.ALL, TPrivilegeLevel.INSERT, TPrivilegeLevel.SELECT)))
-          .error(selectError("functional.alltypestiny"), onDatabase("functional",
-              allExcept(TPrivilegeLevel.ALL, TPrivilegeLevel.INSERT,
+              TPrivilegeLevel.ALL, TPrivilegeLevel.OWNER, TPrivilegeLevel.INSERT,
               TPrivilegeLevel.SELECT)))
+          .error(selectError("functional.alltypestiny"), onDatabase("functional",
+              allExcept(TPrivilegeLevel.ALL, TPrivilegeLevel.OWNER,
+              TPrivilegeLevel.INSERT, TPrivilegeLevel.SELECT)))
           .error(insertError("functional.alltypes"), onTable("functional",
               "alltypestiny", TPrivilegeLevel.SELECT), onTable("functional",
-              "alltypes", allExcept(TPrivilegeLevel.ALL, TPrivilegeLevel.INSERT)))
+              "alltypes", allExcept(TPrivilegeLevel.ALL, TPrivilegeLevel.OWNER,
+              TPrivilegeLevel.INSERT)))
           .error(selectError("functional.alltypestiny"), onTable("functional",
-              "alltypestiny", allExcept(TPrivilegeLevel.ALL, TPrivilegeLevel.SELECT)),
-              onTable("functional", "alltypes", TPrivilegeLevel.INSERT));
+              "alltypestiny", allExcept(TPrivilegeLevel.ALL, TPrivilegeLevel.OWNER,
+              TPrivilegeLevel.SELECT)), onTable("functional", "alltypes",
+              TPrivilegeLevel.INSERT));
     }
 
     // Insert with select on a target view.
@@ -672,24 +755,31 @@ public class AuthorizationStmtTest extends FrontendTestBase {
     authorize("insert into functional.alltypes partition(month, year) " +
         "select * from functional.alltypes_view where id < 100")
         .ok(onServer(TPrivilegeLevel.ALL))
+        .ok(onServer(TPrivilegeLevel.OWNER))
         .ok(onServer(TPrivilegeLevel.INSERT, TPrivilegeLevel.SELECT))
         .ok(onDatabase("functional", TPrivilegeLevel.ALL))
+        .ok(onDatabase("functional", TPrivilegeLevel.OWNER))
         .ok(onDatabase("functional", TPrivilegeLevel.INSERT, TPrivilegeLevel.SELECT))
         .ok(onTable("functional", "alltypes", TPrivilegeLevel.ALL),
             onTable("functional", "alltypes_view", TPrivilegeLevel.ALL))
+        .ok(onTable("functional", "alltypes", TPrivilegeLevel.OWNER),
+            onTable("functional", "alltypes_view", TPrivilegeLevel.OWNER))
         .ok(onTable("functional", "alltypes", TPrivilegeLevel.INSERT),
             onTable("functional", "alltypes_view", TPrivilegeLevel.SELECT))
         .error(selectError("functional.alltypes_view"))
         .error(selectError("functional.alltypes_view"), onServer(allExcept(
-            TPrivilegeLevel.ALL, TPrivilegeLevel.INSERT, TPrivilegeLevel.SELECT)))
+            TPrivilegeLevel.ALL, TPrivilegeLevel.OWNER, TPrivilegeLevel.INSERT,
+            TPrivilegeLevel.SELECT)))
         .error(selectError("functional.alltypes_view"), onDatabase("functional",
-            allExcept(TPrivilegeLevel.ALL, TPrivilegeLevel.INSERT,
+            allExcept(TPrivilegeLevel.ALL, TPrivilegeLevel.OWNER, TPrivilegeLevel.INSERT,
             TPrivilegeLevel.SELECT)))
         .error(insertError("functional.alltypes"), onTable("functional",
             "alltypes_view", TPrivilegeLevel.SELECT), onTable("functional",
-            "alltypes", allExcept(TPrivilegeLevel.ALL, TPrivilegeLevel.INSERT)))
+            "alltypes", allExcept(TPrivilegeLevel.ALL, TPrivilegeLevel.OWNER,
+            TPrivilegeLevel.INSERT)))
         .error(selectError("functional.alltypes_view"), onTable("functional",
-            "alltypes_view", allExcept(TPrivilegeLevel.ALL, TPrivilegeLevel.SELECT)),
+            "alltypes_view", allExcept(TPrivilegeLevel.ALL, TPrivilegeLevel.OWNER,
+            TPrivilegeLevel.SELECT)),
             onTable("functional", "alltypes", TPrivilegeLevel.INSERT));
 
     // Insert with inline view.
@@ -697,31 +787,41 @@ public class AuthorizationStmtTest extends FrontendTestBase {
         "select b.* from functional.alltypesagg a join (select * from " +
         "functional.alltypestiny) b on (a.int_col = b.int_col)")
         .ok(onServer(TPrivilegeLevel.ALL))
+        .ok(onServer(TPrivilegeLevel.OWNER))
         .ok(onServer(TPrivilegeLevel.INSERT, TPrivilegeLevel.SELECT))
         .ok(onDatabase("functional", TPrivilegeLevel.ALL))
+        .ok(onDatabase("functional", TPrivilegeLevel.OWNER))
         .ok(onDatabase("functional", TPrivilegeLevel.INSERT, TPrivilegeLevel.SELECT))
         .ok(onTable("functional", "alltypes", TPrivilegeLevel.ALL),
             onTable("functional", "alltypesagg", TPrivilegeLevel.ALL),
             onTable("functional", "alltypestiny", TPrivilegeLevel.ALL))
+        .ok(onTable("functional", "alltypes", TPrivilegeLevel.OWNER),
+            onTable("functional", "alltypesagg", TPrivilegeLevel.OWNER),
+            onTable("functional", "alltypestiny", TPrivilegeLevel.OWNER))
         .ok(onTable("functional", "alltypes", TPrivilegeLevel.INSERT),
             onTable("functional", "alltypesagg", TPrivilegeLevel.SELECT),
             onTable("functional", "alltypestiny", TPrivilegeLevel.SELECT))
         .error(selectError("functional.alltypesagg"))
         .error(selectError("functional.alltypesagg"), onServer(allExcept(
-            TPrivilegeLevel.ALL, TPrivilegeLevel.INSERT, TPrivilegeLevel.SELECT)))
+            TPrivilegeLevel.ALL, TPrivilegeLevel.OWNER, TPrivilegeLevel.INSERT,
+            TPrivilegeLevel.SELECT)))
         .error(selectError("functional.alltypesagg"), onDatabase("functional", allExcept(
-            TPrivilegeLevel.ALL, TPrivilegeLevel.INSERT, TPrivilegeLevel.SELECT)))
+            TPrivilegeLevel.ALL, TPrivilegeLevel.OWNER, TPrivilegeLevel.INSERT,
+            TPrivilegeLevel.SELECT)))
         .error(insertError("functional.alltypes"), onTable("functional",
             "alltypesagg", TPrivilegeLevel.SELECT), onTable("functional",
             "alltypestiny", TPrivilegeLevel.SELECT), onTable("functional",
-            "alltypes", allExcept(TPrivilegeLevel.ALL, TPrivilegeLevel.INSERT)))
+            "alltypes", allExcept(TPrivilegeLevel.ALL, TPrivilegeLevel.OWNER,
+            TPrivilegeLevel.INSERT)))
         .error(selectError("functional.alltypesagg"), onTable("functional",
-            "alltypesagg", allExcept(TPrivilegeLevel.ALL, TPrivilegeLevel.SELECT)),
+            "alltypesagg", allExcept(TPrivilegeLevel.ALL, TPrivilegeLevel.OWNER,
+            TPrivilegeLevel.SELECT)),
             onTable("functional", "alltypestiny", TPrivilegeLevel.SELECT),
             onTable("functional", "alltypes", TPrivilegeLevel.INSERT))
         .error(selectError("functional.alltypestiny"), onTable("functional",
             "alltypesagg", TPrivilegeLevel.SELECT), onTable("functional",
-            "alltypestiny", allExcept(TPrivilegeLevel.ALL, TPrivilegeLevel.SELECT)),
+            "alltypestiny", allExcept(TPrivilegeLevel.ALL, TPrivilegeLevel.OWNER,
+            TPrivilegeLevel.SELECT)),
             onTable("functional", "alltypes", TPrivilegeLevel.INSERT));
 
     // Inserting into a view is not allowed.
@@ -763,18 +863,22 @@ public class AuthorizationStmtTest extends FrontendTestBase {
     // Truncate a table.
     authorize("truncate table functional.alltypes")
         .ok(onServer(TPrivilegeLevel.ALL))
+        .ok(onServer(TPrivilegeLevel.OWNER))
         .ok(onServer(TPrivilegeLevel.INSERT))
         .ok(onDatabase("functional", TPrivilegeLevel.ALL))
+        .ok(onDatabase("functional", TPrivilegeLevel.OWNER))
         .ok(onDatabase("functional", TPrivilegeLevel.INSERT))
         .ok(onTable("functional", "alltypes", TPrivilegeLevel.ALL))
+        .ok(onTable("functional", "alltypes", TPrivilegeLevel.OWNER))
         .ok(onTable("functional", "alltypes", TPrivilegeLevel.INSERT))
         .error(insertError("functional.alltypes"))
         .error(insertError("functional.alltypes"), onServer(allExcept(
-            TPrivilegeLevel.ALL, TPrivilegeLevel.INSERT)))
+            TPrivilegeLevel.ALL, TPrivilegeLevel.OWNER, TPrivilegeLevel.INSERT)))
         .error(insertError("functional.alltypes"), onDatabase("functional", allExcept(
-            TPrivilegeLevel.ALL, TPrivilegeLevel.INSERT)))
+            TPrivilegeLevel.ALL, TPrivilegeLevel.OWNER, TPrivilegeLevel.INSERT)))
         .error(insertError("functional.alltypes"), onTable("functional", "alltypes",
-            allExcept(TPrivilegeLevel.ALL, TPrivilegeLevel.INSERT)));
+            allExcept(TPrivilegeLevel.ALL, TPrivilegeLevel.OWNER,
+            TPrivilegeLevel.INSERT)));
 
     // Truncate a non-existent database.
     authorize("truncate table nodb.alltypes")
@@ -795,18 +899,31 @@ public class AuthorizationStmtTest extends FrontendTestBase {
     authorize("load data inpath 'hdfs://localhost:20500/test-warehouse/tpch.lineitem' " +
         "into table functional.alltypes partition(month=10, year=2009)")
       .ok(onServer(TPrivilegeLevel.ALL))
+      .ok(onServer(TPrivilegeLevel.OWNER))
       .ok(onDatabase("functional", TPrivilegeLevel.ALL),
           onUri("hdfs://localhost:20500/test-warehouse/tpch.lineitem",
           TPrivilegeLevel.ALL))
+      .ok(onDatabase("functional", TPrivilegeLevel.OWNER),
+          onUri("hdfs://localhost:20500/test-warehouse/tpch.lineitem",
+          TPrivilegeLevel.OWNER))
       .ok(onDatabase("functional", TPrivilegeLevel.INSERT),
           onUri("hdfs://localhost:20500/test-warehouse/tpch.lineitem",
           TPrivilegeLevel.ALL))
+      .ok(onDatabase("functional", TPrivilegeLevel.INSERT),
+          onUri("hdfs://localhost:20500/test-warehouse/tpch.lineitem",
+          TPrivilegeLevel.OWNER))
       .ok(onTable("functional", "alltypes", TPrivilegeLevel.ALL),
           onUri("hdfs://localhost:20500/test-warehouse/tpch.lineitem",
           TPrivilegeLevel.ALL))
+      .ok(onTable("functional", "alltypes", TPrivilegeLevel.OWNER),
+          onUri("hdfs://localhost:20500/test-warehouse/tpch.lineitem",
+          TPrivilegeLevel.OWNER))
       .ok(onTable("functional", "alltypes", TPrivilegeLevel.INSERT),
           onUri("hdfs://localhost:20500/test-warehouse/tpch.lineitem",
           TPrivilegeLevel.ALL))
+      .ok(onTable("functional", "alltypes", TPrivilegeLevel.INSERT),
+          onUri("hdfs://localhost:20500/test-warehouse/tpch.lineitem",
+          TPrivilegeLevel.OWNER))
       .error(insertError("functional.alltypes"))
       .error(accessError("hdfs://localhost:20500/test-warehouse/tpch.lineitem"),
           onDatabase("functional", TPrivilegeLevel.INSERT))
@@ -814,7 +931,10 @@ public class AuthorizationStmtTest extends FrontendTestBase {
           onTable("functional", "alltypes", TPrivilegeLevel.INSERT))
       .error(insertError("functional.alltypes"),
           onUri("hdfs://localhost:20500/test-warehouse/tpch.lineitem",
-          TPrivilegeLevel.ALL));
+          TPrivilegeLevel.ALL))
+      .error(insertError("functional.alltypes"),
+          onUri("hdfs://localhost:20500/test-warehouse/tpch.lineitem",
+          TPrivilegeLevel.OWNER));
 
     // Load from non-existent URI.
     authorize("load data inpath 'hdfs://localhost:20500/test-warehouse/nouri' " +
@@ -830,14 +950,18 @@ public class AuthorizationStmtTest extends FrontendTestBase {
         "into table nodb.alltypes partition(month=10, year=2009)")
         .error(insertError("nodb.alltypes"))
         .error(insertError("nodb.alltypes"), onUri(
-            "hdfs://localhost:20500/test-warehouse/tpch.nouri", TPrivilegeLevel.ALL));
+            "hdfs://localhost:20500/test-warehouse/tpch.nouri", TPrivilegeLevel.ALL))
+        .error(insertError("nodb.alltypes"), onUri(
+            "hdfs://localhost:20500/test-warehouse/tpch.nouri", TPrivilegeLevel.OWNER));
 
     // Load into non-existent table.
     authorize("load data inpath 'hdfs://localhost:20500/test-warehouse/tpch.lineitem' " +
         "into table functional.notbl partition(month=10, year=2009)")
         .error(insertError("functional.notbl"))
         .error(insertError("functional.notbl"), onUri(
-            "hdfs://localhost:20500/test-warehouse/tpch.nouri", TPrivilegeLevel.ALL));
+            "hdfs://localhost:20500/test-warehouse/tpch.nouri", TPrivilegeLevel.ALL))
+        .error(insertError("functional.notbl"), onUri(
+            "hdfs://localhost:20500/test-warehouse/tpch.nouri", TPrivilegeLevel.OWNER));
 
     // Load into a view is not supported.
     authorize("load data inpath 'hdfs://localhost:20500/test-warehouse/tpch.lineitem' " +
@@ -850,6 +974,7 @@ public class AuthorizationStmtTest extends FrontendTestBase {
     // Invalidate metadata on server.
     authorize("invalidate metadata")
         .ok(onServer(TPrivilegeLevel.ALL))
+        .ok(onServer(TPrivilegeLevel.OWNER))
         .ok(onServer(TPrivilegeLevel.REFRESH))
         .error(refreshError("server"));
 
@@ -859,28 +984,33 @@ public class AuthorizationStmtTest extends FrontendTestBase {
           authorize("invalidate metadata functional." + name),
           authorize("refresh functional." + name)}) {
         test.ok(onServer(TPrivilegeLevel.ALL))
+            .ok(onServer(TPrivilegeLevel.OWNER))
             .ok(onServer(TPrivilegeLevel.REFRESH))
             .ok(onDatabase("functional", TPrivilegeLevel.ALL))
+            .ok(onDatabase("functional", TPrivilegeLevel.OWNER))
             .ok(onDatabase("functional", TPrivilegeLevel.REFRESH))
             .ok(onTable("functional", name, TPrivilegeLevel.ALL))
+            .ok(onTable("functional", name, TPrivilegeLevel.OWNER))
             .ok(onTable("functional", name, TPrivilegeLevel.REFRESH))
             .error(refreshError("functional." + name))
             .error(refreshError("functional." + name), onDatabase("functional", allExcept(
-                TPrivilegeLevel.ALL, TPrivilegeLevel.REFRESH)))
+                TPrivilegeLevel.ALL, TPrivilegeLevel.OWNER, TPrivilegeLevel.REFRESH)))
             .error(refreshError("functional." + name), onTable("functional", name,
-                allExcept(TPrivilegeLevel.ALL, TPrivilegeLevel.REFRESH)));
+                allExcept(TPrivilegeLevel.ALL, TPrivilegeLevel.OWNER,
+                TPrivilegeLevel.REFRESH)));
       }
     }
 
     authorize("refresh functions functional")
         .ok(onServer(TPrivilegeLevel.REFRESH))
         .ok(onDatabase("functional", TPrivilegeLevel.ALL))
+        .ok(onDatabase("functional", TPrivilegeLevel.OWNER))
         .ok(onDatabase("functional", TPrivilegeLevel.REFRESH))
         .error(refreshError("functional"))
         .error(refreshError("functional"), onServer(allExcept(TPrivilegeLevel.ALL,
-            TPrivilegeLevel.REFRESH)))
+            TPrivilegeLevel.OWNER, TPrivilegeLevel.REFRESH)))
         .error(refreshError("functional"), onDatabase("functional", allExcept(
-            TPrivilegeLevel.ALL, TPrivilegeLevel.REFRESH)));
+            TPrivilegeLevel.ALL, TPrivilegeLevel.OWNER, TPrivilegeLevel.REFRESH)));
 
     // Reset metadata in non-existent database.
     authorize("invalidate metadata nodb").error(refreshError("default.nodb"));
@@ -1187,69 +1317,81 @@ public class AuthorizationStmtTest extends FrontendTestBase {
     // Compute stats.
     authorize("compute stats functional.alltypes")
         .ok(onServer(TPrivilegeLevel.ALL))
+        .ok(onServer(TPrivilegeLevel.OWNER))
         .ok(onServer(TPrivilegeLevel.ALTER, TPrivilegeLevel.SELECT))
         .ok(onDatabase("functional", TPrivilegeLevel.ALL))
+        .ok(onDatabase("functional", TPrivilegeLevel.OWNER))
         .ok(onDatabase("functional", TPrivilegeLevel.ALTER, TPrivilegeLevel.SELECT))
         .ok(onTable("functional", "alltypes", TPrivilegeLevel.ALL))
+        .ok(onTable("functional", "alltypes", TPrivilegeLevel.OWNER))
         .ok(onTable("functional", "alltypes", TPrivilegeLevel.ALTER,
             TPrivilegeLevel.SELECT))
         .error(alterError("functional.alltypes"))
         .error(alterError("functional.alltypes"), onServer(allExcept(
-            TPrivilegeLevel.ALL, TPrivilegeLevel.ALTER, TPrivilegeLevel.SELECT)))
+            TPrivilegeLevel.ALL, TPrivilegeLevel.OWNER, TPrivilegeLevel.ALTER,
+            TPrivilegeLevel.SELECT)))
         .error(alterError("functional.alltypes"), onDatabase("functional",
-            allExcept(TPrivilegeLevel.ALL, TPrivilegeLevel.ALTER,
+            allExcept(TPrivilegeLevel.ALL, TPrivilegeLevel.OWNER, TPrivilegeLevel.ALTER,
                 TPrivilegeLevel.SELECT)))
         .error(alterError("functional.alltypes"), onTable("functional", "alltypes",
-            allExcept(TPrivilegeLevel.ALL, TPrivilegeLevel.ALTER,
+            allExcept(TPrivilegeLevel.ALL, TPrivilegeLevel.OWNER, TPrivilegeLevel.ALTER,
                 TPrivilegeLevel.SELECT)));
 
     // Compute stats on database that does not exist.
     authorize("compute stats nodb.notbl")
         .error(alterError("nodb.notbl"))
         .error(alterError("nodb.notbl"), onServer(allExcept(
-            TPrivilegeLevel.ALL, TPrivilegeLevel.ALTER, TPrivilegeLevel.SELECT)))
+            TPrivilegeLevel.ALL, TPrivilegeLevel.OWNER, TPrivilegeLevel.ALTER,
+            TPrivilegeLevel.SELECT)))
         .error(alterError("nodb.notbl"), onDatabase("nodb", allExcept(
-            TPrivilegeLevel.ALL, TPrivilegeLevel.ALTER, TPrivilegeLevel.SELECT)));
+            TPrivilegeLevel.ALL, TPrivilegeLevel.OWNER, TPrivilegeLevel.ALTER,
+            TPrivilegeLevel.SELECT)));
 
     // Compute stats on table that does not exist.
     authorize("compute stats functional.notbl")
         .error(alterError("functional.notbl"))
         .error(alterError("functional.notbl"), onServer(allExcept(
-            TPrivilegeLevel.ALL, TPrivilegeLevel.ALTER, TPrivilegeLevel.SELECT)))
+            TPrivilegeLevel.ALL, TPrivilegeLevel.OWNER, TPrivilegeLevel.ALTER,
+            TPrivilegeLevel.SELECT)))
         .error(alterError("functional.notbl"), onDatabase("functional", allExcept(
-            TPrivilegeLevel.ALL, TPrivilegeLevel.ALTER, TPrivilegeLevel.SELECT)));
+            TPrivilegeLevel.ALL, TPrivilegeLevel.OWNER, TPrivilegeLevel.ALTER,
+            TPrivilegeLevel.SELECT)));
 
     // Drop stats.
     authorize("drop stats functional.alltypes")
         .ok(onServer(TPrivilegeLevel.ALL))
+        .ok(onServer(TPrivilegeLevel.OWNER))
         .ok(onServer(TPrivilegeLevel.ALTER))
         .ok(onDatabase("functional", TPrivilegeLevel.ALL))
+        .ok(onDatabase("functional", TPrivilegeLevel.OWNER))
         .ok(onDatabase("functional", TPrivilegeLevel.ALTER))
         .ok(onTable("functional", "alltypes", TPrivilegeLevel.ALL))
+        .ok(onTable("functional", "alltypes", TPrivilegeLevel.OWNER))
         .ok(onTable("functional", "alltypes", TPrivilegeLevel.ALTER))
         .error(alterError("functional.alltypes"))
         .error(alterError("functional.alltypes"), onServer(allExcept(
-            TPrivilegeLevel.ALL, TPrivilegeLevel.ALTER)))
+            TPrivilegeLevel.ALL, TPrivilegeLevel.OWNER, TPrivilegeLevel.ALTER)))
         .error(alterError("functional.alltypes"), onDatabase("functional",
-            allExcept(TPrivilegeLevel.ALL, TPrivilegeLevel.ALTER)))
+            allExcept(TPrivilegeLevel.ALL, TPrivilegeLevel.OWNER, TPrivilegeLevel.ALTER)))
         .error(alterError("functional.alltypes"), onTable("functional", "alltypes",
-            allExcept(TPrivilegeLevel.ALL, TPrivilegeLevel.ALTER)));
+            allExcept(TPrivilegeLevel.ALL, TPrivilegeLevel.OWNER,
+            TPrivilegeLevel.ALTER)));
 
     // Drop stats on database that does not exist.
     authorize("drop stats nodb.notbl")
         .error(alterError("nodb.notbl"))
         .error(alterError("nodb.notbl"), onServer(allExcept(
-            TPrivilegeLevel.ALL, TPrivilegeLevel.ALTER)))
+            TPrivilegeLevel.ALL, TPrivilegeLevel.OWNER, TPrivilegeLevel.ALTER)))
         .error(alterError("nodb.notbl"), onDatabase("nodb", allExcept(
-            TPrivilegeLevel.ALL, TPrivilegeLevel.ALTER)));
+            TPrivilegeLevel.ALL, TPrivilegeLevel.OWNER, TPrivilegeLevel.ALTER)));
 
     // Drop stats on table that does not exist.
     authorize("drop stats functional.notbl")
         .error(alterError("functional.notbl"))
         .error(alterError("functional.notbl"), onServer(allExcept(
-            TPrivilegeLevel.ALL, TPrivilegeLevel.ALTER)))
+            TPrivilegeLevel.ALL, TPrivilegeLevel.OWNER, TPrivilegeLevel.ALTER)))
         .error(alterError("functional.notbl"), onDatabase("functional", allExcept(
-            TPrivilegeLevel.ALL, TPrivilegeLevel.ALTER)));
+            TPrivilegeLevel.ALL, TPrivilegeLevel.OWNER, TPrivilegeLevel.ALTER)));
   }
 
   @Test
@@ -1258,22 +1400,29 @@ public class AuthorizationStmtTest extends FrontendTestBase {
         authorize("create database newdb"),
         authorize("create database if not exists newdb")}) {
       test.ok(onServer(TPrivilegeLevel.ALL))
+          .ok(onServer(TPrivilegeLevel.OWNER))
           .ok(onServer(TPrivilegeLevel.CREATE))
           .error(createError("newdb"))
           .error(createError("newdb"), onServer(allExcept(TPrivilegeLevel.ALL,
-              TPrivilegeLevel.CREATE)));
+              TPrivilegeLevel.OWNER, TPrivilegeLevel.CREATE)));
     }
 
     // Create a database with a specific location.
     authorize("create database newdb location " +
         "'hdfs://localhost:20500/test-warehouse/new_location'")
         .ok(onServer(TPrivilegeLevel.ALL))
+        .ok(onServer(TPrivilegeLevel.OWNER))
         .ok(onServer(TPrivilegeLevel.CREATE), onUri(
             "hdfs://localhost:20500/test-warehouse/new_location", TPrivilegeLevel.ALL))
+        .ok(onServer(TPrivilegeLevel.CREATE), onUri(
+            "hdfs://localhost:20500/test-warehouse/new_location", TPrivilegeLevel.OWNER))
         .error(createError("newdb"))
         .error(createError("newdb"), onServer(allExcept(TPrivilegeLevel.ALL,
-            TPrivilegeLevel.CREATE)), onUri(
+            TPrivilegeLevel.OWNER, TPrivilegeLevel.CREATE)), onUri(
             "hdfs://localhost:20500/test-warehouse/new_location", TPrivilegeLevel.ALL))
+        .error(createError("newdb"), onServer(allExcept(TPrivilegeLevel.ALL,
+            TPrivilegeLevel.OWNER, TPrivilegeLevel.CREATE)), onUri(
+            "hdfs://localhost:20500/test-warehouse/new_location", TPrivilegeLevel.OWNER))
         .error(accessError("hdfs://localhost:20500/test-warehouse/new_location"),
             onServer(TPrivilegeLevel.CREATE));
 
@@ -1283,11 +1432,12 @@ public class AuthorizationStmtTest extends FrontendTestBase {
         authorize("create database if not exists functional")}) {
       test.error(createError("functional"))
           .error(createError("functional"), onServer(allExcept(
-              TPrivilegeLevel.ALL, TPrivilegeLevel.CREATE)));
+              TPrivilegeLevel.ALL, TPrivilegeLevel.OWNER, TPrivilegeLevel.CREATE)));
     }
 
     authorize("create database if not exists _impala_builtins")
-        .error(systemDbError(), onServer(TPrivilegeLevel.ALL));
+        .error(systemDbError(), onServer(TPrivilegeLevel.ALL))
+        .error(systemDbError(), onServer(TPrivilegeLevel.OWNER));
   }
 
   @Test
@@ -1296,20 +1446,24 @@ public class AuthorizationStmtTest extends FrontendTestBase {
         authorize("create table functional.new_table(i int)"),
         authorize("create external table functional.new_table(i int)")}) {
       test.ok(onServer(TPrivilegeLevel.ALL))
+          .ok(onServer(TPrivilegeLevel.OWNER))
           .ok(onServer(TPrivilegeLevel.CREATE))
           .ok(onDatabase("functional", TPrivilegeLevel.ALL))
+          .ok(onDatabase("functional", TPrivilegeLevel.OWNER))
           .ok(onDatabase("functional", TPrivilegeLevel.CREATE))
           .error(createError("functional"), onServer(allExcept(
-              TPrivilegeLevel.ALL, TPrivilegeLevel.CREATE)))
+              TPrivilegeLevel.ALL, TPrivilegeLevel.OWNER, TPrivilegeLevel.CREATE)))
           .error(createError("functional"), onDatabase("functional", allExcept(
-              TPrivilegeLevel.ALL, TPrivilegeLevel.CREATE)));
+              TPrivilegeLevel.ALL, TPrivilegeLevel.OWNER, TPrivilegeLevel.CREATE)));
     }
 
     // Create table like.
     authorize("create table functional.new_table like functional.alltypes")
         .ok(onServer(TPrivilegeLevel.ALL))
+        .ok(onServer(TPrivilegeLevel.OWNER))
         .ok(onServer(join(viewMetadataPrivileges(), TPrivilegeLevel.CREATE)))
         .ok(onDatabase("functional", TPrivilegeLevel.ALL))
+        .ok(onDatabase("functional", TPrivilegeLevel.OWNER))
         .ok(onDatabase("functional", join(viewMetadataPrivileges(),
             TPrivilegeLevel.CREATE)))
         .ok(onDatabase("functional"), onDatabase("functional", TPrivilegeLevel.CREATE),
@@ -1318,9 +1472,10 @@ public class AuthorizationStmtTest extends FrontendTestBase {
         .error(accessError("functional.alltypes"), onServer(allExcept(
             join(viewMetadataPrivileges(), TPrivilegeLevel.CREATE))))
         .error(createError("functional"), onDatabase("functional", allExcept(
-            TPrivilegeLevel.ALL, TPrivilegeLevel.CREATE, TPrivilegeLevel.SELECT)))
+            TPrivilegeLevel.ALL, TPrivilegeLevel.OWNER, TPrivilegeLevel.CREATE,
+            TPrivilegeLevel.SELECT)))
         .error(createError("functional"), onDatabase("functional", allExcept(
-            TPrivilegeLevel.ALL, TPrivilegeLevel.CREATE)), onTable(
+            TPrivilegeLevel.ALL, TPrivilegeLevel.OWNER, TPrivilegeLevel.CREATE)), onTable(
                 "functional", "alltypes", viewMetadataPrivileges()))
         .error(accessError("functional.alltypes"), onDatabase("functional",
             TPrivilegeLevel.CREATE), onTable("functional", "alltypes", allExcept(
@@ -1332,9 +1487,9 @@ public class AuthorizationStmtTest extends FrontendTestBase {
         authorize("create table if not exists functional.alltypes(i int)")}) {
       test.error(createError("functional"))
           .error(createError("functional"), onServer(allExcept(
-          TPrivilegeLevel.ALL, TPrivilegeLevel.CREATE)))
+          TPrivilegeLevel.ALL, TPrivilegeLevel.OWNER, TPrivilegeLevel.CREATE)))
           .error(createError("functional"), onDatabase("functional", allExcept(
-              TPrivilegeLevel.ALL, TPrivilegeLevel.CREATE)));
+              TPrivilegeLevel.ALL, TPrivilegeLevel.OWNER, TPrivilegeLevel.CREATE)));
     }
 
     // CTAS.
@@ -1345,9 +1500,11 @@ public class AuthorizationStmtTest extends FrontendTestBase {
         authorize("explain create table functional.new_table as " +
             "select int_col from functional.alltypes")}) {
       test.ok(onServer(TPrivilegeLevel.ALL))
+          .ok(onServer(TPrivilegeLevel.OWNER))
           .ok(onServer(TPrivilegeLevel.CREATE, TPrivilegeLevel.INSERT,
               TPrivilegeLevel.SELECT))
           .ok(onDatabase("functional", TPrivilegeLevel.ALL))
+          .ok(onDatabase("functional", TPrivilegeLevel.OWNER))
           .ok(onDatabase("functional", TPrivilegeLevel.CREATE, TPrivilegeLevel.INSERT,
               TPrivilegeLevel.SELECT))
           .ok(onDatabase("functional"), onDatabase("functional", TPrivilegeLevel.CREATE,
@@ -1356,32 +1513,44 @@ public class AuthorizationStmtTest extends FrontendTestBase {
           .ok(onDatabase("functional"), onDatabase("functional", TPrivilegeLevel.CREATE,
               TPrivilegeLevel.INSERT), onColumn("functional", "alltypes", "int_col",
               TPrivilegeLevel.ALL))
+          .ok(onDatabase("functional"), onDatabase("functional", TPrivilegeLevel.CREATE,
+              TPrivilegeLevel.INSERT), onColumn("functional", "alltypes", "int_col",
+              TPrivilegeLevel.OWNER))
           .error(createError("functional"))
           .error(createError("functional"), onServer(allExcept(
-              TPrivilegeLevel.ALL, TPrivilegeLevel.CREATE, TPrivilegeLevel.INSERT,
-              TPrivilegeLevel.SELECT)))
+              TPrivilegeLevel.ALL, TPrivilegeLevel.OWNER, TPrivilegeLevel.CREATE,
+              TPrivilegeLevel.INSERT, TPrivilegeLevel.SELECT)))
           .error(createError("functional"), onDatabase("functional", allExcept(
-              TPrivilegeLevel.ALL, TPrivilegeLevel.CREATE, TPrivilegeLevel.INSERT,
-              TPrivilegeLevel.SELECT)))
+              TPrivilegeLevel.ALL, TPrivilegeLevel.OWNER, TPrivilegeLevel.CREATE,
+              TPrivilegeLevel.INSERT, TPrivilegeLevel.SELECT)))
           .error(createError("functional"), onDatabase("functional", allExcept(
-              TPrivilegeLevel.ALL, TPrivilegeLevel.CREATE, TPrivilegeLevel.INSERT)),
+              TPrivilegeLevel.ALL, TPrivilegeLevel.OWNER, TPrivilegeLevel.CREATE,
+              TPrivilegeLevel.INSERT)),
               onTable("functional", "alltypes", TPrivilegeLevel.SELECT))
           .error(selectError("functional"), onDatabase("functional",
               TPrivilegeLevel.CREATE, TPrivilegeLevel.INSERT), onTable("functional",
-              "alltypes", allExcept(TPrivilegeLevel.ALL, TPrivilegeLevel.SELECT)));
+              "alltypes", allExcept(TPrivilegeLevel.ALL, TPrivilegeLevel.OWNER,
+              TPrivilegeLevel.SELECT)));
     }
 
     // Table with a specific location.
     authorize("create table functional.new_table(i int) location " +
         "'hdfs://localhost:20500/test-warehouse/new_table'")
         .ok(onServer(TPrivilegeLevel.ALL))
+        .ok(onServer(TPrivilegeLevel.OWNER))
         .ok(onServer(TPrivilegeLevel.CREATE),
             onUri("hdfs://localhost:20500/test-warehouse/new_table", TPrivilegeLevel.ALL))
+        .ok(onServer(TPrivilegeLevel.CREATE),
+            onUri("hdfs://localhost:20500/test-warehouse/new_table",
+            TPrivilegeLevel.OWNER))
         .error(createError("functional"), onServer(allExcept(
-            TPrivilegeLevel.ALL, TPrivilegeLevel.CREATE)))
+            TPrivilegeLevel.ALL, TPrivilegeLevel.OWNER, TPrivilegeLevel.CREATE)))
         .error(createError("functional"), onDatabase("functional", allExcept(
-            TPrivilegeLevel.ALL, TPrivilegeLevel.CREATE)), onUri(
+            TPrivilegeLevel.ALL, TPrivilegeLevel.OWNER, TPrivilegeLevel.CREATE)), onUri(
                 "hdfs://localhost:20500/test-warehouse/new_table", TPrivilegeLevel.ALL))
+        .error(createError("functional"), onDatabase("functional", allExcept(
+            TPrivilegeLevel.ALL, TPrivilegeLevel.OWNER, TPrivilegeLevel.CREATE)), onUri(
+                "hdfs://localhost:20500/test-warehouse/new_table", TPrivilegeLevel.OWNER))
         .error(accessError("hdfs://localhost:20500/test-warehouse/new_table"),
             onDatabase("functional", TPrivilegeLevel.CREATE));
 
@@ -1389,65 +1558,93 @@ public class AuthorizationStmtTest extends FrontendTestBase {
     authorize("create external table functional.new_table(i int) location " +
         "'hdfs://localhost:20500/test-warehouse/UPPER_CASE/test'")
         .ok(onServer(TPrivilegeLevel.ALL))
+        .ok(onServer(TPrivilegeLevel.OWNER))
         .ok(onServer(TPrivilegeLevel.CREATE),
             onUri("hdfs://localhost:20500/test-warehouse/UPPER_CASE/test",
                 TPrivilegeLevel.ALL))
+        .ok(onServer(TPrivilegeLevel.CREATE),
+            onUri("hdfs://localhost:20500/test-warehouse/UPPER_CASE/test",
+                TPrivilegeLevel.OWNER))
         // Wrong case letters on URI.
         .error(accessError("hdfs://localhost:20500/test-warehouse/UPPER_CASE/test"),
             onServer(TPrivilegeLevel.CREATE),
             onUri("hdfs://localhost:20500/test-warehouse/upper_case/test",
                 TPrivilegeLevel.ALL))
+        .error(accessError("hdfs://localhost:20500/test-warehouse/UPPER_CASE/test"),
+            onServer(TPrivilegeLevel.CREATE),
+            onUri("hdfs://localhost:20500/test-warehouse/upper_case/test",
+                TPrivilegeLevel.OWNER))
         .error(createError("functional"), onServer(allExcept(
-            TPrivilegeLevel.ALL, TPrivilegeLevel.CREATE)))
+            TPrivilegeLevel.ALL, TPrivilegeLevel.OWNER, TPrivilegeLevel.CREATE)))
         .error(createError("functional"), onDatabase("functional", allExcept(
-            TPrivilegeLevel.ALL, TPrivilegeLevel.CREATE)), onUri(
+            TPrivilegeLevel.ALL, TPrivilegeLevel.OWNER, TPrivilegeLevel.CREATE)), onUri(
             "hdfs://localhost:20500/test-warehouse/UPPER_CASE/test", TPrivilegeLevel.ALL))
+        .error(createError("functional"), onDatabase("functional", allExcept(
+            TPrivilegeLevel.ALL, TPrivilegeLevel.OWNER, TPrivilegeLevel.CREATE)), onUri(
+            "hdfs://localhost:20500/test-warehouse/UPPER_CASE/test",
+            TPrivilegeLevel.OWNER))
         .error(accessError("hdfs://localhost:20500/test-warehouse/UPPER_CASE/test"),
             onDatabase("functional", TPrivilegeLevel.CREATE));
 
     authorize("create table functional.new_table like parquet "
         + "'hdfs://localhost:20500/test-warehouse/schemas/alltypestiny.parquet'")
         .ok(onServer(TPrivilegeLevel.ALL))
+        .ok(onServer(TPrivilegeLevel.OWNER))
         .ok(onServer(TPrivilegeLevel.CREATE),
             onUri("hdfs://localhost:20500/test-warehouse/schemas/alltypestiny.parquet",
                 TPrivilegeLevel.ALL))
+        .ok(onServer(TPrivilegeLevel.CREATE),
+            onUri("hdfs://localhost:20500/test-warehouse/schemas/alltypestiny.parquet",
+                TPrivilegeLevel.OWNER))
         .error(accessError(
             "hdfs://localhost:20500/test-warehouse/schemas/alltypestiny.parquet"),
-            onServer(allExcept(TPrivilegeLevel.ALL, TPrivilegeLevel.CREATE)))
+            onServer(allExcept(TPrivilegeLevel.ALL, TPrivilegeLevel.OWNER,
+                TPrivilegeLevel.CREATE)))
         .error(createError("functional"), onDatabase("functional", allExcept(
-            TPrivilegeLevel.ALL, TPrivilegeLevel.CREATE)), onUri(
+            TPrivilegeLevel.ALL, TPrivilegeLevel.OWNER, TPrivilegeLevel.CREATE)), onUri(
             "hdfs://localhost:20500/test-warehouse/schemas/alltypestiny.parquet",
             TPrivilegeLevel.ALL))
+        .error(createError("functional"), onDatabase("functional", allExcept(
+            TPrivilegeLevel.ALL, TPrivilegeLevel.OWNER, TPrivilegeLevel.CREATE)), onUri(
+            "hdfs://localhost:20500/test-warehouse/schemas/alltypestiny.parquet",
+            TPrivilegeLevel.OWNER))
         .error(accessError(
             "hdfs://localhost:20500/test-warehouse/schemas/alltypestiny.parquet"),
             onDatabase("functional", TPrivilegeLevel.CREATE));
 
     authorize("create table if not exists _impala_builtins.new_table(i int)")
-        .error(systemDbError(), onServer(TPrivilegeLevel.ALL));
+        .error(systemDbError(), onServer(TPrivilegeLevel.ALL))
+        .error(systemDbError(), onServer(TPrivilegeLevel.OWNER));
 
-    // IMPALA-4000: Only users with ALL privileges on SERVER may create external Kudu
-    // tables.
+    // IMPALA-4000: Only users with ALL/OWNER privileges on SERVER may create external
+    // Kudu tables.
     authorize("create external table functional.kudu_tbl stored as kudu " +
         "tblproperties ('kudu.master_addresses'='127.0.0.1', 'kudu.table_name'='tbl')")
         .ok(onServer(TPrivilegeLevel.ALL))
+        .ok(onServer(TPrivilegeLevel.OWNER))
         .error(createError("functional"))
-        .error(accessError("server1"), onServer(allExcept(TPrivilegeLevel.ALL)))
-        .error(accessError("server1"), onDatabase("functional", TPrivilegeLevel.ALL));
+        .error(accessError("server1"), onServer(allExcept(TPrivilegeLevel.ALL,
+            TPrivilegeLevel.OWNER)))
+        .error(accessError("server1"), onDatabase("functional", TPrivilegeLevel.ALL))
+        .error(accessError("server1"), onDatabase("functional", TPrivilegeLevel.OWNER));
 
-    // IMPALA-4000: ALL privileges on SERVER are not required to create managed tables.
+    // IMPALA-4000: ALL/OWNER privileges on SERVER are not required to create managed
+    // tables.
     authorize("create table functional.kudu_tbl (i int, j int, primary key (i))" +
         " partition by hash (i) partitions 9 stored as kudu")
         .ok(onServer(TPrivilegeLevel.ALL))
+        .ok(onServer(TPrivilegeLevel.OWNER))
         .ok(onDatabase("functional", TPrivilegeLevel.ALL))
+        .ok(onDatabase("functional", TPrivilegeLevel.OWNER))
         .ok(onDatabase("functional", TPrivilegeLevel.CREATE))
         .error(createError("functional"))
         .error(createError("functional"), onServer(allExcept(
-            TPrivilegeLevel.ALL, TPrivilegeLevel.CREATE)))
+            TPrivilegeLevel.ALL, TPrivilegeLevel.OWNER, TPrivilegeLevel.CREATE)))
         .error(createError("functional"), onDatabase("functional", allExcept(
-            TPrivilegeLevel.ALL, TPrivilegeLevel.CREATE)));
+            TPrivilegeLevel.ALL, TPrivilegeLevel.OWNER, TPrivilegeLevel.CREATE)));
 
     // IMPALA-6451: CTAS for Kudu tables on non-external tables and without
-    // TBLPROPERTIES ('kudu.master_addresses') should not require ALL privileges
+    // TBLPROPERTIES ('kudu.master_addresses') should not require ALL/OWNER privileges
     // on SERVER.
     // The statement below causes the SQL statement to be rewritten.
     authorize("create table functional.kudu_tbl primary key (bigint_col) " +
@@ -1456,22 +1653,24 @@ public class AuthorizationStmtTest extends FrontendTestBase {
         "from functional.alltypes " +
         "where exists (select 1 from functional.alltypes)")
       .ok(onServer(TPrivilegeLevel.ALL))
+      .ok(onServer(TPrivilegeLevel.OWNER))
       .ok(onDatabase("functional", TPrivilegeLevel.CREATE, TPrivilegeLevel.INSERT,
           TPrivilegeLevel.SELECT))
       .error(createError("functional"))
       .error(createError("functional"), onServer(allExcept(TPrivilegeLevel.ALL,
-          TPrivilegeLevel.CREATE, TPrivilegeLevel.INSERT, TPrivilegeLevel.SELECT)))
+          TPrivilegeLevel.OWNER, TPrivilegeLevel.CREATE, TPrivilegeLevel.INSERT,
+          TPrivilegeLevel.SELECT)))
       .error(createError("functional"), onDatabase("functional", allExcept(
-          TPrivilegeLevel.ALL, TPrivilegeLevel.CREATE, TPrivilegeLevel.INSERT,
-          TPrivilegeLevel.SELECT)));
+          TPrivilegeLevel.ALL, TPrivilegeLevel.OWNER, TPrivilegeLevel.CREATE,
+          TPrivilegeLevel.INSERT, TPrivilegeLevel.SELECT)));
 
     // Database does not exist.
     authorize("create table nodb.new_table(i int)")
         .error(createError("nodb"))
         .error(createError("nodb"), onServer(allExcept(
-            TPrivilegeLevel.ALL, TPrivilegeLevel.CREATE)))
+            TPrivilegeLevel.ALL, TPrivilegeLevel.OWNER, TPrivilegeLevel.CREATE)))
         .error(createError("nodb"), onDatabase("functional", allExcept(
-            TPrivilegeLevel.ALL, TPrivilegeLevel.CREATE)));
+            TPrivilegeLevel.ALL, TPrivilegeLevel.OWNER, TPrivilegeLevel.CREATE)));
   }
 
   @Test
@@ -1482,35 +1681,44 @@ public class AuthorizationStmtTest extends FrontendTestBase {
         authorize("create view functional.new_view(a) as " +
             "select int_col from functional.alltypes")}) {
       test.ok(onServer(TPrivilegeLevel.ALL))
+          .ok(onServer(TPrivilegeLevel.OWNER))
           .ok(onServer(TPrivilegeLevel.CREATE, TPrivilegeLevel.SELECT))
           .ok(onDatabase("functional", TPrivilegeLevel.ALL))
+          .ok(onDatabase("functional", TPrivilegeLevel.OWNER))
           .ok(onDatabase("functional", TPrivilegeLevel.CREATE, TPrivilegeLevel.SELECT))
           .ok(onDatabase("functional", TPrivilegeLevel.CREATE), onTable(
               "functional", "alltypes", TPrivilegeLevel.SELECT))
           .ok(onDatabase("functional", TPrivilegeLevel.CREATE), onColumn(
               "functional", "alltypes", "int_col", TPrivilegeLevel.ALL))
+          .ok(onDatabase("functional", TPrivilegeLevel.CREATE), onColumn(
+              "functional", "alltypes", "int_col", TPrivilegeLevel.OWNER))
           .error(selectError("functional.alltypes"))
           .error(selectError("functional.alltypes"), onServer(allExcept(
-              TPrivilegeLevel.ALL, TPrivilegeLevel.CREATE, TPrivilegeLevel.SELECT)))
+              TPrivilegeLevel.ALL, TPrivilegeLevel.OWNER, TPrivilegeLevel.CREATE,
+              TPrivilegeLevel.SELECT)))
           .error(createError("functional"), onDatabase("functional", allExcept(
-              TPrivilegeLevel.ALL, TPrivilegeLevel.CREATE)))
+              TPrivilegeLevel.ALL, TPrivilegeLevel.OWNER, TPrivilegeLevel.CREATE)))
           .error(selectError("functional.alltypes"), onDatabase("functional", allExcept(
-              TPrivilegeLevel.ALL, TPrivilegeLevel.SELECT)))
+              TPrivilegeLevel.ALL, TPrivilegeLevel.OWNER, TPrivilegeLevel.SELECT)))
           .error(selectError("functional.alltypes"), onTable("functional", "alltypes",
-              allExcept(TPrivilegeLevel.ALL, TPrivilegeLevel.SELECT)));
+              allExcept(TPrivilegeLevel.ALL, TPrivilegeLevel.OWNER,
+              TPrivilegeLevel.SELECT)));
     }
 
     // View with constant select.
     authorize("create view functional.new_view as select 1")
         .ok(onServer(TPrivilegeLevel.ALL))
+        .ok(onServer(TPrivilegeLevel.OWNER))
         .ok(onServer(TPrivilegeLevel.CREATE))
         .ok(onDatabase("functional", TPrivilegeLevel.ALL))
+        .ok(onDatabase("functional", TPrivilegeLevel.OWNER))
         .ok(onDatabase("functional", TPrivilegeLevel.CREATE))
         .error(createError("functional"))
         .error(createError("functional"), onServer(allExcept(
-            TPrivilegeLevel.ALL, TPrivilegeLevel.CREATE, TPrivilegeLevel.SELECT)))
+            TPrivilegeLevel.ALL, TPrivilegeLevel.OWNER, TPrivilegeLevel.CREATE,
+            TPrivilegeLevel.SELECT)))
         .error(createError("functional"), onDatabase("functional", allExcept(
-            TPrivilegeLevel.ALL, TPrivilegeLevel.CREATE)));
+            TPrivilegeLevel.ALL, TPrivilegeLevel.OWNER, TPrivilegeLevel.CREATE)));
 
     // View already exists.
     for (AuthzTest test: new AuthzTest[]{
@@ -1520,25 +1728,28 @@ public class AuthorizationStmtTest extends FrontendTestBase {
             "select int_col from functional.alltypes")}) {
       test.error(selectError("functional.alltypes"))
           .error(selectError("functional.alltypes"), onServer(allExcept(
-              TPrivilegeLevel.ALL, TPrivilegeLevel.CREATE, TPrivilegeLevel.SELECT)))
+              TPrivilegeLevel.ALL, TPrivilegeLevel.OWNER, TPrivilegeLevel.CREATE,
+              TPrivilegeLevel.SELECT)))
           .error(createError("functional"), onDatabase("functional", allExcept(
-              TPrivilegeLevel.ALL, TPrivilegeLevel.CREATE)))
+              TPrivilegeLevel.ALL, TPrivilegeLevel.OWNER, TPrivilegeLevel.CREATE)))
           .error(selectError("functional.alltypes"), onDatabase("functional", allExcept(
-              TPrivilegeLevel.ALL, TPrivilegeLevel.SELECT)))
+              TPrivilegeLevel.ALL, TPrivilegeLevel.OWNER, TPrivilegeLevel.SELECT)))
           .error(selectError("functional.alltypes"), onTable("functional", "alltypes",
-              allExcept(TPrivilegeLevel.ALL, TPrivilegeLevel.SELECT)));
+              allExcept(TPrivilegeLevel.ALL, TPrivilegeLevel.OWNER,
+              TPrivilegeLevel.SELECT)));
     }
 
     authorize("create view if not exists _impala_builtins.new_view as select 1")
-        .error(systemDbError(), onServer(TPrivilegeLevel.ALL));
+        .error(systemDbError(), onServer(TPrivilegeLevel.ALL))
+        .error(systemDbError(), onServer(TPrivilegeLevel.OWNER));
 
     // Database does not exist.
     authorize("create view nodb.new_view as select 1")
         .error(createError("nodb"))
         .error(createError("nodb"), onServer(allExcept(
-            TPrivilegeLevel.ALL, TPrivilegeLevel.CREATE)))
+            TPrivilegeLevel.ALL, TPrivilegeLevel.OWNER, TPrivilegeLevel.CREATE)))
         .error(createError("nodb"), onDatabase("functional", allExcept(
-            TPrivilegeLevel.ALL, TPrivilegeLevel.CREATE)));
+            TPrivilegeLevel.ALL, TPrivilegeLevel.OWNER, TPrivilegeLevel.CREATE)));
   }
 
   @Test
@@ -1548,14 +1759,17 @@ public class AuthorizationStmtTest extends FrontendTestBase {
         authorize("drop database functional cascade"),
         authorize("drop database functional restrict")}) {
       test.ok(onServer(TPrivilegeLevel.ALL))
+          .ok(onServer(TPrivilegeLevel.OWNER))
           .ok(onServer(TPrivilegeLevel.DROP))
           .ok(onDatabase("functional", TPrivilegeLevel.ALL))
+          .ok(onDatabase("functional", TPrivilegeLevel.OWNER))
           .ok(onDatabase("functional", TPrivilegeLevel.DROP))
           .error(dropError("functional"))
           .error(dropError("functional"), onServer(allExcept(
-              TPrivilegeLevel.ALL, TPrivilegeLevel.DROP)))
+              TPrivilegeLevel.ALL, TPrivilegeLevel.OWNER, TPrivilegeLevel.DROP)))
           .error(dropError("functional"), onDatabase("functional",
-              allExcept(TPrivilegeLevel.ALL, TPrivilegeLevel.DROP)));
+              allExcept(TPrivilegeLevel.ALL, TPrivilegeLevel.OWNER,
+              TPrivilegeLevel.DROP)));
     }
 
     // Database does not exist.
@@ -1565,7 +1779,8 @@ public class AuthorizationStmtTest extends FrontendTestBase {
         authorize("drop database nodb restrict")}) {
       test.error(dropError("nodb"))
           .error(dropError("nodb"), onServer(
-              allExcept(TPrivilegeLevel.ALL, TPrivilegeLevel.DROP)));
+              allExcept(TPrivilegeLevel.ALL, TPrivilegeLevel.OWNER,
+              TPrivilegeLevel.DROP)));
     }
 
     // Database does not exist but with if exists clause.
@@ -1574,117 +1789,132 @@ public class AuthorizationStmtTest extends FrontendTestBase {
         authorize("drop database if exists nodb cascade"),
         authorize("drop database if exists nodb restrict")}) {
       test.ok(onServer(TPrivilegeLevel.ALL))
+          .ok(onServer(TPrivilegeLevel.OWNER))
           .ok(onServer(TPrivilegeLevel.DROP))
           .error(dropError("nodb"))
           .error(dropError("nodb"), onServer(allExcept(
-              TPrivilegeLevel.ALL, TPrivilegeLevel.DROP)));
+              TPrivilegeLevel.ALL, TPrivilegeLevel.OWNER, TPrivilegeLevel.DROP)));
     }
 
-    // Dropping system database is not allowed even if with ALL privilege on server.
+    // Dropping system database is not allowed even if with ALL/OWNER privilege on server.
     authorize("drop database _impala_builtins")
-        .error(systemDbError(), onServer(TPrivilegeLevel.ALL));
+        .error(systemDbError(), onServer(TPrivilegeLevel.ALL))
+        .error(systemDbError(), onServer(TPrivilegeLevel.OWNER));
   }
 
   @Test
   public void testDropTable() throws ImpalaException {
     authorize("drop table functional.alltypes")
         .ok(onServer(TPrivilegeLevel.ALL))
+        .ok(onServer(TPrivilegeLevel.OWNER))
         .ok(onServer(TPrivilegeLevel.DROP))
         .ok(onDatabase("functional", TPrivilegeLevel.ALL))
+        .ok(onDatabase("functional", TPrivilegeLevel.OWNER))
         .ok(onDatabase("functional", TPrivilegeLevel.DROP))
         .ok(onTable("functional", "alltypes", TPrivilegeLevel.ALL))
+        .ok(onTable("functional", "alltypes", TPrivilegeLevel.OWNER))
         .ok(onTable("functional", "alltypes", TPrivilegeLevel.DROP))
         .error(dropError("functional.alltypes"))
         .error(dropError("functional.alltypes"), onServer(allExcept(
-            TPrivilegeLevel.ALL, TPrivilegeLevel.DROP)))
+            TPrivilegeLevel.ALL, TPrivilegeLevel.OWNER, TPrivilegeLevel.DROP)))
         .error(dropError("functional.alltypes"), onDatabase("functional",
-            allExcept(TPrivilegeLevel.ALL, TPrivilegeLevel.DROP)))
+            allExcept(TPrivilegeLevel.ALL, TPrivilegeLevel.OWNER, TPrivilegeLevel.DROP)))
         .error(dropError("functional.alltypes"), onTable("functional", "alltypes",
-            allExcept(TPrivilegeLevel.ALL, TPrivilegeLevel.DROP)));
+            allExcept(TPrivilegeLevel.ALL, TPrivilegeLevel.OWNER, TPrivilegeLevel.DROP)));
 
     // Database/Table does not exist.
     authorize("drop table nodb.notbl")
         .error(dropError("nodb.notbl"))
         .error(dropError("nodb.notbl"), onServer(allExcept(
-            TPrivilegeLevel.ALL, TPrivilegeLevel.DROP)))
+            TPrivilegeLevel.ALL, TPrivilegeLevel.OWNER, TPrivilegeLevel.DROP)))
         .error(dropError("nodb.notbl"), onDatabase("functional",
-            allExcept(TPrivilegeLevel.ALL, TPrivilegeLevel.DROP)));
+            allExcept(TPrivilegeLevel.ALL, TPrivilegeLevel.OWNER, TPrivilegeLevel.DROP)));
 
     // Table does not exist.
     authorize("drop table functional.notbl")
         .error(dropError("functional.notbl"))
         .error(dropError("functional.notbl"), onServer(allExcept(
-            TPrivilegeLevel.ALL, TPrivilegeLevel.DROP)))
+            TPrivilegeLevel.ALL, TPrivilegeLevel.OWNER, TPrivilegeLevel.DROP)))
         .error(dropError("functional.notbl"), onDatabase("functional",
-            allExcept(TPrivilegeLevel.ALL, TPrivilegeLevel.DROP)));
+            allExcept(TPrivilegeLevel.ALL, TPrivilegeLevel.OWNER, TPrivilegeLevel.DROP)));
 
     // Table does not exist but with if exists clause.
     authorize("drop table if exists functional.notbl")
         .ok(onServer(TPrivilegeLevel.ALL))
+        .ok(onServer(TPrivilegeLevel.OWNER))
         .ok(onServer(TPrivilegeLevel.DROP))
         .ok(onDatabase("functional", TPrivilegeLevel.ALL))
+        .ok(onDatabase("functional", TPrivilegeLevel.OWNER))
         .ok(onDatabase("functional", TPrivilegeLevel.DROP))
         .error(dropError("functional.notbl"))
         .error(dropError("functional.notbl"), onServer(allExcept(
-            TPrivilegeLevel.ALL, TPrivilegeLevel.DROP)))
+            TPrivilegeLevel.ALL, TPrivilegeLevel.OWNER, TPrivilegeLevel.DROP)))
         .error(dropError("functional.notbl"), onDatabase("functional",
-            allExcept(TPrivilegeLevel.ALL, TPrivilegeLevel.DROP)));
+            allExcept(TPrivilegeLevel.ALL, TPrivilegeLevel.OWNER, TPrivilegeLevel.DROP)));
 
-    // Dropping any tables in the system database is not allowed even with ALL privilege
-    // on server.
+    // Dropping any tables in the system database is not allowed even with ALL/OWNER
+    // privilege on server.
     authorize("drop table _impala_builtins.tbl")
-        .error(systemDbError(), onServer(TPrivilegeLevel.ALL));
+        .error(systemDbError(), onServer(TPrivilegeLevel.ALL))
+        .error(systemDbError(), onServer(TPrivilegeLevel.OWNER));
   }
 
   @Test
   public void testDropView() throws ImpalaException {
     authorize("drop view functional.alltypes_view")
         .ok(onServer(TPrivilegeLevel.ALL))
+        .ok(onServer(TPrivilegeLevel.OWNER))
         .ok(onServer(TPrivilegeLevel.DROP))
         .ok(onDatabase("functional", TPrivilegeLevel.ALL))
+        .ok(onDatabase("functional", TPrivilegeLevel.OWNER))
         .ok(onDatabase("functional", TPrivilegeLevel.DROP))
         .ok(onTable("functional", "alltypes_view", TPrivilegeLevel.ALL))
+        .ok(onTable("functional", "alltypes_view", TPrivilegeLevel.OWNER))
         .ok(onTable("functional", "alltypes_view", TPrivilegeLevel.DROP))
         .error(dropError("functional.alltypes_view"))
         .error(dropError("functional.alltypes_view"), onServer(allExcept(
-            TPrivilegeLevel.ALL, TPrivilegeLevel.DROP)))
+            TPrivilegeLevel.ALL, TPrivilegeLevel.OWNER, TPrivilegeLevel.DROP)))
         .error(dropError("functional.alltypes_view"), onDatabase("functional",
-            allExcept(TPrivilegeLevel.ALL, TPrivilegeLevel.DROP)))
+            allExcept(TPrivilegeLevel.ALL, TPrivilegeLevel.OWNER, TPrivilegeLevel.DROP)))
         .error(dropError("functional.alltypes_view"), onTable("functional",
-            "alltypes_view", allExcept(TPrivilegeLevel.ALL, TPrivilegeLevel.DROP)));
+            "alltypes_view", allExcept(TPrivilegeLevel.ALL, TPrivilegeLevel.OWNER,
+            TPrivilegeLevel.DROP)));
 
     // Database does not exist.
     authorize("drop view nodb.noview")
         .error(dropError("nodb.noview"))
         .error(dropError("nodb.noview"), onServer(allExcept(
-            TPrivilegeLevel.ALL, TPrivilegeLevel.DROP)))
+            TPrivilegeLevel.ALL, TPrivilegeLevel.OWNER, TPrivilegeLevel.DROP)))
         .error(dropError("nodb.noview"), onDatabase("functional",
-            allExcept(TPrivilegeLevel.ALL, TPrivilegeLevel.DROP)));
+            allExcept(TPrivilegeLevel.ALL, TPrivilegeLevel.OWNER, TPrivilegeLevel.DROP)));
 
     // View does not exist.
     authorize("drop table functional.noview")
         .error(dropError("functional.noview"))
         .error(dropError("functional.noview"), onServer(allExcept(
-            TPrivilegeLevel.ALL, TPrivilegeLevel.DROP)))
+            TPrivilegeLevel.ALL, TPrivilegeLevel.OWNER, TPrivilegeLevel.DROP)))
         .error(dropError("functional.noview"), onDatabase("functional",
-            allExcept(TPrivilegeLevel.ALL, TPrivilegeLevel.DROP)));
+            allExcept(TPrivilegeLevel.ALL, TPrivilegeLevel.OWNER, TPrivilegeLevel.DROP)));
 
     // View does not exist but with if exists clause.
     authorize("drop table if exists functional.noview")
         .ok(onServer(TPrivilegeLevel.ALL))
+        .ok(onServer(TPrivilegeLevel.OWNER))
         .ok(onServer(TPrivilegeLevel.DROP))
         .ok(onDatabase("functional", TPrivilegeLevel.ALL))
+        .ok(onDatabase("functional", TPrivilegeLevel.OWNER))
         .ok(onDatabase("functional", TPrivilegeLevel.DROP))
         .error(dropError("functional.noview"))
         .error(dropError("functional.noview"), onServer(allExcept(
-            TPrivilegeLevel.ALL, TPrivilegeLevel.DROP)))
+            TPrivilegeLevel.ALL, TPrivilegeLevel.OWNER, TPrivilegeLevel.DROP)))
         .error(dropError("functional.noview"), onDatabase("functional",
-            allExcept(TPrivilegeLevel.ALL, TPrivilegeLevel.DROP)));
+            allExcept(TPrivilegeLevel.ALL, TPrivilegeLevel.OWNER, TPrivilegeLevel.DROP)));
 
-    // Dropping any views in the system database is not allowed even with ALL privilege
-    // on server.
+    // Dropping any views in the system database is not allowed even with ALL/OWNER
+    // privilege on server.
     authorize("drop table _impala_builtins.v")
-        .error(systemDbError(), onServer(TPrivilegeLevel.ALL));
+        .error(systemDbError(), onServer(TPrivilegeLevel.ALL))
+        .error(systemDbError(), onServer(TPrivilegeLevel.OWNER));
   }
 
   @Test
@@ -1715,37 +1945,45 @@ public class AuthorizationStmtTest extends FrontendTestBase {
         authorize("alter table functional.alltypes set owner user foo_owner"),
         authorize("alter table functional.alltypes set owner role foo_owner")}) {
       test.ok(onServer(TPrivilegeLevel.ALL))
+          .ok(onServer(TPrivilegeLevel.OWNER))
           .ok(onServer(TPrivilegeLevel.ALTER))
           .ok(onDatabase("functional", TPrivilegeLevel.ALL))
+          .ok(onDatabase("functional", TPrivilegeLevel.OWNER))
           .ok(onDatabase("functional", TPrivilegeLevel.ALTER))
           .ok(onTable("functional", "alltypes", TPrivilegeLevel.ALTER))
           .error(alterError("functional.alltypes"))
           .error(alterError("functional.alltypes"), onServer(allExcept(
-              TPrivilegeLevel.ALL, TPrivilegeLevel.ALTER)))
+              TPrivilegeLevel.ALL, TPrivilegeLevel.OWNER, TPrivilegeLevel.ALTER)))
           .error(alterError("functional.alltypes"), onDatabase("functional", allExcept(
-              TPrivilegeLevel.ALL, TPrivilegeLevel.ALTER)))
+              TPrivilegeLevel.ALL, TPrivilegeLevel.OWNER, TPrivilegeLevel.ALTER)))
           .error(alterError("functional.alltypes"), onTable("functional", "alltypes",
-              allExcept(TPrivilegeLevel.ALL, TPrivilegeLevel.ALTER)));
+              allExcept(TPrivilegeLevel.ALL, TPrivilegeLevel.OWNER,
+              TPrivilegeLevel.ALTER)));
     }
 
     // Alter table rename.
     authorize("alter table functional.alltypes rename to functional.new_table")
         .ok(onServer(TPrivilegeLevel.ALL))
+        .ok(onServer(TPrivilegeLevel.OWNER))
         .ok(onServer(TPrivilegeLevel.ALTER, TPrivilegeLevel.CREATE))
         .ok(onDatabase("functional", TPrivilegeLevel.ALL))
+        .ok(onDatabase("functional", TPrivilegeLevel.OWNER))
         .ok(onDatabase("functional", TPrivilegeLevel.ALTER, TPrivilegeLevel.CREATE))
         .ok(onDatabase("functional", TPrivilegeLevel.CREATE), onTable("functional",
             "alltypes", TPrivilegeLevel.ALTER))
         .error(alterError("functional.alltypes"))
         .error(alterError("functional.alltypes"), onServer(allExcept(
-            TPrivilegeLevel.ALL, TPrivilegeLevel.ALTER, TPrivilegeLevel.CREATE)))
+            TPrivilegeLevel.ALL, TPrivilegeLevel.OWNER, TPrivilegeLevel.ALTER,
+            TPrivilegeLevel.CREATE)))
         .error(alterError("functional.alltypes"), onDatabase("functional", allExcept(
-            TPrivilegeLevel.ALL, TPrivilegeLevel.ALTER, TPrivilegeLevel.CREATE)))
+            TPrivilegeLevel.ALL, TPrivilegeLevel.OWNER, TPrivilegeLevel.ALTER,
+            TPrivilegeLevel.CREATE)))
         .error(alterError("functional.alltypes"), onDatabase("functional",
             TPrivilegeLevel.CREATE), onTable("functional", "alltypes", allExcept(
-            TPrivilegeLevel.ALL, TPrivilegeLevel.ALTER)))
+            TPrivilegeLevel.ALL, TPrivilegeLevel.OWNER, TPrivilegeLevel.ALTER)))
         .error(createError("functional"), onDatabase("functional",
-            allExcept(TPrivilegeLevel.ALL, TPrivilegeLevel.CREATE)),
+            allExcept(TPrivilegeLevel.ALL, TPrivilegeLevel.OWNER,
+            TPrivilegeLevel.CREATE)),
             onTable("functional", "alltypes", TPrivilegeLevel.ALTER));
 
     // Only for Kudu tables.
@@ -1759,18 +1997,23 @@ public class AuthorizationStmtTest extends FrontendTestBase {
         authorize("alter table functional_kudu.testtbl drop range partition " +
             "1 < values < 2")}) {
       test.ok(onServer(TPrivilegeLevel.ALL))
+          .ok(onServer(TPrivilegeLevel.OWNER))
           .ok(onServer(TPrivilegeLevel.ALTER))
           .ok(onDatabase("functional_kudu", TPrivilegeLevel.ALL))
+          .ok(onDatabase("functional_kudu", TPrivilegeLevel.OWNER))
           .ok(onDatabase("functional_kudu", TPrivilegeLevel.ALTER))
           .ok(onTable("functional_kudu", "testtbl", TPrivilegeLevel.ALL))
+          .ok(onTable("functional_kudu", "testtbl", TPrivilegeLevel.OWNER))
           .ok(onTable("functional_kudu", "testtbl", TPrivilegeLevel.ALTER))
           .error(alterError("functional_kudu.testtbl"))
           .error(alterError("functional_kudu.testtbl"), onServer(allExcept(
-              TPrivilegeLevel.ALL, TPrivilegeLevel.ALTER)))
+              TPrivilegeLevel.ALL, TPrivilegeLevel.OWNER, TPrivilegeLevel.ALTER)))
           .error(alterError("functional_kudu.testtbl"), onDatabase("functional_kudu",
-              allExcept(TPrivilegeLevel.ALL, TPrivilegeLevel.ALTER)))
+              allExcept(TPrivilegeLevel.ALL, TPrivilegeLevel.OWNER,
+              TPrivilegeLevel.ALTER)))
           .error(alterError("functional_kudu.testtbl"), onTable("functional_kudu",
-              "testtbl", allExcept(TPrivilegeLevel.ALL, TPrivilegeLevel.ALTER)));
+              "testtbl", allExcept(TPrivilegeLevel.ALL, TPrivilegeLevel.OWNER,
+              TPrivilegeLevel.ALTER)));
     }
 
     // Alter table set location.
@@ -1780,25 +2023,38 @@ public class AuthorizationStmtTest extends FrontendTestBase {
         authorize("alter table functional.alltypes partition(year=2009, month=1) " +
             "set location 'hdfs://localhost:20500/test-warehouse/new_table'")}) {
       test.ok(onServer(TPrivilegeLevel.ALL))
+          .ok(onServer(TPrivilegeLevel.OWNER))
           .ok(onServer(TPrivilegeLevel.ALTER), onUri(
               "hdfs://localhost:20500/test-warehouse/new_table", TPrivilegeLevel.ALL))
+          .ok(onServer(TPrivilegeLevel.ALTER), onUri(
+              "hdfs://localhost:20500/test-warehouse/new_table", TPrivilegeLevel.OWNER))
           .ok(onDatabase("functional", TPrivilegeLevel.ALL), onUri(
               "hdfs://localhost:20500/test-warehouse/new_table", TPrivilegeLevel.ALL))
+          .ok(onDatabase("functional", TPrivilegeLevel.OWNER), onUri(
+              "hdfs://localhost:20500/test-warehouse/new_table", TPrivilegeLevel.OWNER))
           .ok(onDatabase("functional", TPrivilegeLevel.ALTER), onUri(
               "hdfs://localhost:20500/test-warehouse/new_table", TPrivilegeLevel.ALL))
+          .ok(onDatabase("functional", TPrivilegeLevel.ALTER), onUri(
+              "hdfs://localhost:20500/test-warehouse/new_table", TPrivilegeLevel.OWNER))
           .ok(onTable("functional", "alltypes", TPrivilegeLevel.ALL), onUri(
               "hdfs://localhost:20500/test-warehouse/new_table", TPrivilegeLevel.ALL))
+          .ok(onTable("functional", "alltypes", TPrivilegeLevel.OWNER), onUri(
+              "hdfs://localhost:20500/test-warehouse/new_table", TPrivilegeLevel.OWNER))
           .ok(onTable("functional", "alltypes", TPrivilegeLevel.ALTER), onUri(
               "hdfs://localhost:20500/test-warehouse/new_table", TPrivilegeLevel.ALL))
+          .ok(onTable("functional", "alltypes", TPrivilegeLevel.ALTER), onUri(
+              "hdfs://localhost:20500/test-warehouse/new_table", TPrivilegeLevel.OWNER))
           .error(alterError("functional.alltypes"))
           .error(alterError("functional.alltypes"),
-              onServer(allExcept(TPrivilegeLevel.ALL, TPrivilegeLevel.ALTER)),
+              onServer(allExcept(TPrivilegeLevel.ALL, TPrivilegeLevel.OWNER,
+              TPrivilegeLevel.ALTER)),
               onUri("hdfs://localhost:20500/test-warehouse/new_table"))
           .error(alterError("functional.alltypes"), onDatabase("functional", allExcept(
-              TPrivilegeLevel.ALL, TPrivilegeLevel.ALTER)),
+              TPrivilegeLevel.ALL, TPrivilegeLevel.OWNER, TPrivilegeLevel.ALTER)),
               onUri("hdfs://localhost:20500/test-warehouse/new_table"))
           .error(alterError("functional.alltypes"), onTable("functional", "alltypes",
-              allExcept(TPrivilegeLevel.ALL, TPrivilegeLevel.ALTER)),
+              allExcept(TPrivilegeLevel.ALL, TPrivilegeLevel.OWNER,
+              TPrivilegeLevel.ALTER)),
               onUri("hdfs://localhost:20500/test-warehouse/new_table"))
           .error(accessError("hdfs://localhost:20500/test-warehouse/new_table"),
               onDatabase("functional", TPrivilegeLevel.ALTER))
@@ -1810,15 +2066,15 @@ public class AuthorizationStmtTest extends FrontendTestBase {
     authorize("alter table nodb.alltypes add columns(c1 int)")
         .error(alterError("nodb"))
         .error(alterError("nodb"), onServer(allExcept(
-            TPrivilegeLevel.ALL, TPrivilegeLevel.ALTER)));
+            TPrivilegeLevel.ALL, TPrivilegeLevel.OWNER, TPrivilegeLevel.ALTER)));
 
     // Table does not exist.
     authorize("alter table functional.notbl add columns(c1 int)")
         .error(alterError("functional.notbl"))
         .error(alterError("functional.notbl"), onServer(allExcept(
-            TPrivilegeLevel.ALL, TPrivilegeLevel.ALTER)))
+            TPrivilegeLevel.ALL, TPrivilegeLevel.OWNER, TPrivilegeLevel.ALTER)))
         .error(alterError("functional.notbl"), onDatabase("functional", allExcept(
-            TPrivilegeLevel.ALL, TPrivilegeLevel.ALTER)));
+            TPrivilegeLevel.ALL, TPrivilegeLevel.OWNER, TPrivilegeLevel.ALTER)));
   }
 
   @Test
@@ -1829,95 +2085,115 @@ public class AuthorizationStmtTest extends FrontendTestBase {
         authorize("alter view functional.alltypes_view(a) as " +
             "select int_col from functional.alltypes")}) {
       test.ok(onServer(TPrivilegeLevel.ALL))
+          .ok(onServer(TPrivilegeLevel.OWNER))
           .ok(onServer(TPrivilegeLevel.ALTER), onTable("functional", "alltypes",
               TPrivilegeLevel.SELECT))
           .ok(onDatabase("functional", TPrivilegeLevel.ALL))
+          .ok(onDatabase("functional", TPrivilegeLevel.OWNER))
           .ok(onDatabase("functional", TPrivilegeLevel.ALL, TPrivilegeLevel.ALTER,
               TPrivilege

<TRUNCATED>

[7/7] impala git commit: IMPALA-7343: Update SentryProxy to use Sentry bulk API

Posted by jo...@apache.org.
IMPALA-7343: Update SentryProxy to use Sentry bulk API

Prior to this patch, every Sentry policy update required a Thrift call
to Sentry to get the role privileges per role. This is inefficient.
This patch updates the way Impala update its Sentry policy by using the
new Sentry APIs for getting all roles/users and their associated
privileges in a single Thrift call to Sentry. This patch also updates
SentryProxy to get user privileges.

Before:
- Refreshing 100 roles: 493ms
- Refreshing 1000 roles: 4668ms
- Refreshing 10000 roles: 45636ms

After:
- Refreshing 100 roles: 114ms
- Refreshing 1000 roles: 1021ms
- Refreshing 10000 roles: 10076ms

Testing:
- Ran all FE tests
- Ran all E2E authorization tests

Change-Id: I3ab215f2a5c5111bf1d25eec7fac90506d2f6304
Reviewed-on: http://gerrit.cloudera.org:8080/11250
Reviewed-by: Impala Public Jenkins <im...@cloudera.com>
Tested-by: Impala Public Jenkins <im...@cloudera.com>


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

Branch: refs/heads/master
Commit: 0a901fcb275058fc4c24e243d2a829dfddfef4d5
Parents: 1d4df94
Author: Fredy Wijaya <fw...@cloudera.com>
Authored: Thu Aug 16 15:33:25 2018 -0700
Committer: Impala Public Jenkins <im...@cloudera.com>
Committed: Sat Aug 18 07:31:27 2018 +0000

----------------------------------------------------------------------
 bin/impala-config.sh                            |   2 +-
 .../impala/catalog/CatalogServiceCatalog.java   |  10 +
 .../apache/impala/util/SentryPolicyService.java |  49 ++++-
 .../org/apache/impala/util/SentryProxy.java     | 207 ++++++++++++-------
 4 files changed, 195 insertions(+), 73 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/impala/blob/0a901fcb/bin/impala-config.sh
----------------------------------------------------------------------
diff --git a/bin/impala-config.sh b/bin/impala-config.sh
index 6a7511d..656861d 100755
--- a/bin/impala-config.sh
+++ b/bin/impala-config.sh
@@ -162,7 +162,7 @@ unset IMPALA_KUDU_URL
 : ${CDH_DOWNLOAD_HOST:=native-toolchain.s3.amazonaws.com}
 export CDH_DOWNLOAD_HOST
 export CDH_MAJOR_VERSION=6
-export CDH_BUILD_NUMBER=517354
+export CDH_BUILD_NUMBER=533940
 export IMPALA_HADOOP_VERSION=3.0.0-cdh6.x-SNAPSHOT
 export IMPALA_HBASE_VERSION=2.0.0-cdh6.x-SNAPSHOT
 export IMPALA_HIVE_VERSION=2.1.1-cdh6.x-SNAPSHOT

http://git-wip-us.apache.org/repos/asf/impala/blob/0a901fcb/fe/src/main/java/org/apache/impala/catalog/CatalogServiceCatalog.java
----------------------------------------------------------------------
diff --git a/fe/src/main/java/org/apache/impala/catalog/CatalogServiceCatalog.java b/fe/src/main/java/org/apache/impala/catalog/CatalogServiceCatalog.java
index 252bfe1..1fc43f5 100644
--- a/fe/src/main/java/org/apache/impala/catalog/CatalogServiceCatalog.java
+++ b/fe/src/main/java/org/apache/impala/catalog/CatalogServiceCatalog.java
@@ -1628,6 +1628,16 @@ public class CatalogServiceCatalog extends Catalog {
     return removePrincipalPrivilege(roleName, thriftPriv, TPrincipalType.ROLE);
   }
 
+  /**
+   * Removes a PrincipalPrivilege from the given user name. Returns the removed
+   * PrincipalPrivilege with an incremented catalog version or null if no matching
+   * privilege was found. Throws a CatalogException if no user exists with this name.
+   */
+  public PrincipalPrivilege removeUserPrivilege(String userName, TPrivilege thriftPriv)
+      throws CatalogException {
+    return removePrincipalPrivilege(userName, thriftPriv, TPrincipalType.USER);
+  }
+
   private PrincipalPrivilege removePrincipalPrivilege(String principalName,
       TPrivilege privilege, TPrincipalType type) throws CatalogException {
     versionLock_.writeLock().lock();

http://git-wip-us.apache.org/repos/asf/impala/blob/0a901fcb/fe/src/main/java/org/apache/impala/util/SentryPolicyService.java
----------------------------------------------------------------------
diff --git a/fe/src/main/java/org/apache/impala/util/SentryPolicyService.java b/fe/src/main/java/org/apache/impala/util/SentryPolicyService.java
index f039ee7..5d3ee33 100644
--- a/fe/src/main/java/org/apache/impala/util/SentryPolicyService.java
+++ b/fe/src/main/java/org/apache/impala/util/SentryPolicyService.java
@@ -18,8 +18,12 @@
 package org.apache.impala.util;
 
 import java.util.List;
+import java.util.Map;
+import java.util.Set;
 
+import org.apache.impala.catalog.Principal;
 import org.apache.impala.catalog.PrincipalPrivilege;
+import org.apache.impala.thrift.TPrincipalType;
 import org.apache.sentry.api.service.thrift.SentryPolicyServiceClient;
 import org.apache.sentry.api.service.thrift.TSentryGrantOption;
 import org.apache.sentry.api.service.thrift.TSentryPrivilege;
@@ -429,9 +433,50 @@ public class SentryPolicyService {
   }
 
   /**
+   * Returns a map of all roles with their associated privileges.
+   */
+  public Map<String, Set<TSentryPrivilege>> listAllRolesPrivileges(User requestingUser)
+      throws ImpalaException {
+    return listAllPrincipalsPrivileges(requestingUser, TPrincipalType.ROLE);
+  }
+
+  /**
+   * Returns a map of all users with their associated privileges.
+   */
+  public Map<String, Set<TSentryPrivilege>> listAllUsersPrivileges(User requestingUser)
+      throws ImpalaException {
+    return listAllPrincipalsPrivileges(requestingUser, TPrincipalType.USER);
+  }
+
+  private Map<String, Set<TSentryPrivilege>> listAllPrincipalsPrivileges(
+      User requestingUser, TPrincipalType type) throws ImpalaException {
+    SentryServiceClient client = new SentryServiceClient();
+    try {
+      return type == TPrincipalType.ROLE ?
+          client.get().listAllRolesPrivileges(requestingUser.getShortName()) :
+          client.get().listAllUsersPrivileges(requestingUser.getShortName());
+    } catch (Exception e) {
+      if (SentryUtil.isSentryAccessDenied(e)) {
+        throw new AuthorizationException(String.format(ACCESS_DENIED_ERROR_MSG,
+            requestingUser.getName(),
+            type == TPrincipalType.ROLE ?
+                "LIST_ALL_ROLES_PRIVILEGES" : "LIST_ALL_USERS_PRIVILEGES"));
+      }
+      throw new InternalException(String.format("Error making '%s' RPC to " +
+          "Sentry Service: ",
+          type == TPrincipalType.ROLE ?
+              "listAllRolesPrivileges" :
+              "listAllUsersPrivileges"), e);
+    } finally {
+      client.close();
+    }
+  }
+
+  /**
    * Utility function that converts a TSentryPrivilege to an Impala TPrivilege object.
    */
-  public static TPrivilege sentryPrivilegeToTPrivilege(TSentryPrivilege sentryPriv) {
+  public static TPrivilege sentryPrivilegeToTPrivilege(TSentryPrivilege sentryPriv,
+      Principal principal) {
     TPrivilege privilege = new TPrivilege();
     privilege.setServer_name(sentryPriv.getServerName());
     if (sentryPriv.isSetDbName()) privilege.setDb_name(sentryPriv.getDbName());
@@ -456,6 +501,8 @@ public class SentryPolicyService {
     } else {
       privilege.setHas_grant_opt(false);
     }
+    privilege.setPrincipal_id(principal.getId());
+    privilege.setPrincipal_type(principal.getPrincipalType());
     return privilege;
   }
 }

http://git-wip-us.apache.org/repos/asf/impala/blob/0a901fcb/fe/src/main/java/org/apache/impala/util/SentryProxy.java
----------------------------------------------------------------------
diff --git a/fe/src/main/java/org/apache/impala/util/SentryProxy.java b/fe/src/main/java/org/apache/impala/util/SentryProxy.java
index fa769b6..4d1fb19 100644
--- a/fe/src/main/java/org/apache/impala/util/SentryProxy.java
+++ b/fe/src/main/java/org/apache/impala/util/SentryProxy.java
@@ -27,6 +27,7 @@ import java.util.concurrent.TimeUnit;
 import org.apache.impala.catalog.AuthorizationException;
 import org.apache.impala.catalog.CatalogException;
 import org.apache.impala.catalog.CatalogServiceCatalog;
+import org.apache.impala.catalog.Principal;
 import org.apache.impala.catalog.PrincipalPrivilege;
 import org.apache.impala.catalog.Role;
 import org.apache.impala.thrift.TPrincipalType;
@@ -117,81 +118,18 @@ public class SentryProxy {
 
     public void run() {
       synchronized (SentryProxy.this) {
-        // Assume all roles should be removed. Then query the Policy Service and remove
-        // roles from this set that actually exist.
-        Set<String> rolesToRemove = catalog_.getAuthPolicy().getAllRoleNames();
+        Set<String> rolesToRemove;
+        Set<String> usersToRemove;
+        long startTime = System.currentTimeMillis();
         try {
-          // Read the full policy, adding new/modified roles to "updatedRoles".
-          for (TSentryRole sentryRole:
-              sentryPolicyService_.listAllRoles(processUser_)) {
-            // This role exists and should not be removed, delete it from the
-            // rolesToRemove set.
-            rolesToRemove.remove(sentryRole.getRoleName().toLowerCase());
-
-            Set<String> grantGroups = Sets.newHashSet();
-            for (TSentryGroup group: sentryRole.getGroups()) {
-              grantGroups.add(group.getGroupName());
-            }
-            Role existingRole =
-                catalog_.getAuthPolicy().getRole(sentryRole.getRoleName());
-            Role role;
-            // These roles are the same, use the current role.
-            if (existingRole != null &&
-                existingRole.getGrantGroups().equals(grantGroups)) {
-              role = existingRole;
-              if (resetVersions_) {
-                role.setCatalogVersion(catalog_.incrementAndGetCatalogVersion());
-              }
-            } else {
-              role = catalog_.addRole(sentryRole.getRoleName(), grantGroups);
-            }
-            // Assume all privileges should be removed. Privileges that still exist are
-            // deleted from this set and we are left with the set of privileges that need
-            // to be removed.
-            Set<String> privilegesToRemove = role.getPrivilegeNames();
-            List<TSentryPrivilege> sentryPrivlist = Lists.newArrayList();
-
-            try {
-              sentryPrivlist =
-                sentryPolicyService_.listRolePrivileges(processUser_, role.getName());
-            } catch (ImpalaException e) {
-              String roleName = role.getName() != null ? role.getName(): "null";
-              LOG.error("Error listing the role name: " + roleName, e);
-            }
-
-            // Check all the privileges that are part of this role.
-            for (TSentryPrivilege sentryPriv: sentryPrivlist) {
-              TPrivilege thriftPriv =
-                  SentryPolicyService.sentryPrivilegeToTPrivilege(sentryPriv);
-              thriftPriv.setPrincipal_id(role.getId());
-              thriftPriv.setPrincipal_type(TPrincipalType.ROLE);
-
-              privilegesToRemove.remove(thriftPriv.getPrivilege_name().toLowerCase());
-
-              PrincipalPrivilege existingRolePriv =
-                  role.getPrivilege(thriftPriv.getPrivilege_name());
-              // We already know about this privilege (privileges cannot be modified).
-              if (existingRolePriv != null &&
-                  existingRolePriv.getCreateTimeMs() == sentryPriv.getCreateTime()) {
-                if (resetVersions_) {
-                  existingRolePriv.setCatalogVersion(
-                      catalog_.incrementAndGetCatalogVersion());
-                }
-                continue;
-              }
-              catalog_.addRolePrivilege(role.getName(), thriftPriv);
-            }
-
-            // Remove the privileges that no longer exist.
-            for (String privilegeName: privilegesToRemove) {
-              TPrivilege privilege = new TPrivilege();
-              privilege.setPrivilege_name(privilegeName);
-              catalog_.removeRolePrivilege(role.getName(), privilege);
-            }
-          }
+          rolesToRemove = refreshRolePrivileges();
+          usersToRemove = refreshUserPrivileges();
         } catch (Exception e) {
           LOG.error("Error refreshing Sentry policy: ", e);
           return;
+        } finally {
+          LOG.debug("Refreshing Sentry policy took " +
+              (System.currentTimeMillis() - startTime) + "ms");
         }
 
         // Remove all the roles, incrementing the catalog version to indicate
@@ -199,6 +137,133 @@ public class SentryProxy {
         for (String roleName: rolesToRemove) {
           catalog_.removeRole(roleName);
         }
+        // Remove all the users, incrementing the catalog version to indicate
+        // a change.
+        for (String userName: usersToRemove) {
+          catalog_.removeUser(userName);
+        }
+      }
+    }
+
+    /**
+     * Updates all roles and their associated privileges in the catalog by adding,
+     * removing, and replacing the catalog objects to match those in Sentry since
+     * the last sentry sync update. This method returns a list of roles to be removed.
+     */
+    private Set<String> refreshRolePrivileges() throws ImpalaException {
+      // Assume all roles should be removed. Then query the Policy Service and remove
+      // roles from this set that actually exist.
+      Set<String> rolesToRemove = catalog_.getAuthPolicy().getAllRoleNames();
+      Map<String, Set<TSentryPrivilege>> allRolesPrivileges =
+          sentryPolicyService_.listAllRolesPrivileges(processUser_);
+      // Read the full policy, adding new/modified roles to "updatedRoles".
+      for (TSentryRole sentryRole:
+          sentryPolicyService_.listAllRoles(processUser_)) {
+        // This role exists and should not be removed, delete it from the
+        // rolesToRemove set.
+        rolesToRemove.remove(sentryRole.getRoleName().toLowerCase());
+
+        Set<String> grantGroups = Sets.newHashSet();
+        for (TSentryGroup group: sentryRole.getGroups()) {
+          grantGroups.add(group.getGroupName());
+        }
+        Role existingRole =
+            catalog_.getAuthPolicy().getRole(sentryRole.getRoleName());
+        Role role;
+        // These roles are the same, use the current role.
+        if (existingRole != null &&
+            existingRole.getGrantGroups().equals(grantGroups)) {
+          role = existingRole;
+          if (resetVersions_) {
+            role.setCatalogVersion(catalog_.incrementAndGetCatalogVersion());
+          }
+        } else {
+          role = catalog_.addRole(sentryRole.getRoleName(), grantGroups);
+        }
+        refreshPrivilegesInCatalog(role, allRolesPrivileges);
+      }
+      return rolesToRemove;
+    }
+
+    /**
+     * Updates all users and their associated privileges in the catalog by adding,
+     * removing, and replacing the catalog objects to match those in Sentry since the
+     * last Sentry sync update. Take note that we only store the users with privileges
+     * stored in Sentry and not all available users in the system. This method returns a
+     * list of users to be removed. User privileges do not support grant groups.
+     */
+    private Set<String> refreshUserPrivileges() throws ImpalaException {
+      // Assume all users should be removed. Then query the Policy Service and remove
+      // roles from this set that actually exist.
+      Set<String> usersToRemove = catalog_.getAuthPolicy().getAllUserNames();
+      Map<String, Set<TSentryPrivilege>> allUsersPrivileges =
+          sentryPolicyService_.listAllUsersPrivileges(processUser_);
+      for (Map.Entry<String, Set<TSentryPrivilege>> userPrivilegesEntry:
+          allUsersPrivileges.entrySet()) {
+        String userName = userPrivilegesEntry.getKey();
+        // This user exists and should not be removed so remove it from the
+        // usersToRemove set.
+        usersToRemove.remove(userName);
+
+        org.apache.impala.catalog.User existingUser =
+            catalog_.getAuthPolicy().getUser(userName);
+        org.apache.impala.catalog.User user;
+        if (existingUser != null) {
+          user = existingUser;
+          if (resetVersions_) {
+            user.setCatalogVersion(catalog_.incrementAndGetCatalogVersion());
+          }
+        } else {
+          user = catalog_.addUser(userName);
+        }
+        refreshPrivilegesInCatalog(user, allUsersPrivileges);
+      }
+      return usersToRemove;
+    }
+
+    /**
+     * Updates the privileges for a given principal in the catalog since the last Sentry
+     * sync update.
+     */
+    private void refreshPrivilegesInCatalog(Principal principal,
+        Map<String, Set<TSentryPrivilege>> allPrincipalPrivileges)
+        throws CatalogException {
+      // Assume all privileges should be removed. Privileges that still exist are
+      // deleted from this set and we are left with the set of privileges that need
+      // to be removed.
+      Set<String> privilegesToRemove = principal.getPrivilegeNames();
+      // Check all the privileges that are part of this principal.
+      for (TSentryPrivilege sentryPriv: allPrincipalPrivileges.get(principal.getName())) {
+        TPrivilege thriftPriv =
+            SentryPolicyService.sentryPrivilegeToTPrivilege(sentryPriv, principal);
+        privilegesToRemove.remove(thriftPriv.getPrivilege_name().toLowerCase());
+        PrincipalPrivilege existingPrincipalPriv =
+            principal.getPrivilege(thriftPriv.getPrivilege_name());
+        // We already know about this privilege (privileges cannot be modified).
+        if (existingPrincipalPriv != null &&
+            existingPrincipalPriv.getCreateTimeMs() == sentryPriv.getCreateTime()) {
+          if (resetVersions_) {
+            existingPrincipalPriv.setCatalogVersion(
+                catalog_.incrementAndGetCatalogVersion());
+          }
+          continue;
+        }
+        if (principal.getPrincipalType() == TPrincipalType.ROLE) {
+          catalog_.addRolePrivilege(principal.getName(), thriftPriv);
+        } else {
+          catalog_.addUserPrivilege(principal.getName(), thriftPriv);
+        }
+      }
+
+      // Remove the privileges that no longer exist.
+      for (String privilegeName: privilegesToRemove) {
+        TPrivilege privilege = new TPrivilege();
+        privilege.setPrivilege_name(privilegeName);
+        if (principal.getPrincipalType() == TPrincipalType.ROLE) {
+          catalog_.removeRolePrivilege(principal.getName(), privilege);
+        } else {
+          catalog_.removeUserPrivilege(principal.getName(), privilege);
+        }
       }
     }
   }


[6/7] impala git commit: IMPALA-7345: Add the OWNER privilege

Posted by jo...@apache.org.
IMPALA-7345: Add the OWNER privilege

This patch adds the OWNER privilege to the set of privileges that can
exist for a role/user. The privilege is equivalent to ALL, but cannot
be granted or revoked. It is granted/revoked by Sentry, if configured,
during CREATE, DROP, or ALTER DATABASE/TABLE SET OWNER statements.

Testing:
- Updated authorization tests
- Ran core tests

Change-Id: If63c2faa6daea6deb6d771503fe50943ae070705
Reviewed-on: http://gerrit.cloudera.org:8080/11245
Reviewed-by: Impala Public Jenkins <im...@cloudera.com>
Tested-by: Impala Public Jenkins <im...@cloudera.com>


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

Branch: refs/heads/master
Commit: 1d4df94125a5465a118046f3c46c81fde7740d8a
Parents: 30bb0b3
Author: Adam Holley <gi...@holleyism.com>
Authored: Wed Aug 15 23:22:24 2018 -0500
Committer: Impala Public Jenkins <im...@cloudera.com>
Committed: Sat Aug 18 06:29:20 2018 +0000

----------------------------------------------------------------------
 common/thrift/CatalogObjects.thrift             |   3 +-
 .../apache/impala/authorization/Privilege.java  |   3 +-
 .../impala/analysis/AuthorizationStmtTest.java  | 792 +++++++++++++------
 .../authorization/ImpalaActionFactoryTest.java  |   6 +-
 4 files changed, 566 insertions(+), 238 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/impala/blob/1d4df941/common/thrift/CatalogObjects.thrift
----------------------------------------------------------------------
diff --git a/common/thrift/CatalogObjects.thrift b/common/thrift/CatalogObjects.thrift
index 29f54e4..cbd0ba1 100644
--- a/common/thrift/CatalogObjects.thrift
+++ b/common/thrift/CatalogObjects.thrift
@@ -518,7 +518,8 @@ enum TPrivilegeLevel {
   REFRESH,
   CREATE,
   ALTER,
-  DROP
+  DROP,
+  OWNER
 }
 
 // Represents a privilege in an authorization policy. Privileges contain the level

http://git-wip-us.apache.org/repos/asf/impala/blob/1d4df941/fe/src/main/java/org/apache/impala/authorization/Privilege.java
----------------------------------------------------------------------
diff --git a/fe/src/main/java/org/apache/impala/authorization/Privilege.java b/fe/src/main/java/org/apache/impala/authorization/Privilege.java
index 0b1c2f8..877b6ad 100644
--- a/fe/src/main/java/org/apache/impala/authorization/Privilege.java
+++ b/fe/src/main/java/org/apache/impala/authorization/Privilege.java
@@ -64,7 +64,8 @@ public enum Privilege {
         ALTER.getCode() |
         CREATE.getCode() |
         DROP.getCode() |
-        REFRESH.getCode());
+        REFRESH.getCode()),
+    OWNER("owner", ALL.getCode());
 
     private final BitFieldAction bitFieldAction_;
 


[4/7] impala git commit: IMPALA-7345: Add the OWNER privilege

Posted by jo...@apache.org.
http://git-wip-us.apache.org/repos/asf/impala/blob/1d4df941/fe/src/test/java/org/apache/impala/authorization/ImpalaActionFactoryTest.java
----------------------------------------------------------------------
diff --git a/fe/src/test/java/org/apache/impala/authorization/ImpalaActionFactoryTest.java b/fe/src/test/java/org/apache/impala/authorization/ImpalaActionFactoryTest.java
index bd39839..9d0ecea 100644
--- a/fe/src/test/java/org/apache/impala/authorization/ImpalaActionFactoryTest.java
+++ b/fe/src/test/java/org/apache/impala/authorization/ImpalaActionFactoryTest.java
@@ -57,7 +57,8 @@ public class ImpalaActionFactoryTest {
          ImpalaAction.CREATE,
          ImpalaAction.DROP,
          ImpalaAction.REFRESH,
-         ImpalaAction.ALL);
+         ImpalaAction.ALL,
+         ImpalaAction.OWNER);
      assertBitFieldActions(expected, actual);
 
     actual = factory.getActionsByCode(ImpalaAction.ALL.getCode());
@@ -68,7 +69,8 @@ public class ImpalaActionFactoryTest {
         ImpalaAction.CREATE,
         ImpalaAction.DROP,
         ImpalaAction.REFRESH,
-        ImpalaAction.ALL);
+        ImpalaAction.ALL,
+        ImpalaAction.OWNER);
     assertBitFieldActions(expected, actual);
 
     try {


[2/7] impala git commit: IMPALA-7444: Improve logging of opening/closing/expiring sessions.

Posted by jo...@apache.org.
IMPALA-7444: Improve logging of opening/closing/expiring sessions.

Recent troubleshooting efforts have shown we can improve
logging of client session opening and expiry processing to
enhance serviceability.

This patch adds minor, but useful debug log improvements.

Change-Id: Iecf2d3ce707cc36c21da8a2459c2f68cf0b56a4a
Reviewed-on: http://gerrit.cloudera.org:8080/11234
Reviewed-by: Michael Ho <kw...@cloudera.com>
Tested-by: Impala Public Jenkins <im...@cloudera.com>


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

Branch: refs/heads/master
Commit: 1158883e859c38339800053a28dff5454f93ba5b
Parents: 83bb4ae
Author: Zoram Thanga <zo...@cloudera.com>
Authored: Tue Aug 14 22:41:23 2018 -0700
Committer: Impala Public Jenkins <im...@cloudera.com>
Committed: Sat Aug 18 03:44:50 2018 +0000

----------------------------------------------------------------------
 be/src/service/impala-hs2-server.cc | 14 +++++--
 be/src/service/impala-server.cc     | 66 ++++++++++++++++++--------------
 2 files changed, 47 insertions(+), 33 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/impala/blob/1158883e/be/src/service/impala-hs2-server.cc
----------------------------------------------------------------------
diff --git a/be/src/service/impala-hs2-server.cc b/be/src/service/impala-hs2-server.cc
index c49c65b..353c190 100644
--- a/be/src/service/impala-hs2-server.cc
+++ b/be/src/service/impala-hs2-server.cc
@@ -266,9 +266,6 @@ Status ImpalaServer::TExecuteStatementReqToTQueryContext(
 // HiveServer2 API
 void ImpalaServer::OpenSession(TOpenSessionResp& return_val,
     const TOpenSessionReq& request) {
-  // DO NOT log this Thrift struct in its entirety, in case a bad client sets the
-  // password.
-  VLOG_QUERY << "OpenSession(): username=" << request.username;
 
   // Generate session ID and the secret
   TUniqueId session_id;
@@ -284,6 +281,12 @@ void ImpalaServer::OpenSession(TOpenSessionResp& return_val,
     return_val.__isset.sessionHandle = true;
     UUIDToTUniqueId(session_uuid, &session_id);
   }
+
+  // DO NOT log this Thrift struct in its entirety, in case a bad client sets the
+  // password.
+  VLOG_QUERY << "Opening session: " << PrintId(session_id) << " username: "
+             << request.username;
+
   // create a session state: initialize start time, session type, database and default
   // query options.
   // TODO: put secret in session state map and check it
@@ -329,7 +332,8 @@ void ImpalaServer::OpenSession(TOpenSessionResp& return_val,
             &state->set_query_options_mask);
         if (status.ok() && iequals(v.first, "idle_session_timeout")) {
           state->session_timeout = state->set_query_options.idle_session_timeout;
-          VLOG_QUERY << "OpenSession(): idle_session_timeout="
+          VLOG_QUERY << "OpenSession(): session: " << PrintId(session_id)
+                     <<" idle_session_timeout="
                      << PrettyPrinter::Print(state->session_timeout, TUnit::TIME_S);
         }
       }
@@ -360,6 +364,8 @@ void ImpalaServer::OpenSession(TOpenSessionResp& return_val,
   return_val.__isset.configuration = true;
   return_val.status.__set_statusCode(thrift::TStatusCode::SUCCESS_STATUS);
   return_val.serverProtocolVersion = state->hs2_version;
+  VLOG_QUERY << "Opened session: " << PrintId(session_id) << " username: "
+             << request.username;
 }
 
 void ImpalaServer::CloseSession(TCloseSessionResp& return_val,

http://git-wip-us.apache.org/repos/asf/impala/blob/1158883e/be/src/service/impala-server.cc
----------------------------------------------------------------------
diff --git a/be/src/service/impala-server.cc b/be/src/service/impala-server.cc
index cc8465f..23a09f5 100644
--- a/be/src/service/impala-server.cc
+++ b/be/src/service/impala-server.cc
@@ -1191,6 +1191,8 @@ Status ImpalaServer::CancelInternal(const TUniqueId& query_id, bool check_inflig
 
 Status ImpalaServer::CloseSessionInternal(const TUniqueId& session_id,
     bool ignore_if_absent) {
+  VLOG_QUERY << "Closing session: " << PrintId(session_id);
+
   // Find the session_state and remove it from the map.
   shared_ptr<SessionState> session_state;
   {
@@ -1231,6 +1233,7 @@ Status ImpalaServer::CloseSessionInternal(const TUniqueId& session_id,
   }
   // Reconfigure the poll period of session_timeout_thread_ if necessary.
   UnregisterSessionTimeout(session_state->session_timeout);
+  VLOG_QUERY << "Closed session: " << PrintId(session_id);
   return Status::OK();
 }
 
@@ -1932,39 +1935,44 @@ void ImpalaServer::UnregisterSessionTimeout(int32_t session_timeout) {
       }
     }
 
-    lock_guard<mutex> map_lock(session_state_map_lock_);
     int64_t now = UnixMillis();
+    int expired_cnt = 0;
     VLOG(3) << "Session expiration thread waking up";
-    // TODO: If holding session_state_map_lock_ for the duration of this loop is too
-    // expensive, consider a priority queue.
-    for (SessionStateMap::value_type& session_state: session_state_map_) {
-      unordered_set<TUniqueId> inflight_queries;
-      {
-        lock_guard<mutex> state_lock(session_state.second->lock);
-        if (session_state.second->ref_count > 0) continue;
-        // A session closed by other means is in the process of being removed, and it's
-        // best not to interfere.
-        if (session_state.second->closed || session_state.second->expired) continue;
-        if (session_state.second->session_timeout == 0) continue;
-
-        int64_t last_accessed_ms = session_state.second->last_accessed_ms;
-        int64_t session_timeout_ms = session_state.second->session_timeout * 1000;
-        if (now - last_accessed_ms <= session_timeout_ms) continue;
-        LOG(INFO) << "Expiring session: " << PrintId(session_state.first) << ", user:"
-                  << session_state.second->connected_user << ", last active: "
-                  << ToStringFromUnixMillis(last_accessed_ms);
-        session_state.second->expired = true;
-        ImpaladMetrics::NUM_SESSIONS_EXPIRED->Increment(1L);
-        // Since expired is true, no more queries will be added to the inflight list.
-        inflight_queries.insert(session_state.second->inflight_queries.begin(),
-            session_state.second->inflight_queries.end());
-      }
-      // Unregister all open queries from this session.
-      Status status = Status::Expected("Session expired due to inactivity");
-      for (const TUniqueId& query_id: inflight_queries) {
-        cancellation_thread_pool_->Offer(CancellationWork(query_id, status, true));
+    {
+      // TODO: If holding session_state_map_lock_ for the duration of this loop is too
+      // expensive, consider a priority queue.
+      lock_guard<mutex> map_lock(session_state_map_lock_);
+      for (SessionStateMap::value_type& session_state: session_state_map_) {
+        unordered_set<TUniqueId> inflight_queries;
+        {
+          lock_guard<mutex> state_lock(session_state.second->lock);
+          if (session_state.second->ref_count > 0) continue;
+          // A session closed by other means is in the process of being removed, and it's
+          // best not to interfere.
+          if (session_state.second->closed || session_state.second->expired) continue;
+          if (session_state.second->session_timeout == 0) continue;
+
+          int64_t last_accessed_ms = session_state.second->last_accessed_ms;
+          int64_t session_timeout_ms = session_state.second->session_timeout * 1000;
+          if (now - last_accessed_ms <= session_timeout_ms) continue;
+          LOG(INFO) << "Expiring session: " << PrintId(session_state.first) << ", user: "
+                    << session_state.second->connected_user << ", last active: "
+                    << ToStringFromUnixMillis(last_accessed_ms);
+          session_state.second->expired = true;
+          ++expired_cnt;
+          ImpaladMetrics::NUM_SESSIONS_EXPIRED->Increment(1L);
+          // Since expired is true, no more queries will be added to the inflight list.
+          inflight_queries.insert(session_state.second->inflight_queries.begin(),
+              session_state.second->inflight_queries.end());
+        }
+        // Unregister all open queries from this session.
+        Status status = Status::Expected("Session expired due to inactivity");
+        for (const TUniqueId& query_id: inflight_queries) {
+          cancellation_thread_pool_->Offer(CancellationWork(query_id, status, true));
+        }
       }
     }
+    LOG_IF(INFO, expired_cnt > 0) << "Expired sessions. Count: " << expired_cnt;
   }
 }
 


[3/7] impala git commit: tests: ensure consistent logging format across tests

Posted by jo...@apache.org.
tests: ensure consistent logging format across tests

Many of the test modules included calls to 'logging.basicConfig' at
global scope in their implementation. This meant that by just importing
one of these files, other tests would inherit their logging format. This
is typically a bad idea in Python -- modules should not have side
effects like this on import.

The format was additionally inconsistent. In some cases we had a "--"
prepended to the format, and in others we didn't. The "--" is very
useful since it lets developers copy-paste query-test output back into
the shell to reproduce an issue.

This patch fixes the above by centralizing the logging configuration in
a pytest hook that runs prior to all pytests. A few other non-pytest
related tools now configure logging in their "main" code which is only
triggered when the module is executed directly.

I tested that, with this change, logs still show up properly in the .xml
output files from 'run-tests.py' as well as when running tests manually
from impala-py.test

Change-Id: I55ef0214b43f87da2d71804913ba4caa964f789f
Reviewed-on: http://gerrit.cloudera.org:8080/11225
Reviewed-by: Philip Zeyliger <ph...@cloudera.com>
Tested-by: Impala Public Jenkins <im...@cloudera.com>


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

Branch: refs/heads/master
Commit: 30bb0b3d897e165157dd682846f14b6b8106a87c
Parents: 1158883
Author: Todd Lipcon <to...@apache.org>
Authored: Tue Aug 14 16:38:55 2018 -0700
Committer: Todd Lipcon <to...@apache.org>
Committed: Sat Aug 18 04:21:00 2018 +0000

----------------------------------------------------------------------
 tests/common/impala_cluster.py            |  1 -
 tests/common/impala_service.py            |  2 --
 tests/common/impala_test_suite.py         |  1 -
 tests/common/test_result_verifier.py      |  1 -
 tests/conftest.py                         | 14 +++++++++++++-
 tests/metadata/test_hms_integration.py    |  5 -----
 tests/performance/query_exec_functions.py |  1 -
 tests/performance/query_executor.py       |  1 -
 tests/performance/scheduler.py            |  2 --
 tests/performance/workload_runner.py      |  2 --
 tests/run-tests.py                        |  3 +++
 tests/util/plugin_runner.py               |  1 -
 12 files changed, 16 insertions(+), 18 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/impala/blob/30bb0b3d/tests/common/impala_cluster.py
----------------------------------------------------------------------
diff --git a/tests/common/impala_cluster.py b/tests/common/impala_cluster.py
index d42c450..f04b4b9 100644
--- a/tests/common/impala_cluster.py
+++ b/tests/common/impala_cluster.py
@@ -32,7 +32,6 @@ from tests.common.impala_service import (
     StateStoredService)
 from tests.util.shell_util import exec_process, exec_process_async
 
-logging.basicConfig(level=logging.ERROR, format='%(threadName)s: %(message)s')
 LOG = logging.getLogger('impala_cluster')
 LOG.setLevel(level=logging.DEBUG)
 

http://git-wip-us.apache.org/repos/asf/impala/blob/30bb0b3d/tests/common/impala_service.py
----------------------------------------------------------------------
diff --git a/tests/common/impala_service.py b/tests/common/impala_service.py
index 0934f78..b738b8e 100644
--- a/tests/common/impala_service.py
+++ b/tests/common/impala_service.py
@@ -35,8 +35,6 @@ from RuntimeProfile.ttypes import TRuntimeProfileTree
 import base64
 import zlib
 
-logging.basicConfig(level=logging.ERROR, format='%(asctime)s %(threadName)s: %(message)s',
-    datefmt='%H:%M:%S')
 LOG = logging.getLogger('impala_service')
 LOG.setLevel(level=logging.DEBUG)
 

http://git-wip-us.apache.org/repos/asf/impala/blob/30bb0b3d/tests/common/impala_test_suite.py
----------------------------------------------------------------------
diff --git a/tests/common/impala_test_suite.py b/tests/common/impala_test_suite.py
index e7ac7b5..48a53f2 100644
--- a/tests/common/impala_test_suite.py
+++ b/tests/common/impala_test_suite.py
@@ -79,7 +79,6 @@ from thrift.protocol import TBinaryProtocol
 
 # Initializing the logger before conditional imports, since we will need it
 # for them.
-logging.basicConfig(level=logging.INFO, format='-- %(message)s')
 LOG = logging.getLogger('impala_test_suite')
 
 # The ADLS python client isn't downloaded when ADLS isn't the target FS, so do a

http://git-wip-us.apache.org/repos/asf/impala/blob/30bb0b3d/tests/common/test_result_verifier.py
----------------------------------------------------------------------
diff --git a/tests/common/test_result_verifier.py b/tests/common/test_result_verifier.py
index 2e8bbb1..f071e6b 100644
--- a/tests/common/test_result_verifier.py
+++ b/tests/common/test_result_verifier.py
@@ -26,7 +26,6 @@ from tests.util.test_file_parser import (join_section_lines, remove_comments,
     split_section_lines)
 from tests.util.hdfs_util import NAMENODE
 
-logging.basicConfig(level=logging.INFO, format='%(threadName)s: %(message)s')
 LOG = logging.getLogger('test_result_verfier')
 
 # Special prefix for column values that indicates the actual column value

http://git-wip-us.apache.org/repos/asf/impala/blob/30bb0b3d/tests/conftest.py
----------------------------------------------------------------------
diff --git a/tests/conftest.py b/tests/conftest.py
index 1d64b6b..f8ca1c7 100644
--- a/tests/conftest.py
+++ b/tests/conftest.py
@@ -32,8 +32,8 @@ from tests.common.patterns import is_valid_impala_identifier
 from tests.comparison.db_connection import ImpalaConnection
 from tests.util.filesystem_utils import FILESYSTEM, ISILON_WEBHDFS_PORT
 
-logging.basicConfig(level=logging.INFO, format='%(threadName)s: %(message)s')
 LOG = logging.getLogger('test_configuration')
+LOG_FORMAT = "-- %(asctime)s %(levelname)-8s %(threadName)s: %(message)s"
 
 DEFAULT_CONN_TIMEOUT = 45
 DEFAULT_EXPLORATION_STRATEGY = 'core'
@@ -50,6 +50,18 @@ if FILESYSTEM == 'isilon':
                                                  port=ISILON_WEBHDFS_PORT)
 
 
+def pytest_configure(config):
+  """ Hook startup of pytest to set up log format. """
+  configure_logging()
+
+
+def configure_logging():
+  # Use a "--" since most of our tests output SQL commands, and it's nice to
+  # be able to copy-paste directly from the test output back into a shell to
+  # try to reproduce a failure.
+  logging.basicConfig(level=logging.INFO, format=LOG_FORMAT)
+
+
 def pytest_addoption(parser):
   """Adds a new command line options to py.test"""
   parser.addoption("--exploration_strategy", default=DEFAULT_EXPLORATION_STRATEGY,

http://git-wip-us.apache.org/repos/asf/impala/blob/30bb0b3d/tests/metadata/test_hms_integration.py
----------------------------------------------------------------------
diff --git a/tests/metadata/test_hms_integration.py b/tests/metadata/test_hms_integration.py
index 0f085c4..3ac5cef 100644
--- a/tests/metadata/test_hms_integration.py
+++ b/tests/metadata/test_hms_integration.py
@@ -36,11 +36,6 @@ from tests.common.test_dimensions import (
     create_uncompressed_text_dimension)
 from tests.util.hive_utils import HiveDbWrapper, HiveTableWrapper
 
-import logging
-
-logging.basicConfig(level=logging.INFO, format='%(threadName)s: %(message)s')
-LOG = logging.getLogger('test_configuration')
-
 @SkipIfS3.hive
 @SkipIfADLS.hive
 @SkipIfIsilon.hive

http://git-wip-us.apache.org/repos/asf/impala/blob/30bb0b3d/tests/performance/query_exec_functions.py
----------------------------------------------------------------------
diff --git a/tests/performance/query_exec_functions.py b/tests/performance/query_exec_functions.py
index f7d00ec..0366291 100644
--- a/tests/performance/query_exec_functions.py
+++ b/tests/performance/query_exec_functions.py
@@ -31,7 +31,6 @@ DEFAULT_BEESWAX_PORT = 21000
 DEFAULT_HS2_PORT = 21050
 DEFAULT_HIVE_HS2_PORT = 10000
 
-logging.basicConfig(level=logging.INFO, format='[%(name)s] %(threadName)s: %(message)s')
 LOG = logging.getLogger('query_exec_functions')
 
 def get_hs2_hive_cursor(hiveserver, user=None, use_kerberos=False,

http://git-wip-us.apache.org/repos/asf/impala/blob/30bb0b3d/tests/performance/query_executor.py
----------------------------------------------------------------------
diff --git a/tests/performance/query_executor.py b/tests/performance/query_executor.py
index 1f4bec2..501f34a 100644
--- a/tests/performance/query_executor.py
+++ b/tests/performance/query_executor.py
@@ -36,7 +36,6 @@ import re
 from tests.performance.query import Query
 
 # Setup logging for this module.
-logging.basicConfig(level=logging.INFO, format='[%(name)s] %(threadName)s: %(message)s')
 LOG = logging.getLogger('query_executor')
 LOG.setLevel(level=logging.INFO)
 

http://git-wip-us.apache.org/repos/asf/impala/blob/30bb0b3d/tests/performance/scheduler.py
----------------------------------------------------------------------
diff --git a/tests/performance/scheduler.py b/tests/performance/scheduler.py
index 8a97dd4..760b331 100644
--- a/tests/performance/scheduler.py
+++ b/tests/performance/scheduler.py
@@ -28,11 +28,9 @@ from sys import exit
 from threading import Lock, Thread, Event
 import threading
 
-logging.basicConfig(level=logging.INFO, format='%(name)s %(threadName)s: %(message)s')
 LOG = logging.getLogger('scheduler')
 LOG.setLevel(level=logging.DEBUG)
 
-
 class Scheduler(object):
   """Schedules the submission of workloads across one of more clients.
 

http://git-wip-us.apache.org/repos/asf/impala/blob/30bb0b3d/tests/performance/workload_runner.py
----------------------------------------------------------------------
diff --git a/tests/performance/workload_runner.py b/tests/performance/workload_runner.py
index a50af9c..9cee7e0 100644
--- a/tests/performance/workload_runner.py
+++ b/tests/performance/workload_runner.py
@@ -34,8 +34,6 @@ from tests.performance.query_exec_functions import (
     execute_using_jdbc)
 from tests.performance.scheduler import Scheduler
 
-# Setup Logging
-logging.basicConfig(level=logging.INFO, format='[%(name)s]: %(message)s')
 LOG = logging.getLogger('workload_runner')
 
 

http://git-wip-us.apache.org/repos/asf/impala/blob/30bb0b3d/tests/run-tests.py
----------------------------------------------------------------------
diff --git a/tests/run-tests.py b/tests/run-tests.py
index b1a9fdd..78c152b 100755
--- a/tests/run-tests.py
+++ b/tests/run-tests.py
@@ -24,6 +24,7 @@
 # All additional command line options are passed to py.test.
 from tests.common.impala_cluster import ImpalaCluster
 from tests.common.impala_service import ImpaladService
+from tests.conftest import configure_logging
 import itertools
 import json
 import multiprocessing
@@ -224,6 +225,8 @@ def print_metrics(substring):
 
 
 if __name__ == "__main__":
+  # Ensure that logging is configured for the 'run-test.py' wrapper itself.
+  configure_logging()
   exit_on_error = '-x' in sys.argv or '--exitfirst' in sys.argv
   skip_serial = '--skip-serial' in sys.argv
   if skip_serial:

http://git-wip-us.apache.org/repos/asf/impala/blob/30bb0b3d/tests/util/plugin_runner.py
----------------------------------------------------------------------
diff --git a/tests/util/plugin_runner.py b/tests/util/plugin_runner.py
index 0bd8fe6..a0f0d90 100644
--- a/tests/util/plugin_runner.py
+++ b/tests/util/plugin_runner.py
@@ -22,7 +22,6 @@ import pkgutil
 PLUGIN_DIR = os.path.join(os.environ['IMPALA_HOME'], 'tests', 'benchmark', 'plugins')
 
 # Setup logging for this module.
-logging.basicConfig(level=logging.INFO, format='%(filename)s: %(message)s')
 LOG = logging.getLogger('plugin_runner')
 LOG.setLevel(level=logging.INFO)