-
Notifications
You must be signed in to change notification settings - Fork 3.1k
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
Change result type of concat varchar function to bounded varchar #12922
Conversation
fb4a2de
to
e15651c
Compare
core/trino-main/src/main/java/io/trino/metadata/SignatureBinder.java
Outdated
Show resolved
Hide resolved
core/trino-main/src/test/java/io/trino/operator/scalar/TestStringFunctions.java
Show resolved
Hide resolved
@@ -1627,7 +1627,7 @@ public void testInvalidTypeInfixOperator() | |||
// Comment on why error message references varchar(214783647) instead of varchar(2) which seems expected result type for concatenation in expression. |
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 comment is outdated (apparently since long)
.longVariable("x", "min(2147483647, TOTAL_LONG_LITERAL_SIZE)") | ||
.returnType(parseTypeSignature("varchar(x)", ImmutableSet.of("x"))) |
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.
would it be possible to populate the return type in a functional manner?
like
.returnType(argumentTypes -> { .... })
I know this would harm equivalence on Signature
objects (Signature
defines equals
and there is no strict equality for functions). However, Signature
seems to be used as a map key only for non-calculated signatures (at least here
trino/core/trino-main/src/main/java/io/trino/operator/ParametricImplementationsGroup.java
Lines 164 to 173 in 93e6bc3
if (implementation.getSignature().getTypeVariableConstraints().isEmpty() | |
&& implementation.getSignature().getArgumentTypes().stream().noneMatch(TypeSignature::isCalculated) | |
&& !implementation.getSignature().getReturnType().isCalculated()) { | |
exactImplementations.put(implementation.getSignature(), implementation); | |
} | |
else if (implementation.hasSpecializedTypeParameters()) { | |
specializedImplementations.add(implementation); | |
} | |
else { | |
genericImplementations.add(implementation); |
core/trino-main/src/main/java/io/trino/metadata/SignatureBinder.java
Outdated
Show resolved
Hide resolved
b8eb947
to
63ed2ef
Compare
Updated to functional style. A downside seems we have to provide dummy return type because such information is required for |
@JsonProperty | ||
public Optional<Function<List<TypeSignature>, TypeSignature>> getFunctionalReturnType() |
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.
a Function
can unlikely be json-serializable?
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.
@ebyhr i think this is a blocker for me, unless Signature is not actually getting serialized.
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 think so too. I'm wondering when json (de)serialization happens for Signature
class.
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 don't know that, but you should be able to find out easily, by removing the @Json*
annotations from the class and running tests with DistributedQueryRunner
@martint can you please take a look? |
Just rebased on upstream to resolve conflicts. |
Looks like the only test failing after cc @dain |
public TypeSignature getReturnType() | ||
{ | ||
return returnType; | ||
} | ||
|
||
@JsonProperty | ||
public Optional<Function<List<TypeSignature>, TypeSignature>> getFunctionalReturnType() |
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.
What's a "functional return type"?
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's a method to allow building "return type" (e.g. a + b
) from "function arguments" (e.g. concat(a, b)
). The name might be confusing. Do you have any suggestion?
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.
getReturnTypeDerivation
?
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 your suggestion, changed to getReturnTypeDerivation
.
9976c86
to
2659241
Compare
*/ | ||
@Deprecated | ||
@JsonCreator | ||
public static Signature fromJson( |
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.
cc @dain
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 method is here on purpose to make it clear that this method is only here for Jasckson json parsing because we disable access to private constructors when we use JSON. If the parsing works when this is removed, it implies that the code deserializing the JSON has a misconfigured parser.
@@ -241,6 +254,12 @@ public Builder returnType(TypeSignature returnType) | |||
return this; | |||
} | |||
|
|||
public Builder returnTypeDerivation(Optional<Function<List<TypeSignature>, TypeSignature>> returnTypeDerivation) |
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 could accept without Optional
.
// still required for SHOW FUNCTIONS result | ||
.longVariable("x", "1") |
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.
what does the SHOW FUNCTIONS print now?
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's varchar(x)
after this change
|
||
// still required for SHOW FUNCTIONS result | ||
.longVariable("x", "1") | ||
.returnType(new TypeSignature("varchar", typeVariable("x"))) |
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.
Can we use unbounded varchar as a "fake" return type?
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.
Probably, it requires additional change. Below exception happens after changing to returnType(VARCHAR)
without longVariable
.
Bound signature concat(varchar, varchar):varchar(3) does not match declared signature concat(varchar):varchar
java.lang.IllegalArgumentException: Bound signature concat(varchar, varchar):varchar(3) does not match declared signature concat(varchar):varchar
at com.google.common.base.Preconditions.checkArgument(Preconditions.java:435)
at io.trino.metadata.SignatureBinder.verifyBoundSignature(SignatureBinder.java:332)
at io.trino.metadata.SignatureBinder.extractBoundVariables(SignatureBinder.java:306)
at io.trino.metadata.SignatureBinder.bindFunction(SignatureBinder.java:251)
at io.trino.metadata.FunctionResolver.toFunctionBinding(FunctionResolver.java:127)
at io.trino.metadata.FunctionResolver.matchFunction(FunctionResolver.java:258)
at io.trino.metadata.FunctionResolver.matchFunctionWithCoercion(FunctionResolver.java:233)
at io.trino.metadata.FunctionResolver.resolveFunction(FunctionResolver.java:174)
at io.trino.metadata.MetadataManager.resolvedFunctionInternal(MetadataManager.java:2115)
at io.trino.metadata.MetadataManager.lambda$resolvedFunctionInternal$49(MetadataManager.java:2110)
at java.base/java.util.Optional.orElseGet(Optional.java:364)
at io.trino.metadata.MetadataManager.resolvedFunctionInternal(MetadataManager.java:2110)
at io.trino.metadata.MetadataManager.resolveFunction(MetadataManager.java:2081)
at io.trino.sql.analyzer.ExpressionAnalyzer$Visitor.visitFunctionCall(ExpressionAnalyzer.java:1258)
at io.trino.sql.analyzer.ExpressionAnalyzer$Visitor.visitFunctionCall(ExpressionAnalyzer.java:581)
at io.trino.sql.tree.FunctionCall.accept(FunctionCall.java:121)
at io.trino.sql.tree.StackableAstVisitor.process(StackableAstVisitor.java:27)
at io.trino.sql.analyzer.ExpressionAnalyzer$Visitor.process(ExpressionAnalyzer.java:604)
at io.trino.sql.analyzer.ExpressionAnalyzer.analyze(ExpressionAnalyzer.java:485)
at io.trino.sql.analyzer.ExpressionAnalyzer.analyzeExpression(ExpressionAnalyzer.java:3441)
at io.trino.sql.analyzer.StatementAnalyzer$Visitor.analyzeExpression(StatementAnalyzer.java:4075)
at io.trino.sql.analyzer.StatementAnalyzer$Visitor.analyzeSelectSingleColumn(StatementAnalyzer.java:3887)
at io.trino.sql.analyzer.StatementAnalyzer$Visitor.analyzeSelect(StatementAnalyzer.java:3673)
at io.trino.sql.analyzer.StatementAnalyzer$Visitor.visitQuerySpecification(StatementAnalyzer.java:2436)
at io.trino.sql.analyzer.StatementAnalyzer$Visitor.visitQuerySpecification(StatementAnalyzer.java:457)
at io.trino.sql.tree.QuerySpecification.accept(QuerySpecification.java:155)
at io.trino.sql.tree.AstVisitor.process(AstVisitor.java:27)
at io.trino.sql.analyzer.StatementAnalyzer$Visitor.process(StatementAnalyzer.java:474)
at io.trino.sql.analyzer.StatementAnalyzer$Visitor.process(StatementAnalyzer.java:482)
at io.trino.sql.analyzer.StatementAnalyzer$Visitor.visitQuery(StatementAnalyzer.java:1394)
at io.trino.sql.analyzer.StatementAnalyzer$Visitor.visitQuery(StatementAnalyzer.java:457)
at io.trino.sql.tree.Query.accept(Query.java:107)
at io.trino.sql.tree.AstVisitor.process(AstVisitor.java:27)
at io.trino.sql.analyzer.StatementAnalyzer$Visitor.process(StatementAnalyzer.java:474)
at io.trino.sql.analyzer.StatementAnalyzer.analyze(StatementAnalyzer.java:436)
at io.trino.sql.analyzer.Analyzer.analyze(Analyzer.java:79)
at io.trino.sql.analyzer.Analyzer.analyze(Analyzer.java:71)
at io.trino.execution.SqlQueryExecution.analyze(SqlQueryExecution.java:261)
at io.trino.execution.SqlQueryExecution.<init>(SqlQueryExecution.java:199)
at io.trino.execution.SqlQueryExecution$SqlQueryExecutionFactory.createQueryExecution(SqlQueryExecution.java:825)
at io.trino.dispatcher.LocalDispatchQueryFactory.lambda$createDispatchQuery$0(LocalDispatchQueryFactory.java:135)
at io.trino.$gen.Trino_testversion____20220914_081840_71.call(Unknown Source)
at com.google.common.util.concurrent.TrustedListenableFutureTask$TrustedFutureInterruptibleTask.runInterruptibly(TrustedListenableFutureTask.java:131)
at com.google.common.util.concurrent.InterruptibleTask.run(InterruptibleTask.java:74)
at com.google.common.util.concurrent.TrustedListenableFutureTask.run(TrustedListenableFutureTask.java:82)
at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1136)
at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:635)
at java.base/java.lang.Thread.run(Thread.java:833)
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 doesn't make sense to allow a function definition to declare a return type and a returnTypeDerivation at the same time:
- If they are not the same value, which one matters?
- If they are the same value, why have the returnTypeDerivation at all?
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.
What should be the return type printed by SHOW FUNCTIONS?
throw new IllegalArgumentException("Unsupported type: " + type); | ||
} | ||
|
||
private static TypeSignature calculateReturnType(List<TypeSignature> typeSignatures) |
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.
calculateVarcharConcatReturnType
@dain Gentle reminder. |
@ebyhr there is a CI failure |
The concern I have is that this lets functions define how the result type is derived and completely bypass the type system. It's possible that this will make it harder to improve the type inferencer in the future without breaking backward compatibility, especially if anyone uses this feature to derive a return type that is not what the inference engine would naturally produce if it was capable of doing so. |
Just rebased on upstream to resolve conflicts. |
@martint Do you have any alternative solution for improving |
@martint Gentle reminder. |
@martint Gentle reminder. |
All tests passed except for TestSignature when removed json annotations from Signature class. TestSignature was a unit test verifying json serialization and didn't contain the actual usage. Removing the annotation and test should be fine.
Rebased on upstream to resolve conflicts. |
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 don't understand why this change. If we want concat(varchar(3), varchar(5)
to return varchar(8)
and concat(varchar, varchar(5)
to be unbounded, we should work on the type calculation system to system to support that exact calculation instead of giving functions the ability to opt out of the type system.
*/ | ||
@Deprecated | ||
@JsonCreator | ||
public static Signature fromJson( |
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 method is here on purpose to make it clear that this method is only here for Jasckson json parsing because we disable access to private constructors when we use JSON. If the parsing works when this is removed, it implies that the code deserializing the JSON has a misconfigured parser.
import static io.trino.type.InternalTypeManager.TESTING_TYPE_MANAGER; | ||
import static org.testng.Assert.assertEquals; | ||
|
||
public class TestSignature |
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 would we remove the test that verifies the signature round trips?
Description
Change result type of concat
varchar
function to boundedvarchar
Fixes #12827
Documentation
( ) No documentation is needed.
( ) Sufficient documentation is included in this PR.
( ) Documentation PR is available with #prnumber.
( ) Documentation issue #issuenumber is filed, and can be handled later.
Release notes
( ) No release notes entries required.
( ) Release notes entries required with the following suggested text: