Skip to content
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

QL: Refactor FunctionRegistry to make it pluggable #67761

Merged
merged 4 commits into from
Jan 21, 2021

Conversation

costin
Copy link
Member

@costin costin commented Jan 20, 2021

Break the FunctionRegistry monolith into a common QL base and a SQL
specific registry that handles aspects such as distinct and extract.
In the process clean-up the names and signature of internal interfaces.
Most of the semantics were preserved however the error messages were
slightly tweaked to make them more readable - this shouldn't be a
problem as they are being used internally mainly in test assertions.

Break the FunctionRegistry monolith into a common QL base and a SQL
specific registry that handles aspects such as distinct and extract.
In the process clean-up the names and signature of internal interfaces.
Most of the semantics were preserved however the error messages were
slightly tweaked to make them more readable - this shouldn't be a
problem as they are being used internally mainly in test assertions.
@elasticmachine elasticmachine added the Team:QL (Deprecated) Meta label for query languages team label Jan 20, 2021
@elasticmachine
Copy link
Collaborator

Pinging @elastic/es-ql (Team:QL)

@@ -17,28 +17,19 @@
*/
@FunctionalInterface
public interface Builder {
Function build(UnresolvedFunction uf, boolean distinct, Configuration configuration);
Function build(UnresolvedFunction uf, Configuration configuration, Object... extras);
Copy link
Member Author

Choose a reason for hiding this comment

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

This is the entry point of this PR - the varargs allow subclasses to specify extra parameters however it's up to them to do routing and verify parameters.

Copy link
Contributor

Choose a reason for hiding this comment

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

Could you maybe add some more details to the javadoc of the Builder?

Copy link
Contributor

Choose a reason for hiding this comment

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

This is just a matter of taste, but I find having a single context argument easier to follow. Something like this: palesz@c07981b

Copy link
Contributor

@astefan astefan left a comment

Choose a reason for hiding this comment

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

LGTM
Also, left few minor comments.

BinaryFunctionBuilder<T> ctorRef, String... names) {
FunctionBuilder builder = (source, children, distinct, cfg) -> {
protected static <T extends Function> FunctionDefinition def(Class<T> function, BinaryBuilder<T> ctorRef, String... names) {
FunctionBuilder builder = (source, children, cfg) -> {
boolean isBinaryOptionalParamFunction = OptionalArgument.class.isAssignableFrom(function);
if (isBinaryOptionalParamFunction && (children.size() > 2 || children.size() < 1)) {
throw new QlIllegalArgumentException("expects one or two arguments");
} else if (!isBinaryOptionalParamFunction && children.size() != 2) {
Copy link
Contributor

Choose a reason for hiding this comment

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

I know this may not be the purpose of this PR, but in the rest of our code inequality for a boolean comparison is done with == false. I am also aware that this class is not the only one where we use this style, but maybe we could start cleaning this one up and have a consistent approach as much as possible in the entire QL code base?

Copy link
Member Author

Choose a reason for hiding this comment

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

I didn't see these statements and frankly I was more concerned about touching extra code that leads to extra formatting and extra noise.

As for the consistent approach, we could reinforce it project wide which could align with the spotless/formatting discussion which I believe is scheduled for Elasticsearch 8.

Copy link
Member Author

Choose a reason for hiding this comment

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

Btw, see #67817

return null;
}
if (extras.length != 1 || (extras[0] instanceof Boolean) == false) {
throw new SqlIllegalArgumentException("Invalid number and types of arguments given to function definition");
Copy link
Contributor

Choose a reason for hiding this comment

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

Does it make sense to be more explicit in the error message here? The message says it's an invalid number and type of arguments but it doesn't say what is expecting.

return def(function, builder, true, names);
}

protected interface TertiaryZoneIdAwareBuilder<T> {
Copy link
Contributor

Choose a reason for hiding this comment

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

For a three-argument function here you use tertiary naming, while in FunctionRegistry you use ternary. For an uniform approach, could you use the same style in both places? If so, given the rest of the names - unary, binary -, I'd say it should be ternary.

*/
@SuppressWarnings("overloads") // These are ambiguous if you aren't using ctor references but we always do
protected static <T extends Function> FunctionDefinition def(Class<T> function, CastBuilder<T> ctorRef, String... names) {
SqlFunctionBuilder builder = (source, children, cfg, distinct) ->
Copy link
Contributor

Choose a reason for hiding this comment

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

Shouldn't forbidDistinct be used here as well?

@costin
Copy link
Member Author

costin commented Jan 21, 2021

@elasticmachine update branch

@costin costin merged commit 2f2b690 into elastic:master Jan 21, 2021
@costin costin deleted the ql/refactor-function-builder branch January 21, 2021 13:02
costin added a commit that referenced this pull request Jan 21, 2021
Break the FunctionRegistry monolith into a common QL base and a SQL
specific registry that handles aspects such as distinct and extract.
In the process clean-up the names and signature of internal interfaces.
Most of the semantics were preserved however the error messages were
slightly tweaked to make them more readable - this shouldn't be a
problem as they are being used internally mainly in test assertions.
Copy link
Contributor

@palesz palesz left a comment

Choose a reason for hiding this comment

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

LGTM, two minor comments

register(groupFunctions);
}

protected void register(FunctionDefinition[]... groupFunctions) {
Copy link
Contributor

Choose a reason for hiding this comment

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

minor suggestion: groupFunctions -> functionGroups

Unrelated: Why do we need the groups if we flatten them out anyways?

@@ -17,28 +17,19 @@
*/
@FunctionalInterface
public interface Builder {
Function build(UnresolvedFunction uf, boolean distinct, Configuration configuration);
Function build(UnresolvedFunction uf, Configuration configuration, Object... extras);
Copy link
Contributor

Choose a reason for hiding this comment

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

This is just a matter of taste, but I find having a single context argument easier to follow. Something like this: palesz@c07981b

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
:Analytics/EQL EQL querying :Analytics/SQL SQL querying >refactoring Team:QL (Deprecated) Meta label for query languages team v8.0.0-alpha1
Projects
None yet
Development

Successfully merging this pull request may close these issues.

6 participants