From 97bbb07e875ce8e95548a73f868a0b1b02e506b7 Mon Sep 17 00:00:00 2001
From: Aliaksandr Valialkin <valyala@victoriametrics.com>
Date: Thu, 5 Jan 2023 01:10:58 -0800
Subject: [PATCH] vendor: update github.com/VictoriaMetrics/metricsql from
 v0.50.0 to v0.51.0

Updates https://github.com/VictoriaMetrics/metricsql/pull/7
Updates https://github.com/VictoriaMetrics/VictoriaMetrics/issues/3589
---
 app/vmselect/promql/exec_test.go              |   2 +-
 docs/CHANGELOG.md                             |   1 +
 go.mod                                        |   2 +-
 go.sum                                        |   4 +-
 .../VictoriaMetrics/metricsql/lexer.go        | 172 ++++++++++--------
 vendor/modules.txt                            |   2 +-
 6 files changed, 105 insertions(+), 78 deletions(-)

diff --git a/app/vmselect/promql/exec_test.go b/app/vmselect/promql/exec_test.go
index 6c85d4cfb9..75455eb4e6 100644
--- a/app/vmselect/promql/exec_test.go
+++ b/app/vmselect/promql/exec_test.go
@@ -7232,7 +7232,7 @@ func TestExecSuccess(t *testing.T) {
 	})
 	t.Run(`rollup_scrape_interval()`, func(t *testing.T) {
 		t.Parallel()
-		q := `sort_by_label(rollup_scrape_interval(1[5m:10s]), "rollup")`
+		q := `sort_by_label(rollup_scrape_interval(1[5M:10s]), "rollup")`
 		r1 := netstorage.Result{
 			MetricName: metricNameExpected,
 			Values:     []float64{10, 10, 10, 10, 10, 10},
diff --git a/docs/CHANGELOG.md b/docs/CHANGELOG.md
index 767efcc4b0..8d1c9a24a2 100644
--- a/docs/CHANGELOG.md
+++ b/docs/CHANGELOG.md
@@ -15,6 +15,7 @@ The following tip changes can be tested by building VictoriaMetrics components f
 
 ## v1.79.x long-time support release (LTS)
 
+* BUGFIX: [MetricsQL](https://docs.victoriametrics.com/MetricsQL.html): properly parse durations with uppercase suffixes such as `10S`, `5MS`, `1W`, etc. See [this issue](https://github.com/VictoriaMetrics/VictoriaMetrics/issues/3589).
 * BUGFIX: [vmagent](https://docs.victoriametrics.com/vmagent.html): [dockerswarm_sd_configs](https://docs.victoriametrics.com/sd_configs.html#dockerswarm_sd_configs): properly encode `filters` field. See [this issue](https://github.com/VictoriaMetrics/VictoriaMetrics/issues/3579)
 * BUGFIX: allow specifying values bigger than 2GiB to the following command-line flag values on 32-bit architectures (`386` and `arm`): `-storage.minFreeDiskSpaceBytes` and `-remoteWrite.maxDiskUsagePerURL`. Previously values bigger than 2GiB were incorrectly truncated on these architectures.
 * BUGFIX: [VictoriaMetrics enterprise](https://docs.victoriametrics.com/enterprise.html): expose proper values for `vm_downsampling_partitions_scheduled` and `vm_downsampling_partitions_scheduled_size_bytes` metrics, which were added at [v1.78.0](https://docs.victoriametrics.com/CHANGELOG.html#v1780). See [this feature request](https://github.com/VictoriaMetrics/VictoriaMetrics/issues/2612).
diff --git a/go.mod b/go.mod
index 64f77ce992..21f8af1186 100644
--- a/go.mod
+++ b/go.mod
@@ -10,7 +10,7 @@ require (
 	// like https://github.com/valyala/fasthttp/commit/996610f021ff45fdc98c2ce7884d5fa4e7f9199b
 	github.com/VictoriaMetrics/fasthttp v1.1.0
 	github.com/VictoriaMetrics/metrics v1.23.0
-	github.com/VictoriaMetrics/metricsql v0.50.0
+	github.com/VictoriaMetrics/metricsql v0.51.1
 	github.com/aws/aws-sdk-go v1.44.157
 	github.com/cespare/xxhash/v2 v2.2.0
 
diff --git a/go.sum b/go.sum
index e0ba77d68a..80277207b8 100644
--- a/go.sum
+++ b/go.sum
@@ -91,8 +91,8 @@ github.com/VictoriaMetrics/fasthttp v1.1.0/go.mod h1:/7DMcogqd+aaD3G3Hg5kFgoFwlR
 github.com/VictoriaMetrics/metrics v1.18.1/go.mod h1:ArjwVz7WpgpegX/JpB0zpNF2h2232kErkEnzH1sxMmA=
 github.com/VictoriaMetrics/metrics v1.23.0 h1:WzfqyzCaxUZip+OBbg1+lV33WChDSu4ssYII3nxtpeA=
 github.com/VictoriaMetrics/metrics v1.23.0/go.mod h1:rAr/llLpEnAdTehiNlUxKgnjcOuROSzpw0GvjpEbvFc=
-github.com/VictoriaMetrics/metricsql v0.50.0 h1:MCBhjn1qlfMqPGP6HiR9JgmEw7oTRGm/O8YwSeoaI1E=
-github.com/VictoriaMetrics/metricsql v0.50.0/go.mod h1:6pP1ZeLVJHqJrHlF6Ij3gmpQIznSsgktEcZgsAWYel0=
+github.com/VictoriaMetrics/metricsql v0.51.1 h1:gmh3ZGCDrqUTdhUrr87eJOXMOputDYs1PtLwTfySTsI=
+github.com/VictoriaMetrics/metricsql v0.51.1/go.mod h1:6pP1ZeLVJHqJrHlF6Ij3gmpQIznSsgktEcZgsAWYel0=
 github.com/VividCortex/ewma v1.2.0 h1:f58SaIzcDXrSy3kWaHNvuJgJ3Nmz59Zji6XoJR/q1ow=
 github.com/VividCortex/ewma v1.2.0/go.mod h1:nz4BbCtbLyFDeC9SUHbtcT5644juEuWfUAUnGx7j5l4=
 github.com/VividCortex/gohistogram v1.0.0/go.mod h1:Pf5mBqqDxYaXu3hDrrU+w6nw50o/4+TcAqDqk/vUH7g=
diff --git a/vendor/github.com/VictoriaMetrics/metricsql/lexer.go b/vendor/github.com/VictoriaMetrics/metricsql/lexer.go
index c91f863811..c3aebd80d3 100644
--- a/vendor/github.com/VictoriaMetrics/metricsql/lexer.go
+++ b/vendor/github.com/VictoriaMetrics/metricsql/lexer.go
@@ -331,19 +331,22 @@ func scanNumMultiplier(s string) int {
 func scanIdent(s string) string {
 	i := 0
 	for i < len(s) {
-		if isIdentChar(s[i]) {
-			i++
+		r, size := utf8.DecodeRuneInString(s[i:])
+		if i == 0 && isFirstIdentChar(r) || i > 0 && isIdentChar(r) {
+			i += size
 			continue
 		}
-		if s[i] != '\\' {
+		if r != '\\' {
 			break
 		}
-		i++
-
-		// Do not verify the next char, since it is escaped.
-		// The next char may be encoded as multi-byte UTF8 sequence. See https://en.wikipedia.org/wiki/UTF-8#Encoding
-		_, size := utf8.DecodeRuneInString(s[i:])
 		i += size
+		r, n := decodeEscapeSequence(s[i:])
+		if r == utf8.RuneError {
+			// Invalid escape sequence
+			i -= size
+			break
+		}
+		i += n
 	}
 	if i == 0 {
 		panic("BUG: scanIdent couldn't find a single ident char; make sure isIdentPrefix called before scanIdent")
@@ -360,23 +363,12 @@ func unescapeIdent(s string) string {
 	for {
 		dst = append(dst, s[:n]...)
 		s = s[n+1:]
-		if len(s) == 0 {
-			return string(dst)
-		}
-		if s[0] == 'x' && len(s) >= 3 {
-			h1 := fromHex(s[1])
-			h2 := fromHex(s[2])
-			if h1 >= 0 && h2 >= 0 {
-				dst = append(dst, byte((h1<<4)|h2))
-				s = s[3:]
-			} else {
-				dst = append(dst, s[0])
-				s = s[1:]
-			}
+		r, size := decodeEscapeSequence(s)
+		if r == utf8.RuneError {
+			// Cannot decode escape sequence. Put it in the output as is
+			dst = append(dst, '\\')
 		} else {
-			// UTF8 char. See https://en.wikipedia.org/wiki/UTF-8#Encoding
-			_, size := utf8.DecodeRuneInString(s)
-			dst = append(dst, s[:size]...)
+			dst = utf8.AppendRune(dst, r)
 			s = s[size:]
 		}
 		n = strings.IndexByte(s, '\\')
@@ -387,49 +379,16 @@ func unescapeIdent(s string) string {
 	}
 }
 
-func fromHex(ch byte) int {
-	if ch >= '0' && ch <= '9' {
-		return int(ch - '0')
-	}
-	if ch >= 'a' && ch <= 'f' {
-		return int((ch - 'a') + 10)
-	}
-	if ch >= 'A' && ch <= 'F' {
-		return int((ch - 'A') + 10)
-	}
-	return -1
-}
-
-func toHex(n byte) byte {
-	if n < 10 {
-		return '0' + n
-	}
-	return 'a' + (n - 10)
-}
-
 func appendEscapedIdent(dst []byte, s string) []byte {
-	for i := 0; i < len(s); i++ {
-		ch := s[i]
-		if isIdentChar(ch) {
-			if i == 0 && !isFirstIdentChar(ch) {
-				// hex-encode the first char
-				dst = append(dst, '\\', 'x', toHex(ch>>4), toHex(ch&0xf))
-			} else {
-				dst = append(dst, ch)
-			}
-			continue
-		}
-
-		// escape ch
-		dst = append(dst, '\\')
+	i := 0
+	for i < len(s) {
 		r, size := utf8.DecodeRuneInString(s[i:])
-		if r != utf8.RuneError && unicode.IsPrint(r) {
-			dst = append(dst, s[i:i+size]...)
-			i += size - 1
+		if i == 0 && isFirstIdentChar(r) || i > 0 && isIdentChar(r) {
+			dst = utf8.AppendRune(dst, r)
 		} else {
-			// hex-encode non-printable chars
-			dst = append(dst, 'x', toHex(ch>>4), toHex(ch&0xf))
+			dst = appendEscapeSequence(dst, r)
 		}
+		i += size
 	}
 	return dst
 }
@@ -597,6 +556,7 @@ func DurationValue(s string, step int64) (int64, error) {
 }
 
 func parseSingleDuration(s string, step int64) (float64, error) {
+	s = strings.ToLower(s)
 	numPart := s[:len(s)-1]
 	if strings.HasSuffix(numPart, "m") {
 		// Duration in ms
@@ -656,6 +616,7 @@ func scanSingleDuration(s string, canBeNegative bool) int {
 	if len(s) == 0 {
 		return -1
 	}
+	s = strings.ToLower(s)
 	i := 0
 	if s[0] == '-' && canBeNegative {
 		i++
@@ -703,25 +664,26 @@ func isIdentPrefix(s string) bool {
 	if len(s) == 0 {
 		return false
 	}
-	if s[0] == '\\' {
-		// Assume this is an escape char for the next char.
-		return true
+	r, size := utf8.DecodeRuneInString(s)
+	if r == '\\' {
+		r, _ = decodeEscapeSequence(s[size:])
+		return r != utf8.RuneError
 	}
-	return isFirstIdentChar(s[0])
+	return isFirstIdentChar(r)
 }
 
-func isFirstIdentChar(ch byte) bool {
-	if ch >= 'a' && ch <= 'z' || ch >= 'A' && ch <= 'Z' {
+func isFirstIdentChar(r rune) bool {
+	if unicode.IsLetter(r) {
 		return true
 	}
-	return ch == '_' || ch == ':'
+	return r == '_' || r == ':'
 }
 
-func isIdentChar(ch byte) bool {
-	if isFirstIdentChar(ch) {
+func isIdentChar(r rune) bool {
+	if isFirstIdentChar(r) {
 		return true
 	}
-	return isDecimalChar(ch) || ch == '.'
+	return r < 256 && isDecimalChar(byte(r)) || r == '.'
 }
 
 func isSpaceChar(ch byte) bool {
@@ -732,3 +694,67 @@ func isSpaceChar(ch byte) bool {
 		return false
 	}
 }
+
+func appendEscapeSequence(dst []byte, r rune) []byte {
+	dst = append(dst, '\\')
+	if unicode.IsPrint(r) {
+		return utf8.AppendRune(dst, r)
+	}
+	// hex-encode non-printable chars
+	if r < 256 {
+		return append(dst, 'x', toHex(byte(r>>4)), toHex(byte(r&0xf)))
+	}
+	return append(dst, 'u', toHex(byte(r>>12)), toHex(byte((r>>8)&0xf)), toHex(byte(r>>4)), toHex(byte(r&0xf)))
+}
+
+func decodeEscapeSequence(s string) (rune, int) {
+	if strings.HasPrefix(s, "x") || strings.HasPrefix(s, "X") {
+		if len(s) >= 3 {
+			h1 := fromHex(s[1])
+			h2 := fromHex(s[2])
+			if h1 >= 0 && h2 >= 0 {
+				r := rune((h1 << 4) | h2)
+				return r, 3
+			}
+		}
+		return utf8.RuneError, 0
+	}
+	if strings.HasPrefix(s, "u") || strings.HasPrefix(s, "U") {
+		if len(s) >= 5 {
+			h1 := fromHex(s[1])
+			h2 := fromHex(s[2])
+			h3 := fromHex(s[3])
+			h4 := fromHex(s[4])
+			if h1 >= 0 && h2 >= 0 && h3 >= 0 && h4 >= 0 {
+				return rune((h1 << 12) | (h2 << 8) | (h3 << 4) | h4), 5
+			}
+		}
+		return utf8.RuneError, 0
+	}
+	r, size := utf8.DecodeRuneInString(s)
+	if unicode.IsPrint(r) {
+		return r, size
+	}
+	// Improperly escaped non-printable char
+	return utf8.RuneError, 0
+}
+
+func fromHex(ch byte) int {
+	if ch >= '0' && ch <= '9' {
+		return int(ch - '0')
+	}
+	if ch >= 'a' && ch <= 'f' {
+		return int((ch - 'a') + 10)
+	}
+	if ch >= 'A' && ch <= 'F' {
+		return int((ch - 'A') + 10)
+	}
+	return -1
+}
+
+func toHex(n byte) byte {
+	if n < 10 {
+		return '0' + n
+	}
+	return 'a' + (n - 10)
+}
diff --git a/vendor/modules.txt b/vendor/modules.txt
index e6745dbe10..3f66334720 100644
--- a/vendor/modules.txt
+++ b/vendor/modules.txt
@@ -31,7 +31,7 @@ github.com/VictoriaMetrics/fasthttp/stackless
 # github.com/VictoriaMetrics/metrics v1.23.0
 ## explicit; go 1.15
 github.com/VictoriaMetrics/metrics
-# github.com/VictoriaMetrics/metricsql v0.50.0
+# github.com/VictoriaMetrics/metricsql v0.51.1
 ## explicit; go 1.13
 github.com/VictoriaMetrics/metricsql
 github.com/VictoriaMetrics/metricsql/binaryop