You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@impala.apache.org by kw...@apache.org on 2017/02/13 19:23:24 UTC

[1/4] incubator-impala git commit: IMPALA-4884: Add JVM heap and non-heap usage in metrics and UI

Repository: incubator-impala
Updated Branches:
  refs/heads/master 6242478d0 -> f982c3f76


IMPALA-4884: Add JVM heap and non-heap usage in metrics and UI

This commit adds heap and non-heap memory usage of the embedded
JVM in the memory metrics and exposes these metrics in /memz web page of
the impalad and catalog web UI.

Change-Id: I543d4d428d7240e0f710d67973867162f2fcabc8
Reviewed-on: http://gerrit.cloudera.org:8080/5909
Reviewed-by: Dimitris Tsirogiannis <dt...@cloudera.com>
Tested-by: Impala Public Jenkins


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

Branch: refs/heads/master
Commit: af4699847090ff7d03a588aa1b2e48170c64f03f
Parents: 6242478
Author: Dimitris Tsirogiannis <dt...@cloudera.com>
Authored: Sat Feb 4 21:14:05 2017 -0800
Committer: Impala Public Jenkins <im...@gerrit.cloudera.org>
Committed: Sat Feb 11 01:34:13 2017 +0000

----------------------------------------------------------------------
 be/src/util/default-path-handlers.cc            | 12 +++-
 .../java/org/apache/impala/common/JniUtil.java  | 36 ++++++++++
 www/memz.tmpl                                   | 70 ++++++++++++++++++--
 3 files changed, 110 insertions(+), 8 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-impala/blob/af469984/be/src/util/default-path-handlers.cc
----------------------------------------------------------------------
diff --git a/be/src/util/default-path-handlers.cc b/be/src/util/default-path-handlers.cc
index 91c51d3..732d3b3 100644
--- a/be/src/util/default-path-handlers.cc
+++ b/be/src/util/default-path-handlers.cc
@@ -148,17 +148,23 @@ void MemUsageHandler(MemTracker* mem_tracker, MetricGroup* metric_group,
     if (jvm_group != NULL) {
       Value jvm(kObjectType);
       jvm_group->ToJson(false, document, &jvm);
+      Value heap(kArrayType);
+      Value non_heap(kArrayType);
       Value total(kArrayType);
       for (SizeType i = 0; i < jvm["metrics"].Size(); ++i) {
         if (strstr(jvm["metrics"][i]["name"].GetString(), "total") != nullptr) {
           total.PushBack(jvm["metrics"][i], document->GetAllocator());
+        } else if (strstr(jvm["metrics"][i]["name"].GetString(), "non-heap") != nullptr) {
+          non_heap.PushBack(jvm["metrics"][i], document->GetAllocator());
+        } else if (strstr(jvm["metrics"][i]["name"].GetString(), "heap") != nullptr) {
+          heap.PushBack(jvm["metrics"][i], document->GetAllocator());
         }
       }
-      document->AddMember("jvm", total, document->GetAllocator());
-
+      document->AddMember("jvm_total", total, document->GetAllocator());
+      document->AddMember("jvm_heap", heap, document->GetAllocator());
+      document->AddMember("jvm_non_heap", non_heap, document->GetAllocator());
     }
   }
-
 }
 
 

http://git-wip-us.apache.org/repos/asf/incubator-impala/blob/af469984/fe/src/main/java/org/apache/impala/common/JniUtil.java
----------------------------------------------------------------------
diff --git a/fe/src/main/java/org/apache/impala/common/JniUtil.java b/fe/src/main/java/org/apache/impala/common/JniUtil.java
index 4446a55..223c8bb 100644
--- a/fe/src/main/java/org/apache/impala/common/JniUtil.java
+++ b/fe/src/main/java/org/apache/impala/common/JniUtil.java
@@ -22,6 +22,7 @@ import java.io.PrintWriter;
 import java.io.StringWriter;
 import java.io.Writer;
 import java.lang.management.ManagementFactory;
+import java.lang.management.MemoryMXBean;
 import java.lang.management.MemoryPoolMXBean;
 import java.lang.management.MemoryUsage;
 import java.util.ArrayList;
@@ -142,6 +143,41 @@ public class JniUtil {
         }
       }
     }
+
+    if (request.get_all || request.getMemory_pool().equals("heap")) {
+      // Populate heap usage
+      MemoryMXBean mBean = ManagementFactory.getMemoryMXBean();
+      TJvmMemoryPool heap = new TJvmMemoryPool();
+      MemoryUsage heapUsage = mBean.getHeapMemoryUsage();
+      heap.setCommitted(heapUsage.getCommitted());
+      heap.setInit(heapUsage.getInit());
+      heap.setMax(heapUsage.getMax());
+      heap.setUsed(heapUsage.getUsed());
+      heap.setName("heap");
+      heap.setPeak_committed(0);
+      heap.setPeak_init(0);
+      heap.setPeak_max(0);
+      heap.setPeak_used(0);
+      jvmMetrics.getMemory_pools().add(heap);
+    }
+
+    if (request.get_all || request.getMemory_pool().equals("non-heap")) {
+      // Populate non-heap usage
+      MemoryMXBean mBean = ManagementFactory.getMemoryMXBean();
+      TJvmMemoryPool nonHeap = new TJvmMemoryPool();
+      MemoryUsage nonHeapUsage = mBean.getNonHeapMemoryUsage();
+      nonHeap.setCommitted(nonHeapUsage.getCommitted());
+      nonHeap.setInit(nonHeapUsage.getInit());
+      nonHeap.setMax(nonHeapUsage.getMax());
+      nonHeap.setUsed(nonHeapUsage.getUsed());
+      nonHeap.setName("non-heap");
+      nonHeap.setPeak_committed(0);
+      nonHeap.setPeak_init(0);
+      nonHeap.setPeak_max(0);
+      nonHeap.setPeak_used(0);
+      jvmMetrics.getMemory_pools().add(nonHeap);
+    }
+
     TSerializer serializer = new TSerializer(protocolFactory_);
     try {
       return serializer.serialize(jvmMetrics);

http://git-wip-us.apache.org/repos/asf/incubator-impala/blob/af469984/www/memz.tmpl
----------------------------------------------------------------------
diff --git a/www/memz.tmpl b/www/memz.tmpl
index f54cdaa..48bd651 100644
--- a/www/memz.tmpl
+++ b/www/memz.tmpl
@@ -28,15 +28,15 @@ Memory consumption / limit: <strong>{{consumption}}</strong> / <strong>{{mem_lim
 <h3>Breakdown</h3>
 <pre>{{detailed}}</pre>
 
-{{?jvm}}
-<h3>JVM memory total usage</h3>
+{{?jvm_heap}}
+<h3>JVM heap usage</h3>
 <table class='table table-bordered table-hover'>
   <tr>
     <th>Name</th>
     <th>Value</th>
     <th>Description</th>
   </tr>
-  {{#jvm}}
+  {{#jvm_heap}}
   <tr>
     <td><tt>{{name}}</tt></td>
     {{! Is this a stats metric? }}
@@ -54,8 +54,68 @@ Memory consumption / limit: <strong>{{consumption}}</strong> / <strong>{{mem_lim
       {{description}}
     </td>
   </tr>
-  {{/jvm}}
+  {{/jvm_heap}}
 </table>
-{{/jvm}}
+{{/jvm_heap}}
+
+{{?jvm_non_heap}}
+<h3>JVM non-heap usage</h3>
+<table class='table table-bordered table-hover'>
+  <tr>
+    <th>Name</th>
+    <th>Value</th>
+    <th>Description</th>
+  </tr>
+  {{#jvm_non_heap}}
+  <tr>
+    <td><tt>{{name}}</tt></td>
+    {{! Is this a stats metric? }}
+    {{?mean}}
+    <td>
+      Last (of {{count}}): <strong>{{last}}</strong>.
+      Min: {{min}}, max: {{max}}, avg: {{mean}}</td>
+    {{/mean}}
+    {{^mean}}
+    <td>
+      {{human_readable}}
+    </td>
+    {{/mean}}
+    <td>
+      {{description}}
+    </td>
+  </tr>
+  {{/jvm_non_heap}}
+</table>
+{{/jvm_non_heap}}
+
+{{?jvm_total}}
+<h3>JVM total memory usage</h3>
+<table class='table table-bordered table-hover'>
+  <tr>
+    <th>Name</th>
+    <th>Value</th>
+    <th>Description</th>
+  </tr>
+  {{#jvm_total}}
+  <tr>
+    <td><tt>{{name}}</tt></td>
+    {{! Is this a stats metric? }}
+    {{?mean}}
+    <td>
+      Last (of {{count}}): <strong>{{last}}</strong>.
+      Min: {{min}}, max: {{max}}, avg: {{mean}}</td>
+    {{/mean}}
+    {{^mean}}
+    <td>
+      {{human_readable}}
+    </td>
+    {{/mean}}
+    <td>
+      {{description}}
+    </td>
+  </tr>
+  {{/jvm_total}}
+</table>
+{{/jvm_total}}
 
 {{> www/common-footer.tmpl }}


[4/4] incubator-impala git commit: IMPALA-2020, IMPALA-4809: Codegen support for DECIMAL_V2

Posted by kw...@apache.org.
IMPALA-2020, IMPALA-4809: Codegen support for DECIMAL_V2

Currently, codegen supports converting type attributes (e.g.
decimal type's precision and scale, type's size) obtained via
calls to FunctionContextImpl::GetFnAttr() (previously
Expr::GetConstantInt()) in cross-compiled code to runtime constants.
This change extends this support for the query option DECIMAL_V2.

To test this change, this change also handles a subset of IMPALA-2020:
casting between decimal values is  updated to support rounding (instead
of truncation) when decimal_v2 is true.

This change also cleans up the existing code by moving the codegen
logic Expr::InlineConstant() to the codegen module and the type
related logic in Expr::GetConstantInt() to FunctionContextImpl.

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


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

Branch: refs/heads/master
Commit: f982c3f76e762f646b52a01659796b4edcfbc1ad
Parents: a78726d
Author: Michael Ho <kw...@cloudera.com>
Authored: Fri Feb 3 20:55:18 2017 -0800
Committer: Impala Public Jenkins <im...@gerrit.cloudera.org>
Committed: Sat Feb 11 07:07:45 2017 +0000

----------------------------------------------------------------------
 be/src/benchmarks/hash-benchmark.cc             |  18 +-
 be/src/codegen/llvm-codegen-test.cc             |  60 +++--
 be/src/codegen/llvm-codegen.cc                  |  98 +++++++--
 be/src/codegen/llvm-codegen.h                   |  48 ++--
 be/src/exec/partitioned-aggregation-node.cc     |   8 +-
 be/src/exec/partitioned-aggregation-node.h      |   2 +-
 be/src/exprs/agg-fn-evaluator.cc                |   2 +-
 be/src/exprs/aggregate-functions-ir.cc          |  12 +-
 be/src/exprs/conditional-functions-ir.cc        |  27 ++-
 be/src/exprs/decimal-functions-ir.cc            |  78 +++----
 be/src/exprs/decimal-operators-ir.cc            | 219 ++++++++++---------
 be/src/exprs/decimal-operators.h                |   2 +-
 be/src/exprs/expr-codegen-test.cc               | 152 ++++++++-----
 be/src/exprs/expr-test.cc                       |  12 +-
 be/src/exprs/expr.cc                            |  86 --------
 be/src/exprs/expr.h                             |  59 -----
 be/src/exprs/math-functions-ir.cc               |   4 +-
 be/src/exprs/scalar-fn-call.cc                  |   4 +-
 be/src/runtime/decimal-value.inline.h           |   3 +-
 be/src/runtime/lib-cache.cc                     |   9 +-
 be/src/runtime/runtime-state.cc                 |   2 +-
 be/src/runtime/runtime-state.h                  |   1 +
 be/src/service/frontend.cc                      |   2 +-
 be/src/udf/udf-internal.h                       |  47 +++-
 be/src/udf/udf-test-harness.cc                  |   5 +-
 be/src/udf/udf-test-harness.h                   |   4 +-
 be/src/udf/udf.cc                               |  60 ++++-
 .../queries/QueryTest/decimal.test              |  44 ++++
 tests/query_test/test_decimal_casting.py        |  70 +++---
 29 files changed, 646 insertions(+), 492 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-impala/blob/f982c3f7/be/src/benchmarks/hash-benchmark.cc
----------------------------------------------------------------------
diff --git a/be/src/benchmarks/hash-benchmark.cc b/be/src/benchmarks/hash-benchmark.cc
index 125310b..fb37bed 100644
--- a/be/src/benchmarks/hash-benchmark.cc
+++ b/be/src/benchmarks/hash-benchmark.cc
@@ -26,6 +26,7 @@
 #include "experiments/data-provider.h"
 #include "runtime/mem-tracker.h"
 #include "runtime/string-value.h"
+#include "runtime/test-env.h"
 #include "util/benchmark.h"
 #include "util/cpu-info.h"
 #include "util/hash-util.h"
@@ -419,17 +420,24 @@ int main(int argc, char **argv) {
 
   const int NUM_ROWS = 1024;
 
-  ObjectPool obj_pool;
+  Status status;
+  RuntimeState* state;
+  TestEnv test_env;
+  status = test_env.CreateQueryState(0, 0, 0, nullptr, &state);
+  if (!status.ok()) {
+    cout << "Could not create RuntimeState";
+    return -1;
+  }
+
   MemTracker tracker;
   MemPool mem_pool(&tracker);
-  RuntimeProfile int_profile(&obj_pool, "IntGen");
-  RuntimeProfile mixed_profile(&obj_pool, "MixedGen");
+  RuntimeProfile int_profile(state->obj_pool(), "IntGen");
+  RuntimeProfile mixed_profile(state->obj_pool(), "MixedGen");
   DataProvider int_provider(&mem_pool, &int_profile);
   DataProvider mixed_provider(&mem_pool, &mixed_profile);
 
-  Status status;
   scoped_ptr<LlvmCodeGen> codegen;
-  status = LlvmCodeGen::CreateImpalaCodegen(&obj_pool, NULL, "test", &codegen);
+  status = LlvmCodeGen::CreateImpalaCodegen(state, NULL, "test", &codegen);
   if (!status.ok()) {
     cout << "Could not start codegen.";
     return -1;

http://git-wip-us.apache.org/repos/asf/incubator-impala/blob/f982c3f7/be/src/codegen/llvm-codegen-test.cc
----------------------------------------------------------------------
diff --git a/be/src/codegen/llvm-codegen-test.cc b/be/src/codegen/llvm-codegen-test.cc
index 412426f..59c66f4 100644
--- a/be/src/codegen/llvm-codegen-test.cc
+++ b/be/src/codegen/llvm-codegen-test.cc
@@ -24,6 +24,8 @@
 #include "common/init.h"
 #include "common/object-pool.h"
 #include "runtime/string-value.h"
+#include "runtime/test-env.h"
+#include "service/fe-support.h"
 #include "util/cpu-info.h"
 #include "util/hash-util.h"
 #include "util/path-builder.h"
@@ -38,13 +40,27 @@ namespace impala {
 
 class LlvmCodeGenTest : public testing:: Test {
  protected:
+  scoped_ptr<TestEnv> test_env_;
+  RuntimeState* runtime_state_;
+
+  virtual void SetUp() {
+    test_env_.reset(new TestEnv());
+    EXPECT_OK(test_env_->CreateQueryState(0, 1, 8 * 1024 * 1024, nullptr,
+        &runtime_state_));
+  }
+
+  virtual void TearDown() {
+    runtime_state_ = NULL;
+    test_env_.reset();
+  }
+
   static void LifetimeTest() {
     ObjectPool pool;
     Status status;
     for (int i = 0; i < 10; ++i) {
-      LlvmCodeGen object1(&pool, NULL, "Test");
-      LlvmCodeGen object2(&pool, NULL, "Test");
-      LlvmCodeGen object3(&pool, NULL, "Test");
+      LlvmCodeGen object1(NULL, &pool, NULL, "Test");
+      LlvmCodeGen object2(NULL, &pool, NULL, "Test");
+      LlvmCodeGen object3(NULL, &pool, NULL, "Test");
 
       ASSERT_OK(object1.Init(unique_ptr<Module>(new Module("Test", object1.context()))));
       ASSERT_OK(object2.Init(unique_ptr<Module>(new Module("Test", object2.context()))));
@@ -53,22 +69,12 @@ class LlvmCodeGenTest : public testing:: Test {
   }
 
   // Wrapper to call private test-only methods on LlvmCodeGen object
-  static Status CreateFromFile(
-      ObjectPool* pool, const string& filename, scoped_ptr<LlvmCodeGen>* codegen) {
-    RETURN_IF_ERROR(LlvmCodeGen::CreateFromFile(pool, NULL, filename, "test", codegen));
+  Status CreateFromFile(const string& filename, scoped_ptr<LlvmCodeGen>* codegen) {
+    RETURN_IF_ERROR(LlvmCodeGen::CreateFromFile(runtime_state_,
+        runtime_state_->obj_pool(), NULL, filename, "test", codegen));
     return (*codegen)->MaterializeModule();
   }
 
-  static LlvmCodeGen* CreateCodegen(ObjectPool* pool) {
-    LlvmCodeGen* codegen = pool->Add(new LlvmCodeGen(pool, NULL, "Test"));
-    if (codegen != NULL) {
-      Status status =
-          codegen->Init(unique_ptr<Module>(new Module("Test", codegen->context())));
-      if (!status.ok()) return NULL;
-    }
-    return codegen;
-  }
-
   static void ClearHashFns(LlvmCodeGen* codegen) {
     codegen->ClearHashFns();
   }
@@ -104,11 +110,9 @@ TEST_F(LlvmCodeGenTest, MultithreadedLifetime) {
 
 // Test loading a non-existent file
 TEST_F(LlvmCodeGenTest, BadIRFile) {
-  ObjectPool pool;
   string module_file = "NonExistentFile.ir";
   scoped_ptr<LlvmCodeGen> codegen;
-  EXPECT_FALSE(
-      LlvmCodeGenTest::CreateFromFile(&pool, module_file.c_str(), &codegen).ok());
+  EXPECT_FALSE(CreateFromFile(module_file.c_str(), &codegen).ok());
 }
 
 // IR for the generated linner loop
@@ -159,7 +163,6 @@ Function* CodegenInnerLoop(LlvmCodeGen* codegen, int64_t* jitted_counter, int de
 //   5. Updated the jitted loop in place with another jitted inner loop function
 //   6. Run the loop and make sure the updated is called.
 TEST_F(LlvmCodeGenTest, ReplaceFnCall) {
-  ObjectPool pool;
   const string loop_call_name("_Z21DefaultImplementationv");
   const string loop_name("_Z8TestLoopi");
   typedef void (*TestLoopFn)(int);
@@ -169,7 +172,7 @@ TEST_F(LlvmCodeGenTest, ReplaceFnCall) {
 
   // Part 1: Load the module and make sure everything is loaded correctly.
   scoped_ptr<LlvmCodeGen> codegen;
-  ASSERT_OK(LlvmCodeGenTest::CreateFromFile(&pool, module_file.c_str(), &codegen));
+  ASSERT_OK(CreateFromFile(module_file.c_str(), &codegen));
   EXPECT_TRUE(codegen.get() != NULL);
 
   Function* loop_call = codegen->GetFunction(loop_call_name, false);
@@ -285,10 +288,8 @@ Function* CodegenStringTest(LlvmCodeGen* codegen) {
 // struct.  Just create a simple StringValue struct and make sure the IR can read it
 // and modify it.
 TEST_F(LlvmCodeGenTest, StringValue) {
-  ObjectPool pool;
-
   scoped_ptr<LlvmCodeGen> codegen;
-  ASSERT_OK(LlvmCodeGen::CreateImpalaCodegen(&pool, NULL, "test", &codegen));
+  ASSERT_OK(LlvmCodeGen::CreateImpalaCodegen(runtime_state_, NULL, "test", &codegen));
   EXPECT_TRUE(codegen.get() != NULL);
 
   string str("Test");
@@ -328,10 +329,8 @@ TEST_F(LlvmCodeGenTest, StringValue) {
 
 // Test calling memcpy intrinsic
 TEST_F(LlvmCodeGenTest, MemcpyTest) {
-  ObjectPool pool;
-
   scoped_ptr<LlvmCodeGen> codegen;
-  ASSERT_OK(LlvmCodeGen::CreateImpalaCodegen(&pool, NULL, "test", &codegen));
+  ASSERT_OK(LlvmCodeGen::CreateImpalaCodegen(runtime_state_, NULL, "test", &codegen));
   ASSERT_TRUE(codegen.get() != NULL);
 
   LlvmCodeGen::FnPrototype prototype(codegen.get(), "MemcpyTest", codegen->void_type());
@@ -367,8 +366,6 @@ TEST_F(LlvmCodeGenTest, MemcpyTest) {
 
 // Test codegen for hash
 TEST_F(LlvmCodeGenTest, HashTest) {
-  ObjectPool pool;
-
   // Values to compute hash on
   const char* data1 = "test string";
   const char* data2 = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
@@ -378,7 +375,7 @@ TEST_F(LlvmCodeGenTest, HashTest) {
   // Loop to test both the sse4 on/off paths
   for (int i = 0; i < 2; ++i) {
     scoped_ptr<LlvmCodeGen> codegen;
-    ASSERT_OK(LlvmCodeGen::CreateImpalaCodegen(&pool, NULL, "test", &codegen));
+    ASSERT_OK(LlvmCodeGen::CreateImpalaCodegen(runtime_state_, NULL, "test", &codegen));
     ASSERT_TRUE(codegen.get() != NULL);
 
     Value* llvm_data1 =
@@ -452,7 +449,8 @@ TEST_F(LlvmCodeGenTest, HashTest) {
 
 int main(int argc, char **argv) {
   ::testing::InitGoogleTest(&argc, argv);
-  impala::InitCommonRuntime(argc, argv, false, impala::TestInfo::BE_TEST);
+  impala::InitCommonRuntime(argc, argv, true, impala::TestInfo::BE_TEST);
+  impala::InitFeSupport(false);
   impala::LlvmCodeGen::InitializeLlvm();
   return RUN_ALL_TESTS();
 }

http://git-wip-us.apache.org/repos/asf/incubator-impala/blob/f982c3f7/be/src/codegen/llvm-codegen.cc
----------------------------------------------------------------------
diff --git a/be/src/codegen/llvm-codegen.cc b/be/src/codegen/llvm-codegen.cc
index 6cf43f6..a548654 100644
--- a/be/src/codegen/llvm-codegen.cc
+++ b/be/src/codegen/llvm-codegen.cc
@@ -58,12 +58,14 @@
 #include "codegen/instruction-counter.h"
 #include "codegen/mcjit-mem-mgr.h"
 #include "common/logging.h"
+#include "exprs/anyval-util.h"
 #include "impala-ir/impala-ir-names.h"
 #include "runtime/descriptors.h"
 #include "runtime/hdfs-fs-cache.h"
 #include "runtime/lib-cache.h"
 #include "runtime/mem-pool.h"
 #include "runtime/mem-tracker.h"
+#include "runtime/runtime-state.h"
 #include "runtime/string-value.h"
 #include "runtime/timestamp-value.h"
 #include "util/cpu-info.h"
@@ -170,7 +172,8 @@ Status LlvmCodeGen::InitializeLlvm(bool load_backend) {
 
   ObjectPool init_pool;
   scoped_ptr<LlvmCodeGen> init_codegen;
-  RETURN_IF_ERROR(LlvmCodeGen::CreateFromMemory(&init_pool, NULL, "init", &init_codegen));
+  RETURN_IF_ERROR(LlvmCodeGen::CreateFromMemory(
+      nullptr, &init_pool, nullptr, "init", &init_codegen));
   // LLVM will construct "use" lists only when the entire module is materialized.
   RETURN_IF_ERROR(init_codegen->MaterializeModule());
 
@@ -209,9 +212,10 @@ Status LlvmCodeGen::InitializeLlvm(bool load_backend) {
   return Status::OK();
 }
 
-LlvmCodeGen::LlvmCodeGen(
-    ObjectPool* pool, MemTracker* parent_mem_tracker, const string& id)
-  : id_(id),
+LlvmCodeGen::LlvmCodeGen(RuntimeState* state, ObjectPool* pool,
+    MemTracker* parent_mem_tracker, const string& id)
+  : state_(state),
+    id_(id),
     profile_(pool, "CodeGen"),
     mem_tracker_(new MemTracker(&profile_, -1, "CodeGen", parent_mem_tracker)),
     optimizations_enabled_(false),
@@ -233,9 +237,10 @@ LlvmCodeGen::LlvmCodeGen(
   num_instructions_ = ADD_COUNTER(&profile_, "NumInstructions", TUnit::UNIT);
 }
 
-Status LlvmCodeGen::CreateFromFile(ObjectPool* pool, MemTracker* parent_mem_tracker,
-    const string& file, const string& id, scoped_ptr<LlvmCodeGen>* codegen) {
-  codegen->reset(new LlvmCodeGen(pool, parent_mem_tracker, id));
+Status LlvmCodeGen::CreateFromFile(RuntimeState* state, ObjectPool* pool,
+    MemTracker* parent_mem_tracker, const string& file, const string& id,
+    scoped_ptr<LlvmCodeGen>* codegen) {
+  codegen->reset(new LlvmCodeGen(state, pool, parent_mem_tracker, id));
   SCOPED_TIMER((*codegen)->profile_.total_time_counter());
 
   unique_ptr<Module> loaded_module;
@@ -244,9 +249,9 @@ Status LlvmCodeGen::CreateFromFile(ObjectPool* pool, MemTracker* parent_mem_trac
   return (*codegen)->Init(std::move(loaded_module));
 }
 
-Status LlvmCodeGen::CreateFromMemory(ObjectPool* pool, MemTracker* parent_mem_tracker,
-    const string& id, scoped_ptr<LlvmCodeGen>* codegen) {
-  codegen->reset(new LlvmCodeGen(pool, parent_mem_tracker, id));
+Status LlvmCodeGen::CreateFromMemory(RuntimeState* state, ObjectPool* pool,
+    MemTracker* parent_mem_tracker, const string& id, scoped_ptr<LlvmCodeGen>* codegen) {
+  codegen->reset(new LlvmCodeGen(state, pool, parent_mem_tracker, id));
   SCOPED_TIMER((*codegen)->profile_.total_time_counter());
 
   // Select the appropriate IR version. We cannot use LLVM IR with SSE4.2 instructions on
@@ -355,9 +360,12 @@ void LlvmCodeGen::StripGlobalCtorsDtors(llvm::Module* module) {
   if (destructors != NULL) destructors->eraseFromParent();
 }
 
-Status LlvmCodeGen::CreateImpalaCodegen(ObjectPool* pool, MemTracker* parent_mem_tracker,
-    const string& id, scoped_ptr<LlvmCodeGen>* codegen_ret) {
-  RETURN_IF_ERROR(CreateFromMemory(pool, parent_mem_tracker, id, codegen_ret));
+Status LlvmCodeGen::CreateImpalaCodegen(RuntimeState* state,
+    MemTracker* parent_mem_tracker, const string& id,
+    scoped_ptr<LlvmCodeGen>* codegen_ret) {
+  DCHECK(state != nullptr);
+  RETURN_IF_ERROR(CreateFromMemory(
+      state, state->obj_pool(), parent_mem_tracker, id, codegen_ret));
   LlvmCodeGen* codegen = codegen_ret->get();
 
   // Parse module for cross compiled functions and types
@@ -681,17 +689,19 @@ Function* LlvmCodeGen::GetFunction(IRFunction::Type ir_type, bool clone) {
 bool LlvmCodeGen::VerifyFunction(Function* fn) {
   if (is_corrupt_) return false;
 
-  // Check that there are no calls to Expr::GetConstant(). These should all have been
-  // inlined via Expr::InlineConstants().
+  // Check that there are no calls to FunctionContextImpl::GetConstFnAttr(). These should all
+  // have been inlined via InlineConstFnAttrs().
   for (inst_iterator iter = inst_begin(fn); iter != inst_end(fn); ++iter) {
     Instruction* instr = &*iter;
     if (!isa<CallInst>(instr)) continue;
     CallInst* call_instr = reinterpret_cast<CallInst*>(instr);
     Function* called_fn = call_instr->getCalledFunction();
-    // look for call to Expr::GetConstant()
-    if (called_fn != NULL &&
-        called_fn->getName().find(Expr::GET_CONSTANT_INT_SYMBOL_PREFIX) != string::npos) {
-      LOG(ERROR) << "Found call to Expr::GetConstant*(): " << Print(call_instr);
+
+    // Look for call to FunctionContextImpl::GetConstFnAttr().
+    if (called_fn != nullptr &&
+        called_fn->getName() == FunctionContextImpl::GET_CONST_FN_ATTR_SYMBOL) {
+      LOG(ERROR) << "Found call to FunctionContextImpl::GetConstFnAttr(): "
+                 << Print(call_instr);
       is_corrupt_ = true;
       break;
     }
@@ -916,6 +926,47 @@ int LlvmCodeGen::ReplaceCallSitesWithBoolConst(llvm::Function* caller, bool cons
   return ReplaceCallSitesWithValue(caller, replacement, target_name);
 }
 
+int LlvmCodeGen::InlineConstFnAttrs(const FunctionContext::TypeDesc& ret_type,
+    const vector<FunctionContext::TypeDesc>& arg_types, Function* fn) {
+  int replaced = 0;
+  for (inst_iterator iter = inst_begin(fn), end = inst_end(fn); iter != end; ) {
+    // Increment iter now so we don't mess it up modifying the instruction below
+    Instruction* instr = &*(iter++);
+
+    // Look for call instructions
+    if (!isa<CallInst>(instr)) continue;
+    CallInst* call_instr = cast<CallInst>(instr);
+    Function* called_fn = call_instr->getCalledFunction();
+
+    // Look for call to FunctionContextImpl::GetConstFnAttr().
+    if (called_fn == nullptr ||
+        called_fn->getName() != FunctionContextImpl::GET_CONST_FN_ATTR_SYMBOL) {
+      continue;
+    }
+
+    // 't' and 'i' arguments must be constant
+    ConstantInt* t_arg = dyn_cast<ConstantInt>(call_instr->getArgOperand(1));
+    ConstantInt* i_arg = dyn_cast<ConstantInt>(call_instr->getArgOperand(2));
+    // This optimization is only applied to built-ins which should have constant args.
+    DCHECK(t_arg != nullptr)
+        << "Non-constant 't' argument to FunctionContextImpl::GetConstFnAttr()";
+    DCHECK(i_arg != nullptr)
+        << "Non-constant 'i' argument to FunctionContextImpl::GetConstFnAttr";
+
+    // Replace the called function with the appropriate constant
+    FunctionContextImpl::ConstFnAttr t_val =
+        static_cast<FunctionContextImpl::ConstFnAttr>(t_arg->getSExtValue());
+    int i_val = static_cast<int>(i_arg->getSExtValue());
+    DCHECK(state_ != nullptr);
+    // All supported constants are currently integers.
+    call_instr->replaceAllUsesWith(ConstantInt::get(GetType(TYPE_INT),
+        FunctionContextImpl::GetConstFnAttr(state_, ret_type, arg_types, t_val, i_val)));
+    call_instr->eraseFromParent();
+    ++replaced;
+  }
+  return replaced;
+}
+
 void LlvmCodeGen::FindCallSites(Function* caller, const string& target_name,
       vector<CallInst*>* results) {
   for (inst_iterator iter = inst_begin(caller); iter != inst_end(caller); ++iter) {
@@ -1216,10 +1267,15 @@ void LlvmCodeGen::CodegenDebugTrace(LlvmBuilder* builder, const char* str,
   builder->CreateCall(printf, calling_args);
 }
 
-void LlvmCodeGen::GetSymbols(unordered_set<string>* symbols) {
-  for (const Function& fn: module_->functions()) {
+Status LlvmCodeGen::GetSymbols(const string& file, const string& module_id,
+    unordered_set<string>* symbols) {
+  ObjectPool pool;
+  scoped_ptr<LlvmCodeGen> codegen;
+  RETURN_IF_ERROR(CreateFromFile(nullptr, &pool, nullptr, file, module_id, &codegen));
+  for (const Function& fn: codegen->module_->functions()) {
     if (fn.isMaterializable()) symbols->insert(fn.getName());
   }
+  return Status::OK();
 }
 
 // TODO: cache this function (e.g. all min(int, int) are identical).

http://git-wip-us.apache.org/repos/asf/incubator-impala/blob/f982c3f7/be/src/codegen/llvm-codegen.h
----------------------------------------------------------------------
diff --git a/be/src/codegen/llvm-codegen.h b/be/src/codegen/llvm-codegen.h
index d51faab..058403d 100644
--- a/be/src/codegen/llvm-codegen.h
+++ b/be/src/codegen/llvm-codegen.h
@@ -153,17 +153,9 @@ class LlvmCodeGen {
   /// 'codegen' will contain the created object on success.
   /// 'parent_mem_tracker' - if non-NULL, the CodeGen MemTracker is created under this.
   /// 'id' is used for outputting the IR module for debugging.
-  static Status CreateImpalaCodegen(ObjectPool*, MemTracker* parent_mem_tracker,
+  static Status CreateImpalaCodegen(RuntimeState* state, MemTracker* parent_mem_tracker,
       const std::string& id, boost::scoped_ptr<LlvmCodeGen>* codegen);
 
-  /// Creates a LlvmCodeGen instance initialized with the module bitcode from 'file'.
-  /// 'codegen' will contain the created object on success. The functions in the module
-  /// are materialized lazily. Getting a reference to a function via GetFunction() will
-  /// materialize the function and its callees recursively.
-  static Status CreateFromFile(ObjectPool*, MemTracker* parent_mem_tracker,
-      const std::string& file, const std::string& id,
-      boost::scoped_ptr<LlvmCodeGen>* codegen);
-
   /// Removes all jit compiled dynamically linked functions from the process.
   ~LlvmCodeGen();
 
@@ -204,6 +196,7 @@ class LlvmCodeGen {
     void AddArgument(const NamedVariable& var) {
       args_.push_back(var);
     }
+
     void AddArgument(const std::string& name, llvm::Type* type) {
       args_.push_back(NamedVariable(name, type));
     }
@@ -333,6 +326,17 @@ class LlvmCodeGen {
   int ReplaceCallSitesWithValue(llvm::Function* caller, llvm::Value* replacement,
       const std::string& target_name);
 
+  /// This function replaces calls to FunctionContextImpl::GetConstFnAttr() with constants
+  /// derived from 'return_type', 'arg_types' and the runtime state 'state_'. Please note
+  /// that this function only replaces call instructions inside 'fn' so to replace the
+  /// call to FunctionContextImpl::GetConstFnAttr() inside the callee functions, please
+  /// inline the callee functions (by annotating them with IR_ALWAYS_INLINE).
+  ///
+  /// TODO: implement a loop unroller (or use LLVM's) so we can use
+  /// FunctionContextImpl::GetConstFnAttr() in loops
+  int InlineConstFnAttrs(const FunctionContext::TypeDesc& return_type,
+      const std::vector<FunctionContext::TypeDesc>& arg_types, llvm::Function* fn);
+
   /// Returns a copy of fn. The copy is added to the module.
   llvm::Function* CloneFunction(llvm::Function* fn);
 
@@ -471,8 +475,9 @@ class LlvmCodeGen {
   llvm::Type* void_type() { return void_type_; }
   llvm::Type* i128_type() { return llvm::Type::getIntNTy(context(), 128); }
 
-  /// Fils in 'symbols' with all the symbols in the module.
-  void GetSymbols(boost::unordered_set<std::string>* symbols);
+  /// Load the module temporarily and populate 'symbols' with the symbols in the module.
+  static Status GetSymbols(const string& file, const string& module_id,
+      boost::unordered_set<std::string>* symbols);
 
   /// Generates function to return min/max(v1, v2)
   llvm::Function* CodegenMinMax(const ColumnType& type, bool min);
@@ -524,19 +529,28 @@ class LlvmCodeGen {
   static void FindGlobalUsers(llvm::User* val, std::vector<llvm::GlobalObject*>* users);
 
   /// Top level codegen object. 'module_id' is used for debugging when outputting the IR.
-  LlvmCodeGen(
-      ObjectPool* pool, MemTracker* parent_mem_tracker, const std::string& module_id);
+  LlvmCodeGen(RuntimeState* state, ObjectPool* pool, MemTracker* parent_mem_tracker,
+      const std::string& module_id);
 
   /// Initializes the jitter and execution engine with the given module.
   Status Init(std::unique_ptr<llvm::Module> module);
 
-  /// Creates a LlvmCodeGen instance initialized with the module bitcode in memory.
+  /// Creates a LlvmCodeGen instance initialized with the module bitcode from 'file'.
   /// 'codegen' will contain the created object on success. The functions in the module
   /// are materialized lazily. Getting a reference to a function via GetFunction() will
   /// materialize the function and its callees recursively.
-  static Status CreateFromMemory(ObjectPool* pool, MemTracker* parent_mem_tracker,
+  static Status CreateFromFile(RuntimeState* state, ObjectPool* pool,
+      MemTracker* parent_mem_tracker, const std::string& file,
       const std::string& id, boost::scoped_ptr<LlvmCodeGen>* codegen);
 
+  /// Creates a LlvmCodeGen instance initialized with the module bitcode in memory.
+  /// 'codegen' will contain the created object on success. The functions in the module
+  /// are materialized lazily. Getting a reference to a function via GetFunction() will
+  /// materialize the function and its callees recursively.
+  static Status CreateFromMemory(RuntimeState* state, ObjectPool* pool,
+      MemTracker* parent_mem_tracker, const std::string& id,
+      boost::scoped_ptr<LlvmCodeGen>* codegen);
+
   /// Loads an LLVM module from 'file' which is the local path to the LLVM bitcode file.
   /// The functions in the module are materialized lazily. Getting a reference to the
   /// function via GetFunction() will materialize the function and its callees
@@ -639,6 +653,10 @@ class LlvmCodeGen {
   /// when a module is loaded to ensure that LLVM can resolve references to them.
   static boost::unordered_set<std::string> fns_to_always_materialize_;
 
+  /// Pointer to the RuntimeState which owns this codegen object. Needed in
+  /// InlineConstFnAttr() to access the query options.
+  const RuntimeState* state_;
+
   /// ID used for debugging (can be e.g. the fragment instance ID)
   std::string id_;
 

http://git-wip-us.apache.org/repos/asf/incubator-impala/blob/f982c3f7/be/src/exec/partitioned-aggregation-node.cc
----------------------------------------------------------------------
diff --git a/be/src/exec/partitioned-aggregation-node.cc b/be/src/exec/partitioned-aggregation-node.cc
index 7c75d01..83232d2 100644
--- a/be/src/exec/partitioned-aggregation-node.cc
+++ b/be/src/exec/partitioned-aggregation-node.cc
@@ -1681,8 +1681,8 @@ Status PartitionedAggregationNode::CodegenUpdateSlot(LlvmCodeGen* codegen,
     // Call the UDA to update/merge 'src' into 'dst', with the result stored in
     // 'updated_dst_val'.
     CodegenAnyVal updated_dst_val;
-    RETURN_IF_ERROR(CodegenCallUda(
-        codegen, &builder, evaluator, agg_fn_ctx_arg, input_vals, dst, &updated_dst_val));
+    RETURN_IF_ERROR(CodegenCallUda(codegen, &builder, evaluator, agg_fn_ctx_arg,
+        input_vals, dst, &updated_dst_val));
     result = updated_dst_val.ToNativeValue();
 
     if (slot_desc->is_nullable() && !special_null_handling) {
@@ -1717,7 +1717,7 @@ Status PartitionedAggregationNode::CodegenUpdateSlot(LlvmCodeGen* codegen,
 }
 
 Status PartitionedAggregationNode::CodegenCallUda(LlvmCodeGen* codegen,
-    LlvmBuilder* builder, AggFnEvaluator* evaluator, Value* agg_fn_ctx,
+    LlvmBuilder* builder, AggFnEvaluator* evaluator, Value* agg_fn_ctx_arg,
     const vector<CodegenAnyVal>& input_vals, const CodegenAnyVal& dst,
     CodegenAnyVal* updated_dst_val) {
   DCHECK_EQ(evaluator->input_expr_ctxs().size(), input_vals.size());
@@ -1727,7 +1727,7 @@ Status PartitionedAggregationNode::CodegenCallUda(LlvmCodeGen* codegen,
   // Set up arguments for call to UDA, which are the FunctionContext*, followed by
   // pointers to all input values, followed by a pointer to the destination value.
   vector<Value*> uda_fn_args;
-  uda_fn_args.push_back(agg_fn_ctx);
+  uda_fn_args.push_back(agg_fn_ctx_arg);
 
   // Create pointers to input args to pass to uda_fn. We must use the unlowered type,
   // e.g. IntVal, because the UDA interface expects the values to be passed as const

http://git-wip-us.apache.org/repos/asf/incubator-impala/blob/f982c3f7/be/src/exec/partitioned-aggregation-node.h
----------------------------------------------------------------------
diff --git a/be/src/exec/partitioned-aggregation-node.h b/be/src/exec/partitioned-aggregation-node.h
index 54840c7..f26a252 100644
--- a/be/src/exec/partitioned-aggregation-node.h
+++ b/be/src/exec/partitioned-aggregation-node.h
@@ -651,7 +651,7 @@ class PartitionedAggregationNode : public ExecNode {
   /// operation is applied. The instruction sequence for the UDA call is inserted at
   /// the insert position of 'builder'.
   Status CodegenCallUda(LlvmCodeGen* codegen, LlvmBuilder* builder,
-      AggFnEvaluator* evaluator, llvm::Value* agg_fn_ctx,
+      AggFnEvaluator* evaluator, llvm::Value* agg_fn_ctx_arg,
       const std::vector<CodegenAnyVal>& input_vals, const CodegenAnyVal& dst_val,
       CodegenAnyVal* updated_dst_val);
 

http://git-wip-us.apache.org/repos/asf/incubator-impala/blob/f982c3f7/be/src/exprs/agg-fn-evaluator.cc
----------------------------------------------------------------------
diff --git a/be/src/exprs/agg-fn-evaluator.cc b/be/src/exprs/agg-fn-evaluator.cc
index b623130..81c0277 100644
--- a/be/src/exprs/agg-fn-evaluator.cc
+++ b/be/src/exprs/agg-fn-evaluator.cc
@@ -528,7 +528,7 @@ Status AggFnEvaluator::GetUpdateOrMergeFunction(LlvmCodeGen* codegen, Function**
   if (!(*uda_fn)->isDeclaration()) {
     // TODO: IMPALA-4785: we should also replace references to GetIntermediateType()
     // with constants.
-    Expr::InlineConstants(GetOutputTypeDesc(), arg_type_descs_, codegen, *uda_fn);
+    codegen->InlineConstFnAttrs(GetOutputTypeDesc(), arg_type_descs_, *uda_fn);
     *uda_fn = codegen->FinalizeFunction(*uda_fn);
     if (*uda_fn == NULL) {
       return Status(TErrorCode::UDF_VERIFY_FAILED, symbol, fn_.hdfs_location);

http://git-wip-us.apache.org/repos/asf/incubator-impala/blob/f982c3f7/be/src/exprs/aggregate-functions-ir.cc
----------------------------------------------------------------------
diff --git a/be/src/exprs/aggregate-functions-ir.cc b/be/src/exprs/aggregate-functions-ir.cc
index 8505d43..531598c 100644
--- a/be/src/exprs/aggregate-functions-ir.cc
+++ b/be/src/exprs/aggregate-functions-ir.cc
@@ -401,7 +401,7 @@ IR_ALWAYS_INLINE void AggregateFunctions::DecimalAvgAddOrRemove(FunctionContext*
   // Since the src and dst are guaranteed to be the same scale, we can just
   // do a simple add.
   int m = remove ? -1 : 1;
-  switch (Expr::GetConstantInt(*ctx, Expr::ARG_TYPE_SIZE, 0)) {
+  switch (ctx->impl()->GetConstFnAttr(FunctionContextImpl::ARG_TYPE_SIZE, 0)) {
     case 4:
       avg->sum.val16 += m * src.val4;
       break;
@@ -511,7 +511,7 @@ IR_ALWAYS_INLINE void AggregateFunctions::SumDecimalAddOrSubtract(FunctionContex
     const DecimalVal& src, DecimalVal* dst, bool subtract) {
   if (src.is_null) return;
   if (dst->is_null) InitZero<DecimalVal>(ctx, dst);
-  int precision = Expr::GetConstantInt(*ctx, Expr::ARG_TYPE_PRECISION, 0);
+  int precision = ctx->impl()->GetConstFnAttr(FunctionContextImpl::ARG_TYPE_PRECISION, 0);
   // Since the src and dst are guaranteed to be the same scale, we can just
   // do a simple add.
   int m = subtract ? -1 : 1;
@@ -573,7 +573,7 @@ template<>
 void AggregateFunctions::Min(FunctionContext* ctx,
     const DecimalVal& src, DecimalVal* dst) {
   if (src.is_null) return;
-  int precision = Expr::GetConstantInt(*ctx, Expr::ARG_TYPE_PRECISION, 0);
+  int precision = ctx->impl()->GetConstFnAttr(FunctionContextImpl::ARG_TYPE_PRECISION, 0);
   if (precision <= 9) {
     if (dst->is_null || src.val4 < dst->val4) *dst = src;
   } else if (precision <= 19) {
@@ -587,7 +587,7 @@ template<>
 void AggregateFunctions::Max(FunctionContext* ctx,
     const DecimalVal& src, DecimalVal* dst) {
   if (src.is_null) return;
-  int precision = Expr::GetConstantInt(*ctx, Expr::ARG_TYPE_PRECISION, 0);
+  int precision = ctx->impl()->GetConstFnAttr(FunctionContextImpl::ARG_TYPE_PRECISION, 0);
   if (precision <= 9) {
     if (dst->is_null || src.val4 > dst->val4) *dst = src;
   } else if (precision <= 19) {
@@ -1200,8 +1200,8 @@ void AggregateFunctions::HllUpdate(
   if (src.is_null) return;
   DCHECK(!dst->is_null);
   DCHECK_EQ(dst->len, HLL_LEN);
-  uint64_t hash_value = AnyValUtil::HashDecimal64(
-      src, Expr::GetConstantInt(*ctx, Expr::ARG_TYPE_SIZE, 0), HashUtil::FNV64_SEED);
+  int byte_size = ctx->impl()->GetConstFnAttr(FunctionContextImpl::ARG_TYPE_SIZE, 0);
+  uint64_t hash_value = AnyValUtil::HashDecimal64(src, byte_size, HashUtil::FNV64_SEED);
   if (hash_value != 0) {
     // Use the lower bits to index into the number of streams and then
     // find the first 1 bit after the index bits.

http://git-wip-us.apache.org/repos/asf/incubator-impala/blob/f982c3f7/be/src/exprs/conditional-functions-ir.cc
----------------------------------------------------------------------
diff --git a/be/src/exprs/conditional-functions-ir.cc b/be/src/exprs/conditional-functions-ir.cc
index ed98a87..662f167 100644
--- a/be/src/exprs/conditional-functions-ir.cc
+++ b/be/src/exprs/conditional-functions-ir.cc
@@ -23,11 +23,11 @@ using namespace impala;
 using namespace impala_udf;
 
 #define IS_NULL_COMPUTE_FUNCTION(type) \
-  type IsNullExpr::Get##type(ExprContext* context, const TupleRow* row) { \
+  type IsNullExpr::Get##type(ExprContext* ctx, const TupleRow* row) { \
     DCHECK_EQ(children_.size(), 2); \
-    type val = children_[0]->Get##type(context, row); \
+    type val = children_[0]->Get##type(ctx, row); \
     if (!val.is_null) return val; /* short-circuit */ \
-    return children_[1]->Get##type(context, row); \
+    return children_[1]->Get##type(ctx, row); \
   }
 
 IS_NULL_COMPUTE_FUNCTION(BooleanVal);
@@ -68,15 +68,14 @@ NULL_IF_COMPUTE_FUNCTION(TimestampVal);
 NULL_IF_COMPUTE_FUNCTION(DecimalVal);
 
 #define NULL_IF_ZERO_COMPUTE_FUNCTION(type) \
-  type ConditionalFunctions::NullIfZero(FunctionContext* context, const type& val) { \
+  type ConditionalFunctions::NullIfZero(FunctionContext* ctx, const type& val) { \
     if (val.is_null || val.val == 0) return type::null(); \
     return val; \
   }
 
-DecimalVal ConditionalFunctions::NullIfZero(
-    FunctionContext* context, const DecimalVal& val) {
+DecimalVal ConditionalFunctions::NullIfZero(FunctionContext* ctx, const DecimalVal& val) {
   if (val.is_null) return DecimalVal::null();
-  int type_byte_size = Expr::GetConstantInt(*context, Expr::RETURN_TYPE_SIZE);
+  int type_byte_size = ctx->impl()->GetConstFnAttr(FunctionContextImpl::RETURN_TYPE_SIZE);
   switch (type_byte_size) {
     case 4:
       if (val.val4 == 0) return DecimalVal::null();
@@ -101,7 +100,7 @@ NULL_IF_ZERO_COMPUTE_FUNCTION(FloatVal);
 NULL_IF_ZERO_COMPUTE_FUNCTION(DoubleVal);
 
 #define ZERO_IF_NULL_COMPUTE_FUNCTION(type) \
-  type ConditionalFunctions::ZeroIfNull(FunctionContext* context, const type& val) { \
+  type ConditionalFunctions::ZeroIfNull(FunctionContext* ctx, const type& val) { \
     if (val.is_null) return type(0); \
     return val; \
   }
@@ -115,13 +114,13 @@ ZERO_IF_NULL_COMPUTE_FUNCTION(DoubleVal);
 ZERO_IF_NULL_COMPUTE_FUNCTION(DecimalVal);
 
 #define IF_COMPUTE_FUNCTION(type) \
-  type IfExpr::Get##type(ExprContext* context, const TupleRow* row) { \
+  type IfExpr::Get##type(ExprContext* ctx, const TupleRow* row) { \
     DCHECK_EQ(children_.size(), 3); \
-    BooleanVal cond = children_[0]->GetBooleanVal(context, row); \
+    BooleanVal cond = children_[0]->GetBooleanVal(ctx, row); \
     if (cond.is_null || !cond.val) { \
-      return children_[2]->Get##type(context, row); \
+      return children_[2]->Get##type(ctx, row); \
     } \
-    return children_[1]->Get##type(context, row); \
+    return children_[1]->Get##type(ctx, row); \
   }
 
 IF_COMPUTE_FUNCTION(BooleanVal);
@@ -136,10 +135,10 @@ IF_COMPUTE_FUNCTION(TimestampVal);
 IF_COMPUTE_FUNCTION(DecimalVal);
 
 #define COALESCE_COMPUTE_FUNCTION(type) \
-  type CoalesceExpr::Get##type(ExprContext* context, const TupleRow* row) { \
+  type CoalesceExpr::Get##type(ExprContext* ctx, const TupleRow* row) { \
     DCHECK_GE(children_.size(), 1); \
     for (int i = 0; i < children_.size(); ++i) {                  \
-      type val = children_[i]->Get##type(context, row); \
+      type val = children_[i]->Get##type(ctx, row); \
       if (!val.is_null) return val; \
     } \
     return type::null(); \

http://git-wip-us.apache.org/repos/asf/incubator-impala/blob/f982c3f7/be/src/exprs/decimal-functions-ir.cc
----------------------------------------------------------------------
diff --git a/be/src/exprs/decimal-functions-ir.cc b/be/src/exprs/decimal-functions-ir.cc
index cdbd799..3262e27 100644
--- a/be/src/exprs/decimal-functions-ir.cc
+++ b/be/src/exprs/decimal-functions-ir.cc
@@ -28,17 +28,17 @@
 
 namespace impala {
 
-IntVal DecimalFunctions::Precision(FunctionContext* context, const DecimalVal& val) {
-  return IntVal(Expr::GetConstantInt(*context, Expr::ARG_TYPE_PRECISION, 0));
+IntVal DecimalFunctions::Precision(FunctionContext* ctx, const DecimalVal& val) {
+  return IntVal(ctx->impl()->GetConstFnAttr(FunctionContextImpl::ARG_TYPE_PRECISION, 0));
 }
 
-IntVal DecimalFunctions::Scale(FunctionContext* context, const DecimalVal& val) {
-  return IntVal(Expr::GetConstantInt(*context, Expr::ARG_TYPE_SCALE, 0));
+IntVal DecimalFunctions::Scale(FunctionContext* ctx, const DecimalVal& val) {
+  return IntVal(ctx->impl()->GetConstFnAttr(FunctionContextImpl::ARG_TYPE_SCALE, 0));
 }
 
-DecimalVal DecimalFunctions::Abs(FunctionContext* context, const DecimalVal& val) {
+DecimalVal DecimalFunctions::Abs(FunctionContext* ctx, const DecimalVal& val) {
   if (val.is_null) return DecimalVal::null();
-  int type_byte_size = Expr::GetConstantInt(*context, Expr::ARG_TYPE_SIZE, 0);
+  int type_byte_size = ctx->impl()->GetConstFnAttr(FunctionContextImpl::ARG_TYPE_SIZE, 0);
   switch (type_byte_size) {
     case 4:
       return DecimalVal(abs(val.val4));
@@ -52,79 +52,83 @@ DecimalVal DecimalFunctions::Abs(FunctionContext* context, const DecimalVal& val
   }
 }
 
-DecimalVal DecimalFunctions::Ceil(FunctionContext* context, const DecimalVal& val) {
-  return DecimalOperators::RoundDecimal(context, val, DecimalOperators::CEIL);
+DecimalVal DecimalFunctions::Ceil(FunctionContext* ctx, const DecimalVal& val) {
+  return DecimalOperators::RoundDecimal(ctx, val, DecimalOperators::CEIL);
 }
 
-DecimalVal DecimalFunctions::Floor(FunctionContext* context, const DecimalVal& val) {
-  return DecimalOperators::RoundDecimal(context, val, DecimalOperators::FLOOR);
+DecimalVal DecimalFunctions::Floor(FunctionContext* ctx, const DecimalVal& val) {
+  return DecimalOperators::RoundDecimal(ctx, val, DecimalOperators::FLOOR);
 }
 
-DecimalVal DecimalFunctions::Round(FunctionContext* context, const DecimalVal& val) {
-  return DecimalOperators::RoundDecimal(context, val, DecimalOperators::ROUND);
+DecimalVal DecimalFunctions::Round(FunctionContext* ctx, const DecimalVal& val) {
+  return DecimalOperators::RoundDecimal(ctx, val, DecimalOperators::ROUND);
 }
 
 /// Always inline in IR module so that constants can be replaced.
 IR_ALWAYS_INLINE DecimalVal DecimalFunctions::RoundTo(
-    FunctionContext* context, const DecimalVal& val, int scale,
+    FunctionContext* ctx, const DecimalVal& val, int scale,
     DecimalOperators::DecimalRoundOp op) {
-  int val_precision = Expr::GetConstantInt(*context, Expr::ARG_TYPE_PRECISION, 0);
-  int val_scale = Expr::GetConstantInt(*context, Expr::ARG_TYPE_SCALE, 0);
-  int return_precision = Expr::GetConstantInt(*context, Expr::RETURN_TYPE_PRECISION);
-  int return_scale = Expr::GetConstantInt(*context, Expr::RETURN_TYPE_SCALE);
+  int val_precision =
+      ctx->impl()->GetConstFnAttr(FunctionContextImpl::ARG_TYPE_PRECISION, 0);
+  int val_scale =
+      ctx->impl()->GetConstFnAttr(FunctionContextImpl::ARG_TYPE_SCALE, 0);
+  int return_precision =
+      ctx->impl()->GetConstFnAttr(FunctionContextImpl::RETURN_TYPE_PRECISION);
+  int return_scale =
+      ctx->impl()->GetConstFnAttr(FunctionContextImpl::RETURN_TYPE_SCALE);
   if (scale < 0) {
-    return DecimalOperators::RoundDecimalNegativeScale(context,
+    return DecimalOperators::RoundDecimalNegativeScale(ctx,
         val, val_precision, val_scale, return_precision, return_scale, op, -scale);
   } else {
-    return DecimalOperators::RoundDecimal(context,
+    return DecimalOperators::RoundDecimal(ctx,
         val, val_precision, val_scale, return_precision, return_scale, op);
   }
 }
 
 DecimalVal DecimalFunctions::RoundTo(
-    FunctionContext* context, const DecimalVal& val, const TinyIntVal& scale) {
+    FunctionContext* ctx, const DecimalVal& val, const TinyIntVal& scale) {
   DCHECK(!scale.is_null);
-  return RoundTo(context, val, scale.val, DecimalOperators::ROUND);
+  return RoundTo(ctx, val, scale.val, DecimalOperators::ROUND);
 }
 DecimalVal DecimalFunctions::RoundTo(
-    FunctionContext* context, const DecimalVal& val, const SmallIntVal& scale) {
+    FunctionContext* ctx, const DecimalVal& val, const SmallIntVal& scale) {
   DCHECK(!scale.is_null);
-  return RoundTo(context, val, scale.val, DecimalOperators::ROUND);
+  return RoundTo(ctx, val, scale.val, DecimalOperators::ROUND);
 }
 DecimalVal DecimalFunctions::RoundTo(
-    FunctionContext* context, const DecimalVal& val, const IntVal& scale) {
+    FunctionContext* ctx, const DecimalVal& val, const IntVal& scale) {
   DCHECK(!scale.is_null);
-  return RoundTo(context, val, scale.val, DecimalOperators::ROUND);
+  return RoundTo(ctx, val, scale.val, DecimalOperators::ROUND);
 }
 DecimalVal DecimalFunctions::RoundTo(
-    FunctionContext* context, const DecimalVal& val, const BigIntVal& scale) {
+    FunctionContext* ctx, const DecimalVal& val, const BigIntVal& scale) {
   DCHECK(!scale.is_null);
-  return RoundTo(context, val, scale.val, DecimalOperators::ROUND);
+  return RoundTo(ctx, val, scale.val, DecimalOperators::ROUND);
 }
 
-DecimalVal DecimalFunctions::Truncate(FunctionContext* context, const DecimalVal& val) {
-  return DecimalOperators::RoundDecimal(context, val, DecimalOperators::TRUNCATE);
+DecimalVal DecimalFunctions::Truncate(FunctionContext* ctx, const DecimalVal& val) {
+  return DecimalOperators::RoundDecimal(ctx, val, DecimalOperators::TRUNCATE);
 }
 
 DecimalVal DecimalFunctions::TruncateTo(
-    FunctionContext* context, const DecimalVal& val, const TinyIntVal& scale) {
+    FunctionContext* ctx, const DecimalVal& val, const TinyIntVal& scale) {
   DCHECK(!scale.is_null);
-  return RoundTo(context, val, scale.val, DecimalOperators::TRUNCATE);
+  return RoundTo(ctx, val, scale.val, DecimalOperators::TRUNCATE);
 }
 DecimalVal DecimalFunctions::TruncateTo(
-    FunctionContext* context, const DecimalVal& val, const SmallIntVal& scale) {
+    FunctionContext* ctx, const DecimalVal& val, const SmallIntVal& scale) {
   DCHECK(!scale.is_null);
-  return RoundTo(context, val, scale.val, DecimalOperators::TRUNCATE);
+  return RoundTo(ctx, val, scale.val, DecimalOperators::TRUNCATE);
 }
 DecimalVal DecimalFunctions::TruncateTo(
-    FunctionContext* context, const DecimalVal& val, const IntVal& scale) {
+    FunctionContext* ctx, const DecimalVal& val, const IntVal& scale) {
   DCHECK(!scale.is_null);
-  return RoundTo(context, val, scale.val, DecimalOperators::TRUNCATE);
+  return RoundTo(ctx, val, scale.val, DecimalOperators::TRUNCATE);
 }
 DecimalVal DecimalFunctions::TruncateTo(
-    FunctionContext* context, const DecimalVal& val, const BigIntVal& scale) {
+    FunctionContext* ctx, const DecimalVal& val, const BigIntVal& scale) {
   DCHECK(!scale.is_null);
-  return RoundTo(context, val, scale.val, DecimalOperators::TRUNCATE);
+  return RoundTo(ctx, val, scale.val, DecimalOperators::TRUNCATE);
 }
 
 }

http://git-wip-us.apache.org/repos/asf/incubator-impala/blob/f982c3f7/be/src/exprs/decimal-operators-ir.cc
----------------------------------------------------------------------
diff --git a/be/src/exprs/decimal-operators-ir.cc b/be/src/exprs/decimal-operators-ir.cc
index 516acbc..4dfc8dc 100644
--- a/be/src/exprs/decimal-operators-ir.cc
+++ b/be/src/exprs/decimal-operators-ir.cc
@@ -34,32 +34,32 @@
 
 namespace impala {
 
-#define RETURN_IF_OVERFLOW(context, overflow) \
+#define RETURN_IF_OVERFLOW(ctx, overflow) \
   do {\
     if (UNLIKELY(overflow)) {\
-      context->AddWarning("Expression overflowed, returning NULL");\
+      ctx->AddWarning("Expression overflowed, returning NULL");\
       return DecimalVal::null();\
     }\
   } while (false)
 
 // Inline in IR module so branches can be optimised out.
 IR_ALWAYS_INLINE DecimalVal DecimalOperators::IntToDecimalVal(
-    FunctionContext* context, int precision, int scale, int64_t val) {
+    FunctionContext* ctx, int precision, int scale, int64_t val) {
   bool overflow = false;
   switch (ColumnType::GetDecimalByteSize(precision)) {
     case 4: {
       Decimal4Value dv = Decimal4Value::FromInt(precision, scale, val, &overflow);
-      RETURN_IF_OVERFLOW(context, overflow);
+      RETURN_IF_OVERFLOW(ctx, overflow);
       return DecimalVal(dv.value());
     }
     case 8: {
       Decimal8Value dv = Decimal8Value::FromInt(precision, scale, val, &overflow);
-      RETURN_IF_OVERFLOW(context, overflow);
+      RETURN_IF_OVERFLOW(ctx, overflow);
       return DecimalVal(dv.value());
     }
     case 16: {
       Decimal16Value dv = Decimal16Value::FromInt(precision, scale, val, &overflow);
-      RETURN_IF_OVERFLOW(context, overflow);
+      RETURN_IF_OVERFLOW(ctx, overflow);
       return DecimalVal(dv.value());
     }
     default:
@@ -70,25 +70,25 @@ IR_ALWAYS_INLINE DecimalVal DecimalOperators::IntToDecimalVal(
 
 // Inline in IR module so branches can be optimised out.
 IR_ALWAYS_INLINE DecimalVal DecimalOperators::FloatToDecimalVal(
-    FunctionContext* context, int precision, int scale, double val) {
+    FunctionContext* ctx, int precision, int scale, double val) {
   bool overflow = false;
   switch (ColumnType::GetDecimalByteSize(precision)) {
     case 4: {
       Decimal4Value dv =
           Decimal4Value::FromDouble(precision, scale, val, &overflow);
-      RETURN_IF_OVERFLOW(context, overflow);
+      RETURN_IF_OVERFLOW(ctx, overflow);
       return DecimalVal(dv.value());
     }
     case 8: {
       Decimal8Value dv =
           Decimal8Value::FromDouble(precision, scale, val, &overflow);
-      RETURN_IF_OVERFLOW(context, overflow);
+      RETURN_IF_OVERFLOW(ctx, overflow);
       return DecimalVal(dv.value());
     }
     case 16: {
       Decimal16Value dv =
           Decimal16Value::FromDouble(precision, scale, val, &overflow);
-      RETURN_IF_OVERFLOW(context, overflow);
+      RETURN_IF_OVERFLOW(ctx, overflow);
       return DecimalVal(dv.value());
     }
     default:
@@ -105,28 +105,28 @@ IR_ALWAYS_INLINE DecimalVal DecimalOperators::FloatToDecimalVal(
 // When going from a smaller type to a larger type, we convert and then scale.
 // Inline these functions in IR module so branches can be optimised out.
 
-IR_ALWAYS_INLINE DecimalVal DecimalOperators::ScaleDecimalValue(FunctionContext* context,
+IR_ALWAYS_INLINE DecimalVal DecimalOperators::ScaleDecimalValue(FunctionContext* ctx,
     const Decimal4Value& val, int val_scale, int output_precision, int output_scale) {
   bool overflow = false;
   switch (ColumnType::GetDecimalByteSize(output_precision)) {
     case 4: {
       Decimal4Value scaled_val = val.ScaleTo(
           val_scale, output_scale, output_precision, &overflow);
-      RETURN_IF_OVERFLOW(context, overflow);
+      RETURN_IF_OVERFLOW(ctx, overflow);
       return DecimalVal(scaled_val.value());
     }
     case 8: {
       Decimal8Value val8 = ToDecimal8(val, &overflow);
       Decimal8Value scaled_val = val8.ScaleTo(
           val_scale, output_scale, output_precision, &overflow);
-      RETURN_IF_OVERFLOW(context, overflow);
+      RETURN_IF_OVERFLOW(ctx, overflow);
       return DecimalVal(scaled_val.value());
     }
     case 16: {
       Decimal16Value val16 = ToDecimal16(val, &overflow);
       Decimal16Value scaled_val = val16.ScaleTo(
           val_scale, output_scale, output_precision, &overflow);
-      RETURN_IF_OVERFLOW(context, overflow);
+      RETURN_IF_OVERFLOW(ctx, overflow);
       return DecimalVal(scaled_val.value());
     }
     default:
@@ -135,7 +135,7 @@ IR_ALWAYS_INLINE DecimalVal DecimalOperators::ScaleDecimalValue(FunctionContext*
   }
 }
 
-IR_ALWAYS_INLINE DecimalVal DecimalOperators::ScaleDecimalValue(FunctionContext* context,
+IR_ALWAYS_INLINE DecimalVal DecimalOperators::ScaleDecimalValue(FunctionContext* ctx,
     const Decimal8Value& val, int val_scale, int output_precision, int output_scale) {
   bool overflow = false;
   switch (ColumnType::GetDecimalByteSize(output_precision)) {
@@ -143,20 +143,20 @@ IR_ALWAYS_INLINE DecimalVal DecimalOperators::ScaleDecimalValue(FunctionContext*
       Decimal8Value scaled_val = val.ScaleTo(
           val_scale, output_scale, output_precision, &overflow);
       Decimal4Value val4 = ToDecimal4(scaled_val, &overflow);
-      RETURN_IF_OVERFLOW(context, overflow);
+      RETURN_IF_OVERFLOW(ctx, overflow);
       return DecimalVal(val4.value());
     }
     case 8: {
       Decimal8Value scaled_val = val.ScaleTo(
           val_scale, output_scale, output_precision, &overflow);
-      RETURN_IF_OVERFLOW(context, overflow);
+      RETURN_IF_OVERFLOW(ctx, overflow);
       return DecimalVal(scaled_val.value());
     }
     case 16: {
       Decimal16Value val16 = ToDecimal16(val, &overflow);
       Decimal16Value scaled_val = val16.ScaleTo(
           val_scale, output_scale, output_precision, &overflow);
-      RETURN_IF_OVERFLOW(context, overflow);
+      RETURN_IF_OVERFLOW(ctx, overflow);
       return DecimalVal(scaled_val.value());
     }
     default:
@@ -165,7 +165,7 @@ IR_ALWAYS_INLINE DecimalVal DecimalOperators::ScaleDecimalValue(FunctionContext*
   }
 }
 
-IR_ALWAYS_INLINE DecimalVal DecimalOperators::ScaleDecimalValue(FunctionContext* context,
+IR_ALWAYS_INLINE DecimalVal DecimalOperators::ScaleDecimalValue(FunctionContext* ctx,
     const Decimal16Value& val, int val_scale, int output_precision, int output_scale) {
   bool overflow = false;
   switch (ColumnType::GetDecimalByteSize(output_precision)) {
@@ -173,20 +173,20 @@ IR_ALWAYS_INLINE DecimalVal DecimalOperators::ScaleDecimalValue(FunctionContext*
       Decimal16Value scaled_val = val.ScaleTo(
           val_scale, output_scale, output_precision, &overflow);
       Decimal4Value val4 = ToDecimal4(scaled_val, &overflow);
-      RETURN_IF_OVERFLOW(context, overflow);
+      RETURN_IF_OVERFLOW(ctx, overflow);
       return DecimalVal(val4.value());
     }
     case 8: {
       Decimal16Value scaled_val = val.ScaleTo(
           val_scale, output_scale, output_precision, &overflow);
       Decimal8Value val8 = ToDecimal8(scaled_val, &overflow);
-      RETURN_IF_OVERFLOW(context, overflow);
+      RETURN_IF_OVERFLOW(ctx, overflow);
       return DecimalVal(val8.value());
     }
     case 16: {
       Decimal16Value scaled_val = val.ScaleTo(
           val_scale, output_scale, output_precision, &overflow);
-      RETURN_IF_OVERFLOW(context, overflow);
+      RETURN_IF_OVERFLOW(ctx, overflow);
       return DecimalVal(scaled_val.value());
     }
     default:
@@ -268,29 +268,31 @@ static inline Decimal16Value GetDecimal16Value(
 }
 
 #define CAST_INT_TO_DECIMAL(from_type) \
-  DecimalVal DecimalOperators::CastToDecimalVal( \
-      FunctionContext* context, const from_type& val) { \
+  IR_ALWAYS_INLINE DecimalVal DecimalOperators::CastToDecimalVal( \
+      FunctionContext* ctx, const from_type& val) { \
     if (val.is_null) return DecimalVal::null(); \
-    int precision = Expr::GetConstantInt(*context, Expr::RETURN_TYPE_PRECISION); \
-    int scale = Expr::GetConstantInt(*context, Expr::RETURN_TYPE_SCALE); \
-    return IntToDecimalVal(context, precision, scale, val.val); \
+    int precision = \
+        ctx->impl()->GetConstFnAttr(FunctionContextImpl::RETURN_TYPE_PRECISION); \
+    int scale = ctx->impl()->GetConstFnAttr(FunctionContextImpl::RETURN_TYPE_SCALE); \
+    return IntToDecimalVal(ctx, precision, scale, val.val); \
   }
 
 #define CAST_FLOAT_TO_DECIMAL(from_type) \
-  DecimalVal DecimalOperators::CastToDecimalVal( \
-      FunctionContext* context, const from_type& val) { \
+  IR_ALWAYS_INLINE DecimalVal DecimalOperators::CastToDecimalVal( \
+      FunctionContext* ctx, const from_type& val) { \
     if (val.is_null) return DecimalVal::null(); \
-    int precision = Expr::GetConstantInt(*context, Expr::RETURN_TYPE_PRECISION); \
-    int scale = Expr::GetConstantInt(*context, Expr::RETURN_TYPE_SCALE); \
-    return FloatToDecimalVal(context, precision, scale, val.val); \
+    int precision = \
+        ctx->impl()->GetConstFnAttr(FunctionContextImpl::RETURN_TYPE_PRECISION); \
+    int scale = ctx->impl()->GetConstFnAttr(FunctionContextImpl::RETURN_TYPE_SCALE); \
+    return FloatToDecimalVal(ctx, precision, scale, val.val); \
   }
 
 #define CAST_DECIMAL_TO_INT(to_type) \
-  to_type DecimalOperators::CastTo##to_type( \
-      FunctionContext* context, const DecimalVal& val) { \
+  IR_ALWAYS_INLINE to_type DecimalOperators::CastTo##to_type( \
+      FunctionContext* ctx, const DecimalVal& val) { \
     if (val.is_null) return to_type::null(); \
-    int scale = Expr::GetConstantInt(*context, Expr::ARG_TYPE_SCALE, 0); \
-    switch (Expr::GetConstantInt(*context, Expr::ARG_TYPE_SIZE, 0)) { \
+    int scale = ctx->impl()->GetConstFnAttr(FunctionContextImpl::ARG_TYPE_SCALE, 0); \
+    switch (ctx->impl()->GetConstFnAttr(FunctionContextImpl::ARG_TYPE_SIZE, 0)) { \
       case 4: { \
         Decimal4Value dv(val.val4); \
         return to_type(dv.whole_part(scale)); \
@@ -310,11 +312,11 @@ static inline Decimal16Value GetDecimal16Value(
   }
 
 #define CAST_DECIMAL_TO_FLOAT(to_type) \
-  to_type DecimalOperators::CastTo##to_type( \
-      FunctionContext* context, const DecimalVal& val) { \
+  IR_ALWAYS_INLINE to_type DecimalOperators::CastTo##to_type( \
+      FunctionContext* ctx, const DecimalVal& val) { \
     if (val.is_null) return to_type::null(); \
-    int scale = Expr::GetConstantInt(*context, Expr::ARG_TYPE_SCALE, 0); \
-    switch (Expr::GetConstantInt(*context, Expr::ARG_TYPE_SIZE, 0)) { \
+    int scale = ctx->impl()->GetConstFnAttr(FunctionContextImpl::ARG_TYPE_SCALE, 0); \
+    switch (ctx->impl()->GetConstFnAttr(FunctionContextImpl::ARG_TYPE_SIZE, 0)) { \
       case 4: { \
         Decimal4Value dv(val.val4); \
         return to_type(dv.ToDouble(scale)); \
@@ -349,7 +351,7 @@ CAST_DECIMAL_TO_FLOAT(DoubleVal)
 
 // Inline in IR module so branches can be optimised out.
 IR_ALWAYS_INLINE DecimalVal DecimalOperators::RoundDecimalNegativeScale(
-    FunctionContext* context, const DecimalVal& val, int val_precision, int val_scale,
+    FunctionContext* ctx, const DecimalVal& val, int val_precision, int val_scale,
     int output_precision, int output_scale, const DecimalRoundOp& op,
     int64_t rounding_scale) {
   DCHECK_GT(rounding_scale, 0);
@@ -360,19 +362,19 @@ IR_ALWAYS_INLINE DecimalVal DecimalOperators::RoundDecimalNegativeScale(
   switch (ColumnType::GetDecimalByteSize(val_precision)) {
     case 4: {
       Decimal4Value val4(val.val4);
-      result = ScaleDecimalValue(context, val4, val_scale, output_precision,
+      result = ScaleDecimalValue(ctx, val4, val_scale, output_precision,
           output_scale);
       break;
     }
     case 8: {
       Decimal8Value val8(val.val8);
-      result = ScaleDecimalValue(context, val8, val_scale, output_precision,
+      result = ScaleDecimalValue(ctx, val8, val_scale, output_precision,
           output_scale);
       break;
     }
     case 16: {
       Decimal16Value val16(val.val16);
-      result = ScaleDecimalValue(context, val16, val_scale, output_precision,
+      result = ScaleDecimalValue(ctx, val16, val_scale, output_precision,
           output_scale);
       break;
     }
@@ -410,7 +412,7 @@ IR_ALWAYS_INLINE DecimalVal DecimalOperators::RoundDecimalNegativeScale(
       // Need to check for overflow. This can't happen in the other cases since the
       // FE should have picked a high enough precision.
       if (DecimalUtil::MAX_UNSCALED_DECIMAL16 - abs(delta) < abs(val16.value())) {
-        context->AddWarning("Expression overflowed, returning NULL");
+        ctx->AddWarning("Expression overflowed, returning NULL");
         return DecimalVal::null();
       }
       result.val16 += delta;
@@ -424,7 +426,7 @@ IR_ALWAYS_INLINE DecimalVal DecimalOperators::RoundDecimalNegativeScale(
 }
 
 // Inline in IR module so branches can be optimised out.
-IR_ALWAYS_INLINE DecimalVal DecimalOperators::RoundDecimal(FunctionContext* context,
+IR_ALWAYS_INLINE DecimalVal DecimalOperators::RoundDecimal(FunctionContext* ctx,
     const DecimalVal& val, int val_precision, int val_scale, int output_precision,
     int output_scale, const DecimalRoundOp& op) {
   if (val.is_null) return DecimalVal::null();
@@ -434,23 +436,22 @@ IR_ALWAYS_INLINE DecimalVal DecimalOperators::RoundDecimal(FunctionContext* cont
   switch (ColumnType::GetDecimalByteSize(val_precision)) {
     case 4: {
       Decimal4Value val4(val.val4);
-      result = ScaleDecimalValue(context, val4, val_scale, output_precision,
+      result = ScaleDecimalValue(ctx, val4, val_scale, output_precision,
           output_scale);
       delta = RoundDelta(val4, val_scale, output_scale, op);
       break;
     }
     case 8: {
       Decimal8Value val8(val.val8);
-      result = ScaleDecimalValue(context, val8, val_scale, output_precision,
+      result = ScaleDecimalValue(ctx, val8, val_scale, output_precision,
           output_scale);
       delta = RoundDelta(val8, val_scale, output_scale, op);
       break;
     }
     case 16: {
       Decimal16Value val16(val.val16);
-      result = ScaleDecimalValue(context, val16, val_scale, output_precision,
+      result = ScaleDecimalValue(ctx, val16, val_scale, output_precision,
           output_scale);
-
       delta = RoundDelta(val16, val_scale, output_scale, op);
       break;
     }
@@ -466,41 +467,49 @@ IR_ALWAYS_INLINE DecimalVal DecimalOperators::RoundDecimal(FunctionContext* cont
   // done the cast.
   if (delta == 0) return result;
 
-
-  // The value in 'result' is before the rounding has occurred.
-  // This can't overflow. Rounding to a non-negative scale means at least one digit is
-  // dropped if rounding occurred and the round can add at most one digit before the
-  // decimal.
+  // The value in 'result' is before any rounding has occurred. If there is any rounding,
+  // the ouput's scale must be less than the input's scale.
+  DCHECK_GT(val_scale, output_scale);
   result.val16 += delta;
+
+  // Rounding to a non-negative scale means at least one digit is dropped if rounding
+  // occurred and the round can add at most one digit before the decimal. This cannot
+  // overflow if output_precision >= val_precision. Otherwise, result can overflow.
+  bool overflow = output_precision < val_precision &&
+      abs(result.val16) >= DecimalUtil::GetScaleMultiplier<int128_t>(output_precision);
+  RETURN_IF_OVERFLOW(ctx, overflow);
   return result;
 }
 
 // Inline in IR module so branches can be optimised out.
 IR_ALWAYS_INLINE DecimalVal DecimalOperators::RoundDecimal(
-    FunctionContext* context, const DecimalVal& val, const DecimalRoundOp& op) {
-  int val_precision = Expr::GetConstantInt(*context, Expr::ARG_TYPE_PRECISION, 0);
-  int val_scale = Expr::GetConstantInt(*context, Expr::ARG_TYPE_SCALE, 0);
-  int return_precision = Expr::GetConstantInt(*context, Expr::RETURN_TYPE_PRECISION);
-  int return_scale = Expr::GetConstantInt(*context, Expr::RETURN_TYPE_SCALE);
-  return RoundDecimal(context, val, val_precision, val_scale, return_precision,
+    FunctionContext* ctx, const DecimalVal& val, const DecimalRoundOp& op) {
+  int val_precision =
+      ctx->impl()->GetConstFnAttr(FunctionContextImpl::ARG_TYPE_PRECISION, 0);
+  int val_scale = ctx->impl()->GetConstFnAttr(FunctionContextImpl::ARG_TYPE_SCALE, 0);
+  int return_precision =
+      ctx->impl()->GetConstFnAttr(FunctionContextImpl::RETURN_TYPE_PRECISION);
+  int return_scale = ctx->impl()->GetConstFnAttr(FunctionContextImpl::RETURN_TYPE_SCALE);
+  return RoundDecimal(ctx, val, val_precision, val_scale, return_precision,
       return_scale, op);
 }
 
-// Cast is just RoundDecimal(TRUNCATE).
-// TODO: how we handle cast to a smaller scale is an implementation detail in the spec.
-// We could also choose to cast by doing ROUND.
-DecimalVal DecimalOperators::CastToDecimalVal(
-    FunctionContext* context, const DecimalVal& val) {
-  return RoundDecimal(context, val, TRUNCATE);
+// If query option decimal_v2 is true, cast is RoundDecimal(ROUND).
+// Otherwise, it's RoundDecimal(TRUNCATE).
+IR_ALWAYS_INLINE DecimalVal DecimalOperators::CastToDecimalVal(
+    FunctionContext* ctx, const DecimalVal& val) {
+  int is_decimal_v2 = ctx->impl()->GetConstFnAttr(FunctionContextImpl::DECIMAL_V2);
+  DCHECK(is_decimal_v2 == 0 || is_decimal_v2 == 1);
+  return RoundDecimal(ctx, val, is_decimal_v2 != 0 ? ROUND : TRUNCATE);
 }
 
-DecimalVal DecimalOperators::CastToDecimalVal(
-    FunctionContext* context, const StringVal& val) {
+IR_ALWAYS_INLINE DecimalVal DecimalOperators::CastToDecimalVal(
+    FunctionContext* ctx, const StringVal& val) {
   if (val.is_null) return DecimalVal::null();
   StringParser::ParseResult result;
   DecimalVal dv;
-  int precision = Expr::GetConstantInt(*context, Expr::RETURN_TYPE_PRECISION);
-  int scale = Expr::GetConstantInt(*context, Expr::RETURN_TYPE_SCALE);
+  int precision = ctx->impl()->GetConstFnAttr(FunctionContextImpl::RETURN_TYPE_PRECISION);
+  int scale = ctx->impl()->GetConstFnAttr(FunctionContextImpl::RETURN_TYPE_SCALE);
   switch (ColumnType::GetDecimalByteSize(precision)) {
     case 4: {
       Decimal4Value dv4 = StringParser::StringToDecimal<int32_t>(
@@ -534,10 +543,10 @@ DecimalVal DecimalOperators::CastToDecimalVal(
 }
 
 StringVal DecimalOperators::CastToStringVal(
-    FunctionContext* context, const DecimalVal& val) {
+    FunctionContext* ctx, const DecimalVal& val) {
   if (val.is_null) return StringVal::null();
-  int precision = Expr::GetConstantInt(*context, Expr::ARG_TYPE_PRECISION, 0);
-  int scale = Expr::GetConstantInt(*context, Expr::ARG_TYPE_SCALE, 0);
+  int precision = ctx->impl()->GetConstFnAttr(FunctionContextImpl::ARG_TYPE_PRECISION, 0);
+  int scale = ctx->impl()->GetConstFnAttr(FunctionContextImpl::ARG_TYPE_SCALE, 0);
   string s;
   switch (ColumnType::GetDecimalByteSize(precision)) {
     case 4:
@@ -553,7 +562,7 @@ StringVal DecimalOperators::CastToStringVal(
       DCHECK(false);
       return StringVal::null();
   }
-  StringVal result(context, s.size());
+  StringVal result(ctx, s.size());
   memcpy(result.ptr, s.c_str(), s.size());
   return result;
 }
@@ -574,10 +583,10 @@ IR_ALWAYS_INLINE T DecimalOperators::ConvertToNanoseconds(T val, int scale) {
 }
 
 TimestampVal DecimalOperators::CastToTimestampVal(
-    FunctionContext* context, const DecimalVal& val) {
+    FunctionContext* ctx, const DecimalVal& val) {
   if (val.is_null) return TimestampVal::null();
-  int precision = Expr::GetConstantInt(*context, Expr::ARG_TYPE_PRECISION, 0);
-  int scale = Expr::GetConstantInt(*context, Expr::ARG_TYPE_SCALE, 0);
+  int precision = ctx->impl()->GetConstFnAttr(FunctionContextImpl::ARG_TYPE_PRECISION, 0);
+  int scale = ctx->impl()->GetConstFnAttr(FunctionContextImpl::ARG_TYPE_SCALE, 0);
   TimestampVal result;
   switch (ColumnType::GetDecimalByteSize(precision)) {
     case 4: {
@@ -620,9 +629,9 @@ TimestampVal DecimalOperators::CastToTimestampVal(
 }
 
 BooleanVal DecimalOperators::CastToBooleanVal(
-    FunctionContext* context, const DecimalVal& val) {
+    FunctionContext* ctx, const DecimalVal& val) {
   if (val.is_null) return BooleanVal::null();
-  switch (Expr::GetConstantInt(*context, Expr::ARG_TYPE_SIZE, 0)) {
+  switch (ctx->impl()->GetConstFnAttr(FunctionContextImpl::ARG_TYPE_SIZE, 0)) {
     case 4:
       return BooleanVal(val.val4 != 0);
     case 8:
@@ -637,17 +646,18 @@ BooleanVal DecimalOperators::CastToBooleanVal(
 
 #define DECIMAL_ARITHMETIC_OP(FN_NAME, OP_FN) \
   DecimalVal DecimalOperators::FN_NAME( \
-      FunctionContext* context, const DecimalVal& x, const DecimalVal& y) { \
+      FunctionContext* ctx, const DecimalVal& x, const DecimalVal& y) { \
     if (x.is_null || y.is_null) return DecimalVal::null(); \
     bool overflow = false; \
-    int x_size = Expr::GetConstantInt(*context, Expr::ARG_TYPE_SIZE, 0); \
-    int x_scale = Expr::GetConstantInt(*context, Expr::ARG_TYPE_SCALE, 0); \
-    int y_size = Expr::GetConstantInt(*context, Expr::ARG_TYPE_SIZE, 1); \
-    int y_scale = Expr::GetConstantInt(*context, Expr::ARG_TYPE_SCALE, 1); \
+    int x_size = ctx->impl()->GetConstFnAttr(FunctionContextImpl::ARG_TYPE_SIZE, 0); \
+    int x_scale = ctx->impl()->GetConstFnAttr(FunctionContextImpl::ARG_TYPE_SCALE, 0); \
+    int y_size = ctx->impl()->GetConstFnAttr(FunctionContextImpl::ARG_TYPE_SIZE, 1); \
+    int y_scale = ctx->impl()->GetConstFnAttr(FunctionContextImpl::ARG_TYPE_SCALE, 1); \
     int return_precision = \
-        Expr::GetConstantInt(*context, Expr::RETURN_TYPE_PRECISION); \
-    int return_scale = Expr::GetConstantInt(*context, Expr::RETURN_TYPE_SCALE); \
-    switch (Expr::GetConstantInt(*context, Expr::RETURN_TYPE_SIZE)) { \
+        ctx->impl()->GetConstFnAttr(FunctionContextImpl::RETURN_TYPE_PRECISION); \
+    int return_scale = \
+        ctx->impl()->GetConstFnAttr(FunctionContextImpl::RETURN_TYPE_SCALE); \
+    switch (ctx->impl()->GetConstFnAttr(FunctionContextImpl::RETURN_TYPE_SIZE)) { \
       case 4: { \
         Decimal4Value x_val = GetDecimal4Value(x, x_size, &overflow); \
         Decimal4Value y_val = GetDecimal4Value(y, y_size, &overflow); \
@@ -669,7 +679,7 @@ BooleanVal DecimalOperators::CastToBooleanVal(
         Decimal16Value y_val = GetDecimal16Value(y, y_size, &overflow); \
         Decimal16Value result = x_val.OP_FN<int128_t>(x_scale, y_val, y_scale, \
             return_precision, return_scale, &overflow); \
-        RETURN_IF_OVERFLOW(context, overflow); \
+        RETURN_IF_OVERFLOW(ctx, overflow); \
         return DecimalVal(result.value()); \
       } \
       default: \
@@ -680,18 +690,19 @@ BooleanVal DecimalOperators::CastToBooleanVal(
 
 #define DECIMAL_ARITHMETIC_OP_CHECK_NAN(FN_NAME, OP_FN) \
   DecimalVal DecimalOperators::FN_NAME( \
-      FunctionContext* context, const DecimalVal& x, const DecimalVal& y) { \
+      FunctionContext* ctx, const DecimalVal& x, const DecimalVal& y) { \
     if (x.is_null || y.is_null) return DecimalVal::null(); \
     bool overflow = false; \
     bool is_nan = false; \
-    int x_size = Expr::GetConstantInt(*context, Expr::ARG_TYPE_SIZE, 0); \
-    int x_scale = Expr::GetConstantInt(*context, Expr::ARG_TYPE_SCALE, 0); \
-    int y_size = Expr::GetConstantInt(*context, Expr::ARG_TYPE_SIZE, 1); \
-    int y_scale = Expr::GetConstantInt(*context, Expr::ARG_TYPE_SCALE, 1); \
+    int x_size = ctx->impl()->GetConstFnAttr(FunctionContextImpl::ARG_TYPE_SIZE, 0); \
+    int x_scale = ctx->impl()->GetConstFnAttr(FunctionContextImpl::ARG_TYPE_SCALE, 0); \
+    int y_size = ctx->impl()->GetConstFnAttr(FunctionContextImpl::ARG_TYPE_SIZE, 1); \
+    int y_scale = ctx->impl()->GetConstFnAttr(FunctionContextImpl::ARG_TYPE_SCALE, 1); \
     int return_precision = \
-        Expr::GetConstantInt(*context, Expr::RETURN_TYPE_PRECISION); \
-    int return_scale = Expr::GetConstantInt(*context, Expr::RETURN_TYPE_SCALE); \
-    switch (Expr::GetConstantInt(*context, Expr::RETURN_TYPE_SIZE)) { \
+        ctx->impl()->GetConstFnAttr(FunctionContextImpl::RETURN_TYPE_PRECISION); \
+    int return_scale = \
+        ctx->impl()->GetConstFnAttr(FunctionContextImpl::RETURN_TYPE_SCALE); \
+    switch (ctx->impl()->GetConstFnAttr(FunctionContextImpl::RETURN_TYPE_SIZE)) { \
       case 4: { \
         Decimal4Value x_val = GetDecimal4Value(x, x_size, &overflow); \
         Decimal4Value y_val = GetDecimal4Value(y, y_size, &overflow); \
@@ -715,7 +726,7 @@ BooleanVal DecimalOperators::CastToBooleanVal(
         Decimal16Value y_val = GetDecimal16Value(y, y_size, &overflow); \
         Decimal16Value result = x_val.OP_FN<int128_t>(x_scale, y_val, y_scale, \
             return_precision, return_scale, &is_nan, &overflow); \
-        RETURN_IF_OVERFLOW(context, overflow); \
+        RETURN_IF_OVERFLOW(ctx, overflow); \
         if (is_nan) return DecimalVal::null(); \
         return DecimalVal(result.value()); \
       } \
@@ -727,10 +738,10 @@ BooleanVal DecimalOperators::CastToBooleanVal(
 
 #define DECIMAL_BINARY_OP_NONNULL(OP_FN, X, Y) \
   bool dummy = false; \
-  int x_size = Expr::GetConstantInt(*context, Expr::ARG_TYPE_SIZE, 0); \
-  int x_scale = Expr::GetConstantInt(*context, Expr::ARG_TYPE_SCALE, 0); \
-  int y_size = Expr::GetConstantInt(*context, Expr::ARG_TYPE_SIZE, 1); \
-  int y_scale = Expr::GetConstantInt(*context, Expr::ARG_TYPE_SCALE, 1); \
+  int x_size = ctx->impl()->GetConstFnAttr(FunctionContextImpl::ARG_TYPE_SIZE, 0); \
+  int x_scale = ctx->impl()->GetConstFnAttr(FunctionContextImpl::ARG_TYPE_SCALE, 0); \
+  int y_size = ctx->impl()->GetConstFnAttr(FunctionContextImpl::ARG_TYPE_SIZE, 1); \
+  int y_scale = ctx->impl()->GetConstFnAttr(FunctionContextImpl::ARG_TYPE_SCALE, 1); \
   int byte_size = ::max(x_size, y_size); \
   switch (byte_size) { \
     case 4: { \
@@ -759,14 +770,14 @@ BooleanVal DecimalOperators::CastToBooleanVal(
 
 #define DECIMAL_BINARY_OP(FN_NAME, OP_FN) \
   BooleanVal DecimalOperators::FN_NAME( \
-      FunctionContext* context, const DecimalVal& x, const DecimalVal& y) { \
+      FunctionContext* ctx, const DecimalVal& x, const DecimalVal& y) { \
     if (x.is_null || y.is_null) return BooleanVal::null(); \
     DECIMAL_BINARY_OP_NONNULL(OP_FN, x, y) \
   }
 
 #define NULLSAFE_DECIMAL_BINARY_OP(FN_NAME, OP_FN, IS_EQUAL) \
   BooleanVal DecimalOperators::FN_NAME( \
-      FunctionContext* context, const DecimalVal& x, const DecimalVal& y) { \
+      FunctionContext* ctx, const DecimalVal& x, const DecimalVal& y) { \
     if (x.is_null) return BooleanVal(IS_EQUAL ? y.is_null : !y.is_null); \
     if (y.is_null) return BooleanVal(!IS_EQUAL); \
     DECIMAL_BINARY_OP_NONNULL(OP_FN, x, y) \

http://git-wip-us.apache.org/repos/asf/incubator-impala/blob/f982c3f7/be/src/exprs/decimal-operators.h
----------------------------------------------------------------------
diff --git a/be/src/exprs/decimal-operators.h b/be/src/exprs/decimal-operators.h
index 9f425b0..196ffdb 100644
--- a/be/src/exprs/decimal-operators.h
+++ b/be/src/exprs/decimal-operators.h
@@ -104,7 +104,7 @@ class DecimalOperators {
   };
 
   /// Evaluates a round from 'val' and returns the result, using the rounding rule of
-  /// 'type'.
+  /// 'op. Returns DecimalVal::null() on overflow.
   static DecimalVal RoundDecimal(FunctionContext* context,
       const DecimalVal& val, int val_precision, int val_scale, int output_precision,
       int output_scale, const DecimalRoundOp& op);

http://git-wip-us.apache.org/repos/asf/incubator-impala/blob/f982c3f7/be/src/exprs/expr-codegen-test.cc
----------------------------------------------------------------------
diff --git a/be/src/exprs/expr-codegen-test.cc b/be/src/exprs/expr-codegen-test.cc
index 0c484f4..c73c89e 100644
--- a/be/src/exprs/expr-codegen-test.cc
+++ b/be/src/exprs/expr-codegen-test.cc
@@ -16,30 +16,39 @@
 // under the License.
 
 // The following is cross-compiled to native code and IR, and used in the test below
-
+#include "exprs/decimal-operators.h"
 #include "exprs/expr.h"
 #include "udf/udf.h"
 
 using namespace impala;
 using namespace impala_udf;
 
-// TestGetConstant() fills in the following constants
-struct Constants {
+// TestGetTypeAttrs() fills in the following constants
+struct FnAttr {
   int return_type_size;
   int arg0_type_size;
   int arg1_type_size;
   int arg2_type_size;
 };
 
-IntVal TestGetConstant(
+#ifdef IR_COMPILE
+#include "exprs/decimal-operators-ir.cc"
+#endif
+
+DecimalVal TestGetFnAttrs(
     FunctionContext* ctx, const DecimalVal& arg0, StringVal arg1, StringVal arg2) {
-  Constants* state = reinterpret_cast<Constants*>(
+  FnAttr* state = reinterpret_cast<FnAttr*>(
       ctx->GetFunctionState(FunctionContext::THREAD_LOCAL));
-  state->return_type_size = Expr::GetConstantInt(*ctx, Expr::RETURN_TYPE_SIZE);
-  state->arg0_type_size = Expr::GetConstantInt(*ctx, Expr::ARG_TYPE_SIZE, 0);
-  state->arg1_type_size = Expr::GetConstantInt(*ctx, Expr::ARG_TYPE_SIZE, 1);
-  state->arg2_type_size = Expr::GetConstantInt(*ctx, Expr::ARG_TYPE_SIZE, 2);
-  return IntVal(10);
+  state->return_type_size =
+      ctx->impl()->GetConstFnAttr(FunctionContextImpl::RETURN_TYPE_SIZE);
+  state->arg0_type_size =
+      ctx->impl()->GetConstFnAttr(FunctionContextImpl::ARG_TYPE_SIZE, 0);
+  state->arg1_type_size =
+      ctx->impl()->GetConstFnAttr(FunctionContextImpl::ARG_TYPE_SIZE, 1);
+  state->arg2_type_size =
+      ctx->impl()->GetConstFnAttr(FunctionContextImpl::ARG_TYPE_SIZE, 2);
+  // This function and its callees call FunctionContextImpl::GetConstFnAttr();
+  return DecimalOperators::CastToDecimalVal(ctx, arg0);
 }
 
 // Don't compile the actual test to IR
@@ -48,10 +57,12 @@ IntVal TestGetConstant(
 #include "testutil/gtest-util.h"
 #include "codegen/llvm-codegen.h"
 #include "common/init.h"
+#include "exprs/anyval-util.h"
 #include "exprs/expr-context.h"
 #include "runtime/exec-env.h"
 #include "runtime/mem-tracker.h"
 #include "runtime/runtime-state.h"
+#include "runtime/test-env.h"
 #include "service/fe-support.h"
 #include "udf/udf-internal.h"
 #include "udf/udf-test-harness.h"
@@ -64,28 +75,48 @@ using namespace llvm;
 
 namespace impala {
 
-const char* TEST_GET_CONSTANT_SYMBOL =
-    "_Z15TestGetConstantPN10impala_udf15FunctionContextERKNS_10DecimalValENS_9StringValES5_";
+const char* TEST_GET_FN_ATTR_SYMBOL =
+    "_Z14TestGetFnAttrsPN10impala_udf15FunctionContextERKNS_10DecimalValENS_9StringValES5_";
 
 const int ARG0_PRECISION = 10;
 const int ARG0_SCALE = 2;
 const int ARG1_LEN = 1;
+const int RET_PRECISION = 10;
+const int RET_SCALE = 1;
 
 class ExprCodegenTest : public ::testing::Test {
  protected:
-  int InlineConstants(Expr* expr, LlvmCodeGen* codegen, llvm::Function* fn) {
-    return expr->InlineConstants(codegen, fn);
+  scoped_ptr<TestEnv> test_env_;
+  RuntimeState* runtime_state_;
+  FunctionContext* fn_ctx_;
+  FnAttr fn_type_attr_;
+
+  int InlineConstFnAttrs(Expr* expr, LlvmCodeGen* codegen, llvm::Function* fn) {
+    FunctionContext::TypeDesc ret_type = AnyValUtil::ColumnTypeToTypeDesc(expr->type());
+    vector<FunctionContext::TypeDesc> arg_types;
+    for (const Expr* child : expr->children()) {
+      arg_types.push_back(AnyValUtil::ColumnTypeToTypeDesc(child->type()));
+    }
+    return codegen->InlineConstFnAttrs(ret_type, arg_types, fn);
   }
 
-  static Status CreateFromFile(
-      ObjectPool* pool, const string& filename, scoped_ptr<LlvmCodeGen>* codegen) {
-    RETURN_IF_ERROR(LlvmCodeGen::CreateFromFile(pool, NULL, filename, "test", codegen));
+  Status CreateFromFile(const string& filename, scoped_ptr<LlvmCodeGen>* codegen) {
+    RETURN_IF_ERROR(LlvmCodeGen::CreateFromFile(runtime_state_,
+        runtime_state_->obj_pool(), NULL, filename, "test", codegen));
     return (*codegen)->MaterializeModule();
   }
 
   virtual void SetUp() {
+    TQueryOptions query_options;
+    query_options.__set_decimal_v2(true);
+    test_env_.reset(new TestEnv());
+    EXPECT_OK(test_env_->CreateQueryState(0, 1, 8 * 1024 * 1024, &query_options,
+        &runtime_state_));
+
     FunctionContext::TypeDesc return_type;
-    return_type.type = FunctionContext::TYPE_INT;
+    return_type.type = FunctionContext::TYPE_DECIMAL;
+    return_type.precision = RET_PRECISION;
+    return_type.scale = RET_SCALE;
 
     FunctionContext::TypeDesc arg0_type;
     arg0_type.type = FunctionContext::TYPE_DECIMAL;
@@ -104,23 +135,25 @@ class ExprCodegenTest : public ::testing::Test {
     arg_types.push_back(arg1_type);
     arg_types.push_back(arg2_type);
 
-    fn_ctx_ = UdfTestHarness::CreateTestContext(return_type, arg_types);
+    fn_ctx_ = UdfTestHarness::CreateTestContext(return_type, arg_types, runtime_state_);
 
     // Initialize fn_ctx_ with constants
-    memset(&constants_, -1, sizeof(Constants));
-    fn_ctx_->SetFunctionState(FunctionContext::THREAD_LOCAL, &constants_);
+    memset(&fn_type_attr_, -1, sizeof(FnAttr));
+    fn_ctx_->SetFunctionState(FunctionContext::THREAD_LOCAL, &fn_type_attr_);
   }
 
   virtual void TearDown() {
     fn_ctx_->impl()->Close();
     delete fn_ctx_;
+    runtime_state_ = NULL;
+    test_env_.reset();
   }
 
-  void CheckConstants() {
-    EXPECT_EQ(constants_.return_type_size, 4);
-    EXPECT_EQ(constants_.arg0_type_size, 8);
-    EXPECT_EQ(constants_.arg1_type_size, ARG1_LEN);
-    EXPECT_EQ(constants_.arg2_type_size, 0); // varlen
+  void CheckFnAttr() {
+    EXPECT_EQ(fn_type_attr_.return_type_size, 8);
+    EXPECT_EQ(fn_type_attr_.arg0_type_size, 8);
+    EXPECT_EQ(fn_type_attr_.arg1_type_size, ARG1_LEN);
+    EXPECT_EQ(fn_type_attr_.arg2_type_size, 0); // varlen
   }
 
   static bool VerifyFunction(LlvmCodeGen* codegen, llvm::Function* fn) {
@@ -130,9 +163,6 @@ class ExprCodegenTest : public ::testing::Test {
   static void ResetVerification(LlvmCodeGen* codegen) {
     codegen->ResetVerification();
   }
-
-  FunctionContext* fn_ctx_;
-  Constants constants_;
 };
 
 TExprNode CreateDecimalLiteral(int precision, int scale) {
@@ -183,10 +213,12 @@ TExprNode CreateStringLiteral(int len = -1) {
   return expr;
 }
 
-// Creates a function call to TestGetConstant() in test-udfs.h
-TExprNode CreateFunctionCall(vector<TExprNode> children) {
+// Creates a function call to TestGetFnAttrs() in test-udfs.h
+TExprNode CreateFunctionCall(vector<TExprNode> children, int precision, int scale) {
   TScalarType scalar_type;
-  scalar_type.type = TPrimitiveType::INT;
+  scalar_type.type = TPrimitiveType::DECIMAL;
+  scalar_type.__set_precision(precision);
+  scalar_type.__set_scale(scale);
 
   TTypeNode type;
   type.type = TTypeNodeType::SCALAR;
@@ -196,10 +228,10 @@ TExprNode CreateFunctionCall(vector<TExprNode> children) {
   col_type.__set_types(vector<TTypeNode>(1, type));
 
   TFunctionName fn_name;
-  fn_name.function_name = "test_get_constant";
+  fn_name.function_name = "test_get_type_attr";
 
   TScalarFunction scalar_fn;
-  scalar_fn.symbol = TEST_GET_CONSTANT_SYMBOL;
+  scalar_fn.symbol = TEST_GET_FN_ATTR_SYMBOL;
 
   TFunction fn;
   fn.name = fn_name;
@@ -219,18 +251,22 @@ TExprNode CreateFunctionCall(vector<TExprNode> children) {
   return expr;
 }
 
-TEST_F(ExprCodegenTest, TestGetConstantInterpreted) {
-  DecimalVal arg0_val;
+TEST_F(ExprCodegenTest, TestGetConstFnAttrsInterpreted) {
+  // Call fn and check results'. The input is of type Decimal(10,2) (i.e. 10000.25) and
+  // the output type is Decimal(10,1) (i.e. 10000.3). The precision and scale of arguments
+  // and return types are encoded above (ARG0_*, RET_*);
+  int64_t v = 1000025;
+  DecimalVal arg0_val(v);
   StringVal arg1_val;
   StringVal arg2_val;
-  IntVal result = TestGetConstant(fn_ctx_, arg0_val, arg1_val, arg2_val);
+  DecimalVal result = TestGetFnAttrs(fn_ctx_, arg0_val, arg1_val, arg2_val);
   // sanity check result
   EXPECT_EQ(result.is_null, false);
-  EXPECT_EQ(result.val, 10);
-  CheckConstants();
+  EXPECT_EQ(result.val8, 100003);
+  CheckFnAttr();
 }
 
-TEST_F(ExprCodegenTest, TestInlineConstants) {
+TEST_F(ExprCodegenTest, TestInlineConstFnAttrs) {
   // Setup thrift descriptors
   TExprNode arg0 = CreateDecimalLiteral(ARG0_PRECISION, ARG0_SCALE);
   TExprNode arg1 = CreateStringLiteral(ARG1_LEN);
@@ -241,7 +277,7 @@ TEST_F(ExprCodegenTest, TestInlineConstants) {
   exprs.push_back(arg1);
   exprs.push_back(arg2);
 
-  TExprNode fn_call = CreateFunctionCall(exprs);
+  TExprNode fn_call = CreateFunctionCall(exprs, RET_PRECISION, RET_SCALE);
   exprs.insert(exprs.begin(), fn_call);
 
   TExpr texpr;
@@ -253,21 +289,21 @@ TEST_F(ExprCodegenTest, TestInlineConstants) {
   ExprContext* ctx;
   ASSERT_OK(Expr::CreateExprTree(&pool, texpr, &ctx));
 
-  // Get TestGetConstant() IR function
+  // Get TestGetFnAttrs() IR function
   stringstream test_udf_file;
   test_udf_file << getenv("IMPALA_HOME") << "/be/build/latest/exprs/expr-codegen-test.ll";
   scoped_ptr<LlvmCodeGen> codegen;
-  ASSERT_OK(ExprCodegenTest::CreateFromFile(&pool, test_udf_file.str(), &codegen));
-  Function* fn = codegen->GetFunction(TEST_GET_CONSTANT_SYMBOL, false);
+  ASSERT_OK(CreateFromFile(test_udf_file.str(), &codegen));
+  Function* fn = codegen->GetFunction(TEST_GET_FN_ATTR_SYMBOL, false);
   ASSERT_TRUE(fn != NULL);
 
-  // Function verification should fail because we haven't inlined GetConstant() calls
+  // Function verification should fail because we haven't inlined GetTypeAttr() calls
   bool verification_succeeded = VerifyFunction(codegen.get(), fn);
   EXPECT_FALSE(verification_succeeded);
 
-  // Call InlineConstants() and rerun verification
-  int replaced = InlineConstants(ctx->root(), codegen.get(), fn);
-  EXPECT_EQ(replaced, 4);
+  // Call InlineConstFnAttrs() and rerun verification
+  int replaced = InlineConstFnAttrs(ctx->root(), codegen.get(), fn);
+  EXPECT_EQ(replaced, 9);
   ResetVerification(codegen.get());
   verification_succeeded = VerifyFunction(codegen.get(), fn);
   EXPECT_TRUE(verification_succeeded) << LlvmCodeGen::Print(fn);
@@ -277,17 +313,19 @@ TEST_F(ExprCodegenTest, TestInlineConstants) {
   ASSERT_TRUE(fn != NULL);
   void* fn_ptr;
   codegen->AddFunctionToJit(fn, &fn_ptr);
-  ASSERT_OK(codegen->FinalizeModule());
-  LOG(ERROR) << "Optimized fn: " << LlvmCodeGen::Print(fn);
-
-  // Call fn and check results
-  DecimalVal arg0_val;
-  typedef IntVal (*TestGetConstantType)(FunctionContext*, const DecimalVal&);
-  IntVal result = reinterpret_cast<TestGetConstantType>(fn_ptr)(fn_ctx_, arg0_val);
+  EXPECT_TRUE(codegen->FinalizeModule().ok()) << LlvmCodeGen::Print(fn);
+
+  // Call fn and check results'. The input is of type Decimal(10,2) (i.e. 10000.25) and
+  // the output type is Decimal(10,1) (i.e. 10000.3). The precision and scale of arguments
+  // and return types are encoded above (ARG0_*, RET_*);
+  int64_t v = 1000025;
+  DecimalVal arg0_val(v);
+  typedef DecimalVal (*TestGetFnAttrs)(FunctionContext*, const DecimalVal&);
+  DecimalVal result = reinterpret_cast<TestGetFnAttrs>(fn_ptr)(fn_ctx_, arg0_val);
   // sanity check result
   EXPECT_EQ(result.is_null, false);
-  EXPECT_EQ(result.val, 10);
-  CheckConstants();
+  EXPECT_EQ(result.val8, 100003);
+  CheckFnAttr();
 }
 
 }

http://git-wip-us.apache.org/repos/asf/incubator-impala/blob/f982c3f7/be/src/exprs/expr-test.cc
----------------------------------------------------------------------
diff --git a/be/src/exprs/expr-test.cc b/be/src/exprs/expr-test.cc
index cad716e..1b2dc51 100644
--- a/be/src/exprs/expr-test.cc
+++ b/be/src/exprs/expr-test.cc
@@ -1335,6 +1335,16 @@ DecimalTestCase decimal_cases[] = {
     {{ false, 1, 32, 1 }, { false, 1, 32, 1 }} },
   { "mod(cast('-1.23' as decimal(32,2)), cast('1.0' as decimal(32,2)))",
     {{ false, -23, 32, 2 }, { false, -23, 32, 2 }} },
+  { "cast(cast(0.12344 as decimal(6,5)) as decimal(6,4))",
+    {{ false, 1234, 6, 4 }, { false, 1234, 6, 4 }} },
+  { "cast(cast(0.12345 as decimal(6,5)) as decimal(6,4))",
+    {{ false, 1234, 6, 4 }, { false, 1235, 6, 4 }} },
+  { "cast(cast('0.999' as decimal(4,3)) as decimal(1,0))",
+    {{ false, 0, 1, 0 }, { false, 1, 1, 0 }} },
+  { "cast(cast(999999999.99 as DECIMAL(11,2)) as DECIMAL(9,0))",
+    {{ false, 999999999, 9, 0 }, { true, 0, 9, 0 }} },
+  { "cast(cast(-999999999.99 as DECIMAL(11,2)) as DECIMAL(9,0))",
+    {{ false, -999999999, 9, 0 }, { true, 0, 9, 0 }} },
   { "mod(cast(NULL as decimal(2,0)), cast('10' as decimal(2,0)))",
     {{ true, 0, 2, 0 }, { true, 0, 2, 0 }} },
   { "mod(cast('10' as decimal(2,0)), cast(NULL as decimal(2,0)))",
@@ -1357,7 +1367,7 @@ TEST_F(ExprTest, DecimalArithmeticExprs) {
     string opt = "DECIMAL_V2=" + lexical_cast<string>(v2);
     executor_->pushExecOption(opt);
     for (const DecimalTestCase& c : decimal_cases) {
-      const DecimalExpectedResult& r = c.expected[0];
+      const DecimalExpectedResult& r = c.expected[v2];
       const ColumnType& type = ColumnType::CreateDecimalType(r.precision, r.scale);
       if (r.null) {
         TestIsNull(c.expr, type);

http://git-wip-us.apache.org/repos/asf/incubator-impala/blob/f982c3f7/be/src/exprs/expr.cc
----------------------------------------------------------------------
diff --git a/be/src/exprs/expr.cc b/be/src/exprs/expr.cc
index f119a9d..32c16a0 100644
--- a/be/src/exprs/expr.cc
+++ b/be/src/exprs/expr.cc
@@ -79,8 +79,6 @@ namespace impala {
 
 const char* Expr::LLVM_CLASS_NAME = "class.impala::Expr";
 
-const char* Expr::GET_CONSTANT_INT_SYMBOL_PREFIX = "_ZN6impala4Expr14GetConstantInt";
-
 template<class T>
 bool ParseString(const string& str, T* val) {
   istringstream stream(str);
@@ -589,90 +587,6 @@ Status Expr::GetConstVal(
   return GetFnContextError(context);
 }
 
-int Expr::GetConstantInt(const FunctionContext::TypeDesc& return_type,
-    const std::vector<FunctionContext::TypeDesc>& arg_types, ExprConstant c, int i) {
-  switch (c) {
-    case RETURN_TYPE_SIZE:
-      DCHECK_EQ(i, -1);
-      return AnyValUtil::TypeDescToColumnType(return_type).GetByteSize();
-    case RETURN_TYPE_PRECISION:
-      DCHECK_EQ(i, -1);
-      DCHECK_EQ(return_type.type, FunctionContext::TYPE_DECIMAL);
-      return return_type.precision;
-    case RETURN_TYPE_SCALE:
-      DCHECK_EQ(i, -1);
-      DCHECK_EQ(return_type.type, FunctionContext::TYPE_DECIMAL);
-      return return_type.scale;
-    case ARG_TYPE_SIZE:
-      DCHECK_GE(i, 0);
-      DCHECK_LT(i, arg_types.size());
-      return AnyValUtil::TypeDescToColumnType(arg_types[i]).GetByteSize();
-    case ARG_TYPE_PRECISION:
-      DCHECK_GE(i, 0);
-      DCHECK_LT(i, arg_types.size());
-      DCHECK_EQ(arg_types[i].type, FunctionContext::TYPE_DECIMAL);
-      return arg_types[i].precision;
-    case ARG_TYPE_SCALE:
-      DCHECK_GE(i, 0);
-      DCHECK_LT(i, arg_types.size());
-      DCHECK_EQ(arg_types[i].type, FunctionContext::TYPE_DECIMAL);
-      return arg_types[i].scale;
-    default:
-      CHECK(false) << "NYI";
-      return -1;
-  }
-}
-
-int Expr::GetConstantInt(const FunctionContext& ctx, ExprConstant c, int i) {
-  return GetConstantInt(ctx.GetReturnType(), ctx.impl()->arg_types(), c, i);
-}
-
-int Expr::InlineConstants(LlvmCodeGen* codegen, Function* fn) {
-  FunctionContext::TypeDesc return_type = AnyValUtil::ColumnTypeToTypeDesc(type_);
-  vector<FunctionContext::TypeDesc> arg_types;
-  for (int i = 0; i < children_.size(); ++i) {
-    arg_types.push_back(AnyValUtil::ColumnTypeToTypeDesc(children_[i]->type_));
-  }
-  return InlineConstants(return_type, arg_types, codegen, fn);
-}
-
-int Expr::InlineConstants(const FunctionContext::TypeDesc& return_type,
-    const std::vector<FunctionContext::TypeDesc>& arg_types, LlvmCodeGen* codegen,
-    Function* fn) {
-  int replaced = 0;
-  for (inst_iterator iter = inst_begin(fn), end = inst_end(fn); iter != end;) {
-    // Increment iter now so we don't mess it up modifying the instruction below
-    Instruction* instr = &*(iter++);
-
-    // Look for call instructions
-    if (!isa<CallInst>(instr)) continue;
-    CallInst* call_instr = cast<CallInst>(instr);
-    Function* called_fn = call_instr->getCalledFunction();
-
-    // Look for call to Expr::GetConstant*()
-    if (called_fn == NULL ||
-        called_fn->getName().find(GET_CONSTANT_INT_SYMBOL_PREFIX) == string::npos) {
-      continue;
-    }
-
-    // 'c' and 'i' arguments must be constant
-    ConstantInt* c_arg = dyn_cast<ConstantInt>(call_instr->getArgOperand(1));
-    ConstantInt* i_arg = dyn_cast<ConstantInt>(call_instr->getArgOperand(2));
-    DCHECK(c_arg != NULL) << "Non-constant 'c' argument to Expr::GetConstant*()";
-    DCHECK(i_arg != NULL) << "Non-constant 'i' argument to Expr::GetConstant*()";
-
-    // Replace the called function with the appropriate constant
-    ExprConstant c_val = static_cast<ExprConstant>(c_arg->getSExtValue());
-    int i_val = static_cast<int>(i_arg->getSExtValue());
-    // All supported constants are currently integers.
-    call_instr->replaceAllUsesWith(ConstantInt::get(codegen->GetType(TYPE_INT),
-        GetConstantInt(return_type, arg_types, c_val, i_val)));
-    call_instr->eraseFromParent();
-    ++replaced;
-  }
-  return replaced;
-}
-
 Status Expr::GetCodegendComputeFnWrapper(LlvmCodeGen* codegen, Function** fn) {
   if (ir_compute_fn_ != NULL) {
     *fn = ir_compute_fn_;



[2/4] incubator-impala git commit: IMPALA-4810: fix incorrect expr-test decimal types

Posted by kw...@apache.org.
IMPALA-4810: fix incorrect expr-test decimal types

Many of the types for the decimal round/truncate and related tests
are incorrect. This was never caught because the type was only used
by the string parser, and larger types would work. The change for
IMPALA-4370 adds validation of the expected type, so fix these.

Change-Id: I1e750fc01ab64ff27182670d8e823c012743804b
Reviewed-on: http://gerrit.cloudera.org:8080/5959
Reviewed-by: Dan Hecht <dh...@cloudera.com>
Tested-by: Impala Public Jenkins


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

Branch: refs/heads/master
Commit: a78726db308f41c3a7a710cda8f049d21793bc42
Parents: af46998
Author: Dan Hecht <dh...@cloudera.com>
Authored: Thu Feb 9 14:41:37 2017 -0800
Committer: Impala Public Jenkins <im...@gerrit.cloudera.org>
Committed: Sat Feb 11 03:30:15 2017 +0000

----------------------------------------------------------------------
 be/src/exprs/expr-test.cc | 183 ++++++++++++++++++++++-------------------
 be/src/runtime/types.cc   |   5 +-
 2 files changed, 101 insertions(+), 87 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-impala/blob/a78726db/be/src/exprs/expr-test.cc
----------------------------------------------------------------------
diff --git a/be/src/exprs/expr-test.cc b/be/src/exprs/expr-test.cc
index e6cfcdb..cad716e 100644
--- a/be/src/exprs/expr-test.cc
+++ b/be/src/exprs/expr-test.cc
@@ -400,27 +400,38 @@ class ExprTest : public testing::Test {
         "1970-01-01 00:00:00");
   }
 
+  // Verify that 'expr' has the same precision and scale as 'expected_type'.
+  void TestDecimalResultType(const string& expr, const ColumnType& expected_type) {
+    const string typeof_expr = "typeof(" + expr + ")";
+    const string typeof_result = GetValue(typeof_expr, TYPE_STRING);
+    EXPECT_EQ(expected_type.DebugString(), typeof_result) << typeof_expr;
+  }
+
   // Decimals don't work with TestValue.
   // TODO: figure out what operators need to be implemented to work with EXPECT_EQ
-  // TODO: verify that the result type has the same precision/scale as 'expected_type'.
   template<typename T>
   void TestDecimalValue(const string& expr, const T& expected_result,
       const ColumnType& expected_type) {
+    // Verify precision and scale of the expression match the expected type.
+    TestDecimalResultType(expr, expected_type);
+    // Verify the expression result matches the expected result, for the given the
+    // precision and scale.
     const string value = GetValue(expr, expected_type);
-
     StringParser::ParseResult result;
+    // These require that we've passed the correct type to StringToDecimal(), so these
+    // results are valid only when TestDecimalResultType() succeeded.
     switch (expected_type.GetByteSize()) {
       case 4:
         EXPECT_EQ(expected_result.value(), StringParser::StringToDecimal<int32_t>(
-            &value[0], value.size(), expected_type, &result).value());
+            &value[0], value.size(), expected_type, &result).value()) << expr;
         break;
       case 8:
         EXPECT_EQ(expected_result.value(), StringParser::StringToDecimal<int64_t>(
-            &value[0], value.size(), expected_type, &result).value());
+            &value[0], value.size(), expected_type, &result).value()) << expr;
         break;
       case 16:
         EXPECT_EQ(expected_result.value(), StringParser::StringToDecimal<int128_t>(
-            &value[0], value.size(), expected_type, &result).value());
+            &value[0], value.size(), expected_type, &result).value()) << expr;
         break;
       default:
         EXPECT_TRUE(false) << expected_type << " " << expected_type.GetByteSize();
@@ -1297,7 +1308,7 @@ DecimalTestCase decimal_cases[] = {
   { "cast(1.23 as decimal(8,2)) + cast(1 as decimal(20,3))",
     {{ false, 2230, 21, 3 }, { false, 2230, 21, 3 }} },
   { "cast(1.23 as decimal(30,2)) - cast(1 as decimal(4,3))",
-    {{ false, 230, 34, 3 }, { false, 230, 34, 3 }} },
+    {{ false, 230, 32, 3 }, { false, 230, 32, 3 }} },
   { "cast(1.23 as decimal(30,2)) * cast(1 as decimal(10,3))",
     {{ false, 123000, 38, 5 }, { false, 123000, 38, 5 }} },
   { "cast(1.23 as decimal(30,2)) / cast(1 as decimal(20,3))",
@@ -5489,75 +5500,75 @@ TEST_F(ExprTest, DecimalFunctions) {
 
   // Ceil()
   TestDecimalValue("ceil(cast('0' as decimal(6,5)))", Decimal4Value(0),
-      ColumnType::CreateDecimalType(6, 0));
+      ColumnType::CreateDecimalType(2, 0));
   TestDecimalValue("ceil(cast('3.14159' as decimal(6,5)))", Decimal4Value(4),
-      ColumnType::CreateDecimalType(6, 0));
+      ColumnType::CreateDecimalType(2, 0));
   TestDecimalValue("ceil(cast('-3.14159' as decimal(6,5)))", Decimal4Value(-3),
-      ColumnType::CreateDecimalType(6, 0));
+      ColumnType::CreateDecimalType(2, 0));
   TestDecimalValue("ceil(cast('3' as decimal(6,5)))", Decimal4Value(3),
-      ColumnType::CreateDecimalType(6, 0));
+      ColumnType::CreateDecimalType(2, 0));
   TestDecimalValue("ceil(cast('3.14159' as decimal(13,5)))", Decimal8Value(4),
-      ColumnType::CreateDecimalType(13, 0));
+      ColumnType::CreateDecimalType(9, 0));
   TestDecimalValue("ceil(cast('-3.14159' as decimal(13,5)))", Decimal8Value(-3),
-      ColumnType::CreateDecimalType(13, 0));
+      ColumnType::CreateDecimalType(9, 0));
   TestDecimalValue("ceil(cast('3' as decimal(13,5)))", Decimal8Value(3),
-      ColumnType::CreateDecimalType(13, 0));
+      ColumnType::CreateDecimalType(9, 0));
   TestDecimalValue("ceil(cast('3.14159' as decimal(33,5)))", Decimal16Value(4),
-      ColumnType::CreateDecimalType(33, 0));
+      ColumnType::CreateDecimalType(29, 0));
   TestDecimalValue("ceil(cast('-3.14159' as decimal(33,5)))", Decimal16Value(-3),
-      ColumnType::CreateDecimalType(33, 0));
+      ColumnType::CreateDecimalType(29, 0));
   TestDecimalValue("ceil(cast('3' as decimal(33,5)))", Decimal16Value(3),
-      ColumnType::CreateDecimalType(33, 0));
+      ColumnType::CreateDecimalType(29, 0));
   TestDecimalValue("ceil(cast('9.14159' as decimal(6,5)))", Decimal4Value(10),
       ColumnType::CreateDecimalType(2, 0));
   TestIsNull("ceil(cast(NULL as decimal(2,0)))", ColumnType::CreateDecimalType(2,0));
 
   // Floor()
   TestDecimalValue("floor(cast('3.14159' as decimal(6,5)))", Decimal4Value(3),
-      ColumnType::CreateDecimalType(6, 0));
+      ColumnType::CreateDecimalType(2, 0));
   TestDecimalValue("floor(cast('-3.14159' as decimal(6,5)))", Decimal4Value(-4),
-      ColumnType::CreateDecimalType(6, 0));
+      ColumnType::CreateDecimalType(2, 0));
   TestDecimalValue("floor(cast('3' as decimal(6,5)))", Decimal4Value(3),
-      ColumnType::CreateDecimalType(6, 0));
+      ColumnType::CreateDecimalType(2, 0));
   TestDecimalValue("floor(cast('3.14159' as decimal(13,5)))", Decimal8Value(3),
-      ColumnType::CreateDecimalType(13, 0));
+      ColumnType::CreateDecimalType(9, 0));
   TestDecimalValue("floor(cast('-3.14159' as decimal(13,5)))", Decimal8Value(-4),
-      ColumnType::CreateDecimalType(13, 0));
+      ColumnType::CreateDecimalType(9, 0));
   TestDecimalValue("floor(cast('3' as decimal(13,5)))", Decimal8Value(3),
-      ColumnType::CreateDecimalType(13, 0));
+      ColumnType::CreateDecimalType(9, 0));
   TestDecimalValue("floor(cast('3.14159' as decimal(33,5)))", Decimal16Value(3),
-      ColumnType::CreateDecimalType(33, 0));
+      ColumnType::CreateDecimalType(29, 0));
   TestDecimalValue("floor(cast('-3.14159' as decimal(33,5)))", Decimal16Value(-4),
-      ColumnType::CreateDecimalType(33, 0));
+      ColumnType::CreateDecimalType(29, 0));
   TestDecimalValue("floor(cast('3' as decimal(33,5)))", Decimal16Value(3),
-      ColumnType::CreateDecimalType(33, 0));
+      ColumnType::CreateDecimalType(29, 0));
   TestDecimalValue("floor(cast('-9.14159' as decimal(6,5)))", Decimal4Value(-10),
       ColumnType::CreateDecimalType(2, 0));
   TestIsNull("floor(cast(NULL as decimal(2,0)))", ColumnType::CreateDecimalType(2,0));
 
   // Dfloor() alias
   TestDecimalValue("dfloor(cast('3.14159' as decimal(6,5)))", Decimal4Value(3),
-      ColumnType::CreateDecimalType(6, 0));
+      ColumnType::CreateDecimalType(2, 0));
 
   // Round()
   TestDecimalValue("round(cast('3.14159' as decimal(6,5)))", Decimal4Value(3),
-      ColumnType::CreateDecimalType(6, 0));
+      ColumnType::CreateDecimalType(2, 0));
   TestDecimalValue("round(cast('-3.14159' as decimal(6,5)))", Decimal4Value(-3),
-      ColumnType::CreateDecimalType(6, 0));
+      ColumnType::CreateDecimalType(2, 0));
   TestDecimalValue("round(cast('3' as decimal(6,5)))", Decimal4Value(3),
-      ColumnType::CreateDecimalType(6, 0));
+      ColumnType::CreateDecimalType(2, 0));
   TestDecimalValue("round(cast('3.14159' as decimal(13,5)))", Decimal8Value(3),
-      ColumnType::CreateDecimalType(13, 0));
+      ColumnType::CreateDecimalType(9, 0));
   TestDecimalValue("round(cast('-3.14159' as decimal(13,5)))", Decimal8Value(-3),
-      ColumnType::CreateDecimalType(13, 0));
+      ColumnType::CreateDecimalType(9, 0));
   TestDecimalValue("round(cast('3' as decimal(13,5)))", Decimal8Value(3),
-      ColumnType::CreateDecimalType(13, 0));
+      ColumnType::CreateDecimalType(9, 0));
   TestDecimalValue("round(cast('3.14159' as decimal(33,5)))", Decimal16Value(3),
-      ColumnType::CreateDecimalType(33, 0));
+      ColumnType::CreateDecimalType(29, 0));
   TestDecimalValue("round(cast('-3.14159' as decimal(33,5)))", Decimal16Value(-3),
-      ColumnType::CreateDecimalType(33, 0));
+      ColumnType::CreateDecimalType(29, 0));
   TestDecimalValue("round(cast('3' as decimal(33,5)))", Decimal16Value(3),
-      ColumnType::CreateDecimalType(33, 0));
+      ColumnType::CreateDecimalType(29, 0));
   TestDecimalValue("round(cast('9.54159' as decimal(6,5)))", Decimal4Value(10),
       ColumnType::CreateDecimalType(2, 0));
   TestDecimalValue("round(cast('-9.54159' as decimal(6,5)))", Decimal4Value(-10),
@@ -5566,23 +5577,23 @@ TEST_F(ExprTest, DecimalFunctions) {
 
   // Truncate()
   TestDecimalValue("truncate(cast('3.54159' as decimal(6,5)))", Decimal4Value(3),
-      ColumnType::CreateDecimalType(6, 0));
+      ColumnType::CreateDecimalType(1, 0));
   TestDecimalValue("truncate(cast('-3.54159' as decimal(6,5)))", Decimal4Value(-3),
-      ColumnType::CreateDecimalType(6, 0));
+      ColumnType::CreateDecimalType(1, 0));
   TestDecimalValue("truncate(cast('3' as decimal(6,5)))", Decimal4Value(3),
-      ColumnType::CreateDecimalType(6, 0));
+      ColumnType::CreateDecimalType(1, 0));
   TestDecimalValue("truncate(cast('3.54159' as decimal(13,5)))", Decimal8Value(3),
-      ColumnType::CreateDecimalType(13, 0));
+      ColumnType::CreateDecimalType(8, 0));
   TestDecimalValue("truncate(cast('-3.54159' as decimal(13,5)))", Decimal8Value(-3),
-      ColumnType::CreateDecimalType(13, 0));
+      ColumnType::CreateDecimalType(8, 0));
   TestDecimalValue("truncate(cast('3' as decimal(13,5)))", Decimal8Value(3),
-      ColumnType::CreateDecimalType(13, 0));
+      ColumnType::CreateDecimalType(8, 0));
   TestDecimalValue("truncate(cast('3.54159' as decimal(33,5)))", Decimal16Value(3),
-      ColumnType::CreateDecimalType(33, 0));
+      ColumnType::CreateDecimalType(28, 0));
   TestDecimalValue("truncate(cast('-3.54159' as decimal(33,5)))", Decimal16Value(-3),
-      ColumnType::CreateDecimalType(33, 0));
+      ColumnType::CreateDecimalType(28, 0));
   TestDecimalValue("truncate(cast('3' as decimal(33,5)))", Decimal16Value(3),
-      ColumnType::CreateDecimalType(33, 0));
+      ColumnType::CreateDecimalType(28, 0));
   TestDecimalValue("truncate(cast('9.54159' as decimal(6,5)))", Decimal4Value(9),
       ColumnType::CreateDecimalType(1, 0));
   TestIsNull("truncate(cast(NULL as decimal(2,0)))", ColumnType::CreateDecimalType(2,0));
@@ -5591,15 +5602,15 @@ TEST_F(ExprTest, DecimalFunctions) {
   TestIsNull("round(cast(NULL as decimal(2,0)), 1)", ColumnType::CreateDecimalType(2,0));
 
   TestDecimalValue("round(cast('3.1615' as decimal(6,4)), 0)", Decimal4Value(3),
-      ColumnType::CreateDecimalType(2, 0));
+      ColumnType::CreateDecimalType(3, 0));
   TestDecimalValue("round(cast('-3.1615' as decimal(6,4)), 1)", Decimal4Value(-32),
-      ColumnType::CreateDecimalType(3, 1));
+      ColumnType::CreateDecimalType(4, 1));
   TestDecimalValue("round(cast('3.1615' as decimal(6,4)), 2)", Decimal4Value(316),
-      ColumnType::CreateDecimalType(4, 2));
+      ColumnType::CreateDecimalType(5, 2));
   TestDecimalValue("round(cast('3.1615' as decimal(6,4)), 3)", Decimal4Value(3162),
-      ColumnType::CreateDecimalType(5, 3));
+      ColumnType::CreateDecimalType(6, 3));
   TestDecimalValue("round(cast('-3.1615' as decimal(6,4)), 3)", Decimal4Value(-3162),
-      ColumnType::CreateDecimalType(5, 3));
+      ColumnType::CreateDecimalType(6, 3));
   TestDecimalValue("round(cast('3.1615' as decimal(6,4)), 4)", Decimal4Value(31615),
       ColumnType::CreateDecimalType(6, 4));
   TestDecimalValue("round(cast('-3.1615' as decimal(6,4)), 5)", Decimal4Value(-316150),
@@ -5618,21 +5629,21 @@ TEST_F(ExprTest, DecimalFunctions) {
       ColumnType::CreateDecimalType(5, 1));
 
   TestDecimalValue("round(cast('-3.1615' as decimal(16,4)), 0)", Decimal8Value(-3),
-      ColumnType::CreateDecimalType(12, 0));
+      ColumnType::CreateDecimalType(13, 0));
   TestDecimalValue("round(cast('3.1615' as decimal(16,4)), 1)", Decimal8Value(32),
-      ColumnType::CreateDecimalType(13, 1));
+      ColumnType::CreateDecimalType(14, 1));
   TestDecimalValue("round(cast('-3.1615' as decimal(16,4)), 2)", Decimal8Value(-316),
-      ColumnType::CreateDecimalType(14, 2));
+      ColumnType::CreateDecimalType(15, 2));
   TestDecimalValue("round(cast('3.1615' as decimal(16,4)), 3)", Decimal8Value(3162),
-      ColumnType::CreateDecimalType(15, 3));
+      ColumnType::CreateDecimalType(16, 3));
   TestDecimalValue("round(cast('-3.1615' as decimal(16,4)), 3)", Decimal8Value(-3162),
-      ColumnType::CreateDecimalType(15, 3));
+      ColumnType::CreateDecimalType(16, 3));
   TestDecimalValue("round(cast('-3.1615' as decimal(16,4)), 4)", Decimal8Value(-31615),
       ColumnType::CreateDecimalType(16, 4));
   TestDecimalValue("round(cast('3.1615' as decimal(16,4)), 5)", Decimal8Value(316150),
       ColumnType::CreateDecimalType(17, 5));
   TestDecimalValue("round(cast('-999.951' as decimal(16,3)), 1)", Decimal8Value(-10000),
-      ColumnType::CreateDecimalType(17, 1));
+      ColumnType::CreateDecimalType(15, 1));
 
   TestDecimalValue("round(cast('-175.0' as decimal(15,1)), 0)", Decimal8Value(-175),
       ColumnType::CreateDecimalType(15, 0));
@@ -5646,19 +5657,19 @@ TEST_F(ExprTest, DecimalFunctions) {
       ColumnType::CreateDecimalType(15, 0));
 
   TestDecimalValue("round(cast('3.1615' as decimal(32,4)), 0)", Decimal16Value(3),
-      ColumnType::CreateDecimalType(32, 0));
+      ColumnType::CreateDecimalType(29, 0));
   TestDecimalValue("round(cast('-3.1615' as decimal(32,4)), 1)", Decimal16Value(-32),
-      ColumnType::CreateDecimalType(33, 1));
+      ColumnType::CreateDecimalType(30, 1));
   TestDecimalValue("round(cast('3.1615' as decimal(32,4)), 2)", Decimal16Value(316),
-      ColumnType::CreateDecimalType(34, 2));
+      ColumnType::CreateDecimalType(31, 2));
   TestDecimalValue("round(cast('3.1615' as decimal(32,4)), 3)", Decimal16Value(3162),
-      ColumnType::CreateDecimalType(35, 3));
+      ColumnType::CreateDecimalType(32, 3));
   TestDecimalValue("round(cast('-3.1615' as decimal(32,4)), 3)", Decimal16Value(-3162),
-      ColumnType::CreateDecimalType(36, 3));
+      ColumnType::CreateDecimalType(32, 3));
   TestDecimalValue("round(cast('3.1615' as decimal(32,4)), 4)", Decimal16Value(31615),
-      ColumnType::CreateDecimalType(37, 4));
+      ColumnType::CreateDecimalType(32, 4));
   TestDecimalValue("round(cast('-3.1615' as decimal(32,5)), 5)", Decimal16Value(-316150),
-      ColumnType::CreateDecimalType(38, 5));
+      ColumnType::CreateDecimalType(32, 5));
   TestDecimalValue("round(cast('-175.0' as decimal(35,1)), 0)", Decimal16Value(-175),
       ColumnType::CreateDecimalType(35, 0));
   TestDecimalValue("round(cast('175.0' as decimal(35,1)), -1)", Decimal16Value(180),
@@ -5670,40 +5681,40 @@ TEST_F(ExprTest, DecimalFunctions) {
   TestDecimalValue("round(cast('-175.0' as decimal(35,1)), -4)", Decimal16Value(0),
       ColumnType::CreateDecimalType(35, 0));
   TestDecimalValue("round(cast('99999.9951' as decimal(35,4)), 2)",
-      Decimal16Value(10000000), ColumnType::CreateDecimalType(36, 2));
+      Decimal16Value(10000000), ColumnType::CreateDecimalType(34, 2));
 
   // Dround() alias
   TestDecimalValue("dround(cast('3.14159' as decimal(6,5)))", Decimal4Value(3),
-      ColumnType::CreateDecimalType(6, 0));
+      ColumnType::CreateDecimalType(2, 0));
   TestDecimalValue("dround(cast('99999.9951' as decimal(35,4)), 2)",
-      Decimal16Value(10000000), ColumnType::CreateDecimalType(36, 2));
+      Decimal16Value(10000000), ColumnType::CreateDecimalType(34, 2));
 
   // TruncateTo()
   TestIsNull("truncate(cast(NULL as decimal(2,0)), 1)",
       ColumnType::CreateDecimalType(2,0));
 
   TestDecimalValue("truncate(cast('-3.1615' as decimal(6,4)), 0)", Decimal4Value(-3),
-      ColumnType::CreateDecimalType(6, 0));
+      ColumnType::CreateDecimalType(2, 0));
   TestDecimalValue("truncate(cast('3.1615' as decimal(6,4)), 1)", Decimal4Value(31),
-      ColumnType::CreateDecimalType(6, 1));
+      ColumnType::CreateDecimalType(3, 1));
   TestDecimalValue("truncate(cast('-3.1615' as decimal(6,4)), 2)", Decimal4Value(-316),
-      ColumnType::CreateDecimalType(6, 2));
+      ColumnType::CreateDecimalType(4, 2));
   TestDecimalValue("truncate(cast('3.1615' as decimal(6,4)), 3)", Decimal4Value(3161),
-      ColumnType::CreateDecimalType(6, 3));
+      ColumnType::CreateDecimalType(5, 3));
   TestDecimalValue("truncate(cast('-3.1615' as decimal(6,4)), 4)", Decimal4Value(-31615),
       ColumnType::CreateDecimalType(6, 4));
   TestDecimalValue("truncate(cast('3.1615' as decimal(6,4)), 5)", Decimal4Value(316150),
       ColumnType::CreateDecimalType(7, 5));
   TestDecimalValue("truncate(cast('175.0' as decimal(6,1)), 0)", Decimal4Value(175),
-      ColumnType::CreateDecimalType(6, 0));
+      ColumnType::CreateDecimalType(5, 0));
   TestDecimalValue("truncate(cast('-175.0' as decimal(6,1)), -1)", Decimal4Value(-170),
-      ColumnType::CreateDecimalType(6, 0));
+      ColumnType::CreateDecimalType(5, 0));
   TestDecimalValue("truncate(cast('175.0' as decimal(6,1)), -2)", Decimal4Value(100),
-      ColumnType::CreateDecimalType(6, 0));
+      ColumnType::CreateDecimalType(5, 0));
   TestDecimalValue("truncate(cast('-175.0' as decimal(6,1)), -3)", Decimal4Value(0),
-      ColumnType::CreateDecimalType(6, 0));
+      ColumnType::CreateDecimalType(5, 0));
   TestDecimalValue("truncate(cast('175.0' as decimal(6,1)), -4)", Decimal4Value(0),
-      ColumnType::CreateDecimalType(6, 0));
+      ColumnType::CreateDecimalType(5, 0));
 
   TestDecimalValue("truncate(cast('-3.1615' as decimal(16,4)), 0)", Decimal8Value(-3),
       ColumnType::CreateDecimalType(12, 0));
@@ -5718,15 +5729,15 @@ TEST_F(ExprTest, DecimalFunctions) {
   TestDecimalValue("truncate(cast('-3.1615' as decimal(16,4)), 5)",
       Decimal8Value(-316150), ColumnType::CreateDecimalType(17, 5));
   TestDecimalValue("truncate(cast('-175.0' as decimal(15,1)), 0)", Decimal8Value(-175),
-      ColumnType::CreateDecimalType(15, 0));
+      ColumnType::CreateDecimalType(14, 0));
   TestDecimalValue("truncate(cast('175.0' as decimal(15,1)), -1)", Decimal8Value(170),
-      ColumnType::CreateDecimalType(15, 0));
+      ColumnType::CreateDecimalType(14, 0));
   TestDecimalValue("truncate(cast('-175.0' as decimal(15,1)), -2)", Decimal8Value(-100),
-      ColumnType::CreateDecimalType(15, 0));
+      ColumnType::CreateDecimalType(14, 0));
   TestDecimalValue("truncate(cast('175.0' as decimal(15,1)), -3)", Decimal8Value(0),
-      ColumnType::CreateDecimalType(15, 0));
+      ColumnType::CreateDecimalType(14, 0));
   TestDecimalValue("truncate(cast('-175.0' as decimal(15,1)), -4)", Decimal8Value(0),
-      ColumnType::CreateDecimalType(15, 0));
+      ColumnType::CreateDecimalType(14, 0));
 
   TestDecimalValue("truncate(cast('-3.1615' as decimal(32,4)), 0)",
       Decimal16Value(-3), ColumnType::CreateDecimalType(28, 0));
@@ -5741,21 +5752,21 @@ TEST_F(ExprTest, DecimalFunctions) {
   TestDecimalValue("truncate(cast('3.1615' as decimal(32,4)), 5)",
       Decimal16Value(316150), ColumnType::CreateDecimalType(33, 5));
   TestDecimalValue("truncate(cast('-175.0' as decimal(35,1)), 0)",
-      Decimal16Value(-175), ColumnType::CreateDecimalType(35, 0));
+      Decimal16Value(-175), ColumnType::CreateDecimalType(34, 0));
   TestDecimalValue("truncate(cast('175.0' as decimal(35,1)), -1)",
-      Decimal16Value(170), ColumnType::CreateDecimalType(35, 0));
+      Decimal16Value(170), ColumnType::CreateDecimalType(34, 0));
   TestDecimalValue("truncate(cast('-175.0' as decimal(35,1)), -2)",
-      Decimal16Value(-100), ColumnType::CreateDecimalType(35, 0));
+      Decimal16Value(-100), ColumnType::CreateDecimalType(34, 0));
   TestDecimalValue("truncate(cast('175.0' as decimal(35,1)), -3)",
-      Decimal16Value(0), ColumnType::CreateDecimalType(35, 0));
+      Decimal16Value(0), ColumnType::CreateDecimalType(34, 0));
   TestDecimalValue("truncate(cast('-175.0' as decimal(35,1)), -4)",
-      Decimal16Value(0), ColumnType::CreateDecimalType(35, 0));
+      Decimal16Value(0), ColumnType::CreateDecimalType(34, 0));
 
   // Dtrunc() alias
   TestDecimalValue("dtrunc(cast('3.54159' as decimal(6,5)))", Decimal4Value(3),
-      ColumnType::CreateDecimalType(6, 0));
+      ColumnType::CreateDecimalType(1, 0));
   TestDecimalValue("dtrunc(cast('-3.1615' as decimal(6,4)), 0)", Decimal4Value(-3),
-      ColumnType::CreateDecimalType(6, 0));
+      ColumnType::CreateDecimalType(2, 0));
 
   // Overflow on Round()/etc. This can only happen when the input is has enough
   // leading 9's.

http://git-wip-us.apache.org/repos/asf/incubator-impala/blob/a78726db/be/src/runtime/types.cc
----------------------------------------------------------------------
diff --git a/be/src/runtime/types.cc b/be/src/runtime/types.cc
index f580628..2cc63ef 100644
--- a/be/src/runtime/types.cc
+++ b/be/src/runtime/types.cc
@@ -303,7 +303,10 @@ string ColumnType::DebugString() const {
       ss << "CHAR(" << len << ")";
       return ss.str();
     case TYPE_DECIMAL:
-      ss << "DECIMAL(" << precision << ", " << scale << ")";
+      ss << "DECIMAL(" << precision << "," << scale << ")";
+      return ss.str();
+    case TYPE_VARCHAR:
+      ss << "VARCHAR(" << len << ")";
       return ss.str();
     default:
       return TypeToString(type);


[3/4] incubator-impala git commit: IMPALA-2020, IMPALA-4809: Codegen support for DECIMAL_V2

Posted by kw...@apache.org.
http://git-wip-us.apache.org/repos/asf/incubator-impala/blob/f982c3f7/be/src/exprs/expr.h
----------------------------------------------------------------------
diff --git a/be/src/exprs/expr.h b/be/src/exprs/expr.h
index 13ba312..3a92c21 100644
--- a/be/src/exprs/expr.h
+++ b/be/src/exprs/expr.h
@@ -260,55 +260,8 @@ class Expr {
   /// not strip these symbols.
   static void InitBuiltinsDummy();
 
-  // Any additions to this enum must be reflected in both GetConstant*() and
-  // GetIrConstant().
-  enum ExprConstant {
-    // RETURN_TYPE_*: properties of FunctionContext::GetReturnType().
-    RETURN_TYPE_SIZE, // int
-    RETURN_TYPE_PRECISION, // int
-    RETURN_TYPE_SCALE, // int
-    // ARG_TYPE_* with parameter i: properties of FunctionContext::GetArgType(i).
-    ARG_TYPE_SIZE, // int[]
-    ARG_TYPE_PRECISION, // int[]
-    ARG_TYPE_SCALE, // int[]
-  };
-
-  // Static function for obtaining a runtime constant.  Expr compute functions and
-  // builtins implementing the UDF interface should use this function, rather than
-  // accessing runtime constants directly, so any recognized constants can be inlined via
-  // InlineConstants() in the codegen path. In the interpreted path, this function will
-  // work as-is.
-  //
-  // 'c' determines which constant is returned. The type of the constant is annotated in
-  // the ExprConstant enum above. If the constant is an array, 'i' must be specified and
-  // indicates which element to return. 'i' must always be an immediate integer value so
-  // InlineConstants() can resolve the index, e.g., it cannot be a variable or an
-  // expression like "1 + 1".  For example, if 'c' = ARG_TYPE_SIZE, then 'T' = int and
-  // 0 <= i < children_.size().
-  //
-  // InlineConstants() can be run on the function to replace recognized constants. The
-  // constants are only replaced in the function itself, so any callee functions with
-  // constants to be replaced must be inlined into the function that InlineConstants()
-  // is run on (e.g. by annotating them with IR_ALWAYS_INLINE).
-  //
-  // TODO: implement a loop unroller (or use LLVM's) so we can use GetConstantInt() in
-  // loops
-  static int GetConstantInt(const FunctionContext& ctx, ExprConstant c, int i = -1);
-
-  /// Finds all calls to Expr::GetConstantInt() in 'fn' and replaces them with the
-  /// appropriate runtime constants based on the arguments. 'return_type' is the
-  /// return type of the UDF or UDAF, i.e. the value of FunctionContext::GetReturnType().
-  /// 'arg_types' are the argument types of the UDF or UDAF, i.e. the values of
-  /// FunctionContext::GetArgType().
-  static int InlineConstants(const FunctionContext::TypeDesc& return_type,
-      const std::vector<FunctionContext::TypeDesc>& arg_types, LlvmCodeGen* codegen,
-      llvm::Function* fn);
-
   static const char* LLVM_CLASS_NAME;
 
-  // Expr::GetConstantInt() symbol prefix.
-  static const char* GET_CONSTANT_INT_SYMBOL_PREFIX;
-
  protected:
   friend class AggFnEvaluator;
   friend class CastExpr;
@@ -409,11 +362,6 @@ class Expr {
   /// functions (e.g. in ScalarFnCall() when codegen is disabled).
   llvm::Function* GetStaticGetValWrapper(ColumnType type, LlvmCodeGen* codegen);
 
-  /// Replace all calls to Expr::GetConstant() in 'fn' based on the types of the
-  /// expr and its children. This is a convenience method that invokes the static
-  /// InlineConstants() function with the correct arguments for the expr.
-  int InlineConstants(LlvmCodeGen* codegen, llvm::Function* fn);
-
   /// Simple debug string that provides no expr subclass-specific information
   std::string DebugString(const std::string& expr_name) const;
 
@@ -457,13 +405,6 @@ class Expr {
   static StringVal GetStringVal(Expr* expr, ExprContext* context, const TupleRow* row);
   static TimestampVal GetTimestampVal(Expr* expr, ExprContext* context, const TupleRow* row);
   static DecimalVal GetDecimalVal(Expr* expr, ExprContext* context, const TupleRow* row);
-
-
-  // Helper function for GetConstantInt() and InlineConstants(): return the constant value
-  // given the specific argument and return types.
-  static int GetConstantInt(const FunctionContext::TypeDesc& return_type,
-      const std::vector<FunctionContext::TypeDesc>& arg_types, ExprConstant c,
-      int i = -1);
 };
 
 }

http://git-wip-us.apache.org/repos/asf/incubator-impala/blob/f982c3f7/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 da8df36..6a331ba 100644
--- a/be/src/exprs/math-functions-ir.cc
+++ b/be/src/exprs/math-functions-ir.cc
@@ -433,7 +433,7 @@ template <typename T> T MathFunctions::Negative(FunctionContext* ctx, const T& v
 template <>
 DecimalVal MathFunctions::Negative(FunctionContext* ctx, const DecimalVal& val) {
   if (val.is_null) return val;
-  int type_byte_size = Expr::GetConstantInt(*ctx, Expr::RETURN_TYPE_SIZE);
+  int type_byte_size = ctx->impl()->GetConstFnAttr(FunctionContextImpl::RETURN_TYPE_SIZE);
   switch (type_byte_size) {
     case 4:
       return DecimalVal(-val.val4);
@@ -519,7 +519,7 @@ template <bool ISLEAST> DecimalVal MathFunctions::LeastGreatest(
   DCHECK_GT(num_args, 0);
   if (args[0].is_null) return DecimalVal::null();
   DecimalVal result_val = args[0];
-  int type_byte_size = Expr::GetConstantInt(*ctx, Expr::RETURN_TYPE_SIZE);
+  int type_byte_size = ctx->impl()->GetConstFnAttr(FunctionContextImpl::RETURN_TYPE_SIZE);
   for (int i = 1; i < num_args; ++i) {
     if (args[i].is_null) return DecimalVal::null();
     switch (type_byte_size) {

http://git-wip-us.apache.org/repos/asf/incubator-impala/blob/f982c3f7/be/src/exprs/scalar-fn-call.cc
----------------------------------------------------------------------
diff --git a/be/src/exprs/scalar-fn-call.cc b/be/src/exprs/scalar-fn-call.cc
index 06830b7..59aac6d 100644
--- a/be/src/exprs/scalar-fn-call.cc
+++ b/be/src/exprs/scalar-fn-call.cc
@@ -316,8 +316,8 @@ Status ScalarFnCall::GetCodegendComputeFn(LlvmCodeGen* codegen, Function** fn) {
       NumFixedArgs(), vararg_start_idx_ != -1, &udf, &cache_entry_));
   // Inline constants into the function if it has an IR body.
   if (!udf->isDeclaration()) {
-    InlineConstants(AnyValUtil::ColumnTypeToTypeDesc(type_),
-        AnyValUtil::ColumnTypesToTypeDescs(arg_types), codegen, udf);
+    codegen->InlineConstFnAttrs(AnyValUtil::ColumnTypeToTypeDesc(type_),
+        AnyValUtil::ColumnTypesToTypeDescs(arg_types), udf);
     udf = codegen->FinalizeFunction(udf);
     if (udf == NULL) {
       return Status(

http://git-wip-us.apache.org/repos/asf/incubator-impala/blob/f982c3f7/be/src/runtime/decimal-value.inline.h
----------------------------------------------------------------------
diff --git a/be/src/runtime/decimal-value.inline.h b/be/src/runtime/decimal-value.inline.h
index f886787..3a65a16 100644
--- a/be/src/runtime/decimal-value.inline.h
+++ b/be/src/runtime/decimal-value.inline.h
@@ -79,7 +79,8 @@ inline const T DecimalValue<T>::fractional_part(int scale) const {
 
 template<typename T>
 inline DecimalValue<T> DecimalValue<T>::ScaleTo(int src_scale, int dst_scale,
-    int dst_precision, bool* overflow) const { int delta_scale = src_scale - dst_scale;
+    int dst_precision, bool* overflow) const {
+  int delta_scale = src_scale - dst_scale;
   T result = value();
   T max_value = DecimalUtil::GetScaleMultiplier<T>(dst_precision);
   if (delta_scale >= 0) {

http://git-wip-us.apache.org/repos/asf/incubator-impala/blob/f982c3f7/be/src/runtime/lib-cache.cc
----------------------------------------------------------------------
diff --git a/be/src/runtime/lib-cache.cc b/be/src/runtime/lib-cache.cc
index 00c1159..9a8d873 100644
--- a/be/src/runtime/lib-cache.cc
+++ b/be/src/runtime/lib-cache.cc
@@ -400,12 +400,9 @@ Status LibCache::GetCacheEntryInternal(const string& hdfs_lib_file, LibType type
         DynamicOpen((*entry)->local_path.c_str(), &(*entry)->shared_object_handle));
   } else if (type == TYPE_IR) {
     // Load the module temporarily and populate all symbols.
-    ObjectPool pool;
-    scoped_ptr<LlvmCodeGen> codegen;
-    string module_id = filesystem::path((*entry)->local_path).stem().string();
-    RETURN_IF_ERROR(LlvmCodeGen::CreateFromFile(
-        &pool, NULL, (*entry)->local_path, module_id, &codegen));
-    codegen->GetSymbols(&(*entry)->symbols);
+    const string file = (*entry)->local_path;
+    const string module_id = filesystem::path(file).stem().string();
+    RETURN_IF_ERROR(LlvmCodeGen::GetSymbols(file, module_id, &(*entry)->symbols));
   } else {
     DCHECK_EQ(type, TYPE_JAR);
     // Nothing to do.

http://git-wip-us.apache.org/repos/asf/incubator-impala/blob/f982c3f7/be/src/runtime/runtime-state.cc
----------------------------------------------------------------------
diff --git a/be/src/runtime/runtime-state.cc b/be/src/runtime/runtime-state.cc
index de0fc97..0d7f262 100644
--- a/be/src/runtime/runtime-state.cc
+++ b/be/src/runtime/runtime-state.cc
@@ -156,7 +156,7 @@ Status RuntimeState::CreateBlockMgr() {
 Status RuntimeState::CreateCodegen() {
   if (codegen_.get() != NULL) return Status::OK();
   // TODO: add the fragment ID to the codegen ID as well
-  RETURN_IF_ERROR(LlvmCodeGen::CreateImpalaCodegen(obj_pool_.get(),
+  RETURN_IF_ERROR(LlvmCodeGen::CreateImpalaCodegen(this,
       instance_mem_tracker_.get(), PrintId(fragment_instance_id()), &codegen_));
   codegen_->EnableOptimizations(true);
   profile_.AddChild(codegen_->runtime_profile());

http://git-wip-us.apache.org/repos/asf/incubator-impala/blob/f982c3f7/be/src/runtime/runtime-state.h
----------------------------------------------------------------------
diff --git a/be/src/runtime/runtime-state.h b/be/src/runtime/runtime-state.h
index 14f9d38..97014aa 100644
--- a/be/src/runtime/runtime-state.h
+++ b/be/src/runtime/runtime-state.h
@@ -104,6 +104,7 @@ class RuntimeState {
   bool abort_on_default_limit_exceeded() const {
     return query_options().abort_on_default_limit_exceeded;
   }
+  bool decimal_v2() const { return query_options().decimal_v2; }
   const TQueryCtx& query_ctx() const;
   const TPlanFragmentInstanceCtx& instance_ctx() const { return *instance_ctx_; }
   const TUniqueId& session_id() const { return query_ctx().session.session_id; }

http://git-wip-us.apache.org/repos/asf/incubator-impala/blob/f982c3f7/be/src/service/frontend.cc
----------------------------------------------------------------------
diff --git a/be/src/service/frontend.cc b/be/src/service/frontend.cc
index b83d091..e48cb1e 100644
--- a/be/src/service/frontend.cc
+++ b/be/src/service/frontend.cc
@@ -82,7 +82,7 @@ Frontend::Frontend() {
     {"getTableFiles", "([B)[B", &get_table_files_id_},
     {"showCreateFunction", "([B)Ljava/lang/String;", &show_create_function_id_},
     {"buildTestDescriptorTable", "([B)[B", &build_test_descriptor_table_id_}
-};
+  };
 
   JNIEnv* jni_env = getJNIEnv();
   // create instance of java class JniFrontend

http://git-wip-us.apache.org/repos/asf/incubator-impala/blob/f982c3f7/be/src/udf/udf-internal.h
----------------------------------------------------------------------
diff --git a/be/src/udf/udf-internal.h b/be/src/udf/udf-internal.h
index 96d0fc7..907bb22 100644
--- a/be/src/udf/udf-internal.h
+++ b/be/src/udf/udf-internal.h
@@ -132,12 +132,53 @@ class FunctionContextImpl {
     return arg_types_;
   }
 
-  // UDFs may manipulate DecimalVal arguments via SIMD instructions such as 'movaps'
-  // that require 16-byte memory alignment.
+  RuntimeState* state() { return state_; }
+
+  /// Various static attributes of the UDF/UDA that can be injected as constants
+  /// by codegen. Note that the argument types refer to those in the UDF/UDA signature,
+  /// not the arguments of the C++ functions implementing the UDF/UDA. Any change to
+  /// this enum must be reflected in FunctionContextImpl::GetConstFnAttr().
+  enum ConstFnAttr {
+    /// RETURN_TYPE_*: properties of FunctionContext::GetReturnType()
+    RETURN_TYPE_SIZE, // int
+    RETURN_TYPE_PRECISION, // int
+    RETURN_TYPE_SCALE, // int
+    /// ARG_TYPE_* with parameter i: properties of FunctionContext::GetArgType(i)
+    ARG_TYPE_SIZE, // int[]
+    ARG_TYPE_PRECISION, // int[]
+    ARG_TYPE_SCALE, // int[]
+    /// True if decimal_v2 query option is set.
+    DECIMAL_V2,
+  };
+
+  /// This function returns the various static attributes of the UDF/UDA. Calls to this
+  /// function are replaced by constants injected by codegen. If codegen is disabled,
+  /// this function is interpreted as-is.
+  ///
+  /// 't' is the static function attribute defined in the ConstFnAttr enum above.
+  /// For function attributes of arguments, 'i' holds the argument number (0 indexed).
+  /// Please note that argument refers to the arguments in the signature of the UDF or UDA.
+  /// 'i' must always be an immediate integer value in order to utilize the constant
+  /// replacement when codegen is enabled. e.g., it cannot be a variable or an expression
+  /// like "1 + 1".
+  ///
+  int GetConstFnAttr(ConstFnAttr t, int i = -1);
+
+  /// Return the function attribute 't' defined in ConstFnAttr above.
+  static int GetConstFnAttr(const RuntimeState* state,
+      const impala_udf::FunctionContext::TypeDesc& return_type,
+      const std::vector<impala_udf::FunctionContext::TypeDesc>& arg_types,
+      ConstFnAttr t, int i = -1);
+
+  /// UDFs may manipulate DecimalVal arguments via SIMD instructions such as 'movaps'
+  /// that require 16-byte memory alignment.
   static const int VARARGS_BUFFER_ALIGNMENT = 16;
+
+  /// The LLVM class name for FunctionContext. Used for handcrafted IR.
   static const char* LLVM_FUNCTIONCONTEXT_NAME;
 
-  RuntimeState* state() { return state_; }
+  /// FunctionContextImpl::GetConstFnAttr() symbol. Used for call sites replacement.
+  static const char* GET_CONST_FN_ATTR_SYMBOL;
 
  private:
   friend class impala_udf::FunctionContext;

http://git-wip-us.apache.org/repos/asf/incubator-impala/blob/f982c3f7/be/src/udf/udf-test-harness.cc
----------------------------------------------------------------------
diff --git a/be/src/udf/udf-test-harness.cc b/be/src/udf/udf-test-harness.cc
index 1c3737d..d34b5ee 100644
--- a/be/src/udf/udf-test-harness.cc
+++ b/be/src/udf/udf-test-harness.cc
@@ -18,6 +18,7 @@
 #include "udf/udf-test-harness.h"
 
 #include <vector>
+#include "runtime/runtime-state.h"
 #include "udf/udf-internal.h"
 
 #include "common/names.h"
@@ -27,8 +28,8 @@ using namespace impala;
 
 FunctionContext* UdfTestHarness::CreateTestContext(
     const FunctionContext::TypeDesc& return_type,
-    const vector<FunctionContext::TypeDesc>& arg_types) {
-  return FunctionContextImpl::CreateContext(NULL, NULL, return_type, arg_types, 0, true);
+    const vector<FunctionContext::TypeDesc>& arg_types, RuntimeState* state) {
+  return FunctionContextImpl::CreateContext(state, NULL, return_type, arg_types, 0, true);
 }
 
 void UdfTestHarness::SetConstantArgs(

http://git-wip-us.apache.org/repos/asf/incubator-impala/blob/f982c3f7/be/src/udf/udf-test-harness.h
----------------------------------------------------------------------
diff --git a/be/src/udf/udf-test-harness.h b/be/src/udf/udf-test-harness.h
index 67ba8fc..668a44d 100644
--- a/be/src/udf/udf-test-harness.h
+++ b/be/src/udf/udf-test-harness.h
@@ -24,6 +24,7 @@
 #include <boost/function.hpp>
 #include <boost/scoped_ptr.hpp>
 
+#include "runtime/runtime-state.h"
 #include "udf/udf.h"
 #include "udf/udf-debug.h"
 
@@ -36,7 +37,8 @@ class UdfTestHarness {
   /// argument of the UDF not including the FunctionContext*. The caller is responsible
   /// for calling delete on it. This context has additional debugging validation enabled.
   static FunctionContext* CreateTestContext(const FunctionContext::TypeDesc& return_type,
-      const std::vector<FunctionContext::TypeDesc>& arg_types);
+      const std::vector<FunctionContext::TypeDesc>& arg_types,
+      impala::RuntimeState* state = nullptr);
 
   /// Use with test contexts to test use of IsArgConstant() and GetConstantArg().
   /// constant_args should contain an AnyVal* for each argument of the UDF not including

http://git-wip-us.apache.org/repos/asf/incubator-impala/blob/f982c3f7/be/src/udf/udf.cc
----------------------------------------------------------------------
diff --git a/be/src/udf/udf.cc b/be/src/udf/udf.cc
index cf1fa0f..c2a5270 100644
--- a/be/src/udf/udf.cc
+++ b/be/src/udf/udf.cc
@@ -82,7 +82,12 @@ class RuntimeState {
     assert(false);
   }
 
-  bool abort_on_error() {
+  bool abort_on_error() const {
+    assert(false);
+    return false;
+  }
+
+  bool decimal_v2() const {
     assert(false);
     return false;
   }
@@ -99,6 +104,7 @@ class RuntimeState {
 }
 
 #else
+#include "exprs/anyval-util.h"
 #include "runtime/free-pool.h"
 #include "runtime/mem-tracker.h"
 #include "runtime/runtime-state.h"
@@ -114,6 +120,8 @@ using std::pair;
 const int FunctionContextImpl::VARARGS_BUFFER_ALIGNMENT;
 const char* FunctionContextImpl::LLVM_FUNCTIONCONTEXT_NAME =
     "class.impala_udf::FunctionContext";
+const char* FunctionContextImpl::GET_CONST_FN_ATTR_SYMBOL =
+    "_ZN6impala19FunctionContextImpl14GetConstFnAttrENS0_11ConstFnAttrEi";
 
 static const int MAX_WARNINGS = 1000;
 
@@ -501,3 +509,53 @@ const FunctionContext::TypeDesc* FunctionContext::GetArgType(int arg_idx) const
   if (arg_idx < 0 || arg_idx >= impl_->arg_types_.size()) return NULL;
   return &impl_->arg_types_[arg_idx];
 }
+
+static int GetTypeByteSize(const FunctionContext::TypeDesc& type) {
+#if defined(IMPALA_UDF_SDK_BUILD) && IMPALA_UDF_SDK_BUILD
+  return 0;
+#else
+  return AnyValUtil::TypeDescToColumnType(type).GetByteSize();
+#endif
+}
+
+int FunctionContextImpl::GetConstFnAttr(FunctionContextImpl::ConstFnAttr t, int i) {
+  return GetConstFnAttr(state_, return_type_, arg_types_, t, i);
+}
+
+int FunctionContextImpl::GetConstFnAttr(const RuntimeState* state,
+    const FunctionContext::TypeDesc& return_type,
+    const vector<FunctionContext::TypeDesc>& arg_types,
+    ConstFnAttr t, int i) {
+  switch (t) {
+    case RETURN_TYPE_SIZE:
+      assert(i == -1);
+      return GetTypeByteSize(return_type);
+    case RETURN_TYPE_PRECISION:
+      assert(i == -1);
+      assert(return_type.type == FunctionContext::TYPE_DECIMAL);
+      return return_type.precision;
+    case RETURN_TYPE_SCALE:
+      assert(i == -1);
+      assert(return_type.type == FunctionContext::TYPE_DECIMAL);
+      return return_type.scale;
+    case ARG_TYPE_SIZE:
+      assert(i >= 0);
+      assert(i < arg_types.size());
+      return GetTypeByteSize(arg_types[i]);
+    case ARG_TYPE_PRECISION:
+      assert(i >= 0);
+      assert(i < arg_types.size());
+      assert(arg_types[i].type == FunctionContext::TYPE_DECIMAL);
+      return arg_types[i].precision;
+    case ARG_TYPE_SCALE:
+      assert(i >= 0);
+      assert(i < arg_types.size());
+      assert(arg_types[i].type == FunctionContext::TYPE_DECIMAL);
+      return arg_types[i].scale;
+    case DECIMAL_V2:
+      return state->decimal_v2();
+    default:
+      assert(false);
+      return -1;
+  }
+}

http://git-wip-us.apache.org/repos/asf/incubator-impala/blob/f982c3f7/testdata/workloads/functional-query/queries/QueryTest/decimal.test
----------------------------------------------------------------------
diff --git a/testdata/workloads/functional-query/queries/QueryTest/decimal.test b/testdata/workloads/functional-query/queries/QueryTest/decimal.test
index 6e7cbae..4a1316b 100644
--- a/testdata/workloads/functional-query/queries/QueryTest/decimal.test
+++ b/testdata/workloads/functional-query/queries/QueryTest/decimal.test
@@ -449,3 +449,47 @@ NULL,0.0000
 ---- TYPES
 DECIMAL, DECIMAL
 ====
+---- QUERY
+# Test casting behavior without decimal_v2 query option set.
+set decimal_v2=false;
+select cast(d3 as decimal(20, 3)) from functional.decimal_tbl;
+---- RESULTS
+1.234
+12.345
+123.456
+1234.567
+12345.678
+---- TYPES
+DECIMAL
+====
+---- QUERY
+# Test casting behavior with decimal_v2 query option set.
+set decimal_v2=true;
+select cast(d3 as decimal(20, 3)) from functional.decimal_tbl;
+---- RESULTS
+1.235
+12.346
+123.457
+1234.568
+12345.679
+---- TYPES
+DECIMAL
+====
+---- QUERY
+# Test casting behavior without decimal_v2 query option set.
+set decimal_v2=false;
+select sum(cast(d3 as DECIMAL(20,2)) + cast(d5 as DECIMAL(20,4))) from functional.decimal_tbl;
+---- RESULTS
+26078.2788
+---- TYPES
+DECIMAL
+====
+---- QUERY
+# Test casting behavior with decimal_v2 query option set.
+set decimal_v2=true;
+select sum(cast(d3 as DECIMAL(20,2)) + cast(d5 as DECIMAL(20,4))) from functional.decimal_tbl;
+---- RESULTS
+26078.3189
+---- TYPES
+DECIMAL
+====

http://git-wip-us.apache.org/repos/asf/incubator-impala/blob/f982c3f7/tests/query_test/test_decimal_casting.py
----------------------------------------------------------------------
diff --git a/tests/query_test/test_decimal_casting.py b/tests/query_test/test_decimal_casting.py
index b23e790..fac49c5 100644
--- a/tests/query_test/test_decimal_casting.py
+++ b/tests/query_test/test_decimal_casting.py
@@ -23,6 +23,7 @@ from metacomm.combinatorics.all_pairs2 import all_pairs2 as all_pairs
 from random import randint
 
 from tests.common.impala_test_suite import ImpalaTestSuite
+from tests.common.test_dimensions import create_exec_option_dimension_from_dict
 from tests.common.test_vector import ImpalaTestDimension, ImpalaTestMatrix
 
 class TestDecimalCasting(ImpalaTestSuite):
@@ -42,8 +43,8 @@ class TestDecimalCasting(ImpalaTestSuite):
       # mimics test_vectors.py and takes a subset of all decimal types
       'pairwise' : all_pairs([(p, s) for p in xrange(1, 39) for s in xrange(0, p + 1)])
   }
-  # We can cast for numerrics or string types.
-  CAST_FROM = ['string', 'number']
+  # We can cast for numerics, string or decimal types.
+  CAST_FROM = ['string', 'number', 'decimal']
   # Set the default precisin to 38 to operate on decimal values.
   getcontext().prec = 38
   # Represents a 0 in decimal
@@ -60,6 +61,8 @@ class TestDecimalCasting(ImpalaTestSuite):
         *TestDecimalCasting.DECIMAL_TYPES_MAP[cls.exploration_strategy()]))
     cls.ImpalaTestMatrix.add_dimension(
         ImpalaTestDimension('cast_from', *TestDecimalCasting.CAST_FROM))
+    cls.ImpalaTestMatrix.add_dimension(create_exec_option_dimension_from_dict(
+        {'decimal_v2': ['false']}))
     cls.iterations = 1
 
   def setup_method(self, method):
@@ -79,15 +82,21 @@ class TestDecimalCasting(ImpalaTestSuite):
     assert actual == expected, "Cast: {0}, Expected: {1}, Actual: {2}".format(cast,\
         expected, actual)
 
-  def _normalize_cast_expr(self, decimal_val, scale, from_string=False):
+  def _normalize_cast_expr(self, decimal_val, scale, cast_from):
     """Convert the decimal value to a string litetal to avoid overflow.
 
     If an integer literal is greater than the max bigint supported by Impala, it
     overflows. This methods replaces it with a string literal.
     """
-    if (scale == 0 and abs(decimal_val) > self.max_bigint) or from_string:
-      return "select cast('{0}' as Decimal({1}, {2}))"
-    return "select cast({0} as Decimal({1}, {2}))"
+    # Decimal({1},{2}) is the target type to cast to. If casting from decimal,
+    # Decimal({3},{4}) is the intermediate type to cast {0} to.
+    if (scale == 0 and abs(decimal_val) > self.max_bigint) or (cast_from == 'string'):
+      return "select cast('{0}' as Decimal({1},{2}))"
+    elif cast_from == 'decimal':
+      base_cast = "cast('{0}' as Decimal({3},{4}))"
+      return "select cast(" + base_cast + " as Decimal({1},{2}))"
+    else:
+      return "select cast({0} as Decimal({1},{2}))"
 
   def test_min_max_zero_null(self, vector):
     """Sanity test at limits.
@@ -98,20 +107,21 @@ class TestDecimalCasting(ImpalaTestSuite):
       - NULL is expressible in all decimal types
     """
     precision, scale = vector.get_value('decimal_type')
-    from_string = vector.get_value('cast_from') == 'string'
     dec_max = Decimal('{0}.{1}'.format('9' * (precision - scale), '9' * scale))
     # Multiplying large values eith -1 can produce an overflow.
     dec_min = Decimal('-{0}'.format(str(dec_max)))
-    cast = self._normalize_cast_expr(dec_max, scale, from_string=from_string)
+    cast = self._normalize_cast_expr(dec_max, scale, vector.get_value('cast_from'))
     # Test max
-    res = Decimal(self.execute_scalar(cast.format(dec_max, precision, scale)))
+    res = Decimal(self.execute_scalar(\
+        cast.format(dec_max, precision, scale, precision, scale)))
     self._assert_decimal_result(cast, res, dec_max)
     # Test Min
-    res = Decimal(self.execute_scalar(cast.format(dec_min, precision, scale)))
+    res = Decimal(self.execute_scalar(\
+        cast.format(dec_min, precision, scale, precision, scale)))
     self._assert_decimal_result(cast, res, dec_min)
     # Test zero
-    res = Decimal(self.execute_scalar(cast.format(TestDecimalCasting.DECIMAL_ZERO,
-      precision, scale)))
+    res = Decimal(self.execute_scalar(\
+        cast.format(TestDecimalCasting.DECIMAL_ZERO, precision, scale, precision, scale)))
     self._assert_decimal_result(cast, res, TestDecimalCasting.DECIMAL_ZERO)
     # Test NULL
     null_cast = "select cast(NULL as Decimal({0}, {1}))".format(precision, scale)
@@ -122,11 +132,12 @@ class TestDecimalCasting(ImpalaTestSuite):
     """Test to verify that an exact representation of the desired Decimal type is
     maintained."""
     precision, scale = vector.get_value('decimal_type')
-    from_string = vector.get_value('cast_from') == 'string'
+    if vector.get_value('cast_from') == 'decimal':
+      pytest.skip("Casting between the same decimal type isn't interesting")
     for i in xrange(self.iterations):
       val = self._gen_decimal_val(precision, scale)
-      cast = self._normalize_cast_expr(val, scale, from_string=from_string)\
-          .format(val, precision, scale)
+      cast = self._normalize_cast_expr(val, scale, vector.get_value('cast_from'))\
+          .format(val, precision, scale, precision, scale)
       res = Decimal(self.execute_scalar(cast))
       self._assert_decimal_result(cast, res, val)
 
@@ -134,12 +145,12 @@ class TestDecimalCasting(ImpalaTestSuite):
     """Test to verify that we always return NULL when trying to cast a number with greater
     precision that its intended decimal type"""
     precision, scale = vector.get_value('decimal_type')
-    from_string = vector.get_value('cast_from') == 'string'
     for i in xrange(self.iterations):
       # Generate a decimal with a larger precision than the one we're casting to.
-      val = self._gen_decimal_val(randint(precision + 1, 39), scale)
-      cast = self._normalize_cast_expr(val, scale, from_string=from_string)\
-          .format(val, precision, scale)
+      from_precision = randint(precision + 1, 39)
+      val = self._gen_decimal_val(from_precision, scale)
+      cast = self._normalize_cast_expr(val, scale, vector.get_value('cast_from'))\
+          .format(val, precision, scale, min(38, from_precision), scale)
       res = self.execute_scalar(cast)
       self._assert_decimal_result(cast, res, 'NULL')
 
@@ -148,16 +159,17 @@ class TestDecimalCasting(ImpalaTestSuite):
     than the target decimal type (with no change in precision).
     """
     precision, scale = vector.get_value('decimal_type')
-    from_string = vector.get_value('cast_from') == 'string'
+    is_decimal_v2 = vector.get_value('exec_option')['decimal_v2'] == 'true'
     if precision == scale:
       pytest.skip("Cannot underflow scale when precision and scale are equal")
     for i in xrange(self.iterations):
-      new_scale = randint(scale + 1, precision)
-      val = self._gen_decimal_val(precision, randint(new_scale, precision))
-      # We don't need to normalize the cast expr because scale will never be zero
-      cast = self._normalize_cast_expr(val, scale, from_string=from_string)\
-          .format(val, precision, scale)
-      res = Decimal(self.execute_scalar(cast))
-      # Truncate the decimal value to its target scale with quantize.
-      self._assert_decimal_result(cast, res, val.quantize(Decimal('0e-%s' % scale),
-        rounding=ROUND_DOWN))
+      from_scale = randint(scale + 1, precision)
+      val = self._gen_decimal_val(precision, from_scale)
+      cast = self._normalize_cast_expr(val, scale, vector.get_value('cast_from'))\
+          .format(val, precision, scale, precision, from_scale)
+      res = Decimal(self.execute_scalar(cast, vector.get_value('exec_option')))
+      if is_decimal_v2:
+        expected_val = val.quantize(Decimal('0e-%s' % scale))
+      else:
+        expected_val = val.quantize(Decimal('0e-%s' % scale), rounding=ROUND_DOWN)
+      self._assert_decimal_result(cast, res, expected_val)