|
|
|
package http
|
|
|
|
|
|
|
|
import (
|
|
|
|
"encoding/json"
|
|
|
|
"errors"
|
|
|
|
|
|
|
|
"github.com/rqlite/rqlite/command"
|
|
|
|
)
|
|
|
|
|
|
|
|
var (
|
|
|
|
// ErrNoStatements is returned when a request is empty
|
|
|
|
ErrNoStatements = errors.New("no statements")
|
|
|
|
|
|
|
|
// ErrInvalidRequest is returned when a request cannot be parsed.
|
|
|
|
ErrInvalidRequest = errors.New("invalid request")
|
|
|
|
|
|
|
|
// ErrUnsupportedType is returned when a request contains an unsupported type.
|
|
|
|
ErrUnsupportedType = errors.New("unsupported type")
|
|
|
|
)
|
|
|
|
|
|
|
|
// ParseRequest generates a set of Statements for a given byte slice.
|
|
|
|
func ParseRequest(b []byte) ([]*command.Statement, error) {
|
|
|
|
if b == nil {
|
|
|
|
return nil, ErrNoStatements
|
|
|
|
}
|
|
|
|
|
|
|
|
simple := []string{} // Represents a set of unparameterized queries
|
|
|
|
parameterized := [][]interface{}{} // Represents a set of parameterized queries
|
|
|
|
|
|
|
|
// Try simple form first.
|
|
|
|
err := json.Unmarshal(b, &simple)
|
|
|
|
if err == nil {
|
|
|
|
if len(simple) == 0 {
|
|
|
|
return nil, ErrNoStatements
|
|
|
|
}
|
|
|
|
|
|
|
|
stmts := make([]*command.Statement, len(simple))
|
|
|
|
for i := range simple {
|
|
|
|
stmts[i] = &command.Statement{
|
|
|
|
Sql: simple[i],
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return stmts, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// Next try parameterized form.
|
|
|
|
if err := json.Unmarshal(b, ¶meterized); err != nil {
|
|
|
|
return nil, ErrInvalidRequest
|
|
|
|
}
|
|
|
|
stmts := make([]*command.Statement, len(parameterized))
|
|
|
|
|
|
|
|
for i := range parameterized {
|
|
|
|
if len(parameterized[i]) == 0 {
|
|
|
|
return nil, ErrNoStatements
|
|
|
|
}
|
|
|
|
|
|
|
|
sql, ok := parameterized[i][0].(string)
|
|
|
|
if !ok {
|
|
|
|
return nil, ErrInvalidRequest
|
|
|
|
}
|
|
|
|
stmts[i] = &command.Statement{
|
|
|
|
Sql: sql,
|
|
|
|
Parameters: nil,
|
|
|
|
}
|
|
|
|
if len(parameterized[i]) == 1 {
|
|
|
|
// No actual parameters after the SQL string
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
|
|
|
stmts[i].Parameters = make([]*command.Parameter, 0)
|
|
|
|
for j := range parameterized[i][1:] {
|
|
|
|
m, ok := parameterized[i][j+1].(map[string]interface{})
|
|
|
|
if ok {
|
|
|
|
for k, v := range m {
|
|
|
|
p, err := makeParameter(k, v)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
stmts[i].Parameters = append(stmts[i].Parameters, p)
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
p, err := makeParameter("", parameterized[i][j+1])
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
stmts[i].Parameters = append(stmts[i].Parameters, p)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return stmts, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func makeParameter(name string, i interface{}) (*command.Parameter, error) {
|
|
|
|
switch v := i.(type) {
|
|
|
|
case int:
|
|
|
|
case int64:
|
|
|
|
return &command.Parameter{
|
|
|
|
Value: &command.Parameter_I{
|
|
|
|
I: v,
|
|
|
|
},
|
|
|
|
Name: name,
|
|
|
|
}, nil
|
|
|
|
case float64:
|
|
|
|
return &command.Parameter{
|
|
|
|
Value: &command.Parameter_D{
|
|
|
|
D: v,
|
|
|
|
},
|
|
|
|
Name: name,
|
|
|
|
}, nil
|
|
|
|
case bool:
|
|
|
|
return &command.Parameter{
|
|
|
|
Value: &command.Parameter_B{
|
|
|
|
B: v,
|
|
|
|
},
|
|
|
|
Name: name,
|
|
|
|
}, nil
|
|
|
|
case []byte:
|
|
|
|
return &command.Parameter{
|
|
|
|
Value: &command.Parameter_Y{
|
|
|
|
Y: v,
|
|
|
|
},
|
|
|
|
Name: name,
|
|
|
|
}, nil
|
|
|
|
case string:
|
|
|
|
return &command.Parameter{
|
|
|
|
Value: &command.Parameter_S{
|
|
|
|
S: v,
|
|
|
|
},
|
|
|
|
Name: name,
|
|
|
|
}, nil
|
|
|
|
}
|
|
|
|
return nil, ErrUnsupportedType
|
|
|
|
}
|