IMPALA-7473: fix crash when printing decimal with -v=3

The bug is that gcc emitted an aligned load instruction to
the int128 value_, and we didn't exercise that code path
in our testing. The fix is to use memcpy() to force an
unaligned load.

Also fix a NullPointerException in the frontend encountered
when running some tests with -v=3.

Testing:
Added a unit test that crashed when run with a release build.

Change-Id: I772681b78cda27d6c2086b91151ca8bdcd901828
Reviewed-on: http://gerrit.cloudera.org:8080/12029
Reviewed-by: Impala Public Jenkins <impala-public-jenk...@cloudera.com>
Tested-by: Impala Public Jenkins <impala-public-jenk...@cloudera.com>


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

Branch: refs/heads/master
Commit: 9af2fdc44a06d6309b21cbca1cbc6a3f383c9b81
Parents: 0164fa7
Author: Tim Armstrong <tarmstr...@cloudera.com>
Authored: Thu Nov 29 17:46:52 2018 -0800
Committer: Impala Public Jenkins <impala-public-jenk...@cloudera.com>
Committed: Thu Dec 6 05:42:20 2018 +0000

----------------------------------------------------------------------
 be/src/runtime/decimal-test.cc                  | 20 ++++++++++++++++++++
 be/src/runtime/decimal-value.inline.h           | 14 +++++++++-----
 .../org/apache/impala/planner/PlanNode.java     | 12 ++++++++----
 3 files changed, 37 insertions(+), 9 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/impala/blob/9af2fdc4/be/src/runtime/decimal-test.cc
----------------------------------------------------------------------
diff --git a/be/src/runtime/decimal-test.cc b/be/src/runtime/decimal-test.cc
index 25d2f65..ac9fea7 100644
--- a/be/src/runtime/decimal-test.cc
+++ b/be/src/runtime/decimal-test.cc
@@ -19,9 +19,11 @@
 #include <stdio.h>
 #include <iostream>
 #include <limits>
+#include <sstream>
 #include <boost/cstdint.hpp>
 #include <boost/lexical_cast.hpp>
 #include "runtime/decimal-value.inline.h"
+#include "runtime/raw-value.h"
 #include "runtime/types.h"
 #include "testutil/gtest-util.h"
 #include "util/string-parser.h"
@@ -708,6 +710,24 @@ TEST(DecimalTest, MultiplyScaleOverflow) {
   EXPECT_FALSE(overflow);
 }
 
+// Test that unaligned decimal values are handled correctly.
+TEST(DecimalTest, UnalignedValues) {
+  // Regression test for IMPALA-7473 that triggered a crash in release builds.
+  Decimal16Value aligned(12345);
+  uint8_t* unaligned_mem = 
reinterpret_cast<uint8_t*>(malloc(sizeof(Decimal16Value) + 9));
+  memcpy(&unaligned_mem[9], &aligned, sizeof(aligned));
+  Decimal16Value* unaligned = 
reinterpret_cast<Decimal16Value*>(&unaligned_mem[9]);
+  // VerifyToString() worked even prior to the bugfix because GCC happened to 
generate
+  // code without aligned load instructions.
+  VerifyToString(*unaligned, 28, 2, "123.45");
+  // PrintValue() contained different generated code that wasn't safe for 
unaligned
+  // values.
+  stringstream ss;
+  RawValue::PrintValue(unaligned, ColumnType::CreateDecimalType(28, 2), 0, 
&ss);
+  EXPECT_EQ("123.45", ss.str());
+  free(unaligned_mem);
+}
+
 enum Op {
   ADD,
   SUBTRACT,

http://git-wip-us.apache.org/repos/asf/impala/blob/9af2fdc4/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 cb889e6..29d0159 100644
--- a/be/src/runtime/decimal-value.inline.h
+++ b/be/src/runtime/decimal-value.inline.h
@@ -674,21 +674,25 @@ inline std::string DecimalValue<T>::ToString(const 
ColumnType& type) const {
 
 template<typename T>
 inline std::string DecimalValue<T>::ToString(int precision, int scale) const {
+  T value;
+  // 'value_' may be unaligned. Use memcpy to avoid emitting instructions that 
assume
+  // alignment - see IMPALA-7473.
+  memcpy(&value, &value_, sizeof(T));
   // Decimal values are sent to clients as strings so in the interest of
   // speed the string will be created without the using stringstream with the
   // whole/fractional_part().
   int last_char_idx = precision
       + (scale > 0)   // Add a space for decimal place
       + (scale == precision)   // Add a space for leading 0
-      + (value_ < 0);   // Add a space for negative sign
+      + (value < 0);   // Add a space for negative sign
   std::string str = std::string(last_char_idx, '0');
   // Start filling in the values in reverse order by taking the last digit
   // of the value. Use a positive value and worry about the sign later. At this
   // point the last_char_idx points to the string terminator.
-  T remaining_value = value_;
+  T remaining_value = value;
   int first_digit_idx = 0;
-  if (value_ < 0) {
-    remaining_value = -value_;
+  if (value < 0) {
+    remaining_value = -value;
     first_digit_idx = 1;
   }
   if (scale > 0) {
@@ -710,7 +714,7 @@ inline std::string DecimalValue<T>::ToString(int precision, 
int scale) const {
     }
     // For safety, enforce string length independent of remaining_value.
   } while (last_char_idx > first_digit_idx);
-  if (value_ < 0) str[0] = '-';
+  if (value < 0) str[0] = '-';
   return str;
 }
 

http://git-wip-us.apache.org/repos/asf/impala/blob/9af2fdc4/fe/src/main/java/org/apache/impala/planner/PlanNode.java
----------------------------------------------------------------------
diff --git a/fe/src/main/java/org/apache/impala/planner/PlanNode.java 
b/fe/src/main/java/org/apache/impala/planner/PlanNode.java
index 208c49b..57d3bf4 100644
--- a/fe/src/main/java/org/apache/impala/planner/PlanNode.java
+++ b/fe/src/main/java/org/apache/impala/planner/PlanNode.java
@@ -341,11 +341,15 @@ abstract public class PlanNode extends TreeNode<PlanNode> 
{
     if (detailLevel.ordinal() >= TExplainLevel.EXTENDED.ordinal()) {
       expBuilder.append(detailPrefix);
       expBuilder.append("in pipelines: ");
-      List<String> pipelines = Lists.newArrayList();
-      for (PipelineMembership pipe: pipelines_) {
-        pipelines.add(pipe.getExplainString());
+      if (pipelines_ != null) {
+        List<String> pipelines = Lists.newArrayList();
+        for (PipelineMembership pipe: pipelines_) {
+          pipelines.add(pipe.getExplainString());
+        }
+        expBuilder.append(Joiner.on(", ").join(pipelines) + "\n");
+      } else {
+        expBuilder.append("<not computed>");
       }
-      expBuilder.append(Joiner.on(", ").join(pipelines) + "\n");
     }
 
 

Reply via email to