mirror of
https://github.com/featurebasedb/featurebase.git
synced 2026-08-28 02:44:59 +00:00
Fix failing selects on views defined with date literals (#2313)
* Fix failing selects on views defined with date literals * System variables implementation.
This commit is contained in:
parent
9c082c5c77
commit
2c3be9d1e8
5 changed files with 141 additions and 13 deletions
|
|
@ -58,6 +58,7 @@ func (*ForeignKeyConstraint) node() {}
|
|||
func (*FrameSpec) node() {}
|
||||
func (*Ident) node() {}
|
||||
func (*Variable) node() {}
|
||||
func (*SysVariable) node() {}
|
||||
func (*IndexedColumn) node() {}
|
||||
func (*InsertStatement) node() {}
|
||||
func (*JoinClause) node() {}
|
||||
|
|
@ -248,6 +249,7 @@ func (*Exists) expr() {}
|
|||
func (*ExprList) expr() {}
|
||||
func (*Ident) expr() {}
|
||||
func (*Variable) expr() {}
|
||||
func (*SysVariable) expr() {}
|
||||
func (*NullLit) expr() {}
|
||||
func (*IntegerLit) expr() {}
|
||||
func (*FloatLit) expr() {}
|
||||
|
|
@ -1686,6 +1688,42 @@ func IdentName(ident *Ident) string {
|
|||
return ident.Name
|
||||
}
|
||||
|
||||
// SysVariable represents built-in system variables that can be referenced in the sql for current date, current time and other potential pre-determinable values.
|
||||
// In SQL these system provided data elements are referenced using keywords such as CURRENT_DATE & CURRENT_TIMESTAMP, etc.
|
||||
type SysVariable struct {
|
||||
NamePos Pos // variable position in sql
|
||||
Token Token // parser token mapped to the variable's name/keyword
|
||||
}
|
||||
|
||||
func (*SysVariable) IsLiteral() bool { return false }
|
||||
|
||||
func (svar *SysVariable) Pos() Pos {
|
||||
return svar.NamePos
|
||||
}
|
||||
|
||||
func (svar *SysVariable) Clone() *SysVariable {
|
||||
if svar == nil {
|
||||
return nil
|
||||
}
|
||||
other := *svar
|
||||
return &other
|
||||
}
|
||||
func (svar *SysVariable) Name() string {
|
||||
return tokens[svar.Token]
|
||||
}
|
||||
|
||||
func (svar *SysVariable) String() string {
|
||||
return svar.Name()
|
||||
}
|
||||
|
||||
func (svar *SysVariable) DataType() ExprDataType {
|
||||
switch svar.Token {
|
||||
case CURRENT_DATE, CURRENT_TIMESTAMP:
|
||||
return NewDataTypeTimestamp()
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type Variable struct {
|
||||
NamePos Pos // variable position
|
||||
Name string // variable name
|
||||
|
|
@ -1940,9 +1978,9 @@ func (lit *DateLit) Clone() *DateLit {
|
|||
return &other
|
||||
}
|
||||
|
||||
// String returns the string representation of the expression.
|
||||
// String returns the string representation of the Datetime value.
|
||||
func (lit *DateLit) String() string {
|
||||
return lit.Value.Format(time.RFC3339)
|
||||
return "'" + lit.Value.Format(time.RFC3339) + "'"
|
||||
}
|
||||
|
||||
type UnaryExpr struct {
|
||||
|
|
|
|||
|
|
@ -5,7 +5,6 @@ import (
|
|||
"fmt"
|
||||
"io"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
// Parser represents a SQL parser.
|
||||
|
|
@ -2768,12 +2767,9 @@ func (p *Parser) parseOperand() (expr Expr, err error) {
|
|||
case NULL:
|
||||
return &NullLit{ValuePos: pos}, nil
|
||||
case CURRENT_DATE:
|
||||
now := time.Now().UTC()
|
||||
nowDate := time.Date(now.Year(), now.Month(), now.Day(), 0, 0, 0, 0, now.Location())
|
||||
return &DateLit{ValuePos: pos, Value: nowDate}, nil
|
||||
return &SysVariable{NamePos: pos, Token: tok}, nil
|
||||
case CURRENT_TIMESTAMP:
|
||||
now := time.Now().UTC()
|
||||
return &DateLit{ValuePos: pos, Value: now}, nil
|
||||
return &SysVariable{NamePos: pos, Token: tok}, nil
|
||||
case TRUE, FALSE:
|
||||
return &BoolLit{ValuePos: pos, Value: tok == TRUE}, nil
|
||||
case PLUS, MINUS, BITNOT:
|
||||
|
|
|
|||
|
|
@ -1998,6 +1998,59 @@ func (n *boolLiteralPlanExpression) WithChildren(children ...types.PlanExpressio
|
|||
return n, nil
|
||||
}
|
||||
|
||||
// represents system variables such as CURRENT_DATE and CURRENT_DATETIME
|
||||
type sysVariablePlanExpression struct {
|
||||
name string // name of the system variable
|
||||
token parser.Token // token mapped to the system variable name
|
||||
}
|
||||
|
||||
func newSysVariablePlanExpression(name string, token parser.Token) *sysVariablePlanExpression {
|
||||
return &sysVariablePlanExpression{
|
||||
name: name,
|
||||
token: token,
|
||||
}
|
||||
}
|
||||
|
||||
func (n *sysVariablePlanExpression) Evaluate(currentRow []interface{}) (interface{}, error) {
|
||||
switch n.token {
|
||||
case parser.CURRENT_DATE:
|
||||
dt := time.Now().UTC()
|
||||
return time.Date(dt.Year(), dt.Month(), dt.Day(), 0, 0, 0, 0, dt.Location()), nil
|
||||
case parser.CURRENT_TIMESTAMP:
|
||||
return time.Now().UTC(), nil
|
||||
}
|
||||
return nil, sql3.NewErrInternal(fmt.Sprintf("Mising plan expression implementation for system variable '%s'", n.name))
|
||||
}
|
||||
|
||||
func (n *sysVariablePlanExpression) Type() parser.ExprDataType {
|
||||
switch n.token {
|
||||
case parser.CURRENT_DATE, parser.CURRENT_TIMESTAMP:
|
||||
return parser.NewDataTypeTimestamp()
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (n *sysVariablePlanExpression) String() string {
|
||||
return n.name
|
||||
}
|
||||
|
||||
func (n *sysVariablePlanExpression) Plan() map[string]interface{} {
|
||||
result := make(map[string]interface{})
|
||||
result["_expr"] = fmt.Sprintf("%T", n)
|
||||
result["description"] = n.String()
|
||||
result["dataType"] = n.Type().TypeDescription()
|
||||
result["value"], _ = n.Evaluate(nil)
|
||||
return result
|
||||
}
|
||||
|
||||
func (n *sysVariablePlanExpression) Children() []types.PlanExpression {
|
||||
return []types.PlanExpression{}
|
||||
}
|
||||
|
||||
func (n *sysVariablePlanExpression) WithChildren(children ...types.PlanExpression) (types.PlanExpression, error) {
|
||||
return n, nil
|
||||
}
|
||||
|
||||
// dateLiteralPlanExpression is a date literal
|
||||
type dateLiteralPlanExpression struct {
|
||||
value time.Time
|
||||
|
|
@ -2611,6 +2664,9 @@ func (p *ExecutionPlanner) compileExpr(expr parser.Expr) (_ types.PlanExpression
|
|||
case *parser.DateLit:
|
||||
return newDateLiteralPlanExpression(expr.Value), nil
|
||||
|
||||
case *parser.SysVariable:
|
||||
return newSysVariablePlanExpression(expr.Name(), expr.Token), nil
|
||||
|
||||
case *parser.ParenExpr:
|
||||
return p.compileExpr(expr.X)
|
||||
|
||||
|
|
|
|||
|
|
@ -151,6 +151,9 @@ func (p *ExecutionPlanner) analyzeExpression(ctx context.Context, expr parser.Ex
|
|||
case *parser.DateLit:
|
||||
return e, nil
|
||||
|
||||
case *parser.SysVariable:
|
||||
return e, nil
|
||||
|
||||
case *parser.ParenExpr:
|
||||
pexpr, err := p.analyzeExpression(ctx, e.X, scope)
|
||||
if err != nil {
|
||||
|
|
|
|||
|
|
@ -1,5 +1,7 @@
|
|||
package defs
|
||||
|
||||
import "time"
|
||||
|
||||
var viewTests = TableTest{
|
||||
name: "viewtests",
|
||||
Table: tbl(
|
||||
|
|
@ -8,13 +10,14 @@ var viewTests = TableTest{
|
|||
srcHdr("_id", fldTypeID),
|
||||
srcHdr("a_string", fldTypeString),
|
||||
srcHdr("a_int", fldTypeInt),
|
||||
srcHdr("a_date", fldTypeTimestamp),
|
||||
),
|
||||
srcRows(
|
||||
srcRow(int64(1), "str1", int64(10)),
|
||||
srcRow(int64(2), "str1", int64(20)),
|
||||
srcRow(int64(3), "str2", int64(30)),
|
||||
srcRow(int64(4), "str2", int64(40)),
|
||||
srcRow(int64(5), "str3", int64(50)),
|
||||
srcRow(int64(1), "str1", int64(10), time.Unix(0, 0).UTC()),
|
||||
srcRow(int64(2), "str1", int64(20), time.Unix(0, 0).UTC()),
|
||||
srcRow(int64(3), "str2", int64(30), time.Unix(0, 0).UTC()),
|
||||
srcRow(int64(4), "str2", int64(40), time.Unix(0, 0).UTC()),
|
||||
srcRow(int64(5), "str3", int64(50), time.Unix(0, 0).UTC()),
|
||||
),
|
||||
),
|
||||
SQLTests: []SQLTest{
|
||||
|
|
@ -122,5 +125,37 @@ var viewTests = TableTest{
|
|||
),
|
||||
ExpErr: "table or view 'viewonviewtable' not found",
|
||||
},
|
||||
{
|
||||
name: "create-view-with-built-in-literals",
|
||||
SQLs: sqls(
|
||||
"create view if not exists viewwithliteral as select _id, a_string, a_int, a_date from viewtable where a_date<CURRENT_TIMESTAMP or a_date<CURRENT_DATE or a_date<'2023-03-15T00:00:00Z';",
|
||||
),
|
||||
ExpHdrs: hdrs(),
|
||||
ExpRows: rows(),
|
||||
Compare: CompareExactUnordered,
|
||||
SortStringKeys: true,
|
||||
},
|
||||
{
|
||||
name: "select-view-with-built-in-literals",
|
||||
SQLs: sqls(
|
||||
"select * from viewwithliteral;",
|
||||
"select _id, a_string, a_int, a_date from viewwithliteral;",
|
||||
),
|
||||
ExpHdrs: hdrs(
|
||||
hdr("_id", fldTypeID),
|
||||
hdr("a_string", fldTypeString),
|
||||
hdr("a_int", fldTypeInt),
|
||||
hdr("a_date", fldTypeTimestamp),
|
||||
),
|
||||
ExpRows: rows(
|
||||
row(int64(1), "str1", int64(10), time.Unix(0, 0).UTC()),
|
||||
row(int64(2), "str1", int64(20), time.Unix(0, 0).UTC()),
|
||||
row(int64(3), "str2", int64(30), time.Unix(0, 0).UTC()),
|
||||
row(int64(4), "str2", int64(40), time.Unix(0, 0).UTC()),
|
||||
row(int64(5), "str3", int64(50), time.Unix(0, 0).UTC()),
|
||||
),
|
||||
Compare: CompareExactUnordered,
|
||||
SortStringKeys: true,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue