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

Allow casted literal values in SQL functions accepting literals #15282

Merged
merged 4 commits into from
Nov 1, 2023

Conversation

LakshSingla
Copy link
Contributor

@LakshSingla LakshSingla commented Oct 30, 2023

Description

For SQL functions accepting literals, casting might be allowed or disallowed, depending on whether the OperandTypes.LITERAL checker is used. Using it disallows casted literals like - CAST(100 AS INTEGER.
While this was an acceptable limitation as there's no use of casting a literal that the user has entered, the issue arises due to the Calcite upgrade, where the new JDBC client can't allow wildcards without an explicit cast. Therefore for a function with a signature like FUNC(column, literal), we can't do the following in JDBC.

SELECT FUNC(column, ?) FROM foo

Instead, we'd have to work around it using

SELECT FUNC(column, CAST(? AS TYPE)) FROM foo

But the second query doesn't play nice if the FUNC uses OperandTypes.LITERAL. Therefore this PR allows cast in such functions by modifying the signature to not rely on OperandTypes.LITERAL.

This PR also marks OperandTypes.LITERAL in forbidden-apis so that the user cannot use the LITERAL checker (until JDBC behavior is reverted, if it would).
BasicOperandTypeChecker is removed and the DefaultOperandTypeChecker is the only checker around.

Release note

Functions that accept literals also allow casted literals. This shouldn't have an impact on the queries that the user writes. It enables the SQL functions to accept explicit cast, which is required with JDBC.


This PR has:

  • been self-reviewed.
  • added documentation for new or modified features or behaviors.
  • a release note entry in the PR description.
  • added Javadocs for most classes and all non-trivial methods. Linked related entities via Javadoc links.
  • added or updated version, license, or notice information in licenses.yaml
  • added comments explaining the "why" and the intent of the code wherever would not be obvious for an unfamiliar reader.
  • added unit tests or modified existing tests to cover new code paths, ensuring the threshold for code coverage is met.
  • added integration tests.
  • been tested in a test Druid cluster.

@LakshSingla LakshSingla added this to the 28.0 milestone Oct 31, 2023
@@ -144,7 +143,7 @@ public boolean checkOperandTypes(SqlCallBinding callBinding, boolean throwOnFail

// Verify that 'operand' is a literal number.
if (!SqlUtil.isLiteral(operand)) {
Copy link
Contributor

Choose a reason for hiding this comment

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

doesn't this need to allow cast?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Good catch, it does. I modified the call to allow casts and added test for the same

Comment on lines +164 to +166
for (int i = requiredOperands; i < operands.size(); i++) {
ret.append("]");
}
Copy link
Contributor

Choose a reason for hiding this comment

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

this doesn't seem right. is there a test case to verify signature when there are multiple required operands?

Copy link
Contributor Author

@LakshSingla LakshSingla Oct 31, 2023

Choose a reason for hiding this comment

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

Hmm, git diff shows that I moved BasicOperandTypeChecker -> DefaultOperandTypeChecker. However DefaultOperandTypeChecker was already present in OperatorConversions that I pulled out.

I think it creates a signature like:

'functionName(requiredOperand1, requiredOperand2, [optionalOperand1, [optionalOperand2]])'
  1. We append 'functionName(
  2. Loop begins
  3. For each operand, if it's not the first operand, we append ,
  4. For each operand, if its an optional operand, we append [. At the end of the loop, we'd have open square brackets equal to the number of the optional operands
  5. For each operand, we append the operand name
  6. Loop ends
  7. For the total number of optional operands, we append ] at the end, therefore closing them out.
  8. We append )'

Copy link
Contributor Author

@LakshSingla LakshSingla Oct 31, 2023

Choose a reason for hiding this comment

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

  • There's an APPROX_COUNT_DISTINCT_DS_HLL that takes 3 parameters, out of which the first one is required and the rest two are optional.
  • There's BLOOM_FILTER that takes 2 parameters and both are required
  • And then there's APPROX_QUANTILE_FIXED_BUCKETS which takes in 6 parameters out of which first 5 are required.

Copy link
Contributor

Choose a reason for hiding this comment

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

ah yes. Now I get it. thanks for explaining the flow 😄

Copy link
Contributor

@somu-imply somu-imply left a comment

Choose a reason for hiding this comment

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

Thanks for the changes. Looks good !

Copy link
Contributor

@gianm gianm left a comment

Choose a reason for hiding this comment

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

LGTM, although I did have a suggestion for simplifying TDIGEST_GENERATE_SKETCH as well.

OperandTypes.sequence(SIGNATURE_WITH_COMPRESSION, OperandTypes.ANY, OperandTypes.LITERAL),
OperandTypes.family(SqlTypeFamily.ANY, SqlTypeFamily.NUMERIC)
)
// Validation for signatures like 'TDIGEST_GENERATE_SKETCH(column, compression)'
Copy link
Contributor

Choose a reason for hiding this comment

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

There's no need to have the OperandTypes.or anymore. Just use the default checker with requiredOperandCount(1).

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Resolved! Thanks for the review

@abhishekagarwal87 abhishekagarwal87 merged commit 2ea7177 into apache:master Nov 1, 2023
82 checks passed
abhishekagarwal87 pushed a commit to abhishekagarwal87/druid that referenced this pull request Nov 1, 2023
…he#15282)

Functions that accept literals also allow casted literals. This shouldn't have an impact on the queries that the user writes. It enables the SQL functions to accept explicit cast, which is required with JDBC.
abhishekagarwal87 added a commit that referenced this pull request Nov 1, 2023
…) (#15298)

Functions that accept literals also allow casted literals. This shouldn't have an impact on the queries that the user writes. It enables the SQL functions to accept explicit cast, which is required with JDBC.

Co-authored-by: Laksh Singla <lakshsingla@gmail.com>
CaseyPan pushed a commit to CaseyPan/druid that referenced this pull request Nov 17, 2023
…he#15282)

Functions that accept literals also allow casted literals. This shouldn't have an impact on the queries that the user writes. It enables the SQL functions to accept explicit cast, which is required with JDBC.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

4 participants