github.com/cockroachdb/cockroach@v20.2.0-alpha.1+incompatible/pkg/sql/sem/tree/interval.go (about)

     1  // Copyright 2016 The Cockroach Authors.
     2  //
     3  // Use of this software is governed by the Business Source License
     4  // included in the file licenses/BSL.txt.
     5  //
     6  // As of the Change Date specified in that file, in accordance with
     7  // the Business Source License, use of this software will be governed
     8  // by the Apache License, Version 2.0, included in the file
     9  // licenses/APL.txt.
    10  
    11  package tree
    12  
    13  import (
    14  	"math"
    15  	"strconv"
    16  	"strings"
    17  	"time"
    18  
    19  	"github.com/cockroachdb/cockroach/pkg/sql/pgwire/pgcode"
    20  	"github.com/cockroachdb/cockroach/pkg/sql/pgwire/pgerror"
    21  	"github.com/cockroachdb/cockroach/pkg/sql/types"
    22  	"github.com/cockroachdb/cockroach/pkg/util/duration"
    23  	"github.com/cockroachdb/errors"
    24  )
    25  
    26  type intervalLexer struct {
    27  	str    string
    28  	offset int
    29  	err    error
    30  }
    31  
    32  // consumeNum consumes the next decimal number.
    33  // 1st return value is the integer part.
    34  // 2nd return value is whether a decimal part was encountered.
    35  // 3rd return value is the decimal part as a float.
    36  // If the number is negative, both the 1st and 3rd return
    37  // value are negative.
    38  // The decimal value is returned separately from the integer value so
    39  // as to support large integer values which would not fit in a float.
    40  func (l *intervalLexer) consumeNum() (int64, bool, float64) {
    41  	if l.err != nil {
    42  		return 0, false, 0
    43  	}
    44  
    45  	offset := l.offset
    46  
    47  	neg := false
    48  	if l.offset < len(l.str) && l.str[l.offset] == '-' {
    49  		// Remember a leading negative sign. We can't use "intPart < 0"
    50  		// below, because when the input syntax is "-0.xxxx" intPart is 0.
    51  		neg = true
    52  	}
    53  
    54  	// Integer part before the decimal separator.
    55  	intPart := l.consumeInt()
    56  
    57  	var decPart float64
    58  	hasDecimal := false
    59  	if l.offset < len(l.str) && l.str[l.offset] == '.' {
    60  		hasDecimal = true
    61  		start := l.offset
    62  
    63  		// Advance offset to prepare a valid argument to ParseFloat().
    64  		l.offset++
    65  		for ; l.offset < len(l.str) && l.str[l.offset] >= '0' && l.str[l.offset] <= '9'; l.offset++ {
    66  		}
    67  		// Try to convert.
    68  		value, err := strconv.ParseFloat(l.str[start:l.offset], 64)
    69  		if err != nil {
    70  			l.err = pgerror.Newf(
    71  				pgcode.InvalidDatetimeFormat, "interval: %v", err)
    72  			return 0, false, 0
    73  		}
    74  		decPart = value
    75  	}
    76  
    77  	// Ensure we have something.
    78  	if offset == l.offset {
    79  		l.err = pgerror.Newf(
    80  			pgcode.InvalidDatetimeFormat, "interval: missing number at position %d: %q", offset, l.str)
    81  		return 0, false, 0
    82  	}
    83  
    84  	if neg {
    85  		decPart = -decPart
    86  	}
    87  	return intPart, hasDecimal, decPart
    88  }
    89  
    90  // Consumes the next integer.
    91  func (l *intervalLexer) consumeInt() int64 {
    92  	if l.err != nil {
    93  		return 0
    94  	}
    95  
    96  	start := l.offset
    97  
    98  	// Advance offset to prepare a valid argument to ParseInt().
    99  	if l.offset < len(l.str) && (l.str[l.offset] == '-' || l.str[l.offset] == '+') {
   100  		l.offset++
   101  	}
   102  	for ; l.offset < len(l.str) && l.str[l.offset] >= '0' && l.str[l.offset] <= '9'; l.offset++ {
   103  	}
   104  	// Check if we have something like ".X".
   105  	if start == l.offset && len(l.str) > (l.offset+1) && l.str[l.offset] == '.' {
   106  		return 0
   107  	}
   108  
   109  	x, err := strconv.ParseInt(l.str[start:l.offset], 10, 64)
   110  	if err != nil {
   111  		l.err = pgerror.Newf(
   112  			pgcode.InvalidDatetimeFormat, "interval: %v", err)
   113  		return 0
   114  	}
   115  	if start == l.offset {
   116  		l.err = pgerror.Newf(
   117  			pgcode.InvalidDatetimeFormat, "interval: missing number at position %d: %q", start, l.str)
   118  		return 0
   119  	}
   120  	return x
   121  }
   122  
   123  // Consumes the next unit.
   124  func (l *intervalLexer) consumeUnit(skipCharacter byte) string {
   125  	if l.err != nil {
   126  		return ""
   127  	}
   128  
   129  	offset := l.offset
   130  	for ; l.offset < len(l.str); l.offset++ {
   131  		if (l.str[l.offset] >= '0' && l.str[l.offset] <= '9') ||
   132  			l.str[l.offset] == skipCharacter ||
   133  			l.str[l.offset] == '-' {
   134  			break
   135  		}
   136  	}
   137  
   138  	if offset == l.offset {
   139  		l.err = pgerror.Newf(
   140  			pgcode.InvalidDatetimeFormat, "interval: missing unit at position %d: %q", offset, l.str)
   141  		return ""
   142  	}
   143  	return l.str[offset:l.offset]
   144  }
   145  
   146  // Consumes any number of spaces.
   147  func (l *intervalLexer) consumeSpaces() {
   148  	if l.err != nil {
   149  		return
   150  	}
   151  	for ; l.offset < len(l.str) && l.str[l.offset] == ' '; l.offset++ {
   152  	}
   153  }
   154  
   155  // ISO Units.
   156  var isoDateUnitMap = map[string]duration.Duration{
   157  	"D": duration.MakeDuration(0, 1, 0),
   158  	"W": duration.MakeDuration(0, 7, 0),
   159  	"M": duration.MakeDuration(0, 0, 1),
   160  	"Y": duration.MakeDuration(0, 0, 12),
   161  }
   162  
   163  var isoTimeUnitMap = map[string]duration.Duration{
   164  	"S": duration.MakeDuration(time.Second.Nanoseconds(), 0, 0),
   165  	"M": duration.MakeDuration(time.Minute.Nanoseconds(), 0, 0),
   166  	"H": duration.MakeDuration(time.Hour.Nanoseconds(), 0, 0),
   167  }
   168  
   169  const errInvalidSQLDuration = "invalid input syntax for type interval %s"
   170  
   171  type parsedIndex uint8
   172  
   173  const (
   174  	nothingParsed parsedIndex = iota
   175  	hmsParsed
   176  	dayParsed
   177  	yearMonthParsed
   178  )
   179  
   180  func newInvalidSQLDurationError(s string) error {
   181  	return pgerror.Newf(pgcode.InvalidDatetimeFormat, errInvalidSQLDuration, s)
   182  }
   183  
   184  // Parses a SQL standard interval string.
   185  // See the following links for examples:
   186  //  - http://www.postgresql.org/docs/9.1/static/datatype-datetime.html#DATATYPE-INTERVAL-INPUT-EXAMPLES
   187  //  - http://www.ibm.com/support/knowledgecenter/SSGU8G_12.1.0/com.ibm.esqlc.doc/ids_esqlc_0190.htm
   188  func sqlStdToDuration(s string, itm types.IntervalTypeMetadata) (duration.Duration, error) {
   189  	var d duration.Duration
   190  	parts := strings.Fields(s)
   191  	if len(parts) > 3 || len(parts) == 0 {
   192  		return d, newInvalidSQLDurationError(s)
   193  	}
   194  	// Index of which part(s) have been parsed for detecting bad order such as `HH:MM:SS Year-Month`.
   195  	parsedIdx := nothingParsed
   196  	// Both 'Day' and 'Second' can be float, but 'Day Second'::interval is invalid.
   197  	floatParsed := false
   198  	// Parsing backward makes it easy to distinguish 'Day' and 'Second' when encountering a single value.
   199  	//   `1-2 5 9:` and `1-2 5`
   200  	//        |              |
   201  	// day ---+              |
   202  	// second ---------------+
   203  	for i := len(parts) - 1; i >= 0; i-- {
   204  		// Parses leading sign
   205  		part := parts[i]
   206  
   207  		consumeNeg := func(str string) (newStr string, mult int64, ok bool) {
   208  			neg := false
   209  			// Consumes [-+]
   210  			if str != "" {
   211  				c := str[0]
   212  				if c == '-' || c == '+' {
   213  					neg = c == '-'
   214  					str = str[1:]
   215  				}
   216  			}
   217  			if len(str) == 0 {
   218  				return str, 0, false
   219  			}
   220  			if str[0] == '-' || str[0] == '+' {
   221  				return str, 0, false
   222  			}
   223  
   224  			mult = 1
   225  			if neg {
   226  				mult = -1
   227  			}
   228  			return str, mult, true
   229  		}
   230  
   231  		var mult int64
   232  		var ok bool
   233  		if part, mult, ok = consumeNeg(part); !ok {
   234  			return d, newInvalidSQLDurationError(s)
   235  		}
   236  
   237  		if strings.ContainsRune(part, ':') {
   238  			// Try to parse as HH:MM:SS
   239  			if parsedIdx != nothingParsed {
   240  				return d, newInvalidSQLDurationError(s)
   241  			}
   242  			parsedIdx = hmsParsed
   243  			// Colon-separated intervals in Postgres are odd. They have day, hour,
   244  			// minute, or second parts depending on number of fields and if the field
   245  			// is an int or float.
   246  			//
   247  			// Instead of supporting unit changing based on int or float, use the
   248  			// following rules:
   249  			// - If there is a float at the front, it represents D:(<apply below rules).
   250  			// - Two fields is H:M or M:S.fff (unless using MINUTE TO SECOND, then M:S).
   251  			// - Three fields is H:M:S(.fff)?.
   252  			hms := strings.Split(part, ":")
   253  
   254  			// If the first element is blank or is a float, it represents a day.
   255  			// Take it to days, and simplify logic below to a H:M:S scenario.
   256  			firstComponentIsFloat := strings.Contains(hms[0], ".")
   257  			if firstComponentIsFloat || hms[0] == "" {
   258  				// Negatives are not permitted in this format.
   259  				// Also, there must be more units in front.
   260  				if mult != 1 || len(hms) == 1 {
   261  					return d, newInvalidSQLDurationError(s)
   262  				}
   263  				if firstComponentIsFloat {
   264  					days, err := strconv.ParseFloat(hms[0], 64)
   265  					if err != nil {
   266  						return d, newInvalidSQLDurationError(s)
   267  					}
   268  					d = d.Add(duration.MakeDuration(0, 1, 0).MulFloat(days))
   269  				}
   270  
   271  				hms = hms[1:]
   272  				if hms[0], mult, ok = consumeNeg(hms[0]); !ok {
   273  					return d, newInvalidSQLDurationError(s)
   274  				}
   275  			}
   276  
   277  			// Postgres fills in the blanks of all H:M:S as if they were zero.
   278  			for i := 0; i < len(hms); i++ {
   279  				if hms[i] == "" {
   280  					hms[i] = "0"
   281  				}
   282  			}
   283  
   284  			var hours, mins int64
   285  			var secs float64
   286  
   287  			switch len(hms) {
   288  			case 2:
   289  				// If we find a decimal, it must be the m:s.ffffff format
   290  				var err error
   291  				if strings.Contains(hms[1], ".") || itm.DurationField.IsMinuteToSecond() {
   292  					if mins, err = strconv.ParseInt(hms[0], 10, 64); err != nil {
   293  						return d, newInvalidSQLDurationError(s)
   294  					}
   295  					if secs, err = strconv.ParseFloat(hms[1], 64); err != nil {
   296  						return d, newInvalidSQLDurationError(s)
   297  					}
   298  				} else {
   299  					if hours, err = strconv.ParseInt(hms[0], 10, 64); err != nil {
   300  						return d, newInvalidSQLDurationError(s)
   301  					}
   302  					if mins, err = strconv.ParseInt(hms[1], 10, 64); err != nil {
   303  						return d, newInvalidSQLDurationError(s)
   304  					}
   305  				}
   306  			case 3:
   307  				var err error
   308  				if hours, err = strconv.ParseInt(hms[0], 10, 64); err != nil {
   309  					return d, newInvalidSQLDurationError(s)
   310  				}
   311  				if mins, err = strconv.ParseInt(hms[1], 10, 64); err != nil {
   312  					return d, newInvalidSQLDurationError(s)
   313  				}
   314  				if secs, err = strconv.ParseFloat(hms[2], 64); err != nil {
   315  					return d, newInvalidSQLDurationError(s)
   316  				}
   317  			default:
   318  				return d, newInvalidSQLDurationError(s)
   319  			}
   320  
   321  			// None of these units can be negative, as we explicitly strip the negative
   322  			// unit from the very beginning.
   323  			if hours < 0 || mins < 0 || secs < 0 {
   324  				return d, newInvalidSQLDurationError(s)
   325  			}
   326  
   327  			d = d.Add(duration.MakeDuration(time.Hour.Nanoseconds(), 0, 0).Mul(mult * hours))
   328  			d = d.Add(duration.MakeDuration(time.Minute.Nanoseconds(), 0, 0).Mul(mult * mins))
   329  			d = d.Add(duration.MakeDuration(time.Second.Nanoseconds(), 0, 0).MulFloat(float64(mult) * secs))
   330  		} else if strings.ContainsRune(part, '-') {
   331  			// Try to parse as Year-Month.
   332  			if parsedIdx >= yearMonthParsed {
   333  				return d, newInvalidSQLDurationError(s)
   334  			}
   335  			parsedIdx = yearMonthParsed
   336  
   337  			yms := strings.Split(part, "-")
   338  			if len(yms) != 2 {
   339  				return d, newInvalidSQLDurationError(s)
   340  			}
   341  			year, errYear := strconv.Atoi(yms[0])
   342  			var month int
   343  			var errMonth error
   344  			if yms[1] != "" {
   345  				// postgres technically supports decimals here, but it seems to be buggy
   346  				// due to the way it is parsed on their side.
   347  				// e.g. `select interval '0-2.1'` is different to select interval `'0-2.1 01:00'` --
   348  				// it seems the ".1" represents either a day or a constant, which we cannot
   349  				// replicate because we use spaces for divisors, but also seems like something
   350  				// we shouldn't sink too much time into looking at supporting.
   351  				month, errMonth = strconv.Atoi(yms[1])
   352  			}
   353  			if errYear == nil && errMonth == nil {
   354  				delta := duration.MakeDuration(0, 0, 1).Mul(int64(year)*12 + int64(month))
   355  				if mult < 0 {
   356  					d = d.Sub(delta)
   357  				} else {
   358  					d = d.Add(delta)
   359  				}
   360  			} else {
   361  				return d, newInvalidSQLDurationError(s)
   362  			}
   363  		} else if value, err := strconv.ParseFloat(part, 64); err == nil {
   364  			// We cannot specify '<Day> <Second>'::interval as two floats,
   365  			// but we can in the DAY TO HOUR format, where it is '<Day> <Hour>'.
   366  			if floatParsed && !itm.DurationField.IsDayToHour() {
   367  				return d, newInvalidSQLDurationError(s)
   368  			}
   369  			floatParsed = true
   370  			if parsedIdx == nothingParsed {
   371  				// It must be <DurationType> part because nothing has been parsed.
   372  				switch itm.DurationField.DurationType {
   373  				case types.IntervalDurationType_YEAR:
   374  					d = d.Add(duration.MakeDuration(0, 0, 12).MulFloat(value * float64(mult)))
   375  				case types.IntervalDurationType_MONTH:
   376  					d = d.Add(duration.MakeDuration(0, 0, 1).MulFloat(value * float64(mult)))
   377  				case types.IntervalDurationType_DAY:
   378  					d = d.Add(duration.MakeDuration(0, 1, 0).MulFloat(value * float64(mult)))
   379  				case types.IntervalDurationType_HOUR:
   380  					d = d.Add(duration.MakeDuration(time.Hour.Nanoseconds(), 0, 0).MulFloat(value * float64(mult)))
   381  				case types.IntervalDurationType_MINUTE:
   382  					d = d.Add(duration.MakeDuration(time.Minute.Nanoseconds(), 0, 0).MulFloat(value * float64(mult)))
   383  				case types.IntervalDurationType_SECOND, types.IntervalDurationType_UNSET:
   384  					d = d.Add(duration.MakeDuration(time.Second.Nanoseconds(), 0, 0).MulFloat(value * float64(mult)))
   385  				case types.IntervalDurationType_MILLISECOND:
   386  					d = d.Add(duration.MakeDuration(time.Millisecond.Nanoseconds(), 0, 0).MulFloat(value * float64(mult)))
   387  				default:
   388  					return d, errors.AssertionFailedf("unhandled DurationField constant %#v", itm.DurationField)
   389  				}
   390  				parsedIdx = hmsParsed
   391  			} else if parsedIdx == hmsParsed {
   392  				// Day part.
   393  				delta := duration.MakeDuration(0, 1, 0).MulFloat(value)
   394  				if mult < 0 {
   395  					d = d.Sub(delta)
   396  				} else {
   397  					d = d.Add(delta)
   398  				}
   399  				parsedIdx = dayParsed
   400  			} else {
   401  				return d, newInvalidSQLDurationError(s)
   402  			}
   403  		} else {
   404  			return d, newInvalidSQLDurationError(s)
   405  		}
   406  	}
   407  	return d, nil
   408  }
   409  
   410  // Parses an ISO8601 (with designators) string.
   411  // See the following links for examples:
   412  //  - http://www.postgresql.org/docs/9.1/static/datatype-datetime.html#DATATYPE-INTERVAL-INPUT-EXAMPLES
   413  //  - https://en.wikipedia.org/wiki/ISO_8601#Time_intervals
   414  //  - https://en.wikipedia.org/wiki/ISO_8601#Durations
   415  func iso8601ToDuration(s string) (duration.Duration, error) {
   416  	var d duration.Duration
   417  	if len(s) == 0 || s[0] != 'P' {
   418  		return d, newInvalidSQLDurationError(s)
   419  	}
   420  
   421  	// Advance to offset 1, since we don't care about the leading P.
   422  	l := intervalLexer{str: s, offset: 1, err: nil}
   423  	unitMap := isoDateUnitMap
   424  
   425  	for l.offset < len(s) {
   426  		// Check if we're in the time part yet.
   427  		if s[l.offset] == 'T' {
   428  			unitMap = isoTimeUnitMap
   429  			l.offset++
   430  		}
   431  
   432  		v := l.consumeInt()
   433  		u := l.consumeUnit('T')
   434  		if l.err != nil {
   435  			return d, l.err
   436  		}
   437  
   438  		if unit, ok := unitMap[u]; ok {
   439  			d = d.Add(unit.Mul(v))
   440  		} else {
   441  			return d, pgerror.Newf(
   442  				pgcode.InvalidDatetimeFormat,
   443  				"interval: unknown unit %s in ISO-8601 duration %s", u, s)
   444  		}
   445  	}
   446  
   447  	return d, nil
   448  }
   449  
   450  // unitMap defines for each unit name what is the time duration for
   451  // that unit.
   452  var unitMap = func(
   453  	units map[string]duration.Duration,
   454  	aliases map[string][]string,
   455  ) map[string]duration.Duration {
   456  	for a, alist := range aliases {
   457  		// Pluralize.
   458  		units[a+"s"] = units[a]
   459  		for _, alias := range alist {
   460  			// Populate the remaining aliases.
   461  			units[alias] = units[a]
   462  		}
   463  	}
   464  	return units
   465  }(map[string]duration.Duration{
   466  	// Use DecodeDuration here because ns is the only unit for which we do not
   467  	// want to round nanoseconds since it is only used for multiplication.
   468  	"microsecond": duration.MakeDuration(time.Microsecond.Nanoseconds(), 0, 0),
   469  	"millisecond": duration.MakeDuration(time.Millisecond.Nanoseconds(), 0, 0),
   470  	"second":      duration.MakeDuration(time.Second.Nanoseconds(), 0, 0),
   471  	"minute":      duration.MakeDuration(time.Minute.Nanoseconds(), 0, 0),
   472  	"hour":        duration.MakeDuration(time.Hour.Nanoseconds(), 0, 0),
   473  	"day":         duration.MakeDuration(0, 1, 0),
   474  	"week":        duration.MakeDuration(0, 7, 0),
   475  	"month":       duration.MakeDuration(0, 0, 1),
   476  	"year":        duration.MakeDuration(0, 0, 12),
   477  }, map[string][]string{
   478  	// Include PostgreSQL's unit keywords for compatibility; see
   479  	// https://github.com/postgres/postgres/blob/a01d0fa1d889cc2003e1941e8b98707c4d701ba9/src/backend/utils/adt/datetime.c#L175-L240
   480  	//
   481  	// µ = U+00B5 = micro symbol
   482  	// μ = U+03BC = Greek letter mu
   483  	"microsecond": {"us", "µs", "μs", "usec", "usecs", "usecond", "useconds"},
   484  	"millisecond": {"ms", "msec", "msecs", "msecond", "mseconds"},
   485  	"second":      {"s", "sec", "secs"},
   486  	"minute":      {"m", "min", "mins"},
   487  	"hour":        {"h", "hr", "hrs"},
   488  	"day":         {"d"},
   489  	"week":        {"w"},
   490  	"month":       {"mon", "mons"},
   491  	"year":        {"y", "yr", "yrs"},
   492  })
   493  
   494  // parseDuration parses a duration in the "traditional" Postgres
   495  // format (e.g. '1 day 2 hours', '1 day 03:02:04', etc.) or golang
   496  // format (e.g. '1d2h', '1d3h2m4s', etc.)
   497  func parseDuration(s string, itm types.IntervalTypeMetadata) (duration.Duration, error) {
   498  	var d duration.Duration
   499  	l := intervalLexer{str: s, offset: 0, err: nil}
   500  	l.consumeSpaces()
   501  
   502  	if l.offset == len(l.str) {
   503  		return d, pgerror.Newf(
   504  			pgcode.InvalidDatetimeFormat, "interval: invalid input syntax: %q", l.str)
   505  	}
   506  	for l.offset != len(l.str) {
   507  		// To support -00:XX:XX we record the sign here since -0 doesn't exist
   508  		// as an int64.
   509  		sign := l.str[l.offset] == '-'
   510  		// Parse the next number.
   511  		v, hasDecimal, vp := l.consumeNum()
   512  		l.consumeSpaces()
   513  
   514  		if l.offset < len(l.str) && l.str[l.offset] == ':' && !hasDecimal {
   515  			// Special case: HH:MM[:SS.ffff] or MM:SS.ffff
   516  			delta, err := l.parseShortDuration(v, sign, itm)
   517  			if err != nil {
   518  				return d, err
   519  			}
   520  			d = d.Add(delta)
   521  			continue
   522  		}
   523  
   524  		// Parse the unit.
   525  		u := l.consumeUnit(' ')
   526  		l.consumeSpaces()
   527  		if unit, ok := unitMap[strings.ToLower(u)]; ok {
   528  			// A regular number followed by a unit, such as "9 day".
   529  			d = d.Add(unit.Mul(v))
   530  			if hasDecimal {
   531  				d = addFrac(d, unit, vp)
   532  			}
   533  			continue
   534  		}
   535  
   536  		if u != "" {
   537  			return d, pgerror.Newf(
   538  				pgcode.InvalidDatetimeFormat, "interval: unknown unit %q in duration %q", u, s)
   539  		}
   540  		return d, pgerror.Newf(
   541  			pgcode.InvalidDatetimeFormat, "interval: missing unit at position %d: %q", l.offset, s)
   542  	}
   543  	return d, l.err
   544  }
   545  
   546  func (l *intervalLexer) parseShortDuration(
   547  	h int64, hasSign bool, itm types.IntervalTypeMetadata,
   548  ) (duration.Duration, error) {
   549  	sign := int64(1)
   550  	if hasSign {
   551  		sign = -1
   552  	}
   553  	// postgresToDuration() has rewound the cursor to just after the
   554  	// first number, so that we can check here there are no unwanted
   555  	// spaces.
   556  	if l.str[l.offset] != ':' {
   557  		return duration.Duration{}, pgerror.Newf(
   558  			pgcode.InvalidDatetimeFormat, "interval: invalid format %s", l.str[l.offset:])
   559  	}
   560  	l.offset++
   561  	// Parse the second number.
   562  	m, hasDecimal, mp := l.consumeNum()
   563  
   564  	if m < 0 {
   565  		return duration.Duration{}, pgerror.Newf(
   566  			pgcode.InvalidDatetimeFormat, "interval: invalid format: %s", l.str)
   567  	}
   568  	// We have three possible formats:
   569  	// - MM:SS.ffffff
   570  	// - HH:MM (or MM:SS for MINUTE TO SECOND)
   571  	// - HH:MM:SS[.ffffff]
   572  	//
   573  	// The top format has the "h" field parsed above actually
   574  	// represent minutes. Get this out of the way first.
   575  	if hasDecimal {
   576  		l.consumeSpaces()
   577  		return duration.MakeDuration(
   578  			h*time.Minute.Nanoseconds()+
   579  				sign*(m*time.Second.Nanoseconds()+
   580  					floatToNanos(mp)),
   581  			0,
   582  			0,
   583  		), nil
   584  	}
   585  
   586  	// Remaining formats.
   587  	var s int64
   588  	var sp float64
   589  	hasSecondsComponent := false
   590  	if l.offset != len(l.str) && l.str[l.offset] == ':' {
   591  		hasSecondsComponent = true
   592  		// The last :NN part.
   593  		l.offset++
   594  		s, _, sp = l.consumeNum()
   595  		if s < 0 {
   596  			return duration.Duration{}, pgerror.Newf(
   597  				pgcode.InvalidDatetimeFormat, "interval: invalid format: %s", l.str)
   598  		}
   599  	}
   600  
   601  	l.consumeSpaces()
   602  
   603  	if !hasSecondsComponent && itm.DurationField.IsMinuteToSecond() {
   604  		return duration.MakeDuration(
   605  			h*time.Minute.Nanoseconds()+sign*(m*time.Second.Nanoseconds()),
   606  			0,
   607  			0,
   608  		), nil
   609  	}
   610  	return duration.MakeDuration(
   611  		h*time.Hour.Nanoseconds()+
   612  			sign*(m*time.Minute.Nanoseconds()+
   613  				int64(mp*float64(time.Minute.Nanoseconds()))+
   614  				s*time.Second.Nanoseconds()+
   615  				floatToNanos(sp)),
   616  		0,
   617  		0,
   618  	), nil
   619  }
   620  
   621  // addFrac increases the duration given as first argument by the unit
   622  // given as second argument multiplied by the factor in the third
   623  // argument. For computing fractions there are 30 days to a month and
   624  // 24 hours to a day.
   625  func addFrac(d duration.Duration, unit duration.Duration, f float64) duration.Duration {
   626  	if unit.Months > 0 {
   627  		f = f * float64(unit.Months)
   628  		d.Months += int64(f)
   629  		f = math.Mod(f, 1) * 30
   630  		d.Days += int64(f)
   631  		f = math.Mod(f, 1) * 24
   632  		d.SetNanos(d.Nanos() + int64(float64(time.Hour.Nanoseconds())*f))
   633  	} else if unit.Days > 0 {
   634  		f = f * float64(unit.Days)
   635  		d.Days += int64(f)
   636  		f = math.Mod(f, 1) * 24
   637  		d.SetNanos(d.Nanos() + int64(float64(time.Hour.Nanoseconds())*f))
   638  	} else {
   639  		d.SetNanos(d.Nanos() + int64(float64(unit.Nanos())*f))
   640  	}
   641  	return d
   642  }
   643  
   644  // floatToNanos converts a fractional number representing nanoseconds to the
   645  // number of integer nanoseconds. For example: ".354874219" to "354874219"
   646  // or ".123" to "123000000". This function takes care to round correctly
   647  // when a naive conversion would incorrectly truncate due to floating point
   648  // inaccuracies. This function should match the semantics of rint() from
   649  // Postgres. See:
   650  // https://git.postgresql.org/gitweb/?p=postgresql.git;a=blob;f=src/backend/utils/adt/timestamp.c;h=449164ae7e5b00f6580771017888d4922685a73c;hb=HEAD#l1511
   651  // https://git.postgresql.org/gitweb/?p=postgresql.git;a=blob;f=src/port/rint.c;h=d59d9ab774307b7db2f7cb2347815a30da563fc5;hb=HEAD
   652  func floatToNanos(f float64) int64 {
   653  	return int64(math.Round(f * float64(time.Second.Nanoseconds())))
   654  }