Skip to content

Extend native type rewrite for cast, function call, and simple case expression #25142

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

Open
wants to merge 2 commits into
base: master
Choose a base branch
from

Conversation

HeidiHan0000
Copy link
Contributor

@HeidiHan0000 HeidiHan0000 commented May 19, 2025

This PR extends #24916 by adding support for additional cases in the NativeExecutionTypeRewrite class. Assuming enum types appear as either Cast or DereferenceExpression expressions within NativeExecutionTypeRewrite, implemented logic for rewriting these expressions into their base literal expressions.

Specifically the updates from #24916 are:

  • visitCast - added support for if the expression is a custom type (cast custom type to base type)
  • visitFunctionCall - added support when the argument is a custom type (and not wrapped in Cast)
    • allow rewrites for other functions other than enum_key
  • visitCase - added support for rewriting cast and dereference expression arguments for visitCase
== NO RELEASE NOTE ==

@HeidiHan0000 HeidiHan0000 requested a review from a team as a code owner May 19, 2025 17:02
@HeidiHan0000 HeidiHan0000 requested a review from hantangwangd May 19, 2025 17:02
@prestodb-ci prestodb-ci added the from:Meta PR from Meta label May 19, 2025
@HeidiHan0000 HeidiHan0000 requested a review from zacw7 May 19, 2025 17:13
zacw7
zacw7 previously approved these changes May 20, 2025
Copy link
Member

@zacw7 zacw7 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM. Thanks for adding this.

Copy link
Contributor

@rschlussel rschlussel left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this needs tests too

? new FunctionCall(node.getLocation().get(), QualifiedName.of(FUNCTION_ELEMENT_AT), node.getWindow(), node.getFilter(), node.getOrderBy(), node.isDistinct(), node.isIgnoreNulls(), arguments)
: new FunctionCall(QualifiedName.of(FUNCTION_ELEMENT_AT), node.getWindow(), node.getFilter(), node.getOrderBy(), node.isDistinct(), node.isIgnoreNulls(), arguments);
else {
arguments.set(0, rewriteIfCastOrDereferenceExpression(argument));
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

arguments will be immutable. Need to make a new list.

// Return node without rewriting.
return node;
}
argument = rewriteIfCastOrDereferenceExpression(argument);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

instead of this custom rewriteIfCastOrdereferenceExpression, why don't we visit all the argument s

argumentType = ((TypeWithName) argumentType).getType();
QualifiedName functionName = node.getName();
List<Expression> arguments = node.getArguments();
if (node.getArguments().size() == 1) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why do we specifically check for 1 argument? why not visit all the arguments and rewrite as needed?

argumentType = functionAndTypeResolver.getType(parseTypeSignature(((DereferenceExpression) argument).getBase().toString()));
}
else {
// ENUM_KEY is only supported with Cast or DereferenceExpression for now.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't know if there is a reason why, but based on all the cases I've encountered, custom types were either a cast expression or dereference expression

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's most common, but not sure it's guaranteed. The rewriter should be able to support all query shapes. If we leave an enum_key function in, queries will fail during execution.

return peelIfDereferenceExpression(argument);
}

private Expression peelIfDereferenceExpression(Expression argument)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Are there any downsides to unwrapping the dereference expression? Can we just have this happen always? i.e. have a visitDereferenceExpression() that handles it. (or have visitExpression call to an ExpressionTreeRewriter that handles all the expression rewrites including Dereference, Cast, SimpleCaseExpression, etc. that way we'll automatically recurse into e.g. cast contained in an argument to some other custom function no matter how many layers deep.)

}
}
}
catch (IllegalArgumentException | UnknownTypeException e) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

in what cases does the rewrite fail. Could we write the code so it doesn't happen instead of relying on exception handling, which could mask bugs.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I found that it can fail while getting the type (functionAndTypeResolver.getType) for other dereference expressions, for example for a named field in a row or getting some values from a CTE, as it is not recognized as a valid type. I'm not sure what else we can do if this throws other than return the original node. Would logging help?

}

@Override
protected Node visitSimpleCaseExpression(SimpleCaseExpression node, Void context)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i think if you use an ExpressionTreeRewriter for rewriting expressions generally, you won't need to add custom logic for handling this or other expressions that might have arguments that have enum functions. And it will work better because it can be nested as many layers deep as someone cares to write.

@tdcmeehan
Copy link
Contributor

tdcmeehan commented May 21, 2025

Noob question, why do we rewrite the expression tree rather than the plan fragment?

@zacw7
Copy link
Member

zacw7 commented May 22, 2025

this needs tests too

agree. but it won't be trivial to set up a mock type manager which provides custom types.

@HeidiHan0000
Copy link
Contributor Author

@rschlussel Thanks for the review! I think by calling visitDereferenceExpression or visitExpression and letting that handle the resolving of the custom types will address most of your comments, will update the PR with it

@zacw7
Copy link
Member

zacw7 commented May 23, 2025

Noob question, why do we rewrite the expression tree rather than the plan fragment?

I think either way works?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
from:Meta PR from Meta
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants