-
Notifications
You must be signed in to change notification settings - Fork 1.4k
Use argument type to lookup function for literal only query #13673
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
Use argument type to lookup function for literal only query #13673
Conversation
Codecov ReportAttention: Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## master #13673 +/- ##
============================================
- Coverage 61.75% 57.53% -4.23%
+ Complexity 207 197 -10
============================================
Files 2436 2580 +144
Lines 133233 142424 +9191
Branches 20636 22117 +1481
============================================
- Hits 82274 81939 -335
- Misses 44911 53985 +9074
- Partials 6048 6500 +452
Flags with carried forward coverage won't be shown. Click here to find out more. ☔ View full report in Codecov by Sentry. |
pinot-common/src/main/java/org/apache/pinot/common/utils/request/RequestUtils.java
Outdated
Show resolved
Hide resolved
pinot-common/src/main/java/org/apache/pinot/common/utils/request/RequestUtils.java
Show resolved
Hide resolved
|
|
||
|
|
||
| public class CompileTimeFunctionsInvoker implements QueryRewriter { | ||
|
|
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.
If #13711 is merged first, can we update the test for this query rewriter to also include cases where the polymorphic scalar comparison functions are invoked?
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.
Added, and it works as expected ;-)
| operands.set(i, operand); | ||
| Literal literal = operand.getLiteral(); | ||
| if (compilable && literal != null) { | ||
| RequestUtils.getLiteralTypeAndValue(literal, argumentTypes, arguments, i); |
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.
It might be better to return a pair of {argumentType, argument} from RequestUtils.getLiteralTypeAndValue instead of modifying the array arguments?
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.
I initially did that, then trying to safe a Object by passing in the arrays (also explains the unused import :P). I guess it might be over optimization. Changing it back to using pair to be more readable
| if (operand.getLiteral() == null) { | ||
| operands.set(i, operand); | ||
| Literal literal = operand.getLiteral(); | ||
| if (compilable && literal != null) { |
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.
Why not just directly return expression where we set compilable to false?
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 a recursive call (DFS). Even if we find the current level is not compilable, we still want to check the child level. Added some comments
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.
Ah, that totally makes sense, not sure how I missed that earlier.
| List<Integer> intList = literal.getIntArrayValue(); | ||
| int numInts = intList.size(); | ||
| int[] intArray = new int[numInts]; | ||
| for (int i = 0; i < numInts; i++) { | ||
| intArray[i] = intList.get(i); | ||
| } | ||
| return intArray; |
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.
nit: these could be added as util methods in ArrayListUtils.
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.
Looks like there's similar logic duplicated in LiteralContext as well -
pinot/pinot-common/src/main/java/org/apache/pinot/common/request/context/LiteralContext.java
Lines 106 to 153 in 835949b
| case INT_ARRAY_VALUE: { | |
| _type = DataType.INT; | |
| List<Integer> valueList = literal.getIntArrayValue(); | |
| int numValues = valueList.size(); | |
| int[] values = new int[numValues]; | |
| for (int i = 0; i < numValues; i++) { | |
| values[i] = valueList.get(i); | |
| } | |
| _value = values; | |
| _pinotDataType = PinotDataType.PRIMITIVE_INT_ARRAY; | |
| break; | |
| } | |
| case LONG_ARRAY_VALUE: { | |
| _type = DataType.LONG; | |
| List<Long> valueList = literal.getLongArrayValue(); | |
| int numValues = valueList.size(); | |
| long[] values = new long[numValues]; | |
| for (int i = 0; i < numValues; i++) { | |
| values[i] = valueList.get(i); | |
| } | |
| _value = values; | |
| _pinotDataType = PinotDataType.PRIMITIVE_LONG_ARRAY; | |
| break; | |
| } | |
| case FLOAT_ARRAY_VALUE: { | |
| _type = DataType.FLOAT; | |
| List<Integer> valueList = literal.getFloatArrayValue(); | |
| int numValues = valueList.size(); | |
| float[] values = new float[numValues]; | |
| for (int i = 0; i < numValues; i++) { | |
| values[i] = Float.intBitsToFloat(valueList.get(i)); | |
| } | |
| _value = values; | |
| _pinotDataType = PinotDataType.PRIMITIVE_FLOAT_ARRAY; | |
| break; | |
| } | |
| case DOUBLE_ARRAY_VALUE: { | |
| _type = DataType.DOUBLE; | |
| List<Double> valueList = literal.getDoubleArrayValue(); | |
| int numValues = valueList.size(); | |
| double[] values = new double[numValues]; | |
| for (int i = 0; i < numValues; i++) { | |
| values[i] = valueList.get(i); | |
| } | |
| _value = values; | |
| _pinotDataType = PinotDataType.PRIMITIVE_DOUBLE_ARRAY; | |
| break; | |
| } |
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.
Good point. I didn't move it to ArrayListUtils because of the special handling for float array (we use int array to send float array), but expose these methods as util and simplified LiteralContext
| return expression; | ||
| } | ||
| String canonicalName = FunctionRegistry.canonicalize(function.getOperator()); | ||
| FunctionInfo functionInfo = FunctionRegistry.lookupFunctionInfo(canonicalName, argumentTypes); |
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.
I'm wondering if we might run into some type conversion issues here. For instance, a polymorphic scalar comparison function may implement support for comparing two arguments of the same type (see #13711 for instance). Earlier with the argument count based function lookup, if the two arguments were of a different type, the scalar comparison function invocation might still have worked based on the type conversion in FunctionInvoker. But now, each such polymorphic scalar function implementation would need to either account for all these different type combinations or else fall back to a default implementation and then rely on the FunctionInvoker's type conversion in case of heterogeneous argument types.
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.
The scalar function should take care of all possible combinations of argument types, similar to the backward compatible logic in PolymorphicComparisonScalarFunction when argument type is different
3d10494 to
091a535
Compare
| if (expression.getType() == ExpressionType.LITERAL) { | ||
| computeResultsForLiteral(expression.getLiteral(), columnNames, columnTypes, values, index); | ||
| } | ||
| if (expression.getType() == ExpressionType.FUNCTION) { |
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.
nit: AFAIU computeResultsForLiteral cannot change expressiong.getType(). Am I right? In that case can we add a else to this if (expression.getType() == ExpressionType.FUNCTION) { to improve readability?
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.
Good point.
|
Question: This PR modifies the way functions are resolved, but do not change semantics because right now we don't have overload functions right? ie we have a single Once we add overload functions into our registry (ie |
This PR only affects scalar functions. Aggregates are handled differently. Currently in multi-stage engine, results are converted into the correct type (e.g. result type of SUM(longCol) is converted from double to long before sending to the next stage). |
091a535 to
007c9c2
Compare
FunctionRegistryto support polymorphismLiteral