diff --git a/influxql/ast.go b/influxql/ast.go index 4e189365ef..ce743f1bc0 100644 --- a/influxql/ast.go +++ b/influxql/ast.go @@ -179,6 +179,7 @@ func (*Dimension) node() {} func (Dimensions) node() {} func (*DurationLiteral) node() {} func (*IntegerLiteral) node() {} +func (*UnsignedLiteral) node() {} func (*Field) node() {} func (Fields) node() {} func (*Measurement) node() {} @@ -312,6 +313,7 @@ func (*Call) expr() {} func (*Distinct) expr() {} func (*DurationLiteral) expr() {} func (*IntegerLiteral) expr() {} +func (*UnsignedLiteral) expr() {} func (*NilLiteral) expr() {} func (*NumberLiteral) expr() {} func (*ParenExpr) expr() {} @@ -333,6 +335,7 @@ type Literal interface { func (*BooleanLiteral) literal() {} func (*DurationLiteral) literal() {} func (*IntegerLiteral) literal() {} +func (*UnsignedLiteral) literal() {} func (*NilLiteral) literal() {} func (*NumberLiteral) literal() {} func (*RegexLiteral) literal() {} @@ -3273,6 +3276,15 @@ type IntegerLiteral struct { // String returns a string representation of the literal. func (l *IntegerLiteral) String() string { return fmt.Sprintf("%d", l.Val) } +// UnsignedLiteral represents an unsigned literal. The parser will only use an unsigned literal if the parsed +// integer is greater than math.MaxInt64. +type UnsignedLiteral struct { + Val uint64 +} + +// String returns a string representation of the literal. +func (l *UnsignedLiteral) String() string { return strconv.FormatUint(l.Val, 10) } + // BooleanLiteral represents a boolean literal. type BooleanLiteral struct { Val bool diff --git a/influxql/parser.go b/influxql/parser.go index d33e5bbaba..9dbf309c46 100644 --- a/influxql/parser.go +++ b/influxql/parser.go @@ -2601,6 +2601,11 @@ func (p *Parser) parseUnaryExpr() (Expr, error) { case INTEGER: v, err := strconv.ParseInt(lit, 10, 64) if err != nil { + // The literal may be too large to fit into an int64. If it is, use an unsigned integer. + // The check for negative numbers is handled somewhere else so this should always be a positive number. + if v, err := strconv.ParseUint(lit, 10, 64); err == nil { + return &UnsignedLiteral{Val: v}, nil + } return nil, &ParseError{Message: "unable to parse integer", Pos: pos} } return &IntegerLiteral{Val: v}, nil @@ -2677,6 +2682,16 @@ func (p *Parser) parseUnaryExpr() (Expr, error) { lit.Val *= float64(mul) case *IntegerLiteral: lit.Val *= int64(mul) + case *UnsignedLiteral: + if tok == SUB { + // Because of twos-complement integers and the method we parse, math.MinInt64 will be parsed + // as an UnsignedLiteral because it overflows an int64, but it fits into int64 if it were parsed + // as a negative number instead. + if lit.Val == uint64(math.MaxInt64+1) { + return &IntegerLiteral{Val: int64(-lit.Val)}, nil + } + return nil, fmt.Errorf("constant -%d underflows int64", lit.Val) + } case *DurationLiteral: lit.Val *= time.Duration(mul) case *VarRef, *Call, *ParenExpr: diff --git a/influxql/parser_test.go b/influxql/parser_test.go index 240299d163..d294b27328 100644 --- a/influxql/parser_test.go +++ b/influxql/parser_test.go @@ -3069,6 +3069,9 @@ func TestParser_ParseExpr(t *testing.T) { // Primitives {s: `100.0`, expr: &influxql.NumberLiteral{Val: 100}}, {s: `100`, expr: &influxql.IntegerLiteral{Val: 100}}, + {s: `9223372036854775808`, expr: &influxql.UnsignedLiteral{Val: 9223372036854775808}}, + {s: `-9223372036854775808`, expr: &influxql.IntegerLiteral{Val: -9223372036854775808}}, + {s: `-9223372036854775809`, err: `constant -9223372036854775809 underflows int64`}, {s: `-100.0`, expr: &influxql.NumberLiteral{Val: -100}}, {s: `-100`, expr: &influxql.IntegerLiteral{Val: -100}}, {s: `100.`, expr: &influxql.NumberLiteral{Val: 100}},