[ 
https://issues.apache.org/jira/browse/CALCITE-4347?page=com.atlassian.jira.plugin.system.issuetabpanels:comment-tabpanel&focusedCommentId=17270964#comment-17270964
 ] 

Louis Kuang edited comment on CALCITE-4347 at 1/24/21, 7:42 PM:
----------------------------------------------------------------

Thanks for the detailed feedbacks!
{quote}but I suspect that there are several other functions that are similar. I 
wonder whether it is possible for OperandTypeChecker to notice that all 
overloads give the same type to operand i and therefore deduce the type of 
operand i. By which I mean, can we make OperandTypeChecker implement 
OperandTypeInference in some cases?
{quote}
You are right, there are at least a few functions that are in similar 
situation: POSITION and OVERLAY. Their signatures are a bit different though 
{noformat}
POSITION(STRING, STRING)
POSITION(STRING, STRING, INTEGER)
POSITION(BINARY, BINARY)
POSITION(BINARY, BINARY, INTEGER)

OVERLAY(STRING, STRING, INTEGER)
OVERLAY(STRING, STRING, INTEGER, INTEGER)
OVERLAY(BINARY, BINARY, INTEGER)
OVERLAY(BINARY, BINARY, INTEGER, INTEGER){noformat}
 Not sure if I understand what you mean exactly but here is my interpretation:
{code:java}
public static final SqlOperandTypeChecker 
BINARY_OR_STRING_INTEGER_OPTIONAL_INTEGER = new CompositeOperandTypeChecker(
      CompositeOperandTypeChecker.Composition.SEQUENCE,
      ImmutableList.of(
          OperandTypes.or(BINARY, STRING),
          family(ImmutableList.of(SqlTypeFamily.INTEGER), i -> i == 1)),
      null,
      null
  ) {
    @Override public @Nullable SqlOperandTypeInference typeInference() {
      return (callBinding, returnType, operandTypes) -> {
        operandTypes[0] = returnType;
        RelDataTypeFactory typeFactory = callBinding.getTypeFactory();
        operandTypes[1] = typeFactory.createSqlType(SqlTypeName.INTEGER);
        if (operandTypes.length == 3) {
          operandTypes[2] = typeFactory.createSqlType(SqlTypeName.INTEGER);
        }
      };
    }
  };
{code}
This way the type inference can be obtained from the type checker. However, 
this does not do as much as the checker at 
SqlSubStringFunction#checkOperandTypes, which allow implicit type cast, e.g., 
from STRING to INTEGER. Because of this, I think for this JIRA making the 
modification inplace as you suggest `inferOperandTypes(SqlCallBinding 
callBinding, RelDataType returnType, RelDataType[] operandTypes)` will be 
better.

 
{quote}Your test should check the inferred types of the parameters, not just 
call .ok()
{quote}
Is there an example test that does this that I can reference? The `Sql` fluent 
testing API does not seem to expose a convenient API to do this.

 
{quote}But you should test "WHERE deptno = SUBSTRING(? FROM 2 FOR 4)" and make 
sure that it gives a useful error
{quote}
With `sql("select * from emp where deptno = \^substring(? from 0)\^")`, the 
above implementation would yield
{noformat}
org.apache.calcite.sql.validate.SqlValidatorException: Cannot apply 'SUBSTRING' 
to arguments of type 'SUBSTRING(<INTEGER> FROM <INTEGER>)'. Supported form(s): 
'SUBSTRING(<CHAR> FROM <INTEGER>)'
'SUBSTRING(<CHAR> FROM <INTEGER> FOR <INTEGER>)'
'SUBSTRING(<VARCHAR> FROM <INTEGER>)'
'SUBSTRING(<VARCHAR> FROM <INTEGER> FOR <INTEGER>)'
'SUBSTRING(<BINARY> FROM <INTEGER>)'
'SUBSTRING(<BINARY> FROM <INTEGER> FOR <INTEGER>)'
'SUBSTRING(<VARBINARY> FROM <INTEGER>)'
'SUBSTRING(<VARBINARY> FROM <INTEGER> FOR <INTEGER>)'{noformat}
The first argument in the signature has been silently swapped to `INTEGER` 
because of the return type deduction. The error message is not very clear to 
me. Is there a good way to supply the extra context to the validator exception 
that a deduction of the type of `?` based on the return type has been applied? 
or do you think this is good enough for an error message.


was (Author: lllouiskuang):
Thanks for the detailed feedbacks!

{quote}but I suspect that there are several other functions that are similar. I 
wonder whether it is possible for OperandTypeChecker to notice that all 
overloads give the same type to operand i and therefore deduce the type of 
operand i. By which I mean, can we make OperandTypeChecker implement 
OperandTypeInference in some cases?
{quote}
You are right, there are at least a few functions that are in similar 
situation: POSITION and OVERLAY. Their signatures are a bit different though 
{noformat}
POSITION(STRING, STRING)
POSITION(STRING, STRING, INTEGER)
POSITION(BINARY, BINARY)
POSITION(BINARY, BINARY, INTEGER)

OVERLAY(STRING, STRING, INTEGER)
OVERLAY(STRING, STRING, INTEGER, INTEGER)
OVERLAY(BINARY, BINARY, INTEGER)
OVERLAY(BINARY, BINARY, INTEGER, INTEGER){noformat}
 Not sure if I understand what you mean exactly but here is my interpretation:
{code:java}
public static final SqlOperandTypeChecker 
BINARY_OR_STRING_INTEGER_OPTIONAL_INTEGER = new CompositeOperandTypeChecker(
      CompositeOperandTypeChecker.Composition.SEQUENCE,
      ImmutableList.of(
          OperandTypes.or(BINARY, STRING),
          family(ImmutableList.of(SqlTypeFamily.INTEGER), i -> i == 1)),
      null,
      null
  ) {
    @Override public @Nullable SqlOperandTypeInference typeInference() {
      return (callBinding, returnType, operandTypes) -> {
        operandTypes[0] = returnType;
        RelDataTypeFactory typeFactory = callBinding.getTypeFactory();
        operandTypes[1] = typeFactory.createSqlType(SqlTypeName.INTEGER);
        if (operandTypes.length == 3) {
          operandTypes[2] = typeFactory.createSqlType(SqlTypeName.INTEGER);
        }
      };
    }
  };
{code}
This way the type inference can be obtained from the type checker. However, 
this does not do as much as the checker at 
SqlSubStringFunction#checkOperandTypes, which allow implicit type cast, e.g., 
from STRING to INTEGER. Because of this, I think for this JIRA making the 
modification inplace as you suggest `inferOperandTypes(SqlCallBinding 
callBinding, RelDataType returnType, RelDataType[] operandTypes)` will be 
better.

 
{quote}Your test should check the inferred types of the parameters, not just 
call .ok()
{quote}
Is there an example test that does this that I can reference? The `Sql` fluent 
testing API does not seem to expose a convenient API to do this.

 
{quote}But you should test "WHERE deptno = SUBSTRING(? FROM 2 FOR 4)" and make 
sure that it gives a useful error
{quote}
Regarding the error that is given from `sql("select * from emp where deptno = 
^substring(? from 0)^")`, the above implementation would yield
{noformat}
org.apache.calcite.sql.validate.SqlValidatorException: Cannot apply 'SUBSTRING' 
to arguments of type 'SUBSTRING(<INTEGER> FROM <INTEGER>)'. Supported form(s): 
'SUBSTRING(<CHAR> FROM <INTEGER>)'
'SUBSTRING(<CHAR> FROM <INTEGER> FOR <INTEGER>)'
'SUBSTRING(<VARCHAR> FROM <INTEGER>)'
'SUBSTRING(<VARCHAR> FROM <INTEGER> FOR <INTEGER>)'
'SUBSTRING(<BINARY> FROM <INTEGER>)'
'SUBSTRING(<BINARY> FROM <INTEGER> FOR <INTEGER>)'
'SUBSTRING(<VARBINARY> FROM <INTEGER>)'
'SUBSTRING(<VARBINARY> FROM <INTEGER> FOR <INTEGER>)'{noformat}
The first argument in the signature has been silently swapped to `INTEGER` 
because of the return type deduction. The error message is not very clear to 
me. Is there a good way to supply the extra context to the validator exception 
that a deduction of the type of `?` based on the return type has been applied? 
or do you think this is good enough for an error message.

> Illegal use of dynamic parameter when use preparestatement
> ----------------------------------------------------------
>
>                 Key: CALCITE-4347
>                 URL: https://issues.apache.org/jira/browse/CALCITE-4347
>             Project: Calcite
>          Issue Type: Bug
>          Components: core
>            Reporter: yangzhang
>            Priority: Major
>              Labels: pull-request-available
>          Time Spent: 0.5h
>  Remaining Estimate: 0h
>
> hi , when query with preparestatement, validator throws exception.
> here is 2 sqls:
> 1、select LSTG_FORMAT_NAME
> from kylin_sales
> where LSTG_FORMAT_NAME=substring(?,1,6)
>  
> 2、select LSTG_FORMAT_NAME
> from kylin_sales
> where LSTG_FORMAT_NAME=substring(?,?,6)
>  
> trace is:
> Caused by: org.apache.calcite.runtime.CalciteContextException: At line 4, 
> column 34: Illegal use of dynamic parameter
> at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
> at 
> sun.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:62)
> at 
> sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45)
> at java.lang.reflect.Constructor.newInstance(Constructor.java:423)
> at org.apache.calcite.runtime.Resources$ExInstWithCause.ex(Resources.java:463)
> at org.apache.calcite.sql.SqlUtil.newContextException(SqlUtil.java:803)
> at org.apache.calcite.sql.SqlUtil.newContextException(SqlUtil.java:788)
> at 
> org.apache.calcite.sql.validate.SqlValidatorImpl.newValidationError(SqlValidatorImpl.java:4708)
> at 
> org.apache.calcite.sql.validate.SqlValidatorImpl.inferUnknownTypes(SqlValidatorImpl.java:1710)
> at 
> org.apache.calcite.sql.validate.SqlValidatorImpl.inferUnknownTypes(SqlValidatorImpl.java:1785)
> at 
> org.apache.calcite.sql.validate.SqlValidatorImpl.inferUnknownTypes(SqlValidatorImpl.java:1785)
> at 
> org.apache.calcite.sql.validate.SqlValidatorImpl.validateWhereOrOn(SqlValidatorImpl.java:3906)
> at 
> org.apache.calcite.sql.validate.SqlValidatorImpl.validateWhereClause(SqlValidatorImpl.java:3898)
> at 
> org.apache.calcite.sql.validate.SqlValidatorImpl.validateSelect(SqlValidatorImpl.java:3224)
> at 
> org.apache.calcite.sql.validate.SelectNamespace.validateImpl(SelectNamespace.java:60)
> at 
> org.apache.calcite.sql.validate.AbstractNamespace.validate(AbstractNamespace.java:84)
> at 
> org.apache.calcite.sql.validate.SqlValidatorImpl.validateNamespace(SqlValidatorImpl.java:949)
> at 
> org.apache.calcite.sql.validate.SqlValidatorImpl.validateQuery(SqlValidatorImpl.java:930)
> at org.apache.calcite.sql.SqlSelect.validate(SqlSelect.java:226)
> at 
> org.apache.calcite.sql.validate.SqlValidatorImpl.validateScopedExpression(SqlValidatorImpl.java:905)
> at 
> org.apache.calcite.sql.validate.SqlValidatorImpl.validate(SqlValidatorImpl.java:615)
> at 
> org.apache.calcite.sql2rel.SqlToRelConverter.convertQuery(SqlToRelConverter.java:576)
> at org.apache.calcite.prepare.Prepare.prepareSql(Prepare.java:269)
> at org.apache.calcite.prepare.Prepare.prepareSql(Prepare.java:235)
> at 
> org.apache.calcite.prepare.CalcitePrepareImpl.prepare2_(CalcitePrepareImpl.java:804)
> at 
> org.apache.calcite.prepare.CalcitePrepareImpl.prepare_(CalcitePrepareImpl.java:663)
> at 
> org.apache.calcite.prepare.CalcitePrepareImpl.prepareSql(CalcitePrepareImpl.java:626)
> at 
> org.apache.calcite.jdbc.CalciteConnectionImpl.parseQuery(CalciteConnectionImpl.java:232)
> at 
> org.apache.calcite.jdbc.CalciteConnectionImpl.prepareStatement_(CalciteConnectionImpl.java:214)
>  



--
This message was sent by Atlassian Jira
(v8.3.4#803005)

Reply via email to