-
Notifications
You must be signed in to change notification settings - Fork 1.7k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[Kernel] Change comparator expression to lazy evaluation #2853
Changes from 4 commits
b150f2f
4b7ecba
851fc75
4641e81
844e277
daffb80
70e829c
ec4ddf4
833b04b
1b9e5dd
ee078b7
840f586
4b0818e
be97175
8afbc83
d01c35d
63dc5df
eb30961
15256bd
a086174
1ecc62b
843ad18
a73803b
8d237d5
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,27 @@ | ||
/* | ||
* Copyright (2023) The Delta Lake Project Authors. | ||
* | ||
* 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 io.delta.kernel.data; | ||
|
||
import io.delta.kernel.annotation.Evolving; | ||
|
||
/** | ||
* Comparator interface for lazy evaluating comparator | ||
*/ | ||
@Evolving | ||
public interface VectorComparator { | ||
boolean compare(int compareResult); | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -24,6 +24,7 @@ | |
import io.delta.kernel.client.ExpressionHandler; | ||
import io.delta.kernel.data.ColumnVector; | ||
import io.delta.kernel.data.ColumnarBatch; | ||
import io.delta.kernel.data.VectorComparator; | ||
import io.delta.kernel.expressions.*; | ||
import io.delta.kernel.types.*; | ||
|
||
|
@@ -35,10 +36,7 @@ | |
|
||
import io.delta.kernel.defaults.internal.data.vector.DefaultBooleanVector; | ||
import io.delta.kernel.defaults.internal.data.vector.DefaultConstantVector; | ||
import static io.delta.kernel.defaults.internal.expressions.DefaultExpressionUtils.booleanWrapperVector; | ||
import static io.delta.kernel.defaults.internal.expressions.DefaultExpressionUtils.childAt; | ||
import static io.delta.kernel.defaults.internal.expressions.DefaultExpressionUtils.compare; | ||
import static io.delta.kernel.defaults.internal.expressions.DefaultExpressionUtils.evalNullability; | ||
import static io.delta.kernel.defaults.internal.expressions.DefaultExpressionUtils.*; | ||
import static io.delta.kernel.defaults.internal.expressions.ImplicitCastExpression.canCastTo; | ||
|
||
/** | ||
|
@@ -405,44 +403,53 @@ ColumnVector visitAlwaysFalse(AlwaysFalse alwaysFalse) { | |
@Override | ||
ColumnVector visitComparator(Predicate predicate) { | ||
PredicateChildrenEvalResult argResults = evalBinaryExpressionChildren(predicate); | ||
|
||
int numRows = argResults.rowCount; | ||
boolean[] result = new boolean[numRows]; | ||
boolean[] nullability = evalNullability(argResults.leftResult, argResults.rightResult); | ||
int[] compareResult = compare(argResults.leftResult, argResults.rightResult); | ||
VectorComparator v; | ||
switch (predicate.getName()) { | ||
case "=": | ||
for (int rowId = 0; rowId < numRows; rowId++) { | ||
result[rowId] = compareResult[rowId] == 0; | ||
} | ||
break; | ||
v = new VectorComparator() { | ||
@Override | ||
public boolean compare(int compareResult) { | ||
return compareResult == 0; | ||
} | ||
}; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. can this be defined as a lambda (compareResult) -> (compareResult == 0)?
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. good call, fixed. |
||
return comparatorVector(argResults.leftResult, argResults.rightResult, v); | ||
case ">": | ||
for (int rowId = 0; rowId < numRows; rowId++) { | ||
result[rowId] = compareResult[rowId] > 0; | ||
} | ||
break; | ||
v = new VectorComparator() { | ||
@Override | ||
public boolean compare(int compareResult) { | ||
return compareResult > 0; | ||
} | ||
}; | ||
return comparatorVector(argResults.leftResult, argResults.rightResult, v); | ||
case ">=": | ||
for (int rowId = 0; rowId < numRows; rowId++) { | ||
result[rowId] = compareResult[rowId] >= 0; | ||
} | ||
break; | ||
v = new VectorComparator() { | ||
@Override | ||
public boolean compare(int compareResult) { | ||
return compareResult >= 0; | ||
} | ||
}; | ||
return comparatorVector(argResults.leftResult, argResults.rightResult, v); | ||
case "<": | ||
for (int rowId = 0; rowId < numRows; rowId++) { | ||
result[rowId] = compareResult[rowId] < 0; | ||
} | ||
break; | ||
v = new VectorComparator() { | ||
@Override | ||
public boolean compare(int compareResult) { | ||
return compareResult < 0; | ||
} | ||
}; | ||
return comparatorVector(argResults.leftResult, argResults.rightResult, v); | ||
case "<=": | ||
for (int rowId = 0; rowId < numRows; rowId++) { | ||
result[rowId] = compareResult[rowId] <= 0; | ||
} | ||
break; | ||
v = new VectorComparator() { | ||
@Override | ||
public boolean compare(int compareResult) { | ||
return compareResult <= 0; | ||
} | ||
}; | ||
return comparatorVector(argResults.leftResult, argResults.rightResult, v); | ||
default: | ||
throw DeltaErrors.unsupportedExpression( | ||
predicate, | ||
Optional.of("unsupported expression encountered")); | ||
} | ||
|
||
return new DefaultBooleanVector(numRows, Optional.of(nullability), result); | ||
} | ||
|
||
@Override | ||
|
Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
@@ -24,6 +24,7 @@ | |||||||||||||||||||||||||||||||||||||
import io.delta.kernel.data.ArrayValue; | ||||||||||||||||||||||||||||||||||||||
import io.delta.kernel.data.ColumnVector; | ||||||||||||||||||||||||||||||||||||||
import io.delta.kernel.data.MapValue; | ||||||||||||||||||||||||||||||||||||||
import io.delta.kernel.data.VectorComparator; | ||||||||||||||||||||||||||||||||||||||
import io.delta.kernel.expressions.Expression; | ||||||||||||||||||||||||||||||||||||||
import io.delta.kernel.types.*; | ||||||||||||||||||||||||||||||||||||||
import io.delta.kernel.internal.util.Utils; | ||||||||||||||||||||||||||||||||||||||
|
@@ -33,6 +34,20 @@ | |||||||||||||||||||||||||||||||||||||
* Utility methods used by the default expression evaluator. | ||||||||||||||||||||||||||||||||||||||
*/ | ||||||||||||||||||||||||||||||||||||||
class DefaultExpressionUtils { | ||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||
static final Comparator<BigDecimal> BIGDECIMAL_COMPARATOR = Comparator.naturalOrder(); | ||||||||||||||||||||||||||||||||||||||
static final Comparator<String> STRING_COMPARATOR = Comparator.naturalOrder(); | ||||||||||||||||||||||||||||||||||||||
static final Comparator<byte[]> BINARY_COMPARTOR = (leftOp, rightOp) -> { | ||||||||||||||||||||||||||||||||||||||
int i = 0; | ||||||||||||||||||||||||||||||||||||||
while (i < leftOp.length && i < rightOp.length) { | ||||||||||||||||||||||||||||||||||||||
if (leftOp[i] != rightOp[i]) { | ||||||||||||||||||||||||||||||||||||||
return Byte.compare(leftOp[i], rightOp[i]); | ||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||
i++; | ||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||
return Integer.compare(leftOp.length, rightOp.length); | ||||||||||||||||||||||||||||||||||||||
}; | ||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||
private DefaultExpressionUtils() {} | ||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||
/** | ||||||||||||||||||||||||||||||||||||||
|
@@ -86,6 +101,80 @@ public boolean getBoolean(int rowId) { | |||||||||||||||||||||||||||||||||||||
}; | ||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||
static ColumnVector comparatorVector( | ||||||||||||||||||||||||||||||||||||||
ColumnVector left, | ||||||||||||||||||||||||||||||||||||||
ColumnVector right, VectorComparator vectorComparator) { | ||||||||||||||||||||||||||||||||||||||
checkArgument( | ||||||||||||||||||||||||||||||||||||||
left.getSize() == right.getSize(), | ||||||||||||||||||||||||||||||||||||||
"Left and right operand have different vector sizes."); | ||||||||||||||||||||||||||||||||||||||
return new ColumnVector() { | ||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||
@Override | ||||||||||||||||||||||||||||||||||||||
public DataType getDataType() { | ||||||||||||||||||||||||||||||||||||||
return BooleanType.BOOLEAN; | ||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||
@Override | ||||||||||||||||||||||||||||||||||||||
public void close() { | ||||||||||||||||||||||||||||||||||||||
Utils.closeCloseables(left, right); | ||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||
@Override | ||||||||||||||||||||||||||||||||||||||
public int getSize() { | ||||||||||||||||||||||||||||||||||||||
return left.getSize(); | ||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||
@Override | ||||||||||||||||||||||||||||||||||||||
public boolean isNullAt(int rowId) { | ||||||||||||||||||||||||||||||||||||||
return left.isNullAt(rowId) || right.isNullAt(rowId); | ||||||||||||||||||||||||||||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. In ticket #2541 , it is mentioned that we want to check against the following truth table
But the current behavior is
Do we want to change the behavior? I am guessing we want to follow the truth table here https://spark.apache.org/docs/latest/sql-ref-null-semantics.html#:~:text=In%20order%20to%20compare%20the,both%20the%20operands%20are%20NULL%20. ? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Follow the existing method (i.e the second table). The example I gave in #2541 is for the null safe equals. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Thanks for the clarification, the existing code should follow the second table, once this PR is merged, I will add support for null safe equals. |
||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||
@Override | ||||||||||||||||||||||||||||||||||||||
public boolean getBoolean(int rowId) { | ||||||||||||||||||||||||||||||||||||||
if (isNullAt(rowId)) { | ||||||||||||||||||||||||||||||||||||||
return false; | ||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||
DataType dataType = left.getDataType(); | ||||||||||||||||||||||||||||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The Only this is that we now evaluate the this long
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Hi @vkorukanti good catch, I didn't thought about that and should move the long if statement outside of anonymous class. I have fixed the suggestion, please let me know if this is what you are looking for |
||||||||||||||||||||||||||||||||||||||
if (dataType instanceof BooleanType) { | ||||||||||||||||||||||||||||||||||||||
return vectorComparator.compare( | ||||||||||||||||||||||||||||||||||||||
Boolean.compare(left.getBoolean(rowId), right.getBoolean(rowId))); | ||||||||||||||||||||||||||||||||||||||
} else if (dataType instanceof ByteType) { | ||||||||||||||||||||||||||||||||||||||
return vectorComparator.compare( | ||||||||||||||||||||||||||||||||||||||
Byte.compare(left.getByte(rowId), right.getByte(rowId))); | ||||||||||||||||||||||||||||||||||||||
} else if (dataType instanceof ShortType) { | ||||||||||||||||||||||||||||||||||||||
return vectorComparator.compare( | ||||||||||||||||||||||||||||||||||||||
Short.compare(left.getShort(rowId), right.getShort(rowId))); | ||||||||||||||||||||||||||||||||||||||
} else if (dataType instanceof IntegerType || dataType instanceof DateType) { | ||||||||||||||||||||||||||||||||||||||
return vectorComparator.compare( | ||||||||||||||||||||||||||||||||||||||
Integer.compare(left.getInt(rowId), right.getInt(rowId))); | ||||||||||||||||||||||||||||||||||||||
} else if (dataType instanceof LongType || dataType instanceof TimestampType) { | ||||||||||||||||||||||||||||||||||||||
return vectorComparator.compare( | ||||||||||||||||||||||||||||||||||||||
Long.compare(left.getLong(rowId), right.getLong(rowId))); | ||||||||||||||||||||||||||||||||||||||
} else if (dataType instanceof FloatType) { | ||||||||||||||||||||||||||||||||||||||
return vectorComparator.compare( | ||||||||||||||||||||||||||||||||||||||
Float.compare(left.getFloat(rowId), right.getFloat(rowId))); | ||||||||||||||||||||||||||||||||||||||
} else if (dataType instanceof DoubleType) { | ||||||||||||||||||||||||||||||||||||||
return vectorComparator.compare( | ||||||||||||||||||||||||||||||||||||||
Double.compare(left.getDouble(rowId), right.getDouble(rowId))); | ||||||||||||||||||||||||||||||||||||||
} else if (dataType instanceof DecimalType) { | ||||||||||||||||||||||||||||||||||||||
return vectorComparator.compare( | ||||||||||||||||||||||||||||||||||||||
BIGDECIMAL_COMPARATOR.compare( | ||||||||||||||||||||||||||||||||||||||
left.getDecimal(rowId), right.getDecimal(rowId))); | ||||||||||||||||||||||||||||||||||||||
} else if (dataType instanceof StringType) { | ||||||||||||||||||||||||||||||||||||||
return vectorComparator.compare( | ||||||||||||||||||||||||||||||||||||||
STRING_COMPARATOR.compare( | ||||||||||||||||||||||||||||||||||||||
left.getString(rowId), right.getString(rowId))); | ||||||||||||||||||||||||||||||||||||||
} else if (dataType instanceof BinaryType) { | ||||||||||||||||||||||||||||||||||||||
return vectorComparator.compare( | ||||||||||||||||||||||||||||||||||||||
BINARY_COMPARTOR.compare( | ||||||||||||||||||||||||||||||||||||||
left.getBinary(rowId), right.getBinary(rowId))); | ||||||||||||||||||||||||||||||||||||||
} else { | ||||||||||||||||||||||||||||||||||||||
throw new UnsupportedOperationException(dataType + " can not be compared."); | ||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||
}; | ||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||
/** | ||||||||||||||||||||||||||||||||||||||
* Utility method to compare the left and right according to the natural ordering | ||||||||||||||||||||||||||||||||||||||
* and return an integer array where each row contains the comparison result (-1, 0, 1) for | ||||||||||||||||||||||||||||||||||||||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is applicable only for the default table client module. Please move this to
kernel-defaults
module in package io.delta.kernel.defaults.internal.expressions.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
thanks for pointing that out, fixed.