absl::StatusOr IntervalValue::ParseFromString()

in sql_utils/public/interval_value.cc [574:733]


absl::StatusOr<IntervalValue> IntervalValue::ParseFromString(
    absl::string_view input, functions::DateTimestampPart from,
    functions::DateTimestampPart to) {
  // Sign (empty, '-' or '+') for months and nano fields. There is no special
  // treatment for sign of days, because days are standalone number and are
  // matched and parsed by RE2 as part of ([-+]?\d+) group.
  std::string sign_months;
  std::string sign_nanos;
  // All the datetime fields
  int64_t years = 0;
  int64_t months = 0;
  int64_t days = 0;
  int64_t hours = 0;
  int64_t minutes = 0;
  int64_t seconds = 0;
  // Fractions of seconds
  absl::string_view fraction_digits;
  // Indication whether parsing succeeded.
  bool parsed = false;

  // Seconds are special, because they can have optional fractions
  if (to == functions::SECOND && input.find('.') != input.npos) {
    switch (from) {
      case functions::YEAR:
        parsed = RE2::FullMatch(input, *kREYearToSecondFractions, &sign_months,
                                &years, &months, &days, &sign_nanos, &hours,
                                &minutes, &seconds, &fraction_digits);
        break;
      case functions::MONTH:
        parsed = RE2::FullMatch(input, *kREMonthToSecondFractions, &sign_months,
                                &months, &days, &sign_nanos, &hours, &minutes,
                                &seconds, &fraction_digits);
        break;
      case functions::DAY:
        parsed =
            RE2::FullMatch(input, *kREDayToSecondFractions, &days, &sign_nanos,
                           &hours, &minutes, &seconds, &fraction_digits);
        break;
      case functions::HOUR:
        parsed = RE2::FullMatch(input, *kREHourToSecondFractions, &sign_nanos,
                                &hours, &minutes, &seconds, &fraction_digits);
        break;
      case functions::MINUTE:
        parsed = RE2::FullMatch(input, *kREMinuteToSecondFractions, &sign_nanos,
                                &minutes, &seconds, &fraction_digits);
        break;

      default:
        return MakeEvalError()
               << "Invalid interval datetime fields: "
               << functions::DateTimestampPart_Name(from) << " TO "
               << functions::DateTimestampPart_Name(to);
    }
  } else {
#define DATETIME_PARTS(from, to) (from << 16 | to)
    switch (DATETIME_PARTS(from, to)) {
      case DATETIME_PARTS(functions::YEAR, functions::MONTH):
        parsed = RE2::FullMatch(input, *kREYearToMonth, &sign_months, &years,
                                &months);
        break;
      case DATETIME_PARTS(functions::YEAR, functions::DAY):
        parsed = RE2::FullMatch(input, *kREYearToDay, &sign_months, &years,
                                &months, &days);
        break;
      case DATETIME_PARTS(functions::YEAR, functions::HOUR):
        parsed = RE2::FullMatch(input, *kREYearToHour, &sign_months, &years,
                                &months, &days, &sign_nanos, &hours);
        break;
      case DATETIME_PARTS(functions::YEAR, functions::MINUTE):
        parsed = RE2::FullMatch(input, *kREYearToMinute, &sign_months, &years,
                                &months, &days, &sign_nanos, &hours, &minutes);
        break;
      case DATETIME_PARTS(functions::YEAR, functions::SECOND):
        parsed = RE2::FullMatch(input, *kREYearToSecond, &sign_months, &years,
                                &months, &days, &sign_nanos, &hours, &minutes,
                                &seconds);
        break;
      case DATETIME_PARTS(functions::MONTH, functions::DAY):
        parsed =
            RE2::FullMatch(input, *kREMonthToDay, &sign_months, &months, &days);
        break;
      case DATETIME_PARTS(functions::MONTH, functions::HOUR):
        parsed = RE2::FullMatch(input, *kREMonthToHour, &sign_months, &months,
                                &days, &sign_nanos, &hours);
        break;
      case DATETIME_PARTS(functions::MONTH, functions::MINUTE):
        parsed = RE2::FullMatch(input, *kREMonthToMinute, &sign_months, &months,
                                &days, &sign_nanos, &hours, &minutes);
        break;
      case DATETIME_PARTS(functions::MONTH, functions::SECOND):
        parsed = RE2::FullMatch(input, *kREMonthToSecond, &sign_months, &months,
                                &days, &sign_nanos, &hours, &minutes, &seconds);
        break;
      case DATETIME_PARTS(functions::DAY, functions::HOUR):
        parsed =
            RE2::FullMatch(input, *kREDayToHour, &days, &sign_nanos, &hours);
        break;
      case DATETIME_PARTS(functions::DAY, functions::MINUTE):
        parsed = RE2::FullMatch(input, *kREDayToMinute, &days, &sign_nanos,
                                &hours, &minutes);
        break;
      case DATETIME_PARTS(functions::DAY, functions::SECOND):
        parsed = RE2::FullMatch(input, *kREDayToSecond, &days, &sign_nanos,
                                &hours, &minutes, &seconds);
        break;
      case DATETIME_PARTS(functions::HOUR, functions::MINUTE):
        parsed = RE2::FullMatch(input, *kREHourToMinute, &sign_nanos, &hours,
                                &minutes);
        break;
      case DATETIME_PARTS(functions::HOUR, functions::SECOND):
        parsed = RE2::FullMatch(input, *kREHourToSecond, &sign_nanos, &hours,
                                &minutes, &seconds);
        break;
      case DATETIME_PARTS(functions::MINUTE, functions::SECOND):
        parsed = RE2::FullMatch(input, *kREMinuteToSecond, &sign_nanos,
                                &minutes, &seconds);
        break;

      default:
        return MakeEvalError()
               << "Invalid interval datetime fields: "
               << functions::DateTimestampPart_Name(from) << " TO "
               << functions::DateTimestampPart_Name(to);
    }
#undef DATETIME_PARTS
  }

  if (!parsed) {
    return MakeIntervalParsingError(input);
  }

  absl::Status status;
  int64_t years_as_months;
  if (!functions::Multiply(IntervalValue::kMonthsInYear, years,
                           &years_as_months, &status)) {
    return status;
  }
  if (!functions::Add(years_as_months, months, &months, &status)) {
    return status;
  }
  bool negative_months = !sign_months.empty() && sign_months[0] == '-';
  if (negative_months) {
    months = -months;
  }

  // Result always fits into int128.
  __int128 nanos = IntervalValue::kNanosInHour * hours +
                   IntervalValue::kNanosInMinute * minutes +
                   IntervalValue::kNanosInSecond * seconds;
  if (!fraction_digits.empty()) {
    SQL_ASSIGN_OR_RETURN(int64_t nano_fractions,
                     NanosFromFractionDigits(input, fraction_digits));
    nanos += nano_fractions;
  }
  bool negative_nanos = !sign_nanos.empty() && sign_nanos[0] == '-';
  if (negative_nanos) {
    nanos = -nanos;
  }
  return IntervalValue::FromMonthsDaysNanos(months, days, nanos);
}