IMPALA-197: Outer join on constant expressions returns incorrect results.

This commit is contained in:
Alex Behm
2013-04-09 19:05:09 -07:00
committed by Henry Robinson
parent c9040aee22
commit 861ba05989
21 changed files with 442 additions and 61 deletions

1
.gitignore vendored
View File

@@ -6,6 +6,7 @@ thirdparty
cscope.files
cscope.out
org.eclipse.jdt.core.prefs
org.eclipse.jdt.ui.prefs
*benchmark_results.csv*
load-trevni-*-generated.sh
load-*-generated.sql

View File

@@ -37,7 +37,7 @@ add_library(Exprs
is-null-predicate.cc
like-predicate.cc
math-functions.cc
null-literal.cc
null-literal.cc
opcode-registry.cc
slot-ref.cc
string-literal.cc
@@ -45,6 +45,7 @@ add_library(Exprs
timestamp-functions.cc
timestamp-literal.cc
timezone_db.cc
tuple-is-null-predicate.cc
utility-functions.cc
)

View File

@@ -36,7 +36,52 @@ void* ConditionalFunctions::IfBool(Expr* e, TupleRow* row) {
return &e->result_.bool_val;
}
void* ConditionalFunctions::IfTinyint(Expr* e, TupleRow* row) {
DCHECK_EQ(e->GetNumChildren(), 3);
bool* cond = reinterpret_cast<bool*>(e->children()[0]->GetValue(row));
if (cond == NULL || !*cond) {
int8_t* else_val = reinterpret_cast<int8_t*>(e->children()[2]->GetValue(row));
if (else_val == NULL) return NULL;
e->result_.tinyint_val = *else_val;
} else {
int8_t* then_val = reinterpret_cast<int8_t*>(e->children()[1]->GetValue(row));
if (then_val == NULL) return NULL;
e->result_.tinyint_val = *then_val;
}
return &e->result_.tinyint_val;
}
void* ConditionalFunctions::IfSmallint(Expr* e, TupleRow* row) {
DCHECK_EQ(e->GetNumChildren(), 3);
bool* cond = reinterpret_cast<bool*>(e->children()[0]->GetValue(row));
if (cond == NULL || !*cond) {
int16_t* else_val = reinterpret_cast<int16_t*>(e->children()[2]->GetValue(row));
if (else_val == NULL) return NULL;
e->result_.smallint_val = *else_val;
} else {
int16_t* then_val = reinterpret_cast<int16_t*>(e->children()[1]->GetValue(row));
if (then_val == NULL) return NULL;
e->result_.smallint_val = *then_val;
}
return &e->result_.smallint_val;
}
void* ConditionalFunctions::IfInt(Expr* e, TupleRow* row) {
DCHECK_EQ(e->GetNumChildren(), 3);
bool* cond = reinterpret_cast<bool*>(e->children()[0]->GetValue(row));
if (cond == NULL || !*cond) {
int32_t* else_val = reinterpret_cast<int32_t*>(e->children()[2]->GetValue(row));
if (else_val == NULL) return NULL;
e->result_.int_val = *else_val;
} else {
int32_t* then_val = reinterpret_cast<int32_t*>(e->children()[1]->GetValue(row));
if (then_val == NULL) return NULL;
e->result_.int_val = *then_val;
}
return &e->result_.int_val;
}
void* ConditionalFunctions::IfBigint(Expr* e, TupleRow* row) {
DCHECK_EQ(e->GetNumChildren(), 3);
bool* cond = reinterpret_cast<bool*>(e->children()[0]->GetValue(row));
if (cond == NULL || !*cond) {
@@ -52,6 +97,21 @@ void* ConditionalFunctions::IfInt(Expr* e, TupleRow* row) {
}
void* ConditionalFunctions::IfFloat(Expr* e, TupleRow* row) {
DCHECK_EQ(e->GetNumChildren(), 3);
bool* cond = reinterpret_cast<bool*>(e->children()[0]->GetValue(row));
if (cond == NULL || !*cond) {
float* else_val = reinterpret_cast<float*>(e->children()[2]->GetValue(row));
if (else_val == NULL) return NULL;
e->result_.float_val = *else_val;
} else {
float* then_val = reinterpret_cast<float*>(e->children()[1]->GetValue(row));
if (then_val == NULL) return NULL;
e->result_.float_val = *then_val;
}
return &e->result_.float_val;
}
void* ConditionalFunctions::IfDouble(Expr* e, TupleRow* row) {
DCHECK_EQ(e->GetNumChildren(), 3);
bool* cond = reinterpret_cast<bool*>(e->children()[0]->GetValue(row));
if (cond == NULL || !*cond) {

View File

@@ -26,8 +26,12 @@ class TupleRow;
class ConditionalFunctions {
public:
static void* IfBool(Expr* e, TupleRow* row);
static void* IfSmallint(Expr* e, TupleRow* row);
static void* IfTinyint(Expr* e, TupleRow* row);
static void* IfInt(Expr* e, TupleRow* row);
static void* IfBigint(Expr* e, TupleRow* row);
static void* IfFloat(Expr* e, TupleRow* row);
static void* IfDouble(Expr* e, TupleRow* row);
static void* IfString(Expr* e, TupleRow* row);
static void* IfTimestamp(Expr* e, TupleRow* row);
static void* CoalesceBool(Expr* e, TupleRow* row);

View File

@@ -2222,8 +2222,8 @@ TEST_F(ExprTest, ConditionalFunctions) {
// false or NULL should return the third.
TestValue("if(TRUE, FALSE, TRUE)", TYPE_BOOLEAN, false);
TestValue("if(FALSE, FALSE, TRUE)", TYPE_BOOLEAN, true);
TestValue("if(TRUE, 10, 20)", TYPE_BIGINT, 10);
TestValue("if(FALSE, 10, 20)", TYPE_BIGINT, 20);
TestValue("if(TRUE, 10, 20)", TYPE_TINYINT, 10);
TestValue("if(FALSE, 10, 20)", TYPE_TINYINT, 20);
TestValue("if(TRUE, 5.5, 8.8)", TYPE_DOUBLE, 5.5);
TestValue("if(FALSE, 5.5, 8.8)", TYPE_DOUBLE, 8.8);
TestStringValue("if(TRUE, 'abc', 'defgh')", "abc");

View File

@@ -36,6 +36,7 @@
#include "exprs/opcode-registry.h"
#include "exprs/string-literal.h"
#include "exprs/timestamp-literal.h"
#include "exprs/tuple-is-null-predicate.h"
#include "gen-cpp/Exprs_types.h"
#include "gen-cpp/Data_types.h"
#include "runtime/runtime-state.h"
@@ -350,6 +351,10 @@ Status Expr::CreateExpr(ObjectPool* pool, const TExprNode& texpr_node, Expr** ex
*expr = pool->Add(new StringLiteral(texpr_node));
return Status::OK;
}
case TExprNodeType::TUPLE_IS_NULL_PRED: {
*expr = pool->Add(new TupleIsNullPredicate(texpr_node));
return Status::OK;
}
default:
stringstream os;
os << "Unknown expr node type: " << texpr_node.node_type;

View File

@@ -0,0 +1,67 @@
// Copyright 2012 Cloudera Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "exprs/tuple-is-null-predicate.h"
#include <sstream>
#include "gen-cpp/Exprs_types.h"
using namespace std;
namespace impala {
void* TupleIsNullPredicate::ComputeFn(Expr* e, TupleRow* row) {
TupleIsNullPredicate* p = static_cast<TupleIsNullPredicate*>(e);
// Return true if all of the tuples in tuple_idxs_ are NULL.
p->result_.bool_val = true;
for (int i = 0; i < p->tuple_idxs_.size(); ++i) {
if (row->GetTuple(p->tuple_idxs_[i]) != NULL) {
p->result_.bool_val = false;
break;
}
}
return &p->result_.bool_val;
}
TupleIsNullPredicate::TupleIsNullPredicate(const TExprNode& node)
: Predicate(node),
tuple_ids_(node.tuple_is_null_pred.tuple_ids.begin(),
node.tuple_is_null_pred.tuple_ids.end()) {
}
Status TupleIsNullPredicate::Prepare(RuntimeState* state, const RowDescriptor& row_desc) {
RETURN_IF_ERROR(Expr::PrepareChildren(state, row_desc));
DCHECK_EQ(0, children_.size());
// Resolve tuple ids to tuple indexes.
for (int i = 0; i < tuple_ids_.size(); ++i) {
int32_t tuple_idx = row_desc.GetTupleIdx(tuple_ids_[i]);
DCHECK(row_desc.TupleIsNullable(tuple_idx));
tuple_idxs_.push_back(tuple_idx);
}
compute_fn_ = ComputeFn;
return Status::OK;
}
string TupleIsNullPredicate::DebugString() const {
stringstream out;
out << "TupleIsNullPredicate(tupleids=[";
for (int i = 0; i < tuple_ids_.size(); ++i) {
out << (i == 0 ? "" : " ") << tuple_ids_[i];
}
out << "])";
return out.str();
}
}

View File

@@ -0,0 +1,42 @@
// Copyright 2012 Cloudera Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#ifndef IMPALA_EXPRS_TUPLE_IS_NULL_PREDICATE_H_
#define IMPALA_EXPRS_TUPLE_IS_NULL_PREDICATE_H_
#include "exprs/predicate.h"
namespace impala {
class TExprNode;
class TupleIsNullPredicate: public Predicate {
protected:
friend class Expr;
TupleIsNullPredicate(const TExprNode& node);
virtual Status Prepare(RuntimeState* state, const RowDescriptor& row_desc);
virtual std::string DebugString() const;
private:
static void* ComputeFn(Expr* e, TupleRow* row);
std::vector<TupleId> tuple_ids_;
std::vector<int32_t> tuple_idxs_;
};
}
#endif

View File

@@ -199,10 +199,18 @@ functions = [
# Conditional Functions
['Conditional_If', 'BOOLEAN', ['BOOLEAN', 'BOOLEAN', 'BOOLEAN'], \
'ConditionalFunctions::IfBool', ['if']],
['Conditional_If', 'BIGINT', ['BOOLEAN', 'BIGINT', 'BIGINT'], \
['Conditional_If', 'TINYINT', ['BOOLEAN', 'TINYINT', 'TINYINT'], \
'ConditionalFunctions::IfTinyint', ['if']],
['Conditional_If', 'SMALLINT', ['BOOLEAN', 'SMALLINT', 'SMALLINT'], \
'ConditionalFunctions::IfSmallint', ['if']],
['Conditional_If', 'INT', ['BOOLEAN', 'INT', 'INT'], \
'ConditionalFunctions::IfInt', ['if']],
['Conditional_If', 'DOUBLE', ['BOOLEAN', 'DOUBLE', 'DOUBLE'], \
['Conditional_If', 'BIGINT', ['BOOLEAN', 'BIGINT', 'BIGINT'], \
'ConditionalFunctions::IfBigint', ['if']],
['Conditional_If', 'FLOAT', ['BOOLEAN', 'FLOAT', 'FLOAT'], \
'ConditionalFunctions::IfFloat', ['if']],
['Conditional_If', 'DOUBLE', ['BOOLEAN', 'DOUBLE', 'DOUBLE'], \
'ConditionalFunctions::IfDouble', ['if']],
['Conditional_If', 'STRING', ['BOOLEAN', 'STRING', 'STRING'], \
'ConditionalFunctions::IfString', ['if']],
['Conditional_If', 'TIMESTAMP', ['BOOLEAN', 'TIMESTAMP', 'TIMESTAMP'], \

View File

@@ -37,6 +37,7 @@ enum TExprNodeType {
NULL_LITERAL,
SLOT_REF,
STRING_LITERAL,
TUPLE_IS_NULL_PRED
}
enum TAggregationOp {
@@ -95,6 +96,10 @@ struct TLiteralPredicate {
2: required bool is_null
}
struct TTupleIsNullPredicate {
1: required list<Types.TTupleId> tuple_ids
}
struct TSlotRef {
1: required Types.TSlotId slot_id
}
@@ -122,6 +127,7 @@ struct TExprNode {
14: optional TLiteralPredicate literal_pred
15: optional TSlotRef slot_ref
16: optional TStringLiteral string_literal
17: optional TTupleIsNullPredicate tuple_is_null_pred
}
// A flattened representation of a tree of Expr nodes, obtained by depth-first

View File

@@ -15,8 +15,6 @@
package com.cloudera.impala.analysis;
import java.io.StringReader;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import com.cloudera.impala.catalog.Catalog;
import com.cloudera.impala.common.AnalysisException;
@@ -42,7 +40,7 @@ public class AnalysisContext {
this.catalog = catalog;
this.defaultDatabase = defaultDb;
this.user = user;
this.queryGlobals = createQueryGlobals();
this.queryGlobals = Analyzer.createQueryGlobals();
}
static public class AnalysisResult {
@@ -206,17 +204,4 @@ public class AnalysisContext {
public TQueryGlobals getQueryGlobals() { return queryGlobals; }
/**
* Create query global parameters to be set in each TPlanExecRequest.
*/
private TQueryGlobals createQueryGlobals() {
SimpleDateFormat formatter =
new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSSSSSSSS");
TQueryGlobals queryGlobals = new TQueryGlobals();
Calendar currentDate = Calendar.getInstance();
String nowStr = formatter.format(currentDate.getTime());
queryGlobals.setNow_string(nowStr);
return queryGlobals;
}
}

View File

@@ -14,7 +14,9 @@
package com.cloudera.impala.analysis;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Collections;
import java.util.IdentityHashMap;
import java.util.List;
@@ -112,7 +114,7 @@ public class Analyzer {
*/
public Analyzer(Catalog catalog) {
this(catalog, Catalog.DEFAULT_DB, System.getProperty("user.name"),
new TQueryGlobals());
createQueryGlobals());
}
public Analyzer(Catalog catalog, String defaultDb, String user,
@@ -635,4 +637,17 @@ public class Analyzer {
public TQueryGlobals getQueryGlobals() {
return queryGlobals;
}
/**
* Create query global parameters to be set in each TPlanExecRequest.
*/
public static TQueryGlobals createQueryGlobals() {
SimpleDateFormat formatter =
new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSSSSSSSS");
TQueryGlobals queryGlobals = new TQueryGlobals();
Calendar currentDate = Calendar.getInstance();
String nowStr = formatter.format(currentDate.getTime());
queryGlobals.setNow_string(nowStr);
return queryGlobals;
}
}

View File

@@ -17,6 +17,7 @@ package com.cloudera.impala.analysis;
import java.util.List;
import com.cloudera.impala.common.AnalysisException;
import com.cloudera.impala.common.InternalException;
import com.google.common.base.Preconditions;
/**
@@ -47,7 +48,7 @@ public class BaseTableRef extends TableRef {
* Register this table ref and then analyze the Join clause.
*/
@Override
public void analyze(Analyzer analyzer) throws AnalysisException {
public void analyze(Analyzer analyzer) throws AnalysisException, InternalException {
desc = analyzer.registerBaseTableRef(this);
isAnalyzed = true; // true that we have assigned desc
analyzeJoin(analyzer);

View File

@@ -22,6 +22,7 @@ import org.slf4j.LoggerFactory;
import com.cloudera.impala.common.AnalysisException;
import com.cloudera.impala.common.InternalException;
import com.cloudera.impala.service.FeSupport;
import com.google.common.base.Preconditions;
import com.google.common.collect.Lists;
@@ -46,6 +47,8 @@ public class InlineViewRef extends TableRef {
// maps of all enclosed inlined views; in other words, all SlotRefs
// contained in rhs exprs reference base tables, not contained inline views
// (and therefore can be evaluated at runtime).
// Some rhs exprs are wrapped into IF(TupleIsNull(), NULL, expr) by calling
// makeOutputNullable() if this inline view is a nullable side of an outer join.
private final Expr.SubstitutionMap sMap = new Expr.SubstitutionMap();
/**
@@ -84,18 +87,80 @@ public class InlineViewRef extends TableRef {
materializedTupleIds.add(desc.getId());
}
// Now do the remaining join analysis
analyzeJoin(analyzer);
// create sMap
for (int i = 0; i < queryStmt.getColLabels().size(); ++i) {
String colName = queryStmt.getColLabels().get(i);
SlotDescriptor slotD = analyzer.registerColumnRef(getAliasAsName(), colName);
Expr colExpr = queryStmt.getResultExprs().get(i);
sMap.lhs.add(new SlotRef(slotD));
SlotRef slotRef = new SlotRef(slotD);
sMap.lhs.add(slotRef);
sMap.rhs.add(colExpr);
}
LOG.debug("inline view smap: " + sMap.debugString());
// Now do the remaining join analysis
analyzeJoin(analyzer);
}
/**
* Makes each rhs expr in sMap nullable if necessary by wrapping as follows:
* IF(TupleIsNull(), NULL, rhs expr)
* Should be called only if this inline view is a nullable side of an outer join.
*
* We need to make an rhs exprs nullable if it evaluates to a non-NULL value
* when all of its contained SlotRefs evaluate to NULL.
* For example, constant exprs need to be wrapped or an expr such as
* 'case slotref is null then 1 else 2 end'
*/
protected void makeOutputNullable(Analyzer analyzer)
throws AnalysisException, InternalException {
// Gather all unique rhs SlotRefs into rhsSlotRefs
List<SlotRef> rhsSlotRefs = Lists.newArrayList();
Expr.collectList(sMap.rhs, SlotRef.class, rhsSlotRefs);
// Map for substituting SlotRefs with NullLiterals.
Expr.SubstitutionMap nullSMap = new Expr.SubstitutionMap();
for (SlotRef rhsSlotRef: rhsSlotRefs) {
nullSMap.lhs.add(rhsSlotRef.clone());
nullSMap.rhs.add(new NullLiteral());
}
// Make rhs exprs nullable if necessary.
for (int i = 0; i < sMap.rhs.size(); ++i) {
List<Expr> params = Lists.newArrayList();
if (!requiresNullWrapping(analyzer, sMap.rhs.get(i), nullSMap)) continue;
params.add(new TupleIsNullPredicate(materializedTupleIds));
params.add(new NullLiteral());
params.add(sMap.rhs.get(i));
Expr ifExpr = new FunctionCallExpr("if", params);
ifExpr.analyze(analyzer);
sMap.rhs.set(i, ifExpr);
}
}
/**
* Replaces all SloRefs in expr with a NullLiteral using nullSMap, and evaluates the
* resulting constant expr. Returns true if the constant expr yields a non-NULL value,
* false otherwise.
*/
private boolean requiresNullWrapping(Analyzer analyzer, Expr expr,
Expr.SubstitutionMap nullSMap) throws InternalException {
// If the expr is already wrapped in an IF(TupleIsNull(), NULL, expr)
// then do not try to execute it.
if (expr.contains(TupleIsNullPredicate.class)) return true;
// Replace all SlotRefs in expr with NullLiterals, and wrap the result
// into an IS NOT NULL predicate.
Expr isNotNullLiteralPred = new IsNullPredicate(expr.clone(nullSMap), true);
Preconditions.checkState(isNotNullLiteralPred.isConstant());
// analyze to insert casts, etc.
try {
isNotNullLiteralPred.analyze(analyzer);
} catch (AnalysisException e) {
// this should never happen
throw new InternalException(
"couldn't analyze predicate " + isNotNullLiteralPred.toSql(), e);
}
return FeSupport.EvalPredicate(isNotNullLiteralPred, analyzer.getQueryGlobals());
}
@Override
@@ -114,7 +179,7 @@ public class InlineViewRef extends TableRef {
return inlineViewAnalyzer;
}
public Expr.SubstitutionMap getSelectListExprSMap() {
public Expr.SubstitutionMap getExprSMap() {
Preconditions.checkState(isAnalyzed);
return sMap;
}

View File

@@ -175,7 +175,7 @@ public class SelectStmt extends QueryStmt {
for (TableRef tblRef: tableRefs) {
if (tblRef instanceof InlineViewRef) {
InlineViewRef inlineViewRef = (InlineViewRef) tblRef;
sMap = Expr.SubstitutionMap.combine(sMap, inlineViewRef.getSelectListExprSMap());
sMap = Expr.SubstitutionMap.combine(sMap, inlineViewRef.getExprSMap());
}
}

View File

@@ -19,6 +19,7 @@ import java.util.List;
import com.cloudera.impala.catalog.Table;
import com.cloudera.impala.common.AnalysisException;
import com.cloudera.impala.common.InternalException;
import com.google.common.base.Joiner;
import com.google.common.base.Preconditions;
import com.google.common.collect.Lists;
@@ -145,7 +146,8 @@ public abstract class TableRef extends ParseNodeBase {
* The join clause can only be analyzed after the left table has been analyzed
* and the TupleDescriptor (desc) of this table has been created.
*/
public void analyzeJoin(Analyzer analyzer) throws AnalysisException {
public void analyzeJoin(Analyzer analyzer)
throws AnalysisException, InternalException {
Preconditions.checkState(desc != null);
analyzeJoinHints();
@@ -219,6 +221,13 @@ public abstract class TableRef extends ParseNodeBase {
throw new AnalysisException(joinOpToSql() + " requires an ON or USING clause.");
}
// Make constant expressions from inline view refs nullable in its substitution map.
if (lhsIsNullable && leftTblRef instanceof InlineViewRef) {
((InlineViewRef) leftTblRef).makeOutputNullable(analyzer);
}
if (rhsIsNullable && this instanceof InlineViewRef) {
((InlineViewRef) this).makeOutputNullable(analyzer);
}
}
private String joinOpToSql() {

View File

@@ -0,0 +1,50 @@
// Copyright 2012 Cloudera Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package com.cloudera.impala.analysis;
import java.util.List;
import com.cloudera.impala.thrift.TExprNode;
import com.cloudera.impala.thrift.TExprNodeType;
import com.cloudera.impala.thrift.TTupleIsNullPredicate;
import com.google.common.base.Preconditions;
/**
* Internal expr that returns true if all of the given tuples are NULL, otherwise false.
* Used to make exprs originating from an inline view nullable in an outer join.
* The given tupleIds must be materialized and nullable at the appropriate PlanNode.
*/
public class TupleIsNullPredicate extends Predicate {
private final List<TupleId> tupleIds;
public TupleIsNullPredicate(List<TupleId> tupleIds) {
Preconditions.checkState(tupleIds != null && !tupleIds.isEmpty());
this.tupleIds = tupleIds;
}
@Override
protected void toThrift(TExprNode msg) {
msg.node_type = TExprNodeType.TUPLE_IS_NULL_PRED;
msg.tuple_is_null_pred = new TTupleIsNullPredicate();
for (TupleId tid: tupleIds) {
msg.tuple_is_null_pred.addToTuple_ids(tid.asInt());
}
}
public List<TupleId> getTupleIds() {
return tupleIds;
}
}

View File

@@ -16,9 +16,6 @@ package com.cloudera.impala.planner;
import java.util.List;
import org.apache.thrift.TException;
import org.apache.thrift.TSerializer;
import org.apache.thrift.protocol.TBinaryProtocol;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -29,7 +26,6 @@ import com.cloudera.impala.analysis.SlotRef;
import com.cloudera.impala.common.AnalysisException;
import com.cloudera.impala.common.InternalException;
import com.cloudera.impala.service.FeSupport;
import com.cloudera.impala.thrift.TExpr;
import com.google.common.base.Preconditions;
import com.google.common.collect.Lists;
@@ -81,17 +77,8 @@ public class SingleColumnFilter {
}
// call backend
TExpr thriftExpr = literalConjunct.treeToThrift();
TSerializer serializer = new TSerializer(new TBinaryProtocol.Factory());
try {
if (!FeSupport.EvalPredicate(serializer.serialize(thriftExpr),
serializer.serialize(analyzer.getQueryGlobals()))) {
return false;
}
} catch (TException e) {
// this should never happen
throw new InternalException(
"couldn't execute predicate " + literalConjunct.toSql(), e);
if (!FeSupport.EvalPredicate(literalConjunct, analyzer.getQueryGlobals())) {
return false;
}
}

View File

@@ -14,9 +14,6 @@
package com.cloudera.impala.planner;
import org.apache.thrift.TException;
import org.apache.thrift.TSerializer;
import org.apache.thrift.protocol.TBinaryProtocol;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -28,7 +25,6 @@ import com.cloudera.impala.analysis.Predicate;
import com.cloudera.impala.common.AnalysisException;
import com.cloudera.impala.common.InternalException;
import com.cloudera.impala.service.FeSupport;
import com.cloudera.impala.thrift.TExpr;
import com.google.common.base.Preconditions;
/**
@@ -104,17 +100,7 @@ public class ValueRange {
}
// call backend
TExpr thriftExpr = p.treeToThrift();
TSerializer serializer = new TSerializer(new TBinaryProtocol.Factory());
try {
boolean result = FeSupport.EvalPredicate(serializer.serialize(thriftExpr),
serializer.serialize(analyzer.getQueryGlobals()));
return result;
} catch (TException e) {
// this should never happen
throw new InternalException(
"couldn't execute predicate " + p.toSql() + "\n" + e.toString());
}
return FeSupport.EvalPredicate(p, analyzer.getQueryGlobals());
}
}

View File

@@ -16,9 +16,19 @@ package com.cloudera.impala.service;
import java.io.File;
import org.apache.thrift.TException;
import org.apache.thrift.TSerializer;
import org.apache.thrift.protocol.TBinaryProtocol;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.cloudera.impala.analysis.Expr;
import com.cloudera.impala.catalog.PrimitiveType;
import com.cloudera.impala.common.InternalException;
import com.cloudera.impala.thrift.TExpr;
import com.cloudera.impala.thrift.TQueryGlobals;
import com.google.common.base.Preconditions;
/**
* This class provides the Impala executor functionality to the FE.
* fe-support.cc implements all the native calls.
@@ -35,7 +45,21 @@ public class FeSupport {
public native static boolean NativeEvalPredicate(byte[] thriftPredicate,
byte[] thriftQueryGlobals);
public static boolean EvalPredicate(byte[] thriftPredicate, byte[] thriftQueryGlobals) {
public static boolean EvalPredicate(Expr pred, TQueryGlobals queryGlobals)
throws InternalException {
Preconditions.checkState(pred.getType() == PrimitiveType.BOOLEAN);
TExpr thriftPred = pred.treeToThrift();
TSerializer serializer = new TSerializer(new TBinaryProtocol.Factory());
try {
return FeSupport.EvalPredicate(serializer.serialize(thriftPred),
serializer.serialize(queryGlobals));
} catch (TException e) {
// this should never happen
throw new InternalException("couldn't execute predicate " + pred.toSql(), e);
}
}
private static boolean EvalPredicate(byte[] thriftPredicate, byte[] thriftQueryGlobals) {
try {
return NativeEvalPredicate(thriftPredicate, thriftQueryGlobals);
} catch (UnsatisfiedLinkError e) {

View File

@@ -455,6 +455,71 @@ string
'1'
====
---- QUERY
# test nullable expressions from inline views in left outer join
select x.id, y.a, y.b, y.id from alltypestiny x left outer join
(select 10 as a, case when id is null then 11 else 12 end as b, id from alltypestiny)
y on x.id = (y.id % 2)
order by x.id, y.id limit 100
---- TYPES
int, tinyint, tinyint, int
---- RESULTS
0,10,12,0
0,10,12,2
0,10,12,4
0,10,12,6
1,10,12,1
1,10,12,3
1,10,12,5
1,10,12,7
2,NULL,NULL,NULL
3,NULL,NULL,NULL
4,NULL,NULL,NULL
5,NULL,NULL,NULL
6,NULL,NULL,NULL
7,NULL,NULL,NULL
====
---- QUERY
# test nullable expressions from inline views in full outer join
select x.a, x.b, x.id, y.a, y.b, y.id from
(select 10 as a, case when id is null then 11 else 12 end as b, id from alltypestiny) x
full outer join
(select 20 as a, case when id is null then 21 else 22 end as b, id from alltypestiny) y on x.id = (y.id + 4)
order by x.id, y.id limit 100
---- TYPES
tinyint, tinyint, int, tinyint, tinyint, int
---- RESULTS
10,12,0,NULL,NULL,NULL
10,12,1,NULL,NULL,NULL
10,12,2,NULL,NULL,NULL
10,12,3,NULL,NULL,NULL
10,12,4,20,22,0
10,12,5,20,22,1
10,12,6,20,22,2
10,12,7,20,22,3
NULL,NULL,NULL,20,22,4
NULL,NULL,NULL,20,22,5
NULL,NULL,NULL,20,22,6
NULL,NULL,NULL,20,22,7
====
---- QUERY
# test nullable expressions wrapped multiple times
select z.xa, z.xid, z.ya, z.yid, k.a, k.id from
(select x.a as xa, x.id as xid, y.a as ya, y.id as yid from
(select 10 as a, id from alltypestiny where id = 1) x
full outer join
(select 20 as a, id from alltypestiny where id = 1) y on x.id = (y.id + 2)
) z
full outer join
(select 30 as a, id from alltypestiny where id = 1) k on z.xid = (k.id + 3)
order by z.xid, z.yid, k.id limit 100
---- TYPES
tinyint, int, tinyint, int, tinyint, int
---- RESULTS
10,1,NULL,NULL,NULL,NULL
NULL,NULL,20,1,NULL,NULL
NULL,NULL,NULL,NULL,30,1
====
---- QUERY
# right outer join requires the join op to be partitioned, otherwise non-matches cause
# duplicates
select count(*)