Parse duration literals
parent
2717f9396a
commit
a58c9a63f8
|
@ -163,22 +163,23 @@ Examples:
|
||||||
A duration literal is a representation of a length of time.
|
A duration literal is a representation of a length of time.
|
||||||
It has an integer part and a duration unit part.
|
It has an integer part and a duration unit part.
|
||||||
Multiple durations may be specified together and the resulting duration is the sum of each smaller part.
|
Multiple durations may be specified together and the resulting duration is the sum of each smaller part.
|
||||||
|
When several durations are specified together, larger units must appear before smaller ones, and there can be no repeated units.
|
||||||
|
|
||||||
duration_lit = { int_lit duration_unit } .
|
duration_lit = { int_lit duration_unit } .
|
||||||
duration_unit = "ns" | "us" | "µs" | "ms" | "s" | "m" | "h" | "d" | "w" | "mo" | "y" .
|
duration_unit = "y" | "mo" | "w" | "d" | "h" | "m" | "s" | "ms" | "us" | "µs" | "ns" .
|
||||||
|
|
||||||
| Units | Meaning |
|
| Units | Meaning |
|
||||||
| ----- | ------- |
|
| ----- | ------- |
|
||||||
| ns | nanoseconds (1 billionth of a second) |
|
|
||||||
| us or µs | microseconds (1 millionth of a second) |
|
|
||||||
| ms | milliseconds (1 thousandth of a second) |
|
|
||||||
| s | second |
|
|
||||||
| m | minute (60 seconds) |
|
|
||||||
| h | hour (60 minutes) |
|
|
||||||
| d | day |
|
|
||||||
| w | week (7 days) |
|
|
||||||
| mo | month |
|
|
||||||
| y | year (12 months) |
|
| y | year (12 months) |
|
||||||
|
| mo | month |
|
||||||
|
| w | week (7 days) |
|
||||||
|
| d | day |
|
||||||
|
| h | hour (60 minutes) |
|
||||||
|
| m | minute (60 seconds) |
|
||||||
|
| s | second |
|
||||||
|
| ms | milliseconds (1 thousandth of a second) |
|
||||||
|
| us or µs | microseconds (1 millionth of a second) |
|
||||||
|
| ns | nanoseconds (1 billionth of a second) |
|
||||||
|
|
||||||
Durations represent a length of time.
|
Durations represent a length of time.
|
||||||
Lengths of time are dependent on specific instants in time they occur and as such, durations do not represent a fixed amount of time.
|
Lengths of time are dependent on specific instants in time they occur and as such, durations do not represent a fixed amount of time.
|
||||||
|
|
7824
query/parser/flux.go
7824
query/parser/flux.go
File diff suppressed because it is too large
Load Diff
|
@ -344,11 +344,136 @@ DateTimeLiteral
|
||||||
return datetime(c.text, c.pos)
|
return datetime(c.text, c.pos)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// The order of the choices in this rule and those below matters.
|
||||||
|
// Minutes (m) must appear before milliseconds (ms) and months (mo),
|
||||||
|
// otherwise "10ms" will be parsed as 10 minutes and an extraneous "s".
|
||||||
|
DurationLiteral
|
||||||
|
= durations:(
|
||||||
|
DayDuration
|
||||||
|
/ HourDuration
|
||||||
|
/ MicroSecondDuration
|
||||||
|
/ MilliSecondDuration
|
||||||
|
/ MonthDuration
|
||||||
|
/ MinuteDuration
|
||||||
|
/ NanoSecondDuration
|
||||||
|
/ SecondDuration
|
||||||
|
/ WeekDuration
|
||||||
|
/ YearDuration
|
||||||
|
) {
|
||||||
|
return durationLiteral(durations, c.text, c.pos)
|
||||||
|
}
|
||||||
|
|
||||||
|
YearDuration
|
||||||
|
= mag:IntegerLiteral unit:YearUnits otherParts:(
|
||||||
|
DayDuration
|
||||||
|
/ HourDuration
|
||||||
|
/ MicroSecondDuration
|
||||||
|
/ MilliSecondDuration
|
||||||
|
/ MonthDuration
|
||||||
|
/ MinuteDuration
|
||||||
|
/ NanoSecondDuration
|
||||||
|
/ SecondDuration
|
||||||
|
/ WeekDuration
|
||||||
|
)? {
|
||||||
|
return appendSingleDurations(mag, unit, otherParts, c.text, c.pos)
|
||||||
|
}
|
||||||
|
|
||||||
|
MonthDuration
|
||||||
|
= mag:IntegerLiteral unit:MonthUnits otherParts:(
|
||||||
|
DayDuration
|
||||||
|
/ HourDuration
|
||||||
|
/ MicroSecondDuration
|
||||||
|
/ MilliSecondDuration
|
||||||
|
/ MinuteDuration
|
||||||
|
/ NanoSecondDuration
|
||||||
|
/ SecondDuration
|
||||||
|
/ WeekDuration
|
||||||
|
)? {
|
||||||
|
return appendSingleDurations(mag, unit, otherParts, c.text, c.pos)
|
||||||
|
}
|
||||||
|
|
||||||
|
WeekDuration
|
||||||
|
= mag:IntegerLiteral unit:WeekUnits otherParts:(
|
||||||
|
DayDuration
|
||||||
|
/ HourDuration
|
||||||
|
/ MicroSecondDuration
|
||||||
|
/ MilliSecondDuration
|
||||||
|
/ MinuteDuration
|
||||||
|
/ NanoSecondDuration
|
||||||
|
/ SecondDuration
|
||||||
|
)? {
|
||||||
|
return appendSingleDurations(mag, unit, otherParts, c.text, c.pos)
|
||||||
|
}
|
||||||
|
|
||||||
|
DayDuration
|
||||||
|
= mag:IntegerLiteral unit:DayUnits otherParts:(
|
||||||
|
HourDuration
|
||||||
|
/ MicroSecondDuration
|
||||||
|
/ MilliSecondDuration
|
||||||
|
/ MinuteDuration
|
||||||
|
/ NanoSecondDuration
|
||||||
|
/ SecondDuration
|
||||||
|
)? {
|
||||||
|
return appendSingleDurations(mag, unit, otherParts, c.text, c.pos)
|
||||||
|
}
|
||||||
|
|
||||||
|
HourDuration
|
||||||
|
= mag:IntegerLiteral unit:HourUnits otherParts:(
|
||||||
|
MicroSecondDuration
|
||||||
|
/ MilliSecondDuration
|
||||||
|
/ MinuteDuration
|
||||||
|
/ NanoSecondDuration
|
||||||
|
/ SecondDuration
|
||||||
|
)? {
|
||||||
|
return appendSingleDurations(mag, unit, otherParts, c.text, c.pos)
|
||||||
|
}
|
||||||
|
|
||||||
|
MinuteDuration
|
||||||
|
= mag:IntegerLiteral unit:MinuteUnits otherParts:(
|
||||||
|
MicroSecondDuration
|
||||||
|
/ MilliSecondDuration
|
||||||
|
/ NanoSecondDuration
|
||||||
|
/ SecondDuration
|
||||||
|
)? {
|
||||||
|
return appendSingleDurations(mag, unit, otherParts, c.text, c.pos)
|
||||||
|
}
|
||||||
|
|
||||||
|
SecondDuration
|
||||||
|
= mag:IntegerLiteral unit:SecondUnits otherParts:(
|
||||||
|
MicroSecondDuration
|
||||||
|
/ MilliSecondDuration
|
||||||
|
/ NanoSecondDuration
|
||||||
|
)? {
|
||||||
|
return appendSingleDurations(mag, unit, otherParts, c.text, c.pos)
|
||||||
|
}
|
||||||
|
|
||||||
|
MilliSecondDuration
|
||||||
|
= mag:IntegerLiteral unit:MilliSecondUnits otherParts:(
|
||||||
|
MicroSecondDuration
|
||||||
|
/ NanoSecondDuration
|
||||||
|
)? {
|
||||||
|
return appendSingleDurations(mag, unit, otherParts, c.text, c.pos)
|
||||||
|
}
|
||||||
|
|
||||||
|
MicroSecondDuration
|
||||||
|
= mag:IntegerLiteral unit:MicroSecondUnits otherParts:(
|
||||||
|
NanoSecondDuration
|
||||||
|
)? {
|
||||||
|
return appendSingleDurations(mag, unit, otherParts, c.text, c.pos)
|
||||||
|
}
|
||||||
|
|
||||||
|
NanoSecondDuration
|
||||||
|
= mag:IntegerLiteral unit:NanoSecondUnits {
|
||||||
|
return appendSingleDurations(mag, unit, nil, c.text, c.pos)
|
||||||
|
}
|
||||||
|
|
||||||
NanoSecondUnits
|
NanoSecondUnits
|
||||||
= "ns"
|
= "ns"
|
||||||
|
|
||||||
MicroSecondUnits
|
MicroSecondUnits
|
||||||
= ("us" / "µs" / "μs")
|
= ("us" / "µs" / "μs") {
|
||||||
|
return []byte("us"), nil
|
||||||
|
}
|
||||||
|
|
||||||
MilliSecondUnits
|
MilliSecondUnits
|
||||||
= "ms"
|
= "ms"
|
||||||
|
@ -368,27 +493,11 @@ DayUnits
|
||||||
WeekUnits
|
WeekUnits
|
||||||
= "w"
|
= "w"
|
||||||
|
|
||||||
DurationUnits
|
MonthUnits
|
||||||
= (
|
= "mo"
|
||||||
NanoSecondUnits
|
|
||||||
/ MicroSecondUnits
|
|
||||||
/ MilliSecondUnits
|
|
||||||
/ SecondUnits
|
|
||||||
/ MinuteUnits
|
|
||||||
/ HourUnits
|
|
||||||
/ DayUnits
|
|
||||||
/ WeekUnits
|
|
||||||
)
|
|
||||||
|
|
||||||
SingleDuration
|
YearUnits
|
||||||
= mag:IntegerLiteral unit:DurationUnits {
|
= "y"
|
||||||
return singleDuration(mag, unit, c.text, c.pos)
|
|
||||||
}
|
|
||||||
|
|
||||||
DurationLiteral
|
|
||||||
= durations:SingleDuration+ {
|
|
||||||
return durationLiteral(durations, c.text, c.pos)
|
|
||||||
}
|
|
||||||
|
|
||||||
StringLiteral
|
StringLiteral
|
||||||
= ( '"' DoubleStringChar* '"' ) {
|
= ( '"' DoubleStringChar* '"' ) {
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
|
|
||||||
package parser
|
package parser
|
||||||
|
|
||||||
//go:generate pigeon -optimize-parser -optimize-grammar -o flux.go flux.peg
|
//go:generate pigeon -optimize-parser -o flux.go flux.peg
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/influxdata/platform/query/ast"
|
"github.com/influxdata/platform/query/ast"
|
||||||
|
|
|
@ -1718,6 +1718,81 @@ join(tables:[a,b], on:["t1"], fn: (a,b) => (a["_field"] - b["_field"]) / b["_fie
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
name: "duration literal, all units",
|
||||||
|
raw: `dur = 1y3mo2w1d4h1m30s1ms2µs70ns`,
|
||||||
|
want: &ast.Program{
|
||||||
|
Body: []ast.Statement{&ast.VariableDeclaration{
|
||||||
|
Declarations: []*ast.VariableDeclarator{{
|
||||||
|
ID: &ast.Identifier{Name: "dur"},
|
||||||
|
Init: &ast.DurationLiteral{
|
||||||
|
Values: []ast.Duration{
|
||||||
|
{Magnitude: 1, Unit: "y"},
|
||||||
|
{Magnitude: 3, Unit: "mo"},
|
||||||
|
{Magnitude: 2, Unit: "w"},
|
||||||
|
{Magnitude: 1, Unit: "d"},
|
||||||
|
{Magnitude: 4, Unit: "h"},
|
||||||
|
{Magnitude: 1, Unit: "m"},
|
||||||
|
{Magnitude: 30, Unit: "s"},
|
||||||
|
{Magnitude: 1, Unit: "ms"},
|
||||||
|
{Magnitude: 2, Unit: "us"},
|
||||||
|
{Magnitude: 70, Unit: "ns"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}},
|
||||||
|
}},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "duration literal, months",
|
||||||
|
raw: `dur = 6mo`,
|
||||||
|
want: &ast.Program{
|
||||||
|
Body: []ast.Statement{&ast.VariableDeclaration{
|
||||||
|
Declarations: []*ast.VariableDeclarator{{
|
||||||
|
ID: &ast.Identifier{Name: "dur"},
|
||||||
|
Init: &ast.DurationLiteral{
|
||||||
|
Values: []ast.Duration{
|
||||||
|
{Magnitude: 6, Unit: "mo"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}},
|
||||||
|
}},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "duration literal, milliseconds",
|
||||||
|
raw: `dur = 500ms`,
|
||||||
|
want: &ast.Program{
|
||||||
|
Body: []ast.Statement{&ast.VariableDeclaration{
|
||||||
|
Declarations: []*ast.VariableDeclarator{{
|
||||||
|
ID: &ast.Identifier{Name: "dur"},
|
||||||
|
Init: &ast.DurationLiteral{
|
||||||
|
Values: []ast.Duration{
|
||||||
|
{Magnitude: 500, Unit: "ms"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}},
|
||||||
|
}},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "duration literal, months, minutes, milliseconds",
|
||||||
|
raw: `dur = 6mo30m500ms`,
|
||||||
|
want: &ast.Program{
|
||||||
|
Body: []ast.Statement{&ast.VariableDeclaration{
|
||||||
|
Declarations: []*ast.VariableDeclarator{{
|
||||||
|
ID: &ast.Identifier{Name: "dur"},
|
||||||
|
Init: &ast.DurationLiteral{
|
||||||
|
Values: []ast.Duration{
|
||||||
|
{Magnitude: 6, Unit: "mo"},
|
||||||
|
{Magnitude: 30, Unit: "m"},
|
||||||
|
{Magnitude: 500, Unit: "ms"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}},
|
||||||
|
}},
|
||||||
|
},
|
||||||
|
},
|
||||||
{
|
{
|
||||||
name: "parse error extra gibberish",
|
name: "parse error extra gibberish",
|
||||||
raw: `from(bucket:"Flux/autogen") &^*&H#IUJBN`,
|
raw: `from(bucket:"Flux/autogen") &^*&H#IUJBN`,
|
||||||
|
@ -1728,6 +1803,21 @@ join(tables:[a,b], on:["t1"], fn: (a,b) => (a["_field"] - b["_field"]) / b["_fie
|
||||||
raw: `from(bucket:"Flux/autogen") &^*&H#IUJBN from(bucket:"other/autogen")`,
|
raw: `from(bucket:"Flux/autogen") &^*&H#IUJBN from(bucket:"other/autogen")`,
|
||||||
wantErr: true,
|
wantErr: true,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
name: "parse error from duration literal with repeated units",
|
||||||
|
raw: `from(bucket:"my_bucket") |> range(start: -1d3h2h1m)`,
|
||||||
|
wantErr: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "parser error from duration literal with smaller unit before larger one",
|
||||||
|
raw: `from(bucket:"my_bucket") |> range(start: -1s5m)`,
|
||||||
|
wantErr: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "parser error from duration literal with invalid unit",
|
||||||
|
raw: `from(bucket:"my_bucket") |> range(start: -1s5v)`,
|
||||||
|
wantErr: true,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
for _, tt := range tests {
|
for _, tt := range tests {
|
||||||
tt := tt
|
tt := tt
|
||||||
|
|
|
@ -341,30 +341,41 @@ func regexLiteral(chars interface{}, text []byte, pos position) (*ast.RegexpLite
|
||||||
}
|
}
|
||||||
|
|
||||||
func durationLiteral(durations interface{}, text []byte, pos position) (*ast.DurationLiteral, error) {
|
func durationLiteral(durations interface{}, text []byte, pos position) (*ast.DurationLiteral, error) {
|
||||||
durs := toIfaceSlice(durations)
|
literals := durations.([]*singleDurationLiteral)
|
||||||
literals := make([]*singleDurationLiteral, len(durs))
|
// The slice built by the parser goes from smallest units to largest (opposite of syntax),
|
||||||
for i, d := range durs {
|
// reverse it for the AST produced.
|
||||||
literals[i] = d.(*singleDurationLiteral)
|
for i := 0; i < len(literals) / 2; i++ {
|
||||||
|
j := len(literals) - i - 1
|
||||||
|
literals[i], literals[j] = literals[j], literals[i]
|
||||||
}
|
}
|
||||||
|
|
||||||
return &ast.DurationLiteral{
|
return &ast.DurationLiteral{
|
||||||
BaseNode: base(text, pos),
|
BaseNode: base(text, pos),
|
||||||
Values: toDurationSlice(literals),
|
Values: toDurationSlice(literals),
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func singleDuration(mag, unit interface{}, text []byte, pos position) (*singleDurationLiteral, error) {
|
|
||||||
return &singleDurationLiteral{
|
|
||||||
// Not an AST node
|
|
||||||
magnitude: mag.(*ast.IntegerLiteral),
|
|
||||||
unit: string(unit.([]byte)),
|
|
||||||
}, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
type singleDurationLiteral struct {
|
type singleDurationLiteral struct {
|
||||||
magnitude *ast.IntegerLiteral
|
magnitude *ast.IntegerLiteral
|
||||||
unit string
|
unit string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func appendSingleDurations(mag, unit, otherParts interface{}, text []byte, pos position) ([]*singleDurationLiteral, error) {
|
||||||
|
sdl := &singleDurationLiteral{
|
||||||
|
magnitude: mag.(*ast.IntegerLiteral),
|
||||||
|
unit: string(unit.([]byte)),
|
||||||
|
}
|
||||||
|
|
||||||
|
if otherParts == nil {
|
||||||
|
slice := make([]*singleDurationLiteral, 1, 10)
|
||||||
|
slice[0] = sdl
|
||||||
|
return slice, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
others := otherParts.([]*singleDurationLiteral)
|
||||||
|
return append(others, sdl), nil
|
||||||
|
}
|
||||||
|
|
||||||
func datetime(text []byte, pos position) (*ast.DateTimeLiteral, error) {
|
func datetime(text []byte, pos position) (*ast.DateTimeLiteral, error) {
|
||||||
t, err := time.Parse(time.RFC3339Nano, string(text))
|
t, err := time.Parse(time.RFC3339Nano, string(text))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -1,6 +1,9 @@
|
||||||
{
|
{
|
||||||
|
// Package promql implements a promql parser to build flux query specifications from promql.
|
||||||
package promql
|
package promql
|
||||||
|
|
||||||
|
// DO NOT EDIT: This file is auto generated by the pigeon PEG parser generator.
|
||||||
|
|
||||||
var reservedWords = map[string]bool{}
|
var reservedWords = map[string]bool{}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1367,11 +1367,22 @@ func analyzeRegexpLiteral(lit *ast.RegexpLiteral, declarations DeclarationScope)
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
func toDuration(lit ast.Duration) (time.Duration, error) {
|
func toDuration(lit ast.Duration) (time.Duration, error) {
|
||||||
|
// TODO: This is temporary code until we have proper duration type that takes different months, DST, etc into account
|
||||||
var dur time.Duration
|
var dur time.Duration
|
||||||
var err error
|
var err error
|
||||||
mag := lit.Magnitude
|
mag := lit.Magnitude
|
||||||
unit := lit.Unit
|
unit := lit.Unit
|
||||||
|
|
||||||
switch unit {
|
switch unit {
|
||||||
|
case "y":
|
||||||
|
mag *= 12
|
||||||
|
unit = "mo"
|
||||||
|
fallthrough
|
||||||
|
case "mo":
|
||||||
|
const weeksPerMonth = 365.25 / 12 / 7
|
||||||
|
mag = int64(float64(mag) * weeksPerMonth)
|
||||||
|
unit = "w"
|
||||||
|
fallthrough
|
||||||
case "w":
|
case "w":
|
||||||
mag *= 7
|
mag *= 7
|
||||||
unit = "d"
|
unit = "d"
|
||||||
|
@ -1381,6 +1392,7 @@ func toDuration(lit ast.Duration) (time.Duration, error) {
|
||||||
unit = "h"
|
unit = "h"
|
||||||
fallthrough
|
fallthrough
|
||||||
default:
|
default:
|
||||||
|
// ParseDuration will handle h, m, s, ms, us, ns.
|
||||||
dur, err = time.ParseDuration(strconv.FormatInt(mag, 10) + unit)
|
dur, err = time.ParseDuration(strconv.FormatInt(mag, 10) + unit)
|
||||||
}
|
}
|
||||||
return dur, err
|
return dur, err
|
||||||
|
|
Loading…
Reference in New Issue