|
16 | 16 |
|
17 | 17 | package org.apache.calcite.rel.externalize; |
18 | 18 |
|
19 | | -import com.fasterxml.jackson.databind.type.TypeFactory; |
20 | 19 | import com.google.common.collect.ImmutableList; |
21 | 20 | import com.google.common.collect.ImmutableSet; |
22 | 21 | import com.mapd.calcite.parser.MapDSqlOperatorTable; |
23 | 22 |
|
24 | | -import org.apache.calcite.avatica.AvaticaUtils; |
25 | 23 | import org.apache.calcite.avatica.util.TimeUnitRange; |
26 | 24 | import org.apache.calcite.plan.RelOptCluster; |
27 | | -import org.apache.calcite.plan.RelTraitSet; |
28 | 25 | import org.apache.calcite.rel.RelCollation; |
29 | 26 | import org.apache.calcite.rel.RelCollationImpl; |
30 | 27 | import org.apache.calcite.rel.RelCollations; |
|
55 | 52 | import org.apache.calcite.sql.JoinType; |
56 | 53 | import org.apache.calcite.sql.SqlAggFunction; |
57 | 54 | import org.apache.calcite.sql.SqlFunction; |
58 | | -import org.apache.calcite.sql.SqlIdentifier; |
59 | 55 | import org.apache.calcite.sql.SqlKind; |
60 | 56 | import org.apache.calcite.sql.SqlOperator; |
61 | | -import org.apache.calcite.sql.SqlOperatorTable; |
62 | 57 | import org.apache.calcite.sql.SqlSyntax; |
63 | 58 | import org.apache.calcite.sql.fun.SqlStdOperatorTable; |
64 | 59 | import org.apache.calcite.sql.type.SqlTypeName; |
65 | 60 | import org.apache.calcite.util.ImmutableBitSet; |
66 | 61 | import org.apache.calcite.util.JsonBuilder; |
| 62 | +import org.apache.calcite.util.TimestampString; |
67 | 63 | import org.apache.calcite.util.Util; |
68 | 64 |
|
69 | 65 | import java.lang.reflect.Constructor; |
70 | 66 | import java.lang.reflect.InvocationTargetException; |
71 | 67 | import java.math.BigDecimal; |
| 68 | +import java.time.LocalDateTime; |
| 69 | +import java.time.ZoneOffset; |
72 | 70 | import java.util.ArrayList; |
73 | 71 | import java.util.Collections; |
74 | 72 | import java.util.HashMap; |
|
78 | 76 | import java.util.Set; |
79 | 77 | import java.util.stream.Collectors; |
80 | 78 |
|
| 79 | +import static java.lang.Integer.parseUnsignedInt; |
| 80 | + |
81 | 81 | /** |
82 | 82 | * Utilities for converting {@link org.apache.calcite.rel.RelNode} into JSON |
83 | 83 | * format. |
@@ -345,8 +345,16 @@ private Object toJson(RexNode node) { |
345 | 345 | return map; |
346 | 346 | case LITERAL: |
347 | 347 | final RexLiteral literal = (RexLiteral) node; |
348 | | - final Object value2 = literal.getValue2(); |
| 348 | + final Object value2; |
349 | 349 | map = jsonBuilder.map(); |
| 350 | + |
| 351 | + if (literal.getTypeName() == SqlTypeName.TIMESTAMP) { |
| 352 | + value2 = timestampStringToLong(literal.getValueAs(TimestampString.class), |
| 353 | + literal.getType().getPrecision()); |
| 354 | + } else { |
| 355 | + value2 = literal.getValue2(); |
| 356 | + } |
| 357 | + |
350 | 358 | if (value2 instanceof TimeUnitRange) { |
351 | 359 | map.put("literal", value2.toString()); |
352 | 360 | } else { |
@@ -529,16 +537,23 @@ RexNode toRex(RelInput relInput, Object o) { |
529 | 537 | if (literal instanceof Number) { |
530 | 538 | final SqlTypeName targetTypeName = |
531 | 539 | Util.enumVal(SqlTypeName.class, (String) map.get("target_type")); |
| 540 | + final RelDataTypeFactory typeFactory = cluster.getTypeFactory(); |
| 541 | + |
| 542 | + if (targetTypeName == SqlTypeName.TIMESTAMP) { |
| 543 | + final long value = ((Number) literal).longValue(); |
| 544 | + final int precision = ((Number) map.get("precision")).intValue(); |
| 545 | + final TimestampString ts = longToTimestampString(value, precision); |
| 546 | + return rexBuilder.makeLiteral(ts, |
| 547 | + typeFactory.createSqlType(targetTypeName, precision), false); |
| 548 | + } |
| 549 | + |
532 | 550 | final long scale = ((Number) map.get("scale")).longValue(); |
533 | | - final long precision = ((Number) map.get("precision")).longValue(); |
534 | 551 | final long typeScale = ((Number) map.get("type_scale")).longValue(); |
535 | | - final long typePrecision = ((Number) map.get("type_precision")).longValue(); |
536 | | - RelDataTypeFactory typeFactory = cluster.getTypeFactory(); |
537 | | - |
538 | | - BigDecimal value = |
| 552 | + final BigDecimal value = |
539 | 553 | BigDecimal.valueOf(((Number) literal).longValue(), (int) scale); |
540 | 554 |
|
541 | 555 | if (typeScale != 0 && typeScale != -2147483648) { |
| 556 | + final long typePrecision = ((Number) map.get("type_precision")).longValue(); |
542 | 557 | return rexBuilder.makeLiteral(value, |
543 | 558 | typeFactory.createSqlType( |
544 | 559 | SqlTypeName.DECIMAL, (int) typePrecision, (int) typeScale), |
@@ -568,6 +583,73 @@ RexNode toRex(RelInput relInput, Object o) { |
568 | 583 | } |
569 | 584 | } |
570 | 585 |
|
| 586 | + static TimestampString longToTimestampString(long value, int precision) { |
| 587 | + assert (precision == 0) || (precision == 3) || (precision == 6) || (precision == 9); |
| 588 | + |
| 589 | + long pow = (long) Math.pow(10, precision); |
| 590 | + long seconds = value / pow; |
| 591 | + LocalDateTime dt = LocalDateTime.ofEpochSecond(seconds, 0, ZoneOffset.UTC); |
| 592 | + int year = dt.getYear(); |
| 593 | + assert year < 10000; |
| 594 | + int month = dt.getMonthValue(); |
| 595 | + int day = dt.getDayOfMonth(); |
| 596 | + int hour = dt.getHour(); |
| 597 | + int minute = dt.getMinute(); |
| 598 | + int second = dt.getSecond(); |
| 599 | + |
| 600 | + long fraction = value - (seconds * pow); |
| 601 | + for (; (precision > 0) && ((fraction % 10) == 0); precision--, fraction /= 10) ; // Cut trailing zeros |
| 602 | + |
| 603 | + char[] ts = new char[precision == 0 ? 19 : 20 + precision]; |
| 604 | + ts[0] = (char) ('0' + (year / 1000)); |
| 605 | + ts[1] = (char) ('0' + ((year / 100) % 10)); |
| 606 | + ts[2] = (char) ('0' + ((year / 10) % 10)); |
| 607 | + ts[3] = (char) ('0' + (year % 10)); |
| 608 | + ts[4] = '-'; |
| 609 | + ts[5] = (char) ('0' + (month / 10)); |
| 610 | + ts[6] = (char) ('0' + (month % 10)); |
| 611 | + ts[7] = '-'; |
| 612 | + ts[8] = (char) ('0' + (day / 10)); |
| 613 | + ts[9] = (char) ('0' + (day % 10)); |
| 614 | + ts[10] = ' '; |
| 615 | + ts[11] = (char) ('0' + (hour / 10)); |
| 616 | + ts[12] = (char) ('0' + (hour % 10)); |
| 617 | + ts[13] = ':'; |
| 618 | + ts[14] = (char) ('0' + (minute / 10)); |
| 619 | + ts[15] = (char) ('0' + (minute % 10)); |
| 620 | + ts[16] = ':'; |
| 621 | + ts[17] = (char) ('0' + (second / 10)); |
| 622 | + ts[18] = (char) ('0' + (second % 10)); |
| 623 | + |
| 624 | + if (precision > 0) { |
| 625 | + ts[19] = '.'; |
| 626 | + do { |
| 627 | + ts[19 + precision] = (char) ('0' + (fraction % 10)); |
| 628 | + precision--; |
| 629 | + fraction /= 10; |
| 630 | + } while (precision > 0); |
| 631 | + } |
| 632 | + |
| 633 | + return new TimestampString(new String(ts)); |
| 634 | + } |
| 635 | + |
| 636 | + static long timestampStringToLong(TimestampString ts, int precision) { |
| 637 | + String v = ts.toString(); |
| 638 | + int year = parseUnsignedInt(v, 0, 4, 10); |
| 639 | + int month = parseUnsignedInt(v, 5, 7, 10); |
| 640 | + int day = parseUnsignedInt(v, 8, 10, 10); |
| 641 | + int hour = parseUnsignedInt(v, 11, 13, 10); |
| 642 | + int minute = parseUnsignedInt(v, 14, 16, 10); |
| 643 | + int second = parseUnsignedInt(v, 17, 19, 10); |
| 644 | + long seconds = LocalDateTime.of(year, month, day, hour, minute, second) |
| 645 | + .toInstant(ZoneOffset.UTC).getEpochSecond(); |
| 646 | + int pow = (int) Math.pow(10, precision); |
| 647 | + int len = v.length(); |
| 648 | + if (len == 19) return seconds * pow; |
| 649 | + int fraction = parseUnsignedInt(v, 20, len, 10); |
| 650 | + return seconds * pow + fraction * (pow / ((int) Math.pow(10, len - 20))); |
| 651 | + } |
| 652 | + |
571 | 653 | private List<RexNode> toRexList(RelInput relInput, List operands) { |
572 | 654 | final List<RexNode> list = new ArrayList<RexNode>(); |
573 | 655 | for (Object operand : operands) { |
|
0 commit comments