clintropolis commented on a change in pull request #9893:
URL: https://github.com/apache/druid/pull/9893#discussion_r427752016



##########
File path: docs/querying/sql.md
##########
@@ -322,17 +322,18 @@ String functions accept strings, and return a type 
appropriate to the function.
 |`LOWER(expr)`|Returns expr in all lowercase.|
 |`PARSE_LONG(string[, radix])`|Parses a string into a long (BIGINT) with the 
given radix, or 10 (decimal) if a radix is not provided.|
 |`POSITION(needle IN haystack [FROM fromIndex])`|Returns the index of needle 
within haystack, with indexes starting from 1. The search will begin at 
fromIndex, or 1 if fromIndex is not specified. If the needle is not found, 
returns 0.|
-|`REGEXP_EXTRACT(expr, pattern, [index])`|Apply regular expression pattern and 
extract a capture group, or null if there is no match. If index is unspecified 
or zero, returns the substring that matched the pattern.|
+|`REGEXP_EXTRACT(expr, pattern, [index])`|Apply regular expression `pattern` 
to `expr` and extract a capture group, or `NULL` if there is no match. If index 
is unspecified or zero, returns the substring that matched the pattern. The 
pattern must match starting at the beginning of `expr`, but does not need to 
match the entire string. Note: when `druid.generic.useDefaultValueForNull = 
true`, it is not possible to differentiate an empty-string match from a 
non-match (both will return `NULL`).|
+|`REGEXP_LIKE(expr, pattern)`|Returns whether `expr` matches regular 
expression `pattern`. The pattern must match starting at the beginning of 
`expr`, but does not need to match the entire string. Especially useful in 
WHERE clauses.|

Review comment:
       Should this mention that it is like `LIKE` but with 100% more regex? 
Maybe it's obvious enough since it is in the function name. Regardless, we 
should add docs to `math-expr.md` as well?

##########
File path: 
sql/src/main/java/org/apache/druid/sql/calcite/expression/OperatorConversions.java
##########
@@ -291,15 +294,15 @@ public OperatorBuilder operandTypes(final 
SqlTypeFamily... operandTypes)
       return this;
     }
 
-    public OperatorBuilder operandTypeInference(final SqlOperandTypeInference 
operandTypeInference)

Review comment:
       hmm, I forget why I added this function, but it appears at least now 
that nothing is calling it.. It was added with #6974, specifically [to be able 
to set this as 
non-null](https://github.com/apache/druid/pull/6974/files#diff-251ecf0956707731dab37c9469e83a70R330).
 I guess with this change that block will always be null, so the if can 
probably be removed, though I wish I could remember why I added it. However, 
that PR was open for over a year so .. maybe over that lifetime of fixing it up 
for conflicts it's purpose was lost to time...

##########
File path: 
sql/src/main/java/org/apache/druid/sql/calcite/expression/OperatorConversions.java
##########
@@ -291,15 +294,15 @@ public OperatorBuilder operandTypes(final 
SqlTypeFamily... operandTypes)
       return this;
     }
 
-    public OperatorBuilder operandTypeInference(final SqlOperandTypeInference 
operandTypeInference)
+    public OperatorBuilder requiredOperands(final int requiredOperands)
     {
-      this.operandTypeInference = operandTypeInference;
+      this.requiredOperands = requiredOperands;
       return this;
     }
 
-    public OperatorBuilder requiredOperands(final int requiredOperands)
+    public OperatorBuilder literalOperands(final int... literalOperands)

Review comment:
       this seems handy to have :metal:, but (nit) could you add javadoc 
indicating that these are the positions of the operands that are required to be 
literals? bonus points if you add javadocs to other methods, but also, since 
nothing else has them don't feel obligated to add them even for this unless 
there are other changes to make

##########
File path: 
sql/src/main/java/org/apache/druid/sql/calcite/expression/OperatorConversions.java
##########
@@ -430,36 +434,64 @@ public void inferOperandTypes(
 
   /**
    * Operand type checker that is used in 'simple' situations: there are a 
particular number of operands, with
-   * particular types, some of which may be optional or nullable.
+   * particular types, some of which may be optional or nullable, and some of 
which may be required to be literals.
    */
   private static class DefaultOperandTypeChecker implements 
SqlOperandTypeChecker
   {
     private final List<SqlTypeFamily> operandTypes;
     private final int requiredOperands;
     private final IntSet nullableOperands;
+    private final IntSet literalOperands;
 
     DefaultOperandTypeChecker(
         final List<SqlTypeFamily> operandTypes,
         final int requiredOperands,
-        final IntSet nullableOperands
+        final IntSet nullableOperands,
+        @Nullable final int[] literalOperands
     )
     {
       Preconditions.checkArgument(requiredOperands <= operandTypes.size() && 
requiredOperands >= 0);
       this.operandTypes = Preconditions.checkNotNull(operandTypes, 
"operandTypes");
       this.requiredOperands = requiredOperands;
       this.nullableOperands = Preconditions.checkNotNull(nullableOperands, 
"nullableOperands");
+
+      if (literalOperands == null) {
+        this.literalOperands = IntSets.EMPTY_SET;
+      } else {
+        this.literalOperands = new IntArraySet();
+        Arrays.stream(literalOperands).forEach(this.literalOperands::add);
+      }
     }
 
     @Override
     public boolean checkOperandTypes(SqlCallBinding callBinding, boolean 
throwOnFailure)
     {
-      if (operandTypes.size() != callBinding.getOperandCount()) {
-        // Just like FamilyOperandTypeChecker: assume this is an inapplicable 
sub-rule of a composite rule; don't throw
-        return false;
-      }
-
       for (int i = 0; i < callBinding.operands().size(); i++) {
         final SqlNode operand = callBinding.operands().get(i);
+
+        if (literalOperands.contains(i)) {
+          // Verify that 'operand' is a literal.
+          if (!SqlUtil.isLiteral(operand)) {
+            return throwOrReturn(
+                throwOnFailure,
+                callBinding,
+                cb -> cb.getValidator()
+                        .newValidationError(
+                            operand,
+                            
Static.RESOURCE.argumentMustBeLiteral(callBinding.getOperator().getName())
+                        )
+            );
+          }
+
+          if (!nullableOperands.contains(i) && SqlUtil.isNullLiteral(operand, 
true)) {

Review comment:
       Would it make sense for this null part of the check be consolidated with 
the `else if (operandType.getSqlTypeName() == SqlTypeName.NULL)` branch by 
putting the can be null check first in the if/else chain? I guess 
`SqlUtil.isNullLiteral`, is slightly more expensive of a check than just 
checking that the type name is null, but is also maybe more correct since 
casting a null is still probably null?




----------------------------------------------------------------
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

For queries about this service, please contact Infrastructure at:
[email protected]



---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to