| 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 (e EvaluableExpression) ToSQLQuery() (string, error) { |
| |
| var stream *tokenStream |
| var transactions *expressionOutputStream |
| var transaction string |
| var err error |
| |
| stream = newTokenStream(e.tokens) |
| transactions = new(expressionOutputStream) |
| |
| for stream.hasNext() { |
| |
| transaction, err = e.findNextSQLString(stream, transactions) |
| if err != nil { |
| return "", err |
| } |
| |
| transactions.add(transaction) |
| } |
| |
| return transactions.createString(" "), nil |
| } |
| |
| func (e 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(e.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) |
| } |
| |
| case TERNARY: |
| |
| switch ternarySymbols[token.Value.(string)] { |
| |
| case COALESCE: |
| |
| left := transactions.rollback() |
| right, err := e.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 = "NOT" |
| default: |
| |
| right, err := e.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 := e.findNextSQLString(stream, transactions) |
| if err != nil { |
| return "", err |
| } |
| |
| ret = fmt.Sprintf("POW(%s, %s)", left, right) |
| case MODULUS: |
| |
| left := transactions.rollback() |
| right, err := e.findNextSQLString(stream, transactions) |
| if err != nil { |
| return "", err |
| } |
| |
| ret = fmt.Sprintf("MOD(%s, %s)", left, right) |
| default: |
| ret = fmt.Sprintf("%s", token.Value) |
| } |
| 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 |
| } |