Skip to content

[CALCITE-3414] In calcite-core, use RexToLixTranslator.convert for type conversion code generation uniformly#1507

Closed
DonnyZone wants to merge 1 commit intoapache:masterfrom
DonnyZone:TypeCast
Closed

[CALCITE-3414] In calcite-core, use RexToLixTranslator.convert for type conversion code generation uniformly#1507
DonnyZone wants to merge 1 commit intoapache:masterfrom
DonnyZone:TypeCast

Conversation

@DonnyZone
Copy link
Copy Markdown
Contributor

As illustrated in CALCITE-3414, we try to integrate Types.castIfNecessary's logic into RexToLixTranslator.cast.

RexImpTable.NULL_EXPR,
Expressions.call(operand, "toString"));
} catch (Exception e) {
// No "toString()" method, fall through to (String)x
Copy link
Copy Markdown
Contributor

@yanlin-Lynn yanlin-Lynn Oct 17, 2019

Choose a reason for hiding this comment

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

can this ever happen for no toString method?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Theoretically, a Class should contain toString method. However, there are some special cases in Calcite.
For example (QuidemTest#winagg.iq), if the operand is SqlFunctions.lesser(String.class, String.class), its type will be java.lang.Comparable, which has no toString method, leading to Exception in Expressions.call(operand, "toString").
Because for String.class, the method matched is:

  public static <T extends Comparable<T>> T lesser(T b0, T b1) {
    return b0 == null || b0.compareTo(b1) > 0 ? b1 : b0;
  }

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

OK, got that.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Although the operand type class of SqlFunctions.lesser(String.class, String.class) does not implement toString method, the object should have implement the toString method, because the java Object already has that, did you mean the operand object is not a java Object ?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Yes, the code ...lesser(..).toString will be ok in runtime. It just throws the exception during codegen in Expressions.call(operand, "toString")) through reflection. (String)lesser(...) skips this step.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Do you think we can make an effort for Expressions.call(operand, "toString")) to get the right toString method ? Even though it uses the Java reflection.

Copy link
Copy Markdown
Contributor Author

@DonnyZone DonnyZone Oct 22, 2019

Choose a reason for hiding this comment

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

I investigate this problem, it may be complicated to implement this feature from beginning. However, relying on Guava's TypeResolver, we can easily infer a method's return type according to its argument types, as demonstrated below.
Is it necessary to open another JIRA for this feature?

public static Type inferRuntimeReturnType(final Method method,
      final List<Expression> arguments) {
    final Type returnType = method.getReturnType();
    final Type genericReturnType = method.getGenericReturnType();
    if (returnType == genericReturnType) {
      return returnType;
    }
    Type runtimeReturnType = genericReturnType;
    final Type[] parameterTypes = method.getParameterTypes();
    final Type[] genericParameterTypes = method.getGenericParameterTypes();
    for (int i = 0; i < parameterTypes.length; i++) {
      if (parameterTypes[i] != genericParameterTypes[i]) {
        final Type argType = arguments.get(i).getType();
        final TypeResolver resolver =
            new TypeResolver().where(genericParameterTypes[i], argType);
        runtimeReturnType = resolver.resolveType(runtimeReturnType);
      }
    }
    assert runtimeReturnType != genericReturnType;
    return runtimeReturnType;
  }

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

I'm not sure how this code works, compared to this code, i'm more inclined with the try catch way, do we need to refine the specific exception instead of just Exception ?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Ok, let me narrow it down to RuntimeException.

  public static MethodCallExpression call(Expression target, String methodName,
      Iterable<? extends Expression> arguments) {
    Method method;
    try {
      //noinspection unchecked
      method = Types.toClass(target.getType())
          .getMethod(methodName, Types.toClassArray(arguments));
    } catch (NoSuchMethodException e) {
      throw new RuntimeException("while resolving method '" + methodName
          + "' in class " + target.getType(), e);
    }
    return call(target, method, arguments);
  }

@danny0405 danny0405 changed the title [CALCITE-3414] Unify Expression's type cast and conversion as a robust one [CALCITE-3414] In calcite-core, use RexToLixTranslator.convert for type conversion code generation uniformly Oct 21, 2019
if (fromType == toBox.primitiveClass) {
return Expressions.box(operand, toBox);
}
return Expressions.box(
Copy link
Copy Markdown
Contributor

@danny0405 danny0405 Oct 21, 2019

Choose a reason for hiding this comment

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

Can we give more explain doc here, i understand this code is for the case Integer.valueOf((int) longVal). But i still need to go through the code for a while why this code snippet is needed.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Sure, I will add doc for explanation and example here.

"toString"));
Expression result;
try {
// Try to call "toString()" method
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Instead of try ... catch ... clause, can we use the java reflection to detect if the class has "toString" method first ?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Do you have any advices on efficient code:)? I put some thought to this problem, but failed to get an elegant way, it seems that we should enumerate all methods to check their method names, argument types and return type.
Meanwhile, I found that in RexToLixTranslator, there is already a static function to find method in a class through reflection. But we still need to catch exception. Expressions.call also adopts the similar implementation.

  private static Method findMethod(
      Class<?> clazz, String name, Class... parameterTypes) {
    try {
      return clazz.getMethod(name, parameterTypes);
    } catch (NoSuchMethodException e) {
      throw new RuntimeException(e);
    }
  }

return operand;
}
// E.g. from "Short" to "int".
// Generate "x.intValue()".
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Can we define a new method named needsCast(Type tpe1, Type tpe2) in Types ? Then Types.castIfNecessary and RexToLixTranslator.convert can both reuse this method. The method should include the content:

    if (fromType.equals(toType)) {
      return operand;
    }
    if (toType instanceof Types.RecordType) {
      // We can't extract Class from RecordType since mapping Java Class might not generated yet.
      return operand;
    }
    if (Types.isAssignableFrom(toType, fromType)) {
      return operand;
    }

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Good idea, I will put them into Types for reuse.

…pe conversion code generation uniformly (DonnyZone)
@danny0405 danny0405 added the LGTM-will-merge-soon Overall PR looks OK. Only minor things left. label Oct 23, 2019
@danny0405 danny0405 closed this in 429c0d0 Oct 23, 2019
XuQianJin-Stars pushed a commit to XuQianJin-Stars/calcite that referenced this pull request Nov 17, 2019
…pe conversion code generation uniformly (DonnyZone)

close apache#1507
XuQianJin-Stars pushed a commit to XuQianJin-Stars/calcite that referenced this pull request Nov 20, 2019
…pe conversion code generation uniformly (DonnyZone)

close apache#1507
XuQianJin-Stars pushed a commit to XuQianJin-Stars/calcite that referenced this pull request Nov 20, 2019
…pe conversion code generation uniformly (DonnyZone)

close apache#1507
wangxlong pushed a commit to wangxlong/calcite that referenced this pull request Feb 13, 2020
…pe conversion code generation uniformly (DonnyZone)

close apache#1507
jamesstarr pushed a commit to jamesstarr/calcite that referenced this pull request Aug 28, 2025
…pe conversion code generation uniformly (DonnyZone)

close apache#1507

Change-Id: I5b13aebfa365e57146598213bbc0ce6dd46701a7
jamesstarr pushed a commit to jamesstarr/calcite that referenced this pull request Mar 16, 2026
…pe conversion code generation uniformly (DonnyZone)

close apache#1507

Change-Id: I5b13aebfa365e57146598213bbc0ce6dd46701a7
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

LGTM-will-merge-soon Overall PR looks OK. Only minor things left.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants