Add variable support to PQL

This commit is contained in:
Ben Johnson 2022-02-01 08:30:48 -07:00
parent 8816583cd3
commit 3989b363ce
4 changed files with 1462 additions and 1295 deletions

View file

@ -19,6 +19,22 @@ type Query struct {
conditional []string
}
// ExpandVars recursively replaces variables in the query with their values.
func (q *Query) ExpandVars(vars map[string]interface{}) (*Query, error) {
other := *q
other.Calls = make([]*Call, 0, len(q.Calls))
for _, c := range q.Calls {
newCalls, err := c.ExpandVars(vars)
if err != nil {
return err
}
other.Calls = append(other.Calls, newCalls...)
}
return q, nil
}
func (q *Query) startCall(name string) {
// Coerce every name into a canonical form if we know of one.
if canon, ok := canonicalCaps[strings.ToLower(name)]; ok {
@ -896,6 +912,28 @@ func (c *Call) ArgString(key string) string {
return s
}
// ExpandVars recursively replaces variables in the call with their values.
func (c *Call) ExpandVars(vars map[string]interface{}) ([]*Call, error) {
other := *c
other.Args = CopyArgs(c.Args)
other.Children = make([]*Call, 0, len(c.Children))
// TODO: Replace field variables.
// Recursively expand variables in children.
for _, child := range c.Children {
newChildren, err := child.ExpandVars(vars)
if err != nil {
return nil, err
}
other.Children = append(other.Children, newChildren...)
}
// TODO: Return multiple calls for list.
return []*Call{&other}, nil
}
// Condition represents an operation & value.
// When used in an argument map it represents a binary expression.
type Condition struct {
@ -1034,6 +1072,21 @@ func (cond *Condition) StringSliceValue() ([]string, bool) {
return nil, false
}
// Variable represents a placeholder variable in a query.
type Variable struct {
Name string
}
// NewVariable returns a new instance of Variable.
func NewVariable(name string) *Variable {
return &Variable{Name: name}
}
// String returns the string representation of v.
func (v *Variable) String() string {
return "$" + v.Name
}
func formatValue(v interface{}) string {
switch v := v.(type) {
case nil:
@ -1048,6 +1101,8 @@ func formatValue(v interface{}) string {
return fmt.Sprintf("\"%s\"", v.Format(time.RFC3339Nano))
case *Condition:
return v.String()
case *Variable:
return v.String()
default:
return fmt.Sprintf("%v", v)
}

View file

@ -43,6 +43,7 @@ items <- item (comma items)?
item <- 'null' &(comma / close) { p.addVal(nil) }
/ 'true' &(comma / close) { p.addVal(true) }
/ 'false' &(comma / close) { p.addVal(false) }
/ '$' < variable > { p.addVal(NewVariable(text)) }
/ timefmt { p.addVal(text) }
/ timestampfmt { p.addTimestampVal(text) }
/ < decimal > { p.addNumVal(text) }
@ -54,6 +55,8 @@ item <- 'null' &(comma / close) { p.addVal(nil) }
doublequotedstring <- ( '\\"' / '\\\\' / '\\n' / '\\t' / [^"\\] )*
singlequotedstring <- ( '\\\'' / '\\\\' / '\\n' / '\\t' / [^'\\] )*
variable <- ( [[A-Z]] / '_' ) ( [[A-Z]] / [0-9] / '_' / '-' )*
fieldExpr <- ( [[A-Z]] / '_' ) ( [[A-Z]] / [0-9] / '_' / '-' )*
field <- <fieldExpr / reserved> { p.addField(text) }
reserved <- '_row' / '_col' / '_start' / '_end' / '_timestamp' / '_field'

File diff suppressed because it is too large Load diff

View file

@ -752,6 +752,15 @@ func TestPQLDeepEquality(t *testing.T) {
{Name: "Rows"},
},
}},
{
name: "Variable",
call: "Row(f=$my_VAR123)",
exp: &Call{
Name: "Row",
Args: map[string]interface{}{
"f": &Variable{Name: "my_VAR123"},
},
}},
}
for i, test := range tests {