Beego/vendor/github.com/Knetic/govaluate/EvaluableExpression_sql.go

168 lines
3.5 KiB
Go

package govaluate
import (
"errors"
"fmt"
"regexp"
"time"
)
/*
Returns a string representing this expression as if it were written in SQL.
This function assumes that all parameters exist within the same table, and that the table essentially represents
a serialized object of some sort (e.g., hibernate).
If your data model is more normalized, you may need to consider iterating through each actual token given by `Tokens()`
to create your query.
Boolean values are considered to be "1" for true, "0" for false.
Times are formatted according to this.QueryDateFormat.
*/
func (this EvaluableExpression) ToSQLQuery() (string, error) {
var stream *tokenStream
var transactions *expressionOutputStream
var transaction string
var err error
stream = newTokenStream(this.tokens)
transactions = new(expressionOutputStream)
for stream.hasNext() {
transaction, err = this.findNextSQLString(stream, transactions)
if err != nil {
return "", err
}
transactions.add(transaction)
}
return transactions.createString(" "), nil
}
func (this EvaluableExpression) findNextSQLString(stream *tokenStream, transactions *expressionOutputStream) (string, error) {
var token ExpressionToken
var ret string
token = stream.next()
switch token.Kind {
case STRING:
ret = fmt.Sprintf("'%v'", token.Value)
case PATTERN:
ret = fmt.Sprintf("'%s'", token.Value.(*regexp.Regexp).String())
case TIME:
ret = fmt.Sprintf("'%s'", token.Value.(time.Time).Format(this.QueryDateFormat))
case LOGICALOP:
switch logicalSymbols[token.Value.(string)] {
case AND:
ret = "AND"
case OR:
ret = "OR"
}
case BOOLEAN:
if token.Value.(bool) {
ret = "1"
} else {
ret = "0"
}
case VARIABLE:
ret = fmt.Sprintf("[%s]", token.Value.(string))
case NUMERIC:
ret = fmt.Sprintf("%g", token.Value.(float64))
case COMPARATOR:
switch comparatorSymbols[token.Value.(string)] {
case EQ:
ret = "="
case NEQ:
ret = "<>"
case REQ:
ret = "RLIKE"
case NREQ:
ret = "NOT RLIKE"
default:
ret = fmt.Sprintf("%s", token.Value.(string))
}
case TERNARY:
switch ternarySymbols[token.Value.(string)] {
case COALESCE:
left := transactions.rollback()
right, err := this.findNextSQLString(stream, transactions)
if err != nil {
return "", err
}
ret = fmt.Sprintf("COALESCE(%v, %v)", left, right)
case TERNARY_TRUE:
fallthrough
case TERNARY_FALSE:
return "", errors.New("Ternary operators are unsupported in SQL output")
}
case PREFIX:
switch prefixSymbols[token.Value.(string)] {
case INVERT:
ret = fmt.Sprintf("NOT")
default:
right, err := this.findNextSQLString(stream, transactions)
if err != nil {
return "", err
}
ret = fmt.Sprintf("%s%s", token.Value.(string), right)
}
case MODIFIER:
switch modifierSymbols[token.Value.(string)] {
case EXPONENT:
left := transactions.rollback()
right, err := this.findNextSQLString(stream, transactions)
if err != nil {
return "", err
}
ret = fmt.Sprintf("POW(%s, %s)", left, right)
case MODULUS:
left := transactions.rollback()
right, err := this.findNextSQLString(stream, transactions)
if err != nil {
return "", err
}
ret = fmt.Sprintf("MOD(%s, %s)", left, right)
default:
ret = fmt.Sprintf("%s", token.Value.(string))
}
case CLAUSE:
ret = "("
case CLAUSE_CLOSE:
ret = ")"
case SEPARATOR:
ret = ","
default:
errorMsg := fmt.Sprintf("Unrecognized query token '%s' of kind '%s'", token.Value, token.Kind)
return "", errors.New(errorMsg)
}
return ret, nil
}