@Nullable
  public static Object getValueFromLiteral(ValueLiteralExpression expr) {
    LogicalType logicalType = expr.getOutputDataType().getLogicalType();
    switch (logicalType.getTypeRoot()) {
      case TIMESTAMP_WITHOUT_TIME_ZONE:
        return expr.getValueAs(LocalDateTime.class)
            .map(ldt -> ldt.toInstant(ZoneOffset.UTC).toEpochMilli())
            .orElse(null);
      case TIME_WITHOUT_TIME_ZONE:
        return expr.getValueAs(LocalTime.class)
            .map(lt -> lt.get(ChronoField.MILLI_OF_DAY))
            .orElse(null);
      case DATE:
        return expr.getValueAs(LocalDate.class)
            .map(date -> (int) date.toEpochDay())
            .orElse(null);
      // NOTE: All integral types of size less than Int are encoded as Ints in MT
      case BOOLEAN:
        return expr.getValueAs(Boolean.class).orElse(null);
      case TINYINT:
        return expr.getValueAs(Byte.class).orElse(null);
      case SMALLINT:
        return expr.getValueAs(Short.class).orElse(null);
      case INTEGER:
        return expr.getValueAs(Integer.class).orElse(null);
      case BIGINT:
        return expr.getValueAs(Long.class).orElse(null);
      case FLOAT:
        return expr.getValueAs(Float.class).orElse(null);
      case DOUBLE:
        return expr.getValueAs(Double.class).orElse(null);
      case BINARY:
      case VARBINARY:
        return expr.getValueAs(byte[].class).orElse(null);
      case CHAR:
      case VARCHAR:
        return expr.getValueAs(String.class).orElse(null);
      case DECIMAL:
        return expr.getValueAs(BigDecimal.class).orElse(null);
      default:
        throw new UnsupportedOperationException("Unsupported type: " + logicalType);
    }
  }
@Test
  void getValueFromLiteralForNull() {
    List<RowType.RowField> fields = ((RowType) ROW_DATA_TYPE.getLogicalType()).getFields();
    List<DataType> dataTypes = ROW_DATA_TYPE.getChildren();
    CallExpression callExpression;
    for (int i = 0; i < fields.size(); i++) {
      // 1. Build all types
      callExpression = new CallExpression(
          BuiltInFunctionDefinitions.IS_NOT_NULL,
          Arrays.asList(new FieldReferenceExpression(fields.get(i).getName(),
              dataTypes.get(i),
              2,
              2), new ValueLiteralExpression(null, dataTypes.get(i))),
          DataTypes.BOOLEAN());
      List<Expression> childExprs = callExpression.getChildren();

      // 2. Parse each type
      boolean hasNullLiteral =
          childExprs.stream().anyMatch(e ->
              e instanceof ValueLiteralExpression
                  && ExpressionUtils.getValueFromLiteral((ValueLiteralExpression) e) == null);
      assertTrue(hasNullLiteral);
    }

  }