github.com/influxdata/influxql@v1.1.0/params.go (about)

     1  package influxql
     2  
     3  import (
     4  	"encoding/json"
     5  	"fmt"
     6  	"strconv"
     7  	"strings"
     8  	"time"
     9  )
    10  
    11  // Value represents a value that can be bound
    12  // to a parameter when parsing the query.
    13  type Value interface {
    14  	TokenType() Token
    15  	Value() string
    16  }
    17  
    18  type (
    19  	// Identifier is an identifier value.
    20  	Identifier string
    21  
    22  	// StringValue is a string literal.
    23  	StringValue string
    24  
    25  	// RegexValue is a regexp literal.
    26  	RegexValue string
    27  
    28  	// NumberValue is a number literal.
    29  	NumberValue float64
    30  
    31  	// IntegerValue is an integer literal.
    32  	IntegerValue int64
    33  
    34  	// BooleanValue is a boolean literal.
    35  	BooleanValue bool
    36  
    37  	// DurationValue is a duration literal.
    38  	DurationValue string
    39  
    40  	// ErrorValue is a special value that returns an error during parsing
    41  	// when it is used.
    42  	ErrorValue string
    43  )
    44  
    45  // BindValue will bind an interface value to its influxql value.
    46  // This method of binding values only supports literals.
    47  func BindValue(v interface{}) Value {
    48  	if jv, ok := v.(json.Number); ok {
    49  		var err error
    50  		v, err = jsonNumberToValue(jv)
    51  		if err != nil {
    52  			return ErrorValue(err.Error())
    53  		}
    54  	}
    55  
    56  	switch v := v.(type) {
    57  	case float64:
    58  		return NumberValue(v)
    59  	case int64:
    60  		return IntegerValue(v)
    61  	case string:
    62  		return StringValue(v)
    63  	case bool:
    64  		return BooleanValue(v)
    65  	case map[string]interface{}:
    66  		return bindObjectValue(v)
    67  	default:
    68  		s := fmt.Sprintf("unable to bind parameter with type %T", v)
    69  		return ErrorValue(s)
    70  	}
    71  }
    72  
    73  // bindObjectValue will bind an object to a value.
    74  func bindObjectValue(m map[string]interface{}) Value {
    75  	if len(m) != 1 {
    76  		return ErrorValue("bound object parameter value must have exactly one entry")
    77  	}
    78  
    79  	var (
    80  		k string
    81  		v interface{}
    82  	)
    83  	for k, v = range m {
    84  		// Nothing done here.
    85  	}
    86  
    87  	if jv, ok := v.(json.Number); ok {
    88  		var err error
    89  		v, err = jsonNumberToValue(jv)
    90  		if err != nil {
    91  			return ErrorValue(err.Error())
    92  		}
    93  	}
    94  
    95  	switch k {
    96  	case "ident", "identifier":
    97  		s, ok := v.(string)
    98  		if !ok {
    99  			return ErrorValue("identifier must be a string value")
   100  		}
   101  		return Identifier(s)
   102  	case "regex":
   103  		s, ok := v.(string)
   104  		if !ok {
   105  			return ErrorValue("regex literal must be a string value")
   106  		}
   107  		return RegexValue(s)
   108  	case "string":
   109  		s, ok := v.(string)
   110  		if !ok {
   111  			return ErrorValue("string literal must be a string value")
   112  		}
   113  		return StringValue(s)
   114  	case "float", "number":
   115  		switch f := v.(type) {
   116  		case float64:
   117  			return NumberValue(f)
   118  		case int64:
   119  			return NumberValue(f)
   120  		default:
   121  			return ErrorValue("number literal must be a float value")
   122  		}
   123  	case "int", "integer":
   124  		i, ok := v.(int64)
   125  		if !ok {
   126  			return ErrorValue("integer literal must be an integer value")
   127  		}
   128  		return IntegerValue(i)
   129  	case "duration":
   130  		switch d := v.(type) {
   131  		case string:
   132  			return DurationValue(d)
   133  		case int64:
   134  			return DurationValue(FormatDuration(time.Duration(d)))
   135  		default:
   136  			return ErrorValue("duration literal must be a string or integer value")
   137  		}
   138  	default:
   139  		return ErrorValue(fmt.Sprintf("unknown bind object type: %s", k))
   140  	}
   141  }
   142  
   143  func (v Identifier) TokenType() Token   { return IDENT }
   144  func (v Identifier) Value() string      { return string(v) }
   145  func (v StringValue) TokenType() Token  { return STRING }
   146  func (v StringValue) Value() string     { return string(v) }
   147  func (v RegexValue) TokenType() Token   { return REGEX }
   148  func (v RegexValue) Value() string      { return string(v) }
   149  func (v NumberValue) TokenType() Token  { return NUMBER }
   150  func (v NumberValue) Value() string     { return strconv.FormatFloat(float64(v), 'f', -1, 64) }
   151  func (v IntegerValue) TokenType() Token { return INTEGER }
   152  func (v IntegerValue) Value() string    { return strconv.FormatInt(int64(v), 10) }
   153  func (v BooleanValue) TokenType() Token {
   154  	if v {
   155  		return TRUE
   156  	} else {
   157  		return FALSE
   158  	}
   159  }
   160  func (v BooleanValue) Value() string     { return "" }
   161  func (v DurationValue) TokenType() Token { return DURATIONVAL }
   162  func (v DurationValue) Value() string    { return string(v) }
   163  func (e ErrorValue) TokenType() Token    { return BOUNDPARAM }
   164  func (e ErrorValue) Value() string       { return string(e) }
   165  
   166  func jsonNumberToValue(v json.Number) (interface{}, error) {
   167  	if strings.Contains(string(v), ".") {
   168  		f, err := v.Float64()
   169  		if err != nil {
   170  			return nil, err
   171  		}
   172  		return f, nil
   173  	} else {
   174  		i, err := v.Int64()
   175  		if err != nil {
   176  			return nil, err
   177  		}
   178  		return i, nil
   179  	}
   180  }