168 lines
3.5 KiB
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
|
|
}
|